From 67bec98589df38a85c2709d4f24363e7a075a6eb Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Sun, 22 Jan 2023 14:31:15 -0500 Subject: [PATCH 001/263] Start of restructuring changes - combined `Item.autoEquipCheck` and `Item.autoEquipKeepCheck` - combined `Item.autoEquipMercCheck` and `Item.autoEquipKeepCheckMerc` - changed `Item.getEquippedItem` to `Item.getEquipped` - changed `Item.getEquippedItemMerc` to `Item.getMercEquipped` - wrapped charm section in ItemOverrides.js in an IIFE to fix global scope declaration of helper functions - created `Utils\` folder - created `Utils\General.js` module for declarations that don't need to be in the global scope after they've been used - Changed the configs to IIFE as LoadConfig isn't used again after it's initial call - removed `LoadConfig.call()` from ConfigOverrides.js - removed function body from `Loader.loadScripts` the function itself shouldn't ever be called but if it is proxy it to `Loader.run` --- libs/SoloPlay/BuildFiles/Runewords/Lore.js | 4 +- .../BuildFiles/Runewords/MercInsight.js | 2 +- libs/SoloPlay/BuildFiles/Runewords/Myth.js | 2 +- libs/SoloPlay/BuildFiles/Runewords/Stealth.js | 2 +- libs/SoloPlay/BuildFiles/Runewords/Steel.js | 4 +- .../sorceress/sorceress.SteppingBuild.js | 2 +- libs/SoloPlay/Config/Amazon.js | 50 +- libs/SoloPlay/Config/Assassin.js | 45 +- libs/SoloPlay/Config/Barbarian.js | 72 +- libs/SoloPlay/Config/Druid.js | 43 +- libs/SoloPlay/Config/Necromancer.js | 48 +- libs/SoloPlay/Config/Paladin.js | 62 +- libs/SoloPlay/Config/Sorceress.js | 47 +- .../ClassAttackOverrides/AmazonAttacks-WIP.js | 2 +- .../ClassAttackOverrides/AmazonAttacks.js | 2 +- libs/SoloPlay/Functions/ConfigOverrides.js | 10 - libs/SoloPlay/Functions/Globals.js | 14 +- libs/SoloPlay/Functions/ItemOverrides.js | 1185 +++++++++-------- libs/SoloPlay/Functions/ItemPrototypes.js | 24 +- libs/SoloPlay/Functions/ItemUtilities.js | 879 ++++++------ libs/SoloPlay/Functions/LoaderOverrides.js | 81 +- libs/SoloPlay/Functions/Quest.js | 4 +- libs/SoloPlay/Functions/RunewordsOverrides.js | 2 +- libs/SoloPlay/Scripts/bloodraven.js | 2 +- libs/SoloPlay/Threads/ToolsThread.js | 4 +- libs/SoloPlay/Tools/SoloIndex.js | 2 +- libs/SoloPlay/Utils/General.js | 114 ++ 27 files changed, 1383 insertions(+), 1325 deletions(-) create mode 100644 libs/SoloPlay/Utils/General.js diff --git a/libs/SoloPlay/BuildFiles/Runewords/Lore.js b/libs/SoloPlay/BuildFiles/Runewords/Lore.js index 216b658e..d55518d1 100644 --- a/libs/SoloPlay/BuildFiles/Runewords/Lore.js +++ b/libs/SoloPlay/BuildFiles/Runewords/Lore.js @@ -35,7 +35,7 @@ NTIP.arrayLooping(classLoreHelm); // Normal Helms - if (Item.getEquippedItem(sdk.body.Head).tier < 150) { + if (Item.getEquipped(sdk.body.Head).tier < 150) { NTIP.arrayLooping(loreHelm); } @@ -84,7 +84,7 @@ NTIP.arrayLooping(classLoreHelm); // Normal Helms - if (Item.getEquippedItem(sdk.body.Head).tier < 150) { + if (Item.getEquipped(sdk.body.Head).tier < 150) { NTIP.arrayLooping(loreHelm); } diff --git a/libs/SoloPlay/BuildFiles/Runewords/MercInsight.js b/libs/SoloPlay/BuildFiles/Runewords/MercInsight.js index 9c34ef93..5bf51f37 100644 --- a/libs/SoloPlay/BuildFiles/Runewords/MercInsight.js +++ b/libs/SoloPlay/BuildFiles/Runewords/MercInsight.js @@ -8,7 +8,7 @@ ]; NTIP.arrayLooping(Insight); - if (!me.hell && Item.getEquippedItemMerc(sdk.body.RightArm).prefixnum !== sdk.locale.items.Insight && !Check.haveBase("polearm", 4)) { + if (!me.hell && Item.getMercEquipped(sdk.body.RightArm).prefixnum !== sdk.locale.items.Insight && !Check.haveBase("polearm", 4)) { NTIP.addLine("[name] == voulge && [flag] != ethereal && [quality] == normal && [level] >= 26 && [level] <= 40 # [sockets] == 0 # [maxquantity] == 1"); } diff --git a/libs/SoloPlay/BuildFiles/Runewords/Myth.js b/libs/SoloPlay/BuildFiles/Runewords/Myth.js index 22c11f82..6bfbbbfd 100644 --- a/libs/SoloPlay/BuildFiles/Runewords/Myth.js +++ b/libs/SoloPlay/BuildFiles/Runewords/Myth.js @@ -17,7 +17,7 @@ } // Have Hel rune and currently equipped armor is low tier - if (me.getItem(sdk.items.runes.Hel) && Item.getEquippedItem(sdk.body.Armor).tier < 200) { + if (me.getItem(sdk.items.runes.Hel) && Item.getEquipped(sdk.body.Armor).tier < 200) { NTIP.addLine("[name] == breastplate && [flag] != ethereal && [quality] >= normal && [quality] <= superior # [sockets] == 3 # [maxquantity] == 1"); } diff --git a/libs/SoloPlay/BuildFiles/Runewords/Stealth.js b/libs/SoloPlay/BuildFiles/Runewords/Stealth.js index bebb520c..d5ecc3a0 100644 --- a/libs/SoloPlay/BuildFiles/Runewords/Stealth.js +++ b/libs/SoloPlay/BuildFiles/Runewords/Stealth.js @@ -13,7 +13,7 @@ ]; NTIP.arrayLooping(stealthArmor); - if (Item.getEquippedItem(sdk.body.Armor).tier < 200) { + if (Item.getEquipped(sdk.body.Armor).tier < 200) { NTIP.addLine("[name] == breastplate && [flag] != ethereal && [quality] >= normal && [quality] <= superior # [sockets] == 2 # [maxquantity] == 1"); } diff --git a/libs/SoloPlay/BuildFiles/Runewords/Steel.js b/libs/SoloPlay/BuildFiles/Runewords/Steel.js index 1009ff1a..ba13920c 100644 --- a/libs/SoloPlay/BuildFiles/Runewords/Steel.js +++ b/libs/SoloPlay/BuildFiles/Runewords/Steel.js @@ -5,9 +5,9 @@ ]; NTIP.arrayLooping(Steel); - if (Item.getEquippedItem(sdk.body.LeftArm).tier < 500 && Item.getEquippedItem(sdk.body.LeftArm).tier > 395) { + if (Item.getEquipped(sdk.body.LeftArm).tier < 500 && Item.getEquipped(sdk.body.LeftArm).tier > 395) { NTIP.addLine("[type] == sword && [flag] != ethereal && [quality] >= normal && [quality] <= superior && [wsm] <= 10 && [strreq] <= 150 && [class] == elite # [sockets] == 2 # [maxquantity] == 1"); - } else if (Item.getEquippedItem(sdk.body.LeftArm).tier < 500 && Item.getEquippedItem(sdk.body.LeftArm).tier > 278) { + } else if (Item.getEquipped(sdk.body.LeftArm).tier < 500 && Item.getEquipped(sdk.body.LeftArm).tier > 278) { NTIP.addLine("[type] == sword && [flag] != ethereal && [quality] >= normal && [quality] <= superior && [wsm] <= 10 && [strreq] <= 150 && [class] > normal # [sockets] == 2 # [maxquantity] == 1"); } else { NTIP.addLine("[type] == sword && [flag] != ethereal && [quality] == superior && [wsm] <= 10 && [strreq] <= 150 # [enhanceddamage] >= 10 && [sockets] == 2 # [maxquantity] == 1"); diff --git a/libs/SoloPlay/BuildFiles/sorceress/sorceress.SteppingBuild.js b/libs/SoloPlay/BuildFiles/sorceress/sorceress.SteppingBuild.js index dede47a3..e20e2203 100644 --- a/libs/SoloPlay/BuildFiles/sorceress/sorceress.SteppingBuild.js +++ b/libs/SoloPlay/BuildFiles/sorceress/sorceress.SteppingBuild.js @@ -71,7 +71,7 @@ build.AutoBuildTemplate[1] = buildAutoBuildTempObj(() => { Config.AttackSkill = [-1, sdk.skills.Blizzard, sdk.skills.IceBlast, sdk.skills.Blizzard, sdk.skills.IceBlast, -1, -1]; Config.BeltColumn = ["hp", "hp", "mp", "mp"]; Config.HPBuffer = me.expansion && !me.normal ? 2 : 5; - Config.MPBuffer = (me.expansion && !me.normal || Item.getEquippedItemMerc(sdk.body.RightArm).prefixnum === sdk.locale.items.Insight) ? 2 : 5; + Config.MPBuffer = (me.expansion && !me.normal || Item.getMercEquipped(sdk.body.RightArm).prefixnum === sdk.locale.items.Insight) ? 2 : 5; Config.SkipImmune = ["cold"]; SetUp.belt(); }); diff --git a/libs/SoloPlay/Config/Amazon.js b/libs/SoloPlay/Config/Amazon.js index 6373fd01..8037b6c3 100644 --- a/libs/SoloPlay/Config/Amazon.js +++ b/libs/SoloPlay/Config/Amazon.js @@ -16,7 +16,7 @@ * 4. Save the profile and start */ -function LoadConfig () { +(function LoadConfig () { includeIfNotIncluded("SoloPlay/Functions/MiscOverrides.js"); includeIfNotIncluded("SoloPlay/Functions/Globals.js"); @@ -125,17 +125,17 @@ function LoadConfig () { Config.imbueables = [ {name: sdk.items.MaidenJavelin, condition: () => me.normal && me.expansion}, {name: sdk.items.CeremonialJavelin, condition: () => !me.normal && (me.charlvl < 48 || me.trueStr < 107 || me.trueDex < 151) && me.expansion}, - {name: sdk.items.MatriarchalJavelin, condition: () => (Item.getEquippedItem(sdk.body.RightArm).tier < 100000 && me.trueStr >= 107 && me.trueDex >= 151 && me.expansion)}, - {name: sdk.items.Belt, condition: () => (me.normal && (Item.getEquippedItem(sdk.body.RightArm).tier > 100000 || me.classic))}, - {name: sdk.items.MeshBelt, condition: () => (!me.normal && me.charlvl < 46 && me.trueStr > 58 && (Item.getEquippedItem(sdk.body.RightArm).tier > 100000 || me.classic))}, - {name: sdk.items.SpiderwebSash, condition: () => (!me.normal && me.trueStr > 50 && (Item.getEquippedItem(sdk.body.RightArm).tier > 100000 || me.classic))}, + {name: sdk.items.MatriarchalJavelin, condition: () => (Item.getEquipped(sdk.body.RightArm).tier < 100000 && me.trueStr >= 107 && me.trueDex >= 151 && me.expansion)}, + {name: sdk.items.Belt, condition: () => (me.normal && (Item.getEquipped(sdk.body.RightArm).tier > 100000 || me.classic))}, + {name: sdk.items.MeshBelt, condition: () => (!me.normal && me.charlvl < 46 && me.trueStr > 58 && (Item.getEquipped(sdk.body.RightArm).tier > 100000 || me.classic))}, + {name: sdk.items.SpiderwebSash, condition: () => (!me.normal && me.trueStr > 50 && (Item.getEquipped(sdk.body.RightArm).tier > 100000 || me.classic))}, ]; let imbueArr = SetUp.imbueItems(); !me.smith && NTIP.arrayLooping(imbueArr); - if (Item.getEquippedItem(sdk.body.RightArm).tier < 100000) { + if (Item.getEquipped(sdk.body.RightArm).tier < 100000) { Config.GambleItems.push("Javelin"); Config.GambleItems.push("Pilum"); Config.GambleItems.push("Short Spear"); @@ -145,15 +145,15 @@ function LoadConfig () { switch (me.gametype) { case sdk.game.gametype.Classic: // Res shield - if (Item.getEquippedItem(sdk.body.LeftArm).tier < 487) { + if (Item.getEquipped(sdk.body.LeftArm).tier < 487) { includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/PDiamondShield.js"); } break; case sdk.game.gametype.Expansion: NTIP.addLine("[name] >= Vexrune && [name] <= Zodrune"); + const { basicSocketables, addSocketableObj } = require("../Utils/General"); - // basicSocketables located in Globals Config.socketables = Config.socketables.concat(basicSocketables.all); Config.socketables.push(addSocketableObj(sdk.items.Bill, [], [], me.normal, (item) => item.ilvl >= 26 && item.isBaseType @@ -167,11 +167,11 @@ function LoadConfig () { /* Crafting */ // Going with Blood but TODO is test HitPower vs Blood - if (Item.getEquippedItem(sdk.body.Neck).tier < 100000) { + if (Item.getEquipped(sdk.body.Neck).tier < 100000) { Config.Recipes.push([Recipe.Blood.Amulet]); } - if (Item.getEquippedItem(sdk.body.RingLeft).tier < 100000) { + if (Item.getEquipped(sdk.body.RingLeft).tier < 100000) { Config.Recipes.push([Recipe.Blood.Ring]); } @@ -206,9 +206,9 @@ function LoadConfig () { } // Spirit shield - while lvling and Wf final switch - if ((me.ladder || Developer.addLadderRW) && (Item.getEquippedItem(5).tier < 1000 + if ((me.ladder || Developer.addLadderRW) && (Item.getEquipped(5).tier < 1000 && (["Witchyzon", "Wfzon", "Faithbowzon"].indexOf(SetUp.currentBuild) === -1) - || (SetUp.finalBuild === "Wfzon" && Item.getEquippedItem(12).prefixnum !== sdk.locale.items.Spirit))) { + || (SetUp.finalBuild === "Wfzon" && Item.getEquipped(12).prefixnum !== sdk.locale.items.Spirit))) { includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/SpiritShield.js"); } @@ -231,12 +231,12 @@ function LoadConfig () { } // Infinity - if ((me.ladder || Developer.addLadderRW) && Item.getEquippedItemMerc(sdk.body.RightArm).prefixnum !== sdk.locale.items.Infinity) { + if ((me.ladder || Developer.addLadderRW) && Item.getMercEquipped(sdk.body.RightArm).prefixnum !== sdk.locale.items.Infinity) { includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/MercInfinity.js"); } // Spirit shield - if ((me.ladder || Developer.addLadderRW) && (Item.getEquippedItem(sdk.body.LeftArm).tier < 1000 || Item.getEquippedItem(sdk.body.LeftArmSecondary).prefixnum !== sdk.locale.items.Spirit)) { + if ((me.ladder || Developer.addLadderRW) && (Item.getEquipped(sdk.body.LeftArm).tier < 1000 || Item.getEquipped(sdk.body.LeftArmSecondary).prefixnum !== sdk.locale.items.Spirit)) { includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/SpiritShield.js"); } @@ -256,47 +256,47 @@ function LoadConfig () { } // Merc Insight - if ((me.ladder || Developer.addLadderRW) && Item.getEquippedItemMerc(sdk.body.RightArm).tier < 3600) { + if ((me.ladder || Developer.addLadderRW) && Item.getMercEquipped(sdk.body.RightArm).tier < 3600) { includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/MercInsight.js"); } // Lore - if (Item.getEquippedItem(sdk.body.Head).tier < 250) { + if (Item.getEquipped(sdk.body.Head).tier < 250) { includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/Lore.js"); } // Ancients' Pledge - if (Item.getEquippedItem(sdk.body.LeftArm).tier < 500 && ["Witchyzon", "Wfzon", "Faithbowzon"].indexOf(SetUp.currentBuild) === -1) { + if (Item.getEquipped(sdk.body.LeftArm).tier < 500 && ["Witchyzon", "Wfzon", "Faithbowzon"].indexOf(SetUp.currentBuild) === -1) { includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/AncientsPledge.js"); } // Treachery - if (Item.getEquippedItem(sdk.body.Armor).tier < 634) { + if (Item.getEquipped(sdk.body.Armor).tier < 634) { includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/Treachery.js"); } // Merc Doom - if ((me.ladder || Developer.addLadderRW) && Item.getEquippedItemMerc(sdk.body.RightArm).prefixnum !== 20532 && SetUp.finalBuild !== "Javazon") { + if ((me.ladder || Developer.addLadderRW) && Item.getMercEquipped(sdk.body.RightArm).prefixnum !== 20532 && SetUp.finalBuild !== "Javazon") { includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/MercDoom.js"); } // Merc Fortitude - if (Item.getEquippedItemMerc(sdk.body.Armor).prefixnum !== sdk.locale.items.Fortitude) { + if (Item.getMercEquipped(sdk.body.Armor).prefixnum !== sdk.locale.items.Fortitude) { includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/MercFortitude.js"); } // Merc Treachery - if (Item.getEquippedItemMerc(sdk.body.Armor).tier < 15000) { + if (Item.getMercEquipped(sdk.body.Armor).tier < 15000) { includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/MercTreachery.js"); } // Smoke - if (Item.getEquippedItem(sdk.body.Armor).tier < 634) { + if (Item.getEquipped(sdk.body.Armor).tier < 634) { includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/Smoke.js"); } // Stealth - if (Item.getEquippedItem(sdk.body.Armor).tier < 233) { + if (Item.getEquipped(sdk.body.Armor).tier < 233) { includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/Stealth.js"); } @@ -304,4 +304,6 @@ function LoadConfig () { break; } -} + + return true; +})(); diff --git a/libs/SoloPlay/Config/Assassin.js b/libs/SoloPlay/Config/Assassin.js index 1e85c619..48f7d4d8 100644 --- a/libs/SoloPlay/Config/Assassin.js +++ b/libs/SoloPlay/Config/Assassin.js @@ -14,7 +14,7 @@ * 4. Save the profile and start */ -function LoadConfig () { +(function LoadConfig () { includeIfNotIncluded("SoloPlay/Functions/MiscOverrides.js"); includeIfNotIncluded("SoloPlay/Functions/Globals.js"); @@ -129,18 +129,19 @@ function LoadConfig () { Config.imbueables = [ {name: sdk.items.Claws, condition: () => (me.normal)}, - {name: sdk.items.HandScythe, condition: () => (!me.normal && Item.getEquippedItem(sdk.body.RightArm).tier < 777 && (me.trueStr < 79 || me.trueDex < 79))}, - {name: sdk.items.GreaterTalons, condition: () => (Item.getEquippedItem(sdk.body.RightArm).tier < 777 && me.trueStr >= 79 && me.trueDex >= 79)}, - {name: sdk.items.Belt, condition: () => (me.normal && (Item.getEquippedItem(sdk.body.RightArm).tier > 777))}, - {name: sdk.items.MeshBelt, condition: () => (!me.normal && me.charlvl < 46 && me.trueStr > 58 && (Item.getEquippedItem(sdk.body.RightArm).tier > 777))}, - {name: sdk.items.SpiderwebSash, condition: () => (!me.normal && me.trueStr > 50 && (Item.getEquippedItem(sdk.body.RightArm).tier > 777))}, + {name: sdk.items.HandScythe, condition: () => (!me.normal && Item.getEquipped(sdk.body.RightArm).tier < 777 && (me.trueStr < 79 || me.trueDex < 79))}, + {name: sdk.items.GreaterTalons, condition: () => (Item.getEquipped(sdk.body.RightArm).tier < 777 && me.trueStr >= 79 && me.trueDex >= 79)}, + {name: sdk.items.Belt, condition: () => (me.normal && (Item.getEquipped(sdk.body.RightArm).tier > 777))}, + {name: sdk.items.MeshBelt, condition: () => (!me.normal && me.charlvl < 46 && me.trueStr > 58 && (Item.getEquipped(sdk.body.RightArm).tier > 777))}, + {name: sdk.items.SpiderwebSash, condition: () => (!me.normal && me.trueStr > 50 && (Item.getEquipped(sdk.body.RightArm).tier > 777))}, ].filter((item) => item.condition()); let imbueArr = SetUp.imbueItems(); !me.smith && NTIP.arrayLooping(imbueArr); - // basicSocketables located in Globals + const { basicSocketables, addSocketableObj } = require("../Utils/General"); + Config.socketables = Config.socketables.concat(basicSocketables.caster, basicSocketables.all); Config.socketables.push(addSocketableObj(sdk.items.Monarch, [], [], !me.hell, (item) => !Check.haveBase("monarch", 4) && item.ilvl >= 41 && item.isBaseType && !item.ethereal @@ -156,7 +157,7 @@ function LoadConfig () { )); // Pride - if ((me.ladder || Developer.addLadderRW) && Item.getEquippedItemMerc(sdk.body.RightArm).prefixnum !== sdk.locale.items.Pride) { + if ((me.ladder || Developer.addLadderRW) && Item.getMercEquipped(sdk.body.RightArm).prefixnum !== sdk.locale.items.Pride) { includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/MercPride.js"); } @@ -182,7 +183,7 @@ function LoadConfig () { )); // Infinity - if ((me.ladder || Developer.addLadderRW) && Item.getEquippedItemMerc(sdk.body.RightArm).prefixnum !== sdk.locale.items.Infinity) { + if ((me.ladder || Developer.addLadderRW) && Item.getMercEquipped(sdk.body.RightArm).prefixnum !== sdk.locale.items.Infinity) { includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/MercInfinity.js"); } @@ -203,11 +204,11 @@ function LoadConfig () { Check.itemSockables(sdk.items.Shako, "unique", "Harlequin Crest"); /* Crafting */ - if (Item.getEquippedItem(sdk.body.Neck).tier < 100000) { + if (Item.getEquipped(sdk.body.Neck).tier < 100000) { Config.Recipes.push([Recipe.Caster.Amulet]); } - if (Item.getEquippedItem(sdk.body.RingLeft).tier < 100000) { + if (Item.getEquipped(sdk.body.RingLeft).tier < 100000) { Config.Recipes.push([Recipe.Caster.Ring]); } @@ -217,49 +218,51 @@ function LoadConfig () { } // Spirit Sword - if ((me.ladder || Developer.addLadderRW) && Item.getEquippedItem(sdk.body.RightArm).tier < 777) { + if ((me.ladder || Developer.addLadderRW) && Item.getEquipped(sdk.body.RightArm).tier < 777) { includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/SpiritSword.js"); } // Spirit shield - if ((me.ladder || Developer.addLadderRW) && (Item.getEquippedItem(sdk.body.LeftArm).tier < 1000 || Item.getEquippedItem(sdk.body.LeftArmSecondary).prefixnum !== sdk.locale.items.Spirit)) { + if ((me.ladder || Developer.addLadderRW) && (Item.getEquipped(sdk.body.LeftArm).tier < 1000 || Item.getEquipped(sdk.body.LeftArmSecondary).prefixnum !== sdk.locale.items.Spirit)) { includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/SpiritShield.js"); } // Merc Insight - if ((me.ladder || Developer.addLadderRW) && Item.getEquippedItemMerc(sdk.body.RightArm).tier < 3600) { + if ((me.ladder || Developer.addLadderRW) && Item.getMercEquipped(sdk.body.RightArm).tier < 3600) { includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/MercInsight.js"); } // Lore - if (Item.getEquippedItem(sdk.body.Head).tier < 315) { + if (Item.getEquipped(sdk.body.Head).tier < 315) { includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/Lore.js"); } // Ancients' Pledge - if (Item.getEquippedItem(sdk.body.LeftArm).tier < 500) { + if (Item.getEquipped(sdk.body.LeftArm).tier < 500) { includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/AncientsPledge.js"); } // Merc Fortitude - if (Item.getEquippedItemMerc(sdk.body.Armor).prefixnum !== sdk.locale.items.Fortitude) { + if (Item.getMercEquipped(sdk.body.Armor).prefixnum !== sdk.locale.items.Fortitude) { includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/MercFortitude.js"); } // Merc Treachery - if (Item.getEquippedItemMerc(sdk.body.Armor).tier < 15000) { + if (Item.getMercEquipped(sdk.body.Armor).tier < 15000) { includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/MercTreachery.js"); } // Smoke - if (Item.getEquippedItem(sdk.body.Armor).tier < 450) { + if (Item.getEquipped(sdk.body.Armor).tier < 450) { includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/Smoke.js"); } // Stealth - if (Item.getEquippedItem(sdk.body.Armor).tier < 233) { + if (Item.getEquipped(sdk.body.Armor).tier < 233) { includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/Stealth.js"); } SoloWants.buildList(); -} + + return true; +})(); diff --git a/libs/SoloPlay/Config/Barbarian.js b/libs/SoloPlay/Config/Barbarian.js index 3f661829..9af1b0be 100644 --- a/libs/SoloPlay/Config/Barbarian.js +++ b/libs/SoloPlay/Config/Barbarian.js @@ -16,7 +16,7 @@ * 4. Save the profile and start */ -function LoadConfig () { +(function LoadConfig () { includeIfNotIncluded("SoloPlay/Functions/MiscOverrides.js"); includeIfNotIncluded("SoloPlay/Functions/Globals.js"); @@ -118,10 +118,10 @@ function LoadConfig () { Config.imbueables = [ {name: sdk.items.AvengerGuard, condition: () => (me.normal && me.expansion)}, {name: sdk.items.SlayerGuard, condition: () => (!me.normal && me.trueStr >= 118 && me.expansion)}, - {name: sdk.items.CarnageHelm, condition: () => (Item.getEquippedItem(sdk.body.Head).tier < 100000 && me.trueStr >= 106 && me.expansion)}, - {name: sdk.items.Belt, condition: () => (me.normal && (Item.getEquippedItem(sdk.body.Head).tier > 100000 || me.classic))}, - {name: sdk.items.MeshBelt, condition: () => (!me.normal && me.charlvl < 46 && me.trueStr > 58 && (Item.getEquippedItem(sdk.body.RightArm).tier > 100000 || me.classic))}, - {name: sdk.items.SpiderwebSash, condition: () => (!me.normal && me.trueStr > 50 && (Item.getEquippedItem(sdk.body.RightArm).tier > 100000 || me.classic))}, + {name: sdk.items.CarnageHelm, condition: () => (Item.getEquipped(sdk.body.Head).tier < 100000 && me.trueStr >= 106 && me.expansion)}, + {name: sdk.items.Belt, condition: () => (me.normal && (Item.getEquipped(sdk.body.Head).tier > 100000 || me.classic))}, + {name: sdk.items.MeshBelt, condition: () => (!me.normal && me.charlvl < 46 && me.trueStr > 58 && (Item.getEquipped(sdk.body.RightArm).tier > 100000 || me.classic))}, + {name: sdk.items.SpiderwebSash, condition: () => (!me.normal && me.trueStr > 50 && (Item.getEquipped(sdk.body.RightArm).tier > 100000 || me.classic))}, ].filter((item) => item.condition()); let imbueArr = SetUp.imbueItems(); @@ -133,14 +133,14 @@ function LoadConfig () { break; case sdk.game.gametype.Expansion: NTIP.addLine("[name] >= VexRune && [name] <= ZodRune"); + const { basicSocketables, addSocketableObj } = require("../Utils/General"); - // basicSocketables located in Globals Config.socketables = Config.socketables.concat(basicSocketables.all); Config.socketables.push(addSocketableObj(sdk.items.Flamberge, [], [], - true, (item) => me.normal && Item.getEquippedItem(sdk.body.LeftArm).tier < 600 && !Check.haveBase("sword", 5) && !me.checkItem({name: sdk.locale.items.Honor}).have && item.ilvl >= 41 && item.isBaseType && !item.ethereal + true, (item) => me.normal && Item.getEquipped(sdk.body.LeftArm).tier < 600 && !Check.haveBase("sword", 5) && !me.checkItem({name: sdk.locale.items.Honor}).have && item.ilvl >= 41 && item.isBaseType && !item.ethereal )); Config.socketables.push(addSocketableObj(sdk.items.Zweihander, [], [], - true, (item) => Item.getEquippedItem(sdk.body.LeftArm).tier < 1000 && !Check.haveBase("sword", 5) && !me.checkItem({name: sdk.locale.items.Honor}).have && item.ilvl >= 41 && item.isBaseType && !item.ethereal + true, (item) => Item.getEquipped(sdk.body.LeftArm).tier < 1000 && !Check.haveBase("sword", 5) && !me.checkItem({name: sdk.locale.items.Honor}).have && item.ilvl >= 41 && item.isBaseType && !item.ethereal )); if (SetUp.finalBuild !== "Immortalwhirl") { @@ -151,7 +151,7 @@ function LoadConfig () { if (["Immortalwhirl", "Singer"].indexOf(SetUp.finalBuild) === -1) { // Grief - if ((me.ladder || Developer.addLadderRW) && (!me.checkItem({name: sdk.locale.items.Grief}).have || (SetUp.finalBuild === "Whirlwind" && Item.getEquippedItem(sdk.body.LeftArm).prefixnum !== sdk.locale.items.Grief))) { + if ((me.ladder || Developer.addLadderRW) && (!me.checkItem({name: sdk.locale.items.Grief}).have || (SetUp.finalBuild === "Whirlwind" && Item.getEquipped(sdk.body.LeftArm).prefixnum !== sdk.locale.items.Grief))) { includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/Grief.js"); } @@ -161,7 +161,7 @@ function LoadConfig () { } // Doom - if ((me.ladder || Developer.addLadderRW) && Item.getEquippedItemMerc(sdk.body.RightArm).prefixnum !== 20532) { + if ((me.ladder || Developer.addLadderRW) && Item.getMercEquipped(sdk.body.RightArm).prefixnum !== 20532) { includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/MercDoom.js"); } } @@ -189,7 +189,7 @@ function LoadConfig () { break; case "Singer": // Heart of the Oak - if (Item.getEquippedItem(sdk.body.LeftArm).prefixnum !== sdk.locale.items.HeartoftheOak && me.checkItem({name: sdk.locale.items.Enigma}).have) { + if (Item.getEquipped(sdk.body.LeftArm).prefixnum !== sdk.locale.items.HeartoftheOak && me.checkItem({name: sdk.locale.items.Enigma}).have) { includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/HeartOfTheOak.js"); } @@ -201,7 +201,7 @@ function LoadConfig () { break; case "Immortalwhirl": // Infinity - if ((me.ladder || Developer.addLadderRW) && Item.getEquippedItemMerc(sdk.body.RightArm).prefixnum !== sdk.locale.items.Infinity) { + if ((me.ladder || Developer.addLadderRW) && Item.getMercEquipped(sdk.body.RightArm).prefixnum !== sdk.locale.items.Infinity) { includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/MercInfinity.js"); } @@ -225,15 +225,15 @@ function LoadConfig () { } /* Crafting */ - if (Item.getEquippedItem(sdk.body.Neck).tier < 100000) { + if (Item.getEquipped(sdk.body.Neck).tier < 100000) { Check.currentBuild().caster ? Config.Recipes.push([Recipe.Caster.Amulet]) : Config.Recipes.push([Recipe.Blood.Amulet]); } - if (Item.getEquippedItem(sdk.body.RingLeft).tier < 100000) { + if (Item.getEquipped(sdk.body.RingLeft).tier < 100000) { Check.currentBuild().caster ? Config.Recipes.push([Recipe.Caster.Ring]) : Config.Recipes.push([Recipe.Blood.Ring]); } - if (Item.getEquippedItem(sdk.body.LeftArm).tier < 1370) { + if (Item.getEquipped(sdk.body.LeftArm).tier < 1370) { if (me.rawStrength >= 150 && me.rawDexterity >= 88) { // Upgrade Bloodletter to Elite Config.Recipes.push([Recipe.Unique.Weapon.ToElite, "Gladius", Roll.NonEth]); @@ -269,21 +269,21 @@ function LoadConfig () { } // Lawbringer - Amn/Lem/Ko - if (Item.getEquippedItem(sdk.body.LeftArm).tier < 1370) { + if (Item.getEquipped(sdk.body.LeftArm).tier < 1370) { includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/Lawbringer.js"); } // Voice Of Reason - Lem/Ko/El/Eld - if (Item.getEquippedItem(sdk.body.RightArm).tier > 1100 && Item.getEquippedItem(sdk.body.LeftArm).tier < 1270 && !Check.haveItem("ring", "unique", "Raven Frost")) { + if (Item.getEquipped(sdk.body.RightArm).tier > 1100 && Item.getEquipped(sdk.body.LeftArm).tier < 1270 && !Check.haveItem("ring", "unique", "Raven Frost")) { includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/VoiceOfReason.js"); } // Crescent Moon - Shael/Um/Tir - if (Item.getEquippedItem(sdk.body.LeftArm).tier < 1100) { + if (Item.getEquipped(sdk.body.LeftArm).tier < 1100) { includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/CrescentMoon.js"); } - if (Item.getEquippedItem(sdk.body.LeftArm).tier < 1200) { + if (Item.getEquipped(sdk.body.LeftArm).tier < 1200) { // Cube to Ko Rune if (!me.getItem(sdk.items.runes.Ko)) { Config.Recipes.push([Recipe.Rune, "Hel Rune"]); @@ -302,76 +302,76 @@ function LoadConfig () { } // Honor - Amn/El/Ith/Tir/Sol - if (Item.getEquippedItem(sdk.body.LeftArm).tier < 1050) { + if (Item.getEquipped(sdk.body.LeftArm).tier < 1050) { includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/Honor.js"); } // Merc Insight - if ((me.ladder || Developer.addLadderRW) && Item.getEquippedItemMerc(sdk.body.RightArm).tier < 3600) { + if ((me.ladder || Developer.addLadderRW) && Item.getMercEquipped(sdk.body.RightArm).tier < 3600) { includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/MercInsight.js"); } // Lore - if (Item.getEquippedItem(sdk.body.Head).tier < 100000) { + if (Item.getEquipped(sdk.body.Head).tier < 100000) { includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/Lore.js"); } // Merc Fortitude - if ((me.ladder || Developer.addLadderRW) && Item.getEquippedItemMerc(sdk.body.Armor).prefixnum !== sdk.locale.items.Fortitude) { + if ((me.ladder || Developer.addLadderRW) && Item.getMercEquipped(sdk.body.Armor).prefixnum !== sdk.locale.items.Fortitude) { includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/MercFortitude.js"); } // Merc Treachery - if (Item.getEquippedItemMerc(sdk.body.Armor).tier < 15000 && Item.getEquippedItem(sdk.body.RightArm).tier > 1100) { + if (Item.getMercEquipped(sdk.body.Armor).tier < 15000 && Item.getEquipped(sdk.body.RightArm).tier > 1100) { includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/MercTreachery.js"); } // Treachery - if (Item.getEquippedItem(sdk.body.Armor).tier < 634 && Item.getEquippedItem(sdk.body.RightArm).tier > 1100) { + if (Item.getEquipped(sdk.body.Armor).tier < 634 && Item.getEquipped(sdk.body.RightArm).tier > 1100) { includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/Treachery.js"); } // Smoke - if (Item.getEquippedItem(sdk.body.Armor).tier < 350) { + if (Item.getEquipped(sdk.body.Armor).tier < 350) { includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/Smoke.js"); } // Duress - if (Item.getEquippedItem(sdk.body.Armor).tier < 600 && (me.checkItem({name: sdk.locale.items.CrescentMoon}).have || Item.getEquippedItem(sdk.body.LeftArm).tier > 900)) { + if (Item.getEquipped(sdk.body.Armor).tier < 600 && (me.checkItem({name: sdk.locale.items.CrescentMoon}).have || Item.getEquipped(sdk.body.LeftArm).tier > 900)) { includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/Duress.js"); } // Myth - if (Item.getEquippedItem(sdk.body.Armor).tier < 340) { + if (Item.getEquipped(sdk.body.Armor).tier < 340) { includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/Myth.js"); } // Kings Grace - Amn/Ral/Thul - if (Item.getEquippedItem(sdk.body.LeftArm).tier < 770) { + if (Item.getEquipped(sdk.body.LeftArm).tier < 770) { includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/KingsGrace.js"); } // Steel - Tir/El - if (Item.getEquippedItem(sdk.body.LeftArm).tier < 500) { + if (Item.getEquipped(sdk.body.LeftArm).tier < 500) { includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/Steel.js"); } // Spirit Sword - if ((me.ladder || Developer.addLadderRW) && Item.getEquippedItem(sdk.body.LeftArmSecondary).prefixnum !== sdk.locale.items.Spirit) { + if ((me.ladder || Developer.addLadderRW) && Item.getEquipped(sdk.body.LeftArmSecondary).prefixnum !== sdk.locale.items.Spirit) { includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/SpiritSword.js"); } // Malice - IthElEth - if (Item.getEquippedItem(sdk.body.LeftArm).tier < 175) { + if (Item.getEquipped(sdk.body.LeftArm).tier < 175) { includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/Malice.js"); } // Stealth - if (Item.getEquippedItem(sdk.body.Armor).tier < 233) { + if (Item.getEquipped(sdk.body.Armor).tier < 233) { includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/Stealth.js"); } - /*if (Item.getEquippedItem(sdk.body.Gloves).tier < 233) { + /*if (Item.getEquipped(sdk.body.Gloves).tier < 233) { NTIP.addLine("[name] == heavygloves && [flag] != ethereal && [quality] == magic # [itemchargedskill] >= 0 # [maxquantity] == 1"); Config.Recipes.push([Recipe.Blood.Gloves, "Heavy Gloves"]); // Craft Blood Gloves }*/ @@ -381,4 +381,6 @@ function LoadConfig () { break; } -} + + return true; +})(); diff --git a/libs/SoloPlay/Config/Druid.js b/libs/SoloPlay/Config/Druid.js index 34071673..950a03a9 100644 --- a/libs/SoloPlay/Config/Druid.js +++ b/libs/SoloPlay/Config/Druid.js @@ -16,7 +16,7 @@ * 4. Save the profile and start */ -function LoadConfig () { +(function LoadConfig () { includeIfNotIncluded("SoloPlay/Functions/MiscOverrides.js"); includeIfNotIncluded("SoloPlay/Functions/Globals.js"); @@ -136,18 +136,19 @@ function LoadConfig () { Config.imbueables = [ {name: sdk.items.SpiritMask, condition: () => (me.normal)}, - {name: sdk.items.TotemicMask, condition: () => (!me.normal && Item.getEquippedItem(sdk.body.Head).tier < 100000 && (me.charlvl < 66 || me.trueStr < 118))}, - {name: sdk.items.DreamSpirit, condition: () => (Item.getEquippedItem(sdk.body.Head).tier < 100000 && me.trueStr >= 118)}, - {name: sdk.items.Belt, condition: () => (me.normal && (Item.getEquippedItem(sdk.body.Head).tier > 100000))}, - {name: sdk.items.MeshBelt, condition: () => (!me.normal && me.charlvl < 46 && me.trueStr > 58 && (Item.getEquippedItem(sdk.body.Head).tier > 100000))}, - {name: sdk.items.SpiderwebSash, condition: () => (!me.normal && me.trueStr > 50 && (Item.getEquippedItem(sdk.body.Head).tier > 100000))}, + {name: sdk.items.TotemicMask, condition: () => (!me.normal && Item.getEquipped(sdk.body.Head).tier < 100000 && (me.charlvl < 66 || me.trueStr < 118))}, + {name: sdk.items.DreamSpirit, condition: () => (Item.getEquipped(sdk.body.Head).tier < 100000 && me.trueStr >= 118)}, + {name: sdk.items.Belt, condition: () => (me.normal && (Item.getEquipped(sdk.body.Head).tier > 100000))}, + {name: sdk.items.MeshBelt, condition: () => (!me.normal && me.charlvl < 46 && me.trueStr > 58 && (Item.getEquipped(sdk.body.Head).tier > 100000))}, + {name: sdk.items.SpiderwebSash, condition: () => (!me.normal && me.trueStr > 50 && (Item.getEquipped(sdk.body.Head).tier > 100000))}, ].filter((item) => item.condition()); let imbueArr = SetUp.imbueItems(); !me.smith && NTIP.arrayLooping(imbueArr); - // basicSocketables located in Globals + const { basicSocketables, addSocketableObj } = require("../Utils/General"); + Config.socketables = Config.socketables.concat(basicSocketables.caster, basicSocketables.all); Config.socketables.push(addSocketableObj(sdk.items.Monarch, [], [], !me.hell, (item) => !Check.haveBase("monarch", 4) && item.ilvl >= 41 && item.isBaseType && !item.ethereal @@ -165,11 +166,11 @@ function LoadConfig () { } /* Crafting */ - if (Item.getEquippedItem(sdk.body.Neck).tier < 100000) { + if (Item.getEquipped(sdk.body.Neck).tier < 100000) { Check.currentBuild().caster ? Config.Recipes.push([Recipe.Caster.Amulet]) : Config.Recipes.push([Recipe.Blood.Amulet]); } - if (Item.getEquippedItem(sdk.body.RingLeft).tier < 100000) { + if (Item.getEquipped(sdk.body.RingLeft).tier < 100000) { Check.currentBuild().caster ? Config.Recipes.push([Recipe.Caster.Ring]) : Config.Recipes.push([Recipe.Blood.Ring]); } @@ -204,7 +205,7 @@ function LoadConfig () { } // upgrade magefist - if (Item.getEquippedItem(sdk.body.Gloves).tier < 110000) { + if (Item.getEquipped(sdk.body.Gloves).tier < 110000) { Config.Recipes.push([Recipe.Unique.Armor.ToExceptional, "Light Gauntlets", Roll.NonEth]); Config.Recipes.push([Recipe.Unique.Armor.ToElite, "Battle Gauntlets", Roll.NonEth, "magefist"]); } @@ -248,49 +249,51 @@ function LoadConfig () { Check.itemSockables(sdk.items.TotemicMask, "unique", "Jalal's Mane"); // Spirit Sword - if ((me.ladder || Developer.addLadderRW) && Item.getEquippedItem(sdk.body.RightArm).tier < 777) { + if ((me.ladder || Developer.addLadderRW) && Item.getEquipped(sdk.body.RightArm).tier < 777) { includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/SpiritSword.js"); } // Spirit Shield - if ((me.ladder || Developer.addLadderRW) && (Item.getEquippedItem(sdk.body.LeftArm).tier < 1000 || Item.getEquippedItem(sdk.body.LeftArmSecondary).prefixnum !== sdk.locale.items.Spirit)) { + if ((me.ladder || Developer.addLadderRW) && (Item.getEquipped(sdk.body.LeftArm).tier < 1000 || Item.getEquipped(sdk.body.LeftArmSecondary).prefixnum !== sdk.locale.items.Spirit)) { includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/SpiritShield.js"); } // Merc Insight - if ((me.ladder || Developer.addLadderRW) && Item.getEquippedItemMerc(sdk.body.RightArm).tier < 3600) { + if ((me.ladder || Developer.addLadderRW) && Item.getMercEquipped(sdk.body.RightArm).tier < 3600) { includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/MercInsight.js"); } // Lore - if (Item.getEquippedItem(sdk.body.Head).tier < 100000) { + if (Item.getEquipped(sdk.body.Head).tier < 100000) { includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/Lore.js"); } // Ancients' Pledge - if (Item.getEquippedItem(sdk.body.LeftArm).tier < 500) { + if (Item.getEquipped(sdk.body.LeftArm).tier < 500) { includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/AncientsPledge.js"); } // Merc Fortitude - if (Item.getEquippedItemMerc(sdk.body.Armor).prefixnum !== sdk.locale.items.Fortitude) { + if (Item.getMercEquipped(sdk.body.Armor).prefixnum !== sdk.locale.items.Fortitude) { includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/MercFortitude.js"); } // Merc Treachery - if (Item.getEquippedItemMerc(sdk.body.Armor).tier < 15000) { + if (Item.getMercEquipped(sdk.body.Armor).tier < 15000) { includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/MercTreachery.js"); } // Smoke - if (Item.getEquippedItem(sdk.body.Armor).tier < 634) { + if (Item.getEquipped(sdk.body.Armor).tier < 634) { includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/Smoke.js"); } // Stealth - if (Item.getEquippedItem(sdk.body.Armor).tier < 233) { + if (Item.getEquipped(sdk.body.Armor).tier < 233) { includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/Stealth.js"); } SoloWants.buildList(); -} + + return true; +})(); diff --git a/libs/SoloPlay/Config/Necromancer.js b/libs/SoloPlay/Config/Necromancer.js index 44bbf700..a054a32f 100644 --- a/libs/SoloPlay/Config/Necromancer.js +++ b/libs/SoloPlay/Config/Necromancer.js @@ -15,7 +15,7 @@ * 4. Save the profile and start */ -function LoadConfig () { +(function LoadConfig () { includeIfNotIncluded("SoloPlay/Functions/MiscOverrides.js"); includeIfNotIncluded("SoloPlay/Functions/Globals.js"); @@ -137,10 +137,10 @@ function LoadConfig () { Config.imbueables = [ {name: sdk.items.DemonHead, condition: () => (me.normal && me.expansion)}, {name: sdk.items.HierophantTrophy, condition: () => (!me.normal && (me.charlvl < 66 || me.trueStr < 106) && me.expansion)}, - {name: sdk.items.BloodlordSkull, condition: () => (Item.getEquippedItem(sdk.body.LeftArm).tier < 1000 && me.expansion)}, - {name: sdk.items.Belt, condition: () => (me.normal && (Item.getEquippedItem(sdk.body.LeftArm).tier > 1000 || me.classic))}, - {name: sdk.items.MeshBelt, condition: () => (!me.normal && me.charlvl < 46 && me.trueStr > 58 && (Item.getEquippedItem(sdk.body.LeftArm).tier > 1000 || me.classic))}, - {name: sdk.items.SpiderwebSash, condition: () => (!me.normal && me.trueStr > 50 && (Item.getEquippedItem(sdk.body.LeftArm).tier > 1000 || me.classic))}, + {name: sdk.items.BloodlordSkull, condition: () => (Item.getEquipped(sdk.body.LeftArm).tier < 1000 && me.expansion)}, + {name: sdk.items.Belt, condition: () => (me.normal && (Item.getEquipped(sdk.body.LeftArm).tier > 1000 || me.classic))}, + {name: sdk.items.MeshBelt, condition: () => (!me.normal && me.charlvl < 46 && me.trueStr > 58 && (Item.getEquipped(sdk.body.LeftArm).tier > 1000 || me.classic))}, + {name: sdk.items.SpiderwebSash, condition: () => (!me.normal && me.trueStr > 50 && (Item.getEquipped(sdk.body.LeftArm).tier > 1000 || me.classic))}, ].filter((item) => item.condition()); let imbueArr = SetUp.imbueItems(); @@ -150,15 +150,15 @@ function LoadConfig () { switch (me.gametype) { case sdk.game.gametype.Classic: // Res shield - if (Item.getEquippedItem(sdk.body.LeftArm).tier < 487) { + if (Item.getEquipped(sdk.body.LeftArm).tier < 487) { includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/PDiamondShield.js"); } break; case sdk.game.gametype.Expansion: NTIP.addLine("[name] >= VexRune && [name] <= ZodRune"); + const { basicSocketables, addSocketableObj } = require("../Utils/General"); - // basicSocketables located in Globals Config.socketables = Config.socketables.concat(basicSocketables.caster, basicSocketables.all); Config.socketables.push(addSocketableObj(sdk.items.Monarch, [], [], !me.hell, (item) => !Check.haveBase("monarch", 4) && item.ilvl >= 41 && item.isBaseType && !item.ethereal @@ -168,12 +168,12 @@ function LoadConfig () { )); /* Crafting */ - if (Item.getEquippedItem(sdk.body.Neck).tier < 100000) { + if (Item.getEquipped(sdk.body.Neck).tier < 100000) { Config.Recipes.push([Recipe.Caster.Amulet]); } // upgrade magefist - if (Item.getEquippedItem(sdk.body.Gloves).tier < 110000) { + if (Item.getEquipped(sdk.body.Gloves).tier < 110000) { Config.Recipes.push([Recipe.Unique.Armor.ToExceptional, "Light Gauntlets", Roll.NonEth]); Config.Recipes.push([Recipe.Unique.Armor.ToElite, "Battle Gauntlets", Roll.NonEth, "magefist"]); } @@ -182,11 +182,11 @@ function LoadConfig () { Check.itemSockables(sdk.items.Shako, "unique", "Harlequin Crest"); /* Crafting */ - if (Item.getEquippedItem(sdk.body.Neck).tier < 100000) { + if (Item.getEquipped(sdk.body.Neck).tier < 100000) { Config.Recipes.push([Recipe.Caster.Amulet]); } - if (Item.getEquippedItem(sdk.body.RingLeft).tier < 100000) { + if (Item.getEquipped(sdk.body.RingLeft).tier < 100000) { Config.Recipes.push([Recipe.Caster.Ring]); } @@ -201,7 +201,7 @@ function LoadConfig () { } // Rhyme - if (Item.getEquippedItem(sdk.body.LeftArm).tier < 650) { + if (Item.getEquipped(sdk.body.LeftArm).tier < 650) { includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/Rhyme.js"); } @@ -211,51 +211,51 @@ function LoadConfig () { } // Spirit Sword - if ((me.ladder || Developer.addLadderRW) && Item.getEquippedItem(sdk.body.RightArm).tier < 777) { + if ((me.ladder || Developer.addLadderRW) && Item.getEquipped(sdk.body.RightArm).tier < 777) { includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/SpiritSword.js"); } // Spirit shield - if ((me.ladder || Developer.addLadderRW) && (Item.getEquippedItem(sdk.body.LeftArm).tier < 1000 || Item.getEquippedItem(sdk.body.LeftArmSecondary).prefixnum !== sdk.locale.items.Spirit)) { + if ((me.ladder || Developer.addLadderRW) && (Item.getEquipped(sdk.body.LeftArm).tier < 1000 || Item.getEquipped(sdk.body.LeftArmSecondary).prefixnum !== sdk.locale.items.Spirit)) { includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/SpiritShield.js"); } // Merc Insight - if ((me.ladder || Developer.addLadderRW) && Item.getEquippedItemMerc(sdk.body.RightArm).tier < 3600) { + if ((me.ladder || Developer.addLadderRW) && Item.getMercEquipped(sdk.body.RightArm).tier < 3600) { includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/MercInsight.js"); } - if (!me.haveSome([{name: sdk.locale.items.Enigma}, {name: sdk.locale.items.Bone}]) && Item.getEquippedItem(sdk.body.Armor).tier < 650) { + if (!me.haveSome([{name: sdk.locale.items.Enigma}, {name: sdk.locale.items.Bone}]) && Item.getEquipped(sdk.body.Armor).tier < 650) { includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/Bone.js"); } // Lore - if (Item.getEquippedItem(sdk.body.Head).tier < 315) { + if (Item.getEquipped(sdk.body.Head).tier < 315) { includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/Lore.js"); } // Ancients' Pledge - if (Item.getEquippedItem(sdk.body.LeftArm).tier < 500) { + if (Item.getEquipped(sdk.body.LeftArm).tier < 500) { includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/AncientsPledge.js"); } // Merc Fortitude - if (Item.getEquippedItemMerc(sdk.body.Armor).prefixnum !== sdk.locale.items.Fortitude) { + if (Item.getMercEquipped(sdk.body.Armor).prefixnum !== sdk.locale.items.Fortitude) { includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/MercFortitude.js"); } // Merc Treachery - if (Item.getEquippedItemMerc(sdk.body.Armor).tier < 15000) { + if (Item.getMercEquipped(sdk.body.Armor).tier < 15000) { includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/MercTreachery.js"); } // Smoke - if (Item.getEquippedItem(sdk.body.Armor).tier < 450) { + if (Item.getEquipped(sdk.body.Armor).tier < 450) { includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/Smoke.js"); } // Stealth - if (Item.getEquippedItem(sdk.body.Armor).tier < 233) { + if (Item.getEquipped(sdk.body.Armor).tier < 233) { includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/Stealth.js"); } @@ -263,4 +263,6 @@ function LoadConfig () { break; } -} + + return true; +})(); diff --git a/libs/SoloPlay/Config/Paladin.js b/libs/SoloPlay/Config/Paladin.js index 0dcc86cc..f679d386 100644 --- a/libs/SoloPlay/Config/Paladin.js +++ b/libs/SoloPlay/Config/Paladin.js @@ -22,9 +22,9 @@ // todo: clean-up how cubing to runes is handled // move shared config settings into its own function call -// make and initialize me.equippedItems object so don't have to do repeated Item.getEquippedItem(whatever) call +// make and initialize me.equippedItems object so don't have to do repeated Item.getEquipped(whatever) call -function LoadConfig () { +(function LoadConfig () { includeIfNotIncluded("SoloPlay/Functions/MiscOverrides.js"); includeIfNotIncluded("SoloPlay/Functions/Globals.js"); @@ -138,10 +138,10 @@ function LoadConfig () { Config.imbueables = [ {name: sdk.items.WarScepter, condition: () => me.normal}, {name: sdk.items.DivineScepter, condition: () => (!me.normal && (me.trueStr < 125 || me.trueDex < 60))}, - {name: sdk.items.MightyScepter, condition: () => (Item.getEquippedItem(sdk.body.RightArm).tier < 777 && (me.trueStr >= 125 || me.trueDex >= 60))}, - {name: sdk.items.Belt, condition: () => (me.normal && (Item.getEquippedItem(sdk.body.RightArm).tier > 777 || me.classic))}, - {name: sdk.items.MeshBelt, condition: () => (!me.normal && me.charlvl < 46 && me.trueStr > 58 && (Item.getEquippedItem(sdk.body.RightArm).tier > 777 || me.classic))}, - {name: sdk.items.SpiderwebSash, condition: () => (!me.normal && me.trueStr > 50 && (Item.getEquippedItem(sdk.body.RightArm).tier > 777 || me.classic))}, + {name: sdk.items.MightyScepter, condition: () => (Item.getEquipped(sdk.body.RightArm).tier < 777 && (me.trueStr >= 125 || me.trueDex >= 60))}, + {name: sdk.items.Belt, condition: () => (me.normal && (Item.getEquipped(sdk.body.RightArm).tier > 777 || me.classic))}, + {name: sdk.items.MeshBelt, condition: () => (!me.normal && me.charlvl < 46 && me.trueStr > 58 && (Item.getEquipped(sdk.body.RightArm).tier > 777 || me.classic))}, + {name: sdk.items.SpiderwebSash, condition: () => (!me.normal && me.trueStr > 50 && (Item.getEquipped(sdk.body.RightArm).tier > 777 || me.classic))}, ]; let imbueArr = SetUp.imbueItems(); @@ -151,30 +151,30 @@ function LoadConfig () { switch (me.gametype) { case sdk.game.gametype.Classic: // Res shield - if (Item.getEquippedItem(sdk.body.LeftArm).tier < 487) { + if (Item.getEquipped(sdk.body.LeftArm).tier < 487) { includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/PDiamondShield.js"); } break; case sdk.game.gametype.Expansion: NTIP.addLine("[name] >= VexRune && [name] <= ZodRune"); + const { basicSocketables, addSocketableObj } = require("../Utils/General"); - // basicSocketables located in Globals Config.socketables = Config.socketables.concat(basicSocketables.caster, basicSocketables.all); Config.socketables.push(addSocketableObj(sdk.items.Shako, [sdk.items.runes.Um], [sdk.items.gems.Perfect.Ruby], true, (item) => item.unique && !item.ethereal )); /* Crafting */ - if (Item.getEquippedItem(sdk.body.Neck).tier < 100000) { + if (Item.getEquipped(sdk.body.Neck).tier < 100000) { Check.currentBuild().caster ? Config.Recipes.push([Recipe.Caster.Amulet]) : Config.Recipes.push([Recipe.Blood.Amulet]); } - if (Item.getEquippedItem(sdk.body.RingLeft).tier < 100000) { + if (Item.getEquipped(sdk.body.RingLeft).tier < 100000) { Check.currentBuild().caster ? Config.Recipes.push([Recipe.Caster.Ring]) : Config.Recipes.push([Recipe.Blood.Ring]); } - if (Item.getEquippedItem(sdk.body.Gloves).tier < 110000) { + if (Item.getEquipped(sdk.body.Gloves).tier < 110000) { Config.Recipes.push([Recipe.Unique.Armor.ToExceptional, "Light Gauntlets", Roll.NonEth]); } @@ -229,7 +229,7 @@ function LoadConfig () { } // Spirit Shield - if ((me.ladder || Developer.addLadderRW) && Item.getEquippedItem(sdk.body.LeftArm).tier < 110000) { + if ((me.ladder || Developer.addLadderRW) && Item.getEquipped(sdk.body.LeftArm).tier < 110000) { includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/SpiritShield.js"); } @@ -267,7 +267,7 @@ function LoadConfig () { if (!me.checkItem({name: sdk.locale.items.CalltoArms}).have) { // Cube to Mal rune - if (!me.getItem(sdk.items.runes.Mal) && Item.getEquippedItem(sdk.body.RightArm).tier >= 110000) { + if (!me.getItem(sdk.items.runes.Mal) && Item.getEquipped(sdk.body.RightArm).tier >= 110000) { Config.Recipes.push([Recipe.Rune, "Um Rune"]); } @@ -301,7 +301,7 @@ function LoadConfig () { includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/VoiceOfReason.js"); } - if ((me.ladder || Developer.addLadderRW) && Item.getEquippedItemMerc(sdk.body.Armor).prefixnum !== sdk.locale.items.Fortitude + if ((me.ladder || Developer.addLadderRW) && Item.getMercEquipped(sdk.body.Armor).prefixnum !== sdk.locale.items.Fortitude && me.haveAll([{name: sdk.locale.items.HandofJustice}, {name: sdk.locale.items.Dragon, itemtype: sdk.items.type.Armor}, {name: sdk.locale.items.Dream, itemtype: sdk.items.type.AuricShields}, {name: sdk.locale.items.Dream, itemtype: sdk.items.type.Helm}])) { includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/MercFortitude.js"); @@ -340,7 +340,7 @@ function LoadConfig () { if (!me.checkItem({name: sdk.locale.items.CalltoArms}).have) { // Cube to Mal rune - if (!me.getItem(sdk.items.runes.Mal) && Item.getEquippedItem(sdk.body.RightArm).tier >= 110000) { + if (!me.getItem(sdk.items.runes.Mal) && Item.getEquipped(sdk.body.RightArm).tier >= 110000) { Config.Recipes.push([Recipe.Rune, "Um Rune"]); } @@ -374,13 +374,13 @@ function LoadConfig () { if ((me.ladder || Developer.addLadderRW) && me.haveAll([{name: sdk.locale.items.LastWish}, {name: sdk.locale.items.ChainsofHonor}, {name: sdk.locale.items.Dream, itemtype: sdk.items.type.AuricShields}, {name: sdk.locale.items.Dream, itemtype: sdk.items.type.Helm}])) { // Infinity - if ((me.ladder || Developer.addLadderRW) && Item.getEquippedItemMerc(sdk.body.RightArm).prefixnum !== sdk.locale.items.Infinity) { + if ((me.ladder || Developer.addLadderRW) && Item.getMercEquipped(sdk.body.RightArm).prefixnum !== sdk.locale.items.Infinity) { if (!isIncluded("SoloPlay/BuildFiles/Runewords/MercInfinity.js")) { include("SoloPlay/BuildFiles/Runewords/MercInfinity.js"); } } // Merc Fortitude - if (Item.getEquippedItemMerc(sdk.body.Armor).prefixnum !== sdk.locale.items.Fortitude) { + if (Item.getMercEquipped(sdk.body.Armor).prefixnum !== sdk.locale.items.Fortitude) { if (!isIncluded("SoloPlay/BuildFiles/Runewords/MercFortitude.js")) { include("SoloPlay/BuildFiles/Runewords/MercFortitude.js"); } @@ -437,7 +437,7 @@ function LoadConfig () { if (!me.checkItem({name: sdk.locale.items.CalltoArms}).have) { // Cube to Mal rune - if (!me.getItem(sdk.items.runes.Mal) && Item.getEquippedItem(sdk.body.RightArm).tier >= 110000) { + if (!me.getItem(sdk.items.runes.Mal) && Item.getEquipped(sdk.body.RightArm).tier >= 110000) { Config.Recipes.push([Recipe.Rune, "Um Rune"]); } @@ -459,7 +459,7 @@ function LoadConfig () { includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/VoiceOfReason.js"); } - if ((me.ladder || Developer.addLadderRW) && Item.getEquippedItemMerc(sdk.body.Armor).prefixnum !== sdk.locale.items.Fortitude + if ((me.ladder || Developer.addLadderRW) && Item.getMercEquipped(sdk.body.Armor).prefixnum !== sdk.locale.items.Fortitude && me.haveAll([{name: sdk.locale.items.HandofJustice}, {name: sdk.locale.items.Dragon, itemtype: sdk.items.type.Armor}, {name: sdk.locale.items.Exile, itemtype: sdk.items.type.AuricShields}])) { includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/MercFortitude.js"); } @@ -477,52 +477,52 @@ function LoadConfig () { } // Enigma - Don't make if not Smiter or Hammerdin - if (!me.checkItem({name: sdk.locale.items.Enigma}).have && ["Hammerdin", "Smiter"].indexOf(SetUp.finalBuild) > -1) { + if (!me.checkItem({name: sdk.locale.items.Enigma}).have && ["Hammerdin", "Smiter"].includes(SetUp.finalBuild)) { includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/Enigma.js"); } // Spirit Sword - if ((me.ladder || Developer.addLadderRW) && Item.getEquippedItem(sdk.body.RightArm).tier < 777) { + if ((me.ladder || Developer.addLadderRW) && Item.getEquipped(sdk.body.RightArm).tier < 777) { includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/SpiritSword.js"); } // Spirit Shield - if ((me.ladder || Developer.addLadderRW) && (Item.getEquippedItem(sdk.body.LeftArm).tier < 1000 || Item.getEquippedItem(sdk.body.LeftArmSecondary).prefixnum !== sdk.locale.items.Spirit)) { + if ((me.ladder || Developer.addLadderRW) && (Item.getEquipped(sdk.body.LeftArm).tier < 1000 || Item.getEquipped(sdk.body.LeftArmSecondary).prefixnum !== sdk.locale.items.Spirit)) { includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/SpiritShield.js"); } // Merc Insight - if ((me.ladder || Developer.addLadderRW) && Item.getEquippedItemMerc(sdk.body.RightArm).tier < 3600) { + if ((me.ladder || Developer.addLadderRW) && Item.getMercEquipped(sdk.body.RightArm).tier < 3600) { includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/MercInsight.js"); } // Lore - if (Item.getEquippedItem(sdk.body.Head).tier < 315) { + if (Item.getEquipped(sdk.body.Head).tier < 315) { includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/Lore.js"); } // Ancients' Pledge - if (Item.getEquippedItem(sdk.body.LeftArm).tier < 500) { + if (Item.getEquipped(sdk.body.LeftArm).tier < 500) { includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/AncientsPledge.js"); } // Merc Fortitude - if (Item.getEquippedItemMerc(sdk.body.Armor).prefixnum !== sdk.locale.items.Fortitude && ["Hammerdin", "Smiter"].indexOf(SetUp.finalBuild) > -1) { + if (Item.getMercEquipped(sdk.body.Armor).prefixnum !== sdk.locale.items.Fortitude && ["Hammerdin", "Smiter"].indexOf(SetUp.finalBuild) > -1) { includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/MercFortitude.js"); } // Merc Treachery - if (Item.getEquippedItemMerc(sdk.body.Armor).tier < 15000) { + if (Item.getMercEquipped(sdk.body.Armor).tier < 15000) { includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/MercTreachery.js"); } // Smoke - if (Item.getEquippedItem(sdk.body.Armor).tier < 450) { + if (Item.getEquipped(sdk.body.Armor).tier < 450) { includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/Smoke.js"); } // Stealth - if (Item.getEquippedItem(sdk.body.Armor).tier < 233) { + if (Item.getEquipped(sdk.body.Armor).tier < 233) { includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/Stealth.js"); } @@ -530,4 +530,6 @@ function LoadConfig () { break; } -} + + return true; +})(); diff --git a/libs/SoloPlay/Config/Sorceress.js b/libs/SoloPlay/Config/Sorceress.js index 4096f55b..2ea3e064 100644 --- a/libs/SoloPlay/Config/Sorceress.js +++ b/libs/SoloPlay/Config/Sorceress.js @@ -17,14 +17,13 @@ * 4. Save the profile and start */ -function LoadConfig () { +(function LoadConfig () { includeIfNotIncluded("SoloPlay/Functions/MiscOverrides.js"); includeIfNotIncluded("SoloPlay/Functions/Globals.js"); SetUp.include(); /* Script */ - Scripts.SoloPlay = true; SetUp.config(); /* Chicken configuration. */ @@ -143,10 +142,10 @@ function LoadConfig () { Config.imbueables = [ {name: sdk.items.JaredsStone, condition: () => (me.normal && me.expansion)}, {name: sdk.items.SwirlingCrystal, condition: () => (!me.normal && me.charlvl < 66 && me.expansion)}, - {name: sdk.items.DimensionalShard, condition: () => (Item.getEquippedItem(sdk.body.RightArm).tier < 777 && me.expansion)}, - {name: sdk.items.Belt, condition: () => (me.normal && (Item.getEquippedItem(sdk.body.RightArm).tier > 777 || me.classic))}, - {name: sdk.items.MeshBelt, condition: () => (!me.normal && me.charlvl < 46 && me.trueStr > 58 && (Item.getEquippedItem(sdk.body.RightArm).tier > 777 || me.classic))}, - {name: sdk.items.SpiderwebSash, condition: () => (!me.normal && me.trueStr > 50 && (Item.getEquippedItem(sdk.body.RightArm).tier > 777 || me.classic))}, + {name: sdk.items.DimensionalShard, condition: () => (Item.getEquipped(sdk.body.RightArm).tier < 777 && me.expansion)}, + {name: sdk.items.Belt, condition: () => (me.normal && (Item.getEquipped(sdk.body.RightArm).tier > 777 || me.classic))}, + {name: sdk.items.MeshBelt, condition: () => (!me.normal && me.charlvl < 46 && me.trueStr > 58 && (Item.getEquipped(sdk.body.RightArm).tier > 777 || me.classic))}, + {name: sdk.items.SpiderwebSash, condition: () => (!me.normal && me.trueStr > 50 && (Item.getEquipped(sdk.body.RightArm).tier > 777 || me.classic))}, ].filter((item) => item.condition()); let imbueArr = SetUp.imbueItems(); @@ -156,25 +155,25 @@ function LoadConfig () { switch (me.gametype) { case sdk.game.gametype.Classic: // Res shield - if ((Item.getEquippedItem(sdk.body.LeftArm).tier < 487 && !Item.getEquippedItem(sdk.body.RightArm).twoHanded) || (Item.getEquippedItem(sdk.body.RightArm).tier < 487 && Item.getEquippedItem(sdk.body.RightArm).twoHanded)) { + if ((Item.getEquipped(sdk.body.LeftArm).tier < 487 && !Item.getEquipped(sdk.body.RightArm).twoHanded) || (Item.getEquipped(sdk.body.RightArm).tier < 487 && Item.getEquipped(sdk.body.RightArm).twoHanded)) { includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/PDiamondShield.js"); } break; case sdk.game.gametype.Expansion: NTIP.addLine("[name] >= VexRune && [name] <= ZodRune"); + const { basicSocketables, addSocketableObj } = require("../Utils/General"); /* Crafting */ - if (Item.getEquippedItem(sdk.body.Neck).tier < 100000) { + if (Item.getEquipped(sdk.body.Neck).tier < 100000) { Config.Recipes.push([Recipe.Caster.Amulet]); } - if (Item.getEquippedItem(sdk.body.Gloves).tier < 110000) { + if (Item.getEquipped(sdk.body.Gloves).tier < 110000) { Config.Recipes.push([Recipe.Unique.Armor.ToExceptional, "Light Gauntlets", Roll.NonEth]); ["Blova", "Lightning"].includes(SetUp.finalBuild) && Config.Recipes.push([Recipe.Unique.Armor.ToElite, "Battle Gauntlets", Roll.NonEth, "magefist"]); } - // basicSocketables located in Globals Config.socketables = Config.socketables.concat(basicSocketables.caster, basicSocketables.all); // FinalBuild specific setup @@ -182,12 +181,12 @@ function LoadConfig () { case "Blova": case "Lightning": // Infinity - if ((me.ladder || Developer.addLadderRW) && Item.getEquippedItemMerc(sdk.body.RightArm).prefixnum !== sdk.locale.items.Infinity) { + if ((me.ladder || Developer.addLadderRW) && Item.getMercEquipped(sdk.body.RightArm).prefixnum !== sdk.locale.items.Infinity) { includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/MercInfinity.js"); } // Spirit Shield - if ((me.ladder || Developer.addLadderRW) && SetUp.currentBuild === SetUp.finalBuild && (Item.getEquippedItem(sdk.body.LeftArm).tier < 1000 || Item.getEquippedItem(sdk.body.LeftArmSecondary).prefixnum !== sdk.locale.items.Spirit)) { + if ((me.ladder || Developer.addLadderRW) && SetUp.currentBuild === SetUp.finalBuild && (Item.getEquipped(sdk.body.LeftArm).tier < 1000 || Item.getEquipped(sdk.body.LeftArmSecondary).prefixnum !== sdk.locale.items.Spirit)) { includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/SpiritShield.js"); } @@ -236,7 +235,7 @@ function LoadConfig () { if (!Check.haveItem("shield", "unique", "Moser's Blessed Circle") && !me.haveSome([{name: sdk.locale.items.Sanctuary}, {name: sdk.locale.items.Spirit, itemtype: sdk.items.type.Shield}])) { NTIP.addLine("[name] == perfectdiamond # # [maxquantity] == 2"); - if (Item.getQuantityOwned(me.getItem(sdk.items.gems.Perfect.Diamond) < 2)) { + if (Item.getQuantityOwned(me.getItem(sdk.items.gems.Perfect.Diamond)) < 2) { Config.Recipes.push([Recipe.Gem, "flawlessdiamond"]); } } @@ -254,47 +253,47 @@ function LoadConfig () { } // Spirit Sword - if ((me.ladder || Developer.addLadderRW) && Item.getEquippedItem(sdk.body.RightArm).tier < 777) { + if ((me.ladder || Developer.addLadderRW) && Item.getEquipped(sdk.body.RightArm).tier < 777) { includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/SpiritSword.js"); } // Merc Insight - if ((me.ladder || Developer.addLadderRW) && Item.getEquippedItemMerc(sdk.body.RightArm).tier < 3600) { + if ((me.ladder || Developer.addLadderRW) && Item.getMercEquipped(sdk.body.RightArm).tier < 3600) { includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/MercInsight.js"); } // Lore - if (Item.getEquippedItem(sdk.body.Head).tier < 315) { + if (Item.getEquipped(sdk.body.Head).tier < 315) { includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/Lore.js"); } // Ancients' Pledge - if (Item.getEquippedItem(sdk.body.LeftArm).tier < 500) { + if (Item.getEquipped(sdk.body.LeftArm).tier < 500) { includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/AncientsPledge.js"); } // Merc Fortitude - if (Item.getEquippedItemMerc(sdk.body.Armor).prefixnum !== sdk.locale.items.Fortitude) { + if (Item.getMercEquipped(sdk.body.Armor).prefixnum !== sdk.locale.items.Fortitude) { includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/MercFortitude.js"); } // Bone - if (Item.getEquippedItem(sdk.body.Armor).tier < 450) { + if (Item.getEquipped(sdk.body.Armor).tier < 450) { includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/Bone.js"); } // Merc Treachery - if (Item.getEquippedItemMerc(sdk.body.Armor).tier < 15000) { + if (Item.getMercEquipped(sdk.body.Armor).tier < 15000) { includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/MercTreachery.js"); } // Smoke - if (Item.getEquippedItem(sdk.body.Armor).tier < 300) { + if (Item.getEquipped(sdk.body.Armor).tier < 300) { includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/Smoke.js"); } // Stealth - if (Item.getEquippedItem(sdk.body.Armor).tier < 233) { + if (Item.getEquipped(sdk.body.Armor).tier < 233) { includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/Stealth.js"); } @@ -302,4 +301,6 @@ function LoadConfig () { break; } -} + + return true; +})(); diff --git a/libs/SoloPlay/Functions/ClassAttackOverrides/AmazonAttacks-WIP.js b/libs/SoloPlay/Functions/ClassAttackOverrides/AmazonAttacks-WIP.js index 31c7978e..9572be23 100644 --- a/libs/SoloPlay/Functions/ClassAttackOverrides/AmazonAttacks-WIP.js +++ b/libs/SoloPlay/Functions/ClassAttackOverrides/AmazonAttacks-WIP.js @@ -138,7 +138,7 @@ ClassAttack.doAttack = function (unit) { range: Skill.getRange(sdk.skills.Jab), mana: Skill.getManaCost(sdk.skills.Jab), use: function () { - return (this.level > 0 && Item.getEquippedItem(sdk.body.RightArm).tier >= 1000); + return (this.level > 0 && Item.getEquipped(sdk.body.RightArm).tier >= 1000); } }, }; diff --git a/libs/SoloPlay/Functions/ClassAttackOverrides/AmazonAttacks.js b/libs/SoloPlay/Functions/ClassAttackOverrides/AmazonAttacks.js index 22837a49..31be2cf4 100644 --- a/libs/SoloPlay/Functions/ClassAttackOverrides/AmazonAttacks.js +++ b/libs/SoloPlay/Functions/ClassAttackOverrides/AmazonAttacks.js @@ -42,7 +42,7 @@ ClassAttack.doAttack = function (unit, preattack, once) { let useSlowMissiles = Skill.canUse(sdk.skills.SlowMissiles); let useDecoy = (Skill.canUse(sdk.skills.Dopplezon) && !me.normal); let usePlague = (!me.normal && Skill.canUse(sdk.skills.PlagueJavelin)); - let useJab = (Item.getEquippedItem(sdk.body.RightArm).tier >= 1000 && Skill.canUse(sdk.skills.Jab)); + let useJab = (Item.getEquipped(sdk.body.RightArm).tier >= 1000 && Skill.canUse(sdk.skills.Jab)); let useLightFury = me.getSkill(sdk.skills.LightningFury, sdk.skills.subindex.SoftPoints) >= 10; let forcePlague = (me.getSkill(sdk.skills.PlagueJavelin, sdk.skills.subindex.SoftPoints) >= 15); //Extra poison damage then attack diff --git a/libs/SoloPlay/Functions/ConfigOverrides.js b/libs/SoloPlay/Functions/ConfigOverrides.js index 37f8a8c4..777d24e7 100644 --- a/libs/SoloPlay/Functions/ConfigOverrides.js +++ b/libs/SoloPlay/Functions/ConfigOverrides.js @@ -42,16 +42,6 @@ Config.init = function (notify) { throw new Error("Failed to load character config."); } - try { - LoadConfig.call(); - } catch (e2) { - if (notify) { - console.log("ÿc8Error in " + e2.fileName.substring(e2.fileName.lastIndexOf("\\") + 1, e2.fileName.length) + "(line " + e2.lineNumber + "): " + e2.message); - - throw new Error("Config.init: Error in character config."); - } - } - if (Config.Silence && !Config.LocalChat.Enabled) { // Override the say function with print, so it just gets printed to console global._say = global.say; diff --git a/libs/SoloPlay/Functions/Globals.js b/libs/SoloPlay/Functions/Globals.js index 07ebf8d9..84f2685d 100644 --- a/libs/SoloPlay/Functions/Globals.js +++ b/libs/SoloPlay/Functions/Globals.js @@ -127,7 +127,7 @@ const SetUp = { } // merc check - if (!!me.getMercEx()) { + if (me.getMercEx()) { // TODO: figure out how to ensure we are already using the right merc to prevent re-hiring // can't do an aura check as merc auras are bugged, only useful info from getUnit is the classid let merc = me.getMercEx(); @@ -686,14 +686,14 @@ const Check = { let gold = me.gold; // Almost broken but not quite - if (((Item.getEquippedItem(sdk.body.RightArm).durability <= 30 && Item.getEquippedItem(sdk.body.RightArm).durability > 0) - || (Item.getEquippedItem(sdk.body.LeftArm).durability <= 30 && Item.getEquippedItem(sdk.body.LeftArm).durability > 0) + if (((Item.getEquipped(sdk.body.RightArm).durability <= 30 && Item.getEquipped(sdk.body.RightArm).durability > 0) + || (Item.getEquipped(sdk.body.LeftArm).durability <= 30 && Item.getEquipped(sdk.body.LeftArm).durability > 0) && !me.getMerc() && me.charlvl >= 15 && !me.normal && !me.nightmare && gold < 1000)) { return 1; } // Broken - if ((Item.getEquippedItem(sdk.body.RightArm).durability === 0 || Item.getEquippedItem(sdk.body.LeftArm).durability === 0) && me.charlvl >= 15 && !me.normal && gold < 1000) { + if ((Item.getEquipped(sdk.body.RightArm).durability === 0 || Item.getEquipped(sdk.body.LeftArm).durability === 0) && me.charlvl >= 15 && !me.normal && gold < 1000) { return 2; } @@ -801,7 +801,7 @@ const Check = { || (!me.paladin && me.checkItem({name: sdk.locale.items.Spirit, itemtype: sdk.items.type.Sword}).have) || (me.paladin && me.haveAll([{name: sdk.locale.items.Spirit, itemtype: sdk.items.type.Sword}, {name: sdk.locale.items.Spirit, itemtype: sdk.items.type.AuricShields}])) || (me.necromancer && me.checkItem({name: sdk.locale.items.White}).have - && (me.checkItem({name: sdk.locale.items.Rhyme, itemtype: sdk.items.type.VoodooHeads}).have || Item.getEquippedItem(sdk.body.LeftArm).tier > 800)) + && (me.checkItem({name: sdk.locale.items.Rhyme, itemtype: sdk.items.type.VoodooHeads}).have || Item.getEquipped(sdk.body.LeftArm).tier > 800)) || (me.barbarian && (me.checkItem({name: sdk.locale.items.Lawbringer}).have || me.baal))) { needRunes = false; } @@ -1134,7 +1134,7 @@ const Check = { usePreviousSocketQuest: function () { if (me.classic) return; if (!Check.resistance().Status) { - if (me.weaponswitch === 0 && Item.getEquippedItem(sdk.body.LeftArm).fname.includes("Lidless Wall") && !Item.getEquippedItem(sdk.body.LeftArm).socketed) { + if (me.weaponswitch === 0 && Item.getEquipped(sdk.body.LeftArm).fname.includes("Lidless Wall") && !Item.getEquipped(sdk.body.LeftArm).socketed) { if (!me.normal) { if (!myData.normal.socketUsed) goToDifficulty(sdk.difficulty.Normal, " to use socket quest"); if (me.hell && !myData.nightmare.socketUsed) goToDifficulty(sdk.difficulty.Nightmare, " to use socket quest"); @@ -1209,7 +1209,7 @@ const SoloWants = { let merc = me.getMerc(); switch (true) { case Item.autoEquipCheck(item, true) && me.trueStr >= item.strreq && me.trueDex >= item.dexreq: - case Item.autoEquipKeepCheckMerc(item) && !!merc && merc.rawStrength >= item.strreq && merc.rawDexterity >= item.dexreq: + case Item.autoEquipCheckMerc(item, true) && !!merc && merc.rawStrength >= item.strreq && merc.rawDexterity >= item.dexreq: curr.socketWith.splice(curr.socketWith.indexOf(sdk.items.runes.Hel), 1); break; } diff --git a/libs/SoloPlay/Functions/ItemOverrides.js b/libs/SoloPlay/Functions/ItemOverrides.js index abb0bdc2..e7c7d8d1 100644 --- a/libs/SoloPlay/Functions/ItemOverrides.js +++ b/libs/SoloPlay/Functions/ItemOverrides.js @@ -108,7 +108,7 @@ Item.getBodyLoc = function (item) { }; // todo: clean this up -Item.getEquippedItem = function (bodyLoc = -1) { +Item.getEquipped = function (bodyLoc = -1) { let item = me.getItemsEx().filter((item) => item.isEquipped && item.bodylocation === bodyLoc).first(); if (item) { @@ -174,7 +174,7 @@ Item.autoEquipCheck = function (item, basicCheck = false) { if (tier > 0 && bodyLoc) { for (let i = 0; i < bodyLoc.length; i += 1) { - let equippedItem = this.getEquippedItem(bodyLoc[i]); + let equippedItem = Item.getEquipped(bodyLoc[i]); // rings are special // first check if its a final item - can't use tierscore value as it doesn't count the bloated value @@ -188,11 +188,11 @@ Item.autoEquipCheck = function (item, basicCheck = false) { } } else if (tier > equippedItem.tier && (!basicCheck ? this.canEquip(item) || !item.identified : true)) { if (item.twoHanded && !me.barbarian) { - if (tier < this.getEquippedItem(sdk.body.RightArm).tier + this.getEquippedItem(sdk.body.LeftArm).tier) return false; + if (tier < Item.getEquipped(sdk.body.RightArm).tier + Item.getEquipped(sdk.body.LeftArm).tier) return false; } - if (!me.barbarian && bodyLoc[i] === sdk.body.LeftArm && this.getEquippedItem(bodyLoc[i]).tier === -1) { - if (this.getEquippedItem(sdk.body.RightArm).twoHanded && tier < this.getEquippedItem(sdk.body.RightArm).tier) return false; + if (!me.barbarian && bodyLoc[i] === sdk.body.LeftArm && Item.getEquipped(bodyLoc[i]).tier === -1) { + if (Item.getEquipped(sdk.body.RightArm).twoHanded && tier < Item.getEquipped(sdk.body.RightArm).tier) return false; } return true; @@ -270,7 +270,7 @@ Item.autoEquip = function (task = "") { } // ring check - sometimes a higher tier ring ends up on the wrong finger causing a rollback loop - if (this.getEquippedItem(sdk.body.RingLeft).tierScore > this.getEquippedItem(sdk.body.RingRight).tierScore) { + if (Item.getEquipped(sdk.body.RingLeft).tierScore > Item.getEquipped(sdk.body.RingRight).tierScore) { console.log("ÿc9" + task + "ÿc0 :: Swapping rings, higher tier ring is on the wrong finger"); clickItemAndWait(sdk.clicktypes.click.item.Left, sdk.body.RingLeft); delay(200); @@ -289,7 +289,7 @@ Item.autoEquip = function (task = "") { if (tier > 0 && bodyLoc) { for (let j = 0; j < bodyLoc.length; j += 1) { - const equippedItem = this.getEquippedItem(bodyLoc[j]); + const equippedItem = Item.getEquipped(bodyLoc[j]); // rings are special if (item.isInStorage && item.itemType === sdk.items.type.Ring && (tier < NTIP.MAX_TIER || (equippedItem.finalItem && tier >= NTIP.MAX_TIER))) { Item.identify(item); @@ -306,14 +306,14 @@ Item.autoEquip = function (task = "") { Item.identify(item); if (item.twoHanded && !me.barbarian) { - if (tier < this.getEquippedItem(sdk.body.RightArm).tier + this.getEquippedItem(sdk.body.LeftArm).tier) { + if (tier < Item.getEquipped(sdk.body.RightArm).tier + Item.getEquipped(sdk.body.LeftArm).tier) { continue; } console.log("ÿc9" + task + "ÿc0 :: TwoHandedWep better than sum tier of currently equipped main + shield hand : " + item.fname + " Tier: " + tier); } - if (!me.barbarian && bodyLoc[j] === sdk.body.LeftArm && equippedItem.tier === -1 && this.getEquippedItem(sdk.body.RightArm).twoHanded) { - if (tier < this.getEquippedItem(sdk.body.RightArm).tier) { + if (!me.barbarian && bodyLoc[j] === sdk.body.LeftArm && equippedItem.tier === -1 && Item.getEquipped(sdk.body.RightArm).twoHanded) { + if (tier < Item.getEquipped(sdk.body.RightArm).tier) { continue; } console.log("ÿc9" + task + "ÿc0 :: TwoHandedWep not as good as what we want to equip on our shield hand : " + item.fname + " Tier: " + tier); @@ -362,14 +362,14 @@ Item.equip = function (item, bodyLoc) { if (cursorItem) { // rollback check - let justEquipped = this.getEquippedItem(bodyLoc); + let justEquipped = Item.getEquipped(bodyLoc); let checkScore = 0; switch (cursorItem.itemType) { case sdk.items.type.Ring: checkScore = tierscore(cursorItem, bodyLoc); if (checkScore > justEquipped.tierScore && !justEquipped.finalItem) { console.debug("ROLLING BACK TO OLD ITEM BECAUSE IT WAS BETTER"); - console.debug("OldItem: " + checkScore + " Just Equipped Item: " + this.getEquippedItem(bodyLoc).tierScore); + console.debug("OldItem: " + checkScore + " Just Equipped Item: " + Item.getEquipped(bodyLoc).tierScore); clickItemAndWait(sdk.clicktypes.click.item.Left, bodyLoc); cursorItem = Game.getCursorUnit(); rolledBack = true; @@ -380,7 +380,7 @@ Item.equip = function (item, bodyLoc) { checkScore = NTIP.GetTier(cursorItem); if (checkScore > justEquipped.tier && !item.questItem && !justEquipped.isRuneword/*Wierd bug with runewords that it'll fail to get correct item desc so don't attempt rollback*/) { console.debug("ROLLING BACK TO OLD ITEM BECAUSE IT WAS BETTER"); - console.debug("OldItem: " + checkScore + " Just Equipped Item: " + this.getEquippedItem(bodyLoc).tier); + console.debug("OldItem: " + checkScore + " Just Equipped Item: " + Item.getEquipped(bodyLoc).tier); clickItemAndWait(sdk.clicktypes.click.item.Left, bodyLoc); cursorItem = Game.getCursorUnit(); rolledBack = true; @@ -439,9 +439,15 @@ Item.removeItem = function (bodyLoc = -1, item = undefined) { return false; }; +/** + * @param {ItemUnit} item + */ Item.hasSecondaryTier = (item) => Config.AutoEquip && me.expansion && NTIP.GetSecondaryTier(item) > 0; -Item.getBodyLocSecondary = function (item) { +/** + * @param {ItemUnit} item + */ +Item.getSecondaryBodyLoc = function (item) { let bodyLoc = (() => { switch (true) { case Item.shieldTypes.includes(item.itemType): @@ -460,6 +466,10 @@ Item.getBodyLocSecondary = function (item) { return Array.isArray(bodyLoc) ? bodyLoc : [bodyLoc]; }; +/** + * @param {ItemUnit} item + * @param {11 | 12} bodyLoc + */ Item.secondaryEquip = function (item, bodyLoc) { if (!this.canEquip(item) && me.expansion) return false; // Already equipped in the right slot @@ -499,15 +509,18 @@ Item.secondaryEquip = function (item, bodyLoc) { return equipped; }; +/** + * @param {ItemUnit} item + */ Item.autoEquipCheckSecondary = function (item) { if (!Config.AutoEquip) return true; if (me.classic) return false; let tier = NTIP.GetSecondaryTier(item); - let bodyLoc = Item.getBodyLocSecondary(item); + let bodyLoc = Item.getSecondaryBodyLoc(item); for (let i = 0; tier > 0 && i < bodyLoc.length; i += 1) { - if (tier > Item.getEquippedItem(bodyLoc[i]).secondarytier && (Item.canEquip(item) || !item.identified)) { + if (tier > Item.getEquipped(bodyLoc[i]).secondarytier && (Item.canEquip(item) || !item.identified)) { return true; } } @@ -515,6 +528,9 @@ Item.autoEquipCheckSecondary = function (item) { return false; }; +/** + * @param {string} task + */ Item.autoEquipSecondary = function (task = "") { if (!Config.AutoEquip || me.classic) return true; @@ -545,11 +561,11 @@ Item.autoEquipSecondary = function (task = "") { items.sort(sortEq); const item = items.shift(); const tier = NTIP.GetSecondaryTier(item); - let bodyLoc = this.getBodyLocSecondary(item); + let bodyLoc = Item.getSecondaryBodyLoc(item); if (tier > 0 && bodyLoc) { for (let j = 0; j < bodyLoc.length; j += 1) { - const equippedItem = this.getEquippedItem(bodyLoc[j]); + const equippedItem = Item.getEquipped(bodyLoc[j]); if (item.isInStorage && tier > equippedItem.secondarytier && equippedItem.classid !== sdk.items.quest.KhalimsWill) { Item.identify(item); @@ -573,7 +589,9 @@ Item.autoEquipSecondary = function (task = "") { return true; }; -// AUTO EQUIP MERC - modified from dzik's +/** + * @param {ItemUnit} item + */ Item.hasMercTier = (item) => Config.AutoEquip && me.expansion && NTIP.GetMercTier(item) > 0; // need to re-work using char data so we can shop/keep items if merc is dead *but* we have enough to revive him and buy the item and enough space @@ -583,7 +601,7 @@ Item.canEquipMerc = function (item, bodyLoc) { // dont have merc or he is dead or unidentifed item if (!mercenary || !item.identified) return false; - let curr = Item.getEquippedItemMerc(bodyLoc); + let curr = Item.getMercEquipped(bodyLoc); // Higher requirements if (item.getStat(sdk.stats.LevelReq) > mercenary.getStat(sdk.stats.Level) @@ -635,7 +653,7 @@ Item.equipMerc = function (item, bodyLoc) { return false; }; -Item.getEquippedItemMerc = function (bodyLoc = -1) { +Item.getMercEquipped = function (bodyLoc = -1) { let mercenary = me.getMercEx(); if (mercenary) { @@ -696,26 +714,11 @@ Item.getBodyLocMerc = function (item) { return Array.isArray(bodyLoc) ? bodyLoc : [bodyLoc]; }; -Item.autoEquipCheckMerc = function (item) { - if (!Config.AutoEquip) return true; - if (Config.AutoEquip && !me.getMercEx()) return false; - - let tier = NTIP.GetMercTier(item), bodyLoc = Item.getBodyLocMerc(item); - - if (tier > 0 && bodyLoc) { - for (let i = 0; i < bodyLoc.length; i += 1) { - let oldTier = Item.getEquippedItemMerc(bodyLoc[i]).tier; // Low tier items shouldn't be kept if they can't be equipped - - if (tier > oldTier && (Item.canEquipMerc(item) || !item.identified)) { - return true; - } - } - } - - return false; -}; - -Item.autoEquipKeepCheckMerc = function (item) { +/** + * @param {ItemUnit} item + * @param {boolean} basicCheck + */ +Item.autoEquipCheckMerc = function (item, basicCheck = false) { if (!Config.AutoEquip) return true; if (Config.AutoEquip && !me.getMercEx()) return false; @@ -723,9 +726,9 @@ Item.autoEquipKeepCheckMerc = function (item) { if (tier > 0 && bodyLoc) { for (let i = 0; i < bodyLoc.length; i += 1) { - let oldTier = Item.getEquippedItemMerc(bodyLoc[i]).tier; // Low tier items shouldn't be kept if they can't be equipped + let oldTier = Item.getMercEquipped(bodyLoc[i]).tier; // Low tier items shouldn't be kept if they can't be equipped - if (tier > oldTier) { + if (tier > oldTier && (!basicCheck ? Item.canEquipMerc(item) || !item.identified : true)) { return true; } } @@ -768,7 +771,7 @@ Item.autoEquipMerc = function () { if (tier > 0 && bodyLoc) { for (let j = 0; j < bodyLoc.length; j += 1) { - if ([sdk.storage.Inventory, sdk.storage.Stash].includes(item.location) && tier > Item.getEquippedItemMerc(bodyLoc[j]).tier) { + if ([sdk.storage.Inventory, sdk.storage.Stash].includes(item.location) && tier > Item.getMercEquipped(bodyLoc[j]).tier) { Item.identify(item); console.log("Merc " + name); @@ -816,663 +819,665 @@ Item.removeItemsMerc = function () { return !!mercenary.getItem(); }; -// Charm Autoequip - TODO: clean this section up...sigh -// goals -/* -* need to be able to define what types of charms we want while leveling, and upgrade based on that -* need to be able to define what types of charms we want for final build, upgrade to that -* need to be able to handle different invoquantity values of final charms vs leveling charms -* need to be abel to handle final charms and leveling charms being the same type, in situation where we have enough of a final charm so compare it as a noraml leveling charm -* need to differentiate bewtween cubing charm or pickit wanted charm vs autoequip charm -* example: -* Imagine we are an auradin and we have 9 small charms in our inventory, Seven 5allres/20life and Two random life charms. Our build tells us we should keep 6 of the 5/20s -* so we should keep those. That leaves us with One 5/20 and Two random life charms, we should then compare the tier values and keep the highest of the two then sell or drop the third. -* As it is now, what happens is we don't compare the 7th 5/20 and we add that to the sell list while keeping the 2 lower charms. If we directly add it to the backup then the invoquantity -* gets read from the finalBuild file so instead of only keeping two it says we should keep 6. -*/ -Item.hasCharmTier = (item) => me.expansion && Config.AutoEquip && NTIP.GetCharmTier(item) > 0; -Item.isFinalCharm = (item) => myData.me.charmGids.includes(item.gid); +(function() { + // Charm Autoequip - TODO: clean this section up...sigh + // goals + /* + * need to be able to define what types of charms we want while leveling, and upgrade based on that + * need to be able to define what types of charms we want for final build, upgrade to that + * need to be able to handle different invoquantity values of final charms vs leveling charms + * need to be abel to handle final charms and leveling charms being the same type, in situation where we have enough of a final charm so compare it as a noraml leveling charm + * need to differentiate bewtween cubing charm or pickit wanted charm vs autoequip charm + * example: + * Imagine we are an auradin and we have 9 small charms in our inventory, Seven 5allres/20life and Two random life charms. Our build tells us we should keep 6 of the 5/20s + * so we should keep those. That leaves us with One 5/20 and Two random life charms, we should then compare the tier values and keep the highest of the two then sell or drop the third. + * As it is now, what happens is we don't compare the 7th 5/20 and we add that to the sell list while keeping the 2 lower charms. If we directly add it to the backup then the invoquantity + * gets read from the finalBuild file so instead of only keeping two it says we should keep 6. + */ + Item.hasCharmTier = (item) => me.expansion && Config.AutoEquip && NTIP.GetCharmTier(item) > 0; + Item.isFinalCharm = (item) => myData.me.charmGids.includes(item.gid); -/** - * Iterate over charm checklist, pickit result 0 and 4 get sold - * Otherwise if its not in the stash already and not a final charm try and stash it. I don't remember why I checked if it wasn't a final charm - * @param {ItemUnit[]} checkList - * @param {boolean} verbose - */ -const spliceCharmCheckList = function (checkList = [], verbose = false) { - for (let i = 0; i < checkList.length; i++) { - const currCharm = checkList[i]; - if (!currCharm || [Pickit.Result.UNWANTED, Pickit.Result.TRASH].includes(Pickit.checkItem(currCharm).result)) continue; - if (!currCharm.isInStash && !myData.me.charmGids.includes(currCharm.gid)) { - if (!Storage.Stash.MoveTo(currCharm)) { - verbose && Misc.itemLogger("Dropped", currCharm); - currCharm.drop(); - } else { - if (verbose) { - Cubing.checkItem(currCharm) ? Misc.logItem("Stashed Cubing Ingredient", currCharm) : Misc.logItem("Stashed", currCharm); + /** + * Iterate over charm checklist, pickit result 0 and 4 get sold + * Otherwise if its not in the stash already and not a final charm try and stash it. I don't remember why I checked if it wasn't a final charm + * @param {ItemUnit[]} checkList + * @param {boolean} verbose + */ + const spliceCharmCheckList = function (checkList = [], verbose = false) { + for (let i = 0; i < checkList.length; i++) { + const currCharm = checkList[i]; + if (!currCharm || [Pickit.Result.UNWANTED, Pickit.Result.TRASH].includes(Pickit.checkItem(currCharm).result)) continue; + if (!currCharm.isInStash && !myData.me.charmGids.includes(currCharm.gid)) { + if (!Storage.Stash.MoveTo(currCharm)) { + verbose && Misc.itemLogger("Dropped", currCharm); + currCharm.drop(); + } else { + if (verbose) { + Cubing.checkItem(currCharm) ? Misc.logItem("Stashed Cubing Ingredient", currCharm) : Misc.logItem("Stashed", currCharm); + } } } - } - - checkList.splice(i, 1); - i -= 1; - } -}; -const spliceCharmKeepList = function (keep = [], sell = [], verbose = false) { - if (!keep.length) return; - const id = keep[0].classid; - const cInfo = (() => { - switch (id) { - case sdk.items.SmallCharm: - return CharData.charmData.small.getCountInfo(); - case sdk.items.LargeCharm: - return CharData.charmData.large.getCountInfo(); - case sdk.items.GrandCharm: - return CharData.charmData.grand.getCountInfo(); - default: - return { max: 0 }; + checkList.splice(i, 1); + i -= 1; } - })(); + }; - // sort through kept charms - if (keep.length > cInfo.max) { - keep.sort((a, b) => NTIP.GetCharmTier(b) - NTIP.GetCharmTier(a)); - - // everything after the cap (need a better method for this in the instances where the max cap is less then leveling wanted cap) - for (let i = cInfo.max; i < keep.length; i++) { - if (!!keep[i].classid && !Item.autoEquipCharmCheck(keep[i])) { - sell.push(keep[i]); - verbose && console.log("ÿc8Kolbot-SoloPlayÿc0: CharmEquip Add " + keep[i].fname + " to checkList"); - keep.splice(i, 1); - i -= 1; + const spliceCharmKeepList = function (keep = [], sell = [], verbose = false) { + if (!keep.length) return; + const id = keep[0].classid; + const cInfo = (() => { + switch (id) { + case sdk.items.SmallCharm: + return CharData.charmData.small.getCountInfo(); + case sdk.items.LargeCharm: + return CharData.charmData.large.getCountInfo(); + case sdk.items.GrandCharm: + return CharData.charmData.grand.getCountInfo(); + default: + return { max: 0 }; + } + })(); + + // sort through kept charms + if (keep.length > cInfo.max) { + keep.sort((a, b) => NTIP.GetCharmTier(b) - NTIP.GetCharmTier(a)); + + // everything after the cap (need a better method for this in the instances where the max cap is less then leveling wanted cap) + for (let i = cInfo.max; i < keep.length; i++) { + if (!!keep[i].classid && !Item.autoEquipCharmCheck(keep[i])) { + sell.push(keep[i]); + verbose && console.log("ÿc8Kolbot-SoloPlayÿc0: CharmEquip Add " + keep[i].fname + " to checkList"); + keep.splice(i, 1); + i -= 1; + } } } - } -}; - -Item.autoEquipSC = function () { - let verbose = Developer.debugging.smallCharm; - // build list of our charms - let items = me.getItemsEx() - .filter((charm) => charm.isInStorage && charm.classid === sdk.items.SmallCharm && charm.magic); + }; - if (!items.length) { - verbose && console.debug("No charms found"); - return { keep: [], sell: [] }; - } + Item.autoEquipSC = function () { + let verbose = Developer.debugging.smallCharm; + // build list of our charms + let items = me.getItemsEx() + .filter((charm) => charm.isInStorage && charm.classid === sdk.items.SmallCharm && charm.magic); - let charms = Item.autoEquipCharmSort(items, verbose); - spliceCharmKeepList(charms.keep, charms.checkList, verbose); + if (!items.length) { + verbose && console.debug("No charms found"); + return { keep: [], sell: [] }; + } - verbose && console.log("Small Charm checklist length: " + charms.checkList.length); - spliceCharmCheckList(charms.checkList, verbose); + let charms = Item.autoEquipCharmSort(items, verbose); + spliceCharmKeepList(charms.keep, charms.checkList, verbose); - return { keep: charms.keep, sell: charms.checkList }; -}; + verbose && console.log("Small Charm checklist length: " + charms.checkList.length); + spliceCharmCheckList(charms.checkList, verbose); -Item.autoEquipLC = function () { - let verbose = Developer.debugging.largeCharm; - let items = me.getItemsEx() - .filter((charm) => charm.isInStorage && charm.classid === sdk.items.LargeCharm && charm.magic); + return { keep: charms.keep, sell: charms.checkList }; + }; - if (!items.length) { - verbose && console.debug("No charms found"); - return { keep: [], sell: [] }; - } + Item.autoEquipLC = function () { + let verbose = Developer.debugging.largeCharm; + let items = me.getItemsEx() + .filter((charm) => charm.isInStorage && charm.classid === sdk.items.LargeCharm && charm.magic); - let charms = Item.autoEquipCharmSort(items, verbose); - spliceCharmKeepList(charms.keep, charms.checkList, verbose); + if (!items.length) { + verbose && console.debug("No charms found"); + return { keep: [], sell: [] }; + } - verbose && console.log("Large charm checklist length: " + charms.checkList.length); - spliceCharmCheckList(charms.checkList, verbose); + let charms = Item.autoEquipCharmSort(items, verbose); + spliceCharmKeepList(charms.keep, charms.checkList, verbose); - return { keep: charms.keep, sell: charms.checkList }; -}; - -/** - * @memberof Item - * @returns { { keep: ItemUnit[], sell: ItemUnit[] } } - */ -Item.autoEquipGC = function () { - let verbose = Developer.debugging.largeCharm; - let items = me.getItemsEx() - .filter((charm) => charm.isInStorage && charm.classid === sdk.items.GrandCharm && charm.magic); + verbose && console.log("Large charm checklist length: " + charms.checkList.length); + spliceCharmCheckList(charms.checkList, verbose); - if (!items.length) { - verbose && console.debug("No charms found"); - return { keep: [], sell: [] }; - } + return { keep: charms.keep, sell: charms.checkList }; + }; - let charms = Item.autoEquipCharmSort(items, verbose); - spliceCharmKeepList(charms.keep, verbose); + /** + * @memberof Item + * @returns { { keep: ItemUnit[], sell: ItemUnit[] } } + */ + Item.autoEquipGC = function () { + let verbose = Developer.debugging.largeCharm; + let items = me.getItemsEx() + .filter((charm) => charm.isInStorage && charm.classid === sdk.items.GrandCharm && charm.magic); + + if (!items.length) { + verbose && console.debug("No charms found"); + return { keep: [], sell: [] }; + } - verbose && console.log("Grand charm checklist length: " + charms.checkList.length); - spliceCharmKeepList(charms.keep, charms.checkList, verbose); + let charms = Item.autoEquipCharmSort(items, verbose); + spliceCharmKeepList(charms.keep, verbose); - return { keep: charms.keep, sell: charms.checkList }; -}; + verbose && console.log("Grand charm checklist length: " + charms.checkList.length); + spliceCharmKeepList(charms.keep, charms.checkList, verbose); -Item.autoEquipCharmSort = function (items = [], verbose = false) { - let charms = { - skillerTypeA: [], - skillerTypeB: [], - skillerTypeC: [], - resist: [], - life: [], - magicfind: [], - damage: [], - elemental: [], - backup: [], - keep: [], - checkList: [] + return { keep: charms.keep, sell: charms.checkList }; }; - if (!items.length) { - verbose && console.log("No charms found"); - return charms; - } + Item.autoEquipCharmSort = function (items = [], verbose = false) { + let charms = { + skillerTypeA: [], + skillerTypeB: [], + skillerTypeC: [], + resist: [], + life: [], + magicfind: [], + damage: [], + elemental: [], + backup: [], + keep: [], + checkList: [] + }; + + if (!items.length) { + verbose && console.log("No charms found"); + return charms; + } - const addToCheckList = (item) => charms.checkList.indexOf(item) === -1 && charms.checkList.push(item); - const addToBackUp = (item) => charms.backup.indexOf(item) === -1 && charms.backup.push(item); + const addToCheckList = (item) => charms.checkList.indexOf(item) === -1 && charms.checkList.push(item); + const addToBackUp = (item) => charms.backup.indexOf(item) === -1 && charms.backup.push(item); - const sortCharms = (arr = [], verbose = false, backUpCheck = true) => { - let invoquantity = NTIP.getInvoQuantity(arr[0]); - (invoquantity === undefined || invoquantity === -1) && (invoquantity = 2); - let charmType = Item.getCharmType(arr[0]); - verbose && console.log("Amount of " + charmType + " Charms: " + arr.length + " invoquantity: " + invoquantity); - arr.length > 1 && arr.sort((a, b) => NTIP.GetCharmTier(b) - NTIP.GetCharmTier(a)); + const sortCharms = (arr = [], verbose = false, backUpCheck = true) => { + let invoquantity = NTIP.getInvoQuantity(arr[0]); + (invoquantity === undefined || invoquantity === -1) && (invoquantity = 2); + let charmType = Item.getCharmType(arr[0]); + verbose && console.log("Amount of " + charmType + " Charms: " + arr.length + " invoquantity: " + invoquantity); + arr.length > 1 && arr.sort((a, b) => NTIP.GetCharmTier(b) - NTIP.GetCharmTier(a)); - if (arr.length > invoquantity) { - verbose && arr.forEach((el, index) => console.log(charmType + "[" + index + "] = " + NTIP.GetCharmTier(el))); + if (arr.length > invoquantity) { + verbose && arr.forEach((el, index) => console.log(charmType + "[" + index + "] = " + NTIP.GetCharmTier(el))); - for (let i = invoquantity; i < arr.length; i++) { - backUpCheck ? addToBackUp(arr[i]) : addToCheckList(arr[i]); + for (let i = invoquantity; i < arr.length; i++) { + backUpCheck ? addToBackUp(arr[i]) : addToCheckList(arr[i]); - arr.splice(i, 1); - i -= 1; + arr.splice(i, 1); + i -= 1; + } } - } - }; + }; - verbose && console.log("Amount of items: " + items.length); - items.length > 1 && items.sort((a, b) => NTIP.GetCharmTier(b) - NTIP.GetCharmTier(a)); + verbose && console.log("Amount of items: " + items.length); + items.length > 1 && items.sort((a, b) => NTIP.GetCharmTier(b) - NTIP.GetCharmTier(a)); - const finalCharmInfo = Check.finalBuild().finalCharms; - const finalCharmKeys = Object.keys(finalCharmInfo); + const finalCharmInfo = Check.finalBuild().finalCharms; + const finalCharmKeys = Object.keys(finalCharmInfo); - let found = false; + let found = false; - while (items.length > 0) { - let gid = items[0].gid; - let item = items.shift(); + while (items.length > 0) { + let gid = items[0].gid; + let item = items.shift(); - if (!item.identified) { - let idTool = me.getIdTool(); + if (!item.identified) { + let idTool = me.getIdTool(); - if (idTool) { - item.isInStash && Town.openStash(); - Town.identifyItem(item, idTool); + if (idTool) { + item.isInStash && Town.openStash(); + Town.identifyItem(item, idTool); - } else if (item.isInStash && (getUIFlag(sdk.uiflags.Stash) || Town.openStash())) { - Storage.Inventory.MoveTo(item); - Town.identify(); - } + } else if (item.isInStash && (getUIFlag(sdk.uiflags.Stash) || Town.openStash())) { + Storage.Inventory.MoveTo(item); + Town.identify(); + } - if (!Game.getItem(-1, -1, gid)) { - verbose && console.log("Sold charm during Town.identify()"); - items.shift(); + if (!Game.getItem(-1, -1, gid)) { + verbose && console.log("Sold charm during Town.identify()"); + items.shift(); - continue; + continue; + } } - } - if (myData.me.charmGids.includes(item.gid)) { - charms.keep.push(item); + if (myData.me.charmGids.includes(item.gid)) { + charms.keep.push(item); - continue; - } + continue; + } - let next = false; + let next = false; - for (let i = 0; i < finalCharmKeys.length; i++) { - let cKey = finalCharmKeys[i]; - try { - if (!!myData.me.charms[cKey] && myData.me.charms[cKey].have.indexOf(item.gid) === -1 + for (let i = 0; i < finalCharmKeys.length; i++) { + let cKey = finalCharmKeys[i]; + try { + if (!!myData.me.charms[cKey] && myData.me.charms[cKey].have.indexOf(item.gid) === -1 && myData.me.charms[cKey].have.length < myData.me.charms[cKey].max) { - if (finalCharmInfo[cKey].stats(item)) { - console.debug(item.fname); - myData.me.charmGids.push(item.gid); - myData.me.charms[cKey].have.push(item.gid); - charms.keep.push(item); - found = true; - next = true; + if (finalCharmInfo[cKey].stats(item)) { + console.debug(item.fname); + myData.me.charmGids.push(item.gid); + myData.me.charms[cKey].have.push(item.gid); + charms.keep.push(item); + found = true; + next = true; - break; + break; + } } + } catch (e) { + console.error(e); } - } catch (e) { - console.error(e); } - } - - if (next) { - continue; - } - if (NTIP.GetCharmTier(item) <= 0) { - verbose && console.log("No tier. Adding to checkList: " + item.fname); - addToCheckList(item); - } else if (!NTIP.hasStats(item) && NTIP.GetCharmTier(item) > 0) { - verbose && console.log("Multiple Misc charm: " + item.fname); - charms.backup.push(item); - } else { - let charmType = Item.getCharmType(item); - switch (charmType) { - case "skillerTypeA": - case "skillerTypeB": - case "skillerTypeC": - case "resist": - case "life": - case "magicfind": - case "damage": - case "elemental": - charms[charmType].push(item); - verbose && console.log(charmType + ": " + item.fname); + if (next) { + continue; + } - break; - default: + if (NTIP.GetCharmTier(item) <= 0) { + verbose && console.log("No tier. Adding to checkList: " + item.fname); addToCheckList(item); - verbose && console.log("Failed all checks. Adding to checkList: " + item.fname); + } else if (!NTIP.hasStats(item) && NTIP.GetCharmTier(item) > 0) { + verbose && console.log("Multiple Misc charm: " + item.fname); + charms.backup.push(item); + } else { + let charmType = Item.getCharmType(item); + switch (charmType) { + case "skillerTypeA": + case "skillerTypeB": + case "skillerTypeC": + case "resist": + case "life": + case "magicfind": + case "damage": + case "elemental": + charms[charmType].push(item); + verbose && console.log(charmType + ": " + item.fname); - break; + break; + default: + addToCheckList(item); + verbose && console.log("Failed all checks. Adding to checkList: " + item.fname); + + break; + } } } - } - if (found) { - updateMyData(); - } + if (found) { + updateMyData(); + } - if (!charms.skillerTypeA.length && !charms.skillerTypeB.length && !charms.skillerTypeC.length + if (!charms.skillerTypeA.length && !charms.skillerTypeB.length && !charms.skillerTypeC.length && !charms.damage.length && !charms.resist.length && !charms.elemental.length && !charms.life.length && !charms.backup.length) { - verbose && console.log("No Charms"); - return charms; - } + verbose && console.log("No Charms"); + return charms; + } - charms.skillerTypeA.length > 0 && sortCharms(charms.skillerTypeA, verbose); - charms.skillerTypeB.length > 0 && sortCharms(charms.skillerTypeB, verbose); - charms.skillerTypeC.length > 0 && sortCharms(charms.skillerTypeC, verbose); - charms.resist.length > 0 && sortCharms(charms.resist, verbose); - charms.life.length > 0 && sortCharms(charms.life, verbose); - charms.magicfind.length > 0 && sortCharms(charms.magicfind, verbose); - charms.damage.length > 0 && sortCharms(charms.damage, verbose); - charms.elemental.length > 0 && sortCharms(charms.elemental, verbose); - - // If stats are unspecifed, this will filter charms and keep highest based on invoquantity. If no invoquantity defined it will keep two of that type - charms.backup.length > 0 && sortCharms(charms.backup, verbose, false); - charms.keep = charms.keep.concat(charms.skillerTypeA, charms.skillerTypeB, charms.skillerTypeC, charms.resist, charms.life, charms.magicfind, charms.damage, charms.elemental, charms.backup); - verbose && charms.checkList.forEach((el, index) => console.log("checkList[" + index + "] = " + NTIP.GetCharmTier(el) + " " + el.fname)); + charms.skillerTypeA.length > 0 && sortCharms(charms.skillerTypeA, verbose); + charms.skillerTypeB.length > 0 && sortCharms(charms.skillerTypeB, verbose); + charms.skillerTypeC.length > 0 && sortCharms(charms.skillerTypeC, verbose); + charms.resist.length > 0 && sortCharms(charms.resist, verbose); + charms.life.length > 0 && sortCharms(charms.life, verbose); + charms.magicfind.length > 0 && sortCharms(charms.magicfind, verbose); + charms.damage.length > 0 && sortCharms(charms.damage, verbose); + charms.elemental.length > 0 && sortCharms(charms.elemental, verbose); + + // If stats are unspecifed, this will filter charms and keep highest based on invoquantity. If no invoquantity defined it will keep two of that type + charms.backup.length > 0 && sortCharms(charms.backup, verbose, false); + charms.keep = charms.keep.concat(charms.skillerTypeA, charms.skillerTypeB, charms.skillerTypeC, charms.resist, charms.life, charms.magicfind, charms.damage, charms.elemental, charms.backup); + verbose && charms.checkList.forEach((el, index) => console.log("checkList[" + index + "] = " + NTIP.GetCharmTier(el) + " " + el.fname)); - return charms; -}; - -/** - * @param {ItemUnit} item - */ -Item.autoEquipCharmCheck = function (item = undefined) { - if (!item || NTIP.GetCharmTier(item) <= 0 || !item.isCharm) return false; - // Annhilus, Hellfire Torch, Gheeds - Handled by a different function so return true to keep - if (item.isCharm && item.unique) return true; - // is one of our final charms - if (myData.me.charmGids.includes(item.gid)) return true; - - let lowestCharm; - let items = me.getItemsEx() - .filter(charm => charm.classid === item.classid && charm.isInStorage && charm.magic && NTIP.GetCharmTier(charm) > 0); - if (!items.length) return true; - - let quantityCap = NTIP.getInvoQuantity(item); - let charms = Item.autoEquipCharmSort(items); - let charmType = Item.getCharmType(item); - let cInfo, newList = []; + return charms; + }; - switch (item.classid) { - case sdk.items.SmallCharm: - cInfo = CharData.charmData.small.getCountInfo(); + /** + * @param {ItemUnit} item + */ + Item.autoEquipCharmCheck = function (item = undefined) { + if (!item || NTIP.GetCharmTier(item) <= 0 || !item.isCharm) return false; + // Annhilus, Hellfire Torch, Gheeds - Handled by a different function so return true to keep + if (item.isCharm && item.unique) return true; + // is one of our final charms + if (myData.me.charmGids.includes(item.gid)) return true; + + let lowestCharm; + let items = me.getItemsEx() + .filter(charm => charm.classid === item.classid && charm.isInStorage && charm.magic && NTIP.GetCharmTier(charm) > 0); + if (!items.length) return true; + + let quantityCap = NTIP.getInvoQuantity(item); + let charms = Item.autoEquipCharmSort(items); + let charmType = Item.getCharmType(item); + let cInfo, newList = []; + + switch (item.classid) { + case sdk.items.SmallCharm: + cInfo = CharData.charmData.small.getCountInfo(); - if (cInfo.curr && (cInfo.curr / cInfo.max) * 100 >= 75) { + if (cInfo.curr && (cInfo.curr / cInfo.max) * 100 >= 75) { // chop off past our cap - newList = charms.keep - .sort((a, b) => NTIP.GetCharmTier(b) - NTIP.GetCharmTier(a)) - .slice(0, cInfo.max); - // check if it made the cut - if (!newList.find(i => i.gid === item.gid)) return false; - lowestCharm = newList.last(); - return !!(NTIP.GetCharmTier(item) >= NTIP.GetCharmTier(lowestCharm) || item.gid === lowestCharm.gid); - } + newList = charms.keep + .sort((a, b) => NTIP.GetCharmTier(b) - NTIP.GetCharmTier(a)) + .slice(0, cInfo.max); + // check if it made the cut + if (!newList.find(i => i.gid === item.gid)) return false; + lowestCharm = newList.last(); + return !!(NTIP.GetCharmTier(item) >= NTIP.GetCharmTier(lowestCharm) || item.gid === lowestCharm.gid); + } - break; - case sdk.items.LargeCharm: - cInfo = CharData.charmData.large.getCountInfo(); + break; + case sdk.items.LargeCharm: + cInfo = CharData.charmData.large.getCountInfo(); - if (cInfo.curr && (cInfo.curr / cInfo.max) * 100 >= 75) { + if (cInfo.curr && (cInfo.curr / cInfo.max) * 100 >= 75) { // chop off past our cap - newList = charms.keep - .sort((a, b) => NTIP.GetCharmTier(b) - NTIP.GetCharmTier(a)) - .slice(0, cInfo.max); - // check if it made the cut - if (!newList.find(i => i.gid === item.gid)) return false; - lowestCharm = newList.last(); - return !!(NTIP.GetCharmTier(item) >= NTIP.GetCharmTier(lowestCharm) || item.gid === lowestCharm.gid); - } + newList = charms.keep + .sort((a, b) => NTIP.GetCharmTier(b) - NTIP.GetCharmTier(a)) + .slice(0, cInfo.max); + // check if it made the cut + if (!newList.find(i => i.gid === item.gid)) return false; + lowestCharm = newList.last(); + return !!(NTIP.GetCharmTier(item) >= NTIP.GetCharmTier(lowestCharm) || item.gid === lowestCharm.gid); + } - break; - case sdk.items.GrandCharm: - cInfo = CharData.charmData.grand.getCountInfo(); + break; + case sdk.items.GrandCharm: + cInfo = CharData.charmData.grand.getCountInfo(); - if (cInfo.curr && (cInfo.curr / cInfo.max) * 100 >= 50) { + if (cInfo.curr && (cInfo.curr / cInfo.max) * 100 >= 50) { // chop off past our cap - newList = charms.keep - .sort((a, b) => NTIP.GetCharmTier(b) - NTIP.GetCharmTier(a)) - .slice(0, cInfo.max); - // check if it made the cut - if (!newList.find(i => i.gid === item.gid)) return false; - lowestCharm = newList.last(); - return !!(NTIP.GetCharmTier(item) >= NTIP.GetCharmTier(lowestCharm) || item.gid === lowestCharm.gid); + newList = charms.keep + .sort((a, b) => NTIP.GetCharmTier(b) - NTIP.GetCharmTier(a)) + .slice(0, cInfo.max); + // check if it made the cut + if (!newList.find(i => i.gid === item.gid)) return false; + lowestCharm = newList.last(); + return !!(NTIP.GetCharmTier(item) >= NTIP.GetCharmTier(lowestCharm) || item.gid === lowestCharm.gid); + } + + break; } - break; - } + switch (charmType) { + case "skillerTypeA": + case "skillerTypeB": + case "skillerTypeC": + case "resist": + case "life": + case "magicfind": + case "damage": + case "elemental": + lowestCharm = charms[charmType].last(); + if ((charms[charmType].findIndex(c => c.gid === lowestCharm.gid) + 1) > quantityCap) return false; - switch (charmType) { - case "skillerTypeA": - case "skillerTypeB": - case "skillerTypeC": - case "resist": - case "life": - case "magicfind": - case "damage": - case "elemental": - lowestCharm = charms[charmType].last(); - if ((charms[charmType].findIndex(c => c.gid === lowestCharm.gid) + 1) > quantityCap) return false; - - break; - default: - lowestCharm = charms.backup.last(); - if ((charms.backup.findIndex(c => c.gid === lowestCharm.gid) + 1) > quantityCap) return false; + break; + default: + lowestCharm = charms.backup.last(); + if ((charms.backup.findIndex(c => c.gid === lowestCharm.gid) + 1) > quantityCap) return false; - break; - } + break; + } - return !!lowestCharm ? !!(NTIP.GetCharmTier(item) >= NTIP.GetCharmTier(lowestCharm) || item.gid === lowestCharm.gid) : true; -}; + return !!lowestCharm ? !!(NTIP.GetCharmTier(item) >= NTIP.GetCharmTier(lowestCharm) || item.gid === lowestCharm.gid) : true; + }; -Item.initCharms = function () { + Item.initCharms = function () { // No charms in classic - if (me.classic) return; - let myCharms = me.getItemsEx().filter(item => item.isInStorage && item.isCharm && item.magic); - let changed = false; - - const finalCharmKeys = Object.keys(myData.me.charms); - const check = function (list = [], charms = []) { - for (let i = 0; i < list.length; i++) { - if (!charms.some(c => c.gid === list[i])) { - console.log("A charm was removed from our final list - updated it"); - myData.me.charmGids.remove(list[i]); - list.splice(i, 1); - i--; - changed = true; + if (me.classic) return; + let myCharms = me.getItemsEx().filter(item => item.isInStorage && item.isCharm && item.magic); + let changed = false; + + const finalCharmKeys = Object.keys(myData.me.charms); + const check = function (list = [], charms = []) { + for (let i = 0; i < list.length; i++) { + if (!charms.some(c => c.gid === list[i])) { + console.log("A charm was removed from our final list - updated it"); + myData.me.charmGids.remove(list[i]); + list.splice(i, 1); + i--; + changed = true; + } } - } - }; + }; - for (let i = 0; i < finalCharmKeys.length; i++) { - let cKey = finalCharmKeys[i]; - switch (myData.me.charms[cKey].classid) { - case sdk.items.SmallCharm: - check(myData.me.charms[cKey].have, myCharms); + for (let i = 0; i < finalCharmKeys.length; i++) { + let cKey = finalCharmKeys[i]; + switch (myData.me.charms[cKey].classid) { + case sdk.items.SmallCharm: + check(myData.me.charms[cKey].have, myCharms); - break; - case sdk.items.LargeCharm: - check(myData.me.charms[cKey].have, myCharms); + break; + case sdk.items.LargeCharm: + check(myData.me.charms[cKey].have, myCharms); - break; - case sdk.items.GrandCharm: - check(myData.me.charms[cKey].have, myCharms); + break; + case sdk.items.GrandCharm: + check(myData.me.charms[cKey].have, myCharms); - break; + break; + } } - } - changed && updateMyData(); -}; + changed && updateMyData(); + }; -Item.autoEquipCharms = function () { + Item.autoEquipCharms = function () { // No charms in classic - if (me.classic) return; - - console.log("ÿc8Kolbot-SoloPlayÿc0: Entering charm auto equip"); - let tick = getTickCount(); - let totalKeep = [], totalSell = []; - let GCs = Item.autoEquipGC(); - let LCs = Item.autoEquipLC(); - let SCs = Item.autoEquipSC(); - let specialCharms = me.getItemsEx() - .filter((charm) => charm.isCharm && charm.unique); - let verbose = !!(Developer.debugging.smallCharm || Developer.debugging.largeCharm || Developer.debugging.grandCharm); - - if (verbose) { - console.log("Grand Charms Keep: " + GCs.keep.length + ", Sell: " + GCs.sell.length); - console.log("Large Charms Keep: " + LCs.keep.length + ", Sell: " + LCs.sell.length); - console.log("Small Charms Keep: " + SCs.keep.length + ", Sell: " + SCs.sell.length); - } - - totalKeep = totalKeep.concat(SCs.keep, LCs.keep, GCs.keep, specialCharms); - for (let i = 0; i < totalKeep.length; i++) { - if (!Item.autoEquipCharmCheck(totalKeep[i])) { - totalSell.push(totalKeep[i]); - totalKeep.splice(i, 1); - i--; + if (me.classic) return; + + console.log("ÿc8Kolbot-SoloPlayÿc0: Entering charm auto equip"); + let tick = getTickCount(); + let totalKeep = [], totalSell = []; + let GCs = Item.autoEquipGC(); + let LCs = Item.autoEquipLC(); + let SCs = Item.autoEquipSC(); + let specialCharms = me.getItemsEx() + .filter((charm) => charm.isCharm && charm.unique); + let verbose = !!(Developer.debugging.smallCharm || Developer.debugging.largeCharm || Developer.debugging.grandCharm); + + if (verbose) { + console.log("Grand Charms Keep: " + GCs.keep.length + ", Sell: " + GCs.sell.length); + console.log("Large Charms Keep: " + LCs.keep.length + ", Sell: " + LCs.sell.length); + console.log("Small Charms Keep: " + SCs.keep.length + ", Sell: " + SCs.sell.length); } - } - totalSell = totalSell - .concat(SCs.sell, LCs.sell, GCs.sell) - .filter((charm) => NTIP.CheckItem(charm, NTIP_CheckListNoTier) === Pickit.Result.UNWANTED); - totalKeep.length > 0 && console.log("ÿc8Kolbot-SoloPlayÿc0: Total Charms Kept: " + totalKeep.length); - - if (totalSell.length > 0) { - console.log("ÿc8Kolbot-SoloPlayÿc0: Total Charms Sell: " + totalSell.length); - - for (let i = 0; i < totalSell.length; i++) { - totalSell[i].isInStash && !getUIFlag(sdk.uiflags.Stash) && Town.openStash(); - if (totalSell[i].isInStash && (!totalSell[i].sellable || !Storage.Inventory.MoveTo(totalSell[i]))) { - totalSell[i].drop(); - totalSell.splice(i, 1); - i -= 1; + + totalKeep = totalKeep.concat(SCs.keep, LCs.keep, GCs.keep, specialCharms); + for (let i = 0; i < totalKeep.length; i++) { + if (!Item.autoEquipCharmCheck(totalKeep[i])) { + totalSell.push(totalKeep[i]); + totalKeep.splice(i, 1); + i--; } } + totalSell = totalSell + .concat(SCs.sell, LCs.sell, GCs.sell) + .filter((charm) => NTIP.CheckItem(charm, NTIP_CheckListNoTier) === Pickit.Result.UNWANTED); + totalKeep.length > 0 && console.log("ÿc8Kolbot-SoloPlayÿc0: Total Charms Kept: " + totalKeep.length); - Town.initNPC("Shop", "clearInventory"); + if (totalSell.length > 0) { + console.log("ÿc8Kolbot-SoloPlayÿc0: Total Charms Sell: " + totalSell.length); - if (getUIFlag(sdk.uiflags.Shop) || (Config.PacketShopping && getInteractedNPC() && getInteractedNPC().itemcount > 0)) { for (let i = 0; i < totalSell.length; i++) { - console.log("ÿc8Kolbot-SoloPlayÿc0: Sell old charm " + totalSell[i].name); - verbose && Misc.itemLogger("Sold", totalSell[i]); - verbose && Misc.logItem("CharmEquip Sold", totalSell[i]); - totalSell[i].sell(); + totalSell[i].isInStash && !getUIFlag(sdk.uiflags.Stash) && Town.openStash(); + if (totalSell[i].isInStash && (!totalSell[i].sellable || !Storage.Inventory.MoveTo(totalSell[i]))) { + totalSell[i].drop(); + totalSell.splice(i, 1); + i -= 1; + } } - } - } - if (totalKeep.length > 0) { - for (let i = 0; i < totalKeep.length; i++) { - if (totalKeep[i].isInStash && !Cubing.checkItem(totalKeep[i])) { - !getUIFlag(sdk.uiflags.Stash) && Town.openStash() && delay(300 + me.ping); - if (Storage.Inventory.CanFit(totalKeep[i]) && Storage.Inventory.MoveTo(totalKeep[i])) { - verbose && Misc.logItem("CharmEquip Equipped", totalKeep[i]); + Town.initNPC("Shop", "clearInventory"); + + if (getUIFlag(sdk.uiflags.Shop) || (Config.PacketShopping && getInteractedNPC() && getInteractedNPC().itemcount > 0)) { + for (let i = 0; i < totalSell.length; i++) { + console.log("ÿc8Kolbot-SoloPlayÿc0: Sell old charm " + totalSell[i].name); + verbose && Misc.itemLogger("Sold", totalSell[i]); + verbose && Misc.logItem("CharmEquip Sold", totalSell[i]); + totalSell[i].sell(); } } } - } - me.cancelUIFlags(); - - console.log("ÿc8Kolbot-SoloPlayÿc0: Exiting charm auto equip. Time elapsed: " + Developer.formatTime(getTickCount() - tick)); -}; + if (totalKeep.length > 0) { + for (let i = 0; i < totalKeep.length; i++) { + if (totalKeep[i].isInStash && !Cubing.checkItem(totalKeep[i])) { + !getUIFlag(sdk.uiflags.Stash) && Town.openStash() && delay(300 + me.ping); + if (Storage.Inventory.CanFit(totalKeep[i]) && Storage.Inventory.MoveTo(totalKeep[i])) { + verbose && Misc.logItem("CharmEquip Equipped", totalKeep[i]); + } + } + } + } -// Write charm equip version that checks by item prefix/suffix using a switch case with the various prefixes and suffixes to sort them -// improve this, not sure how but still needs work -// maybe do this by prefix/suffix number instead? -Item.getCharmType = function (charm = undefined) { - if (!charm || !charm.isCharm) return false; - if (charm.unique) return "unique"; - if (!NTIP.hasStats(charm) && NTIP.GetCharmTier(charm) > 0) return "misc"; - - let charmType = ""; - const skillerStats = me.getSkillTabs(); - - if (charm.getStat(sdk.stats.AddSkillTab, skillerStats[0])) { - charmType = "skillerTypeA"; - } else if (charm.getStat(sdk.stats.AddSkillTab, skillerStats[1])) { - charmType = "skillerTypeB"; - } else if (charm.getStat(sdk.stats.AddSkillTab, skillerStats[2])) { - charmType = "skillerTypeC"; - } + me.cancelUIFlags(); - switch (charm.prefix) { - case "Shimmering": - case "Azure": - case "Lapis": - case "Cobalt": - case "Sapphire": - case "Crimson": - case "Russet": - case "Garnet": - case "Ruby": - case "Tangerine": - case "Ocher": - case "Coral": - case "Amber": - case "Beryl": - case "Viridian": - case "Jade": - case "Emerald": - charmType = "resist"; - break; - } + console.log("ÿc8Kolbot-SoloPlayÿc0: Exiting charm auto equip. Time elapsed: " + Developer.formatTime(getTickCount() - tick)); + }; - if (!charmType || charmType === "") { - switch (charm.suffix) { - case "of Fortune": - case "of Good Luck": - charmType = "magicfind"; - break; - case "of Life": - case "of Substinence": // Odd issue, seems to be misspelled wherever item.suffix pulls info from - case "of Vita": - charmType = "life"; - break; + // Write charm equip version that checks by item prefix/suffix using a switch case with the various prefixes and suffixes to sort them + // improve this, not sure how but still needs work + // maybe do this by prefix/suffix number instead? + Item.getCharmType = function (charm = undefined) { + if (!charm || !charm.isCharm) return false; + if (charm.unique) return "unique"; + if (!NTIP.hasStats(charm) && NTIP.GetCharmTier(charm) > 0) return "misc"; + + let charmType = ""; + const skillerStats = me.getSkillTabs(); + + if (charm.getStat(sdk.stats.AddSkillTab, skillerStats[0])) { + charmType = "skillerTypeA"; + } else if (charm.getStat(sdk.stats.AddSkillTab, skillerStats[1])) { + charmType = "skillerTypeB"; + } else if (charm.getStat(sdk.stats.AddSkillTab, skillerStats[2])) { + charmType = "skillerTypeC"; } - } - if (!charmType || charmType === "") { switch (charm.prefix) { - case "Red": - case "Sanguinary": - case "Bloody": - case "Jagged": - case "Forked": - case "Serrated": - case "Bronze": - case "Iron": - case "Steel": - case "Fine": - case "Sharp": - charmType = "damage"; - break; - case "Snowy": - case "Shivering": - case "Boreal": - case "Hibernal": - case "Ember": - case "Smoldering": - case "Smoking": - case "Flaming": - case "Static": - case "Glowing": - case "Arcing": - case "Shocking": - case "Septic": - case "Foul": - case "Toxic": - case "Pestilant": - charmType = "elemental"; + case "Shimmering": + case "Azure": + case "Lapis": + case "Cobalt": + case "Sapphire": + case "Crimson": + case "Russet": + case "Garnet": + case "Ruby": + case "Tangerine": + case "Ocher": + case "Coral": + case "Amber": + case "Beryl": + case "Viridian": + case "Jade": + case "Emerald": + charmType = "resist"; break; } - } - if (!charmType || charmType === "") { - switch (charm.suffix) { - case "of Craftmanship": - case "of Quality": - case "of Maiming": - charmType = "damage"; - break; - case "of Strength": - case "of Dexterity": - charmType = "stats"; - break; - case "of Blight": - case "of Venom": - case "of Pestilence": - case "of Anthrax": - case "of Frost": - case "of Icicle": - case "of Glacier": - case "of Winter": - case "of Flame": - case "of Burning": - case "of Incineration": - case "of Shock": - case "of Lightning": - case "of Thunder": - case "of Storms": - charmType = "elemental"; - break; + if (!charmType || charmType === "") { + switch (charm.suffix) { + case "of Fortune": + case "of Good Luck": + charmType = "magicfind"; + break; + case "of Life": + case "of Substinence": // Odd issue, seems to be misspelled wherever item.suffix pulls info from + case "of Vita": + charmType = "life"; + break; + } } - } - if (!charmType || charmType === "") { - switch (charm.prefix) { - case "Stout": - case "Burly": - case "Stalwart": - charmType = "misc"; - break; - case "Rugged": - charmType = "misc"; - break; - case "Lizard's": - case "Snake's": - case "Serpent's": - charmType = "mana"; - break; + if (!charmType || charmType === "") { + switch (charm.prefix) { + case "Red": + case "Sanguinary": + case "Bloody": + case "Jagged": + case "Forked": + case "Serrated": + case "Bronze": + case "Iron": + case "Steel": + case "Fine": + case "Sharp": + charmType = "damage"; + break; + case "Snowy": + case "Shivering": + case "Boreal": + case "Hibernal": + case "Ember": + case "Smoldering": + case "Smoking": + case "Flaming": + case "Static": + case "Glowing": + case "Arcing": + case "Shocking": + case "Septic": + case "Foul": + case "Toxic": + case "Pestilant": + charmType = "elemental"; + break; + } } - } - if (!charmType || charmType === "") { - switch (charm.suffix) { - case "of Balance": - case "of Greed": - case "of Inertia": - charmType = "misc"; - break; + if (!charmType || charmType === "") { + switch (charm.suffix) { + case "of Craftmanship": + case "of Quality": + case "of Maiming": + charmType = "damage"; + break; + case "of Strength": + case "of Dexterity": + charmType = "stats"; + break; + case "of Blight": + case "of Venom": + case "of Pestilence": + case "of Anthrax": + case "of Frost": + case "of Icicle": + case "of Glacier": + case "of Winter": + case "of Flame": + case "of Burning": + case "of Incineration": + case "of Shock": + case "of Lightning": + case "of Thunder": + case "of Storms": + charmType = "elemental"; + break; + } } - } - return charmType; -}; + if (!charmType || charmType === "") { + switch (charm.prefix) { + case "Stout": + case "Burly": + case "Stalwart": + charmType = "misc"; + break; + case "Rugged": + charmType = "misc"; + break; + case "Lizard's": + case "Snake's": + case "Serpent's": + charmType = "mana"; + break; + } + } + + if (!charmType || charmType === "") { + switch (charm.suffix) { + case "of Balance": + case "of Greed": + case "of Inertia": + charmType = "misc"; + break; + } + } + + return charmType; + }; +})(); const AutoEquip = { /** @@ -1494,7 +1499,7 @@ const AutoEquip = { if (item.isCharm) { return Item.autoEquipCharmCheck(item); } - return Item.autoEquipKeepCheckMerc(item) || Item.autoEquipCheck(item, true) || Item.autoEquipCheckSecondary(item); + return Item.autoEquipCheckMerc(item, true) || Item.autoEquipCheck(item, true) || Item.autoEquipCheckSecondary(item); }, runAutoEquip: function () { diff --git a/libs/SoloPlay/Functions/ItemPrototypes.js b/libs/SoloPlay/Functions/ItemPrototypes.js index 7be26fc6..eb8d10a5 100644 --- a/libs/SoloPlay/Functions/ItemPrototypes.js +++ b/libs/SoloPlay/Functions/ItemPrototypes.js @@ -133,7 +133,7 @@ Unit.prototype.canEquipMerc = function (bodyLoc = -1) { // dont have merc or he is dead if (!mercenary) return false; - let curr = Item.getEquippedItemMerc(bodyLoc); + let curr = Item.getMercEquipped(bodyLoc); // Higher requirements if (this.getStat(sdk.stats.LevelReq) > mercenary.getStat(sdk.stats.Level) @@ -154,14 +154,14 @@ Unit.prototype.autoEquipCheck = function (checkCanEquip = true) { if (tier > 0 && bodyLoc.length) { for (let i = 0; i < bodyLoc.length; i += 1) { - if (tier > Item.getEquippedItem(bodyLoc[i]).tier + if (tier > Item.getEquipped(bodyLoc[i]).tier && ((checkCanEquip ? this.canEquip() : true) || !this.identified)) { if (this.twoHanded && !me.barbarian) { - if (tier < Item.getEquippedItem(sdk.body.RightArm).tier + Item.getEquippedItem(sdk.body.LeftArm).tier) return false; + if (tier < Item.getEquipped(sdk.body.RightArm).tier + Item.getEquipped(sdk.body.LeftArm).tier) return false; } - if (!me.barbarian && bodyLoc[i] === 5 && Item.getEquippedItem(bodyLoc[i]).tier === -1) { - if (Item.getEquippedItem(sdk.body.RightArm).twoHanded && tier < Item.getEquippedItem(sdk.body.RightArm).tier) return false; + if (!me.barbarian && bodyLoc[i] === 5 && Item.getEquipped(bodyLoc[i]).tier === -1) { + if (Item.getEquipped(sdk.body.RightArm).twoHanded && tier < Item.getEquipped(sdk.body.RightArm).tier) return false; } return true; @@ -181,14 +181,14 @@ Unit.prototype.autoEquipCheckSecondary = function (checkCanEquip = true) { if (tier > 0 && bodyLoc.length) { for (let i = 0; i < bodyLoc.length; i += 1) { - if (tier > Item.getEquippedItem(bodyLoc[i]).secondarytier + if (tier > Item.getEquipped(bodyLoc[i]).secondarytier && ((checkCanEquip ? this.canEquip() : true) || !this.identified)) { if (this.twoHanded && !me.barbarian) { - if (tier < Item.getEquippedItem(sdk.body.RightArmSecondary).secondarytier + Item.getEquippedItem(sdk.body.LeftArmSecondary).secondarytier) return false; + if (tier < Item.getEquipped(sdk.body.RightArmSecondary).secondarytier + Item.getEquipped(sdk.body.LeftArmSecondary).secondarytier) return false; } - if (!me.barbarian && bodyLoc[i] === 12 && Item.getEquippedItem(bodyLoc[i]).secondarytier === -1) { - if (Item.getEquippedItem(sdk.body.RightArmSecondary).twoHanded && tier < Item.getEquippedItem(sdk.body.RightArmSecondary).secondarytier) return false; + if (!me.barbarian && bodyLoc[i] === 12 && Item.getEquipped(bodyLoc[i]).secondarytier === -1) { + if (Item.getEquipped(sdk.body.RightArmSecondary).twoHanded && tier < Item.getEquipped(sdk.body.RightArmSecondary).secondarytier) return false; } return true; @@ -208,7 +208,7 @@ Unit.prototype.autoEquipCheckMerc = function (checkCanEquip = true) { if (tier > 0 && bodyLoc.length) { for (let i = 0; i < bodyLoc.length; i += 1) { - let oldTier = Item.getEquippedItemMerc(bodyLoc[i]).tier; + let oldTier = Item.getMercEquipped(bodyLoc[i]).tier; if (tier > oldTier && ((checkCanEquip ? this.canEquipMerc() : true) || !this.identified)) { return true; @@ -260,10 +260,10 @@ Unit.prototype.equipItem = function (bodyLoc = -1) { if (cursorItem) { // rollback check - let justEquipped = Item.getEquippedItem(bodyLoc); + let justEquipped = Item.getEquipped(bodyLoc); if (NTIP.GetTier(cursorItem) > justEquipped.tier && !this.questItem && !justEquipped.isRuneword/*Wierd bug with runewords that it'll fail to get correct item desc so don't attempt rollback*/) { console.debug("ROLLING BACK TO OLD ITEM BECAUSE IT WAS BETTER"); - console.debug("OldItem: " + NTIP.GetTier(cursorItem) + " Just Equipped Item: " + Item.getEquippedItem(bodyLoc).tier); + console.debug("OldItem: " + NTIP.GetTier(cursorItem) + " Just Equipped Item: " + Item.getEquipped(bodyLoc).tier); clickItemAndWait(sdk.clicktypes.click.item.Left, bodyLoc); cursorItem = Game.getCursorUnit(); rolledBack = true; diff --git a/libs/SoloPlay/Functions/ItemUtilities.js b/libs/SoloPlay/Functions/ItemUtilities.js index fe357970..2b427208 100644 --- a/libs/SoloPlay/Functions/ItemUtilities.js +++ b/libs/SoloPlay/Functions/ItemUtilities.js @@ -6,489 +6,490 @@ */ includeIfNotIncluded("common/Item.js"); - -// TODO: clean this up (sigh) - 8/10/22 - update refactored alot, still think more can be done though -const baseSkillsScore = (item, buildInfo) => { - buildInfo === undefined && (buildInfo = Check.currentBuild()); - let generalScore = 0; - let selectedWeights = [30, 20]; - let selectedSkills = [buildInfo.wantedSkills, buildInfo.usefulSkills]; - generalScore += item.getStatEx(sdk.stats.AddClassSkills, me.classid) * 200; // + class skills - generalScore += item.getStatEx(sdk.stats.AddSkillTab, buildInfo.tabSkills) * 100; // + TAB skills - todo handle array of tab skills - - for (let i = 0; i < selectedWeights.length; i++) { - for (let j = 0; j < selectedSkills.length; j++) { - for (let k = 0; k < selectedSkills[j].length; k++) { - generalScore += item.getStatEx(107, selectedSkills[j][k]) * selectedWeights[i]; +(function() { + // TODO: clean this up (sigh) - 8/10/22 - update refactored alot, still think more can be done though + const baseSkillsScore = (item, buildInfo) => { + buildInfo === undefined && (buildInfo = Check.currentBuild()); + let generalScore = 0; + let selectedWeights = [30, 20]; + let selectedSkills = [buildInfo.wantedSkills, buildInfo.usefulSkills]; + generalScore += item.getStatEx(sdk.stats.AddClassSkills, me.classid) * 200; // + class skills + generalScore += item.getStatEx(sdk.stats.AddSkillTab, buildInfo.tabSkills) * 100; // + TAB skills - todo handle array of tab skills + + for (let i = 0; i < selectedWeights.length; i++) { + for (let j = 0; j < selectedSkills.length; j++) { + for (let k = 0; k < selectedSkills[j].length; k++) { + generalScore += item.getStatEx(107, selectedSkills[j][k]) * selectedWeights[i]; + } } } - } - return generalScore; -}; - -Item.betterBaseThanWearing = function (base = undefined, verbose = Developer.debugging.baseCheck) { - if (!base || !base.isBaseType) return false; - - let name = ""; - let itemsResists, baseResists, itemsTotalDmg, baseDmg, itemsDefense, baseDefense; - let baseSkillsTier, equippedSkillsTier; - let result = false, preSocketCheck = false; - let bodyLoc = Item.getBodyLoc(base); - - const checkNoSockets = (item) => [ - sdk.locale.items.AncientsPledge, sdk.locale.items.Exile, sdk.locale.items.Lore, sdk.locale.items.White, sdk.locale.items.Rhyme - ].includes(item.prefixnum) || (item.prefixnum === sdk.locale.items.Spirit && item.getItemType() === "Shield"); - const getRes = (item) => item.getStatEx(sdk.stats.FireResist) + item.getStatEx(sdk.stats.LightResist) + item.getStatEx(sdk.stats.ColdResist) + item.getStatEx(sdk.stats.PoisonResist); - const getDmg = (item) => item.getStatEx(sdk.stats.MinDamage) + item.getStatEx(sdk.stats.MaxDamage); - const getRealDmg = (item, maxED = 0, minOffset = 0, maxOffset = 0) => { - let ED = item.getStatEx(sdk.stats.EnhancedDamage); - ED > maxED && (ED = maxED); - let itemsMinDmg = Math.ceil(((item.getStatEx(sdk.stats.MinDamage) - minOffset) / ((ED + 100) / 100))); - let itemsMaxDmg = Math.ceil(((item.getStatEx(sdk.stats.MaxDamage) - maxOffset) / ((ED + 100) / 100))); - return (itemsMinDmg + itemsMaxDmg); - }; - const getDef = (item) => item.getStatEx(sdk.stats.Defense); - const getRealDef = (item, maxEDef) => { - let ED = item.getStatEx(sdk.stats.ArmorPercent); - ED > maxEDef && (ED = maxEDef); - return (Math.ceil((item.getStatEx(sdk.stats.Defense) / ((ED + 100) / 100)))); - }; - const resCheck = (baseResists, itemsResists) => { - verbose && console.log("ÿc9BetterThanWearingCheckÿc0 :: RW(" + name + ") BaseResists: " + baseResists + " EquippedItem: " + itemsResists); - return (baseResists > itemsResists); - }; - const defCheck = (itemsDefense, baseDefense) => { - verbose && console.log("ÿc9BetterThanWearingCheckÿc0 :: RW(" + name + ") BaseDefense: " + baseDefense + " EquippedItem: " + itemsDefense); - return (baseDefense > itemsDefense); - }; - const dmgCheck = (itemsTotalDmg, baseDmg) => { - verbose && console.log("ÿc9BetterThanWearingCheckÿc0 :: RW(" + name + ") BaseDamage: " + baseDmg + " EquippedItem: " + itemsTotalDmg); - return (baseDmg > itemsTotalDmg); + return generalScore; }; - // @todo - betterThanMercUsing check for now just keep merc items - if ([sdk.items.type.Polearm, sdk.items.type.Spear].includes(base.itemType) || ([sdk.items.type.Armor].includes(base.itemType) && base.ethereal)) return true; - // Can't use so its worse then what we already have - if ((Check.finalBuild().maxStr < base.strreq || Check.finalBuild().maxDex < base.dexreq)) { - console.log("ÿc9BetterThanWearingCheckÿc0 :: " + base.name + " has to high stat requirments strReq: " + base.strreq + " dexReq " + base.dexreq); - return false; - } - // don't toss pb base crescent moon/HoJ/Grief - if (base.classid === sdk.items.PhaseBlade && [3, 4, 5].includes(base.sockets)) return true; - - let items = me.getItemsEx().filter(i => i.isEquipped && bodyLoc.includes(i.bodylocation)); - - for (let i = 0; i < bodyLoc.length; i++) { - let equippedItem = items.find(item => item.bodylocation === bodyLoc[i]); - if (!equippedItem || !equippedItem.runeword || NTIP.GetTier(equippedItem) >= NTIP.MAX_TIER) { - if (i === 0 && bodyLoc.length > 1) continue; - return true; + Item.betterBaseThanWearing = function (base = undefined, verbose = Developer.debugging.baseCheck) { + if (!base || !base.isBaseType) return false; + + let name = ""; + let itemsResists, baseResists, itemsTotalDmg, baseDmg, itemsDefense, baseDefense; + let baseSkillsTier, equippedSkillsTier; + let result = false, preSocketCheck = false; + let bodyLoc = Item.getBodyLoc(base); + + const checkNoSockets = (item) => [ + sdk.locale.items.AncientsPledge, sdk.locale.items.Exile, sdk.locale.items.Lore, sdk.locale.items.White, sdk.locale.items.Rhyme + ].includes(item.prefixnum) || (item.prefixnum === sdk.locale.items.Spirit && item.getItemType() === "Shield"); + const getRes = (item) => item.getStatEx(sdk.stats.FireResist) + item.getStatEx(sdk.stats.LightResist) + item.getStatEx(sdk.stats.ColdResist) + item.getStatEx(sdk.stats.PoisonResist); + const getDmg = (item) => item.getStatEx(sdk.stats.MinDamage) + item.getStatEx(sdk.stats.MaxDamage); + const getRealDmg = (item, maxED = 0, minOffset = 0, maxOffset = 0) => { + let ED = item.getStatEx(sdk.stats.EnhancedDamage); + ED > maxED && (ED = maxED); + let itemsMinDmg = Math.ceil(((item.getStatEx(sdk.stats.MinDamage) - minOffset) / ((ED + 100) / 100))); + let itemsMaxDmg = Math.ceil(((item.getStatEx(sdk.stats.MaxDamage) - maxOffset) / ((ED + 100) / 100))); + return (itemsMinDmg + itemsMaxDmg); + }; + const getDef = (item) => item.getStatEx(sdk.stats.Defense); + const getRealDef = (item, maxEDef) => { + let ED = item.getStatEx(sdk.stats.ArmorPercent); + ED > maxEDef && (ED = maxEDef); + return (Math.ceil((item.getStatEx(sdk.stats.Defense) / ((ED + 100) / 100)))); + }; + const resCheck = (baseResists, itemsResists) => { + verbose && console.log("ÿc9BetterThanWearingCheckÿc0 :: RW(" + name + ") BaseResists: " + baseResists + " EquippedItem: " + itemsResists); + return (baseResists > itemsResists); + }; + const defCheck = (itemsDefense, baseDefense) => { + verbose && console.log("ÿc9BetterThanWearingCheckÿc0 :: RW(" + name + ") BaseDefense: " + baseDefense + " EquippedItem: " + itemsDefense); + return (baseDefense > itemsDefense); + }; + const dmgCheck = (itemsTotalDmg, baseDmg) => { + verbose && console.log("ÿc9BetterThanWearingCheckÿc0 :: RW(" + name + ") BaseDamage: " + baseDmg + " EquippedItem: " + itemsTotalDmg); + return (baseDmg > itemsTotalDmg); + }; + + // @todo - betterThanMercUsing check for now just keep merc items + if ([sdk.items.type.Polearm, sdk.items.type.Spear].includes(base.itemType) || ([sdk.items.type.Armor].includes(base.itemType) && base.ethereal)) return true; + // Can't use so its worse then what we already have + if ((Check.finalBuild().maxStr < base.strreq || Check.finalBuild().maxDex < base.dexreq)) { + console.log("ÿc9BetterThanWearingCheckÿc0 :: " + base.name + " has to high stat requirments strReq: " + base.strreq + " dexReq " + base.dexreq); + return false; } - name = getLocaleString(equippedItem.prefixnum); + // don't toss pb base crescent moon/HoJ/Grief + if (base.classid === sdk.items.PhaseBlade && [3, 4, 5].includes(base.sockets)) return true; - preSocketCheck = checkNoSockets(equippedItem); - if (base.sockets === 0 && !preSocketCheck) return true; + let items = me.getItemsEx().filter(i => i.isEquipped && bodyLoc.includes(i.bodylocation)); - if (base.sockets === equippedItem.sockets || preSocketCheck) { - switch (equippedItem.prefixnum) { - case sdk.locale.items.AncientsPledge: - if (me.paladin) { - [itemsResists, baseResists] = [(getRes(equippedItem) - 187), getRes(base)]; - if (baseResists !== itemsResists && resCheck(baseResists, itemsResists)) return true; - } else { - [itemsDefense, baseDefense] = [getRealDef(equippedItem, 50), getDef(base)]; - if (baseDefense !== itemsDefense && defCheck(itemsDefense, baseDefense)) return true; - } - - break; - case sdk.locale.items.Black: - [itemsTotalDmg, baseDmg] = [getRealDmg(equippedItem, 120), getDmg(base)]; - if (baseDmg !== itemsTotalDmg && dmgCheck(itemsTotalDmg, baseDmg)) return true; - - break; - case sdk.locale.items.CrescentMoon: - [itemsTotalDmg, baseDmg] = [getRealDmg(equippedItem, 220), getDmg(base)]; - if (baseDmg !== itemsTotalDmg && dmgCheck(itemsTotalDmg, baseDmg)) return true; - - break; - case sdk.locale.items.Exile: - [itemsResists, baseResists] = [getRes(equippedItem), getRes(base)]; - if (baseResists !== itemsResists && resCheck(baseResists, itemsResists)) return true; - - break; - case sdk.locale.items.Honor: - [itemsTotalDmg, baseDmg] = [getRealDmg(equippedItem, 160, 9, 9), getDmg(base)]; - if (baseDmg !== itemsTotalDmg && dmgCheck(itemsTotalDmg, baseDmg)) return true; - - break; - case sdk.locale.items.KingsGrace: - [itemsTotalDmg, baseDmg] = [getRealDmg(equippedItem, 100), getDmg(base)]; - if (baseDmg !== itemsTotalDmg && dmgCheck(itemsTotalDmg, baseDmg)) return true; - - break; - case sdk.locale.items.Lawbringer: - [itemsTotalDmg, baseDmg] = [getRealDmg(equippedItem), getDmg(base)]; - if (baseDmg !== itemsTotalDmg && dmgCheck(itemsTotalDmg, baseDmg)) return true; - - break; - case sdk.locale.items.Lore: - [itemsDefense, baseDefense] = [getRealDef(equippedItem), getDef(base)]; - - if (me.barbarian || me.druid) { - // (PrimalHelms and Pelts) - [equippedSkillsTier, baseSkillsTier] = [baseSkillsScore(equippedItem), baseSkillsScore(base)]; - - verbose && console.log("ÿc9BetterThanWearingCheckÿc0 :: RW(Lore) EquippedSkillsTier: " + equippedSkillsTier + " BaseSkillsTier: " + baseSkillsTier); - if (equippedSkillsTier !== baseSkillsTier) { - // Might need to add some type of std deviation, having the skills is probably better but maybe not if in hell with a 50 defense helm - return (baseSkillsTier > equippedSkillsTier); - } else if (baseDefense !== itemsDefense) { - verbose && console.log("ÿc9BetterThanWearingCheckÿc0 :: RW(Lore) BaseDefense: " + baseDefense + " EquippedItem: " + itemsDefense); - return (baseDefense > itemsDefense); + for (let i = 0; i < bodyLoc.length; i++) { + let equippedItem = items.find(item => item.bodylocation === bodyLoc[i]); + if (!equippedItem || !equippedItem.runeword || NTIP.GetTier(equippedItem) >= NTIP.MAX_TIER) { + if (i === 0 && bodyLoc.length > 1) continue; + return true; + } + name = getLocaleString(equippedItem.prefixnum); + + preSocketCheck = checkNoSockets(equippedItem); + if (base.sockets === 0 && !preSocketCheck) return true; + + if (base.sockets === equippedItem.sockets || preSocketCheck) { + switch (equippedItem.prefixnum) { + case sdk.locale.items.AncientsPledge: + if (me.paladin) { + [itemsResists, baseResists] = [(getRes(equippedItem) - 187), getRes(base)]; + if (baseResists !== itemsResists && resCheck(baseResists, itemsResists)) return true; + } else { + [itemsDefense, baseDefense] = [getRealDef(equippedItem, 50), getDef(base)]; + if (baseDefense !== itemsDefense && defCheck(itemsDefense, baseDefense)) return true; } - } else { - if (baseDefense !== itemsDefense && defCheck(itemsDefense, baseDefense)) return true; - } - - break; - case sdk.locale.items.Malice: - [itemsTotalDmg, baseDmg] = [getRealDmg(equippedItem, 33, 9), getDmg(base)]; + + break; + case sdk.locale.items.Black: + [itemsTotalDmg, baseDmg] = [getRealDmg(equippedItem, 120), getDmg(base)]; + if (baseDmg !== itemsTotalDmg && dmgCheck(itemsTotalDmg, baseDmg)) return true; + + break; + case sdk.locale.items.CrescentMoon: + [itemsTotalDmg, baseDmg] = [getRealDmg(equippedItem, 220), getDmg(base)]; + if (baseDmg !== itemsTotalDmg && dmgCheck(itemsTotalDmg, baseDmg)) return true; + + break; + case sdk.locale.items.Exile: + [itemsResists, baseResists] = [getRes(equippedItem), getRes(base)]; + if (baseResists !== itemsResists && resCheck(baseResists, itemsResists)) return true; - if (me.paladin) { - // Paladin TODO: See if its worth it to calculate the added damage skills would add - [equippedSkillsTier, baseSkillsTier] = [baseSkillsScore(equippedItem), baseSkillsScore(base)]; - } + break; + case sdk.locale.items.Honor: + [itemsTotalDmg, baseDmg] = [getRealDmg(equippedItem, 160, 9, 9), getDmg(base)]; + if (baseDmg !== itemsTotalDmg && dmgCheck(itemsTotalDmg, baseDmg)) return true; - if (baseDmg !== itemsTotalDmg && dmgCheck(itemsTotalDmg, baseDmg)) return true; + break; + case sdk.locale.items.KingsGrace: + [itemsTotalDmg, baseDmg] = [getRealDmg(equippedItem, 100), getDmg(base)]; + if (baseDmg !== itemsTotalDmg && dmgCheck(itemsTotalDmg, baseDmg)) return true; - break; - case sdk.locale.items.Rhyme: - if (me.necromancer) { - [equippedSkillsTier, baseSkillsTier] = [baseSkillsScore(equippedItem), baseSkillsScore(base)]; + break; + case sdk.locale.items.Lawbringer: + [itemsTotalDmg, baseDmg] = [getRealDmg(equippedItem), getDmg(base)]; + if (baseDmg !== itemsTotalDmg && dmgCheck(itemsTotalDmg, baseDmg)) return true; - if (equippedSkillsTier !== baseSkillsTier) { - verbose && console.log("ÿc9BetterThanWearingCheckÿc0 :: RW(Rhyme) EquippedSkillsTier: " + equippedSkillsTier + " BaseSkillsTier: " + baseSkillsTier); - // Might need to add some type of std deviation, having the skills is probably better but maybe not if in hell with a 50 defense shield - if (baseSkillsTier > equippedSkillsTier) return true; - } else if (equippedSkillsTier === baseSkillsTier) { - [itemsDefense, baseDefense] = [getRealDef(equippedItem), getDef(base)]; + break; + case sdk.locale.items.Lore: + [itemsDefense, baseDefense] = [getRealDef(equippedItem), getDef(base)]; + + if (me.barbarian || me.druid) { + // (PrimalHelms and Pelts) + [equippedSkillsTier, baseSkillsTier] = [baseSkillsScore(equippedItem), baseSkillsScore(base)]; + + verbose && console.log("ÿc9BetterThanWearingCheckÿc0 :: RW(Lore) EquippedSkillsTier: " + equippedSkillsTier + " BaseSkillsTier: " + baseSkillsTier); + if (equippedSkillsTier !== baseSkillsTier) { + // Might need to add some type of std deviation, having the skills is probably better but maybe not if in hell with a 50 defense helm + return (baseSkillsTier > equippedSkillsTier); + } else if (baseDefense !== itemsDefense) { + verbose && console.log("ÿc9BetterThanWearingCheckÿc0 :: RW(Lore) BaseDefense: " + baseDefense + " EquippedItem: " + itemsDefense); + return (baseDefense > itemsDefense); + } + } else { if (baseDefense !== itemsDefense && defCheck(itemsDefense, baseDefense)) return true; } - } else if (me.paladin) { - [itemsResists, baseResists] = [(getRes(equippedItem) - 100), getRes(base)]; - if (baseResists !== itemsResists && resCheck(baseResists, itemsResists)) return true; - } - break; - case sdk.locale.items.Rift: - [itemsTotalDmg, baseDmg] = [getRealDmg(equippedItem), getDmg(base)]; - if (baseDmg !== itemsTotalDmg && dmgCheck(itemsTotalDmg, baseDmg)) return true; + break; + case sdk.locale.items.Malice: + [itemsTotalDmg, baseDmg] = [getRealDmg(equippedItem, 33, 9), getDmg(base)]; - break; - case sdk.locale.items.Spirit: - if (!me.paladin || bodyLoc[i] !== sdk.body.LeftArm || base.getItemType() !== "Shield") return true; + if (me.paladin) { + // Paladin TODO: See if its worth it to calculate the added damage skills would add + [equippedSkillsTier, baseSkillsTier] = [baseSkillsScore(equippedItem), baseSkillsScore(base)]; + } + + if (baseDmg !== itemsTotalDmg && dmgCheck(itemsTotalDmg, baseDmg)) return true; + + break; + case sdk.locale.items.Rhyme: + if (me.necromancer) { + [equippedSkillsTier, baseSkillsTier] = [baseSkillsScore(equippedItem), baseSkillsScore(base)]; + + if (equippedSkillsTier !== baseSkillsTier) { + verbose && console.log("ÿc9BetterThanWearingCheckÿc0 :: RW(Rhyme) EquippedSkillsTier: " + equippedSkillsTier + " BaseSkillsTier: " + baseSkillsTier); + // Might need to add some type of std deviation, having the skills is probably better but maybe not if in hell with a 50 defense shield + if (baseSkillsTier > equippedSkillsTier) return true; + } else if (equippedSkillsTier === baseSkillsTier) { + [itemsDefense, baseDefense] = [getRealDef(equippedItem), getDef(base)]; + if (baseDefense !== itemsDefense && defCheck(itemsDefense, baseDefense)) return true; + } + } else if (me.paladin) { + [itemsResists, baseResists] = [(getRes(equippedItem) - 100), getRes(base)]; + if (baseResists !== itemsResists && resCheck(baseResists, itemsResists)) return true; + } + + break; + case sdk.locale.items.Rift: + [itemsTotalDmg, baseDmg] = [getRealDmg(equippedItem), getDmg(base)]; + if (baseDmg !== itemsTotalDmg && dmgCheck(itemsTotalDmg, baseDmg)) return true; + + break; + case sdk.locale.items.Spirit: + if (!me.paladin || bodyLoc[i] !== sdk.body.LeftArm || base.getItemType() !== "Shield") return true; - [itemsResists, baseResists] = [(getRes(equippedItem) - 115), getRes(base)]; - if (baseResists !== itemsResists && resCheck(baseResists, itemsResists)) return true; - - break; - case sdk.locale.items.Steel: - [itemsTotalDmg, baseDmg] = [getRealDmg(equippedItem, 20, 3, 3), getDmg(base)]; - if (baseDmg !== itemsTotalDmg && dmgCheck(itemsTotalDmg, baseDmg)) return true; - - break; - case sdk.locale.items.Strength: - [itemsTotalDmg, baseDmg] = [getRealDmg(equippedItem, 35, 9, 9), getDmg(base)]; - if (baseDmg !== itemsTotalDmg && dmgCheck(itemsTotalDmg, baseDmg)) return true; - - break; - case sdk.locale.items.White: - if (me.necromancer) { - [equippedSkillsTier, baseSkillsTier] = [(baseSkillsScore(equippedItem) - 550), baseSkillsScore(base)]; - - if (equippedSkillsTier !== baseSkillsTier) { - verbose && console.log("ÿc9BetterThanWearingCheckÿc0 :: RW(White) EquippedSkillsTier: " + equippedSkillsTier + " BaseSkillsTier: " + baseSkillsTier); - if (baseSkillsTier > equippedSkillsTier) return true; + [itemsResists, baseResists] = [(getRes(equippedItem) - 115), getRes(base)]; + if (baseResists !== itemsResists && resCheck(baseResists, itemsResists)) return true; + + break; + case sdk.locale.items.Steel: + [itemsTotalDmg, baseDmg] = [getRealDmg(equippedItem, 20, 3, 3), getDmg(base)]; + if (baseDmg !== itemsTotalDmg && dmgCheck(itemsTotalDmg, baseDmg)) return true; + + break; + case sdk.locale.items.Strength: + [itemsTotalDmg, baseDmg] = [getRealDmg(equippedItem, 35, 9, 9), getDmg(base)]; + if (baseDmg !== itemsTotalDmg && dmgCheck(itemsTotalDmg, baseDmg)) return true; + + break; + case sdk.locale.items.White: + if (me.necromancer) { + [equippedSkillsTier, baseSkillsTier] = [(baseSkillsScore(equippedItem) - 550), baseSkillsScore(base)]; + + if (equippedSkillsTier !== baseSkillsTier) { + verbose && console.log("ÿc9BetterThanWearingCheckÿc0 :: RW(White) EquippedSkillsTier: " + equippedSkillsTier + " BaseSkillsTier: " + baseSkillsTier); + if (baseSkillsTier > equippedSkillsTier) return true; + } } - } - break; - case sdk.locale.items.Stone: - [itemsDefense, baseDefense] = [getRealDef(equippedItem, 290), getDef(base)]; - if (baseDefense !== itemsDefense && defCheck(itemsDefense, baseDefense)) return true; - - break; - case sdk.locale.items.Smoke: - [itemsDefense, baseDefense] = [getRealDef(equippedItem, 75), getDef(base)]; - if (baseDefense !== itemsDefense && defCheck(itemsDefense, baseDefense)) return true; - - break; - case sdk.locale.items.Prudence: - [itemsDefense, baseDefense] = [getRealDef(equippedItem, 170), getDef(base)]; - if (baseDefense !== itemsDefense && defCheck(itemsDefense, baseDefense)) return true; - - break; - case sdk.locale.items.Gloom: - [itemsDefense, baseDefense] = [getRealDef(equippedItem, 260), getDef(base)]; - if (baseDefense !== itemsDefense && defCheck(itemsDefense, baseDefense)) return true; - - break; - case sdk.locale.items.Fortitude: - [itemsDefense, baseDefense] = [getRealDef(equippedItem, 200), getDef(base)]; - if (baseDefense !== itemsDefense && defCheck(itemsDefense, baseDefense)) return true; - - break; - case sdk.locale.items.Enlightenment: - [itemsDefense, baseDefense] = [getRealDef(equippedItem, 30), getDef(base)]; - if (baseDefense !== itemsDefense && defCheck(itemsDefense, baseDefense)) return true; - - break; - case sdk.locale.items.Duress: - [itemsDefense, baseDefense] = [getRealDef(equippedItem, 200), getDef(base)]; - if (baseDefense !== itemsDefense && defCheck(itemsDefense, baseDefense)) return true; - - break; - case sdk.locale.items.ChainsofHonor: - [itemsDefense, baseDefense] = [getRealDef(equippedItem, 70), getDef(base)]; - if (baseDefense !== itemsDefense && defCheck(itemsDefense, baseDefense)) return true; - - break; - case sdk.locale.items.Bramble: - case sdk.locale.items.Bone: - case sdk.locale.items.Dragon: - case sdk.locale.items.Enigma: - case sdk.locale.items.Lionheart: - case sdk.locale.items.Myth: - case sdk.locale.items.Peace: - case sdk.locale.items.Principle: - case sdk.locale.items.Rain: - case sdk.locale.items.Stealth: - case sdk.locale.items.Treachery: - case sdk.locale.items.Wealth: - [itemsDefense, baseDefense] = [getRealDef(equippedItem), getDef(base)]; - if (baseDefense !== itemsDefense && defCheck(itemsDefense, baseDefense)) return true; - - break; - default: - // Runeword base isn't in the list, keep the base - return true; + break; + case sdk.locale.items.Stone: + [itemsDefense, baseDefense] = [getRealDef(equippedItem, 290), getDef(base)]; + if (baseDefense !== itemsDefense && defCheck(itemsDefense, baseDefense)) return true; + + break; + case sdk.locale.items.Smoke: + [itemsDefense, baseDefense] = [getRealDef(equippedItem, 75), getDef(base)]; + if (baseDefense !== itemsDefense && defCheck(itemsDefense, baseDefense)) return true; + + break; + case sdk.locale.items.Prudence: + [itemsDefense, baseDefense] = [getRealDef(equippedItem, 170), getDef(base)]; + if (baseDefense !== itemsDefense && defCheck(itemsDefense, baseDefense)) return true; + + break; + case sdk.locale.items.Gloom: + [itemsDefense, baseDefense] = [getRealDef(equippedItem, 260), getDef(base)]; + if (baseDefense !== itemsDefense && defCheck(itemsDefense, baseDefense)) return true; + + break; + case sdk.locale.items.Fortitude: + [itemsDefense, baseDefense] = [getRealDef(equippedItem, 200), getDef(base)]; + if (baseDefense !== itemsDefense && defCheck(itemsDefense, baseDefense)) return true; + + break; + case sdk.locale.items.Enlightenment: + [itemsDefense, baseDefense] = [getRealDef(equippedItem, 30), getDef(base)]; + if (baseDefense !== itemsDefense && defCheck(itemsDefense, baseDefense)) return true; + + break; + case sdk.locale.items.Duress: + [itemsDefense, baseDefense] = [getRealDef(equippedItem, 200), getDef(base)]; + if (baseDefense !== itemsDefense && defCheck(itemsDefense, baseDefense)) return true; + + break; + case sdk.locale.items.ChainsofHonor: + [itemsDefense, baseDefense] = [getRealDef(equippedItem, 70), getDef(base)]; + if (baseDefense !== itemsDefense && defCheck(itemsDefense, baseDefense)) return true; + + break; + case sdk.locale.items.Bramble: + case sdk.locale.items.Bone: + case sdk.locale.items.Dragon: + case sdk.locale.items.Enigma: + case sdk.locale.items.Lionheart: + case sdk.locale.items.Myth: + case sdk.locale.items.Peace: + case sdk.locale.items.Principle: + case sdk.locale.items.Rain: + case sdk.locale.items.Stealth: + case sdk.locale.items.Treachery: + case sdk.locale.items.Wealth: + [itemsDefense, baseDefense] = [getRealDef(equippedItem), getDef(base)]; + if (baseDefense !== itemsDefense && defCheck(itemsDefense, baseDefense)) return true; + + break; + default: + // Runeword base isn't in the list, keep the base + return true; + } } } - } - return result; -}; - -Item.betterThanStashed = function (base, verbose) { - if (!base || base.quality > sdk.items.quality.Superior || base.isRuneword) return false; - if (base.sockets === 0 && getBaseStat("items", base.classid, "gemsockets") <= 1) return false; - if (base.sockets === 1) return false; - verbose === undefined && (verbose = Developer.debugging.baseCheck); - - const defenseScore = (item) => ({ - def: item.getStatEx(sdk.stats.Defense), - eDef: item.getStatEx(sdk.stats.ArmorPercent) - }); - - const dmgScore = (item) => ({ - dmg: Math.round((item.getStatEx(sdk.stats.MinDamagePercent) + item.getStatEx(sdk.stats.MaxDamagePercent)) / 2), - twoHandDmg: Math.round((item.getStatEx(sdk.stats.SecondaryMinDamage) + item.getStatEx(sdk.stats.SecondaryMaxDamage)) / 2), - eDmg: item.getStatEx(sdk.stats.EnhancedDamage) - }); - - const generalScore = (item) => { - const buildInfo = Check.currentBuild(); - let generalScore = baseSkillsScore(item, buildInfo); + return result; + }; + + Item.betterThanStashed = function (base, verbose) { + if (!base || base.quality > sdk.items.quality.Superior || base.isRuneword) return false; + if (base.sockets === 0 && getBaseStat("items", base.classid, "gemsockets") <= 1) return false; + if (base.sockets === 1) return false; + verbose === undefined && (verbose = Developer.debugging.baseCheck); + + const defenseScore = (item) => ({ + def: item.getStatEx(sdk.stats.Defense), + eDef: item.getStatEx(sdk.stats.ArmorPercent) + }); + + const dmgScore = (item) => ({ + dmg: Math.round((item.getStatEx(sdk.stats.MinDamagePercent) + item.getStatEx(sdk.stats.MaxDamagePercent)) / 2), + twoHandDmg: Math.round((item.getStatEx(sdk.stats.SecondaryMinDamage) + item.getStatEx(sdk.stats.SecondaryMaxDamage)) / 2), + eDmg: item.getStatEx(sdk.stats.EnhancedDamage) + }); + + const generalScore = (item) => { + const buildInfo = Check.currentBuild(); + let generalScore = baseSkillsScore(item, buildInfo); - if (generalScore === 0) { + if (generalScore === 0) { // should take into account other skills that would be helpful for the current build - me.paladin && (generalScore += item.getStatEx(sdk.stats.FireResist) * 2); - generalScore += item.getStatEx(sdk.stats.Defense) * 0.5; + me.paladin && (generalScore += item.getStatEx(sdk.stats.FireResist) * 2); + generalScore += item.getStatEx(sdk.stats.Defense) * 0.5; - if (!buildInfo.caster) { - let dmg = dmgScore(item); - generalScore += (dmg.dmg + dmg.eDmg); + if (!buildInfo.caster) { + let dmg = dmgScore(item); + generalScore += (dmg.dmg + dmg.eDmg); + } } - } - - return generalScore; - }; - /** - * @todo - better comparison for socketed items to unsocketed items - * - should compare items with same sockets - * - should compare some items (wands, voodooheads, primalhelms, pelts) with no sockets to ones with sockets - * - should be able to compare regular items to class items and take into account the max amount of sockets an item can have - * - */ - function getItemToCompare (itemtypes = [], eth = null, sort = (a, b) => a - b) { - return me.getItemsEx(-1, sdk.items.mode.inStorage) - .filter(item => - itemtypes.includes(item.itemType) + return generalScore; + }; + + /** + * @todo - better comparison for socketed items to unsocketed items + * - should compare items with same sockets + * - should compare some items (wands, voodooheads, primalhelms, pelts) with no sockets to ones with sockets + * - should be able to compare regular items to class items and take into account the max amount of sockets an item can have + * + */ + function getItemToCompare (itemtypes = [], eth = null, sort = (a, b) => a - b) { + return me.getItemsEx(-1, sdk.items.mode.inStorage) + .filter(item => + itemtypes.includes(item.itemType) && ((item.sockets === base.sockets) || (item.sockets > base.sockets)) && (eth === null || item.ethereal === eth)) - .sort(sort) - .last(); - } - - const defenseSort = (a, b) => { - let [aDef, bDef] = [a.getStatEx(sdk.stats.Defense), b.getStatEx(sdk.stats.Defense)]; - if (aDef !== bDef) return aDef - bDef; - return a.getStatEx(sdk.stats.ArmorPercent) - b.getStatEx(sdk.stats.ArmorPercent); - }; + .sort(sort) + .last(); + } - const generalScoreSort = (a, b) => { - let [aScore, bScore] = [generalScore(a), generalScore(b)]; - if (aScore !== bScore) return aScore - bScore; - let [aSoc, bSoc] = [a.sockets, b.sockets]; - if (aSoc !== bSoc) return aSoc - bSoc; - if (a.getItemType() !== "Weapon" && aScore === bScore) { + const defenseSort = (a, b) => { let [aDef, bDef] = [a.getStatEx(sdk.stats.Defense), b.getStatEx(sdk.stats.Defense)]; if (aDef !== bDef) return aDef - bDef; - if (aDef === bDef) a.getStatEx(sdk.stats.ArmorPercent) - b.getStatEx(sdk.stats.ArmorPercent); - } - return a.sockets - b.sockets; - }; - - const twoHandDmgSort = (a, b) => { - let [aDmg, bDmg] = [dmgScore(a), dmgScore(b)]; - if (aDmg.twoHandDmg !== bDmg.twoHandDmg) return aDmg.twoHandDmg - bDmg.twoHandDmg; - return aDmg.eDmg - bDmg.eDmg; - }; + return a.getStatEx(sdk.stats.ArmorPercent) - b.getStatEx(sdk.stats.ArmorPercent); + }; + + const generalScoreSort = (a, b) => { + let [aScore, bScore] = [generalScore(a), generalScore(b)]; + if (aScore !== bScore) return aScore - bScore; + let [aSoc, bSoc] = [a.sockets, b.sockets]; + if (aSoc !== bSoc) return aSoc - bSoc; + if (a.getItemType() !== "Weapon" && aScore === bScore) { + let [aDef, bDef] = [a.getStatEx(sdk.stats.Defense), b.getStatEx(sdk.stats.Defense)]; + if (aDef !== bDef) return aDef - bDef; + if (aDef === bDef) a.getStatEx(sdk.stats.ArmorPercent) - b.getStatEx(sdk.stats.ArmorPercent); + } + return a.sockets - b.sockets; + }; + + const twoHandDmgSort = (a, b) => { + let [aDmg, bDmg] = [dmgScore(a), dmgScore(b)]; + if (aDmg.twoHandDmg !== bDmg.twoHandDmg) return aDmg.twoHandDmg - bDmg.twoHandDmg; + return aDmg.eDmg - bDmg.eDmg; + }; + + const defenseScoreCheck = (base, itemToCheck) => { + let [defScoreBase, defScoreItem] = [defenseScore(base), defenseScore(itemToCheck)]; + verbose && console.log("ÿc9betterThanStashedÿc0 :: BaseScore: ", defScoreBase, " itemToCheckScore: ", defScoreItem); + if (defScoreBase.def > defScoreItem.def || (defScoreBase.def === defScoreItem.def && (defScoreBase.eDef > defScoreItem.eDef || base.ilvl > itemToCheck.ilvl))) { + return true; + } + return false; + }; - const defenseScoreCheck = (base, itemToCheck) => { - let [defScoreBase, defScoreItem] = [defenseScore(base), defenseScore(itemToCheck)]; - verbose && console.log("ÿc9betterThanStashedÿc0 :: BaseScore: ", defScoreBase, " itemToCheckScore: ", defScoreItem); - if (defScoreBase.def > defScoreItem.def || (defScoreBase.def === defScoreItem.def && (defScoreBase.eDef > defScoreItem.eDef || base.ilvl > itemToCheck.ilvl))) { - return true; - } - return false; - }; + const damageScoreCheck = (base, itemToCheck) => { + let [gScoreBase, gScoreCheck] = [generalScore(base), generalScore(itemToCheck)]; + verbose && console.log("ÿc9betterThanStashedÿc0 :: BaseScore: " + generalScore(base) + " itemToCheckScore: " + generalScore(itemToCheck)); + switch (true) { + case (gScoreBase > gScoreCheck || (gScoreBase === gScoreCheck && base.ilvl > itemToCheck.ilvl)): + case (me.barbarian && (gScoreBase === gScoreCheck && Item.getQuantityOwned(base) < 2)): + case (me.assassin && !Check.currentBuild().caster && (gScoreBase === gScoreCheck && Item.getQuantityOwned(base) < 2)): + return true; + } + return false; + }; + + const generalScoreCheck = (base, itemToCheck) => { + let [gScoreBase, gScoreCheck] = [generalScore(base), generalScore(itemToCheck)]; + verbose && console.log("ÿc9betterThanStashedÿc0 :: BaseScore: " + generalScore(base) + " itemToCheckScore: " + generalScore(itemToCheck)); + if (gScoreBase > gScoreCheck) return true; + if (base.getItemType() === "Shield" && gScoreBase === gScoreCheck) return defenseScoreCheck(base, itemToCheck); + return false; + }; + + let checkItem; + + switch (base.itemType) { + case sdk.items.type.Shield: + case sdk.items.type.AuricShields: + case sdk.items.type.VoodooHeads: + if (me.paladin || me.necromancer) { + let iType = [sdk.items.type.Shield]; + me.necromancer ? iType.push(sdk.items.type.VoodooHeads) : iType.push(sdk.items.type.AuricShields); + + checkItem = getItemToCompare(iType, false, generalScoreSort); + if (checkItem === undefined || checkItem.gid === base.gid) return true; - const damageScoreCheck = (base, itemToCheck) => { - let [gScoreBase, gScoreCheck] = [generalScore(base), generalScore(itemToCheck)]; - verbose && console.log("ÿc9betterThanStashedÿc0 :: BaseScore: " + generalScore(base) + " itemToCheckScore: " + generalScore(itemToCheck)); - switch (true) { - case (gScoreBase > gScoreCheck || (gScoreBase === gScoreCheck && base.ilvl > itemToCheck.ilvl)): - case (me.barbarian && (gScoreBase === gScoreCheck && Item.getQuantityOwned(base) < 2)): - case (me.assassin && !Check.currentBuild().caster && (gScoreBase === gScoreCheck && Item.getQuantityOwned(base) < 2)): - return true; - } - return false; - }; + return (base.isInStorage ? generalScoreCheck(base, checkItem) : false); + } - const generalScoreCheck = (base, itemToCheck) => { - let [gScoreBase, gScoreCheck] = [generalScore(base), generalScore(itemToCheck)]; - verbose && console.log("ÿc9betterThanStashedÿc0 :: BaseScore: " + generalScore(base) + " itemToCheckScore: " + generalScore(itemToCheck)); - if (gScoreBase > gScoreCheck) return true; - if (base.getItemType() === "Shield" && gScoreBase === gScoreCheck) return defenseScoreCheck(base, itemToCheck); - return false; - }; + if (base.ethereal || base.sockets === 0) return false; + checkItem = getItemToCompare([sdk.items.type.Shield], false, defenseSort); + if (checkItem === undefined || checkItem.gid === base.gid) return true; + + return (base.isInStorage ? defenseScoreCheck(base, checkItem) : false); + case sdk.items.type.Armor: + if (base.ethereal || base.sockets === 0) return false; + checkItem = getItemToCompare([sdk.items.type.Armor], false, defenseSort); + if (checkItem === undefined || checkItem.gid === base.gid) return true; + + return (base.isInStorage ? defenseScoreCheck(base, checkItem) : false); + case sdk.items.type.Helm: + case sdk.items.type.PrimalHelm: + case sdk.items.type.Circlet: + case sdk.items.type.Pelt: + if (me.barbarian || me.druid) { + let iType = [sdk.items.type.Helm, sdk.items.type.Circlet]; + me.druid ? iType.push(sdk.items.type.Pelt) : iType.push(sdk.items.type.PrimalHelm); + + checkItem = getItemToCompare(iType, false, generalScoreSort); + if (checkItem === undefined || checkItem.gid === base.gid) return true; - let checkItem; + return (base.isInStorage ? generalScoreCheck(base, checkItem) : false); + } - switch (base.itemType) { - case sdk.items.type.Shield: - case sdk.items.type.AuricShields: - case sdk.items.type.VoodooHeads: - if (me.paladin || me.necromancer) { - let iType = [sdk.items.type.Shield]; - me.necromancer ? iType.push(sdk.items.type.VoodooHeads) : iType.push(sdk.items.type.AuricShields); - - checkItem = getItemToCompare(iType, false, generalScoreSort); + if (base.ethereal || base.sockets === 0) return false; + checkItem = getItemToCompare([sdk.items.type.Helm, sdk.items.type.Circlet], false, defenseSort); if (checkItem === undefined || checkItem.gid === base.gid) return true; + + return (base.isInStorage ? defenseScoreCheck(base, checkItem) : false); + case sdk.items.type.Wand: + if (!me.necromancer) return false; + checkItem = getItemToCompare([sdk.items.type.Wand], null, generalScoreSort); + if (checkItem === undefined || checkItem.gid === base.gid) return true; return (base.isInStorage ? generalScoreCheck(base, checkItem) : false); - } + case sdk.items.type.Scepter: + case sdk.items.type.Staff: + case sdk.items.type.Bow: + case sdk.items.type.Axe: + case sdk.items.type.Club: + case sdk.items.type.Sword: + case sdk.items.type.Hammer: + case sdk.items.type.Knife: + case sdk.items.type.Spear: + case sdk.items.type.Crossbow: + case sdk.items.type.Mace: + case sdk.items.type.Orb: + case sdk.items.type.AmazonBow: + case sdk.items.type.AmazonSpear: + // don't toss grief base + // Can't use so it's worse then what we already have + // todo - need better solution to know what the max stats are for our current build and wanted final build + // update - 8/8/2022 - checks final build stat requirements but still need a better check + // don't keep an item if we are only going to be able to use it when we get to our final build but also sometimes like paladin making grief + // we need the item to get to our final build but won't actually be able to use it till then so we can't just use max current build str/dex + if ((Check.finalBuild().maxStr < base.strreq || Check.finalBuild().maxDex < base.dexreq)) return false; + // need better solution for comparison based on what runeword can be made in a base type + // should allow comparing multiple item types given they are all for the same runeword + checkItem = getItemToCompare([base.itemType], false, generalScoreSort); + if (checkItem === undefined || checkItem.gid === base.gid) return true; - if (base.ethereal || base.sockets === 0) return false; - checkItem = getItemToCompare([sdk.items.type.Shield], false, defenseSort); - if (checkItem === undefined || checkItem.gid === base.gid) return true; - - return (base.isInStorage ? defenseScoreCheck(base, checkItem) : false); - case sdk.items.type.Armor: - if (base.ethereal || base.sockets === 0) return false; - checkItem = getItemToCompare([sdk.items.type.Armor], false, defenseSort); - if (checkItem === undefined || checkItem.gid === base.gid) return true; - - return (base.isInStorage ? defenseScoreCheck(base, checkItem) : false); - case sdk.items.type.Helm: - case sdk.items.type.PrimalHelm: - case sdk.items.type.Circlet: - case sdk.items.type.Pelt: - if (me.barbarian || me.druid) { - let iType = [sdk.items.type.Helm, sdk.items.type.Circlet]; - me.druid ? iType.push(sdk.items.type.Pelt) : iType.push(sdk.items.type.PrimalHelm); - - checkItem = getItemToCompare(iType, false, generalScoreSort); + return (base.isInStorage ? damageScoreCheck(base, checkItem) : false); + case sdk.items.type.HandtoHand: + case sdk.items.type.AssassinClaw: + if (!me.assassin) return false; + + checkItem = getItemToCompare([sdk.items.type.HandtoHand, sdk.items.type.AssassinClaw], false, generalScoreSort); if (checkItem === undefined || checkItem.gid === base.gid) return true; - return (base.isInStorage ? generalScoreCheck(base, checkItem) : false); - } + return (base.isInStorage ? damageScoreCheck(base, checkItem) : false); + case sdk.items.type.Polearm: + checkItem = getItemToCompare([sdk.items.type.Polearm], null, twoHandDmgSort); + if (checkItem === undefined || checkItem.gid === base.gid) return true; - if (base.ethereal || base.sockets === 0) return false; - checkItem = getItemToCompare([sdk.items.type.Helm, sdk.items.type.Circlet], false, defenseSort); - if (checkItem === undefined || checkItem.gid === base.gid) return true; - - return (base.isInStorage ? defenseScoreCheck(base, checkItem) : false); - case sdk.items.type.Wand: - if (!me.necromancer) return false; - - checkItem = getItemToCompare([sdk.items.type.Wand], null, generalScoreSort); - if (checkItem === undefined || checkItem.gid === base.gid) return true; - return (base.isInStorage ? generalScoreCheck(base, checkItem) : false); - case sdk.items.type.Scepter: - case sdk.items.type.Staff: - case sdk.items.type.Bow: - case sdk.items.type.Axe: - case sdk.items.type.Club: - case sdk.items.type.Sword: - case sdk.items.type.Hammer: - case sdk.items.type.Knife: - case sdk.items.type.Spear: - case sdk.items.type.Crossbow: - case sdk.items.type.Mace: - case sdk.items.type.Orb: - case sdk.items.type.AmazonBow: - case sdk.items.type.AmazonSpear: - // don't toss grief base - // Can't use so it's worse then what we already have - // todo - need better solution to know what the max stats are for our current build and wanted final build - // update - 8/8/2022 - checks final build stat requirements but still need a better check - // don't keep an item if we are only going to be able to use it when we get to our final build but also sometimes like paladin making grief - // we need the item to get to our final build but won't actually be able to use it till then so we can't just use max current build str/dex - if ((Check.finalBuild().maxStr < base.strreq || Check.finalBuild().maxDex < base.dexreq)) return false; - // need better solution for comparison based on what runeword can be made in a base type - // should allow comparing multiple item types given they are all for the same runeword - checkItem = getItemToCompare([base.itemType], false, generalScoreSort); - if (checkItem === undefined || checkItem.gid === base.gid) return true; - - return (base.isInStorage ? damageScoreCheck(base, checkItem) : false); - case sdk.items.type.HandtoHand: - case sdk.items.type.AssassinClaw: - if (!me.assassin) return false; - - checkItem = getItemToCompare([sdk.items.type.HandtoHand, sdk.items.type.AssassinClaw], false, generalScoreSort); - if (checkItem === undefined || checkItem.gid === base.gid) return true; - - return (base.isInStorage ? damageScoreCheck(base, checkItem) : false); - case sdk.items.type.Polearm: - checkItem = getItemToCompare([sdk.items.type.Polearm], null, twoHandDmgSort); - if (checkItem === undefined || checkItem.gid === base.gid) return true; - - if (base.isInStorage && base.sockets > 0) { - let [baseDmg, checkItemDmg] = [dmgScore(base), dmgScore(checkItem)]; - switch (true) { - case (baseDmg.twoHandDmg > checkItemDmg.twoHandDmg): - case ((baseDmg.twoHandDmg === checkItemDmg.twoHandDmg) && (baseDmg.eDmg > checkItemDmg.eDmg)): - case ((baseDmg.twoHandDmg === checkItemDmg.twoHandDmg) && (baseDmg.eDmg === checkItemDmg.eDmg) && base.ilvl > checkItem.ilvl): - verbose && console.log("ÿc9betterThanStashedÿc0 :: BaseScore: ", baseDmg, " itemToCheckScore: ", checkItemDmg); - return true; + if (base.isInStorage && base.sockets > 0) { + let [baseDmg, checkItemDmg] = [dmgScore(base), dmgScore(checkItem)]; + switch (true) { + case (baseDmg.twoHandDmg > checkItemDmg.twoHandDmg): + case ((baseDmg.twoHandDmg === checkItemDmg.twoHandDmg) && (baseDmg.eDmg > checkItemDmg.eDmg)): + case ((baseDmg.twoHandDmg === checkItemDmg.twoHandDmg) && (baseDmg.eDmg === checkItemDmg.eDmg) && base.ilvl > checkItem.ilvl): + verbose && console.log("ÿc9betterThanStashedÿc0 :: BaseScore: ", baseDmg, " itemToCheckScore: ", checkItemDmg); + return true; + } } - } - break; - } + break; + } - return false; -}; + return false; + }; +})(); diff --git a/libs/SoloPlay/Functions/LoaderOverrides.js b/libs/SoloPlay/Functions/LoaderOverrides.js index b26167b5..40ac85b4 100644 --- a/libs/SoloPlay/Functions/LoaderOverrides.js +++ b/libs/SoloPlay/Functions/LoaderOverrides.js @@ -1,12 +1,11 @@ /** * @filename LoaderOverrides.js -* @author kolton -* @desc script loader, based on mBot's Sequencer.js, modifed by theBGuy for SoloPlay +* @author theBGuy +* @credit kolton +* @desc script loader, based on mBot's Sequencer.js * */ -// TODO: make this a loader for the actual scripts run by SoloPlay rather than the just the SoloPlay base script - includeIfNotIncluded("common/Loader.js"); Loader.getScripts = function () { @@ -29,77 +28,11 @@ Loader.scriptName = function (offset = 0) { return "SoloPlay"; }; +/** + * @deprecated Loader.run is used instead + */ Loader.loadScripts = function () { - let reconfiguration, unmodifiedConfig = {}; - - this.copy(Config, unmodifiedConfig); - - if (!this.fileList.length) { - showConsole(); - - throw new Error("You don't have any valid scripts in bots folder."); - } - - for (let s in Scripts) { - if (Scripts.hasOwnProperty(s) && Scripts[s]) { - this.scriptList.push(s); - } - } - - for (this.scriptIndex = 0; this.scriptIndex < this.scriptList.length; this.scriptIndex++) { - let script = this.scriptList[this.scriptIndex]; - - if (this.fileList.indexOf(script) < 0) { - if (FileTools.exists("libs/SoloPlay/" + script + ".js")) { - console.warn("ÿc1Something went wrong in loader, file exists in folder but didn't get included during init process. Lets ignore the error and continue to include the script by name instead"); - } else { - Misc.errorReport("ÿc1Script " + script + " doesn't exist."); - - continue; - } - } - - if (!include("SoloPlay/" + script + ".js")) { - Misc.errorReport("Failed to include script: " + script); - continue; - } - - if (isIncluded("SoloPlay/" + script + ".js")) { - try { - if (typeof (global[script]) !== "function") throw new Error("Invalid script function name"); - - if (this.skipTown.includes(script) || Town.goToTown()) { - console.log("ÿc2Starting script: ÿc9" + script); - Messaging.sendToScript("libs/SoloPlay/Threads/ToolsThread.js", JSON.stringify({currScript: script})); - reconfiguration = typeof Scripts[script] === "object"; - - if (reconfiguration) { - console.log("ÿc2Copying Config properties from " + script + " object."); - this.copy(Scripts[script], Config); - } - - let tick = getTickCount(); - - if (global[script]()) { - console.log("ÿc7" + script + " :: ÿc0Complete ÿc0- ÿc7Duration: ÿc0" + (new Date(getTickCount() - tick).toISOString().slice(11, -5))); - } - } - } catch (error) { - Misc.errorReport(error, script); - } finally { - // Dont run for last script as that will clear everything anyway - if (this.scriptIndex < this.scriptList.length) { - // remove script function from global scope, so it can be cleared by GC - delete global[script]; - } - - if (reconfiguration) { - console.log("ÿc2Reverting back unmodified config properties."); - this.copy(unmodifiedConfig, Config); - } - } - } - } + return Loader.run(); }; Loader.run = function () { diff --git a/libs/SoloPlay/Functions/Quest.js b/libs/SoloPlay/Functions/Quest.js index 57e2cdc1..869e4507 100644 --- a/libs/SoloPlay/Functions/Quest.js +++ b/libs/SoloPlay/Functions/Quest.js @@ -265,7 +265,7 @@ const Quest = { : null; let smashable = Game.getObject(classid); - if (Item.getEquippedItem(sdk.body.RightArm).classid !== tool || !me.getItem(tool)) return false; + if (Item.getEquipped(sdk.body.RightArm).classid !== tool || !me.getItem(tool)) return false; if (!smashable) return false; let tick = getTickCount(); let questTool = me.getItem(tool); @@ -600,7 +600,7 @@ const Quest = { // Remove Khalim's Will if quest not completed and restarting run. let kw = me.getItem(sdk.items.quest.KhalimsWill); if (kw) { - if (Item.getEquippedItem(sdk.body.RightArm).classid === sdk.items.quest.KhalimsWill) { + if (Item.getEquipped(sdk.body.RightArm).classid === sdk.items.quest.KhalimsWill) { Town.clearInventory(); delay(500); Quest.stashItem(sdk.items.quest.KhalimsWill); diff --git a/libs/SoloPlay/Functions/RunewordsOverrides.js b/libs/SoloPlay/Functions/RunewordsOverrides.js index 0979b505..8fc2224e 100644 --- a/libs/SoloPlay/Functions/RunewordsOverrides.js +++ b/libs/SoloPlay/Functions/RunewordsOverrides.js @@ -44,7 +44,7 @@ Runewords.getBase = function (runeword, base, ethFlag, reroll) { */ if ((!reroll && !item.getItem() && Item.betterBaseThanWearing(item, Developer.debugging.baseCheck)) || - (reroll && item.getItem() && !NTIP.CheckItem(item, this.pickitEntries) && !Item.autoEquipKeepCheckMerc(item) && !Item.autoEquipCheck(item, true))) { + (reroll && item.getItem() && !NTIP.CheckItem(item, this.pickitEntries) && !Item.autoEquipCheckMerc(item, true) && !Item.autoEquipCheck(item, true))) { if (!ethFlag || (ethFlag === Roll.Eth && item.ethereal) || (ethFlag === Roll.NonEth && !item.ethereal)) { return copyUnit(item); } diff --git a/libs/SoloPlay/Scripts/bloodraven.js b/libs/SoloPlay/Scripts/bloodraven.js index 840c1e97..9b07dc0f 100644 --- a/libs/SoloPlay/Scripts/bloodraven.js +++ b/libs/SoloPlay/Scripts/bloodraven.js @@ -63,7 +63,7 @@ function bloodraven () { break; case sdk.game.gametype.Expansion: - if ((me.charlvl < 80 || me.charlvl > 85) && !((me.sorceress || me.druid || me.assassin) && Item.getEquippedItem(sdk.body.RightArm).tier < 100000)) { + if ((me.charlvl < 80 || me.charlvl > 85) && !((me.sorceress || me.druid || me.assassin) && Item.getEquipped(sdk.body.RightArm).tier < 100000)) { return true; } diff --git a/libs/SoloPlay/Threads/ToolsThread.js b/libs/SoloPlay/Threads/ToolsThread.js index 31d58a48..e38f1c50 100644 --- a/libs/SoloPlay/Threads/ToolsThread.js +++ b/libs/SoloPlay/Threads/ToolsThread.js @@ -363,8 +363,8 @@ function main () { } itemString = "ÿc4MaxQuantity: ÿc0" + NTIP.getMaxQuantity(itemToCheck) + " | ÿc4ItemsOwned: ÿc0" + Item.getQuantityOwned(itemToCheck) + " | ÿc4Tier: ÿc0" + NTIP.GetTier(itemToCheck) + (special ? special : "") + " | ÿc4SecondaryTier: ÿc0" + NTIP.GetSecondaryTier(itemToCheck) + " | ÿc4MercTier: ÿc0" + NTIP.GetMercTier(itemToCheck) + "\n" - + "ÿc4AutoEquipKeepCheck: ÿc0" + Item.autoEquipKeepCheck(itemToCheck) + " | ÿc4AutoEquipCheckSecondary: ÿc0" + Item.autoEquipCheckSecondary(itemToCheck) - + " | ÿc4AutoEquipKeepCheckMerc: ÿc0" + Item.autoEquipKeepCheckMerc(itemToCheck) + "\nÿc4Cubing Item: ÿc0" + Cubing.keepItem(itemToCheck) + + "ÿc4AutoEquipKeepCheck: ÿc0" + Item.autoEquipCheck(itemToCheck, true) + " | ÿc4AutoEquipCheckSecondary: ÿc0" + Item.autoEquipCheckSecondary(itemToCheck) + + " | ÿc4AutoEquipKeepCheckMerc: ÿc0" + Item.autoEquipCheckMerc(itemToCheck, true) + "\nÿc4Cubing Item: ÿc0" + Cubing.keepItem(itemToCheck) + " | ÿc4Runeword Item: ÿc0" + Runewords.keepItem(itemToCheck) + " | ÿc4Crafting Item: ÿc0" + CraftingSystem.keepItem(itemToCheck) + " | ÿc4SoloWants Item: ÿc0" + SoloWants.keepItem(itemToCheck) + "\nÿc4ItemType: ÿc0" + itemToCheck.itemType + "| ÿc4Classid: ÿc0" + itemToCheck.classid + "| ÿc4Quality: ÿc0" + itemToCheck.quality; charmString = "ÿc4InvoQuantity: ÿc0" + NTIP.getInvoQuantity(itemToCheck) + " | ÿc4hasStats: ÿc0" + NTIP.hasStats(itemToCheck) + " | ÿc4FinalCharm: ÿc0" + Item.isFinalCharm(itemToCheck) + "\n" diff --git a/libs/SoloPlay/Tools/SoloIndex.js b/libs/SoloPlay/Tools/SoloIndex.js index b4aa58e2..6d988c9f 100644 --- a/libs/SoloPlay/Tools/SoloIndex.js +++ b/libs/SoloPlay/Tools/SoloIndex.js @@ -237,7 +237,7 @@ const SoloIndex = { }, skipIf: function () { if (me.barbarian && (!me.hell || Pather.accessToAct(3) - || (Item.getEquippedItem(sdk.body.LeftArm).tier > 1270 + || (Item.getEquipped(sdk.body.LeftArm).tier > 1270 || me.checkItem({name: sdk.locale.items.Lawbringer}).have))) { return true; } diff --git a/libs/SoloPlay/Utils/General.js b/libs/SoloPlay/Utils/General.js new file mode 100644 index 00000000..b9b2941a --- /dev/null +++ b/libs/SoloPlay/Utils/General.js @@ -0,0 +1,114 @@ +(function (module) { + // these builds are not possible to do on classic + const impossibleClassicBuilds = ["Bumper", "Socketmule", "Witchyzon", "Auradin", "Torchadin", "Immortalwhirl", "Sancdreamer", "Faithbowzon", "Wfzon"]; + // these builds are not possible to do without ladder runewords + const impossibleNonLadderBuilds = ["Auradin", "Sancdreamer", "Faithbowzon"]; + + // SoloPlay general gameplay items + const nipItems = { + General: [ + "[name] == tomeoftownportal", + "[name] == tomeofidentify", + "[name] == gold # [gold] >= me.charlvl * 3 * me.diff", + "(me.charlvl < 20 || me.gold < 500) && [name] == minorhealingpotion", + "(me.charlvl < 25 || me.gold < 2000) && [name] == lighthealingpotion", + "(me.charlvl < 29 || me.gold < 5000) && [name] == healingpotion", + "[name] == greaterhealingpotion", + "[name] == superhealingpotion", + "(me.charlvl < 20 || me.gold < 1000) && [name] == minormanapotion", + "[name] == lightmanapotion", + "[name] == manapotion", + "[name] == greatermanapotion", + "[name] == supermanapotion", + "[name] == rejuvenationpotion", + "[name] == fullrejuvenationpotion", + "[name] == scrolloftownportal # # [maxquantity] == 20", + "[name] == scrollofidentify # # [maxquantity] == 20", + "[name] == key # # [maxquantity] == 12", + ], + + Quest: [ + "[name] == mephisto'ssoulstone", + "[name] == hellforgehammer", + "[name] == scrollofinifuss", + "[name] == keytothecairnstones", + "[name] == bookofskill", + "[name] == horadriccube", + "[name] == shaftofthehoradricstaff", + "[name] == topofthehoradricstaff", + "[name] == horadricstaff", + "[name] == ajadefigurine", + "[name] == thegoldenbird", + "[name] == potionoflife", + "[name] == lamesen'stome", + "[name] == khalim'seye", + "[name] == khalim'sheart", + "[name] == khalim'sbrain", + "[name] == khalim'sflail", + "[name] == khalim'swill", + "[name] == scrollofresistance", + ], + }; + + const addSocketableObj = (classid, socketWith = [], temp = [], useSocketQuest = false, condition = () => {}) => ({ + classid: classid, + socketWith: socketWith, + temp: temp, + useSocketQuest: useSocketQuest, + condition: condition + }); + const basicSocketables = { + caster: [], + all: [], + }; + // insight base + basicSocketables.all.push(addSocketableObj(sdk.items.Bill, [], [], true, (item) => + me.nightmare && item.ilvl >= 26 && item.isBaseType && item.ethereal + )); + // insight base + basicSocketables.all.push(addSocketableObj(sdk.items.ColossusVoulge, [], [], true, (item) => + me.nightmare && item.ilvl >= 26 && item.isBaseType && item.ethereal + )); + // Crown of Ages + basicSocketables.caster.push(addSocketableObj(sdk.items.Corona, [sdk.items.runes.Ber, sdk.items.runes.Um], [sdk.items.gems.Perfect.Ruby], + false, (item) => item.unique + )); + // Moser's + basicSocketables.caster.push(addSocketableObj(sdk.items.RoundShield, [sdk.items.runes.Um], [sdk.items.gems.Perfect.Diamond], + false, (item) => item.unique && !item.ethereal + )); + // Spirit Forge + basicSocketables.caster.push(addSocketableObj(sdk.items.LinkedMail, [sdk.items.runes.Shael], [sdk.items.gems.Perfect.Ruby], + false, (item) => item.unique && !item.ethereal + )); + // Dijjin Slayer + basicSocketables.caster.push(addSocketableObj(sdk.items.Ataghan, [sdk.items.runes.Amn], [sdk.items.gems.Perfect.Skull], + false, (item) => !Check.currentBuild().caster && item.unique && !item.ethereal + )); + // Bone Hew - for merc + basicSocketables.caster.push(addSocketableObj(sdk.items.OgreAxe, [sdk.items.runes.Hel, sdk.items.runes.Amn], [sdk.items.gems.Perfect.Skull], + false, (item) => item.unique + )); + // spirit base + basicSocketables.caster.push(addSocketableObj(sdk.items.BroadSword, [], [], true, (item) => + me.normal && !Check.haveBase("sword", 4) && !me.checkItem({name: sdk.locale.items.Spirit, itemtype: sdk.items.type.Sword}).have + && item.ilvl >= 26 && item.isBaseType && !item.ethereal + )); + // spirit base + basicSocketables.caster.push(addSocketableObj(sdk.items.CrystalSword, [], [], true, (item) => + me.normal && !Check.haveBase("sword", 4) && !me.checkItem({name: sdk.locale.items.Spirit, itemtype: sdk.items.type.Sword}).have + && item.ilvl >= 26 && item.ilvl <= 40 && item.isBaseType && !item.ethereal + )); + // Lidless + basicSocketables.caster.push(addSocketableObj(sdk.items.GrimShield, [sdk.items.runes.Um], [sdk.items.gems.Perfect.Diamond], !me.hell, (item) => + item.unique && (item.isInStorage || (item.isEquipped && !item.isOnSwap)) && !item.ethereal + )); + + module.exports = { + impossibleClassicBuilds: impossibleClassicBuilds, + impossibleNonLadderBuilds: impossibleNonLadderBuilds, + nipItems: nipItems, + basicSocketables: basicSocketables, + addSocketableObj: addSocketableObj, + }; +})(module); From 141b4c0b8395ceb2e5350f926c2b062c413d54d2 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Sun, 22 Jan 2023 14:40:18 -0500 Subject: [PATCH 002/263] More conversions to IIFE - Utility functions for sorc attack don't need to be global - Utility functions for dynamictiers don't need to be global - Container class doesn't need to be global --- .../ClassAttackOverrides/SorceressAttacks.js | 846 ++++++------ libs/SoloPlay/Functions/DynamicTiers.js | 1173 +++++++++-------- libs/SoloPlay/Functions/StorageOverrides.js | 960 +++++++------- 3 files changed, 1502 insertions(+), 1477 deletions(-) diff --git a/libs/SoloPlay/Functions/ClassAttackOverrides/SorceressAttacks.js b/libs/SoloPlay/Functions/ClassAttackOverrides/SorceressAttacks.js index 35a642ea..c31e7895 100644 --- a/libs/SoloPlay/Functions/ClassAttackOverrides/SorceressAttacks.js +++ b/libs/SoloPlay/Functions/ClassAttackOverrides/SorceressAttacks.js @@ -7,15 +7,16 @@ includeIfNotIncluded("common/Attacks/Sorceress.js"); -const slowable = function (unit, freezeable = false) { - return (!!unit && unit.attackable // those that we can attack +(function() { + const slowable = function (unit, freezeable = false) { + return (!!unit && unit.attackable // those that we can attack && Attack.checkResist(unit, "cold") // those that are not frozen yet and those that can be frozen or not yet chilled && (freezeable ? !unit.isFrozen && !unit.getStat(sdk.stats.CannotbeFrozen) : !unit.isChilled) && ![sdk.monsters.Andariel, sdk.monsters.Lord5].includes(unit.classid)); -}; + }; -const frostNovaCheck = function () { + const frostNovaCheck = function () { // return getUnits(sdk.unittype.Monster).some(function(el) { // return !!el && el.distance < 7 && el.attackable // && ![sdk.monsters.Andariel].includes(el.classid) @@ -23,21 +24,21 @@ const frostNovaCheck = function () { // && !checkCollision(me, el, Coords_1.Collision.BLOCK_MISSILE); // }); - // don't build whole list - since we are just trying if at least one passes the test - // todo - test to time difference between these two methods - let mob = Game.getMonster(); - if (mob) { - do { - if (mob.distance < 7 && ![sdk.monsters.Andariel].includes(mob.classid) && mob.attackable + // don't build whole list - since we are just trying if at least one passes the test + // todo - test to time difference between these two methods + let mob = Game.getMonster(); + if (mob) { + do { + if (mob.distance < 7 && ![sdk.monsters.Andariel].includes(mob.classid) && mob.attackable && !mob.isChilled && Attack.checkResist(mob, "cold") && !checkCollision(me, mob, Coords_1.Collision.BLOCK_MISSILE)) { - return true; - } - } while (mob.getNext()); - } - return false; -}; + return true; + } + } while (mob.getNext()); + } + return false; + }; -/** + /** * @typedef {Object} dataObj * @property {number} skill * @property {number} reqLvl @@ -50,528 +51,529 @@ const frostNovaCheck = function () { * @property {Function} calcDmg */ -/** + /** * @param {number} skillId * @param {number} [reqLvl] * @param {number} [range] * @returns {dataObj} */ -const buildDataObj = (skillId = -1, reqLvl = 1, range = 0) => ({ - have: false, skill: skillId, range: range ? range : Infinity, mana: Infinity, timed: false, reqLvl: reqLvl, dmg: 0, - assignValues: function (range) { - this.have = Skill.canUse(this.skill); - if (!this.have) return; - this.range = range || Skill.getRange(this.skill); - this.mana = Skill.getManaCost(this.skill); - this.timed = Skill.isTimed(this.skill); - }, - calcDmg: function (unit) { - if (!this.have) return; - this.dmg = GameData.avgSkillDamage(this.skill, unit); - } -}); + const buildDataObj = (skillId = -1, reqLvl = 1, range = 0) => ({ + have: false, skill: skillId, range: range ? range : Infinity, mana: Infinity, timed: false, reqLvl: reqLvl, dmg: 0, + assignValues: function (range) { + this.have = Skill.canUse(this.skill); + if (!this.have) return; + this.range = range || Skill.getRange(this.skill); + this.mana = Skill.getManaCost(this.skill); + this.timed = Skill.isTimed(this.skill); + }, + calcDmg: function (unit) { + if (!this.have) return; + this.dmg = GameData.avgSkillDamage(this.skill, unit); + } + }); -/** + /** * @param {dataObj} main * @param {dataObj} check * @returns {boolean} */ -const compareDamage = (main, check) => { - if (main.skill === check.skill) return false; - return check.dmg > main.dmg; -}; - -ClassAttack.switchCurse = function (unit, force) { - if (CharData.skillData.haveChargedSkill([sdk.skills.SlowMissiles, sdk.skills.LowerResist, sdk.skills.Weaken]) && unit.curseable) { - const gold = me.gold; - const isBoss = unit.isBoss; - const dangerZone = [sdk.areas.ChaosSanctuary, sdk.areas.ThroneofDestruction].includes(me.area); - if (force && checkCollision(me, unit, sdk.collision.Ranged)) { - if (!Attack.getIntoPosition(unit, 35, sdk.collision.Ranged)) return; - } - // If we have slow missles we might as well use it, currently only on Lighting Enchanted mobs as they are dangerous - // Might be worth it to use on souls too TODO: test this idea - if (CharData.skillData.haveChargedSkill(sdk.skills.SlowMissiles) && gold > 500000 && !isBoss + const compareDamage = (main, check) => { + if (main.skill === check.skill) return false; + return check.dmg > main.dmg; + }; + + ClassAttack.switchCurse = function (unit, force) { + if (CharData.skillData.haveChargedSkill([sdk.skills.SlowMissiles, sdk.skills.LowerResist, sdk.skills.Weaken]) && unit.curseable) { + const gold = me.gold; + const isBoss = unit.isBoss; + const dangerZone = [sdk.areas.ChaosSanctuary, sdk.areas.ThroneofDestruction].includes(me.area); + if (force && checkCollision(me, unit, sdk.collision.Ranged)) { + if (!Attack.getIntoPosition(unit, 35, sdk.collision.Ranged)) return; + } + // If we have slow missles we might as well use it, currently only on Lighting Enchanted mobs as they are dangerous + // Might be worth it to use on souls too TODO: test this idea + if (CharData.skillData.haveChargedSkill(sdk.skills.SlowMissiles) && gold > 500000 && !isBoss && unit.getEnchant(sdk.enchant.LightningEnchanted) && !unit.getState(sdk.states.SlowMissiles) && !checkCollision(me, unit, sdk.collision.Ranged)) { // Cast slow missiles - Attack.castCharges(sdk.skills.SlowMissiles, unit); - } - // Handle Switch casting - if (CharData.skillData.haveChargedSkillOnSwitch(sdk.skills.LowerResist) + Attack.castCharges(sdk.skills.SlowMissiles, unit); + } + // Handle Switch casting + if (CharData.skillData.haveChargedSkillOnSwitch(sdk.skills.LowerResist) && (gold > 500000 || isBoss || dangerZone) && !unit.getState(sdk.states.LowerResist) && !checkCollision(me, unit, sdk.collision.Ranged)) { // Switch cast lower resist - Attack.switchCastCharges(sdk.skills.LowerResist, unit); - } + Attack.switchCastCharges(sdk.skills.LowerResist, unit); + } - if (CharData.skillData.haveChargedSkillOnSwitch(sdk.skills.Weaken) + if (CharData.skillData.haveChargedSkillOnSwitch(sdk.skills.Weaken) && (gold > 500000 || isBoss || dangerZone) && !unit.getState(sdk.states.Weaken) && !unit.getState(sdk.states.LowerResist) && !checkCollision(me, unit, sdk.collision.Ranged)) { // Switch cast weaken - Attack.switchCastCharges(sdk.skills.Weaken, unit); + Attack.switchCastCharges(sdk.skills.Weaken, unit); + } } - } -}; + }; -/** + /** * @param {Unit} unit * @returns {dataObj} */ -ClassAttack.decideDistanceSkill = function (unit) { - const data = {}; - const currLvl = me.charlvl; - data.iceBlast = buildDataObj(sdk.skills.IceBlast, 6, 20); - data.fireBall = buildDataObj(sdk.skills.FireBall, 12, 20); - data.lightning = buildDataObj(sdk.skills.Lightning, 12); - data.glacialSpike = buildDataObj(sdk.skills.GlacialSpike, 18, 25); - data.blizzard = buildDataObj(sdk.skills.Blizzard, 24, 40); - data.meteor = buildDataObj(sdk.skills.Meteor, 24, 40); - data.frozenOrb = buildDataObj(sdk.skills.FrozenOrb, 30); - data.hydra = buildDataObj(sdk.skills.Hydra, 30, 40); - Object.keys(data).forEach(k => typeof data[k] === "object" && currLvl >= data[k].reqLvl && data[k].assignValues()); - Object.keys(data).forEach(k => typeof data[k] === "object" && data[k].have && data[k].calcDmg(unit)); - - let skillCheck = Object.keys(data) - .filter(k => typeof data[k] === "object" && data[k].have && me.mp > data[k].mana + ClassAttack.decideDistanceSkill = function (unit) { + const data = {}; + const currLvl = me.charlvl; + data.iceBlast = buildDataObj(sdk.skills.IceBlast, 6, 20); + data.fireBall = buildDataObj(sdk.skills.FireBall, 12, 20); + data.lightning = buildDataObj(sdk.skills.Lightning, 12); + data.glacialSpike = buildDataObj(sdk.skills.GlacialSpike, 18, 25); + data.blizzard = buildDataObj(sdk.skills.Blizzard, 24, 40); + data.meteor = buildDataObj(sdk.skills.Meteor, 24, 40); + data.frozenOrb = buildDataObj(sdk.skills.FrozenOrb, 30); + data.hydra = buildDataObj(sdk.skills.Hydra, 30, 40); + Object.keys(data).forEach(k => typeof data[k] === "object" && currLvl >= data[k].reqLvl && data[k].assignValues()); + Object.keys(data).forEach(k => typeof data[k] === "object" && data[k].have && data[k].calcDmg(unit)); + + let skillCheck = Object.keys(data) + .filter(k => typeof data[k] === "object" && data[k].have && me.mp > data[k].mana && (!data[k].timed || !me.skillDelay)) - .sort((a, b) => data[b].dmg - data[a].dmg).first(); - return typeof data[skillCheck] === "object" ? data[skillCheck] : buildDataObj(-1); -}; - -ClassAttack.doAttack = function (unit, recheckSkill = false, once = false) { - Developer.debugging.skills && console.log(sdk.colors.Green + "Test Start-----------------------------------------//"); - // unit became invalidated - if (!unit || !unit.attackable) return Attack.Result.SUCCESS; + .sort((a, b) => data[b].dmg - data[a].dmg).first(); + return typeof data[skillCheck] === "object" ? data[skillCheck] : buildDataObj(-1); + }; + + ClassAttack.doAttack = function (unit, recheckSkill = false, once = false) { + Developer.debugging.skills && console.log(sdk.colors.Green + "Test Start-----------------------------------------//"); + // unit became invalidated + if (!unit || !unit.attackable) return Attack.Result.SUCCESS; - const currLvl = me.charlvl; - const index = (unit.isSpecial || unit.isPlayer) ? 1 : 3; - let gid = unit.gid; - let tick = getTickCount(); - let gold = me.gold; + const currLvl = me.charlvl; + const index = (unit.isSpecial || unit.isPlayer) ? 1 : 3; + let gid = unit.gid; + let tick = getTickCount(); + let gold = me.gold; - if (Config.MercWatch && Town.needMerc() && gold > me.mercrevivecost * 3) { - console.debug("mercwatch"); + if (Config.MercWatch && Town.needMerc() && gold > me.mercrevivecost * 3) { + console.debug("mercwatch"); - if (Town.visitTown()) { + if (Town.visitTown()) { // lost reference to the mob we were attacking - if (!unit || !copyUnit(unit).x || !Game.getMonster(-1, -1, gid) || unit.dead) { - console.debug("Lost reference to unit"); - return Attack.Result.SUCCESS; + if (!unit || !copyUnit(unit).x || !Game.getMonster(-1, -1, gid) || unit.dead) { + console.debug("Lost reference to unit"); + return Attack.Result.SUCCESS; + } + gold = me.gold; // reset value after town } - gold = me.gold; // reset value after town } - } - - // Keep Energy Shield active - Skill.canUse(sdk.skills.EnergyShield) && !me.getState(sdk.states.EnergyShield) && Skill.cast(sdk.skills.EnergyShield, sdk.skills.hand.Right); - // Keep Thunder-Storm active - Skill.canUse(sdk.skills.ThunderStorm) && !me.getState(sdk.states.ThunderStorm) && Skill.cast(sdk.skills.ThunderStorm, sdk.skills.hand.Right); - - // Handle Charge skill casting - if (index === 1 && me.expansion && !unit.dead) { - ClassAttack.switchCurse(unit); - } - - const data = {}; - data.static = buildDataObj(sdk.skills.StaticField, 6); - data.frostNova = buildDataObj(sdk.skills.FrostNova, 6, 7); - data.iceBlast = buildDataObj(sdk.skills.IceBlast, 6, 15); - data.nova = buildDataObj(sdk.skills.Nova, 12); - data.fireBall = buildDataObj(sdk.skills.FireBall, 12); - data.lightning = buildDataObj(sdk.skills.Lightning, 12); - data.glacialSpike = buildDataObj(sdk.skills.GlacialSpike, 18, 15); - data.frozenOrb = buildDataObj(sdk.skills.FrozenOrb, 30); - data.hydra = buildDataObj(sdk.skills.Hydra, 30); - data.customTimed = buildDataObj(-1); - data.customUntimed = buildDataObj(-1); - // @todo handle if these are already include in the above list, or should I just build all used skils instead and ignore these? - data.mainTimed = buildDataObj(Config.AttackSkill[index]); - data.mainUntimed = buildDataObj(Config.AttackSkill[index + 1]); - data.secondaryTimed = buildDataObj(Config.AttackSkill[5]); - data.secondaryUntimed = buildDataObj(Config.AttackSkill[6]); - - if (Attack.getCustomAttack(unit)) { - let [ts, uts] = Attack.getCustomAttack(unit); - ts > 0 && (data.customTimed = buildDataObj(ts)); - uts > 0 && (data.customUntimed = buildDataObj(uts)); - } - - Object.keys(data).forEach(k => typeof data[k] === "object" && currLvl >= data[k].reqLvl && data[k].assignValues()); - - if (data.frostNova.have) { - if (me.mp > data.frostNova.mana) { - frostNovaCheck() && Skill.cast(sdk.skills.FrostNova, sdk.skills.hand.Right); - let ticktwo = getTickCount(); - // if the nova cause the death of any monsters around us, its worth it - if (GameData.calculateKillableFallensByFrostNova() > 0) { - Developer.debugging.skills && console.log("took " + ((getTickCount() - ticktwo) / 1000) + " seconds to check calculateKillableFallensByFrostNova. frost nova will kill fallens"); - Skill.cast(sdk.skills.FrostNova, sdk.skills.hand.Right); + + // Keep Energy Shield active + Skill.canUse(sdk.skills.EnergyShield) && !me.getState(sdk.states.EnergyShield) && Skill.cast(sdk.skills.EnergyShield, sdk.skills.hand.Right); + // Keep Thunder-Storm active + Skill.canUse(sdk.skills.ThunderStorm) && !me.getState(sdk.states.ThunderStorm) && Skill.cast(sdk.skills.ThunderStorm, sdk.skills.hand.Right); + + // Handle Charge skill casting + if (index === 1 && me.expansion && !unit.dead) { + ClassAttack.switchCurse(unit); + } + + const data = {}; + data.static = buildDataObj(sdk.skills.StaticField, 6); + data.frostNova = buildDataObj(sdk.skills.FrostNova, 6, 7); + data.iceBlast = buildDataObj(sdk.skills.IceBlast, 6, 15); + data.nova = buildDataObj(sdk.skills.Nova, 12); + data.fireBall = buildDataObj(sdk.skills.FireBall, 12); + data.lightning = buildDataObj(sdk.skills.Lightning, 12); + data.glacialSpike = buildDataObj(sdk.skills.GlacialSpike, 18, 15); + data.frozenOrb = buildDataObj(sdk.skills.FrozenOrb, 30); + data.hydra = buildDataObj(sdk.skills.Hydra, 30); + data.customTimed = buildDataObj(-1); + data.customUntimed = buildDataObj(-1); + // @todo handle if these are already include in the above list, or should I just build all used skils instead and ignore these? + data.mainTimed = buildDataObj(Config.AttackSkill[index]); + data.mainUntimed = buildDataObj(Config.AttackSkill[index + 1]); + data.secondaryTimed = buildDataObj(Config.AttackSkill[5]); + data.secondaryUntimed = buildDataObj(Config.AttackSkill[6]); + + if (Attack.getCustomAttack(unit)) { + let [ts, uts] = Attack.getCustomAttack(unit); + ts > 0 && (data.customTimed = buildDataObj(ts)); + uts > 0 && (data.customUntimed = buildDataObj(uts)); + } + + Object.keys(data).forEach(k => typeof data[k] === "object" && currLvl >= data[k].reqLvl && data[k].assignValues()); + + if (data.frostNova.have) { + if (me.mp > data.frostNova.mana) { + frostNovaCheck() && Skill.cast(sdk.skills.FrostNova, sdk.skills.hand.Right); + let ticktwo = getTickCount(); + // if the nova cause the death of any monsters around us, its worth it + if (GameData.calculateKillableFallensByFrostNova() > 0) { + Developer.debugging.skills && console.log("took " + ((getTickCount() - ticktwo) / 1000) + " seconds to check calculateKillableFallensByFrostNova. frost nova will kill fallens"); + Skill.cast(sdk.skills.FrostNova, sdk.skills.hand.Right); + } } } - } - if (data.glacialSpike.have) { - if (me.mp > data.glacialSpike.mana * 2) { - let shouldSpike = unit && unit.distance < 10 && + if (data.glacialSpike.have) { + if (me.mp > data.glacialSpike.mana * 2) { + let shouldSpike = unit && unit.distance < 10 && getUnits(sdk.unittype.Monster).filter(function (el) { return getDistance(el, unit) < 4 && slowable(el, true); }).length > 1; - if (shouldSpike && !Coords_1.isBlockedBetween(me, unit)) { - Developer.debugging.skills && console.log("SPIKE"); - Skill.cast(sdk.skills.GlacialSpike, sdk.skills.hand.Right, unit); + if (shouldSpike && !Coords_1.isBlockedBetween(me, unit)) { + Developer.debugging.skills && console.log("SPIKE"); + Skill.cast(sdk.skills.GlacialSpike, sdk.skills.hand.Right, unit); + } } } - } - // We lost track of the mob or killed it - if (unit === undefined || !unit || !unit.attackable) return Attack.Result.SUCCESS; + // We lost track of the mob or killed it + if (unit === undefined || !unit || !unit.attackable) return Attack.Result.SUCCESS; - // Set damage values - // redo gamedata to be more efficent - Object.keys(data).forEach(k => typeof data[k] === "object" && data[k].have && data[k].calcDmg(unit)); + // Set damage values + // redo gamedata to be more efficent + Object.keys(data).forEach(k => typeof data[k] === "object" && data[k].have && data[k].calcDmg(unit)); - // log damage values - if (Developer.debugging.skills) { - Object.keys(data).forEach(k => typeof data[k] === "object" && console.log(getSkillById(data[k].skill) + " : " + data[k].dmg)); - } - - // If we have enough mana for Static and it will do more damage than our other skills then duh use it - // should this return afterwards since the calulations will now be different? - if (data.static.have && (data.static.mana * 3) < me.mp) { - let closeMobCheck = getUnits(sdk.unittype.Monster) - .filter(unit => !!unit && unit.attackable && unit.distance < data.static.range) - .find(unit => Attack.checkResist(unit, "lightning") && unit.hpPercent > Config.CastStatic); - if (!!closeMobCheck && data.static.dmg > Math.max(data.mainTimed.dmg, data.mainUntimed.dmg, data.secondaryTimed.dmg, data.secondaryUntimed.dmg) && !Coords_1.isBlockedBetween(me, closeMobCheck)) { - Developer.debugging.skills && console.log("STATIC"); - Skill.cast(sdk.skills.StaticField, sdk.skills.hand.Right, closeMobCheck) && Skill.cast(sdk.skills.StaticField, sdk.skills.hand.Right, closeMobCheck); + // log damage values + if (Developer.debugging.skills) { + Object.keys(data).forEach(k => typeof data[k] === "object" && console.log(getSkillById(data[k].skill) + " : " + data[k].dmg)); } - } - // We lost track of the mob or killed it (recheck after using static) - if (unit === undefined || !unit || !unit.attackable) return true; + // If we have enough mana for Static and it will do more damage than our other skills then duh use it + // should this return afterwards since the calulations will now be different? + if (data.static.have && (data.static.mana * 3) < me.mp) { + let closeMobCheck = getUnits(sdk.unittype.Monster) + .filter(unit => !!unit && unit.attackable && unit.distance < data.static.range) + .find(unit => Attack.checkResist(unit, "lightning") && unit.hpPercent > Config.CastStatic); + if (!!closeMobCheck && data.static.dmg > Math.max(data.mainTimed.dmg, data.mainUntimed.dmg, data.secondaryTimed.dmg, data.secondaryUntimed.dmg) && !Coords_1.isBlockedBetween(me, closeMobCheck)) { + Developer.debugging.skills && console.log("STATIC"); + Skill.cast(sdk.skills.StaticField, sdk.skills.hand.Right, closeMobCheck) && Skill.cast(sdk.skills.StaticField, sdk.skills.hand.Right, closeMobCheck); + } + } + + // We lost track of the mob or killed it (recheck after using static) + if (unit === undefined || !unit || !unit.attackable) return true; - /** - * @todo static field is a good skill but if we are currently out of range, check how dangerous it is to tele to spot before choosing that as our skill - */ - let sortedList = Object.keys(data) - .filter(k => typeof data[k] === "object" && data[k].have && me.mp > data[k].mana + /** + * @todo static field is a good skill but if we are currently out of range, check how dangerous it is to tele to spot before choosing that as our skill + */ + let sortedList = Object.keys(data) + .filter(k => typeof data[k] === "object" && data[k].have && me.mp > data[k].mana && (!data[k].timed || !me.skillDelay) && (data[k].skill !== sdk.skills.StaticField || !recheckSkill)) - .sort((a, b) => data[b].dmg - data[a].dmg); - let skillCheck = data[sortedList[0]].skill === sdk.skills.StaticField && unit.distance > data.static.range && me.inDanger(unit, 15) - ? sortedList.at(1) - : sortedList.at(0); - let timedSkill = typeof data[skillCheck] === "object" ? data[skillCheck] : buildDataObj(-1); - - // throw in another attack when using charged bolt as sometimes it misses - const lowManaData = {}; - lowManaData.fBolt = buildDataObj(sdk.skills.FireBolt); - lowManaData.cBolt = buildDataObj(sdk.skills.ChargedBolt); - lowManaData.iBolt = buildDataObj(sdk.skills.IceBolt); - lowManaData.iBlast = buildDataObj(sdk.skills.IceBlast); - lowManaData.tk = buildDataObj(sdk.skills.Telekinesis, 6, 20); - lowManaData.attack = buildDataObj(sdk.skills.Attack, 1, 4); - if (timedSkill.skill === sdk.skills.ChargedBolt && recheckSkill) { - let temp = timedSkill; - Object.keys(data).forEach(k => { - if (typeof data[k] === "object" && data[k].have && compareDamage(temp, data[k])) { - temp = data[k]; + .sort((a, b) => data[b].dmg - data[a].dmg); + let skillCheck = data[sortedList[0]].skill === sdk.skills.StaticField && unit.distance > data.static.range && me.inDanger(unit, 15) + ? sortedList.at(1) + : sortedList.at(0); + let timedSkill = typeof data[skillCheck] === "object" ? data[skillCheck] : buildDataObj(-1); + + // throw in another attack when using charged bolt as sometimes it misses + const lowManaData = {}; + lowManaData.fBolt = buildDataObj(sdk.skills.FireBolt); + lowManaData.cBolt = buildDataObj(sdk.skills.ChargedBolt); + lowManaData.iBolt = buildDataObj(sdk.skills.IceBolt); + lowManaData.iBlast = buildDataObj(sdk.skills.IceBlast); + lowManaData.tk = buildDataObj(sdk.skills.Telekinesis, 6, 20); + lowManaData.attack = buildDataObj(sdk.skills.Attack, 1, 4); + if (timedSkill.skill === sdk.skills.ChargedBolt && recheckSkill) { + let temp = timedSkill; + Object.keys(data).forEach(k => { + if (typeof data[k] === "object" && data[k].have && compareDamage(temp, data[k])) { + temp = data[k]; + } + }); + if (temp.skill !== timedSkill.skill) { + timedSkill = temp; } - }); - if (temp.skill !== timedSkill.skill) { - timedSkill = temp; } - } - if (!timedSkill.have || timedSkill.mana > me.mp) { - Developer.debugging.skills && console.log("Choosing lower mana skill, Was I not able to use one of my better skills? (" + (!timedSkill.have) + "). Did I not have enough mana? " + (timedSkill.mana > me.mp)); - Object.keys(lowManaData).forEach(k => typeof lowManaData[k] === "object" && currLvl >= lowManaData[k].reqLvl && lowManaData[k].assignValues() && lowManaData[k].calcDmg(unit)); - const timedSkillCheck = Object.keys(lowManaData) - .filter(k => typeof lowManaData[k] === "object" && lowManaData[k].have && me.mp > lowManaData[k].mana) - .sort((a, b) => lowManaData[b].dmg - lowManaData[a].dmg).first(); - console.debug(timedSkillCheck); - timedSkill = (() => { - switch (true) { - case !!timedSkillCheck && [lowManaData.tk.skill, lowManaData.attack.skill].indexOf(lowManaData[timedSkillCheck].skill) === -1: - return lowManaData[timedSkillCheck]; - case !!timedSkillCheck && lowManaData[timedSkillCheck].skill === lowManaData.tk.skill && me.normal: - return lowManaData.tk; - default: - if (me.charlvl < 5) return lowManaData.attack; - return (me.normal && me.checkForMobs({range: 10, coll: (sdk.collision.BlockWall | sdk.collision.Objects | sdk.collision.ClosedDoor)}) ? lowManaData.attack : buildDataObj(-1)); - } - })(); - Object.assign(data, lowManaData); - } - - if (timedSkill.skill === sdk.skills.ChargedBolt && data.secondaryUntimed.skill === sdk.skills.IceBolt && data.secondaryUntimed.have && slowable(unit)) { - timedSkill = data.secondaryUntimed; - } - - const switchBowAttack = (unit) => { - if (Attack.getIntoPosition(unit, 20, sdk.collision.Ranged)) { - try { - const checkForShamans = unit.isFallen && !me.inArea(sdk.areas.BloodMoor); - for (let i = 0; i < 5 && unit.attackable; i++) { - if (checkForShamans && !once) { + if (!timedSkill.have || timedSkill.mana > me.mp) { + Developer.debugging.skills && console.log("Choosing lower mana skill, Was I not able to use one of my better skills? (" + (!timedSkill.have) + "). Did I not have enough mana? " + (timedSkill.mana > me.mp)); + Object.keys(lowManaData).forEach(k => typeof lowManaData[k] === "object" && currLvl >= lowManaData[k].reqLvl && lowManaData[k].assignValues() && lowManaData[k].calcDmg(unit)); + const timedSkillCheck = Object.keys(lowManaData) + .filter(k => typeof lowManaData[k] === "object" && lowManaData[k].have && me.mp > lowManaData[k].mana) + .sort((a, b) => lowManaData[b].dmg - lowManaData[a].dmg).first(); + console.debug(timedSkillCheck); + timedSkill = (() => { + switch (true) { + case !!timedSkillCheck && [lowManaData.tk.skill, lowManaData.attack.skill].indexOf(lowManaData[timedSkillCheck].skill) === -1: + return lowManaData[timedSkillCheck]; + case !!timedSkillCheck && lowManaData[timedSkillCheck].skill === lowManaData.tk.skill && me.normal: + return lowManaData.tk; + default: + if (me.charlvl < 5) return lowManaData.attack; + return (me.normal && me.checkForMobs({range: 10, coll: (sdk.collision.BlockWall | sdk.collision.Objects | sdk.collision.ClosedDoor)}) ? lowManaData.attack : buildDataObj(-1)); + } + })(); + Object.assign(data, lowManaData); + } + + if (timedSkill.skill === sdk.skills.ChargedBolt && data.secondaryUntimed.skill === sdk.skills.IceBolt && data.secondaryUntimed.have && slowable(unit)) { + timedSkill = data.secondaryUntimed; + } + + const switchBowAttack = (unit) => { + if (Attack.getIntoPosition(unit, 20, sdk.collision.Ranged)) { + try { + const checkForShamans = unit.isFallen && !me.inArea(sdk.areas.BloodMoor); + for (let i = 0; i < 5 && unit.attackable; i++) { + if (checkForShamans && !once) { // before we waste time let's see if there is a shaman we should kill - const shaman = getUnits(sdk.unittype.Monster) - .filter(mon => mon.distance < 20 && mon.isShaman && mon.attackable) - .sort((a, b) => a.distance - b.distance).first(); - if (shaman) return ClassAttack.doAttack(shaman, null, true); - } - if (!Attack.useBowOnSwitch(unit, sdk.skills.Attack, i === 5)) return Attack.Result.FAILED; - if (unit.distance < 8 || me.inDanger()) { - if (once) return Attack.Result.FAILED; - let closeMob = getUnits(sdk.unittype.Monster) - .filter(mon => mon.distance < 10 && mon.attackable && mon.gid !== gid) - .sort(Attack.walkingSortMonsters).first(); - if (closeMob) return ClassAttack.doAttack(closeMob, null, true); + const shaman = getUnits(sdk.unittype.Monster) + .filter(mon => mon.distance < 20 && mon.isShaman && mon.attackable) + .sort((a, b) => a.distance - b.distance).first(); + if (shaman) return ClassAttack.doAttack(shaman, null, true); + } + if (!Attack.useBowOnSwitch(unit, sdk.skills.Attack, i === 5)) return Attack.Result.FAILED; + if (unit.distance < 8 || me.inDanger()) { + if (once) return Attack.Result.FAILED; + let closeMob = getUnits(sdk.unittype.Monster) + .filter(mon => mon.distance < 10 && mon.attackable && mon.gid !== gid) + .sort(Attack.walkingSortMonsters).first(); + if (closeMob) return ClassAttack.doAttack(closeMob, null, true); + } } + } finally { + me.weaponswitch !== sdk.player.slot.Main && me.switchWeapons(sdk.player.slot.Main); } - } finally { - me.weaponswitch !== sdk.player.slot.Main && me.switchWeapons(sdk.player.slot.Main); } - } - return unit.dead ? Attack.Result.SUCCESS : Attack.Result.FAILED; - }; + return unit.dead ? Attack.Result.SUCCESS : Attack.Result.FAILED; + }; - if (CharData.skillData.bowData.bowOnSwitch + if (CharData.skillData.bowData.bowOnSwitch && (index !== 1 || !unit.name.includes(getLocaleString(sdk.locale.text.Ghostly))) && ([-1, sdk.skills.Attack].includes(timedSkill.skill) || timedSkill.mana > me.mp || (timedSkill.mana * 3 > me.mp && [sdk.skills.FireBolt, sdk.skills.ChargedBolt].includes(timedSkill.skill)))) { - if (switchBowAttack(unit) === Attack.Result.SUCCESS) return Attack.Result.SUCCESS; - } - - switch (ClassAttack.doCast(unit, timedSkill, data)) { - case Attack.Result.FAILED: - Developer.debugging.skills && console.log(sdk.colors.Red + "Fail Test End----Time elasped[" + ((getTickCount() - tick) / 1000) + " seconds]----------------------//"); - break; - case Attack.Result.SUCCESS: - Developer.debugging.skills && console.log(sdk.colors.Red + "Sucess Test End----Time elasped[" + ((getTickCount() - tick) / 1000) + " seconds]----------------------//"); - return Attack.Result.SUCCESS; - case Attack.Result.CANTATTACK: // Try to telestomp - if (Pather.canTeleport() && Attack.checkResist(unit, "physical") && !!me.getMerc() + if (switchBowAttack(unit) === Attack.Result.SUCCESS) return Attack.Result.SUCCESS; + } + + switch (ClassAttack.doCast(unit, timedSkill, data)) { + case Attack.Result.FAILED: + Developer.debugging.skills && console.log(sdk.colors.Red + "Fail Test End----Time elasped[" + ((getTickCount() - tick) / 1000) + " seconds]----------------------//"); + break; + case Attack.Result.SUCCESS: + Developer.debugging.skills && console.log(sdk.colors.Red + "Sucess Test End----Time elasped[" + ((getTickCount() - tick) / 1000) + " seconds]----------------------//"); + return Attack.Result.SUCCESS; + case Attack.Result.CANTATTACK: // Try to telestomp + if (Pather.canTeleport() && Attack.checkResist(unit, "physical") && !!me.getMerc() && Attack.validSpot(unit.x, unit.y) && (Config.TeleStomp || (!me.hell && (unit.getMobCount(10) < me.maxNearMonsters && unit.isSpecial)))) { - let merc = me.getMerc(); - let haveTK = Skill.canUse(sdk.skills.Telekinesis); - let mercRevive = 0; - - while (unit.attackable) { - if (Misc.townCheck()) { - if (!unit || !copyUnit(unit).x) { - unit = Misc.poll(() => Game.getMonster(-1, -1, gid), 1000, 80); + let merc = me.getMerc(); + let haveTK = Skill.canUse(sdk.skills.Telekinesis); + let mercRevive = 0; + + while (unit.attackable) { + if (Misc.townCheck()) { + if (!unit || !copyUnit(unit).x) { + unit = Misc.poll(() => Game.getMonster(-1, -1, gid), 1000, 80); + } } - } - if (!unit) return Attack.Result.SUCCESS; + if (!unit) return Attack.Result.SUCCESS; - if (Town.needMerc()) { - if (Config.MercWatch && mercRevive < 3) { - Town.visitTown() && (mercRevive++); - } else { - return Attack.Result.CANTATTACK; + if (Town.needMerc()) { + if (Config.MercWatch && mercRevive < 3) { + Town.visitTown() && (mercRevive++); + } else { + return Attack.Result.CANTATTACK; + } + + (merc === undefined || !merc) && (merc = me.getMerc()); } - (merc === undefined || !merc) && (merc = me.getMerc()); - } + if (!!merc && getDistance(merc, unit) > 5) { + Pather.moveToUnit(unit); - if (!!merc && getDistance(merc, unit) > 5) { - Pather.moveToUnit(unit); + let spot = Attack.findSafeSpot(unit, 10, 5, 9); + !!spot && Pather.walkTo(spot.x, spot.y); + } - let spot = Attack.findSafeSpot(unit, 10, 5, 9); - !!spot && Pather.walkTo(spot.x, spot.y); - } + if (Attack.checkResist(unit, "lightning") && data.static.have && unit.hpPercent > Config.CastStatic) { + Skill.cast(sdk.skills.StaticField, sdk.skills.hand.Right); + } - if (Attack.checkResist(unit, "lightning") && data.static.have && unit.hpPercent > Config.CastStatic) { - Skill.cast(sdk.skills.StaticField, sdk.skills.hand.Right); + let closeMob = Attack.getNearestMonster({skipGid: gid}); + !!closeMob ? this.doCast(closeMob, timedSkill, data) : haveTK && Skill.cast(sdk.skills.Telekinesis, sdk.skills.hand.Right, unit); } - let closeMob = Attack.getNearestMonster({skipGid: gid}); - !!closeMob ? this.doCast(closeMob, timedSkill, data) : haveTK && Skill.cast(sdk.skills.Telekinesis, sdk.skills.hand.Right, unit); + return Attack.Result.SUCCESS; } - return Attack.Result.SUCCESS; + break; } - break; - } - - return Attack.Result.FAILED; -}; + return Attack.Result.FAILED; + }; -ClassAttack.doCast = function (unit, choosenSkill, data) { - let noMana = false; - let { skill, range, mana, timed } = choosenSkill; - // unit became invalidated - if (!unit || !unit.attackable) return Attack.Result.SUCCESS; - if (!!skill && me.mp < mana) { - return Attack.Result.NEEDMANA; - } - // No valid skills can be found - if (skill < 0) return Attack.Result.CANTATTACK; + ClassAttack.doCast = function (unit, choosenSkill, data) { + let noMana = false; + let { skill, range, mana, timed } = choosenSkill; + // unit became invalidated + if (!unit || !unit.attackable) return Attack.Result.SUCCESS; + if (!!skill && me.mp < mana) { + return Attack.Result.NEEDMANA; + } + // No valid skills can be found + if (skill < 0) return Attack.Result.CANTATTACK; - // print damage values - Developer.debugging.skills && choosenSkill.have && console.log(sdk.colors.Yellow + "(Selected Main :: " + getSkillById(skill) + ") DMG: " + choosenSkill.dmg); + // print damage values + Developer.debugging.skills && choosenSkill.have && console.log(sdk.colors.Yellow + "(Selected Main :: " + getSkillById(skill) + ") DMG: " + choosenSkill.dmg); - if (![sdk.skills.FrostNova, sdk.skills.Nova, sdk.skills.StaticField].includes(skill)) { - if (Skill.canUse(sdk.skills.Teleport) && me.mp > Skill.getManaCost(sdk.skills.Teleport) + mana && me.inDanger()) { + if (![sdk.skills.FrostNova, sdk.skills.Nova, sdk.skills.StaticField].includes(skill)) { + if (Skill.canUse(sdk.skills.Teleport) && me.mp > Skill.getManaCost(sdk.skills.Teleport) + mana && me.inDanger()) { //console.log("FINDING NEW SPOT"); - Attack.getIntoPosition(unit, range, 0 + Attack.getIntoPosition(unit, range, 0 | Coords_1.BlockBits.LineOfSight | Coords_1.BlockBits.Ranged | Coords_1.BlockBits.Casting | Coords_1.BlockBits.ClosedDoor | Coords_1.BlockBits.Objects, false, true); - } else if (me.inDanger()) { - Attack.getIntoPosition(unit, range + 1, Coords_1.Collision.BLOCK_MISSILE, true); + } else if (me.inDanger()) { + Attack.getIntoPosition(unit, range + 1, Coords_1.Collision.BLOCK_MISSILE, true); + } } - } - if (skill > -1 && (!me.skillDelay || !timed)) { - let ranged = range > 4; + if (skill > -1 && (!me.skillDelay || !timed)) { + let ranged = range > 4; - if (skill === sdk.skills.ChargedBolt && !unit.hasEnchant(sdk.enchant.ManaBurn, sdk.enchant.ColdEnchanted)) { - unit.getMobCount(6, Coords_1.Collision.BLOCK_MISSILE) < 3 && (range = 7); - } + if (skill === sdk.skills.ChargedBolt && !unit.hasEnchant(sdk.enchant.ManaBurn, sdk.enchant.ColdEnchanted)) { + unit.getMobCount(6, Coords_1.Collision.BLOCK_MISSILE) < 3 && (range = 7); + } - if (skill === sdk.skills.Attack) { - if (me.hpPercent < 50 && me.mode !== sdk.player.mode.GettingHit && !me.checkForMobs({range: 12})) { - console.log("Low health but safe right now, going to delay a bit"); - let tick = getTickCount(); - const howLongToDelay = Config.AttackSkill.some(sk => sk > 1 && Skill.canUse(sk)) ? Time.seconds(2) : Time.seconds(1); + if (skill === sdk.skills.Attack) { + if (me.hpPercent < 50 && me.mode !== sdk.player.mode.GettingHit && !me.checkForMobs({range: 12})) { + console.log("Low health but safe right now, going to delay a bit"); + let tick = getTickCount(); + const howLongToDelay = Config.AttackSkill.some(sk => sk > 1 && Skill.canUse(sk)) ? Time.seconds(2) : Time.seconds(1); - while (getTickCount() - tick < howLongToDelay) { - if (me.mode === sdk.player.mode.GettingHit) { - console.debug("no longer safe, we are being attacked"); - break; - } else if (me.hpPercent >= 55) { - return 3; - } + while (getTickCount() - tick < howLongToDelay) { + if (me.mode === sdk.player.mode.GettingHit) { + console.debug("no longer safe, we are being attacked"); + break; + } else if (me.hpPercent >= 55) { + return 3; + } - delay(40); + delay(40); + } } } - } - if (range < 4 && !Attack.validSpot(unit.x, unit.y)) return Attack.Result.FAILED; + if (range < 4 && !Attack.validSpot(unit.x, unit.y)) return Attack.Result.FAILED; - // Only delay if there are no mobs in our immediate area - if (mana > me.mp && !me.checkForMobs({range: 12})) { - let tick = getTickCount(); + // Only delay if there are no mobs in our immediate area + if (mana > me.mp && !me.checkForMobs({range: 12})) { + let tick = getTickCount(); - while (getTickCount() - tick < 750) { - if (mana < me.mp) { - break; - } else if (me.mode === sdk.player.mode.GettingHit) { - console.debug("no longer safe, we are being attacked"); - return Attack.Result.NEEDMANA; - } + while (getTickCount() - tick < 750) { + if (mana < me.mp) { + break; + } else if (me.mode === sdk.player.mode.GettingHit) { + console.debug("no longer safe, we are being attacked"); + return Attack.Result.NEEDMANA; + } - delay(25); + delay(25); + } } - } - // try to prevent missing when the monster is moving by getting just a bit closer - if ([sdk.skills.FireBolt, sdk.skills.IceBolt].includes(skill)) { - range = 12; - } - if (unit.distance > range || Coords_1.isBlockedBetween(me, unit)) { + // try to prevent missing when the monster is moving by getting just a bit closer + if ([sdk.skills.FireBolt, sdk.skills.IceBolt].includes(skill)) { + range = 12; + } + if (unit.distance > range || Coords_1.isBlockedBetween(me, unit)) { // Allow short-distance walking for melee skills - let walk = (range < 4 || (skill === sdk.skills.ChargedBolt && range === 7)) && unit.distance < 10 && !checkCollision(me, unit, Coords_1.BlockBits.BlockWall); + let walk = (range < 4 || (skill === sdk.skills.ChargedBolt && range === 7)) && unit.distance < 10 && !checkCollision(me, unit, Coords_1.BlockBits.BlockWall); - if (ranged) { - if (!Attack.getIntoPosition(unit, range, Coords_1.Collision.BLOCK_MISSILE, walk)) return Attack.Result.FAILED; - } else if (!Attack.getIntoPosition(unit, range, Coords_1.BlockBits.Ranged, walk)) { - return Attack.Result.FAILED; + if (ranged) { + if (!Attack.getIntoPosition(unit, range, Coords_1.Collision.BLOCK_MISSILE, walk)) return Attack.Result.FAILED; + } else if (!Attack.getIntoPosition(unit, range, Coords_1.BlockBits.Ranged, walk)) { + return Attack.Result.FAILED; + } } - } - if (!unit.dead && !checkCollision(me, unit, Coords_1.BlockBits.Ranged)) { - if (skill === sdk.skills.ChargedBolt) { - let preHealth = unit.hp; - let cRetry = 0; - unit.distance <= 1 && Attack.getIntoPosition(unit, range, Coords_1.Collision.BLOCK_MISSILE, true); - for (let i = 0; i < 3; i++) { - !unit.dead && Skill.cast(skill, Skill.getHand(skill), unit.x, unit.y); - if (!Misc.poll(() => unit.dead || unit.hp < preHealth, 300, 50)) { - cRetry++; - // we still might of missed so pick another coord - if (!Attack.getIntoPosition(unit, (range - cRetry), Coords_1.Collision.BLOCK_MISSILE, true)) return Attack.Result.FAILED; + if (!unit.dead && !checkCollision(me, unit, Coords_1.BlockBits.Ranged)) { + if (skill === sdk.skills.ChargedBolt) { + let preHealth = unit.hp; + let cRetry = 0; + unit.distance <= 1 && Attack.getIntoPosition(unit, range, Coords_1.Collision.BLOCK_MISSILE, true); + for (let i = 0; i < 3; i++) { !unit.dead && Skill.cast(skill, Skill.getHand(skill), unit.x, unit.y); - } else { - break; - } - } - } else if (skill === sdk.skills.StaticField) { - let preHealth = unit.hp; - let sRetry = 0; - for (let i = 0; i < 4; i++) { - if (!unit.dead) { - Skill.cast(skill, Skill.getHand(skill), unit); - if (!Misc.poll(() => unit.dead || unit.hp < preHealth, 200, 50)) { - sRetry++; + if (!Misc.poll(() => unit.dead || unit.hp < preHealth, 300, 50)) { + cRetry++; // we still might of missed so pick another coord - if (!Attack.getIntoPosition(unit, (range - sRetry), Coords_1.Collision.BLOCK_MISSILE, true)) return Attack.Result.FAILED; - !unit.dead && Skill.cast(skill, Skill.getHand(skill), unit); - } - - if (data.frostNova.have && me.mp > data.frostNova.mana) { - frostNovaCheck() && Skill.cast(sdk.skills.FrostNova, sdk.skills.hand.Right); - } - - if (mana > me.mp || unit.hpPercent < Config.CastStatic) { + if (!Attack.getIntoPosition(unit, (range - cRetry), Coords_1.Collision.BLOCK_MISSILE, true)) return Attack.Result.FAILED; + !unit.dead && Skill.cast(skill, Skill.getHand(skill), unit.x, unit.y); + } else { break; } - if (me.inDanger()) { - Attack.deploy(unit, range, 5, 9); + } + } else if (skill === sdk.skills.StaticField) { + let preHealth = unit.hp; + let sRetry = 0; + for (let i = 0; i < 4; i++) { + if (!unit.dead) { + Skill.cast(skill, Skill.getHand(skill), unit); + if (!Misc.poll(() => unit.dead || unit.hp < preHealth, 200, 50)) { + sRetry++; + // we still might of missed so pick another coord + if (!Attack.getIntoPosition(unit, (range - sRetry), Coords_1.Collision.BLOCK_MISSILE, true)) return Attack.Result.FAILED; + !unit.dead && Skill.cast(skill, Skill.getHand(skill), unit); + } + + if (data.frostNova.have && me.mp > data.frostNova.mana) { + frostNovaCheck() && Skill.cast(sdk.skills.FrostNova, sdk.skills.hand.Right); + } + + if (mana > me.mp || unit.hpPercent < Config.CastStatic) { + break; + } + if (me.inDanger()) { + Attack.deploy(unit, range, 5, 9); + break; + } + } else { break; } - } else { - break; - } - } - } else { - let targetPoint = GameData.targetPointForSkill(skill, unit); - - if (unit.attackable) { - if (targetPoint) { - Skill.cast(skill, Skill.getHand(skill), targetPoint.x, targetPoint.y); - } else { - Skill.cast(skill, Skill.getHand(skill), unit); } + } else { + let targetPoint = GameData.targetPointForSkill(skill, unit); + + if (unit.attackable) { + if (targetPoint) { + Skill.cast(skill, Skill.getHand(skill), targetPoint.x, targetPoint.y); + } else { + Skill.cast(skill, Skill.getHand(skill), unit); + } - if ([sdk.skills.FireBolt, sdk.skills.IceBolt].includes(skill)) { - let preHealth = unit.hp; - let missileDelay = GameData.timeTillMissleImpact(skill, unit); - missileDelay > 0 && Misc.poll(() => unit.dead || unit.hp < preHealth, missileDelay, 50); - delay(50); + if ([sdk.skills.FireBolt, sdk.skills.IceBolt].includes(skill)) { + let preHealth = unit.hp; + let missileDelay = GameData.timeTillMissleImpact(skill, unit); + missileDelay > 0 && Misc.poll(() => unit.dead || unit.hp < preHealth, missileDelay, 50); + delay(50); + } } } } - } - return Attack.Result.SUCCESS; - } else { - console.debug(choosenSkill); - noMana = true; - } - - for (let i = 0; i < 25; i++) { - if (!me.skillDelay) { - break; + return Attack.Result.SUCCESS; + } else { + console.debug(choosenSkill); + noMana = true; } - if (i % 5 === 0) { - if (me.inDanger()) { + + for (let i = 0; i < 25; i++) { + if (!me.skillDelay) { break; } - } + if (i % 5 === 0) { + if (me.inDanger()) { + break; + } + } - delay(40); - } + delay(40); + } - return noMana ? Attack.Result.NEEDMANA : Attack.Result.SUCCESS; -}; + return noMana ? Attack.Result.NEEDMANA : Attack.Result.SUCCESS; + }; +})(); diff --git a/libs/SoloPlay/Functions/DynamicTiers.js b/libs/SoloPlay/Functions/DynamicTiers.js index 6b25c201..2abaf2aa 100644 --- a/libs/SoloPlay/Functions/DynamicTiers.js +++ b/libs/SoloPlay/Functions/DynamicTiers.js @@ -11,657 +11,676 @@ * no point checking for stats that cannot ever exist. Also handle some of the misc stats that appear as they can be helpful. */ - -/** - * @param {ItemUnit} item - */ -const sumElementalDmg = function (item) { - if (!item) return 0; - let fire = item.getStatEx(sdk.stats.FireMinDamage) + item.getStatEx(sdk.stats.FireMaxDamage); - let light = item.getStatEx(sdk.stats.LightMinDamage) + item.getStatEx(sdk.stats.LightMaxDamage); - let magic = item.getStatEx(sdk.stats.MagicMinDamage) + item.getStatEx(sdk.stats.MagicMaxDamage); - let cold = item.getStatEx(sdk.stats.ColdMinDamage) + item.getStatEx(sdk.stats.ColdMaxDamage); - let poison = (item.getStatEx(sdk.stats.PoisonMinDamage) * 125 / 256); // PSN damage adjusted for damage per frame (125/256) - return (fire + light + magic + cold + poison); -}; - -/** - * @param {ItemUnit} item - */ -const mercscore = function (item) { - const mercWeights = { - IAS: 3.5, - MINDMG: 3, // min damage - MAXDMG: 3, // max damage - SECMINDMG: 3, // secondary min damage - SECMAXDMG: 3, // secondary max damage - ELEDMG: 2, // elemental damage - AR: 0.1, // attack rating - CB: 3, // crushing blow - OW: 3, // open wounds - LL: 8, //lifeleach - // CTC on attack - CTCOAAMP: 5, - CTCOADECREP: 10, - // CTC on striking - CTCOSAMP: 3, - CTCOSDECREP: 8, - // regen - HPREGEN: 2, - FHR: 3, // faster hit recovery - DEF: 0.05, // defense - HP: 2, - STR: 1.5, - DEX: 1.5, - ALL: 60, // + all skills - FR: 2, // fire resist - LR: 2, // lightning resist - CR: 1.5, // cold resist - PR: 1, // poison resist - ABS: 2.7, // absorb damage (fire light magic cold) - DR: 2, // Damage resist - MR: 3, // Magic damage resist +(function() { + /** + * @param {ItemUnit} item + */ + const sumElementalDmg = function (item) { + if (!item) return 0; + let fire = item.getStatEx(sdk.stats.FireMinDamage) + item.getStatEx(sdk.stats.FireMaxDamage); + let light = item.getStatEx(sdk.stats.LightMinDamage) + item.getStatEx(sdk.stats.LightMaxDamage); + let magic = item.getStatEx(sdk.stats.MagicMinDamage) + item.getStatEx(sdk.stats.MagicMaxDamage); + let cold = item.getStatEx(sdk.stats.ColdMinDamage) + item.getStatEx(sdk.stats.ColdMaxDamage); + let poison = (item.getStatEx(sdk.stats.PoisonMinDamage) * 125 / 256); // PSN damage adjusted for damage per frame (125/256) + return (fire + light + magic + cold + poison); }; - let mercRating = 1; - // start - item.prefixnum === sdk.locale.items.Treachery && (mercRating += item.getStatEx(sdk.stats.SkillWhenStruck, 2) * 1000); // fade - mercRating += item.getStatEx(sdk.stats.SkillOnAura, sdk.skills.Conviction) * 1000; // conviction aura - mercRating += item.getStatEx(sdk.stats.SkillOnAura, sdk.skills.Meditation) * 100; // meditation aura - mercRating += item.getStatEx(sdk.stats.AllSkills) * mercWeights.ALL; // add all skills - mercRating += item.getStatEx(sdk.stats.IAS) * mercWeights.IAS; // add IAS - mercRating += item.getStatEx(sdk.stats.ToHit) * mercWeights.AR; // add AR - mercRating += item.getStatEx(sdk.stats.CrushingBlow) * mercWeights.CB; // add crushing blow - mercRating += item.getStatEx(sdk.stats.OpenWounds) * mercWeights.OW; // add open wounds - mercRating += item.getStatEx(sdk.stats.LifeLeech) * mercWeights.LL; // add LL - mercRating += item.getStatEx(sdk.stats.HpRegen) * mercWeights.HPREGEN; // add hp regeneration - mercRating += item.getStatEx(sdk.stats.FHR) * mercWeights.FHR; // add faster hit recovery - mercRating += item.getStatEx(sdk.stats.Defense) * mercWeights.DEF; // add Defense - mercRating += item.getStatEx(sdk.stats.Strength) * mercWeights.STR; // add STR - mercRating += item.getStatEx(sdk.stats.Dexterity) * mercWeights.DEX; // add DEX - mercRating += item.getStatEx(sdk.stats.FireResist) * mercWeights.FR; // add FR - mercRating += item.getStatEx(sdk.stats.ColdResist) * mercWeights.CR; // add CR - mercRating += item.getStatEx(sdk.stats.LightResist) * mercWeights.LR; // add LR - mercRating += item.getStatEx(sdk.stats.PoisonResist) * mercWeights.PR; // add PR - mercRating += (item.getStatEx(sdk.stats.Vitality) + item.getStatEx(sdk.stats.MaxHp) + (item.getStatEx(sdk.stats.PerLevelHp) / 2048 * me.charlvl)) * mercWeights.HP; // add HP - mercRating += sumElementalDmg(item) * mercWeights.ELEDMG; // add elemental damage - mercRating += (item.getStatEx(sdk.stats.AbsorbFirePercent) + item.getStatEx(sdk.stats.AbsorbLightPercent) + item.getStatEx(sdk.stats.AbsorbMagicPercent) + item.getStatEx(sdk.stats.AbsorbColdPercent)) * mercWeights.ABS; // add absorb damage - mercRating += item.getStatEx(sdk.stats.NormalDamageReduction) * mercWeights.DR; // add integer damage resist - mercRating += item.getStatEx(sdk.stats.DamageResist) * mercWeights.DR * 2; // add damage resist % - mercRating += item.getStatEx(sdk.stats.MagicDamageReduction) * mercWeights.MR; // add integer magic damage resist - mercRating += item.getStatEx(sdk.stats.MagicResist) * mercWeights.MR * 2; // add magic damage resist % - - if (!myData) { - myData = CharData.getStats(); - } - - switch (myData.merc.classid) { - case sdk.mercs.Rogue: - case sdk.mercs.IronWolf: - mercRating += item.getStatEx(sdk.stats.MinDamage) * mercWeights.MINDMG; // add MIN damage - mercRating += item.getStatEx(sdk.stats.MaxDamage) * mercWeights.MAXDMG; // add MAX damage - - break; - case sdk.mercs.A5Barb: - if ([item.getStatEx(sdk.stats.SecondaryMinDamage), item.getStatEx(sdk.stats.SecondaryMaxDamage)].includes(0)) { + /** + * @param {ItemUnit} item + */ + const mercscore = function (item) { + const mercWeights = { + IAS: 3.5, + MINDMG: 3, // min damage + MAXDMG: 3, // max damage + SECMINDMG: 3, // secondary min damage + SECMAXDMG: 3, // secondary max damage + ELEDMG: 2, // elemental damage + AR: 0.1, // attack rating + CB: 3, // crushing blow + OW: 3, // open wounds + LL: 8, //lifeleach + // CTC on attack + CTCOAAMP: 5, + CTCOADECREP: 10, + // CTC on striking + CTCOSAMP: 3, + CTCOSDECREP: 8, + // regen + HPREGEN: 2, + FHR: 3, // faster hit recovery + DEF: 0.05, // defense + HP: 2, + STR: 1.5, + DEX: 1.5, + ALL: 60, // + all skills + FR: 2, // fire resist + LR: 2, // lightning resist + CR: 1.5, // cold resist + PR: 1, // poison resist + ABS: 2.7, // absorb damage (fire light magic cold) + DR: 2, // Damage resist + MR: 3, // Magic damage resist + }; + + let mercRating = 1; + // start + item.prefixnum === sdk.locale.items.Treachery && (mercRating += item.getStatEx(sdk.stats.SkillWhenStruck, 2) * 1000); // fade + mercRating += item.getStatEx(sdk.stats.SkillOnAura, sdk.skills.Conviction) * 1000; // conviction aura + mercRating += item.getStatEx(sdk.stats.SkillOnAura, sdk.skills.Meditation) * 100; // meditation aura + mercRating += item.getStatEx(sdk.stats.AllSkills) * mercWeights.ALL; // add all skills + mercRating += item.getStatEx(sdk.stats.IAS) * mercWeights.IAS; // add IAS + mercRating += item.getStatEx(sdk.stats.ToHit) * mercWeights.AR; // add AR + mercRating += item.getStatEx(sdk.stats.CrushingBlow) * mercWeights.CB; // add crushing blow + mercRating += item.getStatEx(sdk.stats.OpenWounds) * mercWeights.OW; // add open wounds + mercRating += item.getStatEx(sdk.stats.LifeLeech) * mercWeights.LL; // add LL + mercRating += item.getStatEx(sdk.stats.HpRegen) * mercWeights.HPREGEN; // add hp regeneration + mercRating += item.getStatEx(sdk.stats.FHR) * mercWeights.FHR; // add faster hit recovery + mercRating += item.getStatEx(sdk.stats.Defense) * mercWeights.DEF; // add Defense + mercRating += item.getStatEx(sdk.stats.Strength) * mercWeights.STR; // add STR + mercRating += item.getStatEx(sdk.stats.Dexterity) * mercWeights.DEX; // add DEX + mercRating += item.getStatEx(sdk.stats.FireResist) * mercWeights.FR; // add FR + mercRating += item.getStatEx(sdk.stats.ColdResist) * mercWeights.CR; // add CR + mercRating += item.getStatEx(sdk.stats.LightResist) * mercWeights.LR; // add LR + mercRating += item.getStatEx(sdk.stats.PoisonResist) * mercWeights.PR; // add PR + mercRating += (item.getStatEx(sdk.stats.Vitality) + item.getStatEx(sdk.stats.MaxHp) + (item.getStatEx(sdk.stats.PerLevelHp) / 2048 * me.charlvl)) * mercWeights.HP; // add HP + mercRating += sumElementalDmg(item) * mercWeights.ELEDMG; // add elemental damage + mercRating += (item.getStatEx(sdk.stats.AbsorbFirePercent) + item.getStatEx(sdk.stats.AbsorbLightPercent) + item.getStatEx(sdk.stats.AbsorbMagicPercent) + item.getStatEx(sdk.stats.AbsorbColdPercent)) * mercWeights.ABS; // add absorb damage + mercRating += item.getStatEx(sdk.stats.NormalDamageReduction) * mercWeights.DR; // add integer damage resist + mercRating += item.getStatEx(sdk.stats.DamageResist) * mercWeights.DR * 2; // add damage resist % + mercRating += item.getStatEx(sdk.stats.MagicDamageReduction) * mercWeights.MR; // add integer magic damage resist + mercRating += item.getStatEx(sdk.stats.MagicResist) * mercWeights.MR * 2; // add magic damage resist % + + if (!myData) { + myData = CharData.getStats(); + } + + switch (myData.merc.classid) { + case sdk.mercs.Rogue: + case sdk.mercs.IronWolf: mercRating += item.getStatEx(sdk.stats.MinDamage) * mercWeights.MINDMG; // add MIN damage mercRating += item.getStatEx(sdk.stats.MaxDamage) * mercWeights.MAXDMG; // add MAX damage + break; + case sdk.mercs.A5Barb: + if ([item.getStatEx(sdk.stats.SecondaryMinDamage), item.getStatEx(sdk.stats.SecondaryMaxDamage)].includes(0)) { + mercRating += item.getStatEx(sdk.stats.MinDamage) * mercWeights.MINDMG; // add MIN damage + mercRating += item.getStatEx(sdk.stats.MaxDamage) * mercWeights.MAXDMG; // add MAX damage + + break; + } + // eslint-disable-next-line no-fallthrough + case sdk.mercs.Guard: + default: + mercRating += item.getStatEx(sdk.stats.SecondaryMinDamage) * mercWeights.SECMINDMG; + mercRating += item.getStatEx(sdk.stats.SecondaryMaxDamage) * mercWeights.SECMAXDMG; + break; } - // eslint-disable-next-line no-fallthrough - case sdk.mercs.Guard: - default: - mercRating += item.getStatEx(sdk.stats.SecondaryMinDamage) * mercWeights.SECMINDMG; - mercRating += item.getStatEx(sdk.stats.SecondaryMaxDamage) * mercWeights.SECMAXDMG; - - break; - } - - if (!me.sorceress && !me.necromancer && !me.assassin) { - mercRating += item.getStatEx(sdk.stats.SkillOnAttack, 4238) * mercWeights.CTCOAAMP; // add CTC amplify damage on attack - mercRating += item.getStatEx(sdk.stats.SkillOnAttack, 4225) * mercWeights.CTCOAAMP; // add CTC amplify damage on attack (magic items) - mercRating += item.getStatEx(sdk.stats.SkillOnAttack, 5583) * mercWeights.CTCOADECREP; // add CTC decrepify on attack - mercRating += item.getStatEx(sdk.stats.SkillOnAttack, 5631) * mercWeights.CTCOADECREP; // add CTC decrepify on attack (magic items) - mercRating += item.getStatEx(sdk.stats.SkillOnHit, 4238) * mercWeights.CTCOSAMP; // add CTC amplify damage on strikng - mercRating += item.getStatEx(sdk.stats.SkillOnHit, 4225) * mercWeights.CTCOSAMP; // add CTC amplify damage on strikng (magic items) - mercRating += item.getStatEx(sdk.stats.SkillOnHit, 5583) * mercWeights.CTCOSDECREP; // add CTC decrepify on strikng - mercRating += item.getStatEx(sdk.stats.SkillOnHit, 5631) * mercWeights.CTCOSDECREP; // add CTC decrepify on strikng (magic items) - } - - for (let x = 0; x < Config.Runewords.length; x += 1) { - let [sockets, baseCID] = [Config.Runewords[x][0].length, Config.Runewords[x][1]]; - if (item.classid === baseCID && item.isBaseType && item.sockets === sockets && !item.isRuneword) return -1; - } - - return mercRating; -}; -/** - * @param {ItemUnit} item - * @param {number} [skillId] - * @param {object} [buildInfo] - */ -const chargeditemscore = function (item, skillId, buildInfo) { - if (!item) return 0; + if (!me.sorceress && !me.necromancer && !me.assassin) { + mercRating += item.getStatEx(sdk.stats.SkillOnAttack, 4238) * mercWeights.CTCOAAMP; // add CTC amplify damage on attack + mercRating += item.getStatEx(sdk.stats.SkillOnAttack, 4225) * mercWeights.CTCOAAMP; // add CTC amplify damage on attack (magic items) + mercRating += item.getStatEx(sdk.stats.SkillOnAttack, 5583) * mercWeights.CTCOADECREP; // add CTC decrepify on attack + mercRating += item.getStatEx(sdk.stats.SkillOnAttack, 5631) * mercWeights.CTCOADECREP; // add CTC decrepify on attack (magic items) + mercRating += item.getStatEx(sdk.stats.SkillOnHit, 4238) * mercWeights.CTCOSAMP; // add CTC amplify damage on strikng + mercRating += item.getStatEx(sdk.stats.SkillOnHit, 4225) * mercWeights.CTCOSAMP; // add CTC amplify damage on strikng (magic items) + mercRating += item.getStatEx(sdk.stats.SkillOnHit, 5583) * mercWeights.CTCOSDECREP; // add CTC decrepify on strikng + mercRating += item.getStatEx(sdk.stats.SkillOnHit, 5631) * mercWeights.CTCOSDECREP; // add CTC decrepify on strikng (magic items) + } - let tier = 0; - !buildInfo && (buildInfo = Check.currentBuild()); + for (let x = 0; x < Config.Runewords.length; x += 1) { + let [sockets, baseCID] = [Config.Runewords[x][0].length, Config.Runewords[x][1]]; + if (item.classid === baseCID && item.isBaseType && item.sockets === sockets && !item.isRuneword) return -1; + } - const chargedWeights = { - Teleport: Pather.canTeleport() ? 0 : 5, - Enchant: buildInfo.caster ? 0 : 10, - InnerSight: me.amazon || buildInfo.caster ? 0 : 10, - SlowMissiles: me.amazon ? 0 : 10, + return mercRating; }; - let stats = item.getStat(-2); - let chargedItems = []; - - if (stats.hasOwnProperty(sdk.stats.ChargedSkill)) { - if (stats[sdk.stats.ChargedSkill] instanceof Array) { - for (let i = 0; i < stats[sdk.stats.ChargedSkill].length; i++) { - if (stats[sdk.stats.ChargedSkill][i] !== undefined) { - chargedItems.push({ - skill: stats[sdk.stats.ChargedSkill][i].skill, - level: stats[sdk.stats.ChargedSkill][i].level, - charges: stats[sdk.stats.ChargedSkill][i].charges, - maxcharges: stats[sdk.stats.ChargedSkill][i].maxcharges - }); + /** + * @param {ItemUnit} item + * @param {number} [skillId] + * @param {object} [buildInfo] + */ + const chargeditemscore = function (item, skillId, buildInfo) { + if (!item) return 0; + + let tier = 0; + !buildInfo && (buildInfo = Check.currentBuild()); + + const chargedWeights = { + Teleport: Pather.canTeleport() ? 0 : 5, + Enchant: buildInfo.caster ? 0 : 10, + InnerSight: me.amazon || buildInfo.caster ? 0 : 10, + SlowMissiles: me.amazon ? 0 : 10, + }; + + let stats = item.getStat(-2); + let chargedItems = []; + + if (stats.hasOwnProperty(sdk.stats.ChargedSkill)) { + if (stats[sdk.stats.ChargedSkill] instanceof Array) { + for (let i = 0; i < stats[sdk.stats.ChargedSkill].length; i++) { + if (stats[sdk.stats.ChargedSkill][i] !== undefined) { + chargedItems.push({ + skill: stats[sdk.stats.ChargedSkill][i].skill, + level: stats[sdk.stats.ChargedSkill][i].level, + charges: stats[sdk.stats.ChargedSkill][i].charges, + maxcharges: stats[sdk.stats.ChargedSkill][i].maxcharges + }); + } } + } else { + chargedItems.push({ + skill: stats[sdk.stats.ChargedSkill].skill, + level: stats[sdk.stats.ChargedSkill].level, + charges: stats[sdk.stats.ChargedSkill].charges, + maxcharges: stats[sdk.stats.ChargedSkill].maxcharges + }); } + } + + chargedItems = chargedItems.filter((v, i, a) => a.findIndex(el => ["skill", "level"].every(k => el[k] === v[k])) === i); + + if (skillId > 0) { + chargedItems = chargedItems.filter(check => check.skill === skillId); + chargedItems.forEach(el => tier += el.level * 5); } else { - chargedItems.push({ - skill: stats[sdk.stats.ChargedSkill].skill, - level: stats[sdk.stats.ChargedSkill].level, - charges: stats[sdk.stats.ChargedSkill].charges, - maxcharges: stats[sdk.stats.ChargedSkill].maxcharges + chargedItems.forEach(function (el) { + try { + let skillName = getSkillById(el.skill).split(" ").join(""); + if (skillName === "Teleport") { + chargedWeights[skillName] > 0 && (tier += el.maxcharges * 2); + } else if (!!chargedWeights[skillName]) { + tier += el.level * chargedWeights[skillName]; + } + } catch (e) { + return; + } }); } - } - - chargedItems = chargedItems.filter((v, i, a) => a.findIndex(el => ["skill", "level"].every(k => el[k] === v[k])) === i); - - if (skillId > 0) { - chargedItems = chargedItems.filter(check => check.skill === skillId); - chargedItems.forEach(el => tier += el.level * 5); - } else { - chargedItems.forEach(function (el) { - try { - let skillName = getSkillById(el.skill).split(" ").join(""); - if (skillName === "Teleport") { - chargedWeights[skillName] > 0 && (tier += el.maxcharges * 2); - } else if (!!chargedWeights[skillName]) { - tier += el.level * chargedWeights[skillName]; - } - } catch (e) { - return; - } - }); - } - - return tier; -}; - -const tierWeights = { - useHardcoreWeights: !!(me.hardcore), - resistWeights: { - FR: (this.useHardcoreWeights ? 5 : 3), // fire resist - LR: (this.useHardcoreWeights ? 5 : 3), // lightning resist - CR: (this.useHardcoreWeights ? 3 : 1.5), // cold resist - PR: (this.useHardcoreWeights ? 5 : 1), // poison resist - MAXFR: 5, - MAXLR: 5, - MAXCR: 3, - MAXPR: 3, - ABS: (this.useHardcoreWeights ? 5 : 3), // absorb damage (fire light magic cold) - DR: (this.useHardcoreWeights ? 4 : 2), // Damage resist - MR: 3, // Magic damage resist - }, - - generalWeights: { - CBF: 25, // cannot be frozen - FRW: 1, // faster run/walk - FHR: 3, // faster hit recovery - DEF: 0.05, // defense - ICB: 2, // increased chance to block - BELTSLOTS: 2, // belt potion storage - MF: 1, //Magic Find - // base stats - HP: 0.5, - MANA: 0.5, - STR: 1, - DEX: 1, - }, + + return tier; + }; + + const tierWeights = { + useHardcoreWeights: !!(me.hardcore), + resistWeights: { + FR: (this.useHardcoreWeights ? 5 : 3), // fire resist + LR: (this.useHardcoreWeights ? 5 : 3), // lightning resist + CR: (this.useHardcoreWeights ? 3 : 1.5), // cold resist + PR: (this.useHardcoreWeights ? 5 : 1), // poison resist + MAXFR: 5, + MAXLR: 5, + MAXCR: 3, + MAXPR: 3, + ABS: (this.useHardcoreWeights ? 5 : 3), // absorb damage (fire light magic cold) + DR: (this.useHardcoreWeights ? 4 : 2), // Damage resist + MR: 3, // Magic damage resist + }, + + generalWeights: { + CBF: 25, // cannot be frozen + FRW: 1, // faster run/walk + FHR: 3, // faster hit recovery + DEF: 0.05, // defense + ICB: 2, // increased chance to block + BELTSLOTS: 2, // belt potion storage + MF: 1, //Magic Find + // base stats + HP: 0.5, + MANA: 0.5, + STR: 1, + DEX: 1, + }, - casterWeights: { + casterWeights: { //breakpoint stats - FCR: (me.assassin ? 2 : 5), - IAS: (me.assassin ? 4 : 0), - // regen - HPREGEN: 2, - MANAREGEN: 2.2, - }, + FCR: (me.assassin ? 2 : 5), + IAS: (me.assassin ? 4 : 0), + // regen + HPREGEN: 2, + MANAREGEN: 2.2, + }, - meleeWeights: { + meleeWeights: { // breakpoint stats - todo actually take breakpoints into account - FCR: 0.5, - IAS: (me.barbarian && me.classic ? 2 : 4), - // Attack - MINDMG: 3, - MAXDMG: 3, - SECMINDMG: 2, - SECMAXDMG: 2, - AVGDMG: 3, - ELEDMG: 0.5, - AR: 0.2, - CB: 4, - OW: 1, - DS: 1.5, - DMGTOUNDEAD: 0.5, - DMGTODEMONS: 0.5, - - // leaching - LL: 4, - ML: 2, - - // regen - HPREGEN: 2, - MANAREGEN: 2, - }, + FCR: 0.5, + IAS: (me.barbarian && me.classic ? 2 : 4), + // Attack + MINDMG: 3, + MAXDMG: 3, + SECMINDMG: 2, + SECMAXDMG: 2, + AVGDMG: 3, + ELEDMG: 0.5, + AR: 0.2, + CB: 4, + OW: 1, + DS: 1.5, + DMGTOUNDEAD: 0.5, + DMGTODEMONS: 0.5, + + // leaching + LL: 4, + ML: 2, + + // regen + HPREGEN: 2, + MANAREGEN: 2, + }, - ctcWeights: { - whenStruck: 2, - onAttack: 2, - onStrike: 1, - skills: { + ctcWeights: { + whenStruck: 2, + onAttack: 2, + onStrike: 1, + skills: { // Sorc skills - Nova: 2, - FrostNova: 4, - IceBlast: 4, - ChargedBolt: 4, - StaticField: 5, - GlacialSpike: 6, - ChainLightning: 6, - Blizzard: 4, - FrozenOrb: 8, - Hydra: 4, - // Necro skills - AmplifyDamage: 5, - Decrepify: 10, - LifeTap: 10, - BoneArmor: 10, - BoneSpear: 8, - BoneSpirit: 8, - PoisonNova: 10, - // Barb skills - Taunt: 5, - Howl: 5, - // Druid skills - CycloneArmor: 10, - Twister: 5, - // Sin skills - Fade: 10, - Venom: 8, - } - }, + Nova: 2, + FrostNova: 4, + IceBlast: 4, + ChargedBolt: 4, + StaticField: 5, + GlacialSpike: 6, + ChainLightning: 6, + Blizzard: 4, + FrozenOrb: 8, + Hydra: 4, + // Necro skills + AmplifyDamage: 5, + Decrepify: 10, + LifeTap: 10, + BoneArmor: 10, + BoneSpear: 8, + BoneSpirit: 8, + PoisonNova: 10, + // Barb skills + Taunt: 5, + Howl: 5, + // Druid skills + CycloneArmor: 10, + Twister: 5, + // Sin skills + Fade: 10, + Venom: 8, + } + }, - skillsWeights: { - ALL: 200, - CLASS: 175, - TAB: 125, - WANTED: 45, - USEFUL: 30, // + wanted supportive skills - }, - - charmWeights: { - ALL: 180, // + all skills - CLASS: 175, // + class tab - TAB: 300, // + skill tab - FR: 3, // fire resist - LR: 5, // lightning resist - CR: 2, // cold resist - PR: 0.5, // poison resist - FRW: 1, // faster run/walk - FHR: (me.barbarian ? 4 : 2), // faster hit recovery - DEF: 0.05, // defense - MF: 2, //Magic Find - // base stats - HP: 1.75, - MANA: 0.8, - STR: 1.0, - DEX: 1.0, - } -}; + skillsWeights: { + ALL: 200, + CLASS: 175, + TAB: 125, + WANTED: 45, + USEFUL: 30, // + wanted supportive skills + }, + + charmWeights: { + ALL: 180, // + all skills + CLASS: 175, // + class tab + TAB: 300, // + skill tab + FR: 3, // fire resist + LR: 5, // lightning resist + CR: 2, // cold resist + PR: 0.5, // poison resist + FRW: 1, // faster run/walk + FHR: (me.barbarian ? 4 : 2), // faster hit recovery + DEF: 0.05, // defense + MF: 2, //Magic Find + // base stats + HP: 1.75, + MANA: 0.8, + STR: 1.0, + DEX: 1.0, + } + }; -/** - * @param {ItemUnit} item - * @param {number} [bodyloc] - */ -const tierscore = function (item, bodyloc) { - const buildInfo = Check.currentBuild(); + /** + * @param {ItemUnit} item + * @param {number} [bodyloc] + */ + const tierscore = function (item, bodyloc) { + const buildInfo = Check.currentBuild(); - const generalScore = () => { - let generalRating = 0; - let canTele = Pather.canTeleport(); + const generalScore = () => { + let generalRating = 0; + let canTele = Pather.canTeleport(); - // get item body location - let itembodyloc = Item.getBodyLoc(item); - bodyloc === undefined && (bodyloc = itembodyloc.last()); // extract bodyloc from array + // get item body location + let itembodyloc = Item.getBodyLoc(item); + bodyloc === undefined && (bodyloc = itembodyloc.last()); // extract bodyloc from array - // get item cbf stat from olditem equipped on body location - let eqItem = me.getEquippedItem(bodyloc); + // get item cbf stat from olditem equipped on body location + let eqItem = me.getEquippedItem(bodyloc); - if (!canTele && item.getStatEx(sdk.stats.CannotbeFrozen)) { + if (!canTele && item.getStatEx(sdk.stats.CannotbeFrozen)) { // check if we have cbf but make sure its not from the item we are trying to un-equip - let haveCBF = (me.getStat(sdk.stats.CannotbeFrozen) > 0 && (eqItem && !eqItem.getStatEx(sdk.stats.CannotbeFrozen))); - // Cannot be frozen is very important for Melee chars - !haveCBF && (generalRating += buildInfo.caster ? tierWeights.generalWeights.CBF : tierWeights.generalWeights.CBF * 4); - } - - // faster run/walk - !canTele && (generalRating += item.getStatEx(sdk.stats.FRW) * tierWeights.generalWeights.FRW); + let haveCBF = (me.getStat(sdk.stats.CannotbeFrozen) > 0 && (eqItem && !eqItem.getStatEx(sdk.stats.CannotbeFrozen))); + // Cannot be frozen is very important for Melee chars + !haveCBF && (generalRating += buildInfo.caster ? tierWeights.generalWeights.CBF : tierWeights.generalWeights.CBF * 4); + } - // belt slots - if (item.itemType === sdk.items.type.Belt) { - const beltSize = ["lbl", "vbl"].includes(item.code) ? 2 : ["mbl", "tbl"].includes(item.code) ? 3 : 4; - // if our current belt-size is better, don't down-grade even if the other stats on the new item are better, not worth the town visits - generalRating += (Storage.BeltSize() > beltSize ? -50 : (beltSize * 4 * tierWeights.generalWeights.BELTSLOTS)); - } + // faster run/walk + !canTele && (generalRating += item.getStatEx(sdk.stats.FRW) * tierWeights.generalWeights.FRW); - // start generalRating - generalRating += item.getStatEx(sdk.stats.MagicBonus) * tierWeights.generalWeights.MF; // add magic find - generalRating += item.getStatEx(sdk.stats.FHR) * tierWeights.generalWeights.FHR; // add faster hit recovery - generalRating += item.getStatEx(sdk.stats.Defense) * tierWeights.generalWeights.DEF; // add Defense - generalRating += (item.getStatEx(sdk.stats.ToBlock) + item.getStatEx(sdk.stats.FBR)) * tierWeights.generalWeights.ICB; //add increased chance to block - generalRating += (item.getStatEx(sdk.stats.Vitality) + item.getStatEx(sdk.stats.MaxHp) + (item.getStatEx(sdk.stats.PerLevelHp) / 2048 * me.charlvl)) * tierWeights.generalWeights.HP; // add HP - generalRating += (item.getStatEx(sdk.stats.Energy) + item.getStatEx(sdk.stats.MaxMana) + (item.getStatEx(sdk.stats.PerLevelMana) / 2048 * me.charlvl)) * tierWeights.generalWeights.MANA;// add mana - generalRating += item.getStatEx(sdk.stats.Strength) * tierWeights.generalWeights.STR; // add STR - generalRating += item.getStatEx(sdk.stats.Dexterity) * tierWeights.generalWeights.DEX; // add DEX - - return generalRating; - }; + // belt slots + if (item.itemType === sdk.items.type.Belt) { + const beltSize = ["lbl", "vbl"].includes(item.code) ? 2 : ["mbl", "tbl"].includes(item.code) ? 3 : 4; + // if our current belt-size is better, don't down-grade even if the other stats on the new item are better, not worth the town visits + generalRating += (Storage.BeltSize() > beltSize ? -50 : (beltSize * 4 * tierWeights.generalWeights.BELTSLOTS)); + } - const resistScore = () => { - let itembodyloc = Item.getBodyLoc(item); - if (!itembodyloc) return 0; - bodyloc === undefined && (bodyloc = itembodyloc.last()); // extract bodyloc from array - - let resistRating = 0; - // current total resists - let [currFR, currCR, currLR, currPR] = [me.fireRes, me.coldRes, me.lightRes, me.poisonRes]; - // get item resists stats from olditem equipped on body location - let eqItem = me.getEquippedItem(bodyloc); - let [olditemFR, olditemCR, olditemLR, olditemPR] = [0, 0, 0, 0]; - if (eqItem) { + // start generalRating + generalRating += item.getStatEx(sdk.stats.MagicBonus) * tierWeights.generalWeights.MF; // add magic find + generalRating += item.getStatEx(sdk.stats.FHR) * tierWeights.generalWeights.FHR; // add faster hit recovery + generalRating += item.getStatEx(sdk.stats.Defense) * tierWeights.generalWeights.DEF; // add Defense + generalRating += (item.getStatEx(sdk.stats.ToBlock) + item.getStatEx(sdk.stats.FBR)) * tierWeights.generalWeights.ICB; //add increased chance to block + generalRating += (item.getStatEx(sdk.stats.Vitality) + item.getStatEx(sdk.stats.MaxHp) + (item.getStatEx(sdk.stats.PerLevelHp) / 2048 * me.charlvl)) * tierWeights.generalWeights.HP; // add HP + generalRating += (item.getStatEx(sdk.stats.Energy) + item.getStatEx(sdk.stats.MaxMana) + (item.getStatEx(sdk.stats.PerLevelMana) / 2048 * me.charlvl)) * tierWeights.generalWeights.MANA;// add mana + generalRating += item.getStatEx(sdk.stats.Strength) * tierWeights.generalWeights.STR; // add STR + generalRating += item.getStatEx(sdk.stats.Dexterity) * tierWeights.generalWeights.DEX; // add DEX + + return generalRating; + }; + + const resistScore = () => { + let itembodyloc = Item.getBodyLoc(item); + if (!itembodyloc) return 0; + bodyloc === undefined && (bodyloc = itembodyloc.last()); // extract bodyloc from array + + let resistRating = 0; + // current total resists + let [currFR, currCR, currLR, currPR] = [me.fireRes, me.coldRes, me.lightRes, me.poisonRes]; + // get item resists stats from olditem equipped on body location + let eqItem = me.getEquippedItem(bodyloc); + let [olditemFR, olditemCR, olditemLR, olditemPR] = [0, 0, 0, 0]; + if (eqItem) { // equipped resists - [olditemFR, olditemCR] = [eqItem.getStatEx(sdk.stats.FireResist), eqItem.getStatEx(sdk.stats.ColdResist)]; - [olditemLR, olditemPR] = [eqItem.getStatEx(sdk.stats.LightResist), eqItem.getStatEx(sdk.stats.PoisonResist)]; - } - // subtract olditem resists from current total resists - let [baseFR, baseCR, baseLR, basePR] = [(currFR - olditemFR), (currCR - olditemCR), (currLR - olditemLR), (currPR - olditemPR)]; - // if baseRes < max resists give score value upto max resists reached - const maxRes = me.hell ? 75 : (75 + me.getResPenalty(me.diff + 1) - me.getResPenalty(me.diff)); - let [FRlimit, CRlimit, LRlimit, PRlimit] = [Math.max(maxRes - baseFR, 0), Math.max(maxRes - baseCR, 0), Math.max(maxRes - baseLR, 0), Math.max(maxRes - basePR, 0)]; - // get new item stats - let [newitemFR, newitemCR] = [Math.max(item.getStatEx(sdk.stats.FireResist), 0), Math.max(item.getStatEx(sdk.stats.ColdResist), 0)]; - let [newitemLR, newitemPR] = [Math.max(item.getStatEx(sdk.stats.LightResist), 0), Math.max(item.getStatEx(sdk.stats.PoisonResist), 0)]; - // newitemRes upto reslimit - let [effectiveFR, effectiveCR] = [Math.min(newitemFR, FRlimit), Math.min(newitemCR, CRlimit)]; - let [effectiveLR, effectivePR] = [Math.min(newitemLR, LRlimit), Math.min(newitemPR, PRlimit)]; - // sum resistRatings - resistRating += effectiveFR * tierWeights.resistWeights.FR; // add fireresist - resistRating += effectiveCR * tierWeights.resistWeights.CR; // add coldresist - resistRating += effectiveLR * tierWeights.resistWeights.LR; // add literesist - resistRating += effectivePR * tierWeights.resistWeights.PR; // add poisonresist - // sum max resists weights - resistRating += (item.getStatEx(sdk.stats.MaxFireResist) * tierWeights.resistWeights.MAXFR); - resistRating += (item.getStatEx(sdk.stats.MaxLightResist) * tierWeights.resistWeights.MAXLR); - resistRating += (item.getStatEx(sdk.stats.MaxColdResist) * tierWeights.resistWeights.MAXCR); - resistRating += (item.getStatEx(sdk.stats.MaxPoisonResist) * tierWeights.resistWeights.MAXPR); - // sum absorb and magic/damage reduction - resistRating += (item.getStatEx(sdk.stats.AbsorbFirePercent) + item.getStatEx(sdk.stats.AbsorbLightPercent) + item.getStatEx(sdk.stats.AbsorbMagicPercent) + item.getStatEx(sdk.stats.AbsorbColdPercent)) * tierWeights.resistWeights.ABS; // add absorb damage - resistRating += item.getStatEx(sdk.stats.NormalDamageReduction) * tierWeights.resistWeights.DR / 2; // add integer damage resist - resistRating += item.getStatEx(sdk.stats.DamageResist) * tierWeights.resistWeights.DR * 2; // add damage resist % - resistRating += item.getStatEx(sdk.stats.MagicDamageReduction) * tierWeights.resistWeights.MR / 2; // add integer magic damage resist - resistRating += item.getStatEx(sdk.stats.MagicResist) * tierWeights.resistWeights.MR * 2; // add magic damage resist % - - return resistRating; - }; - - const buildScore = () => { - let buildRating = 0; - let buildWeights = buildInfo.caster ? tierWeights.casterWeights : tierWeights.meleeWeights; - - me.amazon && item.getStatEx(sdk.stats.ReplenishQuantity) && (buildRating += 50); - //!Pather.canTeleport() && item.getStatEx(sdk.stats.ChargedSkill, 3461) && (buildRating += 50); - - buildRating += item.getStatEx(sdk.stats.FCR) * buildWeights.FCR; // add FCR - buildRating += item.getStatEx(sdk.stats.IAS) * buildWeights.IAS; // add IAS - buildRating += item.getStatEx(sdk.stats.HpRegen) * buildWeights.HPREGEN; // add hp regeneration - buildRating += item.getStatEx(sdk.stats.ManaRecovery) * buildWeights.MANAREGEN; // add mana recovery - !item.isRuneword && (buildRating += (item.sockets * 10)); // priortize sockets - - // pierce/mastery's not sure how I want to weight this so for now just its base value - buildInfo.usefulStats.forEach(stat => buildRating += item.getStatEx(stat)); - - // Melee Specific - if (!buildInfo.caster + [olditemFR, olditemCR] = [eqItem.getStatEx(sdk.stats.FireResist), eqItem.getStatEx(sdk.stats.ColdResist)]; + [olditemLR, olditemPR] = [eqItem.getStatEx(sdk.stats.LightResist), eqItem.getStatEx(sdk.stats.PoisonResist)]; + } + // subtract olditem resists from current total resists + let [baseFR, baseCR, baseLR, basePR] = [(currFR - olditemFR), (currCR - olditemCR), (currLR - olditemLR), (currPR - olditemPR)]; + // if baseRes < max resists give score value upto max resists reached + const maxRes = me.hell ? 75 : (75 + me.getResPenalty(me.diff + 1) - me.getResPenalty(me.diff)); + let [FRlimit, CRlimit, LRlimit, PRlimit] = [Math.max(maxRes - baseFR, 0), Math.max(maxRes - baseCR, 0), Math.max(maxRes - baseLR, 0), Math.max(maxRes - basePR, 0)]; + // get new item stats + let [newitemFR, newitemCR] = [Math.max(item.getStatEx(sdk.stats.FireResist), 0), Math.max(item.getStatEx(sdk.stats.ColdResist), 0)]; + let [newitemLR, newitemPR] = [Math.max(item.getStatEx(sdk.stats.LightResist), 0), Math.max(item.getStatEx(sdk.stats.PoisonResist), 0)]; + // newitemRes upto reslimit + let [effectiveFR, effectiveCR] = [Math.min(newitemFR, FRlimit), Math.min(newitemCR, CRlimit)]; + let [effectiveLR, effectivePR] = [Math.min(newitemLR, LRlimit), Math.min(newitemPR, PRlimit)]; + // sum resistRatings + resistRating += effectiveFR * tierWeights.resistWeights.FR; // add fireresist + resistRating += effectiveCR * tierWeights.resistWeights.CR; // add coldresist + resistRating += effectiveLR * tierWeights.resistWeights.LR; // add literesist + resistRating += effectivePR * tierWeights.resistWeights.PR; // add poisonresist + // sum max resists weights + resistRating += (item.getStatEx(sdk.stats.MaxFireResist) * tierWeights.resistWeights.MAXFR); + resistRating += (item.getStatEx(sdk.stats.MaxLightResist) * tierWeights.resistWeights.MAXLR); + resistRating += (item.getStatEx(sdk.stats.MaxColdResist) * tierWeights.resistWeights.MAXCR); + resistRating += (item.getStatEx(sdk.stats.MaxPoisonResist) * tierWeights.resistWeights.MAXPR); + // sum absorb and magic/damage reduction + resistRating += (item.getStatEx(sdk.stats.AbsorbFirePercent) + item.getStatEx(sdk.stats.AbsorbLightPercent) + item.getStatEx(sdk.stats.AbsorbMagicPercent) + item.getStatEx(sdk.stats.AbsorbColdPercent)) * tierWeights.resistWeights.ABS; // add absorb damage + resistRating += item.getStatEx(sdk.stats.NormalDamageReduction) * tierWeights.resistWeights.DR / 2; // add integer damage resist + resistRating += item.getStatEx(sdk.stats.DamageResist) * tierWeights.resistWeights.DR * 2; // add damage resist % + resistRating += item.getStatEx(sdk.stats.MagicDamageReduction) * tierWeights.resistWeights.MR / 2; // add integer magic damage resist + resistRating += item.getStatEx(sdk.stats.MagicResist) * tierWeights.resistWeights.MR * 2; // add magic damage resist % + + return resistRating; + }; + + const buildScore = () => { + let buildRating = 0; + let buildWeights = buildInfo.caster ? tierWeights.casterWeights : tierWeights.meleeWeights; + + me.amazon && item.getStatEx(sdk.stats.ReplenishQuantity) && (buildRating += 50); + //!Pather.canTeleport() && item.getStatEx(sdk.stats.ChargedSkill, 3461) && (buildRating += 50); + + buildRating += item.getStatEx(sdk.stats.FCR) * buildWeights.FCR; // add FCR + buildRating += item.getStatEx(sdk.stats.IAS) * buildWeights.IAS; // add IAS + buildRating += item.getStatEx(sdk.stats.HpRegen) * buildWeights.HPREGEN; // add hp regeneration + buildRating += item.getStatEx(sdk.stats.ManaRecovery) * buildWeights.MANAREGEN; // add mana recovery + !item.isRuneword && (buildRating += (item.sockets * 10)); // priortize sockets + + // pierce/mastery's not sure how I want to weight this so for now just its base value + buildInfo.usefulStats.forEach(stat => buildRating += item.getStatEx(stat)); + + // Melee Specific + if (!buildInfo.caster || Config.AttackSkill.includes(sdk.skills.Attack) || Config.LowManaSkill.includes(sdk.skills.Attack) || ([sdk.items.type.Bow, sdk.items.type.AmazonBow, sdk.items.type.Crossbow].includes(item.itemType) && CharData.skillData.bowData.bowOnSwitch)) { - let meleeRating = 0; - let eleDmgModifer = [sdk.items.type.Ring, sdk.items.type.Amulet].includes(item.itemType) ? 2 : 1; - - item.getStatEx(sdk.stats.ReplenishDurability) && (meleeRating += 15); - item.getStatEx(sdk.stats.IgnoreTargetDefense) && (meleeRating += 50); - - // should these be added and calc avg dmg instead? - // Sometimes we replace good weps with 2-300 ED weps that may be high dmg but aren't as good as the item we replaced - //buildRating += item.getStatEx(sdk.stats.MinDamage) * buildWeights.MINDMG; // add MIN damage - //buildRating += item.getStatEx(sdk.stats.MaxDamage) * buildWeights.MAXDMG; // add MAX damage - //buildRating += item.getStatEx(sdk.stats.SecondaryMinDamage) * buildWeights.SECMINDMG; // add MIN damage - //buildRating += item.getStatEx(sdk.stats.SecondaryMaxDamage) * buildWeights.SECMAXDMG; // add MAX damage - meleeRating += ((item.getStatEx(sdk.stats.MaxDamage) + item.getStatEx(sdk.stats.MinDamage)) / 2) * tierWeights.meleeWeights.AVGDMG; + let meleeRating = 0; + let eleDmgModifer = [sdk.items.type.Ring, sdk.items.type.Amulet].includes(item.itemType) ? 2 : 1; + + item.getStatEx(sdk.stats.ReplenishDurability) && (meleeRating += 15); + item.getStatEx(sdk.stats.IgnoreTargetDefense) && (meleeRating += 50); + + // should these be added and calc avg dmg instead? + // Sometimes we replace good weps with 2-300 ED weps that may be high dmg but aren't as good as the item we replaced + //buildRating += item.getStatEx(sdk.stats.MinDamage) * buildWeights.MINDMG; // add MIN damage + //buildRating += item.getStatEx(sdk.stats.MaxDamage) * buildWeights.MAXDMG; // add MAX damage + //buildRating += item.getStatEx(sdk.stats.SecondaryMinDamage) * buildWeights.SECMINDMG; // add MIN damage + //buildRating += item.getStatEx(sdk.stats.SecondaryMaxDamage) * buildWeights.SECMAXDMG; // add MAX damage + meleeRating += ((item.getStatEx(sdk.stats.MaxDamage) + item.getStatEx(sdk.stats.MinDamage)) / 2) * tierWeights.meleeWeights.AVGDMG; - meleeRating += sumElementalDmg(item) * (tierWeights.meleeWeights.ELEDMG / eleDmgModifer); // add elemental damage - meleeRating += item.getStatEx(sdk.stats.ToHit) * tierWeights.meleeWeights.AR; // add AR - meleeRating += item.getStatEx(sdk.stats.CrushingBlow) * tierWeights.meleeWeights.CB; // add crushing blow - meleeRating += item.getStatEx(sdk.stats.OpenWounds) * tierWeights.meleeWeights.OW; // add open wounds - meleeRating += item.getStatEx(sdk.stats.DeadlyStrike) * tierWeights.meleeWeights.DS; // add deadly strike - meleeRating += item.getStatEx(sdk.stats.LifeLeech) * tierWeights.meleeWeights.LL; // add LL - meleeRating += item.getStatEx(sdk.stats.ManaDrainMinDamage) * tierWeights.meleeWeights.ML; // add ML - meleeRating += item.getStatEx(sdk.stats.SkillOnAura, sdk.skills.Sanctuary) * 25; // sanctuary aura - meleeRating += item.getStatEx(sdk.stats.DemonDamagePercent) * tierWeights.meleeWeights.DMGTODEMONS; // add damage % to demons - meleeRating += item.getStatEx(sdk.stats.UndeadDamagePercent) * tierWeights.meleeWeights.DMGTOUNDEAD; // add damage % to undead + meleeRating += sumElementalDmg(item) * (tierWeights.meleeWeights.ELEDMG / eleDmgModifer); // add elemental damage + meleeRating += item.getStatEx(sdk.stats.ToHit) * tierWeights.meleeWeights.AR; // add AR + meleeRating += item.getStatEx(sdk.stats.CrushingBlow) * tierWeights.meleeWeights.CB; // add crushing blow + meleeRating += item.getStatEx(sdk.stats.OpenWounds) * tierWeights.meleeWeights.OW; // add open wounds + meleeRating += item.getStatEx(sdk.stats.DeadlyStrike) * tierWeights.meleeWeights.DS; // add deadly strike + meleeRating += item.getStatEx(sdk.stats.LifeLeech) * tierWeights.meleeWeights.LL; // add LL + meleeRating += item.getStatEx(sdk.stats.ManaDrainMinDamage) * tierWeights.meleeWeights.ML; // add ML + meleeRating += item.getStatEx(sdk.stats.SkillOnAura, sdk.skills.Sanctuary) * 25; // sanctuary aura + meleeRating += item.getStatEx(sdk.stats.DemonDamagePercent) * tierWeights.meleeWeights.DMGTODEMONS; // add damage % to demons + meleeRating += item.getStatEx(sdk.stats.UndeadDamagePercent) * tierWeights.meleeWeights.DMGTOUNDEAD; // add damage % to undead - buildRating += (buildInfo.caster ? (meleeRating / 2) : meleeRating); - } + buildRating += (buildInfo.caster ? (meleeRating / 2) : meleeRating); + } - return buildRating; - }; + return buildRating; + }; - const skillsScore = () => { - let skillsRating = 0; - let weaponModifer = !buildInfo.caster && item.getItemType() === "Weapon" ? 4 : 1; + const skillsScore = () => { + let skillsRating = 0; + let weaponModifer = !buildInfo.caster && item.getItemType() === "Weapon" ? 4 : 1; - skillsRating += item.getStatEx(sdk.stats.AllSkills) * (tierWeights.skillsWeights.ALL / weaponModifer); // + all skills - skillsRating += item.getStatEx(sdk.stats.AddClassSkills, me.classid) * (tierWeights.skillsWeights.CLASS / weaponModifer); // + class skills - skillsRating += item.getStatEx(sdk.stats.AddSkillTab, buildInfo.tabSkills) * (tierWeights.skillsWeights.TAB / weaponModifer); // + TAB skills - let selectedWeights = [tierWeights.skillsWeights.WANTED, tierWeights.skillsWeights.USEFUL]; - let selectedSkills = [buildInfo.wantedSkills, buildInfo.usefulSkills]; + skillsRating += item.getStatEx(sdk.stats.AllSkills) * (tierWeights.skillsWeights.ALL / weaponModifer); // + all skills + skillsRating += item.getStatEx(sdk.stats.AddClassSkills, me.classid) * (tierWeights.skillsWeights.CLASS / weaponModifer); // + class skills + skillsRating += item.getStatEx(sdk.stats.AddSkillTab, buildInfo.tabSkills) * (tierWeights.skillsWeights.TAB / weaponModifer); // + TAB skills + let selectedWeights = [tierWeights.skillsWeights.WANTED, tierWeights.skillsWeights.USEFUL]; + let selectedSkills = [buildInfo.wantedSkills, buildInfo.usefulSkills]; - for (let i = 0; i < selectedWeights.length; i++) { - for (let j = 0; j < selectedSkills.length; j++) { - for (let k = 0; k < selectedSkills[j].length; k++) { - skillsRating += item.getStatEx(107, selectedSkills[j][k]) * selectedWeights[i]; + for (let i = 0; i < selectedWeights.length; i++) { + for (let j = 0; j < selectedSkills.length; j++) { + for (let k = 0; k < selectedSkills[j].length; k++) { + skillsRating += item.getStatEx(107, selectedSkills[j][k]) * selectedWeights[i]; + } } } - } - // Spirit Fix for barb - (item.prefixnum === sdk.locale.items.Spirit && !buildInfo.caster) && (skillsRating -= 400); + // Spirit Fix for barb + (item.prefixnum === sdk.locale.items.Spirit && !buildInfo.caster) && (skillsRating -= 400); - return skillsRating; - }; + return skillsRating; + }; - const ctcScore = () => { + const ctcScore = () => { // chance to cast doesn't exist in classic - if (me.classic) return 0; - - const ctcSkillObj = (ctcType, skill, level) => ({ ctcType: ctcType, skill: skill, level: level }); - const meleeCheck = !buildInfo.caster; - let ctcRating = 0, ctcItems = []; - let skill, level; - let stats = item.getStat(-2); - - if (stats.hasOwnProperty(sdk.stats.SkillWhenStruck)) { - if (stats[sdk.stats.SkillWhenStruck] instanceof Array) { - for (let i = 0; i < stats[sdk.stats.SkillWhenStruck].length; i++) { - if (stats[sdk.stats.SkillWhenStruck][i] !== undefined) { - ({ skill, level } = stats[sdk.stats.SkillWhenStruck][i]); - ctcItems.push(ctcSkillObj(sdk.stats.SkillWhenStruck, skill, level)); + if (me.classic) return 0; + + const ctcSkillObj = (ctcType, skill, level) => ({ ctcType: ctcType, skill: skill, level: level }); + const meleeCheck = !buildInfo.caster; + let ctcRating = 0, ctcItems = []; + let skill, level; + let stats = item.getStat(-2); + + if (stats.hasOwnProperty(sdk.stats.SkillWhenStruck)) { + if (stats[sdk.stats.SkillWhenStruck] instanceof Array) { + for (let i = 0; i < stats[sdk.stats.SkillWhenStruck].length; i++) { + if (stats[sdk.stats.SkillWhenStruck][i] !== undefined) { + ({ skill, level } = stats[sdk.stats.SkillWhenStruck][i]); + ctcItems.push(ctcSkillObj(sdk.stats.SkillWhenStruck, skill, level)); + } } + } else { + ({ skill, level } = stats[sdk.stats.SkillWhenStruck]); + ctcItems.push(ctcSkillObj(sdk.stats.SkillWhenStruck, skill, level)); } - } else { - ({ skill, level } = stats[sdk.stats.SkillWhenStruck]); - ctcItems.push(ctcSkillObj(sdk.stats.SkillWhenStruck, skill, level)); } - } - if (meleeCheck) { - if (stats.hasOwnProperty(sdk.stats.SkillOnAttack)) { - if (stats[sdk.stats.SkillOnAttack] instanceof Array) { - for (let i = 0; i < stats[sdk.stats.SkillOnAttack].length; i++) { - if (stats[sdk.stats.SkillOnAttack][i] !== undefined) { - ({ skill, level } = stats[sdk.stats.SkillOnAttack][i]); - ctcItems.push(ctcSkillObj(sdk.stats.SkillOnAttack, skill, level)); + if (meleeCheck) { + if (stats.hasOwnProperty(sdk.stats.SkillOnAttack)) { + if (stats[sdk.stats.SkillOnAttack] instanceof Array) { + for (let i = 0; i < stats[sdk.stats.SkillOnAttack].length; i++) { + if (stats[sdk.stats.SkillOnAttack][i] !== undefined) { + ({ skill, level } = stats[sdk.stats.SkillOnAttack][i]); + ctcItems.push(ctcSkillObj(sdk.stats.SkillOnAttack, skill, level)); + } } + } else { + ({ skill, level } = stats[sdk.stats.SkillOnAttack]); + ctcItems.push(ctcSkillObj(sdk.stats.SkillOnAttack, skill, level)); } - } else { - ({ skill, level } = stats[sdk.stats.SkillOnAttack]); - ctcItems.push(ctcSkillObj(sdk.stats.SkillOnAttack, skill, level)); } - } - if (stats.hasOwnProperty(sdk.stats.SkillOnStrike)) { - if (stats[sdk.stats.SkillOnStrike] instanceof Array) { - for (let i = 0; i < stats[sdk.stats.SkillOnStrike].length; i++) { - if (stats[sdk.stats.SkillOnStrike][i] !== undefined) { - ({ skill, level } = stats[sdk.stats.SkillOnStrike][i]); - ctcItems.push(ctcSkillObj(sdk.stats.SkillOnStrike, skill, level)); + if (stats.hasOwnProperty(sdk.stats.SkillOnStrike)) { + if (stats[sdk.stats.SkillOnStrike] instanceof Array) { + for (let i = 0; i < stats[sdk.stats.SkillOnStrike].length; i++) { + if (stats[sdk.stats.SkillOnStrike][i] !== undefined) { + ({ skill, level } = stats[sdk.stats.SkillOnStrike][i]); + ctcItems.push(ctcSkillObj(sdk.stats.SkillOnStrike, skill, level)); + } } + } else { + ({ skill, level } = stats[sdk.stats.SkillOnStrike]); + ctcItems.push(ctcSkillObj(sdk.stats.SkillOnStrike, skill, level)); } - } else { - ({ skill, level } = stats[sdk.stats.SkillOnStrike]); - ctcItems.push(ctcSkillObj(sdk.stats.SkillOnStrike, skill, level)); + } + } else { + tierWeights.ctcWeights.skills.Venom = 0; + if (me.charlvl > 50) { + tierWeights.ctcWeights.skills.ChargedBolt = 2; } } - } else { - tierWeights.ctcWeights.skills.Venom = 0; - if (me.charlvl > 50) { - tierWeights.ctcWeights.skills.ChargedBolt = 2; - } - } - ctcItems = ctcItems.filter((v, i, a) => a.findIndex(el => ["ctcType", "skill"].every(k => el[k] === v[k])) === i); + ctcItems = ctcItems.filter((v, i, a) => a.findIndex(el => ["ctcType", "skill"].every(k => el[k] === v[k])) === i); - // might come back to redo the tierWieghts object but quick map for ctc - const ctcType = {}; - ctcType[sdk.stats.SkillOnAttack] = tierWeights.ctcWeights.onAttack; - ctcType[sdk.stats.SkillOnStrike] = tierWeights.ctcWeights.onStrike; - ctcType[sdk.stats.SkillWhenStruck] = tierWeights.ctcWeights.whenStruck; + // might come back to redo the tierWieghts object but quick map for ctc + const ctcType = {}; + ctcType[sdk.stats.SkillOnAttack] = tierWeights.ctcWeights.onAttack; + ctcType[sdk.stats.SkillOnStrike] = tierWeights.ctcWeights.onStrike; + ctcType[sdk.stats.SkillWhenStruck] = tierWeights.ctcWeights.whenStruck; - for (let i = 0; i < ctcItems.length; i++) { - try { - let skillName = getSkillById(ctcItems[i].skill).split(" ").join(""); - if (!!tierWeights.ctcWeights.skills[skillName] && ctcType[ctcItems[i].ctcType]) { - ctcRating += (ctcItems[i].level * tierWeights.ctcWeights.skills[skillName] * ctcType[ctcItems[i].ctcType]); + for (let i = 0; i < ctcItems.length; i++) { + try { + let skillName = getSkillById(ctcItems[i].skill).split(" ").join(""); + if (!!tierWeights.ctcWeights.skills[skillName] && ctcType[ctcItems[i].ctcType]) { + ctcRating += (ctcItems[i].level * tierWeights.ctcWeights.skills[skillName] * ctcType[ctcItems[i].ctcType]); + } + } catch (e) { + console.error(e); } - } catch (e) { - console.error(e); + } + + return ctcRating; + }; + + let tier = 1; // set to 1 for native autoequip to use items. + tier += generalScore(); + tier += resistScore(); + tier += buildScore(); + tier += skillsScore(); + tier += ctcScore(); + tier += chargeditemscore(item, -1, buildInfo); + + if (item.isBaseType && !item.isRuneword) { + for (let x = 0; x < Config.Runewords.length; x += 1) { + let [sockets, baseCID] = [Config.Runewords[x][0].length, Config.Runewords[x][1]]; + if (item.classid === baseCID && item.sockets === sockets && !item.getItemsEx().length) return -1; } } - return ctcRating; + return item.questItem ? -1 : Math.max(1, tier); }; - let tier = 1; // set to 1 for native autoequip to use items. - tier += generalScore(); - tier += resistScore(); - tier += buildScore(); - tier += skillsScore(); - tier += ctcScore(); - tier += chargeditemscore(item, -1, buildInfo); - - for (let x = 0; x < Config.Runewords.length; x += 1) { - let [sockets, baseCID] = [Config.Runewords[x][0].length, Config.Runewords[x][1]]; - if (item.classid === baseCID && item.isBaseType && item.sockets === sockets && !item.isRuneword && !item.getItemsEx().length) return -1; - } + /** + * @param {ItemUnit} item + */ + const secondaryscore = function (item) { + let tier = 0; - return item.questItem ? -1 : Math.max(1, tier); -}; + tier += item.getStatEx(sdk.stats.AllSkills) * 200; // + all skills + tier += item.getStatEx(sdk.stats.AddClassSkills, me.classid) * 100; // + class skills + tier += item.getStatEx(sdk.stats.AddSkillTab, Check.finalBuild().tabSkills) * 75; // + TAB skills + let precastSkills = [Check.finalBuild().precastSkills]; -/** - * @param {ItemUnit} item - */ -const secondaryscore = function (item) { - let tier = 0; + for (let i = 0; i < precastSkills.length; i++) { + tier += item.getStatEx(107, precastSkills[i]) * 50; + } - tier += item.getStatEx(sdk.stats.AllSkills) * 200; // + all skills - tier += item.getStatEx(sdk.stats.AddClassSkills, me.classid) * 100; // + class skills - tier += item.getStatEx(sdk.stats.AddSkillTab, Check.finalBuild().tabSkills) * 75; // + TAB skills - let precastSkills = [Check.finalBuild().precastSkills]; + tier += item.getStatEx(sdk.stats.FCR) * 5; // add FCR + tier += item.getStatEx(sdk.stats.FHR) * 3; // add faster hit recovery - for (let i = 0; i < precastSkills.length; i++) { - tier += item.getStatEx(107, precastSkills[i]) * 50; - } + return tier; + }; - tier += item.getStatEx(sdk.stats.FCR) * 5; // add FCR - tier += item.getStatEx(sdk.stats.FHR) * 3; // add faster hit recovery + /** + * @param {ItemUnit} item + */ + const charmscore = function (item) { + if (myData.me.charmGids.includes(item.gid)) return 1000; + // depending on invo space it might be worth it early on to keep 1 or 2 non-skiller grandcharms - @todo test that out + if (!item.unique && item.classid === sdk.items.GrandCharm && !me.getSkillTabs().some(s => item.getStatEx(sdk.stats.AddSkillTab, s))) return -1; + const buildInfo = Check.currentBuild(); + + let charmRating = 1; + + // Gheeds, Torch, annhi - we know possible attributes so don't waste resources checking for all the others + if (item.unique) { + charmRating += item.getStatEx(sdk.stats.Strength); // handle +all atrributes + charmRating += item.getStatEx(sdk.stats.AllRes); + + if (item.isAnni) { + charmRating += item.getStatEx(sdk.stats.AllSkills) * tierWeights.charmWeights.ALL; + charmRating += item.getStatEx(sdk.stats.AddExperience); + } else if (item.isGheeds) { + charmRating += item.getStatEx(sdk.stats.GoldBonus); + charmRating += item.getStatEx(sdk.stats.ReducedPrices) * 1.5; + charmRating += item.getStatEx(sdk.stats.MagicBonus) * tierWeights.charmWeights.MF; + } else { + charmRating += item.getStatEx(sdk.stats.AddClassSkills, me.classid) * tierWeights.charmWeights.CLASS; // + class skills + } + } else { + charmRating += item.getStatEx(sdk.stats.AddSkillTab, buildInfo.tabSkills) * tierWeights.charmWeights.TAB; // + TAB skills + charmRating += item.getStatEx(sdk.stats.FireResist) * tierWeights.charmWeights.FR; // add FR + charmRating += item.getStatEx(sdk.stats.ColdResist) * tierWeights.charmWeights.CR; // add CR + charmRating += item.getStatEx(sdk.stats.LightResist) * tierWeights.charmWeights.LR; // add LR + charmRating += item.getStatEx(sdk.stats.PoisonResist) * tierWeights.charmWeights.PR; // add PR + charmRating += item.getStatEx(sdk.stats.FRW) * tierWeights.charmWeights.FRW; // add faster run walk + charmRating += item.getStatEx(sdk.stats.FHR) * tierWeights.charmWeights.FHR; // add faster hit recovery + charmRating += item.getStatEx(sdk.stats.Defense) * tierWeights.charmWeights.DEF; // add Defense + charmRating += item.getStatEx(sdk.stats.MagicBonus) * tierWeights.charmWeights.MF; // add magic find + charmRating += (item.getStatEx(sdk.stats.Vitality) + item.getStatEx(sdk.stats.MaxHp) + (item.getStatEx(sdk.stats.PerLevelHp) / 2048 * me.charlvl)) * tierWeights.charmWeights.HP; // add HP + charmRating += (item.getStatEx(sdk.stats.Energy) + item.getStatEx(sdk.stats.MaxMana) + (item.getStatEx(sdk.stats.PerLevelMana) / 2048 * me.charlvl)) * tierWeights.charmWeights.MANA;// add mana + charmRating += item.getStatEx(sdk.stats.Strength) * tierWeights.charmWeights.STR; // add STR + charmRating += item.getStatEx(sdk.stats.Dexterity) * tierWeights.charmWeights.DEX; // add DEX + + if (!buildInfo.caster) { + charmRating += item.getStatEx(sdk.stats.MinDamage) * 3; // add MIN damage + charmRating += item.getStatEx(sdk.stats.MaxDamage) * 3; // add MAX damage + charmRating += sumElementalDmg(item); // add elemental damage + charmRating += item.getStatEx(sdk.stats.ToHit) * 0.5; // add AR + } + } - return tier; -}; + return charmRating; + }; -/** - * @param {ItemUnit} item - */ -const charmscore = function (item) { - if (myData.me.charmGids.includes(item.gid)) return 1000; - let charmRating = 1; - - if (!item.unique && item.classid === sdk.items.GrandCharm && !me.getSkillTabs().some(s => item.getStatEx(sdk.stats.AddSkillTab, s))) return -1; - const buildInfo = Check.currentBuild(); - - charmRating += item.getStatEx(sdk.stats.AddSkillTab, buildInfo.tabSkills) * tierWeights.charmWeights.TAB; // + TAB skills - charmRating += item.getStatEx(sdk.stats.FireResist) * tierWeights.charmWeights.FR; // add FR - charmRating += item.getStatEx(sdk.stats.ColdResist) * tierWeights.charmWeights.CR; // add CR - charmRating += item.getStatEx(sdk.stats.LightResist) * tierWeights.charmWeights.LR; // add LR - charmRating += item.getStatEx(sdk.stats.PoisonResist) * tierWeights.charmWeights.PR; // add PR - charmRating += item.getStatEx(sdk.stats.FRW) * tierWeights.charmWeights.FRW; // add faster run walk - charmRating += item.getStatEx(sdk.stats.FHR) * tierWeights.charmWeights.FHR; // add faster hit recovery - charmRating += item.getStatEx(sdk.stats.Defense) * tierWeights.charmWeights.DEF; // add Defense - charmRating += item.getStatEx(sdk.stats.MagicBonus) * tierWeights.charmWeights.MF; // add magic find - charmRating += (item.getStatEx(sdk.stats.Vitality) + item.getStatEx(sdk.stats.MaxHp) + (item.getStatEx(sdk.stats.PerLevelHp) / 2048 * me.charlvl)) * tierWeights.charmWeights.HP; // add HP - charmRating += (item.getStatEx(sdk.stats.Energy) + item.getStatEx(sdk.stats.MaxMana) + (item.getStatEx(sdk.stats.PerLevelMana) / 2048 * me.charlvl)) * tierWeights.charmWeights.MANA;// add mana - charmRating += item.getStatEx(sdk.stats.Strength) * tierWeights.charmWeights.STR; // add STR - charmRating += item.getStatEx(sdk.stats.Dexterity) * tierWeights.charmWeights.DEX; // add DEX - - if (!buildInfo.caster) { - charmRating += item.getStatEx(sdk.stats.MinDamage) * 3; // add MIN damage - charmRating += item.getStatEx(sdk.stats.MaxDamage) * 3; // add MAX damage - charmRating += sumElementalDmg(item); // add elemental damage - charmRating += item.getStatEx(sdk.stats.ToHit) * 0.5; // add AR - } - - // Gheeds, Torch, annhi - if (item.unique) { - charmRating += item.getStatEx(sdk.stats.AllSkills) * tierWeights.charmWeights.ALL; // + all skills - charmRating += item.getStatEx(sdk.stats.AddClassSkills, me.classid) * tierWeights.charmWeights.CLASS; // + class skills - charmRating += item.getStatEx(sdk.stats.GoldBonus); // add gold find - charmRating += item.getStatEx(sdk.stats.ReducedPrices) * 1.5; // add reduced vendor prices - charmRating += item.getStatEx(sdk.stats.Strength); // add STR - } - - return charmRating; -}; + // export to global scope + global.mercscore = mercscore; + global.chargeditemscore = chargeditemscore; + global.tierscore = tierscore; + global.secondaryscore = secondaryscore; + global.charmscore = charmscore; +})(); diff --git a/libs/SoloPlay/Functions/StorageOverrides.js b/libs/SoloPlay/Functions/StorageOverrides.js index 27cc5f85..3f8ae781 100644 --- a/libs/SoloPlay/Functions/StorageOverrides.js +++ b/libs/SoloPlay/Functions/StorageOverrides.js @@ -8,645 +8,649 @@ */ includeIfNotIncluded("SoloPlay/Tools/Developer.js"); - -/** - * @constructor - * @param {string} name - container name - * @param {number} width - container width - * @param {number} height - container height - * @param {number} location - container location - */ -function Container(name, width, height, location) { - this.name = name; - this.width = width; - this.height = height; - this.location = location; - /** @type {number[][]} */ - this.buffer = []; - /** @type {ItemUnit[]} */ - this.itemList = []; - this.openPositions = this.height * this.width; - - for (let h = 0; h < this.height; h += 1) { - this.buffer.push([]); - - for (let w = 0; w < this.width; w += 1) { - this.buffer[h][w] = 0; +(function() { + /** + * @constructor + * @param {string} name - container name + * @param {number} width - container width + * @param {number} height - container height + * @param {number} location - container location + */ + function Container(name, width, height, location) { + this.name = name; + this.width = width; + this.height = height; + this.location = location; + /** @type {number[][]} */ + this.buffer = []; + /** @type {ItemUnit[]} */ + this.itemList = []; + this.openPositions = this.height * this.width; + + for (let h = 0; h < this.height; h += 1) { + this.buffer.push([]); + + for (let w = 0; w < this.width; w += 1) { + this.buffer[h][w] = 0; + } } } -} -/** - * @param {ItemUnit} item - */ -Container.prototype.Mark = function (item) { - let x, y; + /** + * @param {ItemUnit} item + */ + Container.prototype.Mark = function (item) { + let x, y; - // Make sure it is in this container. - if (item.location !== this.location || (item.mode !== sdk.items.mode.inStorage && item.mode !== sdk.items.mode.inBelt)) { - return false; - } - - // Mark item in buffer. - for (x = item.x; x < (item.x + item.sizex); x += 1) { - for (y = item.y; y < (item.y + item.sizey); y += 1) { - this.buffer[y][x] = this.itemList.length + 1; - this.openPositions -= 1; + // Make sure it is in this container. + if (item.location !== this.location || (item.mode !== sdk.items.mode.inStorage && item.mode !== sdk.items.mode.inBelt)) { + return false; } - } - // Add item to list. - this.itemList.push(copyUnit(item)); + // Mark item in buffer. + for (x = item.x; x < (item.x + item.sizex); x += 1) { + for (y = item.y; y < (item.y + item.sizey); y += 1) { + this.buffer[y][x] = this.itemList.length + 1; + this.openPositions -= 1; + } + } - return true; -}; + // Add item to list. + this.itemList.push(copyUnit(item)); -/** - * @param {ItemUnit} item - * @param {number[][]} baseRef - */ -Container.prototype.IsLocked = function (item, baseRef) { - let h, w; - let reference = baseRef.slice(0); + return true; + }; - // Make sure it is in this container. - if (item.mode !== sdk.items.mode.inStorage || item.location !== this.location) { - return false; - } + /** + * @param {ItemUnit} item + * @param {number[][]} baseRef + */ + Container.prototype.IsLocked = function (item, baseRef) { + let h, w; + let reference = baseRef.slice(0); + + // Make sure it is in this container. + if (item.mode !== sdk.items.mode.inStorage || item.location !== this.location) { + return false; + } - // Make sure the item is ours - if (!item.getParent() || item.getParent().type !== me.type || item.getParent().gid !== me.gid) { - return false; - } + // Make sure the item is ours + if (!item.getParent() || item.getParent().type !== me.type || item.getParent().gid !== me.gid) { + return false; + } - //Insure valid reference. - if (typeof (reference) !== "object" || reference.length !== this.buffer.length || reference[0].length !== this.buffer[0].length) { - throw new Error("Storage.IsLocked: Invalid inventory reference"); - } + //Insure valid reference. + if (typeof (reference) !== "object" || reference.length !== this.buffer.length || reference[0].length !== this.buffer[0].length) { + throw new Error("Storage.IsLocked: Invalid inventory reference"); + } - try { + try { // Check if the item lies in a locked spot. - for (h = item.y; h < (item.y + item.sizey); h += 1) { - for (w = item.x; w < (item.x + item.sizex); w += 1) { - if (reference[h][w] === 0) { - return true; + for (h = item.y; h < (item.y + item.sizey); h += 1) { + for (w = item.x; w < (item.x + item.sizex); w += 1) { + if (reference[h][w] === 0) { + return true; + } } } + } catch (e2) { + throw new Error("Storage.IsLocked error! Item info: " + item.name + " " + item.y + " " + item.sizey + " " + item.x + " " + item.sizex + " " + item.mode + " " + item.location); } - } catch (e2) { - throw new Error("Storage.IsLocked error! Item info: " + item.name + " " + item.y + " " + item.sizey + " " + item.x + " " + item.sizex + " " + item.mode + " " + item.location); - } - return false; -}; + return false; + }; -Container.prototype.Reset = function () { - let h, w; + Container.prototype.Reset = function () { + let h, w; - for (h = 0; h < this.height; h += 1) { - for (w = 0; w < this.width; w += 1) { - this.buffer[h][w] = 0; + for (h = 0; h < this.height; h += 1) { + for (w = 0; w < this.width; w += 1) { + this.buffer[h][w] = 0; + } } - } - this.itemList = []; - this.openPositions = this.height * this.width; + this.itemList = []; + this.openPositions = this.height * this.width; - return true; -}; + return true; + }; -/** - * @param {string} name - */ -Container.prototype.cubeSpot = function (name) { - if (name !== "Stash") return true; + /** + * @param {string} name + */ + Container.prototype.cubeSpot = function (name) { + if (name !== "Stash") return true; - let cube = me.getItem(sdk.quest.item.Cube); - if (!cube) return false; + let cube = me.getItem(sdk.quest.item.Cube); + if (!cube) return false; - // Cube is in correct location - if (cube && cube.isInStash && cube.x === 0 && cube.y === 0) { - return true; - } + // Cube is in correct location + if (cube && cube.isInStash && cube.x === 0 && cube.y === 0) { + return true; + } - let makeCubeSpot = this.MakeSpot(cube, {x: 0, y: 0}, true); // NOTE: passing these in buffer order [h/x][w/y] + let makeCubeSpot = this.MakeSpot(cube, {x: 0, y: 0}, true); // NOTE: passing these in buffer order [h/x][w/y] - if (makeCubeSpot) { + if (makeCubeSpot) { // this item cannot be moved - if (makeCubeSpot === -1) return false; - // we couldnt move the item - if (!this.MoveToSpot(cube, makeCubeSpot.y, makeCubeSpot.x)) return false; - } + if (makeCubeSpot === -1) return false; + // we couldnt move the item + if (!this.MoveToSpot(cube, makeCubeSpot.y, makeCubeSpot.x)) return false; + } - return true; -}; + return true; + }; -/** + /** * @param {ItemUnit} item */ -Container.prototype.CanFit = function (item) { - return (!!this.FindSpot(item)); -}; + Container.prototype.CanFit = function (item) { + return (!!this.FindSpot(item)); + }; -/** - * @param {number[]} itemIdsLeft - * @param {number[]} itemIdsRight - */ -Container.prototype.SortItems = function (itemIdsLeft, itemIdsRight) { - Storage.Reload(); + /** + * @param {number[]} itemIdsLeft + * @param {number[]} itemIdsRight + */ + Container.prototype.SortItems = function (itemIdsLeft, itemIdsRight) { + Storage.Reload(); - this.cubeSpot(this.name); + this.cubeSpot(this.name); - let x, y, item, nPos; + let x, y, item, nPos; - for (y = this.width - 1; y >= 0; y--) { - for (x = this.height - 1; x >= 0; x--) { + for (y = this.width - 1; y >= 0; y--) { + for (x = this.height - 1; x >= 0; x--) { - delay(1); + delay(1); - if (this.buffer[x][y] === 0) { - continue; // nothing on this spot - } + if (this.buffer[x][y] === 0) { + continue; // nothing on this spot + } - item = this.itemList[this.buffer[x][y] - 1]; + item = this.itemList[this.buffer[x][y] - 1]; - if (item.classid === sdk.quest.item.Cube && item.isInStash && item.x === 0 && item.y === 0) { - continue; // dont touch the cube - } + if (item.classid === sdk.quest.item.Cube && item.isInStash && item.x === 0 && item.y === 0) { + continue; // dont touch the cube + } - let ix = item.y, iy = item.x; // x and y are backwards! + let ix = item.y, iy = item.x; // x and y are backwards! - if (this.location !== item.location) { - D2Bot.printToConsole("StorageOverrides.js>SortItems WARNING: Detected a non-storage item in the list: " + item.name + " at " + ix + "," + iy, sdk.colors.D2Bot.Gold); - continue; // dont try to touch non-storage items | TODO: prevent non-storage items from getting this far - } + if (this.location !== item.location) { + D2Bot.printToConsole("StorageOverrides.js>SortItems WARNING: Detected a non-storage item in the list: " + item.name + " at " + ix + "," + iy, sdk.colors.D2Bot.Gold); + continue; // dont try to touch non-storage items | TODO: prevent non-storage items from getting this far + } - if (this.location === sdk.storage.Inventory && this.IsLocked(item, Config.Inventory)) { - continue; // locked spot / item - } + if (this.location === sdk.storage.Inventory && this.IsLocked(item, Config.Inventory)) { + continue; // locked spot / item + } - if (ix < x || iy < y) { - continue; // not top left part of item - } + if (ix < x || iy < y) { + continue; // not top left part of item + } - if (item.type !== sdk.unittype.Item) { - D2Bot.printToConsole("StorageOverrides.js>SortItems WARNING: Detected a non-item in the list: " + item.name + " at " + ix + "," + iy, sdk.colors.D2Bot.Gold); - continue; // dont try to touch non-items | TODO: prevent non-items from getting this far - } + if (item.type !== sdk.unittype.Item) { + D2Bot.printToConsole("StorageOverrides.js>SortItems WARNING: Detected a non-item in the list: " + item.name + " at " + ix + "," + iy, sdk.colors.D2Bot.Gold); + continue; // dont try to touch non-items | TODO: prevent non-items from getting this far + } - if (item.mode === sdk.items.mode.onGround) { - D2Bot.printToConsole("StorageOverrides.js>SortItems WARNING: Detected a ground item in the list: " + item.name + " at " + ix + "," + iy, sdk.colors.D2Bot.Gold); - continue; // dont try to touch ground items | TODO: prevent ground items from getting this far - } + if (item.mode === sdk.items.mode.onGround) { + D2Bot.printToConsole("StorageOverrides.js>SortItems WARNING: Detected a ground item in the list: " + item.name + " at " + ix + "," + iy, sdk.colors.D2Bot.Gold); + continue; // dont try to touch ground items | TODO: prevent ground items from getting this far + } - // always sort stash left-to-right - if (this.location === sdk.storage.Stash) { - nPos = this.FindSpot(item); - } else if (this.location === sdk.storage.Inventory && ((!itemIdsLeft && !itemIdsRight) || !itemIdsLeft || itemIdsRight.includes(item.classid) || itemIdsLeft.indexOf(item.classid) === -1)) { + // always sort stash left-to-right + if (this.location === sdk.storage.Stash) { + nPos = this.FindSpot(item); + } else if (this.location === sdk.storage.Inventory && ((!itemIdsLeft && !itemIdsRight) || !itemIdsLeft || itemIdsRight.includes(item.classid) || itemIdsLeft.indexOf(item.classid) === -1)) { // sort from right by default or if specified - nPos = this.FindSpot(item, true, false, SetUp.sortSettings.ItemsSortedFromRightPriority); - } else if (this.location === sdk.storage.Inventory && itemIdsRight.indexOf(item.classid) === -1 && itemIdsLeft.includes(item.classid)) { + nPos = this.FindSpot(item, true, false, SetUp.sortSettings.ItemsSortedFromRightPriority); + } else if (this.location === sdk.storage.Inventory && itemIdsRight.indexOf(item.classid) === -1 && itemIdsLeft.includes(item.classid)) { // sort from left only if specified - nPos = this.FindSpot(item, false, false, SetUp.sortSettings.ItemsSortedFromLeftPriority); - } + nPos = this.FindSpot(item, false, false, SetUp.sortSettings.ItemsSortedFromLeftPriority); + } - // skip if no better spot found - if (!nPos || (nPos.x === ix && nPos.y === iy)) { - continue; - } + // skip if no better spot found + if (!nPos || (nPos.x === ix && nPos.y === iy)) { + continue; + } - if (!this.MoveToSpot(item, nPos.y, nPos.x)) { - continue; // we couldnt move the item - } + if (!this.MoveToSpot(item, nPos.y, nPos.x)) { + continue; // we couldnt move the item + } - // We moved an item so reload & restart - Storage.Reload(); - y = this.width - 0; + // We moved an item so reload & restart + Storage.Reload(); + y = this.width - 0; - break; // Loop again from begin + break; // Loop again from begin + } } - } - //this.Dump(); + //this.Dump(); - return true; -}; + return true; + }; -/** - * @param {ItemUnit} item - * @param {boolean} reverseX - * @param {boolean} reverseY - * @param {number[]} priorityClassIds - */ -Container.prototype.FindSpot = function (item, reverseX, reverseY, priorityClassIds) { + /** + * @param {ItemUnit} item + * @param {boolean} reverseX + * @param {boolean} reverseY + * @param {number[]} priorityClassIds + */ + Container.prototype.FindSpot = function (item, reverseX, reverseY, priorityClassIds) { // Make sure it's a valid item - if (!item) return false; + if (!item) return false; - let x, y, nx, ny, makeSpot; - let startX, startY, endX, endY, xDir = 1, yDir = 1; + let x, y, nx, ny, makeSpot; + let startX, startY, endX, endY, xDir = 1, yDir = 1; - startX = 0; - startY = 0; - endX = this.width - (item.sizex - 1); - endY = this.height - (item.sizey - 1); + startX = 0; + startY = 0; + endX = this.width - (item.sizex - 1); + endY = this.height - (item.sizey - 1); - Storage.Reload(); + Storage.Reload(); - if (reverseX) { // right-to-left - startX = endX - 1; - endX = -1; // stops at 0 - xDir = -1; - } + if (reverseX) { // right-to-left + startX = endX - 1; + endX = -1; // stops at 0 + xDir = -1; + } - if (reverseY) { // bottom-to-top - startY = endY - 1; - endY = -1; // stops at 0 - yDir = -1; - } + if (reverseY) { // bottom-to-top + startY = endY - 1; + endY = -1; // stops at 0 + yDir = -1; + } - //Loop buffer looking for spot to place item. - for (y = startX; y !== endX; y += xDir) { - Loop: - for (x = startY; x !== endY; x += yDir) { + //Loop buffer looking for spot to place item. + for (y = startX; y !== endX; y += xDir) { + Loop: + for (x = startY; x !== endY; x += yDir) { //Check if there is something in this spot. - if (this.buffer[x][y] > 0) { + if (this.buffer[x][y] > 0) { - // TODO: add makespot logic here. priorityClassIds should only be used when sorting -- in town, where it's safe! - // TODO: collapse this down to just a MakeSpot(item, location) call, and have MakeSpot do the priority checks right at the top - let bufferItemClass = this.itemList[this.buffer[x][y] - 1].classid; - let bufferItemGfx = this.itemList[this.buffer[x][y] - 1].gfx; - let bufferItemQuality = this.itemList[this.buffer[x][y] - 1].quality; + // TODO: add makespot logic here. priorityClassIds should only be used when sorting -- in town, where it's safe! + // TODO: collapse this down to just a MakeSpot(item, location) call, and have MakeSpot do the priority checks right at the top + let bufferItemClass = this.itemList[this.buffer[x][y] - 1].classid; + let bufferItemGfx = this.itemList[this.buffer[x][y] - 1].gfx; + let bufferItemQuality = this.itemList[this.buffer[x][y] - 1].quality; - if (SetUp.sortSettings.PrioritySorting && priorityClassIds && priorityClassIds.includes(item.classid) + if (SetUp.sortSettings.PrioritySorting && priorityClassIds && priorityClassIds.includes(item.classid) && !this.IsLocked(this.itemList[this.buffer[x][y] - 1], Config.Inventory) // don't try to make a spot by moving locked items! TODO: move this to the start of loop && (priorityClassIds.indexOf(bufferItemClass) === -1 || priorityClassIds.indexOf(item.classid) < priorityClassIds.indexOf(bufferItemClass))) { // item in this spot needs to move! - makeSpot = this.MakeSpot(item, {x: x, y: y}); // NOTE: passing these in buffer order [h/x][w/y] + makeSpot = this.MakeSpot(item, {x: x, y: y}); // NOTE: passing these in buffer order [h/x][w/y] - if (item.classid !== bufferItemClass // higher priority item + if (item.classid !== bufferItemClass // higher priority item || (item.classid === bufferItemClass && item.quality > bufferItemQuality) // same class, higher quality item || (item.classid === bufferItemClass && item.quality === bufferItemQuality && item.gfx > bufferItemGfx) // same quality, higher graphic item || (Config.AutoEquip && item.classid === bufferItemClass && item.quality === bufferItemQuality && item.gfx === bufferItemGfx // same graphic, higher tier item && NTIP.GetTier(item) > NTIP.GetTier(this.itemList[this.buffer[x][y] - 1]))) { - makeSpot = this.MakeSpot(item, {x: x, y: y}); // NOTE: passing these in buffer order [h/x][w/y] + makeSpot = this.MakeSpot(item, {x: x, y: y}); // NOTE: passing these in buffer order [h/x][w/y] - if (makeSpot) { + if (makeSpot) { // this item cannot be moved - if (makeSpot === -1) return false; + if (makeSpot === -1) return false; - return makeSpot; + return makeSpot; + } } } - } - if (item.gid === undefined) return false; + if (item.gid === undefined) return false; - // ignore same gid - if (item.gid !== this.itemList[this.buffer[x][y] - 1].gid ) { - continue; + // ignore same gid + if (item.gid !== this.itemList[this.buffer[x][y] - 1].gid ) { + continue; + } } - } - //Loop the item size to make sure we can fit it. - for (nx = 0; nx < item.sizey; nx += 1) { - for (ny = 0; ny < item.sizex; ny += 1) { - if (this.buffer[x + nx][y + ny]) { + //Loop the item size to make sure we can fit it. + for (nx = 0; nx < item.sizey; nx += 1) { + for (ny = 0; ny < item.sizex; ny += 1) { + if (this.buffer[x + nx][y + ny]) { // ignore same gid - if (item.gid !== this.itemList[this.buffer[x + nx][y + ny] - 1].gid ) { - continue Loop; + if (item.gid !== this.itemList[this.buffer[x + nx][y + ny] - 1].gid ) { + continue Loop; + } } } } - } - return ({x: x, y: y}); + return ({x: x, y: y}); + } } - } - return false; -}; + return false; + }; -/** - * @param {ItemUnit} item - * @param {{x: number, y: number}} location - * @param {boolean} force - */ -Container.prototype.MakeSpot = function (item, location, force) { - let x, y, endx, endy, tmpLocation; - let [itemsToMove, itemsMoved] = [[], []]; - // TODO: test the scenario where all possible items have been moved, but this item still can't be placed - // e.g. if there are many LCs in an inventory and the spot for a GC can't be freed up without - // moving other items that ARE NOT part of the position desired - - // Make sure it's a valid item and item is in a priority sorting list - if (!item || !item.classid + /** + * @param {ItemUnit} item + * @param {{x: number, y: number}} location + * @param {boolean} force + */ + Container.prototype.MakeSpot = function (item, location, force) { + let x, y, endx, endy, tmpLocation; + let [itemsToMove, itemsMoved] = [[], []]; + // TODO: test the scenario where all possible items have been moved, but this item still can't be placed + // e.g. if there are many LCs in an inventory and the spot for a GC can't be freed up without + // moving other items that ARE NOT part of the position desired + + // Make sure it's a valid item and item is in a priority sorting list + if (!item || !item.classid || (SetUp.sortSettings.ItemsSortedFromRightPriority.indexOf(item.classid) === -1 && SetUp.sortSettings.ItemsSortedFromLeftPriority.indexOf(item.classid) === -1 && !force)) { - return false; // only continue if the item is in the priority sort list - } + return false; // only continue if the item is in the priority sort list + } - // Make sure the item could even fit at the desired location - if (!location //|| !(location.x >= 0) || !(location.y >= 0) + // Make sure the item could even fit at the desired location + if (!location //|| !(location.x >= 0) || !(location.y >= 0) || ((location.y + (item.sizex - 1)) > (this.width - 1)) || ((location.x + (item.sizey - 1)) > (this.height - 1))) { - return false; // location invalid or item could not ever fit in the location - } + return false; // location invalid or item could not ever fit in the location + } - Storage.Reload(); + Storage.Reload(); - // Do not continue if the container doesn't have enough openPositions. - // TODO: esd1 - this could be extended to use Stash for moving things if inventory is too tightly packed - if (item.sizex * item.sizey > this.openPositions) { - return -1; // return a non-false answer to FindSpot so it doesn't keep looking - } + // Do not continue if the container doesn't have enough openPositions. + // TODO: esd1 - this could be extended to use Stash for moving things if inventory is too tightly packed + if (item.sizex * item.sizey > this.openPositions) { + return -1; // return a non-false answer to FindSpot so it doesn't keep looking + } - endy = location.y + (item.sizex - 1); - endx = location.x + (item.sizey - 1); - - // Collect a list of all the items in the way of using this position - for (x = location.x; x <= endx; x += 1) { // item height - for (y = location.y; y <= endy; y += 1) { // item width - if ( this.buffer[x][y] === 0 ) { - continue; // nothing to move from this spot - } else if (item.gid === this.itemList[this.buffer[x][y] - 1].gid) { - continue; // ignore same gid - } else { - itemsToMove.push(copyUnit(this.itemList[this.buffer[x][y] - 1])); // track items that need to move + endy = location.y + (item.sizex - 1); + endx = location.x + (item.sizey - 1); + + // Collect a list of all the items in the way of using this position + for (x = location.x; x <= endx; x += 1) { // item height + for (y = location.y; y <= endy; y += 1) { // item width + if ( this.buffer[x][y] === 0 ) { + continue; // nothing to move from this spot + } else if (item.gid === this.itemList[this.buffer[x][y] - 1].gid) { + continue; // ignore same gid + } else { + itemsToMove.push(copyUnit(this.itemList[this.buffer[x][y] - 1])); // track items that need to move + } } } - } - // Move any item(s) out of the way - if (itemsToMove.length) { - for (let i = 0; i < itemsToMove.length; i++) { - let reverseX = !(SetUp.sortSettings.ItemsSortedFromRight.includes(item.classid)); - tmpLocation = this.FindSpot(itemsToMove[i], reverseX, false); - // D2Bot.printToConsole(itemsToMove[i].name + " moving from " + itemsToMove[i].x + "," + itemsToMove[i].y + " to " + tmpLocation.y + "," + tmpLocation.x, sdk.colors.D2Bot.Gold); + // Move any item(s) out of the way + if (itemsToMove.length) { + for (let i = 0; i < itemsToMove.length; i++) { + let reverseX = !(SetUp.sortSettings.ItemsSortedFromRight.includes(item.classid)); + tmpLocation = this.FindSpot(itemsToMove[i], reverseX, false); + // D2Bot.printToConsole(itemsToMove[i].name + " moving from " + itemsToMove[i].x + "," + itemsToMove[i].y + " to " + tmpLocation.y + "," + tmpLocation.x, sdk.colors.D2Bot.Gold); - if (this.MoveToSpot(itemsToMove[i], tmpLocation.y, tmpLocation.x)) { + if (this.MoveToSpot(itemsToMove[i], tmpLocation.y, tmpLocation.x)) { // D2Bot.printToConsole(itemsToMove[i].name + " moved to " + tmpLocation.y + "," + tmpLocation.x, sdk.colors.D2Bot.Gold); - itemsMoved.push(copyUnit(itemsToMove[i])); - Storage.Reload(); // success on this item, reload! - delay(1); // give reload a moment of time to avoid moving the same item twice - } else { - D2Bot.printToConsole(itemsToMove[i].name + " failed to move to " + tmpLocation.y + "," + tmpLocation.x, sdk.colors.D2Bot.Gold); + itemsMoved.push(copyUnit(itemsToMove[i])); + Storage.Reload(); // success on this item, reload! + delay(1); // give reload a moment of time to avoid moving the same item twice + } else { + D2Bot.printToConsole(itemsToMove[i].name + " failed to move to " + tmpLocation.y + "," + tmpLocation.x, sdk.colors.D2Bot.Gold); - return false; + return false; + } } } - } - - //D2Bot.printToConsole("MakeSpot success! " + item.name + " can now be placed at " + location.y + "," + location.x, sdk.colors.D2Bot.Gold); - return ({x: location.x, y: location.y}); -}; -/** - * @param {ItemUnit} item - * @param {number} mX - * @param {number} mY - */ -Container.prototype.MoveToSpot = function (item, mX, mY) { - let cItem, cube; + //D2Bot.printToConsole("MakeSpot success! " + item.name + " can now be placed at " + location.y + "," + location.x, sdk.colors.D2Bot.Gold); + return ({x: location.x, y: location.y}); + }; - // Cube -> Stash, must place item in inventory first - if (item.location === sdk.storage.Cube && this.location === sdk.storage.Stash && !Storage.Inventory.MoveTo(item)) { - return false; - } + /** + * @param {ItemUnit} item + * @param {number} mX + * @param {number} mY + */ + Container.prototype.MoveToSpot = function (item, mX, mY) { + let cItem, cube; + + // Cube -> Stash, must place item in inventory first + if (item.location === sdk.storage.Cube && this.location === sdk.storage.Stash && !Storage.Inventory.MoveTo(item)) { + return false; + } - // Can't deal with items on ground! - if (item.mode === sdk.items.mode.onGround) return false; - // Item already on the cursor. - if (me.itemoncursor && item.mode !== sdk.items.mode.onCursor) return false; - - // Make sure stash is open - if (this.location === sdk.storage.Stash && !Town.openStash()) return false; - - const [orgX, orgY, orgLoc] = [item.x, item.y, item.location]; - const moveItem = (x, y, location) => { - for (let n = 0; n < 5; n += 1) { - switch (location) { - case sdk.storage.Belt: - cItem = Game.getCursorUnit(); - cItem !== null && sendPacket(1, sdk.packets.send.ItemToBelt, 4, cItem.gid, 4, y); - - break; - case sdk.storage.Inventory: - sendPacket(1, sdk.packets.send.ItemToBuffer, 4, item.gid, 4, x, 4, y, 4, 0x00); - - break; - case sdk.storage.Cube: - cItem = Game.getCursorUnit(); - cube = me.getItem(sdk.quest.item.Cube); - (cItem !== null && cube !== null) && sendPacket(1, sdk.packets.send.ItemToCube, 4, cItem.gid, 4, cube.gid); - - break; - case sdk.storage.Stash: - sendPacket(1, sdk.packets.send.ItemToBuffer, 4, item.gid, 4, x, 4, y, 4, 0x04); - - break; - default: - clickItemAndWait(sdk.clicktypes.click.item.Left, x, y, location); - - break; - } + // Can't deal with items on ground! + if (item.mode === sdk.items.mode.onGround) return false; + // Item already on the cursor. + if (me.itemoncursor && item.mode !== sdk.items.mode.onCursor) return false; + + // Make sure stash is open + if (this.location === sdk.storage.Stash && !Town.openStash()) return false; + + const [orgX, orgY, orgLoc] = [item.x, item.y, item.location]; + const moveItem = (x, y, location) => { + for (let n = 0; n < 5; n += 1) { + switch (location) { + case sdk.storage.Belt: + cItem = Game.getCursorUnit(); + cItem !== null && sendPacket(1, sdk.packets.send.ItemToBelt, 4, cItem.gid, 4, y); + + break; + case sdk.storage.Inventory: + sendPacket(1, sdk.packets.send.ItemToBuffer, 4, item.gid, 4, x, 4, y, 4, 0x00); + + break; + case sdk.storage.Cube: + cItem = Game.getCursorUnit(); + cube = me.getItem(sdk.quest.item.Cube); + (cItem !== null && cube !== null) && sendPacket(1, sdk.packets.send.ItemToCube, 4, cItem.gid, 4, cube.gid); + + break; + case sdk.storage.Stash: + sendPacket(1, sdk.packets.send.ItemToBuffer, 4, item.gid, 4, x, 4, y, 4, 0x04); + + break; + default: + clickItemAndWait(sdk.clicktypes.click.item.Left, x, y, location); + + break; + } - let nDelay = getTickCount(); + let nDelay = getTickCount(); - while ((getTickCount() - nDelay) < Math.max(1000, me.ping * 2 + 200)) { - if (!me.itemoncursor) return true; + while ((getTickCount() - nDelay) < Math.max(1000, me.ping * 2 + 200)) { + if (!me.itemoncursor) return true; - delay(10 + me.ping); + delay(10 + me.ping); + } } + + return false; + }; + + if (Packet.itemToCursor(item)) { + if (moveItem(mX, mY, this.location)) return true; + moveItem(orgX, orgY, orgLoc) && console.debug("Failed to move " + item.fname + " to " + mX + "/" + mY); } return false; }; - if (Packet.itemToCursor(item)) { - if (moveItem(mX, mY, this.location)) return true; - moveItem(orgX, orgY, orgLoc) && console.debug("Failed to move " + item.fname + " to " + mX + "/" + mY); - } - - return false; -}; + /** + * @param {ItemUnit} item + */ + Container.prototype.MoveTo = function (item) { + let nPos; -/** - * @param {ItemUnit} item - */ -Container.prototype.MoveTo = function (item) { - let nPos; - - try { + try { //Can we even fit it in here? - nPos = this.FindSpot(item); - if (!nPos) return false; + nPos = this.FindSpot(item); + if (!nPos) return false; - return this.MoveToSpot(item, nPos.y, nPos.x); - } catch (e) { - console.log("Storage.Container.MoveTo caught error : " + e + " - " + e.toSource()); + return this.MoveToSpot(item, nPos.y, nPos.x); + } catch (e) { + console.log("Storage.Container.MoveTo caught error : " + e + " - " + e.toSource()); - return false; - } -}; + return false; + } + }; -Container.prototype.Dump = function () { - let x, y, string; + Container.prototype.Dump = function () { + let x, y, string; - if (this.UsedSpacePercent() > 60) { - for (x = 0; x < this.height; x += 1) { - string = ""; + if (this.UsedSpacePercent() > 60) { + for (x = 0; x < this.height; x += 1) { + string = ""; - for (y = 0; y < this.width; y += 1) { - string += (this.buffer[x][y] > 0) ? "ÿc1x" : "ÿc0o"; - } + for (y = 0; y < this.width; y += 1) { + string += (this.buffer[x][y] > 0) ? "ÿc1x" : "ÿc0o"; + } - console.log(string); + console.log(string); + } } - } - console.log("ÿc9SoloPlayÿc0: " + this.name + " has used " + this.UsedSpacePercent().toFixed(2) + "% of its total space"); -}; + console.log("ÿc9SoloPlayÿc0: " + this.name + " has used " + this.UsedSpacePercent().toFixed(2) + "% of its total space"); + }; -Container.prototype.UsedSpacePercent = function () { - let usedSpace = 0; - let totalSpace = this.height * this.width; + Container.prototype.UsedSpacePercent = function () { + let usedSpace = 0; + let totalSpace = this.height * this.width; - Storage.Reload(); + Storage.Reload(); - for (let x = 0; x < this.height; x += 1) { - for (let y = 0; y < this.width; y += 1) { - if (this.buffer[x][y] > 0) { - usedSpace += 1; + for (let x = 0; x < this.height; x += 1) { + for (let y = 0; y < this.width; y += 1) { + if (this.buffer[x][y] > 0) { + usedSpace += 1; + } } } - } - return usedSpace * 100 / totalSpace; -}; + return usedSpace * 100 / totalSpace; + }; -/** - * @param {number[][]} baseRef - */ -Container.prototype.Compare = function (baseRef) { - let h, w, n, item, itemList, reference; + /** + * @param {number[][]} baseRef + */ + Container.prototype.Compare = function (baseRef) { + let h, w, n, item, itemList, reference; - Storage.Reload(); + Storage.Reload(); - try { - itemList = []; - reference = baseRef.slice(0, baseRef.length); + try { + itemList = []; + reference = baseRef.slice(0, baseRef.length); - //Insure valid reference. - if (typeof (reference) !== "object" || reference.length !== this.buffer.length || reference[0].length !== this.buffer[0].length) { - throw new Error("Unable to compare different containers."); - } + //Insure valid reference. + if (typeof (reference) !== "object" || reference.length !== this.buffer.length || reference[0].length !== this.buffer[0].length) { + throw new Error("Unable to compare different containers."); + } - for (h = 0; h < this.height; h += 1) { - Loop: - for (w = 0; w < this.width; w += 1) { - item = this.itemList[this.buffer[h][w] - 1]; + for (h = 0; h < this.height; h += 1) { + Loop: + for (w = 0; w < this.width; w += 1) { + item = this.itemList[this.buffer[h][w] - 1]; - if (!item) { - continue; - } + if (!item) { + continue; + } - for (n = 0; n < itemList.length; n += 1) { - if (itemList[n].gid === item.gid) { - continue Loop; + for (n = 0; n < itemList.length; n += 1) { + if (itemList[n].gid === item.gid) { + continue Loop; + } } - } - //Check if the buffers changed and the current buffer has an item there. - if (this.buffer[h][w] > 0 && reference[h][w] > 0) { - itemList.push(copyUnit(item)); + //Check if the buffers changed and the current buffer has an item there. + if (this.buffer[h][w] > 0 && reference[h][w] > 0) { + itemList.push(copyUnit(item)); + } } } - } - return itemList; - } catch (e) { - return false; - } -}; - -Container.prototype.toSource = function () { - return this.buffer.toSource(); -}; + return itemList; + } catch (e) { + return false; + } + }; -/** - * @type {storage} Storage - */ -let Storage = new function () { - this.Init = () => { - this.StashY = me.classic ? 4 : Developer.plugyMode ? 10 : 8; - this.Inventory = new Container("Inventory", 10, 4, 3); - this.TradeScreen = new Container("Inventory", 10, 4, 5); - this.Stash = new Container("Stash", (Developer.plugyMode ? 10 : 6), this.StashY, 7); - this.Belt = new Container("Belt", 4 * this.BeltSize(), 1, 2); - this.Cube = new Container("Horadric Cube", 3, 4, 6); - this.InvRef = []; - - this.Reload(); + Container.prototype.toSource = function () { + return this.buffer.toSource(); }; - this.BeltSize = function () { - let item = me.getItem(-1, sdk.items.mode.Equipped); // get equipped item - if (!item) return 1; // nothing equipped - - do { - if (item.bodylocation === sdk.body.Belt) { - switch (item.code) { - case "lbl": // sash - case "vbl": // light belt - return 2; - case "mbl": // belt - case "tbl": // heavy belt - return 3; - default: // everything else - return 4; + /** + * @type {storage} Storage + */ + let Storage = new function () { + this.Init = () => { + this.StashY = me.classic ? 4 : Developer.plugyMode ? 10 : 8; + this.Inventory = new Container("Inventory", 10, 4, 3); + this.TradeScreen = new Container("Inventory", 10, 4, 5); + this.Stash = new Container("Stash", (Developer.plugyMode ? 10 : 6), this.StashY, 7); + this.Belt = new Container("Belt", 4 * this.BeltSize(), 1, 2); + this.Cube = new Container("Horadric Cube", 3, 4, 6); + this.InvRef = []; + + this.Reload(); + }; + + this.BeltSize = function () { + let item = me.getItem(-1, sdk.items.mode.Equipped); // get equipped item + if (!item) return 1; // nothing equipped + + do { + if (item.bodylocation === sdk.body.Belt) { + switch (item.code) { + case "lbl": // sash + case "vbl": // light belt + return 2; + case "mbl": // belt + case "tbl": // heavy belt + return 3; + default: // everything else + return 4; + } } - } - } while (item.getNext()); + } while (item.getNext()); - return 1; // no belt - }; + return 1; // no belt + }; - this.Reload = function () { - this.Inventory.Reset(); - this.Stash.Reset(); - this.Belt.Reset(); - this.Cube.Reset(); - this.TradeScreen.Reset(); + this.Reload = function () { + this.Inventory.Reset(); + this.Stash.Reset(); + this.Belt.Reset(); + this.Cube.Reset(); + this.TradeScreen.Reset(); - let item = me.getItem(); - if (!item) return false; + let item = me.getItem(); + if (!item) return false; - do { - switch (item.location) { - case sdk.storage.Inventory: - this.Inventory.Mark(item); + do { + switch (item.location) { + case sdk.storage.Inventory: + this.Inventory.Mark(item); - break; - case sdk.storage.TradeWindow: - this.TradeScreen.Mark(item); + break; + case sdk.storage.TradeWindow: + this.TradeScreen.Mark(item); - break; - case sdk.storage.Belt: - this.Belt.Mark(item); + break; + case sdk.storage.Belt: + this.Belt.Mark(item); - break; - case sdk.storage.Cube: - this.Cube.Mark(item); + break; + case sdk.storage.Cube: + this.Cube.Mark(item); - break; - case sdk.storage.Stash: - this.Stash.Mark(item); + break; + case sdk.storage.Stash: + this.Stash.Mark(item); - break; - } - } while (item.getNext()); + break; + } + } while (item.getNext()); - return true; + return true; + }; }; -}; + + // export to global scope + global.Storage = Storage; +})(); From dc76ad280c6de466f4739db696266e0d526e37f4 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Sun, 22 Jan 2023 14:44:05 -0500 Subject: [PATCH 003/263] refactor Global and main soloplay thread - move `this.setup` into it's own IIFE file, it only needs to be called once - create `Utils\Init.js` <-- handles setup now - remove global variables `impossibleClassicBuilds`, `impossibleNonLadderBuilds`, `nipItems`, `basicSocketables`, and object factory function `addSocketableObj`. They are all located in `Utils\General.js` module now --- libs/SoloPlay/Functions/Globals.js | 105 ----------------------------- libs/SoloPlay/SoloPlay.js | 57 +--------------- libs/SoloPlay/Utils/Init.js | 64 ++++++++++++++++++ 3 files changed, 65 insertions(+), 161 deletions(-) create mode 100644 libs/SoloPlay/Utils/Init.js diff --git a/libs/SoloPlay/Functions/Globals.js b/libs/SoloPlay/Functions/Globals.js index 84f2685d..4c0fae16 100644 --- a/libs/SoloPlay/Functions/Globals.js +++ b/libs/SoloPlay/Functions/Globals.js @@ -31,11 +31,6 @@ includeIfNotIncluded("SoloPlay/BuildFiles/" + MYCLASSNAME + "/" + MYCLASSNAME + let myData = CharData.getStats(); -// these builds are not possible to do on classic -const impossibleClassicBuilds = ["Bumper", "Socketmule", "Witchyzon", "Auradin", "Torchadin", "Immortalwhirl", "Sancdreamer", "Faithbowzon", "Wfzon"]; -// these builds are not possible to do without ladder runewords -const impossibleNonLadderBuilds = ["Auradin", "Sancdreamer", "Faithbowzon"]; - Unit.prototype.__defineGetter__("mercid", function () { return !!myData ? myData.merc.classid : me.getMerc().classid; }); @@ -502,106 +497,6 @@ Object.defineProperties(SetUp, { }, }); -// SoloPlay general gameplay items -const nipItems = { - General: [ - "[name] == tomeoftownportal", - "[name] == tomeofidentify", - "[name] == gold # [gold] >= me.charlvl * 3 * me.diff", - "(me.charlvl < 20 || me.gold < 500) && [name] == minorhealingpotion", - "(me.charlvl < 25 || me.gold < 2000) && [name] == lighthealingpotion", - "(me.charlvl < 29 || me.gold < 5000) && [name] == healingpotion", - "[name] == greaterhealingpotion", - "[name] == superhealingpotion", - "(me.charlvl < 20 || me.gold < 1000) && [name] == minormanapotion", - "[name] == lightmanapotion", - "[name] == manapotion", - "[name] == greatermanapotion", - "[name] == supermanapotion", - "[name] == rejuvenationpotion", - "[name] == fullrejuvenationpotion", - "[name] == scrolloftownportal # # [maxquantity] == 20", - "[name] == scrollofidentify # # [maxquantity] == 20", - "[name] == key # # [maxquantity] == 12", - ], - - Quest: [ - "[name] == mephisto'ssoulstone", - "[name] == hellforgehammer", - "[name] == scrollofinifuss", - "[name] == keytothecairnstones", - "[name] == bookofskill", - "[name] == horadriccube", - "[name] == shaftofthehoradricstaff", - "[name] == topofthehoradricstaff", - "[name] == horadricstaff", - "[name] == ajadefigurine", - "[name] == thegoldenbird", - "[name] == potionoflife", - "[name] == lamesen'stome", - "[name] == khalim'seye", - "[name] == khalim'sheart", - "[name] == khalim'sbrain", - "[name] == khalim'sflail", - "[name] == khalim'swill", - "[name] == scrollofresistance", - ], -}; - -const addSocketableObj = (classid, socketWith = [], temp = [], useSocketQuest = false, condition = () => {}) => ({ - classid: classid, - socketWith: socketWith, - temp: temp, - useSocketQuest: useSocketQuest, - condition: condition -}); -const basicSocketables = { - caster: [], - all: [], -}; -// insight base -basicSocketables.all.push(addSocketableObj(sdk.items.Bill, [], [], true, (item) => - me.nightmare && item.ilvl >= 26 && item.isBaseType && item.ethereal -)); -// insight base -basicSocketables.all.push(addSocketableObj(sdk.items.ColossusVoulge, [], [], true, (item) => - me.nightmare && item.ilvl >= 26 && item.isBaseType && item.ethereal -)); -// Crown of Ages -basicSocketables.caster.push(addSocketableObj(sdk.items.Corona, [sdk.items.runes.Ber, sdk.items.runes.Um], [sdk.items.gems.Perfect.Ruby], - false, (item) => item.unique -)); -// Moser's -basicSocketables.caster.push(addSocketableObj(sdk.items.RoundShield, [sdk.items.runes.Um], [sdk.items.gems.Perfect.Diamond], - false, (item) => item.unique && !item.ethereal -)); -// Spirit Forge -basicSocketables.caster.push(addSocketableObj(sdk.items.LinkedMail, [sdk.items.runes.Shael], [sdk.items.gems.Perfect.Ruby], - false, (item) => item.unique && !item.ethereal -)); -// Dijjin Slayer -basicSocketables.caster.push(addSocketableObj(sdk.items.Ataghan, [sdk.items.runes.Amn], [sdk.items.gems.Perfect.Skull], - false, (item) => !Check.currentBuild().caster && item.unique && !item.ethereal -)); -// Bone Hew - for merc -basicSocketables.caster.push(addSocketableObj(sdk.items.OgreAxe, [sdk.items.runes.Hel, sdk.items.runes.Amn], [sdk.items.gems.Perfect.Skull], - false, (item) => item.unique -)); -// spirit base -basicSocketables.caster.push(addSocketableObj(sdk.items.BroadSword, [], [], true, (item) => - me.normal && !Check.haveBase("sword", 4) && !me.checkItem({name: sdk.locale.items.Spirit, itemtype: sdk.items.type.Sword}).have - && item.ilvl >= 26 && item.isBaseType && !item.ethereal -)); -// spirit base -basicSocketables.caster.push(addSocketableObj(sdk.items.CrystalSword, [], [], true, (item) => - me.normal && !Check.haveBase("sword", 4) && !me.checkItem({name: sdk.locale.items.Spirit, itemtype: sdk.items.type.Sword}).have - && item.ilvl >= 26 && item.ilvl <= 40 && item.isBaseType && !item.ethereal -)); -// Lidless -basicSocketables.caster.push(addSocketableObj(sdk.items.GrimShield, [sdk.items.runes.Um], [sdk.items.gems.Perfect.Diamond], !me.hell, (item) => - item.unique && (item.isInStorage || (item.isEquipped && !item.isOnSwap)) && !item.ethereal -)); - // misc const goToDifficulty = function (diff = undefined, reason = "") { try { diff --git a/libs/SoloPlay/SoloPlay.js b/libs/SoloPlay/SoloPlay.js index b3a8f71a..7de8a5a6 100644 --- a/libs/SoloPlay/SoloPlay.js +++ b/libs/SoloPlay/SoloPlay.js @@ -120,61 +120,6 @@ function main () { } }; - this.setup = function () { - myPrint("start setup"); - NTIP.arrayLooping(nipItems.Quest, nipItems.General); - - try { - if (impossibleClassicBuilds.includes(SetUp.finalBuild) && me.classic) { - throw new Error("Kolbot-SoloPlay: " + SetUp.finalBuild + " cannot be used in classic. Change the info tag or remake as an expansion character...Shutting down"); - } - - if (impossibleNonLadderBuilds.includes(SetUp.finalBuild) && !Developer.addLadderRW) { - throw new Error("Kolbot-SoloPlay: " + SetUp.finalBuild + " cannot be used in non-ladder as they require ladder runewords. Change the info tag or remake as an ladder character...Shutting down"); - } - } catch (e) { - D2Bot.printToConsole(e, sdk.colors.D2Bot.Red); - FileTools.remove("data/" + me.profile + ".json"); - FileTools.remove("libs/SoloPlay/Data/" + me.profile + ".GameTime" + ".json"); - D2Bot.stop(); - } - - if (me.charlvl === 1) { - let buckler = me.getItem(sdk.items.Buckler); - !!buckler && buckler.isEquipped && buckler.drop(); - } - - Town.heal() && me.cancelUIFlags(); - Check.checkSpecialCase(); - - // check if any of our currently equipped items are no longer usable - can happen after respec - me.getItemsEx() - .filter(item => item.isEquipped) - .forEach(item => { - if (me.getStat(sdk.stats.Strength) < item.strreq - || me.getStat(sdk.stats.Dexterity) < item.dexreq - || item.ethereal && item.isBroken) { - myPrint("No longer able to use: " + item.fname); - Item.removeItem(null, item); - } else if (sdk.quest.items.includes(item.classid)) { - myPrint("Removing Quest Item: " + item.fname); - Item.removeItem(null, item); - } - }); - - me.getItemsEx() - .filter(item => item.isInInventory && sdk.quest.items.includes(item.classid)) - .forEach(item => { - Quest.stashItem(item); - }); - - me.cancelUIFlags(); - // initialize final charms if we have any - Item.initCharms(); - - return true; - }; - // Initialize libs - load config variables, build pickit list, attacks, containers and cubing and runeword recipes Config.init(true); Pickit.init(true); @@ -269,7 +214,7 @@ function main () { !me.realm && D2Bot.updateRuns(); // Start Running Script - this.setup(); + includeIfNotIncluded("SoloPlay/Utils/Init.js"); // Start Developer mode - this stops the script from progressing past this point and allows running specific scripts/functions through chat commands if (Developer.developerMode.enabled) { diff --git a/libs/SoloPlay/Utils/Init.js b/libs/SoloPlay/Utils/Init.js new file mode 100644 index 00000000..ea7af0b3 --- /dev/null +++ b/libs/SoloPlay/Utils/Init.js @@ -0,0 +1,64 @@ +/** +* @filename Init.js +* @author theBGuy +* @desc Initialization process for main soloplay thread +* +*/ + +(function() { + // Only load this in global scope + if (getScript(true).name.toLowerCase() === "libs\\soloplay\\soloplay.js") { + myPrint("start setup"); + const { nipItems, impossibleClassicBuilds, impossibleNonLadderBuilds } = require("../Utils/General"); + NTIP.arrayLooping(nipItems.Quest, nipItems.General); + + try { + if (impossibleClassicBuilds.includes(SetUp.finalBuild) && me.classic) { + throw new Error("Kolbot-SoloPlay: " + SetUp.finalBuild + " cannot be used in classic. Change the info tag or remake as an expansion character...Shutting down"); + } + + if (impossibleNonLadderBuilds.includes(SetUp.finalBuild) && !Developer.addLadderRW) { + throw new Error("Kolbot-SoloPlay: " + SetUp.finalBuild + " cannot be used in non-ladder as they require ladder runewords. Change the info tag or remake as an ladder character...Shutting down"); + } + } catch (e) { + D2Bot.printToConsole(e, sdk.colors.D2Bot.Red); + FileTools.remove("data/" + me.profile + ".json"); + FileTools.remove("libs/SoloPlay/Data/" + me.profile + ".GameTime" + ".json"); + D2Bot.stop(); + } + + if (me.charlvl === 1) { + let buckler = me.getItem(sdk.items.Buckler); + !!buckler && buckler.isEquipped && buckler.drop(); + } + + Town.heal() && me.cancelUIFlags(); + Check.checkSpecialCase(); + + // check if any of our currently equipped items are no longer usable - can happen after respec + me.getItemsEx() + .filter(item => item.isEquipped) + .forEach(item => { + if (me.getStat(sdk.stats.Strength) < item.strreq + || me.getStat(sdk.stats.Dexterity) < item.dexreq + || item.ethereal && item.isBroken) { + myPrint("No longer able to use: " + item.fname); + Item.removeItem(null, item); + } else if (sdk.quest.items.includes(item.classid)) { + myPrint("Removing Quest Item: " + item.fname); + Item.removeItem(null, item); + } + }); + + me.getItemsEx() + .filter(item => item.isInInventory && sdk.quest.items.includes(item.classid)) + .forEach(item => { + Quest.stashItem(item); + }); + + me.cancelUIFlags(); + // initialize final charms if we have any + Item.initCharms(); + } + return true; +})(); From 0d6a8dcd02828c045b3a5bcce08285bead4552d6 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Sun, 22 Jan 2023 14:47:41 -0500 Subject: [PATCH 004/263] Some general cleanup - moved charClassMap into Starter.receiveCopyData - add `me.className` prototype --- D2BotSoloPlay.dbj | 10 +++- libs/SoloPlay/Functions/Me.js | 8 +++ libs/SoloPlay/Modules/Coords.js | 72 +++++++++++--------------- libs/SoloPlay/Modules/GameData.js | 38 ++++++++------ libs/SoloPlay/Scripts/developermode.js | 2 +- libs/SoloPlay/Scripts/travincal.js | 22 +++----- 6 files changed, 79 insertions(+), 73 deletions(-) diff --git a/D2BotSoloPlay.dbj b/D2BotSoloPlay.dbj index e24e1f33..c9d1710c 100644 --- a/D2BotSoloPlay.dbj +++ b/D2BotSoloPlay.dbj @@ -58,7 +58,6 @@ if (typeof AdvancedConfig[me.profile] === "object") { let joinInfo; let gameTracker; Starter.BNET = ([sdk.game.profiletype.Battlenet, sdk.game.profiletype.OpenBattlenet].includes(Profile().type)); -const charClassMap = {"ZON": "amazon", "SOR": "sorceress", "NEC": "necromancer", "PAL": "paladin", "BAR": "barbarian", "DRU": "druid", "SIN": "assassin"}; // initialize data files if (!FileTools.exists("data/" + me.profile + ".json") && DataFile.create()) { @@ -109,6 +108,15 @@ new Overrides.Override(Starter, Starter.receiveCopyData, function (orignal, mode D2Bot.stop(); } + const charClassMap = { + "ZON": "amazon", + "SOR": "sorceress", + "NEC": "necromancer", + "PAL": "paladin", + "BAR": "barbarian", + "DRU": "druid", + "SIN": "assassin" + }; buildCheck[1] = buildCheck[1].toString().substring(0, 3); if (charClassMap[buildCheck[1]]) { diff --git a/libs/SoloPlay/Functions/Me.js b/libs/SoloPlay/Functions/Me.js index 563b69d8..09c30d1f 100644 --- a/libs/SoloPlay/Functions/Me.js +++ b/libs/SoloPlay/Functions/Me.js @@ -93,6 +93,14 @@ if (!me.hasOwnProperty("PR")) { }); } +if (!me.hasOwnProperty("className")) { + Object.defineProperty(me, "className", { + get: function () { + return sdk.player.class.nameOf(me.classid); + } + }); +} + /** * Soloplay specific but might as well keep with the format */ diff --git a/libs/SoloPlay/Modules/Coords.js b/libs/SoloPlay/Modules/Coords.js index 5da81c74..9dcaecb1 100644 --- a/libs/SoloPlay/Modules/Coords.js +++ b/libs/SoloPlay/Modules/Coords.js @@ -1,19 +1,16 @@ -var __importDefault = (this && this.__importDefault) || function (mod) { - return (mod && mod.__esModule) ? mod : { "default": mod }; -}; +/* eslint-disable dot-notation */ (function (factory) { if (typeof module === "object" && typeof module.exports === "object") { var v = factory(require, exports); if (v !== undefined) module.exports = v; - } - else if (typeof define === "function" && define.amd) { + } else if (typeof define === "function" && define.amd) { define(["require", "exports", "../../modules/sdk"], factory); } })(function (require, exports) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.getSpotsFor = exports.findCastingSpotRange = exports.findCastingSpotSkill = exports.isBlockedBetween = exports.getCollisionBetweenCoords = exports.convertToCoordArray = exports.getCoordsBetween = exports.Collision = exports.BlockBits = void 0; - var sdk_1 = require("../../modules/sdk"); + const sdk_1 = require("../../modules/sdk"); var BlockBits; (function (BlockBits) { BlockBits[BlockBits["BlockWall"] = 1] = "BlockWall"; @@ -50,54 +47,50 @@ var __importDefault = (this && this.__importDefault) || function (mod) { Collision[Collision["BLOCK_MISSILE"] = 2062] = "BLOCK_MISSILE"; })(Collision = exports.Collision || (exports.Collision = {})); function getCoordsBetween(x1, y1, x2, y2) { - var abs = Math.abs, min = Math.min, max = Math.max, floor = Math.floor; - var A = { x: x1, y: y1 }; - var B = { x: x2, y: y2 }; + const abs = Math.abs, min = Math.min, max = Math.max, floor = Math.floor; + const A = { x: x1, y: y1 }; + const B = { x: x2, y: y2 }; if (max(x1, x2) - min(x1, x2) < max(y1, y2) - min(y1, y2)) { // noinspection JSSuspiciousNameCombination return getCoordsBetween(y1, x1, y2, x2).map(function (_a) { - var x = _a.x, y = _a.y; + let x = _a.x, y = _a.y; return ({ x: y, y: x }); }); } function slope(a, b) { - if (a.x === b.x) - return null; + if (a.x === b.x) return null; return (b.y - a.y) / (b.x - a.x); } function intercept(point, slope) { // vertical line - if (slope === null) - return point.x; + if (slope === null) return point.x; return point.y - slope * point.x; } - var m = slope(A, B); - var b = intercept(A, m); - var coordinates = []; - for (var x = min(A.x, B.x); x <= max(A.x, B.x); x++) { - var y = m * x + b; + const m = slope(A, B); + const b = intercept(A, m); + const coordinates = []; + for (let x = min(A.x, B.x); x <= max(A.x, B.x); x++) { + let y = m * x + b; coordinates.push({ x: x, y: y }); } return coordinates.map(function (_a) { - var x = _a.x, y = _a.y; + let x = _a.x, y = _a.y; return ({ x: floor(x), y: floor(y) }); }) .filter(function (el, idx, self) { return self.findIndex(function (other) { return other.x === el.x && other.y === el.y; }) === idx; }); } exports.getCoordsBetween = getCoordsBetween; - var convertToCoordArray = function (args, caller, length) { + const convertToCoordArray = function (args, caller, length) { if (length === void 0) { length = 2; } var coords = []; for (var i = 0; i < args.length; i++) { - if (typeof args[i] === 'number' && i < args.length - 1) { + if (typeof args[i] === "number" && i < args.length - 1) { coords.push({ x: args[i], y: args[++i] }); - } - else { + } else { coords.push(args[i]); } } - if (coords.length !== length) - throw TypeError('Didnt give proper arguments to ' + caller); + if (coords.length !== length) throw TypeError("Didnt give proper arguments to " + caller); return coords; }; exports.convertToCoordArray = convertToCoordArray; @@ -106,7 +99,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) { for (var _i = 0; _i < arguments.length; _i++) { args[_i] = arguments[_i]; } - var _a = exports.convertToCoordArray(args, 'getCollisionBetweenCoords', 2), one = _a[0], two = _a[1]; + var _a = exports.convertToCoordArray(args, "getCollisionBetweenCoords", 2), one = _a[0], two = _a[1]; if (getDistance(one, two) > 50) { return -1; } @@ -120,8 +113,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) { // | (getCollision(me.area, cur.x, cur.y-1) | 0) | (getCollision(me.area, cur.x, cur.y) | 0); }, 0); - } - catch (e) { + } catch (e) { return -1; // Area not loaded } } @@ -142,26 +134,26 @@ var __importDefault = (this && this.__importDefault) || function (mod) { } exports.isBlockedBetween = isBlockedBetween; function checkCollisionBetween(unit1, unit2, coll) { - var args = []; + let args = []; args.push(unit1, unit2); - var collision = getCollisionBetweenCoords.apply(null, args); + let collision = getCollisionBetweenCoords.apply(null, args); return !!(collision & (0 | coll)); } exports.checkCollisionBetween = checkCollisionBetween; Room.prototype.isInRoom = function () { - var args = []; - for (var _i = 0; _i < arguments.length; _i++) { + let args = []; + for (let _i = 0; _i < arguments.length; _i++) { args[_i] = arguments[_i]; } - var _a = exports.convertToCoordArray(args, 'isInRoom', 1)[0], x = _a[0], y = _a[1]; + let _a = exports.convertToCoordArray(args, "isInRoom", 1)[0], x = _a[0], y = _a[1]; return this && x >= this.x * 5 && x < this.x * 5 + this.xsize && y >= this.y * 5 && y < this.y * 5 + this.ysize; }; function findCastingSpotSkill(skill, unit, minRange, thickness, collision) { if (minRange === void 0) { minRange = 5; } if (thickness === void 0) { thickness = 5; } if (collision === void 0) { collision = Collision.BLOCK_MISSILE; } - var range = Skill.getRange(skill); - console.log('Searching range for', skill, Object.keys(sdk_1.skills).find(function (el) { return sdk_1.skills[el] === skill; }), range); + let range = Skill.getRange(skill); + console.log("Searching range for", skill, Object.keys(sdk_1.skills).find(function (el) { return sdk_1.skills[el] === skill; }), range); return findCastingSpotRange(range, unit, minRange, thickness, collision); } exports.findCastingSpotSkill = findCastingSpotSkill; @@ -169,10 +161,9 @@ var __importDefault = (this && this.__importDefault) || function (mod) { if (minRange === void 0) { minRange = 5; } if (thickness === void 0) { thickness = 5; } if (collision === void 0) { collision = Collision.BLOCK_MISSILE; } - var spots = getSpotsFor(collision, thickness, unit) + let spots = getSpotsFor(collision, thickness, unit) .sort(function (a, b) { - if (CollMap.checkColl(a, me, BlockBits.BlockWall, 7)) - return 1; + if (CollMap.checkColl(a, me, BlockBits.BlockWall, 7)) return 1; return getDistance(me, a) - getDistance(me, b); }); return spots.find(function (a) { @@ -188,8 +179,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) { for (var oX = -fieldSize; oX < fieldSize; oX++) { for (var oY = -fieldSize; oY < fieldSize; oY++) { var _a = [unit.x + oX, unit.y + oY], x = _a[0], y = _a[1]; - if (getDistance(unit.x, unit.y, x, y) > 40) - continue; + if (getDistance(unit.x, unit.y, x, y) > 40) continue; var isCol = !!(getCollision(unit.area, x, y) & collision); for (var i = -2; i < 2 && !isCol; i++) { for (var j = -2; j < 2 && !isCol; j++) { diff --git a/libs/SoloPlay/Modules/GameData.js b/libs/SoloPlay/Modules/GameData.js index 75eebb49..a341e634 100644 --- a/libs/SoloPlay/Modules/GameData.js +++ b/libs/SoloPlay/Modules/GameData.js @@ -639,13 +639,15 @@ switch (skillID) { case sdk.skills.ChargedBolt: // more than one bolt can hit but may calc this as splashdamage instead - let baseId = getBaseStat("monstats", unit.classid, "baseid"); - let size = getBaseStat("monstats2", baseId, "sizex"); - (typeof size !== "number" || size < 1 || size > 3) && (size = 3); - let dist = unit.distance; - const modifier = size === 1 ? 0.5 : size === 3 ? 1.5 : size === 2 && dist < 5 ? 1.2 : 1; - dmg.min *= modifier; - dmg.max *= modifier; + if (unit) { + let baseId = getBaseStat("monstats", unit.classid, "baseid"); + let size = getBaseStat("monstats2", baseId, "sizex"); + (typeof size !== "number" || size < 1 || size > 3) && (size = 3); + let dist = unit.distance; + const modifier = size === 1 ? 0.5 : size === 3 ? 1.5 : size === 2 && dist < 5 ? 1.2 : 1; + dmg.min *= modifier; + dmg.max *= modifier; + } // need to take into account the amount of bolts released // the size of the unit we are targetting @@ -1048,6 +1050,7 @@ const conviction = this.getConviction(), ampDmg = this.getAmp(); const isUndead = (typeof unit === "number" ? MonsterData[unit].Undead : MonsterData[unit.classid].Undead); skillDamageInfo = skillDamageInfo || this.allSkillDamage(unit); + console.debug(unit, "---", hp); // if (conviction && unit instanceof Unit && !unit.getState(sdk.states.Conviction)) conviction = 0; //ToDo; enable when fixed telestomp for (let sk in skillDamageInfo) { @@ -1138,26 +1141,28 @@ || avgDmg * (100 - resist) / 100; } - + console.debug(hp, "---/", totalDmg); let tmpEffort = Math.ceil(hp / totalDmg); tmpEffort /= this.dmgModifier(sk | 0, parent || unit); // care for mana - if (GameData.myReference.mp < Skill.getManaCost([sk])) { + if (GameData.myReference.mp < Skill.getManaCost(sk)) { tmpEffort *= 5; // More effort in a skill we dont have mana for } - // check valid location - if (sk === sdk.skills.Blizzard && !getCollision(unit.area, unit.x, unit.y & !Coords_1.BlockBits.IsOnFloor)) { - tmpEffort *= 5; + // check valid location, blizzard and meteor fail over lava + if (typeof unit === "object") { + if ([sdk.skills.Blizzard, sdk.skills.Meteor].indexOf(sk) && !Attack.validSpot(unit.x, unit.y, sk, unit.classid)) { + tmpEffort *= 5; + } } // Use less cool down spells, if something better is around - /*if (this.skillCooldown(sk | 0)) { + /* if (this.skillCooldown(sk | 0)) { console.log("tmpEffort: " + (Math.ceil(tmpEffort)) + " eretEffor: " + eret.effort); tmpEffort *= 5; - }*/ + } */ if (tmpEffort <= eret.effort) { eret.effort = tmpEffort; eret.skill = sk | 0; @@ -1170,6 +1175,7 @@ } } } + console.log(eret, allData); if (all && allData.length) return allData; if (eret.skill >= 0) return eret; return null; @@ -1278,7 +1284,7 @@ tmpEffort /= this.dmgModifier(sk | 0, unit); // care for mana - if (GameData.myReference.mp < Skill.getManaCost([sk])) { + if (GameData.myReference.mp < Skill.getManaCost(sk)) { tmpEffort *= 5; // More effort in a skill we dont have mana for } @@ -1344,7 +1350,7 @@ const effort = [], uniqueSkills = []; for (let i = 50; i < 120; i++) { try { - effort.push(GameData.monsterEffort(i, sdk.areas.ThroneOfDestruction)); + effort.push(GameData.monsterEffort(i, sdk.areas.ThroneofDestruction)); } catch (e) { /*dontcare*/ } diff --git a/libs/SoloPlay/Scripts/developermode.js b/libs/SoloPlay/Scripts/developermode.js index fdf8a5fc..43655158 100644 --- a/libs/SoloPlay/Scripts/developermode.js +++ b/libs/SoloPlay/Scripts/developermode.js @@ -255,8 +255,8 @@ function developermode() { command = false; } + !UnitInfo.cleared && !Game.getSelectedUnit() && UnitInfo.remove(); if (userAddon) { - !UnitInfo.cleared && !Game.getSelectedUnit() && UnitInfo.remove(); uInfo = Game.getSelectedUnit(); UnitInfo.createInfo(uInfo); } diff --git a/libs/SoloPlay/Scripts/travincal.js b/libs/SoloPlay/Scripts/travincal.js index 2c04786d..d09b0aaa 100644 --- a/libs/SoloPlay/Scripts/travincal.js +++ b/libs/SoloPlay/Scripts/travincal.js @@ -13,7 +13,7 @@ function travincal () { Pather.checkWP(sdk.areas.Travincal, true) ? Pather.useWaypoint(sdk.areas.Travincal) : Pather.getWP(sdk.areas.Travincal); Precast.doPrecast(true); - let council = { + const council = { x: me.x + 76, y: me.y - 67 }; @@ -23,9 +23,7 @@ function travincal () { Pickit.pickItems(); // go to orb - if (!Pather.moveToPreset(sdk.areas.Travincal, sdk.unittype.Object, sdk.objects.CompellingOrb)) { - console.log("ÿc8Kolbot-SoloPlayÿc0: Failed to move to compelling orb"); - } + Pather.moveToPreset(sdk.areas.Travincal, sdk.unittype.Object, sdk.objects.CompellingOrb); let orb = Game.getObject(sdk.objects.CompellingOrb); !!orb && Attack.clearPos(orb.x, orb.y, 15); @@ -53,12 +51,12 @@ function travincal () { Town.goToTown(); } - Quest.equipItem(sdk.items.quest.KhalimsWill, 4); + Quest.equipItem(sdk.items.quest.KhalimsWill, sdk.body.RightArm); delay(250 + me.ping); // return to Trav if (!Pather.usePortal(sdk.areas.Travincal, me.name)) { - console.log("ÿc8Kolbot-SoloPlayÿc0: Failed to go back to Travincal and smash orb"); + Pather.moveToPreset(sdk.areas.Travincal, sdk.unittype.Object, sdk.objects.CompellingOrb); } Quest.smashSomething(sdk.objects.CompellingOrb); @@ -86,6 +84,7 @@ function travincal () { } return me.inArea(sdk.areas.DuranceofHateLvl1); }, 10000, 40); + if (!me.inArea(sdk.areas.DuranceofHateLvl1)) { Pather.moveToExit([sdk.areas.DuranceofHateLvl1, sdk.areas.DuranceofHateLvl2]); } else { @@ -93,15 +92,10 @@ function travincal () { } Pather.getWP(sdk.areas.DuranceofHateLvl2); Pather.useWaypoint(sdk.areas.KurastDocktown); + } - if (!Pather.moveToExit(sdk.areas.DuranceofHateLvl1, true)) { - delay(250 + me.ping * 2); - Pather.moveToExit(sdk.areas.DuranceofHateLvl1, true); - } - - if (!Pather.checkWP(sdk.areas.DuranceofHateLvl2)) { - Pather.getWP(sdk.areas.DuranceofHateLvl2); - } + if (!Pather.checkWP(sdk.areas.DuranceofHateLvl2)) { + Pather.getWP(sdk.areas.DuranceofHateLvl2); } return true; From 36cd35751cc1c79f514705e35d2df64bf0cbebd5 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Mon, 30 Jan 2023 19:02:25 -0500 Subject: [PATCH 005/263] valid item check and failure to revive merc fix - check if the x coord of item we tried to make room for is still valid to prevent triggering mule on undefined - If get the "You do not have enough gold for this" popup while attempting to revive merc, handle closing it out before canceling npc menu flag to prevent crash --- libs/SoloPlay/Functions/PickitOverrides.js | 9 ++++++--- libs/SoloPlay/Functions/TownOverrides.js | 4 ++++ 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/libs/SoloPlay/Functions/PickitOverrides.js b/libs/SoloPlay/Functions/PickitOverrides.js index 3d6df1ae..3a8a8d2a 100644 --- a/libs/SoloPlay/Functions/PickitOverrides.js +++ b/libs/SoloPlay/Functions/PickitOverrides.js @@ -724,10 +724,13 @@ Pickit.pickItems = function (range = Config.PickRange, once = false) { } // Can't make room - trigger automule - Misc.itemLogger("No room for", currItem); - console.log("ÿc7Not enough room for " + this.itemColor(currItem) + currItem.name); + if (copyUnit(currItem).x !== undefined) { + Misc.itemLogger("No room for", currItem); + console.log("ÿc7Not enough room for " + Item.color(currItem) + currItem.name); + needMule = true; - needMule = true; + break; + } } // Item can fit - pick it up diff --git a/libs/SoloPlay/Functions/TownOverrides.js b/libs/SoloPlay/Functions/TownOverrides.js index bce5d043..64261ba8 100644 --- a/libs/SoloPlay/Functions/TownOverrides.js +++ b/libs/SoloPlay/Functions/TownOverrides.js @@ -976,6 +976,10 @@ Town.reviveMerc = function () { // "You do not have enough gold for that." if (dialog[lines].text.match(getLocaleString(sdk.locale.dialog.youDoNotHaveEnoughGoldForThat), "gi")) { + dialog.find(line => !line.text.match(getLocaleString(sdk.locale.dialog.youDoNotHaveEnoughGoldForThat), "gi")).handler(); + delay(Math.max(750, me.ping * 2)); + me.cancelUIFlags(); + console.error("Could not revive merc: My current gold: " + me.gold + ", current mercrevivecost: " + me.mercrevivecost); return false; } } From eb3d85016ff7e71b7a0c14dbcea059aa661b9e39 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Fri, 3 Feb 2023 19:32:20 -0500 Subject: [PATCH 006/263] Keep updated with the core changes - handle filename updates and function differences --- D2BotSoloPlay.dbj | 44 ++++--- default.dbj | 80 +++++-------- libs/SoloPlay/Functions/AttackOverrides.js | 18 +-- libs/SoloPlay/Functions/AutoMuleOverrides.js | 2 +- libs/SoloPlay/Functions/AutoStatOverrides.js | 2 +- .../ClassAttackOverrides/AmazonAttacks-WIP.js | 2 +- .../ClassAttackOverrides/AmazonAttacks.js | 2 +- .../ClassAttackOverrides/AssassinAttacks.js | 2 +- .../ClassAttackOverrides/BarbarianAttacks.js | 2 +- .../ClassAttackOverrides/DruidAttacks.js | 2 +- .../NecromancerAttacks.js | 2 +- .../ClassAttackOverrides/PaladinAttacks.js | 2 +- .../ClassAttackOverrides/SorceressAttacks.js | 104 ++++++++-------- libs/SoloPlay/Functions/ConfigOverrides.js | 2 +- libs/SoloPlay/Functions/CubingOverrides.js | 15 +-- libs/SoloPlay/Functions/DynamicTiers.js | 11 +- libs/SoloPlay/Functions/Globals.js | 13 +- libs/SoloPlay/Functions/ItemOverrides.js | 105 ++++++++++++++-- libs/SoloPlay/Functions/ItemPrototypes.js | 2 +- libs/SoloPlay/Functions/ItemUtilities.js | 22 +++- libs/SoloPlay/Functions/LoaderOverrides.js | 2 +- libs/SoloPlay/Functions/Me.js | 2 +- libs/SoloPlay/Functions/MiscOverrides.js | 91 +------------- .../SoloPlay/Functions/MuleloggerOverrides.js | 16 +-- libs/SoloPlay/Functions/NTIPOverrides.js | 2 +- libs/SoloPlay/Functions/PatherOverrides.js | 18 +-- libs/SoloPlay/Functions/PickitOverrides.js | 45 ++++--- libs/SoloPlay/Functions/PrecastOverrides.js | 2 +- libs/SoloPlay/Functions/PrototypeOverrides.js | 2 +- libs/SoloPlay/Functions/Quest.js | 6 +- libs/SoloPlay/Functions/RunewordsOverrides.js | 2 +- libs/SoloPlay/Functions/SkillOverrides.js | 2 +- libs/SoloPlay/Functions/TownOverrides.js | 66 +++++----- libs/SoloPlay/Modules/GameData.js | 8 +- libs/SoloPlay/Modules/Guard.js | 42 +------ libs/SoloPlay/Scripts/a1chests.js | 4 +- libs/SoloPlay/Scripts/a5chests.js | 4 +- libs/SoloPlay/Scripts/ancients.js | 3 +- libs/SoloPlay/Scripts/baal.js | 1 + libs/SoloPlay/Scripts/cows.js | 9 +- libs/SoloPlay/Scripts/developermode.js | 36 +++--- libs/SoloPlay/Scripts/diablo.js | 7 +- libs/SoloPlay/Scripts/tristram.js | 4 + libs/SoloPlay/SoloPlay.js | 113 ++++++++++-------- libs/SoloPlay/Threads/AutoBuildThread.js | 34 ++---- libs/SoloPlay/Threads/EventThread.js | 28 ++--- libs/SoloPlay/Threads/ToolsThread.js | 67 ++++++----- libs/SoloPlay/Threads/TownChicken.js | 36 ++---- libs/SoloPlay/Tools/CharData.js | 38 ++++-- libs/SoloPlay/Tools/Developer.js | 10 ++ libs/SoloPlay/Tools/Tracker.js | 14 +-- 51 files changed, 580 insertions(+), 568 deletions(-) diff --git a/D2BotSoloPlay.dbj b/D2BotSoloPlay.dbj index c9d1710c..7d52e35d 100644 --- a/D2BotSoloPlay.dbj +++ b/D2BotSoloPlay.dbj @@ -4,10 +4,14 @@ * @author theBGuy * @desc Entry script for SoloPlay leveling system * +* +* @typedef {import("./sdk/globals")} */ -include("StarterConfig.js"); -// D2BotSoloPlay specific settings - for global settings see libs/StarterConfig.js +// No touchy! +include("critical.js"); // required + +// D2BotSoloPlay specific settings - for global settings see libs/starter/StarterConfig.js Starter.Config.InvalidPasswordDelay = 10; // Minutes to wait after getting Invalid Password message Starter.Config.GameDoesNotExistTimeout = 600; // Seconds to wait before cancelling the 'Game does not exist.' screen Starter.Config.DelayBeforeLogin = rand(5, 25); // Seconds to wait before logging in @@ -28,18 +32,13 @@ Starter.Config.GlobalAccountPassword = ""; // Set value for a global password fo * - need to handle someone using a premade account */ -// No touchy! -include("polyfill.js"); -include("json2.js"); -include("OOG.js"); -include("automule.js"); -include("gambling.js"); -include("craftingsystem.js"); -include("torchsystem.js"); -include("common/util.js"); -include("common/misc.js"); -include("common/pather.js"); -include("common/prototypes.js"); +// the only things we really need from these are their oog checks +includeSystemLibs(); + +// what do I need from here? +const { getAreaName, Time } = require("./libs/core/Util"); +include("core/experience.js"); +// solo specific include("SoloPlay/Tools/Developer.js"); include("SoloPlay/Tools/CharData.js"); include("SoloPlay/Tools/Tracker.js"); @@ -48,16 +47,15 @@ include("SoloPlay/Tools/OOGOverrides.js"); include("SoloPlay/Functions/SoloEvents.js"); include("SoloPlay/Functions/ConfigOverrides.js"); -let Controls = require("./modules/Control"); -let Overrides = require("./modules/Override"); - -if (typeof AdvancedConfig[me.profile] === "object") { - Object.assign(Starter.Config, AdvancedConfig[me.profile]); +if (typeof Starter.AdvancedConfig[me.profile] === "object") { + Object.assign(Starter.Config, Starter.AdvancedConfig[me.profile]); +} else { + // no need to carry around the reference + delete Starter.AdvancedConfig; } let joinInfo; let gameTracker; -Starter.BNET = ([sdk.game.profiletype.Battlenet, sdk.game.profiletype.OpenBattlenet].includes(Profile().type)); // initialize data files if (!FileTools.exists("data/" + me.profile + ".json") && DataFile.create()) { @@ -337,7 +335,7 @@ function main () { while (!D2Bot.init()) { delay(250); } - load("tools/heartbeat.js"); + load("threads/heartbeat.js"); while (!Object.keys(Starter.gameInfo).length) { D2Bot.requestGameInfo(); @@ -349,7 +347,6 @@ function main () { D2Bot.stop(); } - // Developer.logPerformance && Tracker.checkValidity(); Starter.gameCount = (DataFile.getStats().runs + 1 || 1); if (Starter.gameInfo.error) { @@ -363,7 +360,6 @@ function main () { D2Bot.getProfile(); console.log("Getting Profile"); delay(500); - // console.log(Starter.profileInfo); } if (Starter.profileInfo.charName === "") { @@ -405,7 +401,7 @@ function main () { try { let [exp, myGold, fr, cr, lr, pr] = [xp(), tGold(), fireRes(resPenalty), coldRes(resPenalty), lightRes(resPenalty), poisRes(resPenalty)]; - areaName = !!me.area ? Pather.getAreaName(me.area) : ""; + areaName = !!me.area ? getAreaName(me.area) : ""; statusString = me.name + " | Lvl: " + me.charlvl + " (" + exp + "%) (Diff: " + diffName + ") (A: " + areaName + ") (G: " + myGold + ") (F: " + fr + "/C: " + cr + "/L: " + lr + "/P: " + pr + ")"; } catch (e) { console.error(e); diff --git a/default.dbj b/default.dbj index 8cafbace..3d9d19d6 100644 --- a/default.dbj +++ b/default.dbj @@ -1,23 +1,22 @@ /** * @filename default.dbj -* @author kolton +* @author kolton, theBGuy * @desc gets executed upon gamejoin, main thread for bot * */ js_strict(true); +include("critical.js"); // required -include("json2.js"); -include("NTItemParser.dbl"); -include("OOG.js"); -include("AutoMule.js"); -include("Gambling.js"); -include("CraftingSystem.js"); -include("TorchSystem.js"); -include("MuleLogger.js"); -include("GameAction.js"); -include("common/util.js"); +// globals needed for core gameplay +includeCoreLibs(); -includeCommonLibs(); +// system libs +includeSystemLibs(); +include("systems/mulelogger/MuleLogger.js"); +include("systems/gameaction/GameAction.js"); + +// main thread specific +const LocalChat = require("./libs/modules/LocalChat"); function main () { D2Bot.init(); // Get D2Bot# handle @@ -25,7 +24,6 @@ function main () { (function (global, original) { global.load = function (...args) { - console.trace(); original.apply(this, args); delay(500); }; @@ -39,31 +37,18 @@ function main () { clearAllEvents(); // remove any event listeners from game crash // load heartbeat if it isn't already running - !getScript("tools/heartbeat.js") && load("tools/heartbeat.js"); + !getScript("threads/heartbeat.js") && load("threads/heartbeat.js"); - // SoloPlay runs in it's own thread - if (getScript("D2BotSoloPlay.dbj")) { + // SoloPlay runs in it's own thread - check to ensure it exists in the files + if (getScript("D2BotSoloPlay.dbj") && FileTools.exists("libs/SoloPlay/SoloPlay.js")) { load("libs/SoloPlay/SoloPlay.js"); return true; } + // map mode runs in it's own thread if (getScript("d2botmap.dbj")) { - include("manualplay/MapMode.js"); - MapMode.include(); - Config.init(true); - LocalChat.init(); - - // load threads - me.automap = true; load("libs/manualplay/threads/mapthread.js"); - load("libs/manualplay/threads/maphelper.js"); - load("libs/manualplay/threads/maptoolsthread.js"); - Config.ManualPlayPick && load("libs/manualplay/threads/pickthread.js"); - Config.PublicMode && load("tools/party.js"); - - while (true) { - delay(1000); - } + return true; } // MuleLogger handler @@ -71,8 +56,8 @@ function main () { // don't load default for dropper/mules if (getScript("D2BotDropper.dbj") || getScript("D2BotMule.dbj")) { - include("ItemDB.js"); - load("tools/AreaWatcher.js"); + FileTools.exists("libs/ItemDB.js") && include("ItemDB.js"); + load("threads/AreaWatcher.js"); while (true) { delay(1000); @@ -84,6 +69,7 @@ function main () { let startTime = getTickCount(); this.scriptEvent = function (msg) { + if (msg === "quit") return; if (typeof msg === "string" && msg === "soj") { sojPause = true; sojCounter = 0; @@ -149,18 +135,18 @@ function main () { DataFile.updateStats(["experience", "name"]); // Load threads - load("tools/ToolsThread.js"); - (Config.TownCheck || Config.TownHP > 0 || Config.TownMP > 0) && load("tools/TownChicken.js"); + load("threads/ToolsThread.js"); + (Config.TownCheck || Config.TownHP > 0 || Config.TownMP > 0) && load("threads/TownChicken.js"); - if (Config.DebugMode && FileTools.exists("libs/modules/Guard")) { + if (Config.DebugMode && FileTools.exists("libs/modules/Guard.js")) { require("libs/modules/Guard"); } if (Config.PublicMode) { - Config.PublicMode === true ? require("libs/modules/SimpleParty") : load("tools/Party.js"); + Config.PublicMode === true ? require("libs/modules/SimpleParty") : load("threads/Party.js"); } - Config.AntiHostile && load("tools/AntiHostile.js"); + Config.AntiHostile && load("threads/AntiHostile.js"); if (Config.FastPick) { print("ÿc2Fast pickit active."); @@ -183,13 +169,9 @@ function main () { if (Config.DebugMode) { delay(2000); - let script = getScript(); - - if (script) { - do { - console.log(script); - } while (script.getNext()); - } + getThreads() + .sort((a, b) => b.memory - a.memory) + .forEach(thread => console.debug(thread)); } } @@ -199,11 +181,11 @@ function main () { TorchSystem.keyCheck() && scriptBroadcast("torch"); // Auto skill and stat - if (Config.AutoSkill.Enabled && include("common/AutoSkill.js")) { + if (Config.AutoSkill.Enabled && include("core/Auto/AutoSkill.js")) { AutoSkill.init(Config.AutoSkill.Build, Config.AutoSkill.Save); } - if (Config.AutoStat.Enabled && include("common/AutoStat.js")) { + if (Config.AutoStat.Enabled && include("core/Auto/AutoStat.js")) { AutoStat.init(Config.AutoStat.Build, Config.AutoStat.Save, Config.AutoStat.BlockChance, Config.AutoStat.UseBulk); } @@ -259,6 +241,8 @@ function main () { } } + removeEventListener("scriptmsg", this.scriptEvent); + AutoMule.muleCheck() && scriptBroadcast("mule"); CraftingSystem.checkFullSets() && scriptBroadcast("crafting"); TorchSystem.keyCheck() && scriptBroadcast("torch"); @@ -270,6 +254,8 @@ function main () { scriptBroadcast("muleAnni"); } + removeEventListener("copydata", this.copyDataEvent); + scriptBroadcast("quit"); return true; diff --git a/libs/SoloPlay/Functions/AttackOverrides.js b/libs/SoloPlay/Functions/AttackOverrides.js index 8ca7fe94..3a2eb42a 100644 --- a/libs/SoloPlay/Functions/AttackOverrides.js +++ b/libs/SoloPlay/Functions/AttackOverrides.js @@ -6,21 +6,21 @@ * */ -includeIfNotIncluded("common/Attack.js"); +includeIfNotIncluded("core/Attack.js"); Attack.stopClear = false; Attack.init = function () { const CLASSNAME = sdk.player.class.nameOf(me.classid); if (Config.Wereform) { - include("common/Attacks/wereform.js"); - } else if (Config.CustomClassAttack && FileTools.exists("libs/common/Attacks/" + Config.CustomClassAttack + ".js")) { + include("core/Attacks/wereform.js"); + } else if (Config.CustomClassAttack && FileTools.exists("libs/core/Attacks/" + Config.CustomClassAttack + ".js")) { console.log("Loading custom attack file"); - include("common/Attacks/" + Config.CustomClassAttack + ".js"); + include("core/Attacks/" + Config.CustomClassAttack + ".js"); } else { if (!include("SoloPlay/Functions/ClassAttackOverrides/" + CLASSNAME + "Attacks.js")) { console.log(sdk.colors.Red + "Failed to include: " + "SoloPlay/Functions/ClassAttackOverrides/" + CLASSNAME + "Attacks.js"); console.log(sdk.colors.Blue + "Loading default attacks instead"); - include("common/Attacks/" + CLASSNAME + ".js"); + include("core/Attacks/" + CLASSNAME + ".js"); } } @@ -314,7 +314,7 @@ Attack.clearLocations = function (list = []) { return true; }; -Attack.clearPos = function (x = undefined, y = undefined, range = 15, pickit = true) { +Attack.clearPos = function (x, y, range = 15, pickit = true) { while (!me.gameReady) { delay(40); } @@ -379,7 +379,7 @@ Attack.clearPos = function (x = undefined, y = undefined, range = 15, pickit = t let secAttack = me.barbarian ? (isSpecial ? 2 : 4) : 5; if (Config.AttackSkill[secAttack] > -1 && (!Attack.checkResist(target, Config.AttackSkill[isSpecial ? 1 : 3]) - || (me.paladin && Config.AttackSkill[isSpecial ? 1 : 3] === sdk.skills.BlessedHammer && !ClassAttack.getHammerPosition(target)))) { + || (me.paladin && Config.AttackSkill[isSpecial ? 1 : 3] === sdk.skills.BlessedHammer && !ClassAttack.getHammerPosition(target)))) { skillCheck = Config.AttackSkill[secAttack]; } else { skillCheck = Config.AttackSkill[isSpecial ? 1 : 3]; @@ -961,6 +961,10 @@ new Overrides.Override(Attack, Attack.sortMonsters, function(orignal, unitA, uni return orignal(unitA, unitB); }).apply(); +/** + * @param {Monster} unitA + * @param {Monster} unitB + */ Attack.walkingSortMonsters = function (unitA, unitB) { // sort main bosses first if ((unitA.isPrimeEvil) && (unitB.isPrimeEvil)) return getDistance(me, unitA) - getDistance(me, unitB); diff --git a/libs/SoloPlay/Functions/AutoMuleOverrides.js b/libs/SoloPlay/Functions/AutoMuleOverrides.js index a8218bdc..c58d7134 100644 --- a/libs/SoloPlay/Functions/AutoMuleOverrides.js +++ b/libs/SoloPlay/Functions/AutoMuleOverrides.js @@ -10,7 +10,7 @@ * essentially a basic form of item sharing */ -includeIfNotIncluded("Automule.js"); +includeIfNotIncluded("systems/automule/Automule.js"); AutoMule.getMuleItems = function () { let info = this.getInfo(); diff --git a/libs/SoloPlay/Functions/AutoStatOverrides.js b/libs/SoloPlay/Functions/AutoStatOverrides.js index 6b21ac43..dc180b0d 100644 --- a/libs/SoloPlay/Functions/AutoStatOverrides.js +++ b/libs/SoloPlay/Functions/AutoStatOverrides.js @@ -6,7 +6,7 @@ * */ -includeIfNotIncluded("common/AutoStat.js"); +includeIfNotIncluded("core/Auto/AutoStat.js"); AutoStat.init = function (statBuildOrder, save = 0, block = 0, bulkStat = true) { AutoStat.statBuildOrder = statBuildOrder; diff --git a/libs/SoloPlay/Functions/ClassAttackOverrides/AmazonAttacks-WIP.js b/libs/SoloPlay/Functions/ClassAttackOverrides/AmazonAttacks-WIP.js index 9572be23..1aa78253 100644 --- a/libs/SoloPlay/Functions/ClassAttackOverrides/AmazonAttacks-WIP.js +++ b/libs/SoloPlay/Functions/ClassAttackOverrides/AmazonAttacks-WIP.js @@ -7,7 +7,7 @@ // TODO: clean up this whole file -includeIfNotIncluded("common/Attacks/Amazon.js"); +includeIfNotIncluded("core/Attacks/Amazon.js"); ClassAttack.decoyTick = getTickCount(); diff --git a/libs/SoloPlay/Functions/ClassAttackOverrides/AmazonAttacks.js b/libs/SoloPlay/Functions/ClassAttackOverrides/AmazonAttacks.js index 31be2cf4..e8bf20b2 100644 --- a/libs/SoloPlay/Functions/ClassAttackOverrides/AmazonAttacks.js +++ b/libs/SoloPlay/Functions/ClassAttackOverrides/AmazonAttacks.js @@ -11,7 +11,7 @@ * - test early on using a bow on switch for ranged attacks (might be worth a point in magic arrow) */ -includeIfNotIncluded("common/Attacks/Amazon.js"); +includeIfNotIncluded("core/Attacks/Amazon.js"); ClassAttack.decoyTick = getTickCount(); diff --git a/libs/SoloPlay/Functions/ClassAttackOverrides/AssassinAttacks.js b/libs/SoloPlay/Functions/ClassAttackOverrides/AssassinAttacks.js index 48f3d16f..5370393e 100644 --- a/libs/SoloPlay/Functions/ClassAttackOverrides/AssassinAttacks.js +++ b/libs/SoloPlay/Functions/ClassAttackOverrides/AssassinAttacks.js @@ -10,7 +10,7 @@ * Test utilizing marital art skills if we have them */ -includeIfNotIncluded("common/Attacks/Assassin.js"); +includeIfNotIncluded("core/Attacks/Assassin.js"); ClassAttack.mindBlast = function (unit) { if (!unit || !Skill.canUse(sdk.skills.MindBlast)) return; diff --git a/libs/SoloPlay/Functions/ClassAttackOverrides/BarbarianAttacks.js b/libs/SoloPlay/Functions/ClassAttackOverrides/BarbarianAttacks.js index f59f796c..eadea985 100644 --- a/libs/SoloPlay/Functions/ClassAttackOverrides/BarbarianAttacks.js +++ b/libs/SoloPlay/Functions/ClassAttackOverrides/BarbarianAttacks.js @@ -5,7 +5,7 @@ * */ -includeIfNotIncluded("common/Attacks/Barbarian.js"); +includeIfNotIncluded("core/Attacks/Barbarian.js"); /** * @todo: diff --git a/libs/SoloPlay/Functions/ClassAttackOverrides/DruidAttacks.js b/libs/SoloPlay/Functions/ClassAttackOverrides/DruidAttacks.js index b65d68fc..80fc14ea 100644 --- a/libs/SoloPlay/Functions/ClassAttackOverrides/DruidAttacks.js +++ b/libs/SoloPlay/Functions/ClassAttackOverrides/DruidAttacks.js @@ -10,7 +10,7 @@ * Test traveling in wolf form/ utilizing wereform if we have it and need to perform normal attack */ -includeIfNotIncluded("common/Attacks/Druid.js"); +includeIfNotIncluded("core/Attacks/Druid.js"); ClassAttack.doAttack = function (unit, preattack) { if (!unit) return Attack.Result.SUCCESS; diff --git a/libs/SoloPlay/Functions/ClassAttackOverrides/NecromancerAttacks.js b/libs/SoloPlay/Functions/ClassAttackOverrides/NecromancerAttacks.js index 33af666f..a3414dfb 100644 --- a/libs/SoloPlay/Functions/ClassAttackOverrides/NecromancerAttacks.js +++ b/libs/SoloPlay/Functions/ClassAttackOverrides/NecromancerAttacks.js @@ -5,7 +5,7 @@ * */ -includeIfNotIncluded("common/Attacks/Necromancer.js"); +includeIfNotIncluded("core/Attacks/Necromancer.js"); ClassAttack.curseIndex = []; // @todo refactor this diff --git a/libs/SoloPlay/Functions/ClassAttackOverrides/PaladinAttacks.js b/libs/SoloPlay/Functions/ClassAttackOverrides/PaladinAttacks.js index 9d5dd562..d8160945 100644 --- a/libs/SoloPlay/Functions/ClassAttackOverrides/PaladinAttacks.js +++ b/libs/SoloPlay/Functions/ClassAttackOverrides/PaladinAttacks.js @@ -5,7 +5,7 @@ * */ -includeIfNotIncluded("common/Attacks/Paladin.js"); +includeIfNotIncluded("core/Attacks/Paladin.js"); // eslint-disable-next-line no-unused-vars ClassAttack.doAttack = function (unit = undefined, preattack = false, once = false) { diff --git a/libs/SoloPlay/Functions/ClassAttackOverrides/SorceressAttacks.js b/libs/SoloPlay/Functions/ClassAttackOverrides/SorceressAttacks.js index c31e7895..3c13eb9e 100644 --- a/libs/SoloPlay/Functions/ClassAttackOverrides/SorceressAttacks.js +++ b/libs/SoloPlay/Functions/ClassAttackOverrides/SorceressAttacks.js @@ -5,15 +5,15 @@ * */ -includeIfNotIncluded("common/Attacks/Sorceress.js"); +includeIfNotIncluded("core/Attacks/Sorceress.js"); (function() { const slowable = function (unit, freezeable = false) { return (!!unit && unit.attackable // those that we can attack - && Attack.checkResist(unit, "cold") - // those that are not frozen yet and those that can be frozen or not yet chilled - && (freezeable ? !unit.isFrozen && !unit.getStat(sdk.stats.CannotbeFrozen) : !unit.isChilled) - && ![sdk.monsters.Andariel, sdk.monsters.Lord5].includes(unit.classid)); + && Attack.checkResist(unit, "cold") + // those that are not frozen yet and those that can be frozen or not yet chilled + && (freezeable ? !unit.isFrozen && !unit.getStat(sdk.stats.CannotbeFrozen) : !unit.isChilled) + && ![sdk.monsters.Andariel, sdk.monsters.Lord5].includes(unit.classid)); }; const frostNovaCheck = function () { @@ -39,24 +39,24 @@ includeIfNotIncluded("common/Attacks/Sorceress.js"); }; /** - * @typedef {Object} dataObj - * @property {number} skill - * @property {number} reqLvl - * @property {number} range - * @property {boolean} have - * @property {number} mana - * @property {boolean} timed - * @property {number} dmg - * @property {Function} assignValues - * @property {Function} calcDmg - */ + * @typedef {Object} dataObj + * @property {number} skill + * @property {number} reqLvl + * @property {number} range + * @property {boolean} have + * @property {number} mana + * @property {boolean} timed + * @property {number} dmg + * @property {Function} assignValues + * @property {Function} calcDmg + */ /** - * @param {number} skillId - * @param {number} [reqLvl] - * @param {number} [range] - * @returns {dataObj} - */ + * @param {number} skillId + * @param {number} [reqLvl] + * @param {number} [range] + * @returns {dataObj} + */ const buildDataObj = (skillId = -1, reqLvl = 1, range = 0) => ({ have: false, skill: skillId, range: range ? range : Infinity, mana: Infinity, timed: false, reqLvl: reqLvl, dmg: 0, assignValues: function (range) { @@ -73,15 +73,20 @@ includeIfNotIncluded("common/Attacks/Sorceress.js"); }); /** - * @param {dataObj} main - * @param {dataObj} check - * @returns {boolean} - */ + * @param {dataObj} main + * @param {dataObj} check + * @returns {boolean} + */ const compareDamage = (main, check) => { if (main.skill === check.skill) return false; return check.dmg > main.dmg; }; + /** + * @param {Monster} unit + * @param {boolean} force + * @todo keep track of when, what, and who we last casted on to prevent spamming charged skills in a short period of time + */ ClassAttack.switchCurse = function (unit, force) { if (CharData.skillData.haveChargedSkill([sdk.skills.SlowMissiles, sdk.skills.LowerResist, sdk.skills.Weaken]) && unit.curseable) { const gold = me.gold; @@ -93,34 +98,34 @@ includeIfNotIncluded("common/Attacks/Sorceress.js"); // If we have slow missles we might as well use it, currently only on Lighting Enchanted mobs as they are dangerous // Might be worth it to use on souls too TODO: test this idea if (CharData.skillData.haveChargedSkill(sdk.skills.SlowMissiles) && gold > 500000 && !isBoss - && unit.getEnchant(sdk.enchant.LightningEnchanted) && !unit.getState(sdk.states.SlowMissiles) - && !checkCollision(me, unit, sdk.collision.Ranged)) { - // Cast slow missiles + && unit.getEnchant(sdk.enchant.LightningEnchanted) && !unit.getState(sdk.states.SlowMissiles) + && !checkCollision(me, unit, sdk.collision.Ranged)) { + // Cast slow missiles Attack.castCharges(sdk.skills.SlowMissiles, unit); } // Handle Switch casting if (CharData.skillData.haveChargedSkillOnSwitch(sdk.skills.LowerResist) - && (gold > 500000 || isBoss || dangerZone) - && !unit.getState(sdk.states.LowerResist) - && !checkCollision(me, unit, sdk.collision.Ranged)) { - // Switch cast lower resist + && (gold > 500000 || isBoss || dangerZone) + && !unit.getState(sdk.states.LowerResist) + && !checkCollision(me, unit, sdk.collision.Ranged)) { + // Switch cast lower resist Attack.switchCastCharges(sdk.skills.LowerResist, unit); } if (CharData.skillData.haveChargedSkillOnSwitch(sdk.skills.Weaken) - && (gold > 500000 || isBoss || dangerZone) - && !unit.getState(sdk.states.Weaken) && !unit.getState(sdk.states.LowerResist) - && !checkCollision(me, unit, sdk.collision.Ranged)) { - // Switch cast weaken + && (gold > 500000 || isBoss || dangerZone) + && !unit.getState(sdk.states.Weaken) && !unit.getState(sdk.states.LowerResist) + && !checkCollision(me, unit, sdk.collision.Ranged)) { + // Switch cast weaken Attack.switchCastCharges(sdk.skills.Weaken, unit); } } }; /** - * @param {Unit} unit - * @returns {dataObj} - */ + * @param {Unit} unit + * @returns {dataObj} + */ ClassAttack.decideDistanceSkill = function (unit) { const data = {}; const currLvl = me.charlvl; @@ -137,7 +142,7 @@ includeIfNotIncluded("common/Attacks/Sorceress.js"); let skillCheck = Object.keys(data) .filter(k => typeof data[k] === "object" && data[k].have && me.mp > data[k].mana - && (!data[k].timed || !me.skillDelay)) + && (!data[k].timed || !me.skillDelay)) .sort((a, b) => data[b].dmg - data[a].dmg).first(); return typeof data[skillCheck] === "object" ? data[skillCheck] : buildDataObj(-1); }; @@ -157,7 +162,7 @@ includeIfNotIncluded("common/Attacks/Sorceress.js"); console.debug("mercwatch"); if (Town.visitTown()) { - // lost reference to the mob we were attacking + // lost reference to the mob we were attacking if (!unit || !copyUnit(unit).x || !Game.getMonster(-1, -1, gid) || unit.dead) { console.debug("Lost reference to unit"); return Attack.Result.SUCCESS; @@ -339,10 +344,10 @@ includeIfNotIncluded("common/Attacks/Sorceress.js"); }; if (CharData.skillData.bowData.bowOnSwitch - && (index !== 1 || !unit.name.includes(getLocaleString(sdk.locale.text.Ghostly))) - && ([-1, sdk.skills.Attack].includes(timedSkill.skill) - || timedSkill.mana > me.mp - || (timedSkill.mana * 3 > me.mp && [sdk.skills.FireBolt, sdk.skills.ChargedBolt].includes(timedSkill.skill)))) { + && (index !== 1 || !unit.name.includes(getLocaleString(sdk.locale.text.Ghostly))) + && ([-1, sdk.skills.Attack].includes(timedSkill.skill) + || timedSkill.mana > me.mp + || (timedSkill.mana * 3 > me.mp && [sdk.skills.FireBolt, sdk.skills.ChargedBolt].includes(timedSkill.skill)))) { if (switchBowAttack(unit) === Attack.Result.SUCCESS) return Attack.Result.SUCCESS; } @@ -355,8 +360,8 @@ includeIfNotIncluded("common/Attacks/Sorceress.js"); return Attack.Result.SUCCESS; case Attack.Result.CANTATTACK: // Try to telestomp if (Pather.canTeleport() && Attack.checkResist(unit, "physical") && !!me.getMerc() - && Attack.validSpot(unit.x, unit.y) - && (Config.TeleStomp || (!me.hell && (unit.getMobCount(10) < me.maxNearMonsters && unit.isSpecial)))) { + && Attack.validSpot(unit.x, unit.y) + && (Config.TeleStomp || (!me.hell && (unit.getMobCount(10) < me.maxNearMonsters && unit.isSpecial)))) { let merc = me.getMerc(); let haveTK = Skill.canUse(sdk.skills.Telekinesis); let mercRevive = 0; @@ -419,8 +424,11 @@ includeIfNotIncluded("common/Attacks/Sorceress.js"); Developer.debugging.skills && choosenSkill.have && console.log(sdk.colors.Yellow + "(Selected Main :: " + getSkillById(skill) + ") DMG: " + choosenSkill.dmg); if (![sdk.skills.FrostNova, sdk.skills.Nova, sdk.skills.StaticField].includes(skill)) { + // need like a potential danger check, sometimes while me might not be immeadiate danger because there aren't a whole + // lot of monsters around, we can suddenly be in danger if a ranged monsters hits us or if one of the monsters near us + // does a lot of damage quickly if (Skill.canUse(sdk.skills.Teleport) && me.mp > Skill.getManaCost(sdk.skills.Teleport) + mana && me.inDanger()) { - //console.log("FINDING NEW SPOT"); + //console.log("FINDING NEW SPOT"); Attack.getIntoPosition(unit, range, 0 | Coords_1.BlockBits.LineOfSight | Coords_1.BlockBits.Ranged diff --git a/libs/SoloPlay/Functions/ConfigOverrides.js b/libs/SoloPlay/Functions/ConfigOverrides.js index 777d24e7..17c41117 100644 --- a/libs/SoloPlay/Functions/ConfigOverrides.js +++ b/libs/SoloPlay/Functions/ConfigOverrides.js @@ -5,7 +5,7 @@ * */ -includeIfNotIncluded("common/Config.js"); +includeIfNotIncluded("core/Config.js"); Config.init = function (notify) { const formats = ((className, profile, charname, realm) => ({ diff --git a/libs/SoloPlay/Functions/CubingOverrides.js b/libs/SoloPlay/Functions/CubingOverrides.js index 0dd8e910..78e2bf91 100644 --- a/libs/SoloPlay/Functions/CubingOverrides.js +++ b/libs/SoloPlay/Functions/CubingOverrides.js @@ -5,7 +5,7 @@ * */ -includeIfNotIncluded("common/Cubing.js"); +includeIfNotIncluded("core/Cubing.js"); Recipe.Reroll.Charm = 56; Recipe.Socket.LowMagic = 57; @@ -580,13 +580,14 @@ Cubing.emptyCube = function () { return true; }; - +/** @param {ItemUnit} unit */ Cubing.checkItem = function (unit) { if (!Config.Cubing || !unit) return false; for (let i = 0; i < this.validIngredients.length; i++) { // not the same item but the same type of item - if (unit.mode !== sdk.items.mode.Equipped && unit.gid !== this.validIngredients[i].gid && unit.classid === this.validIngredients[i].classid && unit.quality === this.validIngredients[i].quality) { + if (unit.mode !== sdk.items.mode.Equipped && unit.gid !== this.validIngredients[i].gid + && unit.classid === this.validIngredients[i].classid && unit.quality === this.validIngredients[i].quality) { // item is better than the one we currently have, so add it to validIngredient array and remove old item if (unit.ilvl > this.validIngredients[i].ilvl && this.validItem(unit, this.validIngredients[i].recipe)) { this.validIngredients.push({classid: unit.classid, quality: unit.quality, ilvl: unit.ilvl, gid: unit.gid, recipe: this.validIngredients[i].recipe}); @@ -843,18 +844,18 @@ Cubing.doCubing = function () { if (Storage.Inventory.CanFit(items[j])) { Storage.Inventory.MoveTo(items[j]); } else { - Misc.itemLogger("Dropped", items[j], "doCubing"); + Item.logger("Dropped", items[j], "doCubing"); items[j].drop(); } } - Developer.debugging.crafting && Misc.logItem("Crafted but didn't want", items[j]); + Developer.debugging.crafting && Item.logItem("Crafted but didn't want", items[j]); break; case Pickit.Result.WANTED: case Pickit.Result.SOLOWANTS: - Misc.itemLogger("Cubing Kept", items[j]); - Misc.logItem("Cubing Kept", items[j], result.line); + Item.logger("Cubing Kept", items[j]); + Item.logItem("Cubing Kept", items[j], result.line); break; case Pickit.Result.CRAFTING: // Crafting System diff --git a/libs/SoloPlay/Functions/DynamicTiers.js b/libs/SoloPlay/Functions/DynamicTiers.js index 2abaf2aa..8e6e52d7 100644 --- a/libs/SoloPlay/Functions/DynamicTiers.js +++ b/libs/SoloPlay/Functions/DynamicTiers.js @@ -447,15 +447,20 @@ // Melee Specific if (!buildInfo.caster - || Config.AttackSkill.includes(sdk.skills.Attack) - || Config.LowManaSkill.includes(sdk.skills.Attack) - || ([sdk.items.type.Bow, sdk.items.type.AmazonBow, sdk.items.type.Crossbow].includes(item.itemType) && CharData.skillData.bowData.bowOnSwitch)) { + || Config.AttackSkill.includes(sdk.skills.Attack) + || Config.LowManaSkill.includes(sdk.skills.Attack) + || ([sdk.items.type.Bow, sdk.items.type.AmazonBow, sdk.items.type.Crossbow].includes(item.itemType) && CharData.skillData.bowData.bowOnSwitch)) { let meleeRating = 0; let eleDmgModifer = [sdk.items.type.Ring, sdk.items.type.Amulet].includes(item.itemType) ? 2 : 1; item.getStatEx(sdk.stats.ReplenishDurability) && (meleeRating += 15); item.getStatEx(sdk.stats.IgnoreTargetDefense) && (meleeRating += 50); + // dirty fix maybe? + if (me.barbarian && SetUp.currentBuild !== "Immortalwhirl" && item.strictlyTwoHanded) { + return 0; + } + // should these be added and calc avg dmg instead? // Sometimes we replace good weps with 2-300 ED weps that may be high dmg but aren't as good as the item we replaced //buildRating += item.getStatEx(sdk.stats.MinDamage) * buildWeights.MINDMG; // add MIN damage diff --git a/libs/SoloPlay/Functions/Globals.js b/libs/SoloPlay/Functions/Globals.js index 4c0fae16..cc9904be 100644 --- a/libs/SoloPlay/Functions/Globals.js +++ b/libs/SoloPlay/Functions/Globals.js @@ -10,11 +10,14 @@ * @todo * - split up this file into appropriate sections */ -includeIfNotIncluded("OOG.js"); + +// all we really need from oog is D2Bot +includeIfNotIncluded("oog/D2Bot.js"); includeIfNotIncluded("SoloPlay/Tools/Developer.js"); includeIfNotIncluded("SoloPlay/Tools/CharData.js"); includeIfNotIncluded("SoloPlay/Functions/PrototypeOverrides.js"); +// not every thread needs these /** @global */ const Overrides = require("../../modules/Override"); /** @global */ @@ -29,6 +32,10 @@ const AreaData = require("../Modules/AreaData"); const MYCLASSNAME = sdk.player.class.nameOf(me.classid).toLowerCase(); includeIfNotIncluded("SoloPlay/BuildFiles/" + MYCLASSNAME + "/" + MYCLASSNAME + ".js"); +/** + * @global + * @type {charData} + */ let myData = CharData.getStats(); Unit.prototype.__defineGetter__("mercid", function () { @@ -55,7 +62,7 @@ function myPrint (str = "", toConsole = false, color = 0) { } function updateMyData () { - let obj = JSON.stringify(Misc.copy(myData)); + let obj = JSON.stringify(copyObj(myData)); let myThread = getScript(true).name; CharData.threads.forEach(function (script) { let curr = getScript(script); @@ -88,7 +95,7 @@ const SetUp = { CharData.updateData("me", myData); } - let temp = Misc.copy(myData); + let temp = copyObj(myData); if (myData.me.currentBuild !== CharInfo.getActiveBuild()) { myData.me.currentBuild = CharInfo.getActiveBuild(); diff --git a/libs/SoloPlay/Functions/ItemOverrides.js b/libs/SoloPlay/Functions/ItemOverrides.js index e7c7d8d1..a37408da 100644 --- a/libs/SoloPlay/Functions/ItemOverrides.js +++ b/libs/SoloPlay/Functions/ItemOverrides.js @@ -6,9 +6,7 @@ * */ -includeIfNotIncluded("common/Misc.js"); -includeIfNotIncluded("common/Item.js"); -includeIfNotIncluded("SoloPlay/Functions/PrototypeOverrides.js"); +includeIfNotIncluded("core/Item.js"); includeIfNotIncluded("SoloPlay/Functions/ItemPrototypes.js"); Item.weaponTypes = [ @@ -248,7 +246,7 @@ Item.autoEquip = function (task = "") { SoloWants.addToList(item); SoloWants.ensureList(); } - Developer.debugging.autoEquip && Misc.logItem(task, me.getItem(-1, -1, gid)); + Developer.debugging.autoEquip && Item.logItem(task, me.getItem(-1, -1, gid)); Developer.logEquipped && MuleLogger.logEquippedItems(); } else if (!noStash && item.lvlreq > me.charlvl && !item.isInStash) { if (Storage.Stash.CanFit(item)) { @@ -575,7 +573,7 @@ Item.autoEquipSecondary = function (task = "") { if (this.secondaryEquip(item, bodyLoc[j])) { console.log("ÿc9SecondaryEquipÿc0 :: Equipped: " + prettyName + " SecondaryTier: " + tier); - Developer.debugging.autoEquip && Misc.logItem("Equipped switch", me.getItem(-1, -1, gid)); + Developer.debugging.autoEquip && Item.logItem("Equipped switch", me.getItem(-1, -1, gid)); Developer.logEquipped && MuleLogger.logEquippedItems(); } @@ -626,7 +624,7 @@ Item.equipMerc = function (item, bodyLoc) { if (item.toCursor()) { if (clickItem(sdk.clicktypes.click.item.Mercenary, bodyLoc)) { delay(500 + me.ping * 2); - Developer.debugging.autoEquip && Misc.logItem("Merc Equipped", mercenary.getItem(item.classid)); + Developer.debugging.autoEquip && Item.logItem("Merc Equipped", mercenary.getItem(item.classid)); } let check = mercenary.getItem(item.classid); @@ -781,7 +779,7 @@ Item.autoEquipMerc = function () { if (cursorItem) { cursorItem.drop(); - Developer.debugging.autoEquip && Misc.logItem("Merc Dropped", cursorItem); + Developer.debugging.autoEquip && Item.logItem("Merc Dropped", cursorItem); } break; @@ -849,11 +847,11 @@ Item.removeItemsMerc = function () { if (!currCharm || [Pickit.Result.UNWANTED, Pickit.Result.TRASH].includes(Pickit.checkItem(currCharm).result)) continue; if (!currCharm.isInStash && !myData.me.charmGids.includes(currCharm.gid)) { if (!Storage.Stash.MoveTo(currCharm)) { - verbose && Misc.itemLogger("Dropped", currCharm); + verbose && Item.logger("Dropped", currCharm); currCharm.drop(); } else { if (verbose) { - Cubing.checkItem(currCharm) ? Misc.logItem("Stashed Cubing Ingredient", currCharm) : Misc.logItem("Stashed", currCharm); + Cubing.checkItem(currCharm) ? Item.logItem("Stashed Cubing Ingredient", currCharm) : Item.logItem("Stashed", currCharm); } } } @@ -1302,8 +1300,8 @@ Item.removeItemsMerc = function () { if (getUIFlag(sdk.uiflags.Shop) || (Config.PacketShopping && getInteractedNPC() && getInteractedNPC().itemcount > 0)) { for (let i = 0; i < totalSell.length; i++) { console.log("ÿc8Kolbot-SoloPlayÿc0: Sell old charm " + totalSell[i].name); - verbose && Misc.itemLogger("Sold", totalSell[i]); - verbose && Misc.logItem("CharmEquip Sold", totalSell[i]); + verbose && Item.logger("Sold", totalSell[i]); + verbose && Item.logItem("CharmEquip Sold", totalSell[i]); totalSell[i].sell(); } } @@ -1314,7 +1312,7 @@ Item.removeItemsMerc = function () { if (totalKeep[i].isInStash && !Cubing.checkItem(totalKeep[i])) { !getUIFlag(sdk.uiflags.Stash) && Town.openStash() && delay(300 + me.ping); if (Storage.Inventory.CanFit(totalKeep[i]) && Storage.Inventory.MoveTo(totalKeep[i])) { - verbose && Misc.logItem("CharmEquip Equipped", totalKeep[i]); + verbose && Item.logItem("CharmEquip Equipped", totalKeep[i]); } } } @@ -1479,6 +1477,89 @@ Item.removeItemsMerc = function () { }; })(); +// Log kept item stats in the manager. +Item.logItem = function (action, unit, keptLine, force) { + if (!this.useItemLog || unit === undefined || !unit || !unit.fname) return false; + if (!Config.LogKeys && ["pk1", "pk2", "pk3"].includes(unit.code)) return false; + if (!Config.LogOrgans && ["dhn", "bey", "mbr"].includes(unit.code)) return false; + if (!Config.LogLowRunes && ["r01", "r02", "r03", "r04", "r05", "r06", "r07", "r08", "r09", "r10", "r11", "r12", "r13", "r14"].includes(unit.code)) return false; + if (!Config.LogMiddleRunes && ["r15", "r16", "r17", "r18", "r19", "r20", "r21", "r22", "r23"].includes(unit.code)) return false; + if (!Config.LogHighRunes && ["r24", "r25", "r26", "r27", "r28", "r29", "r30", "r31", "r32", "r33"].includes(unit.code)) return false; + if (!Config.LogLowGems && ["gcv", "gcy", "gcb", "gcg", "gcr", "gcw", "skc", "gfv", "gfy", "gfb", "gfg", "gfr", "gfw", "skf", "gsv", "gsy", "gsb", "gsg", "gsr", "gsw", "sku"].includes(unit.code)) return false; + if (!Config.LogHighGems && ["gzv", "gly", "glb", "glg", "glr", "glw", "skl", "gpv", "gpy", "gpb", "gpg", "gpr", "gpw", "skz"].includes(unit.code)) return false; + + for (let i = 0; i < Config.SkipLogging.length; i++) { + if (Config.SkipLogging[i] === unit.classid || Config.SkipLogging[i] === unit.code) return false; + } + + let lastArea; + let name = unit.fname.split("\n").reverse().join(" ").replace(/ÿc[0-9!"+<:;.*]|\/|\\/g, "").trim(); + let desc = (this.getItemDesc(unit) || ""); + let color = (unit.getColor() || -1); + + if (action.match("kept", "i")) { + lastArea = DataFile.getStats().lastArea; + lastArea && (desc += ("\n\\xffc0Area: " + lastArea)); + } + + const mercCheck = action.match("Merc"); + const hasTier = AutoEquip.hasTier(unit); + const charmCheck = (unit.isCharm && Item.autoEquipCharmCheck(unit)); + const nTResult = NTIP.CheckItem(unit, NTIP_CheckListNoTier) === 1; + + if (!action.match("kept", "i") && !action.match("Shopped") && hasTier) { + if (!mercCheck) { + NTIP.GetCharmTier(unit) > 0 && (desc += ("\n\\xffc0Autoequip charm tier: " + NTIP.GetCharmTier(unit))); + NTIP.GetTier(unit) > 0 && (desc += ("\n\\xffc0Autoequip char tier: " + NTIP.GetTier(unit))); + } else { + desc += ("\n\\xffc0Autoequip merc tier: " + NTIP.GetMercTier(unit)); + } + } + + // should stop logging items unless we wish to see them or it's part of normal pickit + if (!nTResult && !force) { + switch (true) { + case (unit.questItem || unit.isBaseType): + case (!unit.isCharm && hasTier && !Developer.debugging.autoEquip): + case (charmCheck && !Developer.debugging.smallCharm && unit.classid === sdk.items.SmallCharm): + case (charmCheck && !Developer.debugging.largeCharm && unit.classid === sdk.items.LargeCharm): + case (charmCheck && !Developer.debugging.grandCharm && unit.classid === sdk.items.GrandCharm): + return true; + default: + break; + } + } + + let code = this.getItemCode(unit); + let sock = unit.getItem(); + + if (sock) { + do { + if (sock.itemType === sdk.items.type.Jewel) { + desc += "\n\n"; + desc += this.getItemDesc(sock); + } + } while (sock.getNext()); + } + + keptLine && (desc += ("\n\\xffc0Line: " + keptLine)); + desc += "$" + (unit.getFlag(sdk.items.flags.Ethereal) ? ":eth" : ""); + + let itemObj = { + title: action + " " + name, + description: desc, + image: code, + textColor: unit.quality, + itemColor: color, + header: "", + sockets: this.getItemSockets(unit) + }; + + D2Bot.printToItemLog(itemObj); + + return true; +}; + const AutoEquip = { /** * @param {ItemUnit} item diff --git a/libs/SoloPlay/Functions/ItemPrototypes.js b/libs/SoloPlay/Functions/ItemPrototypes.js index eb8d10a5..c850d2df 100644 --- a/libs/SoloPlay/Functions/ItemPrototypes.js +++ b/libs/SoloPlay/Functions/ItemPrototypes.js @@ -336,7 +336,7 @@ Unit.prototype.equipMerc = function (bodyLoc = -1) { !!cursorItem && !this.shouldKeep() && this.drop(); } - Developer.debugging.autoEquip && Misc.logItem("Merc Equipped", me.getMerc().getItem(this.classid)); + Developer.debugging.autoEquip && Item.logItem("Merc Equipped", me.getMerc().getItem(this.classid)); Developer.logEquipped && MuleLogger.logEquippedItems(); return true; diff --git a/libs/SoloPlay/Functions/ItemUtilities.js b/libs/SoloPlay/Functions/ItemUtilities.js index 2b427208..ceac0519 100644 --- a/libs/SoloPlay/Functions/ItemUtilities.js +++ b/libs/SoloPlay/Functions/ItemUtilities.js @@ -5,7 +5,7 @@ * */ -includeIfNotIncluded("common/Item.js"); +includeIfNotIncluded("core/Item.js"); (function() { // TODO: clean this up (sigh) - 8/10/22 - update refactored alot, still think more can be done though const baseSkillsScore = (item, buildInfo) => { @@ -279,6 +279,10 @@ includeIfNotIncluded("common/Item.js"); return result; }; + /** + * @param {ItemUnit} base + * @param {boolean} verbose + */ Item.betterThanStashed = function (base, verbose) { if (!base || base.quality > sdk.items.quality.Superior || base.isRuneword) return false; if (base.sockets === 0 && getBaseStat("items", base.classid, "gemsockets") <= 1) return false; @@ -325,18 +329,26 @@ includeIfNotIncluded("common/Item.js"); return me.getItemsEx(-1, sdk.items.mode.inStorage) .filter(item => itemtypes.includes(item.itemType) - && ((item.sockets === base.sockets) || (item.sockets > base.sockets)) - && (eth === null || item.ethereal === eth)) + && ((item.sockets === base.sockets) || (item.sockets > base.sockets)) + && (eth === null || item.ethereal === eth)) .sort(sort) .last(); } + /** + * @param {ItemUnit} a + * @param {ItemUnit} b + */ const defenseSort = (a, b) => { let [aDef, bDef] = [a.getStatEx(sdk.stats.Defense), b.getStatEx(sdk.stats.Defense)]; if (aDef !== bDef) return aDef - bDef; return a.getStatEx(sdk.stats.ArmorPercent) - b.getStatEx(sdk.stats.ArmorPercent); }; + /** + * @param {ItemUnit} a + * @param {ItemUnit} b + */ const generalScoreSort = (a, b) => { let [aScore, bScore] = [generalScore(a), generalScore(b)]; if (aScore !== bScore) return aScore - bScore; @@ -350,6 +362,10 @@ includeIfNotIncluded("common/Item.js"); return a.sockets - b.sockets; }; + /** + * @param {ItemUnit} a + * @param {ItemUnit} b + */ const twoHandDmgSort = (a, b) => { let [aDmg, bDmg] = [dmgScore(a), dmgScore(b)]; if (aDmg.twoHandDmg !== bDmg.twoHandDmg) return aDmg.twoHandDmg - bDmg.twoHandDmg; diff --git a/libs/SoloPlay/Functions/LoaderOverrides.js b/libs/SoloPlay/Functions/LoaderOverrides.js index 40ac85b4..c212c9c4 100644 --- a/libs/SoloPlay/Functions/LoaderOverrides.js +++ b/libs/SoloPlay/Functions/LoaderOverrides.js @@ -6,7 +6,7 @@ * */ -includeIfNotIncluded("common/Loader.js"); +includeIfNotIncluded("core/Loader.js"); Loader.getScripts = function () { let fileList = dopen("libs/SoloPlay/Scripts").getFiles(); diff --git a/libs/SoloPlay/Functions/Me.js b/libs/SoloPlay/Functions/Me.js index 09c30d1f..5ba6220d 100644 --- a/libs/SoloPlay/Functions/Me.js +++ b/libs/SoloPlay/Functions/Me.js @@ -5,7 +5,7 @@ * */ -includeIfNotIncluded("common/Prototypes.js"); +includeIfNotIncluded("core/Prototypes.js"); /** * @description me prototypes for soloplay with checks to ensure forwards compatibility diff --git a/libs/SoloPlay/Functions/MiscOverrides.js b/libs/SoloPlay/Functions/MiscOverrides.js index 54bcfa93..1dc0b40b 100644 --- a/libs/SoloPlay/Functions/MiscOverrides.js +++ b/libs/SoloPlay/Functions/MiscOverrides.js @@ -6,7 +6,7 @@ * */ -includeIfNotIncluded("common/Misc.js"); +includeIfNotIncluded("core/Misc.js"); Misc.townEnabled = true; @@ -520,7 +520,7 @@ Misc.addSocketablesToItem = function (item, runes = []) { if (item.getItemsEx().length > preSockets) { D2Bot.printToConsole("Added socketable: " + rune.fname + " to " + item.fname, sdk.colors.D2Bot.Gold); - Misc.logItem("Added " + rune.name + " to: ", item, null, true); + Item.logItem("Added " + rune.name + " to: ", item, null, true); preSockets++; } } @@ -709,89 +709,6 @@ Misc.checkSocketables = function () { } }; -// Log kept item stats in the manager. -Misc.logItem = function (action, unit, keptLine, force) { - if (!this.useItemLog || unit === undefined || !unit || !unit.fname) return false; - if (!Config.LogKeys && ["pk1", "pk2", "pk3"].includes(unit.code)) return false; - if (!Config.LogOrgans && ["dhn", "bey", "mbr"].includes(unit.code)) return false; - if (!Config.LogLowRunes && ["r01", "r02", "r03", "r04", "r05", "r06", "r07", "r08", "r09", "r10", "r11", "r12", "r13", "r14"].includes(unit.code)) return false; - if (!Config.LogMiddleRunes && ["r15", "r16", "r17", "r18", "r19", "r20", "r21", "r22", "r23"].includes(unit.code)) return false; - if (!Config.LogHighRunes && ["r24", "r25", "r26", "r27", "r28", "r29", "r30", "r31", "r32", "r33"].includes(unit.code)) return false; - if (!Config.LogLowGems && ["gcv", "gcy", "gcb", "gcg", "gcr", "gcw", "skc", "gfv", "gfy", "gfb", "gfg", "gfr", "gfw", "skf", "gsv", "gsy", "gsb", "gsg", "gsr", "gsw", "sku"].includes(unit.code)) return false; - if (!Config.LogHighGems && ["gzv", "gly", "glb", "glg", "glr", "glw", "skl", "gpv", "gpy", "gpb", "gpg", "gpr", "gpw", "skz"].includes(unit.code)) return false; - - for (let i = 0; i < Config.SkipLogging.length; i++) { - if (Config.SkipLogging[i] === unit.classid || Config.SkipLogging[i] === unit.code) return false; - } - - let lastArea; - let name = unit.fname.split("\n").reverse().join(" ").replace(/ÿc[0-9!"+<:;.*]|\/|\\/g, "").trim(); - let desc = (this.getItemDesc(unit) || ""); - let color = (unit.getColor() || -1); - - if (action.match("kept", "i")) { - lastArea = DataFile.getStats().lastArea; - lastArea && (desc += ("\n\\xffc0Area: " + lastArea)); - } - - const mercCheck = action.match("Merc"); - const hasTier = AutoEquip.hasTier(unit); - const charmCheck = (unit.isCharm && Item.autoEquipCharmCheck(unit)); - const nTResult = NTIP.CheckItem(unit, NTIP_CheckListNoTier) === 1; - - if (!action.match("kept", "i") && !action.match("Shopped") && hasTier) { - if (!mercCheck) { - NTIP.GetCharmTier(unit) > 0 && (desc += ("\n\\xffc0Autoequip charm tier: " + NTIP.GetCharmTier(unit))); - NTIP.GetTier(unit) > 0 && (desc += ("\n\\xffc0Autoequip char tier: " + NTIP.GetTier(unit))); - } else { - desc += ("\n\\xffc0Autoequip merc tier: " + NTIP.GetMercTier(unit)); - } - } - - // should stop logging items unless we wish to see them or it's part of normal pickit - if (!nTResult && !force) { - switch (true) { - case (unit.questItem || unit.isBaseType): - case (!unit.isCharm && hasTier && !Developer.debugging.autoEquip): - case (charmCheck && !Developer.debugging.smallCharm && unit.classid === sdk.items.SmallCharm): - case (charmCheck && !Developer.debugging.largeCharm && unit.classid === sdk.items.LargeCharm): - case (charmCheck && !Developer.debugging.grandCharm && unit.classid === sdk.items.GrandCharm): - return true; - default: - break; - } - } - - let code = this.getItemCode(unit); - let sock = unit.getItem(); - - if (sock) { - do { - if (sock.itemType === sdk.items.type.Jewel) { - desc += "\n\n"; - desc += this.getItemDesc(sock); - } - } while (sock.getNext()); - } - - keptLine && (desc += ("\n\\xffc0Line: " + keptLine)); - desc += "$" + (unit.getFlag(sdk.items.flags.Ethereal) ? ":eth" : ""); - - let itemObj = { - title: action + " " + name, - description: desc, - image: code, - textColor: unit.quality, - itemColor: color, - header: "", - sockets: this.getItemSockets(unit) - }; - - D2Bot.printToItemLog(itemObj); - - return true; -}; - Misc.errorReport = function (error, script) { let msg, oogmsg, filemsg, source, stack; let stackLog = ""; @@ -806,7 +723,7 @@ Misc.errorReport = function (error, script) { } else { source = error.fileName.substring(error.fileName.lastIndexOf("\\") + 1, error.fileName.length); msg = "ÿc1Error in ÿc0" + script + " ÿc1(" + source + " line ÿc1" + error.lineNumber + "): ÿc1" + error.message; - oogmsg = " Error in " + script + " (" + source + " #" + error.lineNumber + ") " + error.message + " (Area: " + Pather.getAreaName(me.area) + ", Ping:" + me.ping + ", Game: " + me.gamename + ")"; + oogmsg = " Error in " + script + " (" + source + " #" + error.lineNumber + ") " + error.message + " (Area: " + getAreaName(me.area) + ", Ping:" + me.ping + ", Game: " + me.gamename + ")"; filemsg = dateString + " <" + me.profile + "> " + msg.replace(/ÿc[0-9!"+<:;.*]/gi, "") + "\n"; if (error.hasOwnProperty("stack")) { @@ -842,7 +759,7 @@ Misc.errorReport = function (error, script) { showConsole(); console.log(msg); - this.fileAction("logs/ScriptErrorLog.txt", 2, filemsg); + FileAction.read("logs/ScriptErrorLog.txt", filemsg); takeScreenshot(); delay(500); }; diff --git a/libs/SoloPlay/Functions/MuleloggerOverrides.js b/libs/SoloPlay/Functions/MuleloggerOverrides.js index 1a91ce53..ddf03dbd 100644 --- a/libs/SoloPlay/Functions/MuleloggerOverrides.js +++ b/libs/SoloPlay/Functions/MuleloggerOverrides.js @@ -5,24 +5,24 @@ * */ -includeIfNotIncluded("MuleLogger.js"); +includeIfNotIncluded("systems/mulelogger/MuleLogger.js"); includeIfNotIncluded("SoloPlay/Functions/NTIPOverrides.js"); includeIfNotIncluded("SoloPlay/Functions/MiscOverrides.js"); // Added type parameter and logging tier value under picture on char viewer tab MuleLogger.logItem = function (unit, logIlvl, type = "Player") { - if (!isIncluded("common/misc.js")) { - include("common/misc.js"); - include("common/util.js"); + if (!isIncluded("core/misc.js")) { + include("core/misc.js"); + include("core/util.js"); } logIlvl === undefined && (logIlvl = this.LogItemLevel); let header = ""; let name = unit.itemType + "_" + unit.fname.split("\n").reverse().join(" ").replace(/(y|ÿ)c[0-9!"+<:;.*]|\/|\\/g, "").trim(); - let desc = (Misc.getItemDesc(unit, logIlvl) || ""); + let desc = (Item.getItemDesc(unit, logIlvl) || ""); let color = (unit.getColor() || -1); - let code = Misc.getItemCode(unit); + let code = Item.getItemCode(unit); if (NTIP.GetMercTier(unit) > 0 || NTIP.GetTier(unit) > 0 || NTIP.GetCharmTier(unit) > 0 || NTIP.GetSecondaryTier(unit) > 0) { if (unit.mode === sdk.items.mode.inStorage && type === "Player") { @@ -46,7 +46,7 @@ MuleLogger.logItem = function (unit, logIlvl, type = "Player") { for (let i = 0; i < sock.length; i += 1) { if (sock[i].itemType === sdk.items.type.Jewel) { desc += "\n\n"; - desc += Misc.getItemDesc(sock[i]); + desc += Item.getItemDesc(sock[i]); } } } @@ -59,7 +59,7 @@ MuleLogger.logItem = function (unit, logIlvl, type = "Player") { title: name, description: desc, header: header, - sockets: Misc.getItemSockets(unit) + sockets: Item.getItemSockets(unit) }; }; diff --git a/libs/SoloPlay/Functions/NTIPOverrides.js b/libs/SoloPlay/Functions/NTIPOverrides.js index 7fd7dbea..c71a79cd 100644 --- a/libs/SoloPlay/Functions/NTIPOverrides.js +++ b/libs/SoloPlay/Functions/NTIPOverrides.js @@ -6,7 +6,7 @@ * */ -includeIfNotIncluded("NTItemParser.dbl"); +includeIfNotIncluded("core/NTItemParser.js"); includeIfNotIncluded("SoloPlay/Functions/PrototypeOverrides.js"); /** diff --git a/libs/SoloPlay/Functions/PatherOverrides.js b/libs/SoloPlay/Functions/PatherOverrides.js index 3c5e77ec..d9f0ea1f 100644 --- a/libs/SoloPlay/Functions/PatherOverrides.js +++ b/libs/SoloPlay/Functions/PatherOverrides.js @@ -6,7 +6,7 @@ * */ -includeIfNotIncluded("common/Pather.js"); +includeIfNotIncluded("core/Pather.js"); Developer.debugging.pathing && (PathDebug.enableHooks = true); @@ -53,6 +53,7 @@ NodeAction.killMonsters = function (arg = {}) { * @todo: * - we don't need this if we have a lightning chain based skill, e.g light sorc, light zon * - better monster sorting. If we are low level priortize killing easy targets like zombies/quill rats while ignoring fallens unless they are in our path + * - ignore dolls when walking unless absolutely necessary because we are blocked */ if (!arg.canTele && arg.clearPath !== false) { let monList = []; @@ -416,7 +417,6 @@ Pather.move = function (target, givenSettings = {}) { if (me.dead) return false; // main path Pather.recursion && (Pather.currentWalkingPath = path); - Pather.clearUIFlags(); node = path.shift(); @@ -541,6 +541,10 @@ Pather.move = function (target, givenSettings = {}) { } } + /** + * @todo handle passing in a callback function + */ + delay(5); } @@ -583,7 +587,7 @@ Pather.useWaypoint = function useWaypoint(targetArea, check = false) { break; } - console.log("ÿc7Start ÿc8(useWaypoint) ÿc0:: ÿc7targetArea: ÿc0" + this.getAreaName(targetArea) + " ÿc7myArea: ÿc0" + this.getAreaName(me.area)); + console.log("ÿc7Start ÿc8(useWaypoint) ÿc0:: ÿc7targetArea: ÿc0" + getAreaName(targetArea) + " ÿc7myArea: ÿc0" + getAreaName(me.area)); let wpTick = getTickCount(); for (let i = 0; i < 12; i += 1) { @@ -707,7 +711,7 @@ Pather.useWaypoint = function useWaypoint(targetArea, check = false) { while (getTickCount() - tick < Math.max(Math.round((i + 1) * 1000 / (i / 5 + 1)), pingDelay * 4)) { if (me.area === targetArea) { delay(1500); - console.log("ÿc7End ÿc8(useWaypoint) ÿc0:: ÿc7targetArea: ÿc0" + this.getAreaName(targetArea) + " ÿc7myArea: ÿc0" + this.getAreaName(me.area) + "ÿc0 - ÿc7Duration: ÿc0" + (Time.format(getTickCount() - wpTick))); + console.log("ÿc7End ÿc8(useWaypoint) ÿc0:: ÿc7targetArea: ÿc0" + getAreaName(targetArea) + " ÿc7myArea: ÿc0" + getAreaName(me.area) + "ÿc0 - ÿc7Duration: ÿc0" + (Time.format(getTickCount() - wpTick))); return true; } @@ -738,7 +742,7 @@ Pather.useWaypoint = function useWaypoint(targetArea, check = false) { if (me.area === targetArea) { delay(500); - console.log("ÿc7End ÿc8(useWaypoint) ÿc0:: ÿc7targetArea: ÿc0" + this.getAreaName(targetArea) + " ÿc7myArea: ÿc0" + this.getAreaName(me.area) + "ÿc0 - ÿc7Duration: ÿc0" + (Time.format(getTickCount() - wpTick))); + console.log("ÿc7End ÿc8(useWaypoint) ÿc0:: ÿc7targetArea: ÿc0" + getAreaName(targetArea) + " ÿc7myArea: ÿc0" + getAreaName(me.area) + "ÿc0 - ÿc7Duration: ÿc0" + (Time.format(getTickCount() - wpTick))); return true; } @@ -750,7 +754,7 @@ Pather.useWaypoint = function useWaypoint(targetArea, check = false) { Pather.clearToExit = function (currentarea, targetarea, cleartype = true) { let tick = getTickCount(); let retry = 0; - console.log("ÿc8Kolbot-SoloPlayÿc0: Start clearToExit. ÿc8Currently in: ÿc0" + Pather.getAreaName(me.area) + "ÿc8Clearing to: ÿc0" + Pather.getAreaName(targetarea)); + console.log("ÿc8Kolbot-SoloPlayÿc0: Start clearToExit. ÿc8Currently in: ÿc0" + getAreaName(me.area) + "ÿc8Clearing to: ÿc0" + getAreaName(targetarea)); me.area !== currentarea && Pather.journeyTo(currentarea); @@ -766,7 +770,7 @@ Pather.clearToExit = function (currentarea, targetarea, cleartype = true) { Misc.poll(() => me.gameReady, 1000, 100); if (retry > 5) { - console.log("ÿc8Kolbot-SoloPlayÿc0: clearToExit. ÿc2Failed to move to: ÿc0" + Pather.getAreaName(targetarea)); + console.log("ÿc8Kolbot-SoloPlayÿc0: clearToExit. ÿc2Failed to move to: ÿc0" + getAreaName(targetarea)); break; } diff --git a/libs/SoloPlay/Functions/PickitOverrides.js b/libs/SoloPlay/Functions/PickitOverrides.js index 3a8a8d2a..a6100e3a 100644 --- a/libs/SoloPlay/Functions/PickitOverrides.js +++ b/libs/SoloPlay/Functions/PickitOverrides.js @@ -6,7 +6,7 @@ * */ -includeIfNotIncluded("common/Pickit.js"); +includeIfNotIncluded("core/Pickit.js"); includeIfNotIncluded("SoloPlay/Functions/PrototypeOverrides.js"); includeIfNotIncluded("SoloPlay/Functions/NTIPOverrides.js"); includeIfNotIncluded("SoloPlay/Functions/MiscOverrides.js"); @@ -30,6 +30,9 @@ Pickit.minItemKeepGoldValue = function () { } }; +/** + * @param {ItemUnit} unit + */ Pickit.checkItem = function (unit) { const rval = NTIP.CheckItem(unit, false, true); const resultObj = (result, line = null) => ({ @@ -340,7 +343,18 @@ Pickit.canPick = function (unit) { Pickit.toCursorPick = []; +/** + * @override + * @param {ItemUnit} unit + * @param {PickitResult} status + * @param {string} keptLine + * @param {boolean} clearBeforePick + */ Pickit.pickItem = function (unit, status, keptLine, clearBeforePick = true) { + /** + * @constructor + * @param {ItemUnit} unit + */ function ItemStats (unit) { let self = this; self.x = unit.x; @@ -350,7 +364,7 @@ Pickit.pickItem = function (unit, status, keptLine, clearBeforePick = true) { self.type = unit.itemType; self.classid = unit.classid; self.name = unit.name; - self.color = Pickit.itemColor(unit); + self.color = Item.color(unit); self.gold = unit.getStat(sdk.stats.Gold); self.dist = (unit.distance || Infinity); let canTk = (Skill.haveTK && Pickit.tkable.includes(self.type) && Pickit.toCursorPick.indexOf(unit.gid) === -1 @@ -497,8 +511,8 @@ Pickit.pickItem = function (unit, status, keptLine, clearBeforePick = true) { console.log("ÿc7Picked up " + stats.color + stats.name + " ÿc0(ilvl " + stats.ilvl + (stats.sockets > 0 ? ") (sockets " + stats.sockets : "") + (keptLine ? ") (" + keptLine + ")" : ")")); if (this.ignoreLog.indexOf(stats.type) === -1) { - Misc.itemLogger("Kept", item); - Misc.logItem("Kept", item, keptLine); + Item.logger("Kept", item); + Item.logItem("Kept", item, keptLine); } if (item.identified && item.isInInventory && AutoEquip.wanted(item)) { @@ -508,13 +522,13 @@ Pickit.pickItem = function (unit, status, keptLine, clearBeforePick = true) { break; case Pickit.Result.CUBING: console.log("ÿc7Picked up " + stats.color + stats.name + " ÿc0(ilvl " + stats.ilvl + ")" + " (Cubing)"); - Misc.itemLogger("Kept", item, "Cubing " + me.findItems(item.classid).length); + Item.logger("Kept", item, "Cubing " + me.findItems(item.classid).length); Cubing.update(); break; case Pickit.Result.RUNEWORD: console.log("ÿc7Picked up " + stats.color + stats.name + " ÿc0(ilvl " + stats.ilvl + ")" + " (Runewords)"); - Misc.itemLogger("Kept", item, "Runewords"); + Item.logger("Kept", item, "Runewords"); Runewords.update(stats.classid, gid); break; @@ -615,7 +629,9 @@ Pickit.essessntialsPick = function (clearBeforePick = false, ignoreGold = false, let canFit = (Storage.Inventory.CanFit(currItem) || Pickit.canFit(currItem)); // Field id when our used space is above a certain percent or if we are full try to make room with FieldID - if (Config.FieldID.Enabled && (!canFit || Storage.Inventory.UsedSpacePercent() > Config.FieldID.UsedSpace)) { + // or if we have an exp shrine so we don't waste it + if ((Config.FieldID.Enabled || me.getState(sdk.states.ShrineExperience)) + && (!canFit || Storage.Inventory.UsedSpacePercent() > Config.FieldID.UsedSpace)) { me.fieldID() && (canFit = (currItem.gid !== undefined && Storage.Inventory.CanFit(currItem))); } @@ -623,7 +639,7 @@ Pickit.essessntialsPick = function (clearBeforePick = false, ignoreGold = false, if (!canFit) { // Check if any of the current inventory items can be stashed or need to be identified and eventually sold to make room if (this.canMakeRoom()) { - console.log("ÿc7Trying to make room for " + this.itemColor(currItem) + currItem.name); + console.log("ÿc7Trying to make room for " + Item.color(currItem) + currItem.name); // Go to town and do town chores if (Town.visitTown()) { @@ -633,14 +649,14 @@ Pickit.essessntialsPick = function (clearBeforePick = false, ignoreGold = false, } // Town visit failed - abort - console.log("ÿc7Not enough room for " + this.itemColor(currItem) + currItem.name); + console.log("ÿc7Not enough room for " + Item.color(currItem) + currItem.name); return false; } // Can't make room - Misc.itemLogger("No room for", currItem); - console.log("ÿc7Not enough room for " + this.itemColor(currItem) + currItem.name); + Item.logger("No room for", currItem); + console.log("ÿc7Not enough room for " + Item.color(currItem) + currItem.name); } // Item can fit - pick it up @@ -708,7 +724,7 @@ Pickit.pickItems = function (range = Config.PickRange, once = false) { if (!canFit) { // Check if any of the current inventory items can be stashed or need to be identified and eventually sold to make room if (this.canMakeRoom()) { - console.log("ÿc7Trying to make room for " + this.itemColor(currItem) + currItem.name); + console.log("ÿc7Trying to make room for " + Item.color(currItem) + currItem.name); // Go to town and do town chores if (Town.visitTown()) { @@ -718,14 +734,15 @@ Pickit.pickItems = function (range = Config.PickRange, once = false) { } // Town visit failed - abort - console.log("ÿc7Not enough room for " + this.itemColor(currItem) + currItem.name); + console.log("ÿc7Not enough room for " + Item.color(currItem) + currItem.name); return false; } + // Can't make room - trigger automule if (copyUnit(currItem).x !== undefined) { - Misc.itemLogger("No room for", currItem); + Item.logger("No room for", currItem); console.log("ÿc7Not enough room for " + Item.color(currItem) + currItem.name); needMule = true; diff --git a/libs/SoloPlay/Functions/PrecastOverrides.js b/libs/SoloPlay/Functions/PrecastOverrides.js index 452910ae..d816facb 100644 --- a/libs/SoloPlay/Functions/PrecastOverrides.js +++ b/libs/SoloPlay/Functions/PrecastOverrides.js @@ -5,7 +5,7 @@ * */ -includeIfNotIncluded("common/Precast.js"); +includeIfNotIncluded("core/Precast.js"); Precast.enabled = true; diff --git a/libs/SoloPlay/Functions/PrototypeOverrides.js b/libs/SoloPlay/Functions/PrototypeOverrides.js index bcd6b0f1..a828d649 100644 --- a/libs/SoloPlay/Functions/PrototypeOverrides.js +++ b/libs/SoloPlay/Functions/PrototypeOverrides.js @@ -6,7 +6,7 @@ * */ -includeIfNotIncluded("common/Prototypes.js"); +includeIfNotIncluded("core/Prototypes.js"); includeIfNotIncluded("SoloPlay/Functions/Me.js"); includeIfNotIncluded("SoloPlay/Functions/Polyfills.js"); diff --git a/libs/SoloPlay/Functions/Quest.js b/libs/SoloPlay/Functions/Quest.js index 869e4507..023f7d98 100644 --- a/libs/SoloPlay/Functions/Quest.js +++ b/libs/SoloPlay/Functions/Quest.js @@ -1,7 +1,7 @@ /** * @filename Quest.js * @author theBGuy -* @credit Dark-f, JeanMax, https://github.com/SetupSonic/clean-sonic/blob/master/libs/sonic/common/Quest.js +* @credit Dark-f, JeanMax, https://github.com/SetupSonic/clean-sonic/blob/master/libs/sonic/core/Quest.js * @desc Miscellaneous quest tasks for leveling * */ @@ -415,7 +415,7 @@ const Quest = { throw new Error("Failed to socket item"); } - Misc.logItem("Used my " + sdk.difficulty.nameOf(me.diff) + " socket quest on : ", item, null, true); + Item.logItem("Used my " + sdk.difficulty.nameOf(me.diff) + " socket quest on : ", item, null, true); D2Bot.printToConsole("Kolbot-SoloPlay :: Used my " + sdk.difficulty.nameOf(me.diff) + " socket quest on : " + item.name, sdk.colors.D2Bot.Gold); CharData.updateData(sdk.difficulty.nameOf(me.diff), "socketUsed", true); myData[sdk.difficulty.nameOf(me.diff).toLowerCase()].socketUsed = true; @@ -491,7 +491,7 @@ const Quest = { throw new Error("Failed to imbue item"); } - Misc.logItem("Used my " + sdk.difficulty.nameOf(me.diff) + " imbue quest on : ", item, null, true); + Item.logItem("Used my " + sdk.difficulty.nameOf(me.diff) + " imbue quest on : ", item, null, true); D2Bot.printToConsole("Kolbot-SoloPlay :: Used my " + sdk.difficulty.nameOf(me.diff) + " imbue quest on : " + item.name, sdk.colors.D2Bot.Gold); CharData.updateData(sdk.difficulty.nameOf(me.diff), "imbueUsed", true); myData[sdk.difficulty.nameOf(me.diff).toLowerCase()].imbueUsed = true; diff --git a/libs/SoloPlay/Functions/RunewordsOverrides.js b/libs/SoloPlay/Functions/RunewordsOverrides.js index 8fc2224e..bb85e9e1 100644 --- a/libs/SoloPlay/Functions/RunewordsOverrides.js +++ b/libs/SoloPlay/Functions/RunewordsOverrides.js @@ -5,7 +5,7 @@ * */ -!includeIfNotIncluded("common/Runewords.js"); +!includeIfNotIncluded("core/Runewords.js"); // Don't use ladder-only on NL Runeword.Brand = (me.ladder || Developer.addLadderRW) ? [sdk.items.runes.Jah, sdk.items.runes.Lo, sdk.items.runes.Mal, sdk.items.runes.Gul] : false; // Jah + Lo + Mal + Gul diff --git a/libs/SoloPlay/Functions/SkillOverrides.js b/libs/SoloPlay/Functions/SkillOverrides.js index 85831479..1a9855b6 100644 --- a/libs/SoloPlay/Functions/SkillOverrides.js +++ b/libs/SoloPlay/Functions/SkillOverrides.js @@ -5,7 +5,7 @@ * */ -includeIfNotIncluded("common/Misc.js"); +includeIfNotIncluded("core/Skill.js"); includeIfNotIncluded("SoloPlay/Tools/Developer.js"); Skill.forcePacket = (Developer.forcePacketCasting.enabled && !Developer.forcePacketCasting.excludeProfiles.includes(me.profile)); diff --git a/libs/SoloPlay/Functions/TownOverrides.js b/libs/SoloPlay/Functions/TownOverrides.js index 64261ba8..78722d69 100644 --- a/libs/SoloPlay/Functions/TownOverrides.js +++ b/libs/SoloPlay/Functions/TownOverrides.js @@ -12,7 +12,7 @@ * - some of these functions might be better as part of me. Example Town.getTpTool -> me.getTpTool makes more sense */ -includeIfNotIncluded("common/Town.js"); +includeIfNotIncluded("core/Town.js"); new Overrides.Override(Town, Town.drinkPots, function(orignal, type) { const objDrank = orignal(type, false); @@ -38,7 +38,7 @@ new Overrides.Override(Town, Town.drinkPots, function(orignal, type) { CharData.buffData[objID].duration += (objDrank.quantity * 30 * 1000) - (getTickCount() - CharData.buffData[objID].tick); } - console.log("ÿc9DrinkPotsÿc0 :: drank " + objDrank.quantity + " " + objDrank.potName + "s. Timer [" + Developer.formatTime(CharData.buffData[objID].duration) + "]"); + console.log("ÿc9DrinkPotsÿc0 :: drank " + objDrank.quantity + " " + objDrank.potName + "s. Timer [" + Tracker.formatTime(CharData.buffData[objID].duration) + "]"); } } } catch (e) { @@ -232,7 +232,7 @@ Town.sellItems = function (itemList = []) { try { if (getUIFlag(sdk.uiflags.Shop) || getUIFlag(sdk.uiflags.NPCMenu)) { console.log("sell " + item.name); - Misc.itemLogger("Sold", item); + Item.logger("Sold", item); item.sell(); delay(100); } @@ -327,33 +327,33 @@ Town.itemResult = function (item, result, system = "", sell = false) { switch (result.result) { case Pickit.Result.WANTED: case Pickit.Result.SOLOWANTS: - Misc.itemLogger("Kept", item); - Misc.logItem("Kept", item, result.line); + Item.logger("Kept", item); + Item.logItem("Kept", item, result.line); system === "Field" && ((Item.autoEquipCheck(item) && Item.autoEquip("Field")) || (Item.autoEquipCheckSecondary(item) && Item.autoEquipSecondary("Field"))); break; case Pickit.Result.UNID: // At low level its not worth keeping these items until we can Id them it just takes up too much room if (sell && me.charlvl < 10 && item.magic && item.classid !== sdk.items.SmallCharm) { - Misc.itemLogger("Sold", item); + Item.logger("Sold", item); item.sell(); } break; case Pickit.Result.CUBING: - Misc.itemLogger("Kept", item, "Cubing-" + system); + Item.logger("Kept", item, "Cubing-" + system); Cubing.update(); break; case Pickit.Result.RUNEWORD: break; case Pickit.Result.CRAFTING: - Misc.itemLogger("Kept", item, "CraftSys-" + system); + Item.logger("Kept", item, "CraftSys-" + system); CraftingSystem.update(item); break; case Pickit.Result.SOLOSYSTEM: - Misc.itemLogger("Kept", item, "SoloWants-" + system); + Item.logger("Kept", item, "SoloWants-" + system); SoloWants.update(item); break; @@ -364,11 +364,11 @@ Town.itemResult = function (item, result, system = "", sell = false) { case (Developer.debugging.smallCharm && item.classid === sdk.items.SmallCharm): case (Developer.debugging.largeCharm && item.classid === sdk.items.LargeCharm): case (Developer.debugging.grandCharm && item.classid === sdk.items.GrandCharm): - Misc.logItem("Sold", item); + Item.logItem("Sold", item); break; default: - Misc.itemLogger("Sold", item); + Item.logger("Sold", item); break; } @@ -422,7 +422,7 @@ Town.cainID = function (force = false) { try { console.log("sell " + item.name); this.initNPC("Shop", "clearInventory"); - Misc.itemLogger("Sold", item); + Item.logger("Sold", item); item.sell(); } catch (e) { console.error(e); @@ -431,24 +431,24 @@ Town.cainID = function (force = false) { break; case Pickit.Result.WANTED: case Pickit.Result.SOLOWANTS: - Misc.itemLogger("Kept", item); - Misc.logItem("Kept", item, result.line); + Item.logger("Kept", item); + Item.logItem("Kept", item, result.line); break; case Pickit.Result.CUBING: - Misc.itemLogger("Kept", item, "Cubing-Town"); + Item.logger("Kept", item, "Cubing-Town"); Cubing.update(); break; case Pickit.Result.RUNEWORD: // (doesn't trigger normally) break; case Pickit.Result.CRAFTING: - Misc.itemLogger("Kept", item, "CraftSys-Town"); + Item.logger("Kept", item, "CraftSys-Town"); CraftingSystem.update(item); break; case Pickit.Result.SOLOSYSTEM: - Misc.itemLogger("Kept", item, "SoloWants-Town"); + Item.logger("Kept", item, "SoloWants-Town"); SoloWants.update(item); break; @@ -467,11 +467,11 @@ Town.cainID = function (force = false) { case (Developer.debugging.smallCharm && item.classid === sdk.items.SmallCharm): case (Developer.debugging.largeCharm && item.classid === sdk.items.LargeCharm): case (Developer.debugging.grandCharm && item.classid === sdk.items.GrandCharm): - Misc.logItem("Sold", item); + Item.logItem("Sold", item); break; default: - Misc.itemLogger("Dropped", item, "clearInventory"); + Item.logger("Dropped", item, "clearInventory"); break; } @@ -479,7 +479,7 @@ Town.cainID = function (force = false) { item.sell(); } else { console.log("clearInventory dropped " + item.name); - Misc.itemLogger("Dropped", item, "clearInventory"); + Item.logger("Dropped", item, "clearInventory"); item.drop(); } @@ -559,7 +559,7 @@ Town.identify = function () { // Items for gold, will sell magics, etc. w/o id, but at low levels // magics are often not worth iding. case Pickit.Result.TRASH: - Misc.itemLogger("Sold", item); + Item.logger("Sold", item); item.sell(); break; @@ -650,8 +650,8 @@ Town.shopItems = function (force = false) { action === undefined && (action = ""); tierInfo === undefined && (tierInfo = ""); console.log("ÿc8Kolbot-SoloPlayÿc0: " + action + (tierInfo ? " " + tierInfo : "")); - Misc.itemLogger(action, item); - Developer.debugging.autoEquip && Misc.logItem("Shopped " + action, item, result.line !== undefined ? result.line : "null"); + Item.logger(action, item); + Developer.debugging.autoEquip && Item.logItem("Shopped " + action, item, result.line !== undefined ? result.line : "null"); }; for (let i = 0; i < items.length; i++) { @@ -803,8 +803,8 @@ Town.gamble = function () { switch (result.result) { case Pickit.Result.WANTED: - Misc.itemLogger("Gambled", newItem); - Misc.logItem("Gambled", newItem, result.line); + Item.logger("Gambled", newItem); + Item.logItem("Gambled", newItem, result.line); list.push(newItem.gid); break; @@ -818,7 +818,7 @@ Town.gamble = function () { break; default: - Misc.itemLogger("Sold", newItem, "Gambling"); + Item.logger("Sold", newItem, "Gambling"); newItem.sell(); if (!Config.PacketShopping) { @@ -841,7 +841,7 @@ Town.gamble = function () { * @param {ItemUnit} item */ Town.canStash = function (item) { - if (this.ignoredItemTypes.includes(item.itemType) + if (Town.ignoreType(item.itemType) || [sdk.items.quest.HoradricStaff, sdk.items.quest.KhalimsWill].includes(item.classid) || (item.isCharm && Item.autoEquipCharmCheck(item))) { return false; @@ -866,7 +866,7 @@ Town.stash = function (stashGold = true) { case Town.systemsKeep(item): case AutoEquip.wanted(item) && pickResult === Pickit.Result.UNWANTED: // wanted but can't use yet case !item.sellable: // quest/essences/keys/ect - Storage.Stash.MoveTo(item) && Misc.itemLogger("Stashed", item); + Storage.Stash.MoveTo(item) && Item.logger("Stashed", item); break; default: break; @@ -1131,7 +1131,7 @@ Town.clearInventory = function () { try { if (getUIFlag(sdk.uiflags.Shop) || getUIFlag(sdk.uiflags.NPCMenu)) { console.log("clearInventory sell " + item.name); - Misc.itemLogger("Sold", item); + Item.logger("Sold", item); item.sell(); delay(100); } @@ -1226,8 +1226,8 @@ Town.clearJunk = function () { if (getUIFlag(sdk.uiflags.Shop) || (Config.PacketShopping && getInteractedNPC() && getInteractedNPC().itemcount > 0)) { for (let i = 0; i < junkToSell.length; i++) { console.log("ÿc9JunkCheckÿc0 :: Sell " + junkToSell[i].name); - Misc.itemLogger("Sold", junkToSell[i]); - Developer.debugging.junkCheck && Misc.logItem("JunkCheck Sold", junkToSell[i]); + Item.logger("Sold", junkToSell[i]); + Developer.debugging.junkCheck && Item.logItem("JunkCheck Sold", junkToSell[i]); junkToSell[i].sell(); delay(100); @@ -1238,8 +1238,8 @@ Town.clearJunk = function () { for (let i = 0; i < junkToDrop.length; i++) { console.log("ÿc9JunkCheckÿc0 :: Drop " + junkToDrop[i].name); - Misc.itemLogger("Sold", junkToDrop[i]); - Developer.debugging.junkCheck && Misc.logItem("JunkCheck Sold", junkToDrop[i]); + Item.logger("Sold", junkToDrop[i]); + Developer.debugging.junkCheck && Item.logItem("JunkCheck Sold", junkToDrop[i]); junkToDrop[i].drop(); delay(100); diff --git a/libs/SoloPlay/Modules/GameData.js b/libs/SoloPlay/Modules/GameData.js index a341e634..41c70851 100644 --- a/libs/SoloPlay/Modules/GameData.js +++ b/libs/SoloPlay/Modules/GameData.js @@ -1170,7 +1170,7 @@ eret.name = getSkillById(eret.skill); eret.cooldown = this.skillCooldown(sk | 0); if (all) { - allData.unshift(Misc.copy(eret)); + allData.unshift(copyObj(eret)); } } } @@ -1299,7 +1299,7 @@ eret.type = skillDamageInfo[eret.skill].type; eret.name = getSkillById(eret.skill); eret.cooldown = this.skillCooldown(sk | 0); - allData.unshift(Misc.copy(eret)); + allData.unshift(copyObj(eret)); } } } @@ -1394,7 +1394,7 @@ }, /*weaponSpeedModifier: function (weapon1Code, charClass = GameData.myReference.classid, weapon2Code = null) { - let weapons = new CSV("sdk/weapons.txt"); + let weapons = new CSV("sdk/txt/weapons.txt"); let weapon1Data = weapons.findObject("code", weapon1Code); if (!weapon2Code) { return weapon1Data.speed; @@ -1712,7 +1712,7 @@ // CharSpeed ​​... This is the animation speed of the attack animation that the unchanged character would use. // weaponIAS ... All IAS on our weapon or weapon base // */ - // let weaponData = (new CSV("sdk/weapons.txt")).findObject("code", weaponCode); + // let weaponData = (new CSV("sdk/txt/weapons.txt")).findObject("code", weaponCode); // if (!weaponData) { // console.log(sdk.colors.Orange + "No weapon data found for code " + weaponCode); // } diff --git a/libs/SoloPlay/Modules/Guard.js b/libs/SoloPlay/Modules/Guard.js index 5d54bee2..875706c4 100644 --- a/libs/SoloPlay/Modules/Guard.js +++ b/libs/SoloPlay/Modules/Guard.js @@ -1,4 +1,5 @@ (function (module, require, thread) { + "use strict"; const Messaging = require("../../modules/Messaging"); const Worker = require("../../modules/Worker"); const sdk = require("../../modules/sdk"); @@ -45,9 +46,9 @@ this.update = () => { stack = myStack.match(/[^\r\n]+/g); stack = stack && stack.slice(6/*skip path to here*/).map(el => { - let line = el.substr(el.lastIndexOf(":") + 1), - functionName = el.substr(0, el.indexOf("@")), - filename = el.substr(el.lastIndexOf("\\") + 1); + let line = el.substr(el.lastIndexOf(":") + 1); + let functionName = el.substr(0, el.indexOf("@")); + let filename = el.substr(el.lastIndexOf("\\") + 1); filename = filename.substr(0, filename.indexOf(".")); @@ -59,32 +60,6 @@ }).update; - Worker.runInBackground.ping = (new function () { - let myHeartbeat = 0; - - // recv heartbeat - Messaging.on("Guard", (data => typeof data === "object" && data && data.hasOwnProperty("heartbeat") && (myHeartbeat = data.heartbeat))); - - this.update = function () { - // Do not deal with this shit if default is paused - const script = getScript("default.dbj"); - if (!script || !script.running) { - myHeartbeat = getTickCount(); - return true; - } - if (myHeartbeat && getTickCount() - myHeartbeat > (2 * 6e4)) { - console.log("Default.dbj seems to have crashed"); - myHeartbeat = 0; - if (script) script.stop(); - console.log("Waiting 10 seconds to restart default.dbj"); - delay(1e4); - load("default.dbj"); - console.log("Starting default.dbj"); - } - return true; - }; - }).update; - let quiting = false; addEventListener("scriptmsg", data => data === "quit" && (quiting = true)); @@ -100,15 +75,6 @@ return true; }); - let timer = getTickCount(); - Worker.runInBackground.heartbeatForGuard = function () { - if ((getTickCount() - timer) < 1000 || (timer = getTickCount()) && false) return true; - - // Every second or so, we send a heartbeat tick - Messaging.send({Guard: {heartbeat: getTickCount()}}); - - return true; - }; break; } case "loaded": { diff --git a/libs/SoloPlay/Scripts/a1chests.js b/libs/SoloPlay/Scripts/a1chests.js index 8eaed4a8..3339e248 100644 --- a/libs/SoloPlay/Scripts/a1chests.js +++ b/libs/SoloPlay/Scripts/a1chests.js @@ -18,13 +18,13 @@ function a1chests() { continue; } - myPrint("Moving to " + Pather.getAreaName(areas[i])); + myPrint("Moving to " + getAreaName(areas[i])); Pather.journeyTo(areas[i]); Precast.doPrecast(); Misc.openChestsInArea(areas[i]); Town.doChores(); } catch (e) { - console.debug("ÿc8Kolbot-SoloPlayÿc0: Failed to move to " + Pather.getAreaName(areas[i]), e); + console.debug("ÿc8Kolbot-SoloPlayÿc0: Failed to move to " + getAreaName(areas[i]), e); continue; } } diff --git a/libs/SoloPlay/Scripts/a5chests.js b/libs/SoloPlay/Scripts/a5chests.js index c04c7874..8cc16140 100644 --- a/libs/SoloPlay/Scripts/a5chests.js +++ b/libs/SoloPlay/Scripts/a5chests.js @@ -19,13 +19,13 @@ function a5chests() { continue; } - myPrint("Moving to " + Pather.getAreaName(areas[i])); + myPrint("Moving to " + getAreaName(areas[i])); Pather.journeyTo(areas[i]); Precast.doPrecast(); Misc.openChestsInArea(areas[i]); Town.doChores(); } catch (e) { - console.debug("ÿc8Kolbot-SoloPlayÿc0: Failed to move to " + Pather.getAreaName(areas[i]), e); + console.debug("ÿc8Kolbot-SoloPlayÿc0: Failed to move to " + getAreaName(areas[i]), e); continue; } } diff --git a/libs/SoloPlay/Scripts/ancients.js b/libs/SoloPlay/Scripts/ancients.js index 4ad37fe4..580bc92c 100644 --- a/libs/SoloPlay/Scripts/ancients.js +++ b/libs/SoloPlay/Scripts/ancients.js @@ -7,6 +7,7 @@ */ function ancients () { + include("core/Common/Ancients.js"); Town.doChores(false, { fullChores: true }); myPrint("starting ancients"); @@ -22,7 +23,7 @@ function ancients () { // ancients prep Town.doChores(false, { thawing: true, antidote: true, stamina: true, fullChores: true }); - let tempConfig = Misc.copy(Config); // save and update config settings + let tempConfig = copyObj(Config); // save and update config settings Config.TownCheck = false; Config.MercWatch = false; diff --git a/libs/SoloPlay/Scripts/baal.js b/libs/SoloPlay/Scripts/baal.js index a401a891..460aa87d 100644 --- a/libs/SoloPlay/Scripts/baal.js +++ b/libs/SoloPlay/Scripts/baal.js @@ -7,6 +7,7 @@ */ function baal () { + include("core/Common/Baal.js"); Config.BossPriority = false; let decoyTick = 0; diff --git a/libs/SoloPlay/Scripts/cows.js b/libs/SoloPlay/Scripts/cows.js index 115887b5..6777cd94 100644 --- a/libs/SoloPlay/Scripts/cows.js +++ b/libs/SoloPlay/Scripts/cows.js @@ -6,7 +6,7 @@ */ function cows () { - this.getLeg = function () { + const getLeg = function () { if (me.getItem(sdk.items.quest.WirtsLeg)) return me.getItem(sdk.items.quest.WirtsLeg); // Cain is incomplete, complete it then continue @isid0re @@ -33,7 +33,7 @@ function cows () { return me.getItem(sdk.items.quest.WirtsLeg); }; - this.openPortal = function (portalID, ...classIDS) { + const openPortal = function (portalID, ...classIDS) { !me.inArea(sdk.areas.RogueEncampment) && Town.goToTown(1); let npc, tome, scroll; @@ -116,10 +116,10 @@ function cows () { Town.doChores(false, { fullChores: true }); myPrint("starting cows"); - if (!Pather.getPortal(sdk.areas.MooMooFarm) && !this.getLeg()) return true; + if (!Pather.getPortal(sdk.areas.MooMooFarm) && !getLeg()) return true; Town.doChores(); - this.openPortal(sdk.areas.MooMooFarm, sdk.items.quest.WirtsLeg, sdk.items.TomeofTownPortal); + openPortal(sdk.areas.MooMooFarm, sdk.items.quest.WirtsLeg, sdk.items.TomeofTownPortal); Town.fillTome(sdk.items.TomeofTownPortal); // when does this become not worth it @@ -132,6 +132,7 @@ function cows () { Town.move("stash"); if (Misc.poll(() => Pather.usePortal(sdk.areas.MooMooFarm), Time.seconds(30), Time.seconds(1))) { + include("core/Common/Cows.js"); const Worker = require("../../modules/Worker"); let kingTick = getTickCount(); let king; diff --git a/libs/SoloPlay/Scripts/developermode.js b/libs/SoloPlay/Scripts/developermode.js index 43655158..8aa20888 100644 --- a/libs/SoloPlay/Scripts/developermode.js +++ b/libs/SoloPlay/Scripts/developermode.js @@ -4,10 +4,8 @@ * @desc developer mode made easy * */ -include("UnitInfo.js"); function developermode() { - let uInfo = null; let [done, action, command, userAddon, test] = [false, false, false, false, false]; let [watchSent, watchRecv, blockSent, blockRecv] = [[], [], [], []]; const runCommand = function (msg) { @@ -170,7 +168,10 @@ function developermode() { } }; - // Received packet handler + /** + * Received packet handler + * @param {number[]} pBytes + */ const packetReceived = function (pBytes) { let ID = pBytes[0].toString(16); @@ -179,7 +180,7 @@ function developermode() { if (watchRecv.includes(ID)) { let size = pBytes.length; - let array = [].slice.call(pBytes); + let array = [].slice.call(pBytes).map(pByte => pByte.toString(16)); array.shift(); console.log("ÿc2S ÿc8" + ID + "ÿc0 " + array.join(" ") + " ÿc5(" + size + " Bytes)"); } @@ -220,19 +221,18 @@ function developermode() { return false; }; - myPrint("ÿc8Kolbot-SoloPlayÿc0: starting developermode"); - addEventListener("gamepacketsent", packetSent); - addEventListener("gamepacket", packetReceived); - Config.Silence = false; + const UnitInfo = new (require("../../modules/UnitInfo")); try { + myPrint("ÿc8Kolbot-SoloPlayÿc0: starting developermode"); + addEventListener("gamepacketsent", packetSent); + addEventListener("gamepacket", packetReceived); + Config.Silence = false; + while (!done) { - if (action) { - if (!UnitInfo.cleared) { - UnitInfo.remove(); - userAddon = false; - } + UnitInfo.check(); + if (action) { Loader.runScript(action); me.overhead("Done with action"); @@ -240,11 +240,6 @@ function developermode() { } if (command) { - if (!UnitInfo.cleared) { - UnitInfo.remove(); - userAddon = false; - } - try { eval(command); } catch (e) { @@ -255,13 +250,12 @@ function developermode() { command = false; } - !UnitInfo.cleared && !Game.getSelectedUnit() && UnitInfo.remove(); if (userAddon) { - uInfo = Game.getSelectedUnit(); - UnitInfo.createInfo(uInfo); + UnitInfo.createInfo(Game.getSelectedUnit()); } if (test) { + console.debug("done"); me.overhead("done"); test = false; } diff --git a/libs/SoloPlay/Scripts/diablo.js b/libs/SoloPlay/Scripts/diablo.js index 92c6b9ab..b50962c3 100644 --- a/libs/SoloPlay/Scripts/diablo.js +++ b/libs/SoloPlay/Scripts/diablo.js @@ -8,8 +8,9 @@ // todo: clean this up, listen for lights game packet while opening/checking seals function diablo () { + include("core/Common/Diablo.js"); // Start Diablo Quest - this.diabloPrep = function () { + const diabloPrep = function () { let tick = getTickCount(); let decoyDuration = (me.amazon ? Skill.getDuration(sdk.skills.Decoy) : 0); @@ -143,7 +144,7 @@ function diablo () { (me.sorceress || me.necromancer || me.assassin) ? Pather.moveNear(7792, 5292, 37) : Pather.moveTo(7788, 5292, 3, 30); - this.diabloPrep(); + diabloPrep(); let theD = Game.getMonster(sdk.monsters.Diablo); if (!theD) { @@ -157,7 +158,7 @@ function diablo () { } (me.sorceress || me.necromancer || me.assassin) ? Pather.moveNear(7792, 5292, 37) : Pather.moveTo(7788, 5292, 3, 30); - this.diabloPrep(); + diabloPrep(); } !Attack.pwnDia() && Attack.killTarget(sdk.monsters.Diablo); diff --git a/libs/SoloPlay/Scripts/tristram.js b/libs/SoloPlay/Scripts/tristram.js index dcb3abb8..6dcc42f4 100644 --- a/libs/SoloPlay/Scripts/tristram.js +++ b/libs/SoloPlay/Scripts/tristram.js @@ -57,6 +57,8 @@ function tristram () { if (!Misc.checkQuest(sdk.quest.id.TheSearchForCain, 4) && me.getItem(sdk.items.quest.KeytotheCairnStones)) { try { + include("core/Common/Cain.js"); + const stoneIds = [ sdk.quest.chest.StoneAlpha, sdk.quest.chest.StoneBeta, sdk.quest.chest.StoneGamma, sdk.quest.chest.StoneDelta, sdk.quest.chest.StoneLambda @@ -131,5 +133,7 @@ function tristram () { Attack.clearLocations(spots); } + delete Common.Cain; + return true; } diff --git a/libs/SoloPlay/SoloPlay.js b/libs/SoloPlay/SoloPlay.js index 7de8a5a6..df2b1da1 100644 --- a/libs/SoloPlay/SoloPlay.js +++ b/libs/SoloPlay/SoloPlay.js @@ -5,28 +5,15 @@ * */ js_strict(true); +include("critical.js"); + +// globals needed for core gameplay +includeCoreLibs({ exclude: ["Storage.js"]}); + +// system libs +includeSystemLibs(); +include("systems/mulelogger/MuleLogger.js"); -include("json2.js"); -include("NTItemParser.dbl"); -include("OOG.js"); -include("AutoMule.js"); -include("Gambling.js"); -include("CraftingSystem.js"); -include("TorchSystem.js"); -include("MuleLogger.js"); -include("common/Attack.js"); -include("common/Common.js"); -include("common/Cubing.js"); -include("common/CollMap.js"); -include("common/Config.js"); -include("common/misc.js"); -include("common/util.js"); -include("common/Pickit.js"); -include("common/Pather.js"); -include("common/Precast.js"); -include("common/Prototypes.js"); -include("common/Runewords.js"); -include("common/Town.js"); // Include SoloPlay's librarys include("SoloPlay/Tools/Developer.js"); include("SoloPlay/Tools/Tracker.js"); @@ -35,9 +22,15 @@ include("SoloPlay/Tools/SoloIndex.js"); include("SoloPlay/Functions/ConfigOverrides.js"); include("SoloPlay/Functions/Globals.js"); -// @todo -// call loader from here and change loader to use the soloplay script files -// todo - global skip gid array +// main thread specific +const LocalChat = require("../modules/LocalChat"); + +/** + * @todo + * - Add priority to runewords/cubing + * - proper script skipping + gold runs when needed + * - fix autoequip issues with rings and dual wielding + */ function main () { D2Bot.init(); // Get D2Bot# handle @@ -50,6 +43,16 @@ function main () { }; })([].filter.constructor("return this")(), load); + /** + * Fixes d2bs bug where this returns the "function" + */ + (function (original) { + me.move = function (...args) { + original.apply(this, args); + return true; + }; + })(me.move); + // wait until game is ready while (!me.gameReady) { delay(50); @@ -58,7 +61,7 @@ function main () { clearAllEvents(); // remove any event listeners from game crash // load heartbeat if it isn't already running - !getScript("tools/heartbeat.js") && load("tools/heartbeat.js"); + !getScript("threads/heartbeat.js") && load("threads/heartbeat.js"); SetUp.include(); SetUp.init(); @@ -72,6 +75,11 @@ function main () { if (msg && typeof msg === "string" && msg !== "") { switch (true) { + case msg === "nextScript": + // testing - works so maybe can handle other events as well? + me.emit("nextScript"); + + break; case msg === "soj": sojPause = true; sojCounter = 0; @@ -95,9 +103,15 @@ function main () { break; case msg.toLowerCase() === "test": - console.debug(sdk.colors.Green + "//-----------DataDump Start-----------//\nÿc8MainData ::\n", getScript(true).name, - myData, "\nÿc8BuffData ::\n", CharData.buffData, "\nÿc8SkillData ::\n", CharData.skillData, "\n" + sdk.colors.Red + "//-----------DataDump End-----------//"); - + { + console.debug(sdk.colors.Green + "//-----------DataDump Start-----------//", + "\nÿc8ThreadData ::\n", getScript(true), + "\nÿc8MainData ::\n", myData, + "\nÿc8BuffData ::\n", CharData.buffData, + "\nÿc8SkillData ::\n", CharData.skillData, + "\nÿc8GlobalVariabls ::\n", Object.keys(global), + "\n" + sdk.colors.Red + "//-----------DataDump End-----------//"); + } break; } } @@ -135,7 +149,7 @@ function main () { addEventListener("copydata", this.copyDataEvent); // AutoMule/TorchSystem/Gambling/Crafting handler - if (AutoMule.inGameCheck() || TorchSystem.inGameCheck() || Gambling.inGameCheck() || CraftingSystem.inGameCheck()) { + if (AutoMule.inGameCheck() || TorchSystem.inGameCheck() || Gambling.inGameCheck() || CraftingSystem.inGameCheck() || SoloEvents.inGameCheck()) { return true; } @@ -152,26 +166,34 @@ function main () { DataFile.updateStats(["experience", "name"]); - // Load threads - if (SoloEvents.inGameCheck()) return true; + // Load threads - for now split between old system with threads or opt in for new system with workers load("libs/SoloPlay/Threads/ToolsThread.js"); - load("libs/SoloPlay/Threads/EventThread.js"); - load("libs/SoloPlay/Threads/TownChicken.js"); + if ((Developer.testingMode.enabled && Developer.testingMode.profiles.some(prof => String.isEqual(prof, me.profile)))) { + removeEventListener("scriptmsg", AutoBuild.levelUpHandler); + // require("./Modules/eventEmitter"); // needs work + + includeIfNotIncluded("SoloPlay/Workers/EventWorker.js"); + includeIfNotIncluded("SoloPlay/Workers/TownChickenWorker.js"); + includeIfNotIncluded("SoloPlay/Workers/AutoBuildWorker.js"); + SoloEvents.filePath = "libs/SoloPlay/SoloPlay.js"; // hacky for now, don't want to mess up others running so we just broadcast to ourselves + } else { + load("libs/SoloPlay/Threads/EventThread.js"); + load("libs/SoloPlay/Threads/TownChicken.js"); + load("libs/SoloPlay/Threads/AutoBuildThread.js"); + } // Load guard if we want to see the stack as it runs if (Developer.debugging.showStack.enabled) { // check in case we reloaded and guard was still running let guard = getScript("libs/SoloPlay/Modules/Guard.js"); !!guard && guard.running && guard.stop(); - Developer.debugging.showStack.profiles.some(profile => profile.toLowerCase() === "all" || profile.toLowerCase() === me.profile.toLowerCase()) && require("../SoloPlay/Modules/Guard"); + Developer.debugging.showStack.profiles.some(prof => String.isEqual(prof, me.profile) || String.isEqual(prof, "all")) && require("../SoloPlay/Modules/Guard"); delay(1000); } if (Config.PublicMode) { - Config.PublicMode === true ? require("libs/modules/SimpleParty") : load("tools/Party.js"); + Config.PublicMode === true ? require("libs/modules/SimpleParty") : load("threads/Party.js"); } - - // Config.AntiHostile && load("tools/AntiHostile.js"); // One time maintenance - check cursor, get corpse, clear leftover items, pick items in case anything important was dropped Cubing.cursorCheck(); @@ -185,28 +207,17 @@ function main () { Pickit.pickItems(); me.hpPercent <= 10 && Town.heal() && me.cancelUIFlags(); - if (Config.DebugMode) { - delay(2000); - let script = getScript(); - - if (script) { - do { - console.log(script); - } while (script.getNext()); - } - } - me.automap = Config.AutoMap; // Next game = drop keys TorchSystem.keyCheck() && scriptBroadcast("torch"); // Auto skill and stat - if (Config.AutoSkill.Enabled && include("common/AutoSkill.js")) { + if (Config.AutoSkill.Enabled && include("core/Auto/AutoSkill.js")) { AutoSkill.init(Config.AutoSkill.Build, Config.AutoSkill.Save); } - if (Config.AutoStat.Enabled && include("common/AutoStat.js")) { + if (Config.AutoStat.Enabled && include("core/Auto/AutoStat.js")) { AutoStat.init(Config.AutoStat.Build, Config.AutoStat.Save, Config.AutoStat.BlockChance, Config.AutoStat.UseBulk); } @@ -218,7 +229,7 @@ function main () { // Start Developer mode - this stops the script from progressing past this point and allows running specific scripts/functions through chat commands if (Developer.developerMode.enabled) { - if (Developer.developerMode.profiles.some(profile => profile.toLowerCase() === me.profile.toLowerCase())) { + if (Developer.developerMode.profiles.some(prof => String.isEqual(prof, me.profile))) { Developer.debugging.pathing && (me.automap = true); Loader.runScript("developermode"); } diff --git a/libs/SoloPlay/Threads/AutoBuildThread.js b/libs/SoloPlay/Threads/AutoBuildThread.js index 035feede..d44d7150 100644 --- a/libs/SoloPlay/Threads/AutoBuildThread.js +++ b/libs/SoloPlay/Threads/AutoBuildThread.js @@ -5,30 +5,18 @@ * */ js_strict(true); +include("critical.js"); + +// globals needed for core gameplay +includeCoreLibs({ exclude: ["Storage.js"]}); +// needed for this thread +include("core/Auto/AutoSkill.js"); +include("core/Auto/AutoStat.js"); + +// system libs +includeSystemLibs(); +include("systems/mulelogger/MuleLogger.js"); -include("json2.js"); -include("NTItemParser.dbl"); -include("OOG.js"); -include("common/util.js"); -include("AutoMule.js"); -include("Gambling.js"); -include("CraftingSystem.js"); -include("TorchSystem.js"); -include("MuleLogger.js"); -include("common/AutoSkill.js"); -include("common/AutoStat.js"); -include("common/Attack.js"); -include("common/Common.js"); -include("common/Cubing.js"); -include("common/CollMap.js"); -include("common/Config.js"); -include("common/misc.js"); -include("common/Pickit.js"); -include("common/Pather.js"); -include("common/Precast.js"); -include("common/Prototypes.js"); -include("common/Runewords.js"); -include("common/Town.js"); // Include SoloPlay's librarys include("SoloPlay/Tools/Developer.js"); include("SoloPlay/Tools/Tracker.js"); diff --git a/libs/SoloPlay/Threads/EventThread.js b/libs/SoloPlay/Threads/EventThread.js index 24373cc4..243f9f89 100644 --- a/libs/SoloPlay/Threads/EventThread.js +++ b/libs/SoloPlay/Threads/EventThread.js @@ -5,25 +5,15 @@ * */ js_strict(true); +include("critical.js"); + +// globals needed for core gameplay +includeCoreLibs({ exclude: ["Storage.js"]}); + +// system libs +includeSystemLibs(); +include("systems/mulelogger/MuleLogger.js"); -include("json2.js"); -include("NTItemParser.dbl"); -include("OOG.js"); -include("Gambling.js"); -include("CraftingSystem.js"); -include("common/util.js"); -include("common/Attack.js"); -include("common/Common.js"); -include("common/Cubing.js"); -include("common/Config.js"); -include("common/CollMap.js"); -include("common/misc.js"); -include("common/Pickit.js"); -include("common/Pather.js"); -include("common/Precast.js"); -include("common/Prototypes.js"); -include("common/Runewords.js"); -include("common/Town.js"); // Include SoloPlay's librarys include("SoloPlay/Tools/Developer.js"); include("SoloPlay/Tools/Tracker.js"); @@ -38,7 +28,7 @@ function main () { let tickDelay = 0; let townChicken = false; let [action, profiles] = [[], []]; - const threads = ["libs/SoloPlay/SoloPlay.js", "libs/SoloPlay/Threads/TownChicken.js", "tools/antihostile.js", "tools/party.js"]; + const threads = ["libs/SoloPlay/SoloPlay.js", "libs/SoloPlay/Threads/TownChicken.js", "threads/antihostile.js", "threads/party.js"]; console.log("ÿc8Kolbot-SoloPlayÿc0: Start EventThread"); D2Bot.init(); diff --git a/libs/SoloPlay/Threads/ToolsThread.js b/libs/SoloPlay/Threads/ToolsThread.js index e38f1c50..6a12ddbd 100644 --- a/libs/SoloPlay/Threads/ToolsThread.js +++ b/libs/SoloPlay/Threads/ToolsThread.js @@ -5,28 +5,16 @@ * */ js_strict(true); +include("critical.js"); + +// globals needed for core gameplay +includeCoreLibs({ exclude: ["Storage.js"]}); +include("core/Common/Tools.js"); + +// system libs +includeSystemLibs(); +include("systems/mulelogger/MuleLogger.js"); -include("json2.js"); -include("NTItemParser.dbl"); -include("OOG.js"); -include("AutoMule.js"); -include("Gambling.js"); -include("CraftingSystem.js"); -include("TorchSystem.js"); -include("MuleLogger.js"); -include("common/Attack.js"); -include("common/Common.js"); -include("common/Cubing.js"); -include("common/CollMap.js"); -include("common/Config.js"); -include("common/misc.js"); -include("common/util.js"); -include("common/Pickit.js"); -include("common/Pather.js"); -include("common/Precast.js"); -include("common/Prototypes.js"); -include("common/Runewords.js"); -include("common/Town.js"); // Include SoloPlay's librarys include("SoloPlay/Tools/Developer.js"); include("SoloPlay/Tools/Tracker.js"); @@ -35,6 +23,10 @@ include("SoloPlay/Tools/SoloIndex.js"); include("SoloPlay/Functions/ConfigOverrides.js"); include("SoloPlay/Functions/Globals.js"); +/** + * @todo trim the uneeded files/global variables from this file + */ + function main () { let ironGolem, tick, quitListDelayTime; let canQuit = true; @@ -70,7 +62,7 @@ function main () { // General functions this.togglePause = function () { - let scripts = ["libs/SoloPlay/SoloPlay.js", "libs/SoloPlay/Threads/TownChicken.js", "tools/antihostile.js", "tools/party.js"]; + let scripts = ["libs/SoloPlay/SoloPlay.js", "libs/SoloPlay/Threads/TownChicken.js", "threads/antihostile.js", "threads/party.js"]; for (let l = 0; l < scripts.length; l += 1) { let script = getScript(scripts[l]); @@ -301,7 +293,7 @@ function main () { break; case sdk.keys.Insert: // reveal level - me.overhead("Revealing " + Pather.getAreaName(me.area)); + me.overhead("Revealing " + getAreaName(me.area)); revealLevel(true); break; @@ -503,9 +495,15 @@ function main () { break; case msg.toLowerCase() === "test": - console.debug(sdk.colors.Green + "//-----------DataDump Start-----------//\nÿc8MainData ::\n", - myData, "\nÿc8BuffData ::\n", CharData.buffData, "\nÿc8SkillData ::\n", CharData.skillData, "\n" + sdk.colors.Red + "//-----------DataDump End-----------//"); - updated = true; + { + console.debug(sdk.colors.Green + "//-----------DataDump Start-----------//", + "\nÿc8ThreadData ::\n", getScript(true), + "\nÿc8MainData ::\n", myData, + "\nÿc8BuffData ::\n", CharData.buffData, + "\nÿc8SkillData ::\n", CharData.skillData, + "\nÿc8GlobalVariabls ::\n", Object.keys(global), + "\n" + sdk.colors.Red + "//-----------DataDump End-----------//"); + } break; } @@ -555,7 +553,7 @@ function main () { }; // Cache variables to prevent a bug where d2bs loses the reference to Config object - Config = Misc.copy(Config); + Config = copyObj(Config); tick = getTickCount(); addEventListener("keyup", this.keyEvent); @@ -577,6 +575,9 @@ function main () { console.warn("Without logPerformance set, the overlay will only show partial values"); } + // getUnit test + getUnit(-1) === null && console.warn("getUnit bug detected"); + // Start while (true) { try { @@ -586,7 +587,7 @@ function main () { Config.UseRejuvHP > 0 && me.hpPercent < Config.UseRejuvHP && this.drinkPotion(Common.Toolsthread.pots.Rejuv); if (Config.LifeChicken > 0 && me.hpPercent <= Config.LifeChicken && !me.inTown) { - !Developer.hideChickens && D2Bot.printToConsole("Life Chicken (" + me.hp + "/" + me.hpmax + ")" + Attack.getNearestMonster() + " in " + Pather.getAreaName(me.area) + ". Ping: " + me.ping, sdk.colors.D2Bot.Red); + !Developer.hideChickens && D2Bot.printToConsole("Life Chicken (" + me.hp + "/" + me.hpmax + ")" + Attack.getNearestMonster() + " in " + getAreaName(me.area) + ". Ping: " + me.ping, sdk.colors.D2Bot.Red); this.exit(true); break; @@ -600,7 +601,7 @@ function main () { [sdk.states.Frozen, sdk.states.FrozenSolid].some(state => me.getState(state)) && this.drinkSpecialPotion(sdk.items.ThawingPotion); if (Config.ManaChicken > 0 && me.mpPercent <= Config.ManaChicken && !me.inTown) { - !Developer.hideChickens && D2Bot.printToConsole("Mana Chicken: (" + me.mp + "/" + me.mpmax + ") in " + Pather.getAreaName(me.area), sdk.colors.D2Bot.Red); + !Developer.hideChickens && D2Bot.printToConsole("Mana Chicken: (" + me.mp + "/" + me.mpmax + ") in " + getAreaName(me.area), sdk.colors.D2Bot.Red); this.exit(true); break; @@ -614,7 +615,7 @@ function main () { if (ironGolem) { // ironGolem.hpmax is bugged with BO if (ironGolem.hp <= Math.floor(128 * Config.IronGolemChicken / 100)) { - !Developer.hideChickens && D2Bot.printToConsole("Irom Golem Chicken in " + Pather.getAreaName(me.area), sdk.colors.D2Bot.Red); + !Developer.hideChickens && D2Bot.printToConsole("Irom Golem Chicken in " + getAreaName(me.area), sdk.colors.D2Bot.Red); this.exit(true); break; @@ -629,7 +630,7 @@ function main () { if (mercHP > 0 && merc.mode !== sdk.monsters.mode.Dead) { if (mercHP < Config.MercChicken) { - !Developer.hideChickens && D2Bot.printToConsole("Merc Chicken in " + Pather.getAreaName(me.area), sdk.colors.D2Bot.Red); + !Developer.hideChickens && D2Bot.printToConsole("Merc Chicken in " + getAreaName(me.area), sdk.colors.D2Bot.Red); this.exit(true); break; @@ -684,8 +685,8 @@ function main () { !!restart && this.restart(); - if (debugInfo.area !== Pather.getAreaName(me.area)) { - debugInfo.area = Pather.getAreaName(me.area); + if (debugInfo.area !== getAreaName(me.area)) { + debugInfo.area = getAreaName(me.area); DataFile.updateStats("debugInfo", JSON.stringify(debugInfo)); } diff --git a/libs/SoloPlay/Threads/TownChicken.js b/libs/SoloPlay/Threads/TownChicken.js index 2b912176..977c4dda 100644 --- a/libs/SoloPlay/Threads/TownChicken.js +++ b/libs/SoloPlay/Threads/TownChicken.js @@ -5,25 +5,15 @@ * */ js_strict(true); +include("critical.js"); + +// globals needed for core gameplay +includeCoreLibs({ exclude: ["Storage.js"]}); + +// system libs +includeSystemLibs(); +include("systems/mulelogger/MuleLogger.js"); -include("json2.js"); -include("NTItemParser.dbl"); -include("OOG.js"); -include("Gambling.js"); -include("CraftingSystem.js"); -include("common/util.js"); -include("common/Attack.js"); -include("common/Common.js"); -include("common/Cubing.js"); -include("common/Config.js"); -include("common/CollMap.js"); -include("common/misc.js"); -include("common/Pickit.js"); -include("common/Pather.js"); -include("common/Precast.js"); -include("common/Prototypes.js"); -include("common/Runewords.js"); -include("common/Town.js"); // Include SoloPlay's librarys include("SoloPlay/Tools/Developer.js"); include("SoloPlay/Tools/Tracker.js"); @@ -297,13 +287,13 @@ function main() { } } - console.log("ÿc8End ÿc0:: ÿc8visitTown - currentArea: " + Pather.getAreaName(me.area)); + console.log("ÿc8End ÿc0:: ÿc8visitTown - currentArea: " + getAreaName(me.area)); return me.area === preArea; }; this.togglePause = function () { - let scripts = ["libs/SoloPlay/SoloPlay.js", "tools/antihostile.js"]; + let scripts = ["libs/SoloPlay/SoloPlay.js", "threads/antihostile.js"]; for (let i = 0; i < scripts.length; i++) { let script = getScript(scripts[i]); @@ -352,7 +342,7 @@ function main() { switch (me.area) { case sdk.areas.ArreatSummit: case sdk.areas.UberTristram: - console.warn("Don't tp from " + Pather.getAreaName(me.area)); + console.warn("Don't tp from " + getAreaName(me.area)); return; default: console.log("townCheck message recieved. First check passed."); @@ -435,7 +425,7 @@ function main() { try { myPrint("ÿc8TownChicken :: ÿc0Going to town"); Messaging.sendToScript("libs/SoloPlay/Threads/EventThread.js", "townchickenOn"); - [Attack.stopClear, SoloEvents.townChicken] = [true, true]; + [Attack.stopClear, SoloEvents.townChicken.running] = [true, true]; // determine if this is really worth it if (useHowl || useTerror) { @@ -461,7 +451,7 @@ function main() { console.log("ÿc8TownChicken :: Took: " + Time.format(getTickCount() - t4) + " to visit town"); this.togglePause(); Messaging.sendToScript("libs/SoloPlay/Threads/EventThread.js", "townchickenOff"); - [Attack.stopClear, SoloEvents.townChicken, townCheck, fastTown] = [false, false, false, false]; + [Attack.stopClear, SoloEvents.townChicken.running, townCheck, fastTown] = [false, false, false, false]; } } diff --git a/libs/SoloPlay/Tools/CharData.js b/libs/SoloPlay/Tools/CharData.js index 127c7359..48cd9ecc 100644 --- a/libs/SoloPlay/Tools/CharData.js +++ b/libs/SoloPlay/Tools/CharData.js @@ -66,7 +66,7 @@ const CharData = { folder && folder.create(me.profile); } - Misc.fileAction(this.filePath, 1, string); + FileAction.write(this.filePath, string); return obj; }, @@ -75,7 +75,7 @@ const CharData = { if (!FileTools.exists(this.filePath)) return CharData.loginData.create(); let obj; - let string = Misc.fileAction(this.filePath, 0); + let string = FileAction.read(this.filePath); try { obj = JSON.parse(string); @@ -89,7 +89,7 @@ const CharData = { getStats: function () { let obj = this.getObj(); - return Misc.clone(obj); + return clone(obj); }, updateData: function (arg, property, value) { @@ -99,12 +99,12 @@ const CharData = { if (typeof property === "object") { obj = Object.assign(obj, property); - return Misc.fileAction(this.filePath, 1, JSON.stringify(obj, null, 2)); + return FileAction.write(this.filePath, JSON.stringify(obj, null, 2)); } if (!!obj[arg] && obj[arg].hasOwnProperty(property)) { obj[arg][property] = value; - return Misc.fileAction(this.filePath, 1, JSON.stringify(obj, null, 2)); + return FileAction.write(this.filePath, JSON.stringify(obj, null, 2)); } return false; @@ -185,7 +185,7 @@ const CharData = { }, update: function () { - const obj = JSON.stringify(Misc.copy(this)); + const obj = JSON.stringify(copyObj(this)); const myThread = getScript(true).name; CharData.threads.forEach(function (script) { let curr = getScript(script); @@ -238,7 +238,7 @@ const CharData = { }, update: function () { - let obj = JSON.stringify(Misc.copy(this)); + let obj = JSON.stringify(copyObj(this)); let myThread = getScript(true).name; CharData.threads.forEach(function (script) { let curr = getScript(script); @@ -261,7 +261,7 @@ const CharData = { // updates config obj across all threads - excluding our current updateConfig: function () { - let obj = JSON.stringify(Misc.copy(Config)); + let obj = JSON.stringify(copyObj(Config)); let myThread = getScript(true).name; CharData.threads.forEach(function (script) { let curr = getScript(script); @@ -271,6 +271,10 @@ const CharData = { }); }, + /** + * + * @returns {charData} + */ create: function () { let obj = Object.assign({}, this.default); let string = JSON.stringify(obj, null, 2); @@ -280,16 +284,20 @@ const CharData = { folder && folder.create(me.profile); } - Misc.fileAction(this.filePath, 1, string); + FileAction.write(this.filePath, string); return obj; }, + /** + * + * @returns {charData} + */ getObj: function () { if (!FileTools.exists(this.filePath)) return CharData.create(); let obj; - let string = Misc.fileAction(this.filePath, 0); + let string = FileAction.read(this.filePath); try { obj = JSON.parse(string); @@ -301,9 +309,13 @@ const CharData = { return obj ? obj : this.default; }, + /** + * + * @returns {charData} + */ getStats: function () { let obj = this.getObj(); - return Misc.clone(obj); + return clone(obj); }, updateData: function (arg, property, value) { @@ -319,12 +331,12 @@ const CharData = { if (typeof property === "object") { obj = Object.assign(obj, property); - return Misc.fileAction(this.filePath, 1, JSON.stringify(obj, null, 2)); + return FileAction.write(this.filePath, JSON.stringify(obj, null, 2)); } if (!!obj[arg] && obj[arg].hasOwnProperty(property)) { obj[arg][property] = value; - return Misc.fileAction(this.filePath, 1, JSON.stringify(obj, null, 2)); + return FileAction.write(this.filePath, JSON.stringify(obj, null, 2)); } return false; diff --git a/libs/SoloPlay/Tools/Developer.js b/libs/SoloPlay/Tools/Developer.js index 980508ab..b5bbd82c 100644 --- a/libs/SoloPlay/Tools/Developer.js +++ b/libs/SoloPlay/Tools/Developer.js @@ -5,6 +5,11 @@ * */ +/** + * @todo + * - add override to GlobalAccount here to allow per profile options + * - add name choices in similar manner, would have to experiment with max lengths allowed as a prefix + */ const Developer = { // @desc - set to true if using the PlugY mod - allows use of larger stash plugyMode: false, @@ -49,6 +54,11 @@ const Developer = { // Enter in the profiles that you wish to start in developermode, i.e "scl-sorc" profiles: [""], }, + testingMode: { + enabled: false, + // Enter in the profiles that you wish to start in testing mode, i.e "scl-sorc" + profiles: [""], + }, // @desc [experimental don't use] - set email during account creation setEmail: { enabled: false, diff --git a/libs/SoloPlay/Tools/Tracker.js b/libs/SoloPlay/Tools/Tracker.js index b7d5fe41..79990420 100644 --- a/libs/SoloPlay/Tools/Tracker.js +++ b/libs/SoloPlay/Tools/Tracker.js @@ -5,9 +5,9 @@ * */ +includeIfNotIncluded("core/experience.js"); includeIfNotIncluded("SoloPlay/Tools/Developer.js"); includeIfNotIncluded("SoloPlay/Functions/PrototypeOverrides.js"); -includeIfNotIncluded("SoloPlay/Functions/MiscOverrides.js"); const Tracker = { GTPath: "libs/SoloPlay/Data/" + me.profile + "/" + me.profile + "-GameTime.json", @@ -50,8 +50,8 @@ const Tracker = { this.resetGameTime(); // for now just re-init the header so it's easier to look at the file and see where we restarted // might later save the files to a sub folder and re-init a new one - FileTools.exists(this.LPPath) && Misc.fileAction(this.LPPath, 2, this.LPHeader); - FileTools.exists(this.SPPath) && Misc.fileAction(this.SPPath, 2, this.SPHeader); + FileTools.exists(this.LPPath) && FileAction.append(this.LPPath, this.LPHeader); + FileTools.exists(this.SPPath) && FileAction.append(this.SPPath, this.SPHeader); }, checkValidity: function () { @@ -101,7 +101,7 @@ const Tracker = { + "," + GOLD + "," + FR + "," + CR + "," + LR + "," + PR + "," + currentBuild + "\n" ); - Misc.fileAction(Tracker.SPPath, 2, string); + FileAction.append(Tracker.SPPath, string); this.tick = GameTracker.LastSave; return true; @@ -125,7 +125,7 @@ const Tracker = { // csv file const diffString = sdk.difficulty.nameOf(me.diff); - const areaName = Pather.getAreaName(me.area); + const areaName = getAreaName(me.area); const currentBuild = SetUp.currentBuild; const gainAMT = me.getStat(sdk.stats.Experience) - Experience.totalExp[me.charlvl - 1]; const gainTime = gainAMT / (splitTime / 60000); @@ -136,14 +136,14 @@ const Tracker = { + GOLD + "," + FR + "," + CR + "," + LR + "," + PR + "," + currentBuild + "\n" ); - Misc.fileAction(Tracker.LPPath, 2, string); + FileAction.append(Tracker.LPPath, string); this.tick = GameTracker.LastSave; return true; }, update: function (oogTick = 0) { - let heartBeat = getScript("tools/heartbeat.js"); + let heartBeat = getScript("threads/heartbeat.js"); if (!heartBeat) { console.debug("Couldn't find heartbeat"); return false; From fb413221388b075e58d12296e7988e2e5a5eb850 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Sat, 4 Feb 2023 00:35:38 -0500 Subject: [PATCH 007/263] Moved Developer functions into Tracker - Don't mix config file with logic file. The developer functions getObj, readObj, and writeObj were more for the tracker anyway --- D2BotSoloPlay.dbj | 8 +- libs/SoloPlay/Functions/Globals.js | 8 +- libs/SoloPlay/Functions/ItemOverrides.js | 6 +- libs/SoloPlay/Functions/LoaderOverrides.js | 6 +- libs/SoloPlay/Threads/ToolsThread.js | 2 +- libs/SoloPlay/Tools/Developer.js | 65 ------------- libs/SoloPlay/Tools/Overlay.js | 12 +-- libs/SoloPlay/Tools/Tracker.js | 108 ++++++++++++++++----- 8 files changed, 107 insertions(+), 108 deletions(-) diff --git a/D2BotSoloPlay.dbj b/D2BotSoloPlay.dbj index 7d52e35d..a58ec7cb 100644 --- a/D2BotSoloPlay.dbj +++ b/D2BotSoloPlay.dbj @@ -171,10 +171,10 @@ function timer (tick) { if (Developer.displayClockInConsole && Developer.logPerformance) { try { - gameTracker === undefined && (gameTracker = Developer.readObj(Tracker.GTPath)); + gameTracker === undefined && (gameTracker = Tracker.readObj(Tracker.GTPath)); let [tTime, tInGame, tDays] = [(gameTracker.Total + currInGame), (gameTracker.InGame + currInGame), (gameTracker.Total + currInGame)]; - let [totalTime, totalInGame, totalDays] = [Developer.formatTime(tTime), Developer.formatTime(tInGame), Developer.totalDays(tDays)]; - timeStr += ("(Days: " + totalDays + ") (Total: " + totalTime + ") (IG: " + totalInGame + ") (OOG: " + Developer.formatTime(gameTracker.OOG) + ")"); + let [totalTime, totalInGame, totalDays] = [Tracker.formatTime(tTime), Tracker.formatTime(tInGame), Tracker.totalDays(tDays)]; + timeStr += ("(Days: " + totalDays + ") (Total: " + totalTime + ") (IG: " + totalInGame + ") (OOG: " + Tracker.formatTime(gameTracker.OOG) + ")"); } catch (e) { console.log(e); } @@ -392,7 +392,7 @@ function main () { DataFile.updateStats("runs", Starter.gameCount); DataFile.updateStats("ingameTick"); Developer.logPerformance && Tracker.update((getTickCount() - oogTick)); - Developer.displayClockInConsole && (gameTracker = Developer.readObj(Tracker.GTPath)); + Developer.displayClockInConsole && (gameTracker = Tracker.readObj(Tracker.GTPath)); oogTick = 0; } diff --git a/libs/SoloPlay/Functions/Globals.js b/libs/SoloPlay/Functions/Globals.js index cc9904be..df404fbf 100644 --- a/libs/SoloPlay/Functions/Globals.js +++ b/libs/SoloPlay/Functions/Globals.js @@ -302,11 +302,11 @@ const SetUp = { includeIfNotIncluded("SoloPlay/Tools/NameGen.js"); includeIfNotIncluded("SoloPlay/Tools/Tracker.js"); let gameObj, printTotalTime = Developer.logPerformance; - printTotalTime && (gameObj = Developer.readObj(Tracker.GTPath)); + printTotalTime && (gameObj = Tracker.readObj(Tracker.GTPath)); // log info myPrint(this.finalBuild + " goal reached. On to the next."); - D2Bot.printToConsole("Kolbot-SoloPlay: " + this.finalBuild + " goal reached" + (printTotalTime ? " (" + (Developer.formatTime(gameObj.Total + Developer.timer(gameObj.LastSave))) + "). " : ". ") + "Making next...", sdk.colors.D2Bot.Gold); + D2Bot.printToConsole("Kolbot-SoloPlay: " + this.finalBuild + " goal reached" + (printTotalTime ? " (" + (Tracker.formatTime(gameObj.Total + Tracker.timer(gameObj.LastSave))) + "). " : ". ") + "Making next...", sdk.colors.D2Bot.Gold); D2Bot.setProfile(null, null, NameGen()); CharData.delete(true); @@ -1016,7 +1016,7 @@ const Check = { } if (goalReached) { - const gameObj = Developer.logPerformance ? Developer.readObj(Tracker.GTPath) : null; + const gameObj = Developer.logPerformance ? Tracker.readObj(Tracker.GTPath) : null; switch (true) { case (SetUp.finalBuild === "Bumper" && Developer.fillAccount.bumpers): @@ -1025,7 +1025,7 @@ const Check = { break; default: - D2Bot.printToConsole("Kolbot-SoloPlay " + goal + " goal reached." + (gameObj ? " (" + (Developer.formatTime(gameObj.Total + Developer.timer(gameObj.LastSave))) + ")" : ""), sdk.colors.D2Bot.Gold); + D2Bot.printToConsole("Kolbot-SoloPlay " + goal + " goal reached." + (gameObj ? " (" + (Tracker.formatTime(gameObj.Total + Tracker.timer(gameObj.LastSave))) + ")" : ""), sdk.colors.D2Bot.Gold); Developer.logPerformance && Tracker.update(); D2Bot.stop(); } diff --git a/libs/SoloPlay/Functions/ItemOverrides.js b/libs/SoloPlay/Functions/ItemOverrides.js index a37408da..f61c0f9d 100644 --- a/libs/SoloPlay/Functions/ItemOverrides.js +++ b/libs/SoloPlay/Functions/ItemOverrides.js @@ -328,7 +328,7 @@ Item.autoEquip = function (task = "") { } } - console.log("ÿc8Kolbot-SoloPlayÿc0: Exiting ÿc9" + task + "ÿc0. Time elapsed: " + Developer.formatTime(getTickCount() - tick)); + console.log("ÿc8Kolbot-SoloPlayÿc0: Exiting ÿc9" + task + "ÿc0. Time elapsed: " + Tracker.formatTime(getTickCount() - tick)); return true; }; @@ -583,7 +583,7 @@ Item.autoEquipSecondary = function (task = "") { } } - console.log("ÿc8Kolbot-SoloPlayÿc0: Exiting secondary auto equip. Time elapsed: " + Developer.formatTime(getTickCount() - tick)); + console.log("ÿc8Kolbot-SoloPlayÿc0: Exiting secondary auto equip. Time elapsed: " + Tracker.formatTime(getTickCount() - tick)); return true; }; @@ -1320,7 +1320,7 @@ Item.removeItemsMerc = function () { me.cancelUIFlags(); - console.log("ÿc8Kolbot-SoloPlayÿc0: Exiting charm auto equip. Time elapsed: " + Developer.formatTime(getTickCount() - tick)); + console.log("ÿc8Kolbot-SoloPlayÿc0: Exiting charm auto equip. Time elapsed: " + Tracker.formatTime(getTickCount() - tick)); }; // Write charm equip version that checks by item prefix/suffix using a switch case with the various prefixes and suffixes to sort them diff --git a/libs/SoloPlay/Functions/LoaderOverrides.js b/libs/SoloPlay/Functions/LoaderOverrides.js index c212c9c4..1047131c 100644 --- a/libs/SoloPlay/Functions/LoaderOverrides.js +++ b/libs/SoloPlay/Functions/LoaderOverrides.js @@ -74,10 +74,10 @@ Loader.run = function () { SoloIndex.doneList.push(scriptName); // skip logging if we didn't actually finish it !SoloIndex.retryList.includes(scriptName) && Developer.logPerformance && Tracker.script(tick, scriptName, currentExp); - console.log("ÿc8Kolbot-SoloPlayÿc0: Old maxgametime: " + Developer.formatTime(me.maxgametime)); + console.log("ÿc8Kolbot-SoloPlayÿc0: Old maxgametime: " + Tracker.formatTime(me.maxgametime)); me.maxgametime += (getTickCount() - tick); - console.log("ÿc8Kolbot-SoloPlayÿc0: New maxgametime: " + Developer.formatTime(me.maxgametime)); - console.log("ÿc8Kolbot-SoloPlayÿc0 :: ÿc8" + scriptName + "ÿc0 - ÿc7Duration: ÿc0" + Developer.formatTime(getTickCount() - tick)); + console.log("ÿc8Kolbot-SoloPlayÿc0: New maxgametime: " + Tracker.formatTime(me.maxgametime)); + console.log("ÿc8Kolbot-SoloPlayÿc0 :: ÿc8" + scriptName + "ÿc0 - ÿc7Duration: ÿc0" + Tracker.formatTime(getTickCount() - tick)); // remove script function from function scope, so it can be cleared by GC if (this.scriptIndex < SoloIndex.scripts.length) { diff --git a/libs/SoloPlay/Threads/ToolsThread.js b/libs/SoloPlay/Threads/ToolsThread.js index 6a12ddbd..367b60a6 100644 --- a/libs/SoloPlay/Threads/ToolsThread.js +++ b/libs/SoloPlay/Threads/ToolsThread.js @@ -104,7 +104,7 @@ function main () { chickenExit && D2Bot.updateChickens(); Config.LogExperience && Experience.log(); Developer.logPerformance && Tracker.update(); - console.log("ÿc8Run duration ÿc2" + Developer.formatTime(getTickCount() - me.gamestarttime)); + console.log("ÿc8Run duration ÿc2" + Tracker.formatTime(getTickCount() - me.gamestarttime)); this.stopDefault(); quit(); }; diff --git a/libs/SoloPlay/Tools/Developer.js b/libs/SoloPlay/Tools/Developer.js index b5bbd82c..6dddfc0a 100644 --- a/libs/SoloPlay/Tools/Developer.js +++ b/libs/SoloPlay/Tools/Developer.js @@ -84,71 +84,6 @@ const Developer = { profiles: [""], }, }, - - /* Developer tools */ - getObj: function (path) { - let obj, OBJstring = Misc.fileAction(path, 0); - - try { - obj = JSON.parse(OBJstring); - } catch (e) { - // If we failed, file might be corrupted, so create a new one - Misc.errorReport(e, "Developer"); - FileTools.remove(path); - Tracker.initialize(); - OBJstring = Misc.fileAction(path, 0); - obj = JSON.parse(OBJstring); - } - - if (obj) { - return obj; - } - - console.error("ÿc8Kolbot-SoloPlayÿc0: Failed to read Obj. (Developer.parseObj)"); - - return false; - }, - - readObj: function (jsonPath) { - let obj = this.getObj(jsonPath); - return Misc.clone(obj); - }, - - writeObj: function (obj, path) { - let string; - try { - string = JSON.stringify(obj, null, 2); - // try to parse the string to ensure it converted correctly - JSON.parse(string); - // JSON.parse throws an error if it fails so if we are here now we are good - Misc.fileAction(path, 1, string); - } catch (e) { - console.warn("Malformed JSON object"); - Misc.errorReport(e, "Developer"); - return false; - } - - return true; - }, - - timer: function (tick) { - return getTickCount() - tick; - }, - - formatTime: function (milliseconds) { - let seconds = milliseconds / 1000; - let sec = (seconds % 60).toFixed(0); - let minutes = (Math.floor(seconds / 60) % 60).toFixed(0); - let hours = Math.floor(seconds / 3600).toFixed(0); - let timeString = hours.toString().padStart(2, "0") + ":" + minutes.toString().padStart(2, "0") + ":" + sec.toString().padStart(2, "0"); - - return timeString; - }, - - totalDays: function (milliseconds) { - let days = Math.floor(milliseconds / 86.4e6).toFixed(0); - return days.toString().padStart(1, "0"); - }, }; // Set after Developer has been initialized - always load guard in developer mode diff --git a/libs/SoloPlay/Tools/Overlay.js b/libs/SoloPlay/Tools/Overlay.js index ad76c1dc..c50b7bc0 100644 --- a/libs/SoloPlay/Tools/Overlay.js +++ b/libs/SoloPlay/Tools/Overlay.js @@ -22,20 +22,20 @@ const Overlay = { level: () => myData.me.level, text: { hooks: [], - GameTracker: Developer.readObj(Tracker.GTPath), + GameTracker: Tracker.readObj(Tracker.GTPath), enabled: true, charlvl: 0, tick: 0, clock: function () { if (!Developer.logPerformance) return ""; - this.GameTracker === undefined && (this.GameTracker = Developer.readObj(Tracker.GTPath)); + this.GameTracker === undefined && (this.GameTracker = Tracker.readObj(Tracker.GTPath)); this.tick = getTickCount(); let currInGame = getTickCount() - me.gamestarttime; - let totalTime = Developer.formatTime(this.GameTracker.Total + currInGame); - let totalInGame = Developer.formatTime(this.GameTracker.InGame + currInGame); + let totalTime = Tracker.formatTime(this.GameTracker.Total + currInGame); + let totalInGame = Tracker.formatTime(this.GameTracker.InGame + currInGame); - return ("Total: ÿc0" + totalTime + "ÿc4 InGame: ÿc0" + totalInGame + "ÿc4 OOG: ÿc0" + Developer.formatTime(this.GameTracker.OOG)); + return ("Total: ÿc0" + totalTime + "ÿc4 InGame: ÿc0" + totalInGame + "ÿc4 OOG: ÿc0" + Tracker.formatTime(this.GameTracker.OOG)); }, timer: function () { @@ -64,7 +64,7 @@ const Overlay = { } if (Developer.logPerformance) { - this.GameTracker === undefined && (this.GameTracker = Developer.readObj(Tracker.GTPath)); + this.GameTracker === undefined && (this.GameTracker = Tracker.readObj(Tracker.GTPath)); if (!this.getHook("times")) { this.add("times"); } else { diff --git a/libs/SoloPlay/Tools/Tracker.js b/libs/SoloPlay/Tools/Tracker.js index 79990420..2a465a2c 100644 --- a/libs/SoloPlay/Tools/Tracker.js +++ b/libs/SoloPlay/Tools/Tracker.js @@ -35,15 +35,60 @@ const Tracker = { folder.create(me.profile); } - !FileTools.exists(this.GTPath) && Developer.writeObj(GameTracker, this.GTPath); - !FileTools.exists(this.LPPath) && Misc.fileAction(this.LPPath, 1, this.LPHeader); - !FileTools.exists(this.SPPath) && Misc.fileAction(this.SPPath, 1, this.SPHeader); + !FileTools.exists(this.GTPath) && Tracker.writeObj(GameTracker, this.GTPath); + !FileTools.exists(this.LPPath) && FileAction.write(this.LPPath, this.LPHeader); + !FileTools.exists(this.SPPath) && FileAction.write(this.SPPath, this.SPHeader); + + return true; + }, + + getObj: function (path) { + let obj, OBJstring = FileAction.read(path); + + try { + obj = JSON.parse(OBJstring); + } catch (e) { + // If we failed, file might be corrupted, so create a new one + Misc.errorReport(e, "Tracker"); + FileTools.remove(path); + Tracker.initialize(); + OBJstring = FileAction.read(path); + obj = JSON.parse(OBJstring); + } + + if (obj) { + return obj; + } + + console.error("ÿc8Kolbot-SoloPlayÿc0: Failed to read Obj. (Tracker.getObj)"); + + return false; + }, + + readObj: function (jsonPath) { + let obj = this.getObj(jsonPath); + return clone(obj); + }, + + writeObj: function (obj, path) { + let string; + try { + string = JSON.stringify(obj, null, 2); + // try to parse the string to ensure it converted correctly + JSON.parse(string); + // JSON.parse throws an error if it fails so if we are here now we are good + FileAction.write(path, string); + } catch (e) { + console.warn("Malformed JSON object"); + console.error(e); + return false; + } return true; }, resetGameTime: function () { - Developer.writeObj(Object.assign({}, this.default), this.GTPath); + Tracker.writeObj(Object.assign({}, this.default), this.GTPath); }, reset: function () { @@ -55,7 +100,7 @@ const Tracker = { }, checkValidity: function () { - const GameTracker = Developer.readObj(this.GTPath); + const GameTracker = Tracker.readObj(this.GTPath); let found = false; GameTracker && Object.keys(GameTracker).forEach(function (key) { if (GameTracker[key] < 0) { @@ -64,7 +109,26 @@ const Tracker = { found = true; } }); - found && Developer.writeObj(GameTracker, this.GTPath); + found && Tracker.writeObj(GameTracker, this.GTPath); + }, + + timer: function (tick) { + return getTickCount() - tick; + }, + + formatTime: function (milliseconds) { + let seconds = milliseconds / 1000; + let sec = (seconds % 60).toFixed(0); + let minutes = (Math.floor(seconds / 60) % 60).toFixed(0); + let hours = Math.floor(seconds / 3600).toFixed(0); + let timeString = hours.toString().padStart(2, "0") + ":" + minutes.toString().padStart(2, "0") + ":" + sec.toString().padStart(2, "0"); + + return timeString; + }, + + totalDays: function (milliseconds) { + let days = Math.floor(milliseconds / 86.4e6).toFixed(0); + return days.toString().padStart(1, "0"); }, logLeveling: function (obj) { @@ -74,20 +138,20 @@ const Tracker = { }, script: function (starttime, subscript, startexp) { - const GameTracker = Developer.readObj(Tracker.GTPath); + const GameTracker = Tracker.readObj(Tracker.GTPath); // GameTracker // this seems to happen when my pc restarts so set last save equal to current tick count and then continue GameTracker.LastSave > getTickCount() && (GameTracker.LastSave = getTickCount()); const newTick = me.gamestarttime >= GameTracker.LastSave ? me.gamestarttime : GameTracker.LastSave; - GameTracker.InGame += Developer.timer(newTick); - GameTracker.Total += Developer.timer(newTick); + GameTracker.InGame += Tracker.timer(newTick); + GameTracker.Total += Tracker.timer(newTick); GameTracker.LastSave = getTickCount(); - Developer.writeObj(GameTracker, Tracker.GTPath); + Tracker.writeObj(GameTracker, Tracker.GTPath); // csv file - const scriptTime = Developer.timer(starttime); + const scriptTime = Tracker.timer(starttime); const currLevel = me.charlvl; const diffString = sdk.difficulty.nameOf(me.diff); const gainAMT = me.getStat(sdk.stats.Experience) - startexp; @@ -96,7 +160,7 @@ const Tracker = { const currentBuild = SetUp.currentBuild; const [GOLD, FR, CR, LR, PR] = [me.gold, me.realFR, me.realCR, me.realLR, me.realPR]; const string = ( - Developer.formatTime(GameTracker.Total) + "," + Developer.formatTime(GameTracker.InGame) + "," + Developer.formatTime(scriptTime) + Tracker.formatTime(GameTracker.Total) + "," + Tracker.formatTime(GameTracker.InGame) + "," + Tracker.formatTime(scriptTime) + "," + subscript + "," + currLevel + "," + gainAMT + "," + gainTime + "," + gainPercent + "," + diffString + "," + GOLD + "," + FR + "," + CR + "," + LR + "," + PR + "," + currentBuild + "\n" ); @@ -108,7 +172,7 @@ const Tracker = { }, leveling: function () { - const GameTracker = Developer.readObj(this.GTPath); + const GameTracker = Tracker.readObj(this.GTPath); // GameTracker // this seems to happen when my pc restarts so set last save equal to current tick count and then continue @@ -116,12 +180,12 @@ const Tracker = { const newSave = getTickCount(); const newTick = me.gamestarttime > GameTracker.LastSave ? me.gamestarttime : GameTracker.LastSave; - const splitTime = Developer.timer(GameTracker.LastLevel); - GameTracker.InGame += Developer.timer(newTick); - GameTracker.Total += Developer.timer(newTick); + const splitTime = Tracker.timer(GameTracker.LastLevel); + GameTracker.InGame += Tracker.timer(newTick); + GameTracker.Total += Tracker.timer(newTick); GameTracker.LastLevel = newSave; GameTracker.LastSave = newSave; - Developer.writeObj(GameTracker, Tracker.GTPath); + Tracker.writeObj(GameTracker, Tracker.GTPath); // csv file const diffString = sdk.difficulty.nameOf(me.diff); @@ -131,7 +195,7 @@ const Tracker = { const gainTime = gainAMT / (splitTime / 60000); const [GOLD, FR, CR, LR, PR] = [me.gold, me.realFR, me.realCR, me.realLR, me.realPR]; const string = ( - Developer.formatTime(GameTracker.Total) + "," + Developer.formatTime(GameTracker.InGame) + "," + Developer.formatTime(splitTime) + "," + Tracker.formatTime(GameTracker.Total) + "," + Tracker.formatTime(GameTracker.InGame) + "," + Tracker.formatTime(splitTime) + "," + areaName + "," + me.charlvl + "," + gainAMT + "," + gainTime + "," + diffString + "," + GOLD + "," + FR + "," + CR + "," + LR + "," + PR + "," + currentBuild + "\n" ); @@ -153,7 +217,7 @@ const Tracker = { return false; } - const GameTracker = Developer.readObj(this.GTPath); + const GameTracker = Tracker.readObj(this.GTPath); // this seems to happen when my pc restarts so set last save equal to current tick count and then continue GameTracker.LastSave > getTickCount() && (GameTracker.LastSave = getTickCount()); @@ -162,10 +226,10 @@ const Tracker = { const newTick = me.gamestarttime > GameTracker.LastSave ? me.gamestarttime : GameTracker.LastSave; GameTracker.OOG += oogTick; - GameTracker.InGame += Developer.timer(newTick); - GameTracker.Total += (Developer.timer(newTick) + oogTick); + GameTracker.InGame += Tracker.timer(newTick); + GameTracker.Total += (Tracker.timer(newTick) + oogTick); GameTracker.LastSave = getTickCount(); - Developer.writeObj(GameTracker, Tracker.GTPath); + Tracker.writeObj(GameTracker, Tracker.GTPath); this.tick = GameTracker.LastSave; return true; From 0f8cb3f11c41293d3838f27ea18516784e3f06c2 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Sat, 4 Feb 2023 00:45:42 -0500 Subject: [PATCH 008/263] Turn NameGen into module and cleanup entry + oog + SoloEvents - NameGen is better served as a module we can call when needed rather than always being included - Move a lot of the logic out of D2BotSoloPlay.dbj and into OOGOverrides where it really should be - Wrapped the core logic in OOGOverrides in an IEFE to keep some of the loose functions block scoped - Turned SoloEvents into a UMD module --- D2BotSoloPlay.dbj | 243 +--- libs/SoloPlay/Functions/Globals.js | 3 +- libs/SoloPlay/Functions/SoloEvents.js | 756 +++++------ libs/SoloPlay/Tools/NameGen.js | 26 +- libs/SoloPlay/Tools/OOGOverrides.js | 1704 ++++++++++++++----------- 5 files changed, 1390 insertions(+), 1342 deletions(-) diff --git a/D2BotSoloPlay.dbj b/D2BotSoloPlay.dbj index a58ec7cb..3a3aaeaf 100644 --- a/D2BotSoloPlay.dbj +++ b/D2BotSoloPlay.dbj @@ -42,9 +42,18 @@ include("core/experience.js"); include("SoloPlay/Tools/Developer.js"); include("SoloPlay/Tools/CharData.js"); include("SoloPlay/Tools/Tracker.js"); -include("SoloPlay/Tools/NameGen.js"); include("SoloPlay/Tools/OOGOverrides.js"); -include("SoloPlay/Functions/SoloEvents.js"); + +// keep the namespace name but only grab the properties we need for this thread +const SoloEvents = (() => { + let { outOfGameCheck, check, gameInfo } = require("./libs/SoloPlay/Functions/SoloEvents"); + return { + check: check, + gameInfo: gameInfo, + outOfGameCheck: outOfGameCheck, + }; +})(); +// is this needed? soloplay doesn't run in default.dbj anymore include("SoloPlay/Functions/ConfigOverrides.js"); if (typeof Starter.AdvancedConfig[me.profile] === "object") { @@ -73,98 +82,6 @@ if (!FileTools.exists(CharData.filePath) && CharData.loginData.create()) { Developer.logPerformance && Tracker.initialize(); -new Overrides.Override(Starter, Starter.receiveCopyData, function (orignal, mode, msg) { - switch (mode) { - case 1: // Join Info - console.log("Got Join Info"); - joinInfo = JSON.parse(msg); - - SoloEvents.gameInfo.gameName = joinInfo.gameName.toLowerCase(); - SoloEvents.gameInfo.gamePass = joinInfo.gamePass.toLowerCase(); - - break; - case 1638: - try { - let obj = JSON.parse(msg); - Starter.profileInfo.profile = me.profile.toUpperCase(); - Starter.profileInfo.account = obj.Account; - Starter.profileInfo.password = ""; - Starter.profileInfo.charName = obj.Character; - Starter.profileInfo.tag = (obj.Tag.trim().capitalize(true) || ""); - Starter.profileInfo.difficulty = obj.Difficulty; - obj.Realm = obj.Realm.toLowerCase(); - Starter.profileInfo.realm = ["east", "west"].includes(obj.Realm) ? "us" + obj.Realm : obj.Realm; - - let buildCheck = Starter.profileInfo.profile.split("-"); // SCL-ZON123 - Starter.profileInfo.hardcore = buildCheck[0].includes("HC"); // SC softcore = false - Starter.profileInfo.expansion = buildCheck[0].indexOf("CC") === -1; // not CC so not classic - true - Starter.profileInfo.ladder = buildCheck[0].indexOf("NL") === -1; // not NL so its ladder - true - - if (buildCheck.length <= 1) { - D2Bot.printToConsole('Please update profile name. Example: "HCCNL-PAL" will make a Hardcore Classic NonLadder Paladin', sdk.colors.D2Bot.Gold); - D2Bot.printToConsole("If you are still confused please read the included readMe. https://github.com/blizzhackers/kolbot-SoloPlay/blob/main/README.md", sdk.colors.D2Bot.Gold); - D2Bot.stop(); - } - - const charClassMap = { - "ZON": "amazon", - "SOR": "sorceress", - "NEC": "necromancer", - "PAL": "paladin", - "BAR": "barbarian", - "DRU": "druid", - "SIN": "assassin" - }; - buildCheck[1] = buildCheck[1].toString().substring(0, 3); - - if (charClassMap[buildCheck[1]]) { - Starter.profileInfo.charClass = charClassMap[buildCheck[1]]; - } else { - throw new Error("Invalid profile name, couldn't set character class"); - } - - if (Starter.profileInfo.tag !== "") { - { - let soloStats = CharData.getStats(); - - if (!soloStats.me.finalBuild || soloStats.me.finalBuild !== Starter.profileInfo.tag) { - D2Bot.setProfile(null, null, null, null, null, Starter.profileInfo.tag); - soloStats.me.finalBuild = Starter.profileInfo.tag; - soloStats.me.charms = {}; - CharData.updateData("me", soloStats); - } - - if (!["Start", "Stepping", "Leveling"].includes(soloStats.me.currentBuild) && soloStats.me.currentBuild !== soloStats.me.finalBuild) { - soloStats.me.currentBuild = "Leveling"; - soloStats.me.charms = {}; - CharData.updateData("me", soloStats); - } - } - } else { - throw new Error("Please update profile InfoTag. Missing the finalBuild."); - } - } catch (e) { - Misc.errorReport(e, "D2BotSoloPlay.dbj"); - D2Bot.stop(); - } - - break; - default: - orignal(mode, msg); - } -}).apply(); - -new Overrides.Override(Starter, Starter.scriptMsgEvent, function (orignal, msg) { - if (typeof msg !== "string") return; - if (msg === "event") { - SoloEvents.check = true; - } else if (msg === "diffChange") { - Starter.checkDifficulty(); - } else { - orignal(msg); - } -}).apply(); - function timer (tick) { const currInGame = (getTickCount() - tick); let timeStr = " (Time: " + Time.format(currInGame) + ") "; @@ -182,142 +99,6 @@ function timer (tick) { return timeStr; } -const oogCheck = () => (AutoMule.outOfGameCheck() || TorchSystem.outOfGameCheck() || Gambling.outOfGameCheck() || CraftingSystem.outOfGameCheck() || SoloEvents.outOfGameCheck()); - -const locations = {}; -locations[sdk.game.locations.PreSplash] = () => ControlAction.click(); -locations[sdk.game.locations.GatewaySelect] = () => Controls.GatewayCancel.click(); -locations[sdk.game.locations.SplashScreen] = () => Starter.LocationEvents.login(); -locations[sdk.game.locations.MainMenu] = () => Starter.LocationEvents.login(); -locations[sdk.game.locations.Login] = () => Starter.LocationEvents.login(); -locations[sdk.game.locations.OtherMultiplayer] = () => Starter.LocationEvents.otherMultiplayerSelect(); -locations[sdk.game.locations.TcpIp] = () => Profile().type === sdk.game.profiletype.TcpIpHost ? Controls.TcpIpHost.click() : Controls.TcpIpCancel.click(); -locations[sdk.game.locations.TcpIpEnterIp] = () => Controls.TcpIpCancel.click(); -locations[sdk.game.locations.LoginError] = () => Starter.LocationEvents.loginError(); -locations[sdk.game.locations.LoginUnableToConnect] = () => Starter.LocationEvents.unableToConnect(); -locations[sdk.game.locations.TcpIpUnableToConnect] = () => Starter.LocationEvents.unableToConnect(); -locations[sdk.game.locations.CdKeyInUse] = () => Starter.LocationEvents.loginError(); -locations[sdk.game.locations.InvalidCdKey] = () => Starter.LocationEvents.loginError(); -locations[sdk.game.locations.RealmDown] = () => Starter.LocationEvents.realmDown(); -locations[sdk.game.locations.Disconnected] = () => { - ControlAction.timeoutDelay("Disconnected", 3000); - Controls.OkCentered.click(); -}; -locations[sdk.game.locations.RegisterEmail] = () => Controls.EmailDontRegisterContinue.control ? Controls.EmailDontRegisterContinue.click() : Controls.EmailDontRegister.click(); -locations[sdk.game.locations.MainMenuConnecting] = (loc) => !Starter.locationTimeout(Starter.Config.ConnectingTimeout * 1e3, loc) && Controls.LoginCancelWait.click(); -locations[sdk.game.locations.CharSelectPleaseWait] = (loc) => !Starter.locationTimeout(Starter.Config.PleaseWaitTimeout * 1e3, loc) && Controls.OkCentered.click(); -locations[sdk.game.locations.CharSelect] = (loc) => Starter.LocationEvents.charSelect(loc); -locations[sdk.game.locations.CharSelectConnecting] = (loc) => Starter.LocationEvents.charSelect(loc); -locations[sdk.game.locations.CharSelectNoChars] = (loc) => Starter.LocationEvents.charSelect(loc); -locations[sdk.game.locations.SelectDifficultySP] = () => Starter.LocationEvents.selectDifficultySP(); -locations[sdk.game.locations.CharacterCreate] = (loc) => !Starter.locationTimeout(5e3, loc) && Controls.CharSelectExit.click(); -locations[sdk.game.locations.ServerDown] = () => { - ControlAction.timeoutDelay("Server Down", Time.minutes(5)); - Controls.OkCentered.click(); -}; -locations[sdk.game.locations.LobbyPleaseWait] = (loc) => !Starter.locationTimeout(Starter.Config.PleaseWaitTimeout * 1e3, loc) && Controls.OkCentered.click(); -locations[sdk.game.locations.Lobby] = () => { - D2Bot.updateStatus("Lobby"); - ControlAction.saveInfo(Starter.profileInfo); - - me.blockKeys = false; - - !Starter.firstLogin && (Starter.firstLogin = true); - Starter.lastGameStatus === "pending" && (Starter.gameCount += 1); - - if (Starter.Config.PingQuitDelay && Starter.pingQuit) { - ControlAction.timeoutDelay("Ping Delay", Starter.Config.PingQuitDelay * 1e3); - Starter.pingQuit = false; - } - - if (Starter.Config.JoinChannel !== "" && Controls.LobbyEnterChat.click()) return; - - if (Starter.inGame || Starter.gameInfo.error) { - !Starter.gameStart && (Starter.gameStart = DataFile.getStats().ingameTick); - - if (getTickCount() - Starter.gameStart < Starter.Config.MinGameTime * 1e3 && !joinInfo) { - ControlAction.timeoutDelay("Min game time wait", Starter.Config.MinGameTime * 1e3 + Starter.gameStart - getTickCount()); - } - } - - if (Starter.inGame) { - if (oogCheck()) return; - - D2Bot.updateRuns(); - - Starter.gameCount += 1; - Starter.lastGameStatus = "ready"; - Starter.inGame = false; - - if (Starter.Config.ResetCount && Starter.gameCount > Starter.Config.ResetCount) { - Starter.gameCount = 1; - DataFile.updateStats("runs", Starter.gameCount); - } - } - - Starter.LocationEvents.openCreateGameWindow(); -}; -locations[sdk.game.locations.LobbyChat] = () => Starter.LocationEvents.lobbyChat(); -locations[sdk.game.locations.CreateGame] = (loc) => { - ControlAction.timeoutDelay("Create Game Delay", Starter.Config.DelayBeforeLogin * 1e3); - D2Bot.updateStatus("Creating Game"); - - if (typeof Starter.Config.CharacterDifference === "number") { - Controls.CharacterDifference.disabled === sdk.game.controls.Disabled && Controls.CharacterDifferenceButton.click(); - Controls.CharacterDifference.setText(Starter.Config.CharacterDifference.toString()); - } else if (!Starter.Config.CharacterDifference && Controls.CharacterDifference.disabled === 5) { - Controls.CharacterDifferenceButton.click(); - } - - typeof Starter.Config.MaxPlayerCount === "number" && Controls.MaxPlayerCount.setText(Starter.Config.MaxPlayerCount.toString()); - - D2Bot.requestGameInfo(); - delay(500); - - // todo - really don't need use profiles set difficulty for online. Only single player so re-write difficulty stuff - Starter.checkDifficulty(); - - Starter.gameInfo.gameName = DataFile.getStats().gameName; - Starter.gameInfo.gamePass = Starter.randomString(5, true); - - switch (true) { - case Starter.gameInfo.gameName === "": - case Starter.gameInfo.gameName === "Name": - Starter.gameInfo.gameName = Starter.profileInfo.charName.substring(0, 7) + "-" + Starter.randomString(3, false) + "-"; - - break; - } - - // FTJ handler - if (Starter.lastGameStatus === "pending") { - Starter.isUp = "no"; - - D2Bot.printToConsole("Failed to create game"); - ControlAction.timeoutDelay("FTJ delay", Starter.Config.FTJDelay * 1e3); - D2Bot.updateRuns(); - } - - ControlAction.createGame((Starter.gameInfo.gameName + Starter.gameCount), Starter.gameInfo.gamePass, Starter.gameInfo.difficulty, Starter.Config.CreateGameDelay * 1000); - Starter.lastGameStatus = "pending"; - Starter.setNextGame(Starter.gameInfo); - Starter.locationTimeout(10000, loc); -}; -locations[sdk.game.locations.GameNameExists] = () => { - Controls.CreateGameWindow.click(); - Starter.gameCount += 1; - Starter.lastGameStatus = "ready"; -}; -locations[sdk.game.locations.WaitingInLine] = () => Starter.LocationEvents.waitingInLine(); -locations[sdk.game.locations.JoinGame] = () => Starter.LocationEvents.openCreateGameWindow(); -locations[sdk.game.locations.Ladder] = () => Starter.LocationEvents.openCreateGameWindow(); -locations[sdk.game.locations.ChannelList] = () => Starter.LocationEvents.openCreateGameWindow(); -locations[sdk.game.locations.LobbyLostConnection] = () => { - ControlAction.timeoutDelay("LostConnection", 3000); - Controls.OkCentered.click(); -}; -locations[sdk.game.locations.GameDoesNotExist] = () => Starter.LocationEvents.gameDoesNotExist(); -locations[sdk.game.locations.GameIsFull] = () => Starter.LocationEvents.openCreateGameWindow(); - function main () { debugLog(me.profile); addEventListener("copydata", Starter.receiveCopyData); @@ -364,7 +145,7 @@ function main () { if (Starter.profileInfo.charName === "") { console.log("Generating Character Name"); - Starter.profileInfo.charName = NameGen(); + Starter.profileInfo.charName = require("./libs/SoloPlay/Tools/NameGen")(); delay(50); } diff --git a/libs/SoloPlay/Functions/Globals.js b/libs/SoloPlay/Functions/Globals.js index df404fbf..d759a405 100644 --- a/libs/SoloPlay/Functions/Globals.js +++ b/libs/SoloPlay/Functions/Globals.js @@ -299,7 +299,6 @@ const SetUp = { }, makeNext: function () { - includeIfNotIncluded("SoloPlay/Tools/NameGen.js"); includeIfNotIncluded("SoloPlay/Tools/Tracker.js"); let gameObj, printTotalTime = Developer.logPerformance; printTotalTime && (gameObj = Tracker.readObj(Tracker.GTPath)); @@ -308,7 +307,7 @@ const SetUp = { myPrint(this.finalBuild + " goal reached. On to the next."); D2Bot.printToConsole("Kolbot-SoloPlay: " + this.finalBuild + " goal reached" + (printTotalTime ? " (" + (Tracker.formatTime(gameObj.Total + Tracker.timer(gameObj.LastSave))) + "). " : ". ") + "Making next...", sdk.colors.D2Bot.Gold); - D2Bot.setProfile(null, null, NameGen()); + D2Bot.setProfile(null, null, require("../Tools/NameGen")()); CharData.delete(true); delay(250); D2Bot.restart(); diff --git a/libs/SoloPlay/Functions/SoloEvents.js b/libs/SoloPlay/Functions/SoloEvents.js index 29a6ae2d..ad240c65 100644 --- a/libs/SoloPlay/Functions/SoloEvents.js +++ b/libs/SoloPlay/Functions/SoloEvents.js @@ -5,446 +5,462 @@ * */ -const SoloEvents = { - filePath: "libs/SoloPlay/Threads/EventThread.js", - check: false, - inGame: false, - cloneWalked: false, - townChicken: false, - profileResponded: false, - gameInfo: { - gameName: "", - gamePass: "", - }, - - outOfGameCheck: function () { - if (!this.check) return false; - - if (this.gameInfo.gameName.length > 0) { - D2Bot.printToConsole("Kolbot-SoloPlay :: SoloEvents.outOfGameCheck(): Attempting to join other bots game", sdk.colors.D2Bot.Gold); - SoloEvents.inGame = true; - me.blockmouse = true; - - delay(2000); - joinGame(this.gameInfo.gameName, this.gameInfo.gamePass); - - me.blockmouse = false; - - delay(5000); - - while (me.ingame) { - delay(1000); - } +(function (root, factory) { + if (typeof module === "object" && typeof module.exports === "object") { + const v = factory(); + if (v !== undefined) module.exports = v; + } else if (typeof define === "function" && define.amd) { + define([], factory); + } else { + root.SoloEvents = factory(); + } +}(this, function() { + const SoloEvents = { + filePath: "libs/SoloPlay/Threads/EventThread.js", + check: false, + inGame: false, + cloneWalked: false, + townChicken: { + disabled: false, + running: false, + }, + profileResponded: false, + gameInfo: { + gameName: "", + gamePass: "", + }, + + outOfGameCheck: function () { + if (!this.check) return false; + + if (this.gameInfo.gameName.length > 0) { + D2Bot.printToConsole("Kolbot-SoloPlay :: SoloEvents.outOfGameCheck(): Attempting to join other bots game", sdk.colors.D2Bot.Gold); + SoloEvents.inGame = true; + me.blockMouse = true; + + delay(2000); + joinGame(this.gameInfo.gameName, this.gameInfo.gamePass); + + me.blockMouse = false; + + delay(5000); + + while (me.ingame) { + delay(1000); + } - console.log("ÿc8Kolbot-SoloPlayÿc0: End of SoloEvents.outOfGameCheck()"); - SoloEvents.inGame = false; - SoloEvents.check = false; - this.gameInfo.gameName = ""; - this.gameInfo.gamePass = ""; + console.log("ÿc8Kolbot-SoloPlayÿc0: End of SoloEvents.outOfGameCheck()"); + SoloEvents.inGame = false; + SoloEvents.check = false; + this.gameInfo.gameName = ""; + this.gameInfo.gamePass = ""; - return true; - } + return true; + } - return false; - }, + return false; + }, - inGameCheck: function () { - if (me.ingame && me.hell && !me.classic && Misc.getPlayerCount() > 1) { - let possibleChars = this.getCharacterNames(); + inGameCheck: function () { + if (me.ingame && me.hell && !me.classic && Misc.getPlayerCount() > 1) { + let possibleChars = this.getCharacterNames(); - for (let i = 0; i < possibleChars.length; i++) { - if (Misc.findPlayer(possibleChars[i].toLowerCase())) { - if (!me.inArea(sdk.areas.RogueEncampment)) { - Town.goToTown(1); - } + for (let i = 0; i < possibleChars.length; i++) { + if (Misc.findPlayer(possibleChars[i].toLowerCase())) { + if (!me.inArea(sdk.areas.RogueEncampment)) { + Town.goToTown(1); + } - Town.move("stash"); + Town.move("stash"); - let torch, anni, tick = getTickCount(); + let torch, anni, tick = getTickCount(); - me.overhead("Waiting for charm to drop"); - while (getTickCount() - tick < 120 * 1000) { - anni = me.findItem(sdk.items.SmallCharm, sdk.items.mode.onGround, -1, sdk.items.quality.Unique); - torch = me.findItem(sdk.items.LargeCharm, sdk.items.mode.onGround, -1, sdk.items.quality.Unique); + me.overhead("Waiting for charm to drop"); + while (getTickCount() - tick < 120 * 1000) { + anni = me.findItem(sdk.items.SmallCharm, sdk.items.mode.onGround, -1, sdk.items.quality.Unique); + torch = me.findItem(sdk.items.LargeCharm, sdk.items.mode.onGround, -1, sdk.items.quality.Unique); - if (torch || anni) { - break; + if (torch || anni) { + break; + } } - } - if (torch || anni) { - for (let j = 0; j < 12 || me.findItem((anni ? sdk.items.SmallCharm : sdk.items.LargeCharm), sdk.items.mode.inStorage, -1, sdk.items.quality.Unique); j++) { - Town.move("stash"); - me.overhead("Looking for " + (anni ? "Annihilus" : "Torch")); - Pickit.pickItems(); - delay(10000); // 10 seconds + if (torch || anni) { + for (let j = 0; j < 12 || me.findItem((anni ? sdk.items.SmallCharm : sdk.items.LargeCharm), sdk.items.mode.inStorage, -1, sdk.items.quality.Unique); j++) { + Town.move("stash"); + me.overhead("Looking for " + (anni ? "Annihilus" : "Torch")); + Pickit.pickItems(); + delay(10000); // 10 seconds + } + } else { + me.overhead("No charm on ground"); } - } else { - me.overhead("No charm on ground"); - } - quit(); - return true; + quit(); + return true; + } } - } - console.log("Couldnt find player"); - } + console.log("Couldnt find player"); + } - return false; - }, + return false; + }, - getProfiles: function () { - let profileInfo, realm = me.realm.toLowerCase(), profileList = []; - //realm = "useast"; // testing purposes + getProfiles: function () { + let profileInfo, realm = me.realm.toLowerCase(), profileList = []; + //realm = "useast"; // testing purposes - if (!FileTools.exists("logs/Kolbot-SoloPlay/" + realm)) { - return profileList; - } + if (!FileTools.exists("logs/Kolbot-SoloPlay/" + realm)) { + return profileList; + } - let files = dopen("logs/Kolbot-SoloPlay/" + realm + "/").getFiles(); + let files = dopen("logs/Kolbot-SoloPlay/" + realm + "/").getFiles(); - for (let i = 0; i < files.length; i++) { - try { - profileInfo = Developer.readObj("logs/Kolbot-SoloPlay/" + realm + "/" + files[i]); - if (profileList.indexOf(profileInfo.profile) === -1) { - profileList.push(profileInfo.profile); + for (let i = 0; i < files.length; i++) { + try { + profileInfo = Tracker.readObj("logs/Kolbot-SoloPlay/" + realm + "/" + files[i]); + if (profileList.indexOf(profileInfo.profile) === -1) { + profileList.push(profileInfo.profile); + } + } catch (e) { + console.log(e); } - } catch (e) { - console.log(e); } - } - return profileList; - }, + return profileList; + }, - getCharacterNames: function () { - let characterInfo, realm = me.realm.toLowerCase(), charList = []; - //realm = "useast"; // testing purposes + getCharacterNames: function () { + let characterInfo, realm = me.realm.toLowerCase(), charList = []; + //realm = "useast"; // testing purposes - if (!FileTools.exists("logs/Kolbot-SoloPlay/" + realm)) { - return profileList; - } + if (!FileTools.exists("logs/Kolbot-SoloPlay/" + realm)) { + return profileList; + } - let files = dopen("logs/Kolbot-SoloPlay/" + realm + "/").getFiles(); + let files = dopen("logs/Kolbot-SoloPlay/" + realm + "/").getFiles(); - for (let i = 0; i < files.length; i++) { - try { - characterInfo = Developer.readObj("logs/Kolbot-SoloPlay/" + realm + "/" + files[i]); - if (charList.indexOf(characterInfo.charName) === -1) { - charList.push(characterInfo.charName); + for (let i = 0; i < files.length; i++) { + try { + characterInfo = Tracker.readObj("logs/Kolbot-SoloPlay/" + realm + "/" + files[i]); + if (charList.indexOf(characterInfo.charName) === -1) { + charList.push(characterInfo.charName); + } + } catch (e) { + console.log(e); } - } catch (e) { - console.log(e); } - } - return charList; - }, + return charList; + }, - sendToProfile: function (profile, message, mode = 65) { - if (profile.toLowerCase() !== me.profile.toLowerCase()) { - sendCopyData(null, profile, mode, JSON.stringify(message)); - } - }, + sendToProfile: function (profile, message, mode = 65) { + if (profile.toLowerCase() !== me.profile.toLowerCase()) { + sendCopyData(null, profile, mode, JSON.stringify(message)); + } + }, - sendToList: function (message, mode = 55) { - let profiles = this.getProfiles(); + sendToList: function (message, mode = 55) { + let profiles = this.getProfiles(); - if (!profiles || profiles === undefined) { - return false; - } - - return profiles.forEach((profileName) => { - if (profileName.toLowerCase() !== me.profile.toLowerCase()) { - sendCopyData(null, profileName, mode, JSON.stringify(message)); + if (!profiles || profiles === undefined) { + return false; } - }); - }, - dropCharm: function (charm) { - if (!charm || charm === undefined) return false; + return profiles.forEach((profileName) => { + if (profileName.toLowerCase() !== me.profile.toLowerCase()) { + sendCopyData(null, profileName, mode, JSON.stringify(message)); + } + }); + }, + + dropCharm: function (charm) { + if (!charm || charm === undefined) return false; - D2Bot.printToConsole("Kolbot-SoloPlay :: Dropping " + charm.name, sdk.colors.D2Bot.Orange); - let orginalLocation = {act: me.act, area: me.area, x: me.x, y: me.y}; + D2Bot.printToConsole("Kolbot-SoloPlay :: Dropping " + charm.name, sdk.colors.D2Bot.Orange); + let orginalLocation = {act: me.act, area: me.area, x: me.x, y: me.y}; - if (!me.inTown) { - Town.goToTown(1); - Town.move("stash"); - charm.drop(); - } + if (!me.inTown) { + Town.goToTown(1); + Town.move("stash"); + charm.drop(); + } - if (me.act !== orginalLocation.act) { - Town.goToTown(orginalLocation.act); - } + if (me.act !== orginalLocation.act) { + Town.goToTown(orginalLocation.act); + } - Town.move("portalspot"); + Town.move("portalspot"); - if (!Pather.usePortal(orginalLocation.area)) { - Pather.journeyTo(orginalLocation.area); - } + if (!Pather.usePortal(orginalLocation.area)) { + Pather.journeyTo(orginalLocation.area); + } - return Pather.moveTo(orginalLocation.x, orginalLocation.y); - }, + return Pather.moveTo(orginalLocation.x, orginalLocation.y); + }, - // @todo redo this, I think better option would be to make this it's own script - // end the current script but insert it to be continued after dclone is dead - killdclone: function () { - D2Bot.printToConsole("Kolbot-SoloPlay :: Trying to kill DClone.", sdk.colors.D2Bot.Orange); - let orginalLocation = {area: me.area, x: me.x, y: me.y}; + // @todo redo this, I think better option would be to make this it's own script + // end the current script but insert it to be continued after dclone is dead + killdclone: function () { + D2Bot.printToConsole("Kolbot-SoloPlay :: Trying to kill DClone.", sdk.colors.D2Bot.Orange); + let orginalLocation = {area: me.area, x: me.x, y: me.y}; - !me.inTown && Town.goToTown(); + !me.inTown && Town.goToTown(); - if (Pather.accessToAct(2) && Pather.checkWP(sdk.areas.ArcaneSanctuary)) { - Pather.useWaypoint(sdk.areas.ArcaneSanctuary); - Precast.doPrecast(true); + if (Pather.accessToAct(2) && Pather.checkWP(sdk.areas.ArcaneSanctuary)) { + Pather.useWaypoint(sdk.areas.ArcaneSanctuary); + Precast.doPrecast(true); - if (!Pather.usePortal(null)) { - console.log("ÿc8Kolbot-SoloPlayÿc1: Failed to move to Palace Cellar"); + if (!Pather.usePortal(null)) { + console.log("ÿc8Kolbot-SoloPlayÿc1: Failed to move to Palace Cellar"); + } + } else if (Pather.checkWP(sdk.areas.InnerCloister)) { + Pather.useWaypoint(sdk.areas.InnerCloister); + Pather.moveTo(20047, 4898); + } else { + Pather.useWaypoint(sdk.areas.ColdPlains); + Pather.moveToExit([sdk.areas.BloodMoor, sdk.areas.DenofEvil], true); + Pather.moveToPreset(me.area, sdk.unittype.Monster, sdk.monsters.preset.Corpsefire, 0, 0, false, true); } - } else if (Pather.checkWP(sdk.areas.InnerCloister)) { - Pather.useWaypoint(sdk.areas.InnerCloister); - Pather.moveTo(20047, 4898); - } else { - Pather.useWaypoint(sdk.areas.ColdPlains); - Pather.moveToExit([sdk.areas.BloodMoor, sdk.areas.DenofEvil], true); - Pather.moveToPreset(me.area, sdk.unittype.Monster, sdk.monsters.preset.Corpsefire, 0, 0, false, true); - } - Attack.killTarget(sdk.monsters.DiabloClone); - Pickit.pickItems(); + Attack.killTarget(sdk.monsters.DiabloClone); + Pickit.pickItems(); - let newAnni = Game.getItem(sdk.items.SmallCharm, sdk.items.mode.onGround); - let oldAnni = me.findItem(sdk.items.SmallCharm, sdk.items.mode.inStorage, -1, sdk.items.quality.Unique); + let newAnni = Game.getItem(sdk.items.SmallCharm, sdk.items.mode.onGround); + let oldAnni = me.findItem(sdk.items.SmallCharm, sdk.items.mode.inStorage, -1, sdk.items.quality.Unique); - if (newAnni && oldAnni) { - this.sendToList({profile: me.profile, ladder: me.ladder}, 60); + if (newAnni && oldAnni) { + this.sendToList({profile: me.profile, ladder: me.ladder}, 60); - let tick = getTickCount(); + let tick = getTickCount(); + + while (getTickCount() - tick < 10000) { + me.overhead("Waiting to see if I get a response from other profiles"); + + if (this.profileResponded) { + me.overhead("Recieved response, dropping old Annihilus in Rogue Encampment"); + break; + } - while (getTickCount() - tick < 10000) { - me.overhead("Waiting to see if I get a response from other profiles"); + delay(50); + } - if (this.profileResponded) { - me.overhead("Recieved response, dropping old Annihilus in Rogue Encampment"); - break; + if (newAnni && oldAnni && this.profileResponded) { + this.dropCharm(oldAnni); + } else { + me.overhead("No response from other profiles"); } - delay(50); + SoloEvents.profileResponded = false; + Pickit.pickItems(); } - if (newAnni && oldAnni && this.profileResponded) { - this.dropCharm(oldAnni); - } else { - me.overhead("No response from other profiles"); + if ((newAnni && oldAnni && !this.profileResponded) && AutoMule.getInfo() && AutoMule.getInfo().hasOwnProperty("torchMuleInfo")) { + scriptBroadcast("muleAnni"); } - SoloEvents.profileResponded = false; - Pickit.pickItems(); - } - - if ((newAnni && oldAnni && !this.profileResponded) && AutoMule.getInfo() && AutoMule.getInfo().hasOwnProperty("torchMuleInfo")) { - scriptBroadcast("muleAnni"); - } - - // Move back to where we orignally where - Pather.journeyTo(orginalLocation.area); - Pather.moveTo(orginalLocation.x, orginalLocation.y); - SoloEvents.cloneWalked = false; - }, - - moveSettings: { - allowTeleport: false, - allowClearing: false, - allowPicking: false, - allowTown: false, - retry: 10, - }, - - moveTo: function (x, y, givenSettings) { - // Abort if dead - if (me.dead) return false; - return Pather.move({ x: x, y: y }, Object.assign({}, SoloEvents.moveSettings, givenSettings)); - }, - - skip: function () { - let tick = getTickCount(); - myPrint("Attempting baal wave skip"); - - // Disable anything that will cause us to stop - [Precast.enabled, Misc.townEnabled, Pickit.enabled] = [false, false, false]; - me.barbarian && (Config.FindItem = false); - - // Prep, move to throne entrance - while (getTickCount() - tick < 6500) { - this.moveTo(15091, 5073, { allowTeleport: true }); - } - - tick = getTickCount(); - - // 5 second delay (5000ms), then leave throne - while (getTickCount() - tick < 5000) { - this.moveTo(15098, 5082, { allowTeleport: true }); - } - - tick = getTickCount(); - this.moveTo(15099, 5078); // Re-enter throne - - // 2 second delay (2000ms) - while (getTickCount() - tick < 2000) { - this.moveTo(15098, 5082); - } - - this.moveTo(15099, 5078); - - // Re-enable - [Precast.enabled, Misc.townEnabled, Pickit.enabled] = [true, true, true]; - - let skipWorked = getUnits(sdk.unittype.Monster) - .some(el => el.attackable && el.x >= 15070 && el.x <= 15120 && el.y >= 5000 && el.y <= 5075); - myPrint("skip " + (skipWorked ? "worked" : "failed")); - }, - - dodge: function () { - let diablo = Game.getMonster(sdk.monsters.Diablo); - // Credit @Jaenster - const shouldDodge = function (coord) { - return !!diablo && getUnits(sdk.unittype.Missile) - // For every missle that isnt from our merc - .filter((missile) => missile && diablo && diablo.gid === missile.owner) - // if any - .some(function (missile) { - let xoff = Math.abs(coord.x - missile.targetx); - let yoff = Math.abs(coord.y - missile.targety); - let xdist = Math.abs(coord.x - missile.x); - let ydist = Math.abs(coord.y - missile.y); - // If missile wants to hit is and is close to us - return xoff < 10 && yoff < 10 && xdist < 15 && ydist < 15; - }); - }; - - if (diablo && shouldDodge(me)) { + // Move back to where we orignally where + Pather.journeyTo(orginalLocation.area); + Pather.moveTo(orginalLocation.x, orginalLocation.y); + SoloEvents.cloneWalked = false; + }, + + moveSettings: { + allowTeleport: false, + allowClearing: false, + allowPicking: false, + allowTown: false, + retry: 10, + }, + + moveTo: function (x, y, givenSettings) { + // Abort if dead + if (me.dead) return false; + return Pather.move({ x: x, y: y }, Object.assign({}, SoloEvents.moveSettings, givenSettings)); + }, + + skip: function () { let tick = getTickCount(); - let overrides = { allowTeleport: false, allowClearing: false, allowTown: false }; + myPrint("Attempting baal wave skip"); + // Disable anything that will cause us to stop [Precast.enabled, Misc.townEnabled, Pickit.enabled] = [false, false, false]; - console.log("DODGE"); - // Disable things that will cause us to stop - let dist = me.assassin ? 15 : 3; + me.barbarian && (Config.FindItem = false); - while (getTickCount() - tick < 2000) { - // Above D - if (me.y <= diablo.y) { - // Move east - me.x <= diablo.x && this.moveTo(diablo.x + dist, diablo.y, overrides); - // Move south - me.x > diablo.x && this.moveTo(diablo.x, diablo.y + dist, overrides); - } + // Prep, move to throne entrance + while (getTickCount() - tick < 6500) { + this.moveTo(15091, 5073, { allowTeleport: true }); + } - // Below D - if (me.y > diablo.y) { - // Move west - me.x >= diablo.x && this.moveTo(diablo.x - dist, diablo.y, overrides); - // Move north - me.x < diablo.x && this.moveTo(diablo.x, diablo.y - dist, overrides); - } + tick = getTickCount(); + + // 5 second delay (5000ms), then leave throne + while (getTickCount() - tick < 5000) { + this.moveTo(15098, 5082, { allowTeleport: true }); } + tick = getTickCount(); + this.moveTo(15099, 5078); // Re-enter throne + + // 2 second delay (2000ms) + while (getTickCount() - tick < 2000) { + this.moveTo(15098, 5082); + } + + this.moveTo(15099, 5078); + // Re-enable [Precast.enabled, Misc.townEnabled, Pickit.enabled] = [true, true, true]; - } - }, - - finishDen: function () { - Pickit.pickItems(); - - // No Tome, or tome has no tps, or no scrolls - if (!me.canTpToTown() && !me.inTown) { - // should really check how close the town exit is - Pather.moveToExit([sdk.areas.BloodMoor, sdk.areas.ColdPlains], true); - Pather.getWP(sdk.areas.ColdPlains); - Pather.useWaypoint(sdk.areas.RogueEncampment); - } else { - Town.goToTown(); - } - - Town.npcInteract("akara"); - }, - - bugAndy: function () { - Town.goToTown(); - Pather.changeAct(); - delay(2000 + me.ping); - - // Now check my area - if (me.act === 2) { - // Act change sucessful, Andy has been bugged - myPrint("Andy bug " + (!me.getQuest(sdk.quest.id.SistersToTheSlaughter, 15) ? "sucessful" : "failed")); - scriptBroadcast("quit"); - } - }, - - diaEvent: function (bytes = []) { - if (!bytes.length) return; - // dia lightning - if (bytes[0] === 0x4C && bytes[6] === 193) { - Messaging.sendToScript(SoloEvents.filePath, "dodge"); - } - }, - - skippedWaves: [], - - baalEvent: function (bytes = []) { - if (!bytes.length) return; - // baal wave - if (bytes[0] === 0xA4) { - if ((me.hell && me.paladin && !Attack.auradin) - || me.barbarian - || me.gold < 5000 - || (!me.baal && SetUp.finalBuild !== "Bumper")) { - let waveMonster = ((bytes[1]) | (bytes[2] << 8)); - let wave = [ - sdk.monsters.WarpedShaman, - sdk.monsters.BaalSubjectMummy, - sdk.monsters.Council4, - sdk.monsters.VenomLord2, - sdk.monsters.ListerTheTormenter - ].indexOf(waveMonster); - console.debug("Wave # " + wave); - if (SoloEvents.skippedWaves.includes(wave)) return; - const waveBoss = { - COLENZO: 0, - ACHMEL: 1, - BARTUC: 2, - VENTAR: 3, - LISTER: 4 - }; - - switch (wave) { - case waveBoss.COLENZO: - break; - case waveBoss.ACHMEL: - if ((me.paladin && !Attack.auradin && me.hell) - || (me.barbarian && ((me.charlvl < CharInfo.levelCap && !me.baal) - || me.hardcore))) { - Messaging.sendToScript(SoloEvents.filePath, "skip"); - SoloEvents.skippedWaves.push(wave); + + let skipWorked = getUnits(sdk.unittype.Monster) + .some(el => el.attackable && el.x >= 15070 && el.x <= 15120 && el.y >= 5000 && el.y <= 5075); + myPrint("skip " + (skipWorked ? "worked" : "failed")); + }, + + dodge: function () { + let diablo = Game.getMonster(sdk.monsters.Diablo); + // Credit @Jaenster + const shouldDodge = function (coord) { + return !!diablo && getUnits(sdk.unittype.Missile) + // For every missle that isnt from our merc + .filter((missile) => missile && diablo && diablo.gid === missile.owner) + // if any + .some(function (missile) { + let xoff = Math.abs(coord.x - missile.targetx); + let yoff = Math.abs(coord.y - missile.targety); + let xdist = Math.abs(coord.x - missile.x); + let ydist = Math.abs(coord.y - missile.y); + // If missile wants to hit is and is close to us + return xoff < 10 && yoff < 10 && xdist < 15 && ydist < 15; + }); + }; + + if (diablo && shouldDodge(me)) { + let tick = getTickCount(); + let overrides = { allowTeleport: false, allowClearing: false, allowTown: false }; + // Disable anything that will cause us to stop + [Precast.enabled, Misc.townEnabled, Pickit.enabled] = [false, false, false]; + console.log("DODGE"); + // Disable things that will cause us to stop + let dist = me.assassin ? 15 : 3; + + while (getTickCount() - tick < 2000) { + // Above D + if (me.y <= diablo.y) { + // Move east + me.x <= diablo.x && this.moveTo(diablo.x + dist, diablo.y, overrides); + // Move south + me.x > diablo.x && this.moveTo(diablo.x, diablo.y + dist, overrides); } - break; - case waveBoss.BARTUC: - case waveBoss.VENTAR: - break; - case waveBoss.LISTER: - if ((me.barbarian && (me.charlvl < CharInfo.levelCap || !me.baal || me.hardcore)) - || (me.charlvl < CharInfo.levelCap && (me.gold < 5000 || (!me.baal && SetUp.finalBuild !== "Bumper")))) { - Messaging.sendToScript(SoloEvents.filePath, "skip"); - SoloEvents.skippedWaves.push(wave); + // Below D + if (me.y > diablo.y) { + // Move west + me.x >= diablo.x && this.moveTo(diablo.x - dist, diablo.y, overrides); + // Move north + me.x < diablo.x && this.moveTo(diablo.x, diablo.y - dist, overrides); } + } + + // Re-enable + [Precast.enabled, Misc.townEnabled, Pickit.enabled] = [true, true, true]; + } + }, + + finishDen: function () { + Pickit.pickItems(); + + // No Tome, or tome has no tps, or no scrolls + if (!me.canTpToTown() && !me.inTown) { + // should really check how close the town exit is + Pather.moveToExit([sdk.areas.BloodMoor, sdk.areas.ColdPlains], true); + Pather.getWP(sdk.areas.ColdPlains); + Pather.useWaypoint(sdk.areas.RogueEncampment); + } else { + Town.goToTown(1); + } + + Town.npcInteract("akara"); + }, - break; + bugAndy: function () { + Town.goToTown(); + Pather.changeAct(); + delay(2000 + me.ping); + + // Now check my area + if (me.act === 2) { + // Act change sucessful, Andy has been bugged + myPrint("Andy bug " + (!me.getQuest(sdk.quest.id.SistersToTheSlaughter, 15) ? "sucessful" : "failed")); + scriptBroadcast("quit"); + } + }, + + diaEvent: function (bytes = []) { + if (!bytes.length) return; + // dia lightning + if (bytes[0] === 0x4C && bytes[6] === 193) { + Messaging.sendToScript(SoloEvents.filePath, "dodge"); + } + }, + + skippedWaves: [], + + baalEvent: function (bytes = []) { + if (!bytes.length) return; + // baal wave + if (bytes[0] === 0xA4) { + if ((me.hell && me.paladin && !Attack.auradin) + || me.barbarian + || me.gold < 5000 + || (!me.baal && SetUp.finalBuild !== "Bumper")) { + let waveMonster = ((bytes[1]) | (bytes[2] << 8)); + let wave = [ + sdk.monsters.WarpedShaman, + sdk.monsters.BaalSubjectMummy, + sdk.monsters.Council4, + sdk.monsters.VenomLord2, + sdk.monsters.ListerTheTormenter + ].indexOf(waveMonster); + console.debug("Wave # " + wave); + if (SoloEvents.skippedWaves.includes(wave)) return; + const waveBoss = { + COLENZO: 0, + ACHMEL: 1, + BARTUC: 2, + VENTAR: 3, + LISTER: 4 + }; + + switch (wave) { + case waveBoss.COLENZO: + break; + case waveBoss.ACHMEL: + if ((me.paladin && !Attack.auradin && me.hell) + || (me.barbarian && ((me.charlvl < CharInfo.levelCap && !me.baal) + || me.hardcore))) { + Messaging.sendToScript(SoloEvents.filePath, "skip"); + SoloEvents.skippedWaves.push(wave); + } + + break; + case waveBoss.BARTUC: + case waveBoss.VENTAR: + break; + case waveBoss.LISTER: + if ((me.barbarian && (me.charlvl < CharInfo.levelCap || !me.baal || me.hardcore)) + || (me.charlvl < CharInfo.levelCap && (me.gold < 5000 || (!me.baal && SetUp.finalBuild !== "Bumper")))) { + Messaging.sendToScript(SoloEvents.filePath, "skip"); + SoloEvents.skippedWaves.push(wave); + } + + break; + } } } - } - }, -}; + }, + }; + + return SoloEvents; +})); diff --git a/libs/SoloPlay/Tools/NameGen.js b/libs/SoloPlay/Tools/NameGen.js index 12fd0156..0f1e03e2 100644 --- a/libs/SoloPlay/Tools/NameGen.js +++ b/libs/SoloPlay/Tools/NameGen.js @@ -5,7 +5,7 @@ * */ -const NameGen = function () { +(function (module) { const adjectives = [ "Ancient", "Angry", "Artful", "Able", "Abundant", "Accepting", "Acclaimed", "Active", "Addictive", "Adept", "Adequate", "Admired", "Adorable", "Adored", "Agile", "Amazing", "Amiable", "Amicable", "Amusing", "Anxious", "Anxious", "Apathetic", "Aquatic", "Arrogant", "Artistic", @@ -234,15 +234,19 @@ const NameGen = function () { "zoo", "zoology", ]; - //let random1 = Math.floor(Math.random() * (adjectives.length + 1)); - let adjective = adjectives[rand(0, adjectives.length - 1)]; - let list2Limit = 16 - adjective.length; - let list2 = nouns.filter(function (element) { - return element.length < list2Limit; - }); + const NameGen = function () { + //let random1 = Math.floor(Math.random() * (adjectives.length + 1)); + let adjective = adjectives[rand(0, adjectives.length - 1)]; + let list2Limit = 16 - adjective.length; + let list2 = nouns.filter(function (element) { + return element.length < list2Limit; + }); - let noun = list2[rand(0, list2.length - 1)]; - let namechosen = adjective.toLowerCase() + noun.toLowerCase(); + let noun = list2[rand(0, list2.length - 1)]; + let namechosen = adjective + noun; - return namechosen; -}; + return namechosen.toLowerCase(); + }; + + module.exports = NameGen; +})(module); diff --git a/libs/SoloPlay/Tools/OOGOverrides.js b/libs/SoloPlay/Tools/OOGOverrides.js index 8adce412..41100a4a 100644 --- a/libs/SoloPlay/Tools/OOGOverrides.js +++ b/libs/SoloPlay/Tools/OOGOverrides.js @@ -4,6 +4,10 @@ * @desc OOG.js fixes to improve functionality * */ + +/** + * @typedef {import("../../modules/Control")} Controls + */ includeIfNotIncluded("OOG.js"); (function (global, original) { @@ -13,228 +17,266 @@ includeIfNotIncluded("OOG.js"); }; })([].filter.constructor("return this")(), login); -ControlAction.scrollDown = function () { - me.blockMouse = true; - for (let i = 0; i < 4; i++) { - sendKey(sdk.keys.code.DownArrow); - } - me.blockMouse = false; -}; - -ControlAction.makeCharacter = function (info) { - me.blockMouse = true; - !info.charClass && (info.charClass = "barbarian"); - - let clickCoords = []; - let soloStats = CharData.getStats(); - - soloStats.me.startTime !== 0 && Tracker.reset(); - if (soloStats.me.currentBuild !== "Start" || soloStats.me.level > 1) { - let finalBuild = soloStats.me.finalBuild; - Object.assign(soloStats, CharData.default); - soloStats.me.finalBuild = finalBuild; - CharData.updateData("me", soloStats); - } - - D2Bot.updateStatus("Making Character: " + info.charName); - - // cycle until in lobby - while (getLocation() !== sdk.game.locations.Lobby) { - switch (getLocation()) { - case sdk.game.locations.CharSelect: - case sdk.game.locations.CharSelectConnecting: - case sdk.game.locations.CharSelectNoChars: - let control = Controls.CharSelectCreate.control; - - // Create Character greyed out - if (control && control.disabled === sdk.game.controls.Disabled) { - me.blockMouse = false; +const locations = {}; +(function() { + const Controls = require("../../modules/Control"); + const Overrides = require("../../modules/Override"); - return false; - } + new Overrides.Override(Starter, Starter.receiveCopyData, function (orignal, mode, msg) { + switch (mode) { + case 1: // Join Info + console.log("Got Join Info"); + joinInfo = JSON.parse(msg); - Controls.CharSelectCreate.click(); + SoloEvents.gameInfo.gameName = joinInfo.gameName.toLowerCase(); + SoloEvents.gameInfo.gamePass = joinInfo.gamePass.toLowerCase(); break; - case sdk.game.locations.LobbyPleaseWait: - D2Bot.restart(); // single player error on finding character + case 1638: + try { + let obj = JSON.parse(msg); + Starter.profileInfo.profile = me.profile.toUpperCase(); + Starter.profileInfo.account = obj.Account; + Starter.profileInfo.password = ""; + Starter.profileInfo.charName = obj.Character; + Starter.profileInfo.tag = (obj.Tag.trim().capitalize(true) || ""); + Starter.profileInfo.difficulty = obj.Difficulty; + obj.Realm = obj.Realm.toLowerCase(); + Starter.profileInfo.realm = ["east", "west"].includes(obj.Realm) ? "us" + obj.Realm : obj.Realm; + + let buildCheck = Starter.profileInfo.profile.split("-"); // SCL-ZON123 + Starter.profileInfo.hardcore = buildCheck[0].includes("HC"); // SC softcore = false + Starter.profileInfo.expansion = buildCheck[0].indexOf("CC") === -1; // not CC so not classic - true + Starter.profileInfo.ladder = buildCheck[0].indexOf("NL") === -1; // not NL so its ladder - true + + if (buildCheck.length <= 1) { + D2Bot.printToConsole('Please update profile name. Example: "HCCNL-PAL" will make a Hardcore Classic NonLadder Paladin', sdk.colors.D2Bot.Gold); + D2Bot.printToConsole("If you are still confused please read the included readMe. https://github.com/blizzhackers/kolbot-SoloPlay/blob/main/README.md", sdk.colors.D2Bot.Gold); + D2Bot.stop(); + } - break; - case sdk.game.locations.CharacterCreate: - clickCoords = (() => { - switch (info.charClass) { - case "barbarian": - return [400, 280]; - case "amazon": - return [100, 280]; - case "necromancer": - return [300, 290]; - case "sorceress": - return [620, 270]; - case "assassin": - return [200, 280]; - case "druid": - return [700, 280]; - case "paladin": - default: - return [521, 260]; + const charClassMap = { + "ZON": "amazon", + "SOR": "sorceress", + "NEC": "necromancer", + "PAL": "paladin", + "BAR": "barbarian", + "DRU": "druid", + "SIN": "assassin" + }; + buildCheck[1] = buildCheck[1].toString().substring(0, 3); + + if (charClassMap[buildCheck[1]]) { + Starter.profileInfo.charClass = charClassMap[buildCheck[1]]; + } else { + throw new Error("Invalid profile name, couldn't set character class"); } - })(); - getControl().click(clickCoords[0], clickCoords[1]); - delay(500); + if (Starter.profileInfo.tag !== "") { + { + let soloStats = CharData.getStats(); - break; - case sdk.game.locations.NewCharSelected: - // hardcore char warning - if (Controls.CharCreateHCWarningOk.control) { - Controls.CharCreateHCWarningOk.click(); - } else { - Controls.CharCreateCharName.setText(info.charName); - - if (!info.expansion) { - // @credit isid0re - if (["druid", "assassin"].includes(info.charClass)) { - D2Bot.printToConsole("Error in profile name. Expansion characters cannot be made in classic", sdk.colors.D2Bot.Red); - D2Bot.stop(); + if (!soloStats.me.finalBuild || soloStats.me.finalBuild !== Starter.profileInfo.tag) { + D2Bot.setProfile(null, null, null, null, null, Starter.profileInfo.tag); + soloStats.me.finalBuild = Starter.profileInfo.tag; + soloStats.me.charms = {}; + CharData.updateData("me", soloStats); + } - return false; + if (!["Start", "Stepping", "Leveling"].includes(soloStats.me.currentBuild) && soloStats.me.currentBuild !== soloStats.me.finalBuild) { + soloStats.me.currentBuild = "Leveling"; + soloStats.me.charms = {}; + CharData.updateData("me", soloStats); + } } - - Controls.CharCreateExpansion.click(); + } else { + throw new Error("Please update profile InfoTag. Missing the finalBuild."); } - - !info.ladder && Controls.CharCreateLadder.click(); - info.hardcore && Controls.CharCreateHardcore.click(); - Controls.CreateNewAccountOk.click(); + } catch (e) { + console.error(e); + D2Bot.stop(); } - break; - case sdk.game.locations.OkCenteredErrorPopUp: - // char name exists (text box 4, 268, 320, 264, 120) - ControlAction.timeoutDelay("Character Name exists: " + info.charName + ". Making new Name.", 5e3); - info.charName = NameGen(); - delay(500); - Controls.OkCentered.click(); - D2Bot.updateStatus("Making Character: " + info.charName); - break; default: - break; + orignal(mode, msg); } - - // Singleplayer loop break fix. - if (me.ingame) { - break; + }).apply(); + + new Overrides.Override(Starter, Starter.scriptMsgEvent, function (orignal, msg) { + if (typeof msg !== "string") return; + if (msg === "event") { + SoloEvents.check = true; + } else if (msg === "diffChange") { + Starter.checkDifficulty(); + } else if (msg === "test") { + console.debug(sdk.colors.Green + "//-----------DataDump Start-----------//", + "\nÿc8ThreadData ::\n", getScript(true), + "\nÿc8GlobalVariabls ::\n", Object.keys(global), + "\n" + sdk.colors.Red + "//-----------DataDump End-----------//"); + } else { + orignal(msg); } + }).apply(); - delay(500); - } - - me.blockMouse = false; - D2Bot.setProfile(null, null, info.charName, "Normal"); - let gamename = Starter.profileInfo.charName.substring(0, 7) + "-" + Starter.randomString(3, false) + "-"; - DataFile.updateStats("gameName", gamename); - - return true; -}; - -ControlAction.findCharacter = function (info) { - let count = 0; - let singlePlayer = ![sdk.game.gametype.OpenBattlenet, sdk.game.gametype.BattleNet].includes(Profile().type); - // offline doesn't have a character limit cap - let cap = singlePlayer ? 999 : 24; - let tick = getTickCount(); - let firstCheck; - - while (getLocation() !== sdk.game.locations.CharSelect) { - if (getTickCount() - tick >= 5000) { - break; + ControlAction.scrollDown = function () { + me.blockMouse = true; + for (let i = 0; i < 4; i++) { + sendKey(sdk.keys.code.DownArrow); } + me.blockMouse = false; + }; + + ControlAction.makeCharacter = function (info) { + me.blockMouse = true; + !info.charClass && (info.charClass = "barbarian"); - delay(25); - } + let clickCoords = []; + let soloStats = CharData.getStats(); + const NameGen = require("./NameGen"); - if (getLocation() === sdk.game.locations.CharSelectConnecting) { - if (!Starter.charSelectConnecting()) { - D2Bot.printToConsole("Stuck at connecting screen"); - D2Bot.restart(); + soloStats.me.startTime !== 0 && Tracker.reset(); + if (soloStats.me.currentBuild !== "Start" || soloStats.me.level > 1) { + let finalBuild = soloStats.me.finalBuild; + Object.assign(soloStats, CharData.default); + soloStats.me.finalBuild = finalBuild; + CharData.updateData("me", soloStats); } - } - // start from beginning of the char list - sendKey(sdk.keys.code.Home); + D2Bot.updateStatus("Making Character: " + info.charName); + + // cycle until in lobby + while (getLocation() !== sdk.game.locations.Lobby) { + switch (getLocation()) { + case sdk.game.locations.CharSelect: + case sdk.game.locations.CharSelectConnecting: + case sdk.game.locations.CharSelectNoChars: + let control = Controls.CharSelectCreate.control; - while (getLocation() === sdk.game.locations.CharSelect && count < cap) { - let control = Controls.CharSelectCharInfo0.control; + // Create Character greyed out + if (control && control.disabled === sdk.game.controls.Disabled) { + me.blockMouse = false; - if (control) { - firstCheck = control.getText(); - do { - let text = control.getText(); + return false; + } + + Controls.CharSelectCreate.click(); - if (text instanceof Array && typeof text[1] === "string") { - count++; + break; + case sdk.game.locations.LobbyPleaseWait: + D2Bot.restart(); // single player error on finding character - if (text[1].toLowerCase() === info.charName.toLowerCase()) { - return true; + break; + case sdk.game.locations.CharacterCreate: + clickCoords = (() => { + switch (info.charClass) { + case "barbarian": + return [400, 280]; + case "amazon": + return [100, 280]; + case "necromancer": + return [300, 290]; + case "sorceress": + return [620, 270]; + case "assassin": + return [200, 280]; + case "druid": + return [700, 280]; + case "paladin": + default: + return [521, 260]; } - } - } while (count < cap && control.getNext()); - } + })(); - // check for additional characters up to 24 (online) or 999 offline (no character limit cap) - if (count > 0 && count % 8 === 0) { - if (Controls.CharSelectChar6.click()) { - this.scrollDown(); - let check = Controls.CharSelectCharInfo0.control; + getControl().click(clickCoords[0], clickCoords[1]); + delay(500); - if (!!firstCheck && !!check) { - let nameCheck = check.getText(); + break; + case sdk.game.locations.NewCharSelected: + // hardcore char warning + if (Controls.CharCreateHCWarningOk.control) { + Controls.CharCreateHCWarningOk.click(); + } else { + Controls.CharCreateCharName.setText(info.charName); - if (firstCheck[1].toLowerCase() === nameCheck[1].toLowerCase()) { - return false; + if (!info.expansion) { + // @credit isid0re + if (["druid", "assassin"].includes(info.charClass)) { + D2Bot.printToConsole("Error in profile name. Expansion characters cannot be made in classic", sdk.colors.D2Bot.Red); + D2Bot.stop(); + + return false; + } + + Controls.CharCreateExpansion.click(); } + + !info.ladder && Controls.CharCreateLadder.click(); + info.hardcore && Controls.CharCreateHardcore.click(); + Controls.CreateNewAccountOk.click(); } + + break; + case sdk.game.locations.OkCenteredErrorPopUp: + // char name exists (text box 4, 268, 320, 264, 120) + ControlAction.timeoutDelay("Character Name exists: " + info.charName + ". Making new Name.", 5e3); + info.charName = NameGen(); + delay(500); + Controls.OkCentered.click(); + D2Bot.updateStatus("Making Character: " + info.charName); + + break; + default: + break; } - } else { - // no further check necessary - break; - } - } - return false; -}; + // Singleplayer loop break fix. + if (me.ingame) { + break; + } -ControlAction.loginCharacter = function (info, startFromTop = true) { - me.blockMouse = true; + delay(500); + } + + me.blockMouse = false; + D2Bot.setProfile(null, null, info.charName, "Normal"); + let gamename = Starter.profileInfo.charName.substring(0, 7) + "-" + Starter.randomString(3, false) + "-"; + DataFile.updateStats("gameName", gamename); - let count = 0; + return true; + }; - // start from beginning of the char list - startFromTop && sendKey(sdk.keys.code.Home); + ControlAction.findCharacter = function (info) { + let count = 0; + let singlePlayer = ![sdk.game.gametype.OpenBattlenet, sdk.game.gametype.BattleNet].includes(Profile().type); + // offline doesn't have a character limit cap + let cap = singlePlayer ? 999 : 24; + let tick = getTickCount(); + let firstCheck; - MainLoop: - // cycle until in lobby or in game - while (getLocation() !== sdk.game.locations.Lobby) { - switch (getLocation()) { - case sdk.game.locations.SplashScreen: - case sdk.game.locations.MainMenu: - case sdk.game.locations.Login: - if (getLocation() === sdk.game.locations.MainMenu - && Profile().type === sdk.game.profiletype.SinglePlayer - && Controls.SinglePlayer.click()) { - Starter.checkDifficulty(); + while (getLocation() !== sdk.game.locations.CharSelect) { + if (getTickCount() - tick >= 5000) { break; - } else if (Starter.BNET) { - Starter.LocationEvents.login(); } - break; - case sdk.game.locations.CharSelect: + delay(25); + } + + if (getLocation() === sdk.game.locations.CharSelectConnecting) { + if (!Starter.charSelectConnecting()) { + D2Bot.printToConsole("Stuck at connecting screen"); + D2Bot.restart(); + } + } + + // start from beginning of the char list + sendKey(sdk.keys.code.Home); + + while (getLocation() === sdk.game.locations.CharSelect && count < cap) { let control = Controls.CharSelectCharInfo0.control; if (control) { + firstCheck = control.getText(); do { let text = control.getText(); @@ -242,715 +284,921 @@ ControlAction.loginCharacter = function (info, startFromTop = true) { count++; if (text[1].toLowerCase() === info.charName.toLowerCase()) { - control.click(); - Controls.CreateNewAccountOk.click(); - me.blockMouse = false; - - if (getLocation() === sdk.game.locations.SelectDifficultySP) { - try { - Starter.LocationEvents.selectDifficultySP(); - Starter.locationTimeout(Time.seconds(3), sdk.game.locations.SelectDifficultySP); - } catch (err) { - break MainLoop; - } - - if (me.ingame) { - return true; - } - } - return true; } } - } while (control.getNext()); + } while (count < cap && control.getNext()); } - // check for additional characters up to 24 - if (count === 8 || count === 16) { - Controls.CharSelectChar6.click() && this.scrollDown(); + // check for additional characters up to 24 (online) or 999 offline (no character limit cap) + if (count > 0 && count % 8 === 0) { + if (Controls.CharSelectChar6.click()) { + this.scrollDown(); + let check = Controls.CharSelectCharInfo0.control; + + if (!!firstCheck && !!check) { + let nameCheck = check.getText(); + + if (firstCheck[1].toLowerCase() === nameCheck[1].toLowerCase()) { + return false; + } + } + } } else { // no further check necessary - break MainLoop; + break; } + } - break; - case sdk.game.locations.CharSelectNoChars: - Controls.CharSelectExit.click(); + return false; + }; - break; - case sdk.game.locations.Disconnected: - case sdk.game.locations.OkCenteredErrorPopUp: - break MainLoop; - default: - break; + ControlAction.loginCharacter = function (info, startFromTop = true) { + me.blockMouse = true; + + let count = 0; + + // start from beginning of the char list + startFromTop && sendKey(sdk.keys.code.Home); + + MainLoop: + // cycle until in lobby or in game + while (getLocation() !== sdk.game.locations.Lobby) { + switch (getLocation()) { + case sdk.game.locations.SplashScreen: + case sdk.game.locations.MainMenu: + case sdk.game.locations.Login: + if (getLocation() === sdk.game.locations.MainMenu + && Profile().type === sdk.game.profiletype.SinglePlayer + && Controls.SinglePlayer.click()) { + Starter.checkDifficulty(); + break; + } else if (Starter.BNET) { + Starter.LocationEvents.login(); + } + + break; + case sdk.game.locations.CharSelect: + let control = Controls.CharSelectCharInfo0.control; + + if (control) { + do { + let text = control.getText(); + + if (text instanceof Array && typeof text[1] === "string") { + count++; + + if (text[1].toLowerCase() === info.charName.toLowerCase()) { + control.click(); + Controls.CreateNewAccountOk.click(); + me.blockMouse = false; + + if (getLocation() === sdk.game.locations.SelectDifficultySP) { + try { + Starter.LocationEvents.selectDifficultySP(); + Starter.locationTimeout(Time.seconds(3), sdk.game.locations.SelectDifficultySP); + } catch (err) { + break MainLoop; + } + + if (me.ingame) { + return true; + } + } + + return true; + } + } + } while (control.getNext()); + } + + // check for additional characters up to 24 + if (count === 8 || count === 16) { + Controls.CharSelectChar6.click() && this.scrollDown(); + } else { + // no further check necessary + break MainLoop; + } + + break; + case sdk.game.locations.CharSelectNoChars: + Controls.CharSelectExit.click(); + + break; + case sdk.game.locations.Disconnected: + case sdk.game.locations.OkCenteredErrorPopUp: + break MainLoop; + default: + break; + } + + delay(100); } - delay(100); - } + me.blockMouse = false; - me.blockMouse = false; + return false; + }; - return false; -}; + // need open bnet check + ControlAction.makeAccount = function (info) { + me.blockMouse = true; -// need open bnet check -ControlAction.makeAccount = function (info) { - me.blockMouse = true; + let tick; + let realms = { "uswest": 0, "useast": 1, "asia": 2, "europe": 3 }; + D2Bot.updateStatus("Making Account: " + info.account); - let tick; - let realms = { "uswest": 0, "useast": 1, "asia": 2, "europe": 3 }; - D2Bot.updateStatus("Making Account: " + info.account); + // cycle until in empty char screen + while (getLocation() !== sdk.game.locations.CharSelectNoChars) { + switch (getLocation()) { + case sdk.game.locations.MainMenu: + ControlAction.clickRealm(realms[info.realm]); + Controls.BattleNet.click(); - // cycle until in empty char screen - while (getLocation() !== sdk.game.locations.CharSelectNoChars) { - switch (getLocation()) { - case sdk.game.locations.MainMenu: - ControlAction.clickRealm(realms[info.realm]); - Controls.BattleNet.click(); + break; + case sdk.game.locations.Login: + Controls.CreateNewAccount.click(); - break; - case sdk.game.locations.Login: - Controls.CreateNewAccount.click(); + break; + case sdk.game.locations.LoginError: + case sdk.game.locations.LoginUnableToConnect: + return false; + case sdk.game.locations.SplashScreen: + Controls.SplashScreen.click(); - break; - case sdk.game.locations.LoginError: - case sdk.game.locations.LoginUnableToConnect: - return false; - case sdk.game.locations.SplashScreen: - Controls.SplashScreen.click(); + break; + case sdk.game.locations.MainMenuConnecting: + tick = getTickCount(); - break; - case sdk.game.locations.MainMenuConnecting: - tick = getTickCount(); + while (getLocation() === sdk.game.locations.MainMenuConnecting) { + if (getTickCount() - tick > 10000) { + Controls.LoginCancelWait.click(); + } - while (getLocation() === sdk.game.locations.MainMenuConnecting) { - if (getTickCount() - tick > 10000) { - Controls.LoginCancelWait.click(); + delay(500); } - delay(500); - } + break; + case sdk.game.locations.CharacterCreate: + Controls.CharSelectExit.click(); - break; - case sdk.game.locations.CharacterCreate: - Controls.CharSelectExit.click(); + break; + case sdk.game.locations.OkCenteredErrorPopUp: + info.account = ""; + info.password = ""; + D2Bot.setProfile(info.account, info.password); + D2Bot.restart(true); - break; - case sdk.game.locations.OkCenteredErrorPopUp: - info.account = ""; - info.password = ""; - D2Bot.setProfile(info.account, info.password); - D2Bot.restart(true); + break; + case sdk.game.locations.TermsOfUse: + Controls.TermsOfUseAgree.click(); - break; - case sdk.game.locations.TermsOfUse: - Controls.TermsOfUseAgree.click(); + break; + case sdk.game.locations.CreateNewAccount: + Controls.CreateNewAccountName.setText(info.account); + Controls.CreateNewAccountPassword.setText(info.password); + Controls.CreateNewAccountConfirmPassword.setText(info.password); + Controls.CreateNewAccountOk.click(); - break; - case sdk.game.locations.CreateNewAccount: - Controls.CreateNewAccountName.setText(info.account); - Controls.CreateNewAccountPassword.setText(info.password); - Controls.CreateNewAccountConfirmPassword.setText(info.password); - Controls.CreateNewAccountOk.click(); + break; + case sdk.game.locations.PleaseRead: + Controls.PleaseReadOk.click(); - break; - case sdk.game.locations.PleaseRead: - Controls.PleaseReadOk.click(); + break; + case sdk.game.locations.RegisterEmail: + if (Developer.setEmail.enabled + && (!Developer.setEmail.profiles.length || Developer.setEmail.profiles.includes(me.profile)) + && (!Developer.setEmail.realms.length || Developer.setEmail.realms.includes(Profile().gateway.toLowerCase()))) { + ControlAction.setEmail(); + } else { + Controls.EmailDontRegisterContinue.control ? Controls.EmailDontRegisterContinue.click() : Controls.EmailDontRegister.click(); + } - break; - case sdk.game.locations.RegisterEmail: - if (Developer.setEmail.enabled - && (!Developer.setEmail.profiles.length || Developer.setEmail.profiles.includes(me.profile)) - && (!Developer.setEmail.realms.length || Developer.setEmail.realms.includes(Profile().gateway.toLowerCase()))) { - ControlAction.setEmail(); - } else { - Controls.EmailDontRegisterContinue.control ? Controls.EmailDontRegisterContinue.click() : Controls.EmailDontRegister.click(); + break; + default: + break; } - break; - default: - break; + delay(100); } - delay(100); - } + me.blockMouse = false; - me.blockMouse = false; + return true; + }; - return true; -}; + ControlAction.deleteAndRemakeChar = function (info) { + me.blockMouse = true; -ControlAction.deleteAndRemakeChar = function (info) { - me.blockMouse = true; + ControlAction.findCharacter(info); - ControlAction.findCharacter(info); + MainLoop: + // Cycle until in lobby + while (getLocation() !== sdk.game.locations.Lobby) { + switch (getLocation()) { + case sdk.game.locations.CharSelect: + let control = Controls.CharSelectCharInfo0.control; - MainLoop: - // Cycle until in lobby - while (getLocation() !== sdk.game.locations.Lobby) { - switch (getLocation()) { - case sdk.game.locations.CharSelect: - let control = Controls.CharSelectCharInfo0.control; + if (control) { + do { + let text = control.getText(); - if (control) { - do { - let text = control.getText(); + if (text instanceof Array && typeof text[1] === "string" && text[1].toLowerCase() === info.charName.toLowerCase()) { + control.click(); + Controls.CharSelectDelete.click(); + delay(500); + Controls.CharDeleteYes.click(); - if (text instanceof Array && typeof text[1] === "string" && text[1].toLowerCase() === info.charName.toLowerCase()) { - control.click(); - Controls.CharSelectDelete.click(); - delay(500); - Controls.CharDeleteYes.click(); + break MainLoop; + } + } while (control.getNext()); + } - break MainLoop; - } - } while (control.getNext()); - } + break; + case sdk.game.locations.CharSelectNoChars: + break MainLoop; - break; - case sdk.game.locations.CharSelectNoChars: - break MainLoop; + case sdk.game.locations.Disconnected: + case sdk.game.locations.OkCenteredErrorPopUp: + me.blockMouse = false; - case sdk.game.locations.Disconnected: - case sdk.game.locations.OkCenteredErrorPopUp: - me.blockMouse = false; + return false; + default: + break; + } - return false; - default: - break; + delay(100); } - delay(100); - } + me.blockMouse = false; - me.blockMouse = false; + // Delete old files - leaving csv file's for now as I don't think they interfere with the overlay + CharData.delete(true); + DataFile.create(); + CharData.updateData("me", "finalBuild", Starter.profileInfo.tag); + Developer.logPerformance && Tracker.initialize(); + D2Bot.printToConsole("Deleted: " + info.charName + ". Now remaking...", sdk.colors.D2Bot.Gold); + ControlAction.makeCharacter(Starter.profileInfo); - // Delete old files - leaving csv file's for now as I don't think they interfere with the overlay - CharData.delete(true); - DataFile.create(); - CharData.updateData("me", "finalBuild", Starter.profileInfo.tag); - Developer.logPerformance && Tracker.initialize(); - D2Bot.printToConsole("Deleted: " + info.charName + ". Now remaking...", sdk.colors.D2Bot.Gold); - ControlAction.makeCharacter(Starter.profileInfo); + return true; + }; - return true; -}; + ControlAction.saveInfo = function (info) { + // Data-file already exists + if (FileTools.exists("logs/Kolbot-SoloPlay/" + info.realm + "/" + info.charClass + "-" + info.charClass + "-" + info.charName + ".json")) { + return; + } -ControlAction.saveInfo = function (info) { - // Data-file already exists - if (FileTools.exists("logs/Kolbot-SoloPlay/" + info.realm + "/" + info.charClass + "-" + info.charClass + "-" + info.charName + ".json")) { - return; - } + let folder; - let folder; + if (!FileTools.exists("logs/Kolbot-SoloPlay")) { + folder = dopen("logs"); + folder.create("Kolbot-SoloPlay"); + } - if (!FileTools.exists("logs/Kolbot-SoloPlay")) { - folder = dopen("logs"); - folder.create("Kolbot-SoloPlay"); - } + if (!FileTools.exists("logs/Kolbot-SoloPlay/" + info.realm)) { + folder = dopen("logs/Kolbot-SoloPlay"); + folder.create(info.realm); + } - if (!FileTools.exists("logs/Kolbot-SoloPlay/" + info.realm)) { - folder = dopen("logs/Kolbot-SoloPlay"); - folder.create(info.realm); - } + if (!FileTools.exists("logs/Kolbot-SoloPlay/" + info.realm + "/" + info.charClass + "-" + info.charName + ".json")) { + FileTools.writeText("logs/Kolbot-SoloPlay/" + info.realm + "/" + info.charClass + "-" + info.charName + ".json", JSON.stringify(info)); + } + }; - if (!FileTools.exists("logs/Kolbot-SoloPlay/" + info.realm + "/" + info.charClass + "-" + info.charName + ".json")) { - FileTools.writeText("logs/Kolbot-SoloPlay/" + info.realm + "/" + info.charClass + "-" + info.charName + ".json", JSON.stringify(info)); - } -}; + ControlAction.loginAccount = function (info) { + me.blockMouse = true; -ControlAction.loginAccount = function (info) { - me.blockMouse = true; + let locTick; + let tick = getTickCount(); + let realms = { "uswest": 0, "useast": 1, "asia": 2, "europe": 3 }; - let locTick; - let tick = getTickCount(); - let realms = { "uswest": 0, "useast": 1, "asia": 2, "europe": 3 }; + MainLoop: + while (true) { + switch (getLocation()) { + case sdk.game.locations.PreSplash: + break; + case sdk.game.locations.MainMenu: + info.realm && ControlAction.clickRealm(realms[info.realm]); + Controls.BattleNet.click(); - MainLoop: - while (true) { - switch (getLocation()) { - case sdk.game.locations.PreSplash: - break; - case sdk.game.locations.MainMenu: - info.realm && ControlAction.clickRealm(realms[info.realm]); - Controls.BattleNet.click(); + break; + case sdk.game.locations.Login: + Controls.LoginUsername.setText(info.account); + Controls.LoginPassword.setText(info.password); + Controls.Login.click(); - break; - case sdk.game.locations.Login: - Controls.LoginUsername.setText(info.account); - Controls.LoginPassword.setText(info.password); - Controls.Login.click(); + break; + case sdk.game.locations.LoginUnableToConnect: + case sdk.game.locations.RealmDown: + // Unable to connect, let the caller handle it. + me.blockMouse = false; - break; - case sdk.game.locations.LoginUnableToConnect: - case sdk.game.locations.RealmDown: - // Unable to connect, let the caller handle it. - me.blockMouse = false; + return false; + case sdk.game.locations.CharSelect: + break MainLoop; + case sdk.game.locations.SplashScreen: + Controls.SplashScreen.click(); - return false; - case sdk.game.locations.CharSelect: - break MainLoop; - case sdk.game.locations.SplashScreen: - Controls.SplashScreen.click(); + break; + case sdk.game.locations.CharSelectPleaseWait: + case sdk.game.locations.MainMenuConnecting: + case sdk.game.locations.CharSelectConnecting: + break; + case sdk.game.locations.CharSelectNoChars: + // make sure we're not on connecting screen + locTick = getTickCount(); - break; - case sdk.game.locations.CharSelectPleaseWait: - case sdk.game.locations.MainMenuConnecting: - case sdk.game.locations.CharSelectConnecting: - break; - case sdk.game.locations.CharSelectNoChars: - // make sure we're not on connecting screen - locTick = getTickCount(); + while (getTickCount() - locTick < 3000 && getLocation() === sdk.game.locations.CharSelectNoChars) { + delay(25); + } - while (getTickCount() - locTick < 3000 && getLocation() === sdk.game.locations.CharSelectNoChars) { - delay(25); - } + if (getLocation() === sdk.game.locations.CharSelectConnecting) { + break; + } - if (getLocation() === sdk.game.locations.CharSelectConnecting) { + break MainLoop; // break if we're sure we're on empty char screen + case sdk.game.locations.Lobby: + case sdk.game.locations.LobbyChat: + // somehow we are in the lobby? + Control.LobbyQuit.click(); + break; - } + default: + console.log(getLocation()); - break MainLoop; // break if we're sure we're on empty char screen - case sdk.game.locations.Lobby: - case sdk.game.locations.LobbyChat: - // somehow we are in the lobby? - Control.LobbyQuit.click(); - - break; - default: - console.log(getLocation()); + me.blockMouse = false; - me.blockMouse = false; + return false; + } - return false; - } + if (getTickCount() - tick >= 20000) { + return false; + } - if (getTickCount() - tick >= 20000) { - return false; + delay(100); } - delay(100); - } + delay(1000); - delay(1000); + me.blockMouse = false; - me.blockMouse = false; + return getLocation() === sdk.game.locations.CharSelect || getLocation() === sdk.game.locations.CharSelectNoChars; + }; + + Starter.randomNumberString = function (len) { + len === undefined && (len = rand(2, 5)); - return getLocation() === sdk.game.locations.CharSelect || getLocation() === sdk.game.locations.CharSelectNoChars; -}; + let rval = ""; + let vals = "0123456789"; -Starter.randomNumberString = function (len) { - len === undefined && (len = rand(2, 5)); + for (let i = 0; i < len; i += 1) { + rval += vals[rand(0, vals.length - 1)]; + } + + return rval; + }; - let rval = ""; - let vals = "0123456789"; + Starter.charSelectConnecting = function () { + if (getLocation() === sdk.game.locations.CharSelectConnecting) { + // bugged? lets see if we can unbug it + // Click create char button on infinite "connecting" screen + Controls.CharSelectCreate.click() && delay(1000); + Controls.CharSelectExit.click() && delay(1000); - for (let i = 0; i < len; i += 1) { - rval += vals[rand(0, vals.length - 1)]; - } + return (getLocation() !== sdk.game.locations.CharSelectConnecting); + } else { + return true; + } + }; - return rval; -}; + Starter.BNET = ([sdk.game.profiletype.Battlenet, sdk.game.profiletype.OpenBattlenet].includes(Profile().type)); + Starter.LocationEvents.oogCheck = function () { + return (AutoMule.outOfGameCheck() || TorchSystem.outOfGameCheck() || Gambling.outOfGameCheck() || CraftingSystem.outOfGameCheck() || SoloEvents.outOfGameCheck()); + }; -Starter.charSelectConnecting = function () { - if (getLocation() === sdk.game.locations.CharSelectConnecting) { - // bugged? lets see if we can unbug it - // Click create char button on infinite "connecting" screen - Controls.CharSelectCreate.click() && delay(1000); - Controls.CharSelectExit.click() && delay(1000); + Starter.checkDifficulty = function () { + let setDiff = CharData.getStats().me.setDifficulty; + if (setDiff) { + console.debug(setDiff); + Starter.gameInfo.difficulty = setDiff; + } + }; - return (getLocation() !== sdk.game.locations.CharSelectConnecting); - } else { - return true; - } -}; - -Starter.BNET = false; -Starter.LocationEvents.oogCheck = function () { - return (AutoMule.outOfGameCheck() || TorchSystem.outOfGameCheck() || Gambling.outOfGameCheck() || CraftingSystem.outOfGameCheck() || SoloEvents.outOfGameCheck()); -}; - -Starter.checkDifficulty = function () { - let setDiff = CharData.getStats().me.setDifficulty; - if (setDiff) { - Starter.gameInfo.difficulty = setDiff; - } -}; - -Starter.LocationEvents.login = function () { - Starter.inGame && (Starter.inGame = false); - if (getLocation() === sdk.game.locations.MainMenu && Starter.firstRun - && Profile().type === sdk.game.profiletype.SinglePlayer - && Controls.SinglePlayer.click()) { - return; - } - - // Wrong char select screen fix - if (getLocation() === sdk.game.locations.CharSelect) { - hideConsole(); // seems to fix odd crash with single-player characters if the console is open to type in - if ((Profile().type === sdk.game.profiletype.Battlenet && !Controls.CharSelectCurrentRealm.control) - || ((Profile().type !== sdk.game.profiletype.Battlenet && Controls.CharSelectCurrentRealm.control))) { - Controls.CharSelectExit.click(); - + Starter.LocationEvents.login = function () { + Starter.inGame && (Starter.inGame = false); + if (getLocation() === sdk.game.locations.MainMenu && Starter.firstRun + && Profile().type === sdk.game.profiletype.SinglePlayer + && Controls.SinglePlayer.click()) { return; } - } - // Multiple realm botting fix in case of R/D or disconnect - Starter.firstLogin && getLocation() === sdk.game.locations.Login && Controls.CharSelectExit.click(); + // Wrong char select screen fix + if (getLocation() === sdk.game.locations.CharSelect) { + hideConsole(); // seems to fix odd crash with single-player characters if the console is open to type in + if ((Profile().type === sdk.game.profiletype.Battlenet && !Controls.CharSelectCurrentRealm.control) + || ((Profile().type !== sdk.game.profiletype.Battlenet && Controls.CharSelectCurrentRealm.control))) { + Controls.CharSelectExit.click(); - D2Bot.updateStatus("Logging In"); - - try { - // make battlenet accounts/characters - if (Starter.BNET) { - ControlAction.timeoutDelay("Login Delay", Starter.Config.DelayBeforeLogin * 1e3); - D2Bot.updateStatus("Logging in"); - // existing account - if (Starter.profileInfo.account !== "") { - try { - // ControlAction.loginAccount(Starter.profileInfo); - login(me.profile); - } catch (error) { - if (DataFile.getStats().AcctPswd) { - Starter.profileInfo.account = DataFile.getStats().AcctName; - Starter.profileInfo.password = DataFile.getStats().AcctPswd; - - for (let i = 0; i < 5; i++) { - if (ControlAction.loginAccount(Starter.profileInfo)) { - break; - } + return; + } + } - if (getLocation() === sdk.game.locations.CharSelectConnecting) { - if (Starter.charSelectConnecting()) { + // Multiple realm botting fix in case of R/D or disconnect + Starter.firstLogin && getLocation() === sdk.game.locations.Login && Controls.CharSelectExit.click(); + + D2Bot.updateStatus("Logging In"); + + try { + // make battlenet accounts/characters + if (Starter.BNET) { + ControlAction.timeoutDelay("Login Delay", Starter.Config.DelayBeforeLogin * 1e3); + D2Bot.updateStatus("Logging in"); + // existing account + if (Starter.profileInfo.account !== "") { + try { + // ControlAction.loginAccount(Starter.profileInfo); + login(me.profile); + } catch (error) { + if (DataFile.getStats().AcctPswd) { + Starter.profileInfo.account = DataFile.getStats().AcctName; + Starter.profileInfo.password = DataFile.getStats().AcctPswd; + + for (let i = 0; i < 5; i++) { + if (ControlAction.loginAccount(Starter.profileInfo)) { break; } + + if (getLocation() === sdk.game.locations.CharSelectConnecting) { + if (Starter.charSelectConnecting()) { + break; + } + } + + ControlAction.timeoutDelay("Unable to Connect", Starter.Config.UnableToConnectDelay * 6e4); + Starter.profileInfo.account = DataFile.getStats().AcctName; + Starter.profileInfo.password = DataFile.getStats().AcctPswd; + } + } + } + } else { + // new account + if (Starter.profileInfo.account === "") { + if (Starter.Config.GlobalAccount || Starter.Config.GlobalAccountPassword) { + Starter.profileInfo.account = Starter.Config.GlobalAccount.length > 0 ? Starter.Config.GlobalAccount + Starter.randomNumberString(Starter.Config.AccountSuffixLength) : Starter.randomString(12, true); + Starter.profileInfo.password = Starter.Config.GlobalAccountPassword.length > 0 ? Starter.Config.GlobalAccountPassword : Starter.randomString(12, true); + + try { + if (Starter.profileInfo.account.length > 15) throw new Error("Account name exceeds MAXIMUM length (15). Please enter a shorter name or reduce the AccountSuffixLength under StarterConfig"); + if (Starter.profileInfo.password.length > 15) throw new Error("Password name exceeds MAXIMUM length (15). Please enter a shorter name under StarterConfig"); + } catch (e) { + D2Bot.printToConsole("Kolbot-SoloPlay: " + e.message, sdk.colors.D2Bot.Gold); + D2Bot.setProfile("", "", null, "Normal"); + D2Bot.stop(); } - ControlAction.timeoutDelay("Unable to Connect", Starter.Config.UnableToConnectDelay * 6e4); - Starter.profileInfo.account = DataFile.getStats().AcctName; - Starter.profileInfo.password = DataFile.getStats().AcctPswd; + console.log("Kolbot-SoloPlay :: Generated account information. " + (Starter.Config.GlobalAccount.length > 0 ? "Pre-defined " : "Random ") + "account used"); + console.log("Kolbot-SoloPlay :: Generated password information. " + (Starter.Config.GlobalAccountPassword.length > 0 ? "Pre-defined " : "Random ") + "password used"); + ControlAction.timeoutDelay("Generating Account Information", Starter.Config.DelayBeforeLogin * 1e3); + } else { + Starter.profileInfo.account = Starter.randomString(12, true); + Starter.profileInfo.password = Starter.randomString(12, true); + console.log("Generating Random Account Information"); + ControlAction.timeoutDelay("Generating Random Account Information", Starter.Config.DelayBeforeLogin * 1e3); + } + + if (ControlAction.makeAccount(Starter.profileInfo)) { + D2Bot.setProfile(Starter.profileInfo.account, Starter.profileInfo.password, null, "Normal"); + DataFile.updateStats("AcctName", Starter.profileInfo.account); + DataFile.updateStats("AcctPswd", Starter.profileInfo.password); + + return; + } else { + Starter.profileInfo.account = ""; + Starter.profileInfo.password = ""; + D2Bot.setProfile(Starter.profileInfo.account, Starter.profileInfo.password, null, "Normal"); + D2Bot.restart(true); } } } } else { - // new account - if (Starter.profileInfo.account === "") { - if (Starter.Config.GlobalAccount || Starter.Config.GlobalAccountPassword) { - Starter.profileInfo.account = Starter.Config.GlobalAccount.length > 0 ? Starter.Config.GlobalAccount + Starter.randomNumberString(Starter.Config.AccountSuffixLength) : Starter.randomString(12, true); - Starter.profileInfo.password = Starter.Config.GlobalAccountPassword.length > 0 ? Starter.Config.GlobalAccountPassword : Starter.randomString(12, true); - - try { - if (Starter.profileInfo.account.length > 15) throw new Error("Account name exceeds MAXIMUM length (15). Please enter a shorter name or reduce the AccountSuffixLength under StarterConfig"); - if (Starter.profileInfo.password.length > 15) throw new Error("Password name exceeds MAXIMUM length (15). Please enter a shorter name under StarterConfig"); - } catch (e) { - D2Bot.printToConsole("Kolbot-SoloPlay: " + e.message, sdk.colors.D2Bot.Gold); - D2Bot.setProfile("", "", null, "Normal"); - D2Bot.stop(); + // SP/TCP characters + try { + if (getLocation() === sdk.game.locations.MainMenu && Profile().type === sdk.game.profiletype.SinglePlayer) { + Controls.SinglePlayer.click(); + } + Starter.checkDifficulty(); + Starter.LocationEvents.charSelect(getLocation()); + } catch (err) { + console.error(err); + // Try to find the character and if that fails, make character + if (!ControlAction.findCharacter(Starter.profileInfo)) { + // Pop-up that happens when choosing a dead HC char + if (getLocation() === sdk.game.locations.OkCenteredErrorPopUp) { + Controls.OkCentered.click(); // Exit from that pop-up + D2Bot.printToConsole("Character died", sdk.colors.D2Bot.Red); + ControlAction.deleteAndRemakeChar(Starter.profileInfo); + } else { + // If make character fails, check how many characters are on that account + if (!ControlAction.makeCharacter(Starter.profileInfo)) { + // Account is full + if (ControlAction.getCharacters().length >= 18) { + D2Bot.printToConsole("Kolbot-SoloPlay: Account is full", sdk.colors.D2Bot.Orange); + D2Bot.stop(); + } + } } - - console.log("Kolbot-SoloPlay :: Generated account information. " + (Starter.Config.GlobalAccount.length > 0 ? "Pre-defined " : "Random ") + "account used"); - console.log("Kolbot-SoloPlay :: Generated password information. " + (Starter.Config.GlobalAccountPassword.length > 0 ? "Pre-defined " : "Random ") + "password used"); - ControlAction.timeoutDelay("Generating Account Information", Starter.Config.DelayBeforeLogin * 1e3); - } else { - Starter.profileInfo.account = Starter.randomString(12, true); - Starter.profileInfo.password = Starter.randomString(12, true); - console.log("Generating Random Account Information"); - ControlAction.timeoutDelay("Generating Random Account Information", Starter.Config.DelayBeforeLogin * 1e3); } + } + } + } catch (e) { + console.log(e + " " + getLocation()); + } + }; - if (ControlAction.makeAccount(Starter.profileInfo)) { - D2Bot.setProfile(Starter.profileInfo.account, Starter.profileInfo.password, null, "Normal"); - DataFile.updateStats("AcctName", Starter.profileInfo.account); - DataFile.updateStats("AcctPswd", Starter.profileInfo.password); + Starter.LocationEvents.loginError = function () { + let string = ""; + let text = Controls.LoginErrorText.getText(); - return; - } else { - Starter.profileInfo.account = ""; - Starter.profileInfo.password = ""; - D2Bot.setProfile(Starter.profileInfo.account, Starter.profileInfo.password, null, "Normal"); - D2Bot.restart(true); - } + if (text) { + for (let i = 0; i < text.length; i++) { + string += text[i]; + + if (i !== text.length - 1) { + string += " "; } } - } else { - // SP/TCP characters - try { - if (getLocation() === sdk.game.locations.MainMenu && Profile().type === sdk.game.profiletype.SinglePlayer) { - Controls.SinglePlayer.click(); + + switch (string) { + case getLocaleString(sdk.locale.text.UsernameIncludedIllegalChars): + case getLocaleString(sdk.locale.text.UsernameIncludedDisallowedwords): + case getLocaleString(sdk.locale.text.UsernameMustBeAtLeast): + case getLocaleString(sdk.locale.text.PasswordMustBeAtLeast): + case getLocaleString(sdk.locale.text.AccountMustBeAtLeast): + case getLocaleString(sdk.locale.text.PasswordCantBeMoreThan): + case getLocaleString(sdk.locale.text.AccountCantBeMoreThan): + D2Bot.printToConsole(string); + D2Bot.stop(); + + break; + case getLocaleString(sdk.locale.text.InvalidPassword): + D2Bot.updateStatus("Invalid Password"); + D2Bot.printToConsole("Invalid Password"); + ControlAction.timeoutDelay("Invalid password delay", Starter.Config.InvalidPasswordDelay * 6e4); + D2Bot.printToConsole("Invalid Password - Restart"); + D2Bot.restart(); + + break; + case getLocaleString(5208): // Invalid account + case getLocaleString(5239): // An account name already exists + case getLocaleString(5249): // Unable to create account + D2Bot.updateStatus("Invalid Account Name"); + D2Bot.printToConsole("Invalid Account Name"); + Starter.profileInfo.account = ""; + Starter.profileInfo.password = ""; + D2Bot.setProfile(Starter.profileInfo.account, Starter.profileInfo.password); + D2Bot.restart(true); + + break; + case getLocaleString(5202): // cd key intended for another product + case getLocaleString(10915): // lod key intended for another product + D2Bot.updateStatus("Invalid CDKey"); + D2Bot.printToConsole("Invalid CDKey: " + Starter.gameInfo.mpq, sdk.colors.D2Bot.Gold); + D2Bot.CDKeyDisabled(); + + if (Starter.gameInfo.switchKeys) { + ControlAction.timeoutDelay("Key switch delay", Starter.Config.SwitchKeyDelay * 1000); + D2Bot.restart(true); + } else { + D2Bot.stop(); } - Starter.checkDifficulty(); - Starter.LocationEvents.charSelect(getLocation()); - } catch (err) { - console.error(err); - // Try to find the character and if that fails, make character - if (!ControlAction.findCharacter(Starter.profileInfo)) { - // Pop-up that happens when choosing a dead HC char - if (getLocation() === sdk.game.locations.OkCenteredErrorPopUp) { - Controls.OkCentered.click(); // Exit from that pop-up - D2Bot.printToConsole("Character died", sdk.colors.D2Bot.Red); - ControlAction.deleteAndRemakeChar(Starter.profileInfo); - } else { - // If make character fails, check how many characters are on that account - if (!ControlAction.makeCharacter(Starter.profileInfo)) { - // Account is full - if (ControlAction.getCharacters().length >= 18) { - D2Bot.printToConsole("Kolbot-SoloPlay: Account is full", sdk.colors.D2Bot.Orange); - D2Bot.stop(); - } - } - } + + break; + case getLocaleString(5199): + D2Bot.updateStatus("Disabled CDKey"); + D2Bot.printToConsole("Disabled CDKey: " + Starter.gameInfo.mpq, sdk.colors.D2Bot.Gold); + D2Bot.CDKeyDisabled(); + + if (Starter.gameInfo.switchKeys) { + ControlAction.timeoutDelay("Key switch delay", Starter.Config.SwitchKeyDelay * 1000); + D2Bot.restart(true); + } else { + D2Bot.stop(); } - } - } - } catch (e) { - console.log(e + " " + getLocation()); - } -}; -Starter.LocationEvents.loginError = function () { - let string = ""; - let text = Controls.LoginErrorText.getText(); + break; + case getLocaleString(10913): + D2Bot.updateStatus("Disabled LoD CDKey"); + D2Bot.printToConsole("Disabled LoD CDKey: " + Starter.gameInfo.mpq, sdk.colors.D2Bot.Gold); + D2Bot.CDKeyDisabled(); + + if (Starter.gameInfo.switchKeys) { + ControlAction.timeoutDelay("Key switch delay", Starter.Config.SwitchKeyDelay * 1000); + D2Bot.restart(true); + } else { + D2Bot.stop(); + } - if (text) { - for (let i = 0; i < text.length; i++) { - string += text[i]; + break; + case getLocaleString(5347): + D2Bot.updateStatus("Disconnected from battle.net."); + D2Bot.printToConsole("Disconnected from battle.net."); + Controls.OkCentered.click(); + Controls.LoginErrorOk.click(); + + return; + default: + D2Bot.updateStatus("Login Error"); + D2Bot.printToConsole("Login Error - " + string); + + if (Starter.gameInfo.switchKeys) { + ControlAction.timeoutDelay("Key switch delay", Starter.Config.SwitchKeyDelay * 1000); + D2Bot.restart(true); + } else { + D2Bot.stop(); + } - if (i !== text.length - 1) { - string += " "; + break; } } - switch (string) { - case getLocaleString(sdk.locale.text.UsernameIncludedIllegalChars): - case getLocaleString(sdk.locale.text.UsernameIncludedDisallowedwords): - case getLocaleString(sdk.locale.text.UsernameMustBeAtLeast): - case getLocaleString(sdk.locale.text.PasswordMustBeAtLeast): - case getLocaleString(sdk.locale.text.AccountMustBeAtLeast): - case getLocaleString(sdk.locale.text.PasswordCantBeMoreThan): - case getLocaleString(sdk.locale.text.AccountCantBeMoreThan): - D2Bot.printToConsole(string); - D2Bot.stop(); + Controls.LoginErrorOk.click(); + delay(1000); + Controls.CharSelectExit.click(); + }; - break; - case getLocaleString(sdk.locale.text.InvalidPassword): - D2Bot.updateStatus("Invalid Password"); - D2Bot.printToConsole("Invalid Password"); - ControlAction.timeoutDelay("Invalid password delay", Starter.Config.InvalidPasswordDelay * 6e4); - D2Bot.printToConsole("Invalid Password - Restart"); - D2Bot.restart(); + Starter.LocationEvents.charSelect = function (loc) { + let string = ""; + let text = Controls.CharSelectError.getText(); - break; - case getLocaleString(5208): // Invalid account - case getLocaleString(5239): // An account name already exists - case getLocaleString(5249): // Unable to create account - D2Bot.updateStatus("Invalid Account Name"); - D2Bot.printToConsole("Invalid Account Name"); - Starter.profileInfo.account = ""; - Starter.profileInfo.password = ""; - D2Bot.setProfile(Starter.profileInfo.account, Starter.profileInfo.password); - D2Bot.restart(true); + if (text) { + for (let i = 0; i < text.length; i++) { + string += text[i]; - break; - case getLocaleString(5202): // cd key intended for another product - case getLocaleString(10915): // lod key intended for another product - D2Bot.updateStatus("Invalid CDKey"); - D2Bot.printToConsole("Invalid CDKey: " + Starter.gameInfo.mpq, sdk.colors.D2Bot.Gold); - D2Bot.CDKeyDisabled(); - - if (Starter.gameInfo.switchKeys) { - ControlAction.timeoutDelay("Key switch delay", Starter.Config.SwitchKeyDelay * 1000); - D2Bot.restart(true); - } else { - D2Bot.stop(); + if (i !== text.length - 1) { + string += " "; + } } - break; - case getLocaleString(5199): - D2Bot.updateStatus("Disabled CDKey"); - D2Bot.printToConsole("Disabled CDKey: " + Starter.gameInfo.mpq, sdk.colors.D2Bot.Gold); - D2Bot.CDKeyDisabled(); + // CDKey disabled from realm play + if (string === getLocaleString(sdk.locale.text.CdKeyDisabledFromRealm)) { + D2Bot.updateStatus("Realm Disabled CDKey"); + D2Bot.printToConsole("Realm Disabled CDKey: " + Starter.gameInfo.mpq, sdk.colors.D2Bot.Gold); + D2Bot.CDKeyDisabled(); - if (Starter.gameInfo.switchKeys) { - ControlAction.timeoutDelay("Key switch delay", Starter.Config.SwitchKeyDelay * 1000); - D2Bot.restart(true); - } else { - D2Bot.stop(); + if (Starter.gameInfo.switchKeys) { + ControlAction.timeoutDelay("Key switch delay", Starter.Config.SwitchKeyDelay * 1000); + D2Bot.restart(true); + } else { + D2Bot.stop(); + } } + } - break; - case getLocaleString(10913): - D2Bot.updateStatus("Disabled LoD CDKey"); - D2Bot.printToConsole("Disabled LoD CDKey: " + Starter.gameInfo.mpq, sdk.colors.D2Bot.Gold); - D2Bot.CDKeyDisabled(); + if (Starter.deadCheck && ControlAction.deleteAndRemakeChar(Starter.profileInfo)) { + Starter.deadCheck = false; + } - if (Starter.gameInfo.switchKeys) { - ControlAction.timeoutDelay("Key switch delay", Starter.Config.SwitchKeyDelay * 1000); - D2Bot.restart(true); + if (Object.keys(Starter.profileInfo).length) { + if (!ControlAction.findCharacter(Starter.profileInfo)) { + if (Starter.profileInfo.charName === DataFile.getObj().name + && getLocation() !== sdk.game.locations.CharSelectNoChars && ControlAction.getCharacters().length === 0) { + ControlAction.timeoutDelay("[R/D] Character not found ", 18e4); + D2Bot.printToConsole("Avoid Creating New Character - Restart"); + D2Bot.restart(); + } else { + if (!ControlAction.makeCharacter(Starter.profileInfo)) { + if (ControlAction.getCharacters().length >= 18) { + D2Bot.printToConsole("Kolbot-SoloPlay: Account is full", sdk.colors.D2Bot.Red); + D2Bot.stop(); + } + } + } } else { - D2Bot.stop(); + ControlAction.loginCharacter(Starter.profileInfo, false); } + } - break; - case getLocaleString(5347): - D2Bot.updateStatus("Disconnected from battle.net."); - D2Bot.printToConsole("Disconnected from battle.net."); - Controls.OkCentered.click(); - Controls.LoginErrorOk.click(); + if (!Starter.locationTimeout(Starter.Config.ConnectingTimeout * 1e3, loc)) { + Controls.CharSelectExit.click(); + Starter.gameInfo.rdBlocker && D2Bot.restart(); + } + }; - return; - default: - D2Bot.updateStatus("Login Error"); - D2Bot.printToConsole("Login Error - " + string); + Starter.LocationEvents.lobbyChat = function () { + D2Bot.updateStatus("Lobby Chat"); + Starter.lastGameStatus === "pending" && (Starter.gameCount += 1); - if (Starter.gameInfo.switchKeys) { - ControlAction.timeoutDelay("Key switch delay", Starter.Config.SwitchKeyDelay * 1000); - D2Bot.restart(true); - } else { - D2Bot.stop(); - } + if (Starter.inGame || Starter.gameInfo.error) { + !Starter.gameStart && (Starter.gameStart = DataFile.getStats().ingameTick); - break; + if (getTickCount() - Starter.gameStart < Starter.Config.MinGameTime * 1e3) { + ControlAction.timeoutDelay("Min game time wait", Starter.Config.MinGameTime * 1e3 + Starter.gameStart - getTickCount()); + } } - } - Controls.LoginErrorOk.click(); - delay(1000); - Controls.CharSelectExit.click(); -}; + if (Starter.inGame) { + if (oogCheck()) return; -Starter.LocationEvents.charSelect = function (loc) { - let string = ""; - let text = Controls.CharSelectError.getText(); + console.log("updating runs"); + D2Bot.updateRuns(); - if (text) { - for (let i = 0; i < text.length; i++) { - string += text[i]; + Starter.gameCount += 1; + Starter.lastGameStatus = "ready"; + Starter.inGame = false; - if (i !== text.length - 1) { - string += " "; + if (Starter.Config.ResetCount && Starter.gameCount > Starter.Config.ResetCount) { + Starter.gameCount = 1; + DataFile.updateStats("runs", Starter.gameCount); } - } - // CDKey disabled from realm play - if (string === getLocaleString(sdk.locale.text.CdKeyDisabledFromRealm)) { - D2Bot.updateStatus("Realm Disabled CDKey"); - D2Bot.printToConsole("Realm Disabled CDKey: " + Starter.gameInfo.mpq, sdk.colors.D2Bot.Gold); - D2Bot.CDKeyDisabled(); + Starter.chanInfo.afterMsg = Starter.Config.AfterGameMessage; - if (Starter.gameInfo.switchKeys) { - ControlAction.timeoutDelay("Key switch delay", Starter.Config.SwitchKeyDelay * 1000); - D2Bot.restart(true); - } else { - D2Bot.stop(); + if (Starter.chanInfo.afterMsg) { + !Array.isArray(Starter.chanInfo.afterMsg) && (Starter.chanInfo.afterMsg = [Starter.chanInfo.afterMsg]); + + for (let i = 0; i < Starter.chanInfo.afterMsg.length; i++) { + Starter.sayMsg(Starter.chanInfo.afterMsg[i]); + delay(500); + } } } - } - if (Starter.deadCheck && ControlAction.deleteAndRemakeChar(Starter.profileInfo)) { - Starter.deadCheck = false; - } + if (!Starter.chatActionsDone) { + Starter.chatActionsDone = true; - if (Object.keys(Starter.profileInfo).length) { - if (!ControlAction.findCharacter(Starter.profileInfo)) { - if (Starter.profileInfo.charName === DataFile.getObj().name - && getLocation() !== sdk.game.locations.CharSelectNoChars && ControlAction.getCharacters().length === 0) { - ControlAction.timeoutDelay("[R/D] Character not found ", 18e4); - D2Bot.printToConsole("Avoid Creating New Character - Restart"); - D2Bot.restart(); - } else { - if (!ControlAction.makeCharacter(Starter.profileInfo)) { - if (ControlAction.getCharacters().length >= 18) { - D2Bot.printToConsole("Kolbot-SoloPlay: Account is full", sdk.colors.D2Bot.Red); - D2Bot.stop(); + Starter.chanInfo.joinChannel = Starter.Config.JoinChannel; + Starter.chanInfo.firstMsg = Starter.Config.FirstJoinMessage; + + if (Starter.chanInfo.joinChannel) { + !Array.isArray(Starter.chanInfo.joinChannel) && (Starter.chanInfo.joinChannel = [Starter.chanInfo.joinChannel]); + !Array.isArray(Starter.chanInfo.firstMsg) && (Starter.chanInfo.firstMsg = [Starter.chanInfo.firstMsg]); + + for (let i = 0; i < Starter.chanInfo.joinChannel.length; i++) { + ControlAction.timeoutDelay("Chat delay", Starter.Config.ChatActionsDelay * 1e3); + + if (ControlAction.joinChannel(Starter.chanInfo.joinChannel[i])) { + Starter.useChat = true; + } else { + console.log("ÿc1Unable to join channel, disabling chat messages."); + + Starter.useChat = false; + } + + if (Starter.chanInfo.firstMsg[i] !== "") { + Starter.sayMsg(Starter.chanInfo.firstMsg[i]); + delay(500); } } } - } else { - ControlAction.loginCharacter(Starter.profileInfo, false); } - } - - if (!Starter.locationTimeout(Starter.Config.ConnectingTimeout * 1e3, loc)) { - Controls.CharSelectExit.click(); - Starter.gameInfo.rdBlocker && D2Bot.restart(); - } -}; - -Starter.LocationEvents.lobbyChat = function () { - D2Bot.updateStatus("Lobby Chat"); - Starter.lastGameStatus === "pending" && (Starter.gameCount += 1); - if (Starter.inGame || Starter.gameInfo.error) { - !Starter.gameStart && (Starter.gameStart = DataFile.getStats().ingameTick); + // Announce game + Starter.chanInfo.announce = Starter.Config.AnnounceGames; - if (getTickCount() - Starter.gameStart < Starter.Config.MinGameTime * 1e3) { - ControlAction.timeoutDelay("Min game time wait", Starter.Config.MinGameTime * 1e3 + Starter.gameStart - getTickCount()); + if (Starter.chanInfo.announce) { + Starter.sayMsg("Next game is " + Starter.gameInfo.gameName + Starter.gameCount + (Starter.gameInfo.gamePass === "" ? "" : "//" + Starter.gameInfo.gamePass)); } - } - if (Starter.inGame) { - if (oogCheck()) return; + Starter.LocationEvents.openCreateGameWindow(); + }; - console.log("updating runs"); - D2Bot.updateRuns(); + const oogCheck = () => ( + AutoMule.outOfGameCheck() || TorchSystem.outOfGameCheck() + || Gambling.outOfGameCheck() || CraftingSystem.outOfGameCheck() || SoloEvents.outOfGameCheck() + ); + + locations[sdk.game.locations.PreSplash] = () => ControlAction.click(); + locations[sdk.game.locations.GatewaySelect] = () => Controls.GatewayCancel.click(); + locations[sdk.game.locations.SplashScreen] = () => Starter.LocationEvents.login(); + locations[sdk.game.locations.MainMenu] = () => Starter.LocationEvents.login(); + locations[sdk.game.locations.Login] = () => Starter.LocationEvents.login(); + locations[sdk.game.locations.OtherMultiplayer] = () => Starter.LocationEvents.otherMultiplayerSelect(); + locations[sdk.game.locations.TcpIp] = () => Profile().type === sdk.game.profiletype.TcpIpHost ? Controls.TcpIpHost.click() : Controls.TcpIpCancel.click(); + locations[sdk.game.locations.TcpIpEnterIp] = () => Controls.TcpIpCancel.click(); + locations[sdk.game.locations.LoginError] = () => Starter.LocationEvents.loginError(); + locations[sdk.game.locations.LoginUnableToConnect] = () => Starter.LocationEvents.unableToConnect(); + locations[sdk.game.locations.TcpIpUnableToConnect] = () => Starter.LocationEvents.unableToConnect(); + locations[sdk.game.locations.CdKeyInUse] = () => Starter.LocationEvents.loginError(); + locations[sdk.game.locations.InvalidCdKey] = () => Starter.LocationEvents.loginError(); + locations[sdk.game.locations.RealmDown] = () => Starter.LocationEvents.realmDown(); + locations[sdk.game.locations.Disconnected] = () => { + ControlAction.timeoutDelay("Disconnected", 3000); + Controls.OkCentered.click(); + }; + locations[sdk.game.locations.RegisterEmail] = () => Controls.EmailDontRegisterContinue.control ? Controls.EmailDontRegisterContinue.click() : Controls.EmailDontRegister.click(); + locations[sdk.game.locations.MainMenuConnecting] = (loc) => !Starter.locationTimeout(Starter.Config.ConnectingTimeout * 1e3, loc) && Controls.LoginCancelWait.click(); + locations[sdk.game.locations.CharSelectPleaseWait] = (loc) => !Starter.locationTimeout(Starter.Config.PleaseWaitTimeout * 1e3, loc) && Controls.OkCentered.click(); + locations[sdk.game.locations.CharSelect] = (loc) => Starter.LocationEvents.charSelect(loc); + locations[sdk.game.locations.CharSelectConnecting] = (loc) => Starter.LocationEvents.charSelect(loc); + locations[sdk.game.locations.CharSelectNoChars] = (loc) => Starter.LocationEvents.charSelect(loc); + locations[sdk.game.locations.SelectDifficultySP] = () => Starter.LocationEvents.selectDifficultySP(); + locations[sdk.game.locations.CharacterCreate] = (loc) => !Starter.locationTimeout(5e3, loc) && Controls.CharSelectExit.click(); + locations[sdk.game.locations.ServerDown] = () => { + ControlAction.timeoutDelay("Server Down", Time.minutes(5)); + Controls.OkCentered.click(); + }; + locations[sdk.game.locations.LobbyPleaseWait] = (loc) => !Starter.locationTimeout(Starter.Config.PleaseWaitTimeout * 1e3, loc) && Controls.OkCentered.click(); + locations[sdk.game.locations.Lobby] = () => { + D2Bot.updateStatus("Lobby"); + ControlAction.saveInfo(Starter.profileInfo); - Starter.gameCount += 1; - Starter.lastGameStatus = "ready"; - Starter.inGame = false; + me.blockKeys = false; + + !Starter.firstLogin && (Starter.firstLogin = true); + Starter.lastGameStatus === "pending" && (Starter.gameCount += 1); - if (Starter.Config.ResetCount && Starter.gameCount > Starter.Config.ResetCount) { - Starter.gameCount = 1; - DataFile.updateStats("runs", Starter.gameCount); + if (Starter.Config.PingQuitDelay && Starter.pingQuit) { + ControlAction.timeoutDelay("Ping Delay", Starter.Config.PingQuitDelay * 1e3); + Starter.pingQuit = false; } - Starter.chanInfo.afterMsg = Starter.Config.AfterGameMessage; + if (Starter.Config.JoinChannel !== "" && Controls.LobbyEnterChat.click()) return; - if (Starter.chanInfo.afterMsg) { - !Array.isArray(Starter.chanInfo.afterMsg) && (Starter.chanInfo.afterMsg = [Starter.chanInfo.afterMsg]); + if (Starter.inGame || Starter.gameInfo.error) { + !Starter.gameStart && (Starter.gameStart = DataFile.getStats().ingameTick); - for (let i = 0; i < Starter.chanInfo.afterMsg.length; i++) { - Starter.sayMsg(Starter.chanInfo.afterMsg[i]); - delay(500); + if (getTickCount() - Starter.gameStart < Starter.Config.MinGameTime * 1e3 && !joinInfo) { + ControlAction.timeoutDelay("Min game time wait", Starter.Config.MinGameTime * 1e3 + Starter.gameStart - getTickCount()); } } - } - if (!Starter.chatActionsDone) { - Starter.chatActionsDone = true; + if (Starter.inGame) { + if (oogCheck()) return; - Starter.chanInfo.joinChannel = Starter.Config.JoinChannel; - Starter.chanInfo.firstMsg = Starter.Config.FirstJoinMessage; + D2Bot.updateRuns(); - if (Starter.chanInfo.joinChannel) { - !Array.isArray(Starter.chanInfo.joinChannel) && (Starter.chanInfo.joinChannel = [Starter.chanInfo.joinChannel]); - !Array.isArray(Starter.chanInfo.firstMsg) && (Starter.chanInfo.firstMsg = [Starter.chanInfo.firstMsg]); + Starter.gameCount += 1; + Starter.lastGameStatus = "ready"; + Starter.inGame = false; - for (let i = 0; i < Starter.chanInfo.joinChannel.length; i++) { - ControlAction.timeoutDelay("Chat delay", Starter.Config.ChatActionsDelay * 1e3); + if (Starter.Config.ResetCount && Starter.gameCount > Starter.Config.ResetCount) { + Starter.gameCount = 1; + DataFile.updateStats("runs", Starter.gameCount); + } + } - if (ControlAction.joinChannel(Starter.chanInfo.joinChannel[i])) { - Starter.useChat = true; - } else { - console.log("ÿc1Unable to join channel, disabling chat messages."); + Starter.LocationEvents.openCreateGameWindow(); + }; + locations[sdk.game.locations.LobbyChat] = () => Starter.LocationEvents.lobbyChat(); + locations[sdk.game.locations.CreateGame] = (loc) => { + ControlAction.timeoutDelay("Create Game Delay", Starter.Config.DelayBeforeLogin * 1e3); + D2Bot.updateStatus("Creating Game"); + + if (typeof Starter.Config.CharacterDifference === "number") { + Controls.CharacterDifference.disabled === sdk.game.controls.Disabled && Controls.CharacterDifferenceButton.click(); + Controls.CharacterDifference.setText(Starter.Config.CharacterDifference.toString()); + } else if (!Starter.Config.CharacterDifference && Controls.CharacterDifference.disabled === 5) { + Controls.CharacterDifferenceButton.click(); + } - Starter.useChat = false; - } + typeof Starter.Config.MaxPlayerCount === "number" && Controls.MaxPlayerCount.setText(Starter.Config.MaxPlayerCount.toString()); - if (Starter.chanInfo.firstMsg[i] !== "") { - Starter.sayMsg(Starter.chanInfo.firstMsg[i]); - delay(500); - } - } + D2Bot.requestGameInfo(); + delay(500); + + // todo - really don't need use profiles set difficulty for online. Only single player so re-write difficulty stuff + Starter.checkDifficulty(); + + Starter.gameInfo.gameName = DataFile.getStats().gameName; + Starter.gameInfo.gamePass = Starter.randomString(5, true); + + switch (true) { + case Starter.gameInfo.gameName === "": + case Starter.gameInfo.gameName === "Name": + Starter.gameInfo.gameName = Starter.profileInfo.charName.substring(0, 7) + "-" + Starter.randomString(3, false) + "-"; + + break; } - } - // Announce game - Starter.chanInfo.announce = Starter.Config.AnnounceGames; + // FTJ handler + if (Starter.lastGameStatus === "pending") { + Starter.isUp = "no"; + + D2Bot.printToConsole("Failed to create game"); + ControlAction.timeoutDelay("FTJ delay", Starter.Config.FTJDelay * 1e3); + D2Bot.updateRuns(); + } - if (Starter.chanInfo.announce) { - Starter.sayMsg("Next game is " + Starter.gameInfo.gameName + Starter.gameCount + (Starter.gameInfo.gamePass === "" ? "" : "//" + Starter.gameInfo.gamePass)); - } + ControlAction.createGame((Starter.gameInfo.gameName + Starter.gameCount), Starter.gameInfo.gamePass, Starter.gameInfo.difficulty, Starter.Config.CreateGameDelay * 1000); + Starter.lastGameStatus = "pending"; + Starter.setNextGame(Starter.gameInfo); + Starter.locationTimeout(10000, loc); + }; + locations[sdk.game.locations.GameNameExists] = () => { + Controls.CreateGameWindow.click(); + Starter.gameCount += 1; + Starter.lastGameStatus = "ready"; + }; + locations[sdk.game.locations.WaitingInLine] = () => Starter.LocationEvents.waitingInLine(); + locations[sdk.game.locations.JoinGame] = () => Starter.LocationEvents.openCreateGameWindow(); + locations[sdk.game.locations.Ladder] = () => Starter.LocationEvents.openCreateGameWindow(); + locations[sdk.game.locations.ChannelList] = () => Starter.LocationEvents.openCreateGameWindow(); + locations[sdk.game.locations.LobbyLostConnection] = () => { + ControlAction.timeoutDelay("LostConnection", 3000); + Controls.OkCentered.click(); + }; + locations[sdk.game.locations.GameDoesNotExist] = () => Starter.LocationEvents.gameDoesNotExist(); + locations[sdk.game.locations.GameIsFull] = () => Starter.LocationEvents.openCreateGameWindow(); +})(); - Starter.LocationEvents.openCreateGameWindow(); -}; From 4dd911d6297b3a89f85c2f8077d63bc755f98140 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Sat, 4 Feb 2023 00:55:28 -0500 Subject: [PATCH 009/263] Some general cleanup - nothing really important here, removed Scripts.SoloPlay from config files. Doesn't exist anymore as soloplay isn't run as a script - exported the levelUpHandler out of autobuild - lots of comment spacing fixes - Track gold at start and end of town chores, mostly just for debugging currently - Turned PotData into a UMD module so it can be included normally if wanted - Fix some global scope leaks from the function files --- libs/SoloPlay/Config/Amazon.js | 1 - libs/SoloPlay/Config/Assassin.js | 1 - libs/SoloPlay/Config/Barbarian.js | 1 - libs/SoloPlay/Config/Druid.js | 1 - libs/SoloPlay/Config/Necromancer.js | 1 - libs/SoloPlay/Config/Paladin.js | 1 - libs/SoloPlay/Config/Sorceress.js | 4 ++-- libs/SoloPlay/Functions/AutoBuildOverrides.js | 1 + libs/SoloPlay/Functions/StorageOverrides.js | 8 +++++-- libs/SoloPlay/Functions/TownOverrides.js | 2 ++ libs/SoloPlay/Modules/Clear.js | 5 ++-- libs/SoloPlay/Modules/Events.js | 23 ++++++++++++------ libs/SoloPlay/Modules/LocaleStringID.js | 4 ++-- libs/SoloPlay/Modules/MissileData.js | 4 ++-- libs/SoloPlay/Modules/Mock.js | 20 +++++++++------- libs/SoloPlay/Modules/PotData.js | 20 ++++++++++++---- libs/SoloPlay/Modules/TownGuard.js | 23 ------------------ libs/SoloPlay/Scripts/den.js | 6 ++--- libs/SoloPlay/Scripts/hellforge.js | 7 ++++++ libs/SoloPlay/Scripts/maggotlair.js | 4 ++-- libs/SoloPlay/Scripts/mephisto.js | 4 ++-- libs/SoloPlay/Scripts/nith.js | 9 +++---- libs/SoloPlay/Scripts/worldstone.js | 24 ++++++++++++++++++- 23 files changed, 101 insertions(+), 73 deletions(-) diff --git a/libs/SoloPlay/Config/Amazon.js b/libs/SoloPlay/Config/Amazon.js index 8037b6c3..3237f980 100644 --- a/libs/SoloPlay/Config/Amazon.js +++ b/libs/SoloPlay/Config/Amazon.js @@ -23,7 +23,6 @@ SetUp.include(); /* Script */ - Scripts.SoloPlay = true; SetUp.config(); /* Chicken configuration. */ diff --git a/libs/SoloPlay/Config/Assassin.js b/libs/SoloPlay/Config/Assassin.js index 48f7d4d8..91e7d4c8 100644 --- a/libs/SoloPlay/Config/Assassin.js +++ b/libs/SoloPlay/Config/Assassin.js @@ -21,7 +21,6 @@ SetUp.include(); /* Script */ - Scripts.SoloPlay = true; SetUp.config(); /* Chicken configuration. */ diff --git a/libs/SoloPlay/Config/Barbarian.js b/libs/SoloPlay/Config/Barbarian.js index 9af1b0be..2e95b90e 100644 --- a/libs/SoloPlay/Config/Barbarian.js +++ b/libs/SoloPlay/Config/Barbarian.js @@ -23,7 +23,6 @@ SetUp.include(); /* Script */ - Scripts.SoloPlay = true; SetUp.config(); /* Chicken configuration. */ diff --git a/libs/SoloPlay/Config/Druid.js b/libs/SoloPlay/Config/Druid.js index 950a03a9..733a3ee4 100644 --- a/libs/SoloPlay/Config/Druid.js +++ b/libs/SoloPlay/Config/Druid.js @@ -23,7 +23,6 @@ SetUp.include(); /* Script */ - Scripts.SoloPlay = true; SetUp.config(); /* Chicken configuration. */ diff --git a/libs/SoloPlay/Config/Necromancer.js b/libs/SoloPlay/Config/Necromancer.js index a054a32f..6b2a44a4 100644 --- a/libs/SoloPlay/Config/Necromancer.js +++ b/libs/SoloPlay/Config/Necromancer.js @@ -22,7 +22,6 @@ SetUp.include(); /* Script */ - Scripts.SoloPlay = true; SetUp.config(); /* Chicken configuration. */ diff --git a/libs/SoloPlay/Config/Paladin.js b/libs/SoloPlay/Config/Paladin.js index f679d386..5e234268 100644 --- a/libs/SoloPlay/Config/Paladin.js +++ b/libs/SoloPlay/Config/Paladin.js @@ -31,7 +31,6 @@ SetUp.include(); /* Script */ - Scripts.SoloPlay = true; SetUp.config(); /* Chicken configuration. */ diff --git a/libs/SoloPlay/Config/Sorceress.js b/libs/SoloPlay/Config/Sorceress.js index 2ea3e064..c58df758 100644 --- a/libs/SoloPlay/Config/Sorceress.js +++ b/libs/SoloPlay/Config/Sorceress.js @@ -45,8 +45,8 @@ /* Pickit configuration. */ Config.PickRange = 40; - // Config.PickitFiles.push("kolton.nip"); - // Config.PickitFiles.push("LLD.nip"); + // Config.PickitFiles.push("kolton.nip"); + // Config.PickitFiles.push("test.nip"); /* Gambling configuration. */ Config.Gamble = true; diff --git a/libs/SoloPlay/Functions/AutoBuildOverrides.js b/libs/SoloPlay/Functions/AutoBuildOverrides.js index 7aa3d0b4..041efb71 100644 --- a/libs/SoloPlay/Functions/AutoBuildOverrides.js +++ b/libs/SoloPlay/Functions/AutoBuildOverrides.js @@ -111,6 +111,7 @@ const AutoBuild = new function AutoBuild () { debug && log.call(this, result); } + this.levelUpHandler = levelUpHandler; this.print = myPrint; this.initialize = initialize; this.applyConfigUpdates = applyConfigUpdates; diff --git a/libs/SoloPlay/Functions/StorageOverrides.js b/libs/SoloPlay/Functions/StorageOverrides.js index 3f8ae781..f5f81b0c 100644 --- a/libs/SoloPlay/Functions/StorageOverrides.js +++ b/libs/SoloPlay/Functions/StorageOverrides.js @@ -239,9 +239,13 @@ includeIfNotIncluded("SoloPlay/Tools/Developer.js"); * @param {number[]} priorityClassIds */ Container.prototype.FindSpot = function (item, reverseX, reverseY, priorityClassIds) { - // Make sure it's a valid item + // Make sure it's a valid item if (!item) return false; + /** + * @todo review this to see why it sometimes fails when there is actually enough room + */ + let x, y, nx, ny, makeSpot; let startX, startY, endX, endY, xDir = 1, yDir = 1; @@ -577,7 +581,7 @@ includeIfNotIncluded("SoloPlay/Tools/Developer.js"); /** * @type {storage} Storage */ - let Storage = new function () { + const Storage = new function () { this.Init = () => { this.StashY = me.classic ? 4 : Developer.plugyMode ? 10 : 8; this.Inventory = new Container("Inventory", 10, 4, 3); diff --git a/libs/SoloPlay/Functions/TownOverrides.js b/libs/SoloPlay/Functions/TownOverrides.js index 78722d69..b8e698d6 100644 --- a/libs/SoloPlay/Functions/TownOverrides.js +++ b/libs/SoloPlay/Functions/TownOverrides.js @@ -1261,6 +1261,7 @@ Town.doChores = function (repair = false, givenTasks = {}) { console.info(true); console.time("doChores"); + console.debug("doChores Inital Gold :: " + me.gold); !me.inTown && Town.goToTown(); @@ -1319,6 +1320,7 @@ Town.doChores = function (repair = false, givenTasks = {}) { } delay(300); + console.debug("doChores Ending Gold :: " + me.gold); console.info(false, null, "doChores"); Town.lastInteractedNPC.reset(); // unassign diff --git a/libs/SoloPlay/Modules/Clear.js b/libs/SoloPlay/Modules/Clear.js index fe1eab03..4369fc1a 100644 --- a/libs/SoloPlay/Modules/Clear.js +++ b/libs/SoloPlay/Modules/Clear.js @@ -3,10 +3,9 @@ var __importDefault = (this && this.__importDefault) || function (mod) { }; (function (factory) { if (typeof module === "object" && typeof module.exports === "object") { - var v = factory(require, exports); + const v = factory(require, exports); if (v !== undefined) module.exports = v; - } - else if (typeof define === "function" && define.amd) { + } else if (typeof define === "function" && define.amd) { define(["require", "exports", "../../modules/sdk", "./Events", "./Coords", "./MissileData"], factory); } })(function (require, exports) { diff --git a/libs/SoloPlay/Modules/Events.js b/libs/SoloPlay/Modules/Events.js index 26ec7cb4..915d8c07 100644 --- a/libs/SoloPlay/Modules/Events.js +++ b/libs/SoloPlay/Modules/Events.js @@ -1,9 +1,10 @@ -const __spreadArray = (this && this.__spreadArray) || function (to, from) { - for (let i = 0, il = from.length, j = to.length; i < il; i++, j++) { - to[j] = from[i]; - } - return to; -}; +/** + * @filename Events.js + * @author Jaenster + * @desc Transpiled UMD event module + * + */ + (function (factory) { if (typeof module === "object" && typeof module.exports === "object") { let v = factory(require, exports); @@ -17,7 +18,14 @@ const __spreadArray = (this && this.__spreadArray) || function (to, from) { exports.Events = void 0; let handlers = new WeakMap(); let onceHandlers = new WeakMap(); - let Events = /** @class */ (function () { + const __spreadArray = (this && this.__spreadArray) || function (to, from) { + for (let i = 0, il = from.length, j = to.length; i < il; i++, j++) { + to[j] = from[i]; + } + return to; + }; + // eslint-disable-next-line no-var + var Events = /** @class */ (function () { function Events() { } // Generic type S to give to EventHandler to typehint this function gets the same this as where the event is registered @@ -28,6 +36,7 @@ const __spreadArray = (this && this.__spreadArray) || function (to, from) { !map.has(key) ? map.set(key, set = []) : set = map.get(key); // Add this handler, since it has to be unique we dont need to check if it exists set.push(handler); + console.debug(set); return this; }; Events.prototype.once = function (key, handler) { diff --git a/libs/SoloPlay/Modules/LocaleStringID.js b/libs/SoloPlay/Modules/LocaleStringID.js index 7f4f9e3d..133617d5 100644 --- a/libs/SoloPlay/Modules/LocaleStringID.js +++ b/libs/SoloPlay/Modules/LocaleStringID.js @@ -3,7 +3,7 @@ * @author Nishimura-Katsuo * @desc locale string indexes from NameStr ids */ -(function (module, require) { +(function (module) { let LocaleStringID = { "WarrivAct1IntroGossip1": 0, "WarrivAct1IntroPalGossip1": 1, @@ -7803,4 +7803,4 @@ LocaleStringName: LocaleStringName, LocaleStringID: LocaleStringID }; -})(module, require); +})(module); diff --git a/libs/SoloPlay/Modules/MissileData.js b/libs/SoloPlay/Modules/MissileData.js index be798b02..7ddf9996 100644 --- a/libs/SoloPlay/Modules/MissileData.js +++ b/libs/SoloPlay/Modules/MissileData.js @@ -1,7 +1,7 @@ (function (module, require) { /** - * MissilesData - */ + * MissilesData + */ const MISSILES_COUNT = 385; const MissilesData = Array(MISSILES_COUNT); diff --git a/libs/SoloPlay/Modules/Mock.js b/libs/SoloPlay/Modules/Mock.js index d8276f0a..8ed9dacf 100644 --- a/libs/SoloPlay/Modules/Mock.js +++ b/libs/SoloPlay/Modules/Mock.js @@ -1,3 +1,4 @@ +/* eslint-disable no-var */ var __extends = (this && this.__extends) || (function () { var extendStatics = function (d, b) { extendStatics = Object.setPrototypeOf || @@ -6,16 +7,16 @@ var __extends = (this && this.__extends) || (function () { return extendStatics(d, b); }; return function (d, b) { - if (typeof b !== "function" && b !== null) - throw new TypeError("Class extends value " + String(b) + " is not a constructor or null"); + if (typeof b !== "function" && b !== null) throw new TypeError("Class extends value " + String(b) + " is not a constructor or null"); extendStatics(d, b); function __() { this.constructor = d; } d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); }; })(); var __spreadArray = (this && this.__spreadArray) || function (to, from) { - for (var i = 0, il = from.length, j = to.length; i < il; i++, j++) + for (var i = 0, il = from.length, j = to.length; i < il; i++, j++) { to[j] = from[i]; + } return to; }; var __importDefault = (this && this.__importDefault) || function (mod) { @@ -25,8 +26,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) { if (typeof module === "object" && typeof module.exports === "object") { var v = factory(require, exports); if (v !== undefined) module.exports = v; - } - else if (typeof define === "function" && define.amd) { + } else if (typeof define === "function" && define.amd) { define(["require", "exports", "./utilities", "../../modules/sdk"], factory); } })(function (require, exports) { @@ -69,7 +69,6 @@ var __importDefault = (this && this.__importDefault) || function (mod) { Mockable.prototype.getItems = function () { return this.overrides.items || []; }; - ; Mockable.prototype.toJSON = function () { var _this = this; var obj = {}; @@ -104,6 +103,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) { // its shifted with 8 bytes return (item.getStat(-1) || []) .map(function (_a) { + // eslint-disable-next-line no-unused-vars var major = _a[0], minor = _a[1], value = _a[2]; return [major, minor, item.getStat(major, minor)]; }); @@ -122,7 +122,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) { if (settings === void 0) { settings = {}; } // Add to settings var initializer = Object.keys(item) - .filter(function (key) { return typeof item[key] !== 'function'; }) + .filter(function (key) { return typeof item[key] !== "function"; }) .reduce(function (acc, key) { acc[key] = item[key]; return acc; @@ -154,8 +154,10 @@ var __importDefault = (this && this.__importDefault) || function (mod) { MockItem.fromPlayer = function (from) { if (from === void 0) { from = me; } return from.getItemsEx() - .filter(function (item) { return item.location === sdk_1.default.storage.Equipment - || (item.location === sdk_1.default.storage.Inventory && [603, 604, 605].indexOf(item.classid) > -1); }) + .filter(function (item) { + return item.location === sdk_1.default.storage.Equipment + || (item.location === sdk_1.default.storage.Inventory && [603, 604, 605].indexOf(item.classid) > -1); + }) .map(function (x) { return MockItem.fromItem(x); }); }; return MockItem; diff --git a/libs/SoloPlay/Modules/PotData.js b/libs/SoloPlay/Modules/PotData.js index f4707827..22da0c7a 100644 --- a/libs/SoloPlay/Modules/PotData.js +++ b/libs/SoloPlay/Modules/PotData.js @@ -1,10 +1,20 @@ /** * @description Data about pots - * @author ryancrunchi + * @author ryancrunchi, theBGuy + * @type UMD module */ -// eslint-disable-next-line no-unused-vars -(function (module, require) { +(function (root, factory) { + if (typeof module === "object" && typeof module.exports === "object") { + const v = factory(); + if (v !== undefined) module.exports = v; + } else if (typeof define === "function" && define.amd) { + define([], factory); + } else { + root.PotData = factory(); + } +}(this, function () { + "use strict"; const PotData = { pots: [], getMpPots: function () { @@ -110,5 +120,5 @@ ] }; - module.exports = PotData; -})(module, require); + return PotData; +})); diff --git a/libs/SoloPlay/Modules/TownGuard.js b/libs/SoloPlay/Modules/TownGuard.js index 5fb7b3a1..65ecd1da 100644 --- a/libs/SoloPlay/Modules/TownGuard.js +++ b/libs/SoloPlay/Modules/TownGuard.js @@ -59,20 +59,6 @@ }).update; - Worker.runInBackground.ping = (new function () { - // recv heartbeat - Messaging.on("TownGuard", (data => typeof data === "object" && data && data.hasOwnProperty("heartbeat"))); - - this.update = function () { - // Only deal with this shit if default is paused - townchicken - const script = getScript("default.dbj"); - if (script && script.running) { - return true; - } - return true; - }; - }).update; - let quiting = false; addEventListener("scriptmsg", data => data === "quit" && (quiting = true)); @@ -88,15 +74,6 @@ return true; }); - let timer = getTickCount(); - Worker.runInBackground.heartbeatForTownGuard = function () { - if ((getTickCount() - timer) < 1000 || (timer = getTickCount()) && false) return true; - - // Every second or so, we send a heartbeat tick - Messaging.send({TownGuard: {heartbeat: getTickCount()}}); - - return true; - }; break; } case "loaded": { diff --git a/libs/SoloPlay/Scripts/den.js b/libs/SoloPlay/Scripts/den.js index c7457989..9e68d0a1 100644 --- a/libs/SoloPlay/Scripts/den.js +++ b/libs/SoloPlay/Scripts/den.js @@ -86,7 +86,7 @@ function den () { Attack.clear(20); Pather.moveToExit(sdk.areas.DenofEvil, true); - this.denLightsListener = function (bytes = []) { + const denLightsListener = function (bytes = []) { if (!bytes.length) return; // d2gs unique event - den lights if (bytes[0] === 0x89) { @@ -95,7 +95,7 @@ function den () { }; if (me.inArea(sdk.areas.DenofEvil)) { - addEventListener("gamepacket", this.denLightsListener); + addEventListener("gamepacket", denLightsListener); const Worker = require("../../modules/Worker"); let corpsefire; let corpseTick = getTickCount(); @@ -164,7 +164,7 @@ function den () { } catch (e) { // } finally { - removeEventListener("gamepacket", this.denLightsListener); + removeEventListener("gamepacket", denLightsListener); SoloEvents.finishDen(); killTracker = true; me.getStat(sdk.stats.NewSkills) > 0 && AutoSkill.init(Config.AutoSkill.Build, Config.AutoSkill.Save); diff --git a/libs/SoloPlay/Scripts/hellforge.js b/libs/SoloPlay/Scripts/hellforge.js index 7030be58..c481a5dd 100644 --- a/libs/SoloPlay/Scripts/hellforge.js +++ b/libs/SoloPlay/Scripts/hellforge.js @@ -16,6 +16,13 @@ function hellforge () { Pather.checkWP(sdk.areas.RiverofFlame, true) ? Pather.useWaypoint(sdk.areas.RiverofFlame) : Pather.getWP(sdk.areas.RiverofFlame); Precast.doPrecast(true); + /** + * @todo + * - Calculate safe spots to cast from so we can kill heph from a distance + * - Possibly try luring the rest of the monsters away from the forge? + * - Generate path and use callback to stop after we detect heph in range instead of moving all the way to the forge + */ + if (!Pather.moveToPreset(me.area, sdk.unittype.Object, sdk.quest.chest.HellForge)) { console.warn("ÿc8Kolbot-SoloPlayÿc0: Failed to move to Hephasto"); } diff --git a/libs/SoloPlay/Scripts/maggotlair.js b/libs/SoloPlay/Scripts/maggotlair.js index 1a286cd1..1bb367cd 100644 --- a/libs/SoloPlay/Scripts/maggotlair.js +++ b/libs/SoloPlay/Scripts/maggotlair.js @@ -6,7 +6,7 @@ */ function maggotlair () { - this.clearBeetles = function () { + const clearBeetles = function () { let room = getRoom(); if (!room) return false; @@ -50,7 +50,7 @@ function maggotlair () { Pather.checkWP(sdk.areas.FarOasis, true) ? Pather.useWaypoint(sdk.areas.FarOasis) : Pather.getWP(sdk.areas.FarOasis); Precast.doPrecast(true); [sdk.areas.MaggotLairLvl1, sdk.areas.MaggotLairLvl2, sdk.areas.MaggotLairLvl3].forEach((mLair, i) => { - Pather.moveToExit(mLair, true) && this.clearBeetles(); + Pather.moveToExit(mLair, true) && clearBeetles(); console.log("Cleared mLair " + (i + 1)); }); diff --git a/libs/SoloPlay/Scripts/mephisto.js b/libs/SoloPlay/Scripts/mephisto.js index c895da3c..8925ce6c 100644 --- a/libs/SoloPlay/Scripts/mephisto.js +++ b/libs/SoloPlay/Scripts/mephisto.js @@ -6,7 +6,7 @@ */ function mephisto () { - this.killCouncil = function () { + const killCouncil = function () { let coords = [17600, 8125, 17600, 8015, 17643, 8068]; for (let i = 0; i < coords.length; i += 2) { @@ -65,7 +65,7 @@ function mephisto () { Config.PickRange !== oldPickRange && (Config.PickRange = oldPickRange); Pickit.pickItems(); - me.mephisto && !me.hell && this.killCouncil(); + me.mephisto && !me.hell && killCouncil(); Pather.moveTo(17581, 8070); delay(250 + me.ping * 2); diff --git a/libs/SoloPlay/Scripts/nith.js b/libs/SoloPlay/Scripts/nith.js index 33592870..97aebe75 100644 --- a/libs/SoloPlay/Scripts/nith.js +++ b/libs/SoloPlay/Scripts/nith.js @@ -1,8 +1,9 @@ /** -* @filename nith.js -* @author theBGuy -* @credit kolton -* @desc kill Nihlathak for Destruction key +* @filename nith.js +* @author theBGuy +* @credit kolton +* @desc kill Nihlathak for Destruction key +* */ function nith() { diff --git a/libs/SoloPlay/Scripts/worldstone.js b/libs/SoloPlay/Scripts/worldstone.js index 420524b5..27ffef78 100644 --- a/libs/SoloPlay/Scripts/worldstone.js +++ b/libs/SoloPlay/Scripts/worldstone.js @@ -12,9 +12,31 @@ function worldstone() { Pather.useWaypoint(sdk.areas.WorldstoneLvl2); Precast.doPrecast(true); + /** + * Calc distances so we know whether to tp to town or not after clearing WSK1 + * - WP -> WSK3 + * - WSK1 -> WSK3 + * @todo Take into account walking vs tele and adjust distance check accordingly + */ + + /** @type {Exit[]} */ + let exits = getArea().exits; + let WS1 = exits.find(t => t.target === sdk.areas.WorldstoneLvl1); + let WS3 = exits.find(t => t.target === sdk.areas.WorldstoneLvl3); + let wpToWS3 = WS3.distance; + let ws1ToWS3 = getDistance(WS1, WS3); + Attack.clearLevel(Config.ClearType); Pather.moveToExit(sdk.areas.WorldstoneLvl1, true) && Attack.clearLevel(Config.ClearType); - Pather.moveToExit([sdk.areas.WorldstoneLvl2, sdk.areas.WorldstoneLvl3], true) && Attack.clearLevel(Config.ClearType); + if (wpToWS3 < ws1ToWS3 + Pather.getDistanceToExit(me.area, sdk.areas.WorldstoneLvl2)) { + console.log("Going to town to start from WSK2 waypoint."); + Town.goToTown(); + Pather.useWaypoint(sdk.areas.WorldstoneLvl2); + } else { + Pather.moveToExit(sdk.areas.WorldstoneLvl2, true); + } + + Pather.moveToExit(sdk.areas.WorldstoneLvl3, true) && Attack.clearLevel(Config.ClearType); return true; } From 74729b03c4c059ed59111d2d5a0f6173e644d1bf Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Sat, 4 Feb 2023 01:02:00 -0500 Subject: [PATCH 010/263] ClassAttack updates - Amazon got better weapon quantity checks and just some general cleanup of the file. Also handle if for some reason our weapon disappears (Saw this when died while trying to smash orb in Travincal) - Barb just got fixes to prevent global scope pollution - Paladin got experimental mercwatch tracking to prevent rehiring a merc that dies in 3 seconds and draining us of gold - Sorc got use of switch casting BattleCry from CTA if we have it and are in range, mostly for making static safer. It will never intentionally get within that range. It's just if we are within that range already, very helpful for any bosses and even making dolls safer --- .../ClassAttackOverrides/AmazonAttacks.js | 61 +- .../ClassAttackOverrides/BarbarianAttacks.js | 675 +++++++++--------- .../ClassAttackOverrides/PaladinAttacks.js | 8 +- .../ClassAttackOverrides/SorceressAttacks.js | 16 + libs/SoloPlay/Functions/Me.js | 14 +- libs/SoloPlay/Functions/PrototypeOverrides.js | 12 + 6 files changed, 427 insertions(+), 359 deletions(-) diff --git a/libs/SoloPlay/Functions/ClassAttackOverrides/AmazonAttacks.js b/libs/SoloPlay/Functions/ClassAttackOverrides/AmazonAttacks.js index e8bf20b2..8ac9008f 100644 --- a/libs/SoloPlay/Functions/ClassAttackOverrides/AmazonAttacks.js +++ b/libs/SoloPlay/Functions/ClassAttackOverrides/AmazonAttacks.js @@ -33,27 +33,48 @@ ClassAttack.doAttack = function (unit, preattack, once) { } } + let gold = me.gold; let preattackRange = Skill.getRange(Config.AttackSkill[0]); let decoyDuration = Skill.getDuration(sdk.skills.Dopplezon); - let gold = me.gold; const index = (unit.isSpecial || unit.isPlayer) ? 1 : 3; + const useSkills = { + InnerSight: false, + SlowMissiles: false, + Jab: false, + Plague: false, + LightFury: false, + }; - let useInnerSight = Skill.canUse(sdk.skills.InnerSight); - let useSlowMissiles = Skill.canUse(sdk.skills.SlowMissiles); - let useDecoy = (Skill.canUse(sdk.skills.Dopplezon) && !me.normal); - let usePlague = (!me.normal && Skill.canUse(sdk.skills.PlagueJavelin)); - let useJab = (Item.getEquipped(sdk.body.RightArm).tier >= 1000 && Skill.canUse(sdk.skills.Jab)); - let useLightFury = me.getSkill(sdk.skills.LightningFury, sdk.skills.subindex.SoftPoints) >= 10; - let forcePlague = (me.getSkill(sdk.skills.PlagueJavelin, sdk.skills.subindex.SoftPoints) >= 15); //Extra poison damage then attack + useSkills.InnerSight = Skill.canUse(sdk.skills.InnerSight); + useSkills.SlowMissiles = Skill.canUse(sdk.skills.SlowMissiles); + useSkills.Decoy = (Skill.canUse(sdk.skills.Dopplezon) && !me.normal); + + // check weapon + let [allowThrowing, forcePlague] = [false, false]; + let equippedWep = me.getEquippedItem(sdk.body.RightArm); + if (equippedWep) { + allowThrowing = (equippedWep.ethereal && equippedWep.quantityPercent > 25); + useSkills.Jab = (NTIP.GetTier(equippedWep) >= 1000 && Skill.canUse(sdk.skills.Jab)); + if (allowThrowing) { + useSkills.Plague = (!me.normal && Skill.canUse(sdk.skills.PlagueJavelin)); + useSkills.LightFury = (me.getSkill(sdk.skills.LightningFury, sdk.skills.subindex.SoftPoints) >= 10); + forcePlague = (me.getSkill(sdk.skills.PlagueJavelin, sdk.skills.subindex.SoftPoints) >= 15); // Extra poison damage then attack + } + } else { + console.warn("We don't have a weapon?"); + console.debug("Go to town, maybe can get one."); + Town.visitTown(true); + // we are probably screwed if we can't get a weapon, maybe go back a difficulty? + } // Precast Section -----------------------------------------------------------------------------------------------------------------// - if (useSlowMissiles) { + if (useSkills.SlowMissiles) { if (!unit.getState(sdk.states.SlowMissiles)) { if ((unit.distance > 3 || unit.getEnchant(sdk.enchant.LightningEnchanted)) && unit.distance < 13 && !checkCollision(me, unit, sdk.collision.Ranged)) { // Act Bosses and mini-bosses are immune to Slow Missles and pointless to use on lister or Cows, Use Inner-Sight instead if ([sdk.monsters.HellBovine].includes(unit.classid) || unit.isBoss) { // Check if already in this state - if (useInnerSight && !unit.getState(sdk.states.InnerSight)) { + if (useSkills.InnerSight && !unit.getState(sdk.states.InnerSight)) { Skill.cast(sdk.skills.InnerSight, sdk.skills.hand.Right, unit); } } else { @@ -63,15 +84,15 @@ ClassAttack.doAttack = function (unit, preattack, once) { } } - if (Skill.canUse(sdk.skills.LightningFury) && unit.getEnchant(sdk.enchant.ManaBurn) && unit.getMobCount(7) > 2) { - useLightFury = true; + if (allowThrowing && Skill.canUse(sdk.skills.LightningFury) && unit.getEnchant(sdk.enchant.ManaBurn) && unit.getMobCount(7) > 2) { + useSkills.LightFury = true; } - if (Skill.canUse(sdk.skills.PlagueJavelin) && unit.getEnchant(sdk.enchant.ManaBurn) && unit.getMobCount(7) > 2) { + if (allowThrowing && Skill.canUse(sdk.skills.PlagueJavelin) && unit.getEnchant(sdk.enchant.ManaBurn) && unit.getMobCount(7) > 2) { forcePlague = true; } - if (useInnerSight) { + if (useSkills.InnerSight) { if (!unit.getState(sdk.states.InnerSight) && unit.distance > 3 && unit.distance < 13 && !checkCollision(me, unit, sdk.collision.Ranged)) { Skill.cast(sdk.skills.InnerSight, sdk.skills.hand.Right, unit); } @@ -99,7 +120,7 @@ ClassAttack.doAttack = function (unit, preattack, once) { Skill.switchCast(sdk.skills.BattleCry, {oSkill: true}); } - if (useDecoy) { + if (useSkills.Decoy) { // Act Bosses or Immune to my main boss skill if ((unit.isPrimeEvil) || !Attack.checkResist(unit, Config.AttackSkill[1])) { Misc.poll(() => !me.skillDelay, 1000, 40); @@ -127,10 +148,10 @@ ClassAttack.doAttack = function (unit, preattack, once) { } // Only try attacking light immunes if I have my end game javelin - preAttack with Plague Javelin - if ((usePlague) && !Attack.checkResist(unit, "lightning")) { + if ((useSkills.Plague) && !Attack.checkResist(unit, "lightning")) { if ((unit.distance <= 15) && !checkCollision(me, unit, sdk.collision.Ranged)) { // Cast Slow-Missles, then proceed with Plague Jav. Lowers amount of damage from projectiles. - !unit.getState(sdk.states.SlowMissiles) && useSlowMissiles && Skill.cast(sdk.skills.SlowMissiles, sdk.skills.hand.Right, unit); + !unit.getState(sdk.states.SlowMissiles) && useSkills.SlowMissiles && Skill.cast(sdk.skills.SlowMissiles, sdk.skills.hand.Right, unit); // Handle Switch casting if (!unit.dead) { @@ -145,7 +166,7 @@ ClassAttack.doAttack = function (unit, preattack, once) { Skill.cast(sdk.skills.PlagueJavelin, Skill.getHand(sdk.skills.PlagueJavelin), unit); } - if (!useJab) { + if (!useSkills.Jab) { // We are within melee distance might as well use jab rather than stand there // Make sure monster is not physical immune if (unit.distance < 4 && Attack.checkResist(unit, "physical")) { @@ -168,7 +189,7 @@ ClassAttack.doAttack = function (unit, preattack, once) { } // Only try attacking immunes if I have my end game javelin and they aren't lightning enchanted - use jab as main attack - if (useJab && !Attack.checkResist(unit, Config.AttackSkill[1]) && Attack.checkResist(unit, "physical") && !unit.getEnchant(sdk.enchant.LightningEnchanted)) { + if (useSkills.Jab && !Attack.checkResist(unit, Config.AttackSkill[1]) && Attack.checkResist(unit, "physical") && !unit.getEnchant(sdk.enchant.LightningEnchanted)) { if ((unit.distance > 3 || checkCollision(me, unit, sdk.collision.Ranged)) && !Attack.getIntoPosition(unit, 3, sdk.collision.BlockWall)) { return Attack.Result.FAILED; } @@ -184,7 +205,7 @@ ClassAttack.doAttack = function (unit, preattack, once) { } } - if (useLightFury) { + if (useSkills.LightFury) { if ((unit.distance >= 8 && unit.distance <= 15) && !checkCollision(me, unit, sdk.collision.Ranged)) { Skill.cast(sdk.skills.LightningFury, Skill.getHand(sdk.skills.LightningFury), unit); } diff --git a/libs/SoloPlay/Functions/ClassAttackOverrides/BarbarianAttacks.js b/libs/SoloPlay/Functions/ClassAttackOverrides/BarbarianAttacks.js index eadea985..b543741e 100644 --- a/libs/SoloPlay/Functions/ClassAttackOverrides/BarbarianAttacks.js +++ b/libs/SoloPlay/Functions/ClassAttackOverrides/BarbarianAttacks.js @@ -13,428 +13,429 @@ includeIfNotIncluded("core/Attacks/Barbarian.js"); * - use leap attack with getIntoPosition, long distance or when targetting summoners * - use leap/leap attack with dodge, useful if we can't tele it provides a similar benefit */ +(function() { + ClassAttack.warCryTick = 0; + + const howlCheck = function () { + let levelCheck = (me.getSkill(sdk.skills.Howl, sdk.skills.subindex.SoftPoints) + me.charlvl + 1); + return getUnits(sdk.unittype.Monster).filter(function (el) { + return (!!el && el.attackable && el.distance < 6 && el.scareable && GameData.monsterLevel(el.classid, me.area) < levelCheck && !el.isStunned + && [sdk.states.BattleCry, sdk.states.AmplifyDamage, sdk.states.Decrepify, sdk.states.Terror, sdk.states.Taunt].every(state => !el.getState(state)) + && !checkCollision(me, el, Coords_1.Collision.BLOCK_MISSILE)); + }).length > me.maxNearMonsters; + }; -ClassAttack.warCryTick = 0; - -const howlCheck = function () { - let levelCheck = (me.getSkill(sdk.skills.Howl, sdk.skills.subindex.SoftPoints) + me.charlvl + 1); - return getUnits(sdk.unittype.Monster).filter(function (el) { - return (!!el && el.attackable && el.distance < 6 && el.scareable && GameData.monsterLevel(el.classid, me.area) < levelCheck && !el.isStunned - && [sdk.states.BattleCry, sdk.states.AmplifyDamage, sdk.states.Decrepify, sdk.states.Terror, sdk.states.Taunt].every(state => !el.getState(state)) - && !checkCollision(me, el, Coords_1.Collision.BLOCK_MISSILE)); - }).length > me.maxNearMonsters; -}; - -const battleCryCheck = function () { - return getUnits(sdk.unittype.Monster).some(function (el) { - if (el === undefined) return false; - return (el.attackable && el.distance < 5 && el.curseable - && [sdk.states.BattleCry, sdk.states.AmplifyDamage, sdk.states.Decrepify, sdk.states.Terror, sdk.states.Taunt].every(state => !el.getState(state)) - && !checkCollision(me, el, Coords_1.Collision.BLOCK_MISSILE)); - }); -}; - -const warCryCheck = function () { - return getUnits(sdk.unittype.Monster).some(function (el) { - if (el === undefined) return false; - return (el.attackable && el.distance < 5 && !(el.isSpecial) && el.curseable - && ![sdk.monsters.Andariel, sdk.monsters.Duriel, sdk.monsters.Mephisto, sdk.monsters.Diablo, sdk.monsters.Baal, sdk.monsters.Tentacle1, - sdk.monsters.BaalClone, sdk.monsters.KorlictheProtector, sdk.monsters.TalictheDefender, sdk.monsters.MadawctheGuardian].includes(el.classid) - && (!el.isStunned || getTickCount() - ClassAttack.warCryTick >= 1500) && !checkCollision(me, el, Coords_1.Collision.BLOCK_MISSILE)); - }); -}; - -ClassAttack.tauntMonsters = function (unit, attackSkill, data) { - // Don't have skill - // Only mob in these areas are bosses - // Can't taunt Main bosses or MinionsofDestruction - if (!Skill.canUse(sdk.skills.Taunt) || !data) return; - if ([sdk.areas.DurielsLair, sdk.areas.ArreatSummit, sdk.areas.WorldstoneChamber].includes(me.area)) return; - if (unit.isPrimeEvil || unit.classid === sdk.monsters.ListerTheTormenter) return; - - let range = (!me.inArea(sdk.areas.ThroneofDestruction) ? 15 : 30); - let rangedMobsClassIDs = [ - sdk.monsters.Afflicted, sdk.monsters.Tainted, sdk.monsters.Misshapen1, sdk.monsters.Disfigured, sdk.monsters.Damned1, sdk.monsters.Gloam1, sdk.monsters.SwampGhost, - sdk.monsters.BurningSoul2, sdk.monsters.BlackSoul1, sdk.monsters.GhoulLord1, sdk.monsters.NightLord, sdk.monsters.DarkLord1, sdk.monsters.BloodLord1, - sdk.monsters.Banished, sdk.monsters.SkeletonArcher, sdk.monsters.ReturnedArcher1, sdk.monsters.BoneArcher1, sdk.monsters.BurningDeadArcher1, sdk.monsters.HorrorArcher1, - sdk.monsters.Sexton, sdk.monsters.Cantor, sdk.monsters.Heirophant1, sdk.monsters.DoomKnight, sdk.monsters.VenomLord1, sdk.monsters.Horror1, sdk.monsters.Horror2, - sdk.monsters.Horror3, sdk.monsters.Horror4, sdk.monsters.Horror5, sdk.monsters.Lord1, sdk.monsters.Lord2, sdk.monsters.Lord3, sdk.monsters.Lord4, - sdk.monsters.Lord4, sdk.monsters.Afflicted2, sdk.monsters.Tainted, sdk.monsters.Misshapen2, sdk.monsters.Disfigured2, sdk.monsters.Damned2, sdk.monsters.DarkShaman2, - sdk.monsters.DevilkinShaman, sdk.monsters.DarkShaman2, sdk.monsters.DarkLord2 - ]; - let dangerousAndSummoners = [ - sdk.monsters.Dominus2, sdk.monsters.Witch1, sdk.monsters.VileWitch2, sdk.monsters.Gloam2, sdk.monsters.BlackSoul2, sdk.monsters.BurningSoul1, - sdk.monsters.FallenShaman, sdk.monsters.CarverShaman2, sdk.monsters.DevilkinShaman2, sdk.monsters.DarkShaman1, sdk.monsters.HollowOne, sdk.monsters.Guardian1, - sdk.monsters.Unraveler1, sdk.monsters.Ancient1, sdk.monsters.BaalSubjectMummy, sdk.monsters.Council4, sdk.monsters.VenomLord2, sdk.monsters.Ancient2, - sdk.monsters.Ancient3, sdk.monsters.Succubusexp1, sdk.monsters.VileTemptress, sdk.monsters.StygianHarlot, sdk.monsters.Temptress1, sdk.monsters.Temptress2, - sdk.monsters.Dominus1, sdk.monsters.VileWitch1, sdk.monsters.StygianFury, sdk.monsters.Witch2, sdk.monsters.Witch3 - ]; - - [sdk.areas.RiverofFlame, sdk.areas.ChaosSanctuary].includes(me.area) && rangedMobsClassIDs.push(sdk.monsters.Strangler1, sdk.monsters.StormCaster1); - - let list = getUnits(sdk.unittype.Monster) - .filter(function (mob) { - return ([sdk.monsters.spectype.All, sdk.monsters.spectype.Minion].includes(mob.spectype) - && [sdk.states.BattleCry, sdk.states.Decrepify, sdk.states.Taunt].every(state => !mob.getState(state)) - && ((rangedMobsClassIDs.includes(mob.classid) && mob.distance <= range) || (dangerousAndSummoners.includes(mob.classid) && mob.distance <= 30))); - }) - .sort(Sort.units); - - if (list.length >= 1) { - for (let i = 0; i < list.length; i++) { - let currMob = list[i]; - if (battleCryCheck() && Skill.cast(sdk.skills.BattleCry, sdk.skills.hand.Right)) { - continue; - } + const battleCryCheck = function () { + return getUnits(sdk.unittype.Monster).some(function (el) { + if (el === undefined) return false; + return (el.attackable && el.distance < 5 && el.curseable + && [sdk.states.BattleCry, sdk.states.AmplifyDamage, sdk.states.Decrepify, sdk.states.Terror, sdk.states.Taunt].every(state => !el.getState(state)) + && !checkCollision(me, el, Coords_1.Collision.BLOCK_MISSILE)); + }); + }; - if (data.howl.have && !data.warCry.have && data.howl.mana < me.mp && howlCheck()) { - Skill.cast(sdk.skills.Howl, sdk.skills.hand.Right); - } else if (data.warCry.have && data.warCry.mana < me.mp && warCryCheck()) { - Skill.cast(sdk.skills.WarCry, sdk.skills.hand.Right); - } + const warCryCheck = function () { + return getUnits(sdk.unittype.Monster).some(function (el) { + if (el === undefined) return false; + return (el.attackable && el.distance < 5 && !(el.isSpecial) && el.curseable + && ![sdk.monsters.Andariel, sdk.monsters.Duriel, sdk.monsters.Mephisto, sdk.monsters.Diablo, sdk.monsters.Baal, sdk.monsters.Tentacle1, + sdk.monsters.BaalClone, sdk.monsters.KorlictheProtector, sdk.monsters.TalictheDefender, sdk.monsters.MadawctheGuardian].includes(el.classid) + && (!el.isStunned || getTickCount() - ClassAttack.warCryTick >= 1500) && !checkCollision(me, el, Coords_1.Collision.BLOCK_MISSILE)); + }); + }; - if (!!currMob && !currMob.dead && [sdk.states.Terror, sdk.states.BattleCry, sdk.states.Decrepify, sdk.states.Taunt].every(state => !currMob.getState(state)) - && data.taunt.mana < me.mp && !Coords_1.isBlockedBetween(me, currMob)) { - me.overhead("Taunting: " + currMob.name + " | classid: " + currMob.classid); - Skill.cast(sdk.skills.Taunt, sdk.skills.hand.Right, currMob); - } + ClassAttack.tauntMonsters = function (unit, attackSkill, data) { + // Don't have skill + // Only mob in these areas are bosses + // Can't taunt Main bosses or MinionsofDestruction + if (!Skill.canUse(sdk.skills.Taunt) || !data) return; + if ([sdk.areas.DurielsLair, sdk.areas.ArreatSummit, sdk.areas.WorldstoneChamber].includes(me.area)) return; + if (unit.isPrimeEvil || unit.classid === sdk.monsters.ListerTheTormenter) return; + + let range = (!me.inArea(sdk.areas.ThroneofDestruction) ? 15 : 30); + let rangedMobsClassIDs = [ + sdk.monsters.Afflicted, sdk.monsters.Tainted, sdk.monsters.Misshapen1, sdk.monsters.Disfigured, sdk.monsters.Damned1, sdk.monsters.Gloam1, sdk.monsters.SwampGhost, + sdk.monsters.BurningSoul2, sdk.monsters.BlackSoul1, sdk.monsters.GhoulLord1, sdk.monsters.NightLord, sdk.monsters.DarkLord1, sdk.monsters.BloodLord1, + sdk.monsters.Banished, sdk.monsters.SkeletonArcher, sdk.monsters.ReturnedArcher1, sdk.monsters.BoneArcher1, sdk.monsters.BurningDeadArcher1, sdk.monsters.HorrorArcher1, + sdk.monsters.Sexton, sdk.monsters.Cantor, sdk.monsters.Heirophant1, sdk.monsters.DoomKnight, sdk.monsters.VenomLord1, sdk.monsters.Horror1, sdk.monsters.Horror2, + sdk.monsters.Horror3, sdk.monsters.Horror4, sdk.monsters.Horror5, sdk.monsters.Lord1, sdk.monsters.Lord2, sdk.monsters.Lord3, sdk.monsters.Lord4, + sdk.monsters.Lord4, sdk.monsters.Afflicted2, sdk.monsters.Tainted, sdk.monsters.Misshapen2, sdk.monsters.Disfigured2, sdk.monsters.Damned2, sdk.monsters.DarkShaman2, + sdk.monsters.DevilkinShaman, sdk.monsters.DarkShaman2, sdk.monsters.DarkLord2 + ]; + let dangerousAndSummoners = [ + sdk.monsters.Dominus2, sdk.monsters.Witch1, sdk.monsters.VileWitch2, sdk.monsters.Gloam2, sdk.monsters.BlackSoul2, sdk.monsters.BurningSoul1, + sdk.monsters.FallenShaman, sdk.monsters.CarverShaman2, sdk.monsters.DevilkinShaman2, sdk.monsters.DarkShaman1, sdk.monsters.HollowOne, sdk.monsters.Guardian1, + sdk.monsters.Unraveler1, sdk.monsters.Ancient1, sdk.monsters.BaalSubjectMummy, sdk.monsters.Council4, sdk.monsters.VenomLord2, sdk.monsters.Ancient2, + sdk.monsters.Ancient3, sdk.monsters.Succubusexp1, sdk.monsters.VileTemptress, sdk.monsters.StygianHarlot, sdk.monsters.Temptress1, sdk.monsters.Temptress2, + sdk.monsters.Dominus1, sdk.monsters.VileWitch1, sdk.monsters.StygianFury, sdk.monsters.Witch2, sdk.monsters.Witch3 + ]; + + [sdk.areas.RiverofFlame, sdk.areas.ChaosSanctuary].includes(me.area) && rangedMobsClassIDs.push(sdk.monsters.Strangler1, sdk.monsters.StormCaster1); + + let list = getUnits(sdk.unittype.Monster) + .filter(function (mob) { + return ([sdk.monsters.spectype.All, sdk.monsters.spectype.Minion].includes(mob.spectype) + && [sdk.states.BattleCry, sdk.states.Decrepify, sdk.states.Taunt].every(state => !mob.getState(state)) + && ((rangedMobsClassIDs.includes(mob.classid) && mob.distance <= range) || (dangerousAndSummoners.includes(mob.classid) && mob.distance <= 30))); + }) + .sort(Sort.units); + + if (list.length >= 1) { + for (let i = 0; i < list.length; i++) { + let currMob = list[i]; + if (battleCryCheck() && Skill.cast(sdk.skills.BattleCry, sdk.skills.hand.Right)) { + continue; + } + + if (data.howl.have && !data.warCry.have && data.howl.mana < me.mp && howlCheck()) { + Skill.cast(sdk.skills.Howl, sdk.skills.hand.Right); + } else if (data.warCry.have && data.warCry.mana < me.mp && warCryCheck()) { + Skill.cast(sdk.skills.WarCry, sdk.skills.hand.Right); + } + + if (!!currMob && !currMob.dead && [sdk.states.Terror, sdk.states.BattleCry, sdk.states.Decrepify, sdk.states.Taunt].every(state => !currMob.getState(state)) + && data.taunt.mana < me.mp && !Coords_1.isBlockedBetween(me, currMob)) { + me.overhead("Taunting: " + currMob.name + " | classid: " + currMob.classid); + Skill.cast(sdk.skills.Taunt, sdk.skills.hand.Right, currMob); + } - this.doCast(unit, attackSkill, data); + this.doCast(unit, attackSkill, data); + } } - } -}; + }; -ClassAttack.doAttack = function (unit = undefined, preattack = false) { - if (unit === undefined || !unit || unit.dead) return true; + ClassAttack.doAttack = function (unit = undefined, preattack = false) { + if (unit === undefined || !unit || unit.dead) return true; - let gid = unit.gid; - let needRepair = [], gold = me.gold; - me.charlvl >= 5 && (needRepair = me.needRepair()); + let gid = unit.gid; + let needRepair = [], gold = me.gold; + me.charlvl >= 5 && (needRepair = me.needRepair()); - if ((Config.MercWatch && Town.needMerc()) || needRepair.length > 0) { - console.log("towncheck"); + if ((Config.MercWatch && Town.needMerc()) || needRepair.length > 0) { + console.log("towncheck"); - if (Town.visitTown(!!needRepair.length)) { - // lost reference to the mob we were attacking - if (!unit || !copyUnit(unit).x || !Game.getMonster(-1, -1, gid) || unit.dead) { - return Attack.Result.SUCCESS; + if (Town.visitTown(!!needRepair.length)) { + // lost reference to the mob we were attacking + if (!unit || !copyUnit(unit).x || !Game.getMonster(-1, -1, gid) || unit.dead) { + return Attack.Result.SUCCESS; + } } } - } - - const index = (unit.isSpecial || unit.isPlayer) ? 1 : 3; - let attackSkill = Attack.getCustomAttack(unit) ? Attack.getCustomAttack(unit)[0] : Config.AttackSkill[index]; - - if (!Attack.checkResist(unit, attackSkill)) { - attackSkill = -1; + + const index = (unit.isSpecial || unit.isPlayer) ? 1 : 3; + let attackSkill = Attack.getCustomAttack(unit) ? Attack.getCustomAttack(unit)[0] : Config.AttackSkill[index]; - if (Config.AttackSkill[index + 1] > -1 && Skill.canUse(Config.AttackSkill[index + 1]) && Attack.checkResist(unit, Config.AttackSkill[index + 1])) { - attackSkill = Config.AttackSkill[index + 1]; - } - } + if (!Attack.checkResist(unit, attackSkill)) { + attackSkill = -1; - if (me.expansion && index === 1 && !unit.dead) { - if (CharData.skillData.haveChargedSkill(sdk.skills.SlowMissiles) && unit.getEnchant(sdk.enchant.LightningEnchanted) && !unit.getState(sdk.states.SlowMissiles) && unit.curseable && - (gold > 500000 && !unit.isBoss) && !checkCollision(me, unit, sdk.collision.Ranged)) { - // Cast slow missiles - Attack.castCharges(sdk.skills.SlowMissiles, unit); + if (Config.AttackSkill[index + 1] > -1 && Skill.canUse(Config.AttackSkill[index + 1]) && Attack.checkResist(unit, Config.AttackSkill[index + 1])) { + attackSkill = Config.AttackSkill[index + 1]; + } } - if (CharData.skillData.haveChargedSkill(sdk.skills.InnerSight) && !unit.getState(sdk.states.InnerSight) && unit.curseable && - gold > 500000 && !checkCollision(me, unit, sdk.collision.Ranged)) { - // Cast slow missiles - Attack.castCharges(sdk.skills.InnerSight, unit); - } - } - - const buildDataObj = (skillId = -1, reqLvl = 1) => ({ - have: false, skill: skillId, range: Infinity, mana: Infinity, timed: false, reqLvl: reqLvl, - assignValues: function (range) { - this.have = Skill.canUse(this.skill); - if (!this.have) return; - this.range = range || Skill.getRange(this.skill); - this.mana = Skill.getManaCost(this.skill); - this.timed = Skill.isTimed(this.skill); - } - }); - const currLvl = me.charlvl; - const data = { - switchCast: false, - howl: buildDataObj(sdk.skills.Howl, 1), - bash: buildDataObj(sdk.skills.Bash, 1), - taunt: buildDataObj(sdk.skills.Taunt, 6), - leap: buildDataObj(sdk.skills.Leap, 6), - doubleSwing: buildDataObj(sdk.skills.DoubleSwing, 6), - stun: buildDataObj(sdk.skills.Stun, 12), - battleCry: buildDataObj(sdk.skills.BattleCry, 18), - concentrate: buildDataObj(sdk.skills.Concentrate, 18), - leapAttack: buildDataObj(sdk.skills.LeapAttack, 18), - grimWard: buildDataObj(sdk.skills.GrimWard, 24), - warCry: buildDataObj(sdk.skills.WarCry, 30), - whirlwind: buildDataObj(sdk.skills.Whirlwind, 30), - main: buildDataObj(Config.AttackSkill[index], 1), - secondary: buildDataObj(Config.AttackSkill[index + 1], 1), - }; - - // TODO: calculate damage values for physcial attacks - Object.keys(data).forEach(k => typeof data[k] === "object" && currLvl >= data[k].reqLvl && data[k].assignValues()); - // console.debug(data); - - // Low mana skill - if (Skill.getManaCost(attackSkill) > me.mp && Config.LowManaSkill[0] > -1 && Attack.checkResist(unit, Config.LowManaSkill[0])) { - attackSkill = Config.LowManaSkill[0]; - } - - if ([sdk.skills.DoubleSwing, sdk.skills.DoubleThrow, sdk.skills.Frenzy].includes(attackSkill) && !me.dualWielding || !Skill.canUse(attackSkill)) { - let oneHandSk = [data.bash, data.stun, data.concentrate, data.leapAttack, data.whirlwind] - .filter((skill) => skill.have && me.mp > skill.mana) - .sort((a, b) => GameData.physicalAttackDamage(b.skill) - GameData.physicalAttackDamage(a.skill)).first(); - attackSkill = oneHandSk ? oneHandSk.skill : 0; - } - - if (data.howl.have && attackSkill !== sdk.skills.Whirlwind && data.howl.mana < me.mp && howlCheck() && me.hpPercent <= 85) { - data.grimWard.have ? this.grimWard(6) : Skill.cast(sdk.skills.Howl, sdk.skills.hand.Right); - } - - data.taunt.have && this.tauntMonsters(unit, attackSkill, data); - - if (!unit.dead && data.battleCry.have && !me.skillDelay) { - // Unit not already in Battle Cry, decrepify, terror, or taunt state. Don't want to overwrite helpful cureses - if ([sdk.states.BattleCry, sdk.states.Decrepify, sdk.states.Terror, sdk.states.Taunt].every(state => !unit.getState(state))) { - if (unit.distance > data.battleCry.range || checkCollision(me, unit, sdk.collision.Ranged)) { - if (!Attack.getIntoPosition(unit, data.battleCry.range, sdk.collision.Ranged)) { - return Attack.Result.FAILED; - } + if (me.expansion && index === 1 && !unit.dead) { + if (CharData.skillData.haveChargedSkill(sdk.skills.SlowMissiles) && unit.getEnchant(sdk.enchant.LightningEnchanted) && !unit.getState(sdk.states.SlowMissiles) && unit.curseable && + (gold > 500000 && !unit.isBoss) && !checkCollision(me, unit, sdk.collision.Ranged)) { + // Cast slow missiles + Attack.castCharges(sdk.skills.SlowMissiles, unit); } - if (unit.distance < data.battleCry.range) { - data.switchCast ? Skill.switchCast(sdk.skills.BattleCry, {hand: 0, switchBack: !data.warCry.have}) : Skill.cast(sdk.skills.BattleCry, sdk.skills.hand.Right, unit); + if (CharData.skillData.haveChargedSkill(sdk.skills.InnerSight) && !unit.getState(sdk.states.InnerSight) && unit.curseable && + gold > 500000 && !checkCollision(me, unit, sdk.collision.Ranged)) { + // Cast slow missiles + Attack.castCharges(sdk.skills.InnerSight, unit); } } - } - - // TODO: write GameData.killableSummonsByWarCry - if (data.warCry.have && data.warCry.mana < me.mp && !me.skillDelay && warCryCheck()) { - data.switchCast ? Skill.switchCast(sdk.skills.WarCry, {hand: 0}) : Skill.cast(sdk.skills.WarCry, sdk.skills.hand.Right, unit); - this.warCryTick = getTickCount(); - } - - // Probably going to get rid of preattack - if (preattack && Config.AttackSkill[0] > 0 && Config.AttackSkill[0] !== sdk.skills.WarCry && Skill.canUse(Config.AttackSkill[0]) - && Attack.checkResist(unit, Attack.getSkillElement(Config.AttackSkill[0])) && (Skill.getManaCost(Config.AttackSkill[0]) < me.mp) - && (!me.getState(sdk.states.SkillDelay) || !Skill.isTimed(Config.AttackSkill[0]))) { - if (unit.distance > Skill.getRange(Config.AttackSkill[0]) || checkCollision(me, unit, sdk.collision.Ranged)) { - if (!Attack.getIntoPosition(unit, Skill.getRange(Config.AttackSkill[0]), sdk.collision.Ranged)) { - return Attack.Result.FAILED; + + const buildDataObj = (skillId = -1, reqLvl = 1) => ({ + have: false, skill: skillId, range: Infinity, mana: Infinity, timed: false, reqLvl: reqLvl, + assignValues: function (range) { + this.have = Skill.canUse(this.skill); + if (!this.have) return; + this.range = range || Skill.getRange(this.skill); + this.mana = Skill.getManaCost(this.skill); + this.timed = Skill.isTimed(this.skill); } + }); + const currLvl = me.charlvl; + const data = { + switchCast: false, + howl: buildDataObj(sdk.skills.Howl, 1), + bash: buildDataObj(sdk.skills.Bash, 1), + taunt: buildDataObj(sdk.skills.Taunt, 6), + leap: buildDataObj(sdk.skills.Leap, 6), + doubleSwing: buildDataObj(sdk.skills.DoubleSwing, 6), + stun: buildDataObj(sdk.skills.Stun, 12), + battleCry: buildDataObj(sdk.skills.BattleCry, 18), + concentrate: buildDataObj(sdk.skills.Concentrate, 18), + leapAttack: buildDataObj(sdk.skills.LeapAttack, 18), + grimWard: buildDataObj(sdk.skills.GrimWard, 24), + warCry: buildDataObj(sdk.skills.WarCry, 30), + whirlwind: buildDataObj(sdk.skills.Whirlwind, 30), + main: buildDataObj(Config.AttackSkill[index], 1), + secondary: buildDataObj(Config.AttackSkill[index + 1], 1), + }; + + // TODO: calculate damage values for physcial attacks + Object.keys(data).forEach(k => typeof data[k] === "object" && currLvl >= data[k].reqLvl && data[k].assignValues()); + // console.debug(data); + + // Low mana skill + if (Skill.getManaCost(attackSkill) > me.mp && Config.LowManaSkill[0] > -1 && Attack.checkResist(unit, Config.LowManaSkill[0])) { + attackSkill = Config.LowManaSkill[0]; } - Skill.cast(Config.AttackSkill[0], Skill.getHand(Config.AttackSkill[0]), unit); - - return Attack.Result.SUCCESS; - } + if ([sdk.skills.DoubleSwing, sdk.skills.DoubleThrow, sdk.skills.Frenzy].includes(attackSkill) && !me.dualWielding || !Skill.canUse(attackSkill)) { + let oneHandSk = [data.bash, data.stun, data.concentrate, data.leapAttack, data.whirlwind] + .filter((skill) => skill.have && me.mp > skill.mana) + .sort((a, b) => GameData.physicalAttackDamage(b.skill) - GameData.physicalAttackDamage(a.skill)).first(); + attackSkill = oneHandSk ? oneHandSk.skill : 0; + } - if (index === 1) { - if (data.howl.have && attackSkill !== sdk.skills.Whirlwind && data.howl.mana < me.mp && howlCheck()) { - data.grimWard.have ? this.grimWard(6) : !data.warCry.have ? Skill.cast(sdk.skills.Howl, Skill.getHand(sdk.skills.Howl)) : null; + if (data.howl.have && attackSkill !== sdk.skills.Whirlwind && data.howl.mana < me.mp && howlCheck() && me.hpPercent <= 85) { + data.grimWard.have ? this.grimWard(6) : Skill.cast(sdk.skills.Howl, sdk.skills.hand.Right); } - } - if (attackSkill === sdk.skills.DoubleThrow && (me.getWeaponQuantity() <= 3 || me.getWeaponQuantity(sdk.body.LeftArm) <= 3) && data.secondary.have) { - attackSkill = data.secondary.skill; - } + data.taunt.have && this.tauntMonsters(unit, attackSkill, data); - // Telestomp with barb is pointless - return this.doCast(unit, attackSkill, data); -}; + if (!unit.dead && data.battleCry.have && !me.skillDelay) { + // Unit not already in Battle Cry, decrepify, terror, or taunt state. Don't want to overwrite helpful cureses + if ([sdk.states.BattleCry, sdk.states.Decrepify, sdk.states.Terror, sdk.states.Taunt].every(state => !unit.getState(state))) { + if (unit.distance > data.battleCry.range || checkCollision(me, unit, sdk.collision.Ranged)) { + if (!Attack.getIntoPosition(unit, data.battleCry.range, sdk.collision.Ranged)) { + return Attack.Result.FAILED; + } + } -ClassAttack.doCast = function (unit, attackSkill, data) { - // In case of failing to switch back to main weapon slot - me.weaponswitch === 1 && me.switchWeapons(0); - // No attack skill - if (attackSkill < 0 || !data) return Attack.Result.CANTATTACK; + if (unit.distance < data.battleCry.range) { + data.switchCast ? Skill.switchCast(sdk.skills.BattleCry, {hand: 0, switchBack: !data.warCry.have}) : Skill.cast(sdk.skills.BattleCry, sdk.skills.hand.Right, unit); + } + } + } - let walk; + // TODO: write GameData.killableSummonsByWarCry + if (data.warCry.have && data.warCry.mana < me.mp && !me.skillDelay && warCryCheck()) { + data.switchCast ? Skill.switchCast(sdk.skills.WarCry, {hand: 0}) : Skill.cast(sdk.skills.WarCry, sdk.skills.hand.Right, unit); + this.warCryTick = getTickCount(); + } - switch (attackSkill) { - case sdk.skills.Whirlwind: - if (unit.distance > Skill.getRange(attackSkill) || checkCollision(me, unit, sdk.collision.BlockWall)) { - if (!Attack.getIntoPosition(unit, Skill.getRange(attackSkill), sdk.collision.BlockWall, 2)) { - return Attack.Result.FAILED; + // Probably going to get rid of preattack + if (preattack && Config.AttackSkill[0] > 0 && Config.AttackSkill[0] !== sdk.skills.WarCry && Skill.canUse(Config.AttackSkill[0]) + && Attack.checkResist(unit, Attack.getSkillElement(Config.AttackSkill[0])) && (Skill.getManaCost(Config.AttackSkill[0]) < me.mp) + && (!me.getState(sdk.states.SkillDelay) || !Skill.isTimed(Config.AttackSkill[0]))) { + if (unit.distance > Skill.getRange(Config.AttackSkill[0]) || checkCollision(me, unit, sdk.collision.Ranged)) { + if (!Attack.getIntoPosition(unit, Skill.getRange(Config.AttackSkill[0]), sdk.collision.Ranged)) { + return Attack.Result.FAILED; + } } + + Skill.cast(Config.AttackSkill[0], Skill.getHand(Config.AttackSkill[0]), unit); + + return Attack.Result.SUCCESS; } - !unit.dead && Attack.whirlwind(unit); + if (index === 1) { + if (data.howl.have && attackSkill !== sdk.skills.Whirlwind && data.howl.mana < me.mp && howlCheck()) { + data.grimWard.have ? this.grimWard(6) : !data.warCry.have ? Skill.cast(sdk.skills.Howl, Skill.getHand(sdk.skills.Howl)) : null; + } + } - return Attack.Result.SUCCESS; - default: - if (Skill.getRange(attackSkill) < 4 && !Attack.validSpot(unit.x, unit.y, attackSkill, unit.classid)) { - return Attack.Result.FAILED; + if (attackSkill === sdk.skills.DoubleThrow && (me.getWeaponQuantity() <= 3 || me.getWeaponQuantity(sdk.body.LeftArm) <= 3) && data.secondary.have) { + attackSkill = data.secondary.skill; } - if (unit.distance > Skill.getRange(attackSkill) || checkCollision(me, unit, sdk.collision.Ranged)) { - walk = (Skill.getRange(attackSkill) < 4 && unit.distance < 10 && !checkCollision(me, unit, sdk.collision.BlockWall)); + // Telestomp with barb is pointless + return this.doCast(unit, attackSkill, data); + }; + + ClassAttack.doCast = function (unit, attackSkill, data) { + // In case of failing to switch back to main weapon slot + me.weaponswitch === 1 && me.switchWeapons(0); + // No attack skill + if (attackSkill < 0 || !data) return Attack.Result.CANTATTACK; + + let walk; - // think this should be re-written in pather with some form of leap pathing similar to teleport - // leap/leap attack is incredibly useful because we can leap straight to chaos or over mobs/doors/some walls ect - if (data.leapAttack.have && !checkCollision(me, unit, sdk.collision.BlockWall) && unit.distance > 6) { - Skill.cast(sdk.skills.LeapAttack, sdk.skills.hand.Right, unit.x, unit.y); + switch (attackSkill) { + case sdk.skills.Whirlwind: + if (unit.distance > Skill.getRange(attackSkill) || checkCollision(me, unit, sdk.collision.BlockWall)) { + if (!Attack.getIntoPosition(unit, Skill.getRange(attackSkill), sdk.collision.BlockWall, 2)) { + return Attack.Result.FAILED; + } } - if (!Attack.getIntoPosition(unit, Skill.getRange(attackSkill), sdk.collision.Ranged, walk)) { + !unit.dead && Attack.whirlwind(unit); + + return Attack.Result.SUCCESS; + default: + if (Skill.getRange(attackSkill) < 4 && !Attack.validSpot(unit.x, unit.y, attackSkill, unit.classid)) { return Attack.Result.FAILED; } - } - if (!unit.dead) { - Skill.cast(attackSkill, Skill.getHand(attackSkill), unit); + if (unit.distance > Skill.getRange(attackSkill) || checkCollision(me, unit, sdk.collision.Ranged)) { + walk = (Skill.getRange(attackSkill) < 4 && unit.distance < 10 && !checkCollision(me, unit, sdk.collision.BlockWall)); - if (!unit.dead && attackSkill === sdk.skills.Berserk && me.dualWielding - && Skill.canUse(sdk.skills.Frenzy) && unit.distance < 4 && !me.getState(sdk.states.Frenzy)) { - Skill.cast(sdk.skills.Frenzy, Skill.getHand(sdk.skills.Frenzy), unit); + // think this should be re-written in pather with some form of leap pathing similar to teleport + // leap/leap attack is incredibly useful because we can leap straight to chaos or over mobs/doors/some walls ect + if (data.leapAttack.have && !checkCollision(me, unit, sdk.collision.BlockWall) && unit.distance > 6) { + Skill.cast(sdk.skills.LeapAttack, sdk.skills.hand.Right, unit.x, unit.y); + } + + if (!Attack.getIntoPosition(unit, Skill.getRange(attackSkill), sdk.collision.Ranged, walk)) { + return Attack.Result.FAILED; + } } - if (!unit.dead && attackSkill === sdk.skills.Berserk && data.concentrate.have && me.mp > data.concentrate.mana) { - Skill.cast(sdk.skills.Concentrate, Skill.getHand(sdk.skills.Concentrate), unit); + if (!unit.dead) { + Skill.cast(attackSkill, Skill.getHand(attackSkill), unit); + + if (!unit.dead && attackSkill === sdk.skills.Berserk && me.dualWielding + && Skill.canUse(sdk.skills.Frenzy) && unit.distance < 4 && !me.getState(sdk.states.Frenzy)) { + Skill.cast(sdk.skills.Frenzy, Skill.getHand(sdk.skills.Frenzy), unit); + } + + if (!unit.dead && attackSkill === sdk.skills.Berserk && data.concentrate.have && me.mp > data.concentrate.mana) { + Skill.cast(sdk.skills.Concentrate, Skill.getHand(sdk.skills.Concentrate), unit); + } + + // Remove this for now, needs more data calculations to decide if its actually worth using (% dmg, %crushing blow, # of mobs filtering phys immunes unless maybe we do ele dmg from something) + // if (useWhirl && !unit.dead && (me.getMobCount(6, Coords_1.Collision.BLOCK_MISSILE | Coords_1.BlockBits.BlockWall) >= 3 || ([156, 211, 242, 243, 544, 571].indexOf(unit.classid) > -1) && !me.hell)) { + // this.whirlwind(unit); + // } } - // Remove this for now, needs more data calculations to decide if its actually worth using (% dmg, %crushing blow, # of mobs filtering phys immunes unless maybe we do ele dmg from something) - // if (useWhirl && !unit.dead && (me.getMobCount(6, Coords_1.Collision.BLOCK_MISSILE | Coords_1.BlockBits.BlockWall) >= 3 || ([156, 211, 242, 243, 544, 571].indexOf(unit.classid) > -1) && !me.hell)) { - // this.whirlwind(unit); - // } + return Attack.Result.SUCCESS; } + }; - return Attack.Result.SUCCESS; - } -}; + ClassAttack.afterAttack = function (pickit = false) { + Precast.doPrecast(false); -ClassAttack.afterAttack = function (pickit = false) { - Precast.doPrecast(false); + let needRepair = me.charlvl < 5 ? [] : me.needRepair(); + + // Repair check, make sure i have a tome + if (needRepair.length > 0 && me.getItem(sdk.items.TomeofTownPortal)) { + Town.visitTown(true); + } - let needRepair = me.charlvl < 5 ? [] : me.needRepair(); - - // Repair check, make sure i have a tome - if (needRepair.length > 0 && me.getItem(sdk.items.TomeofTownPortal)) { - Town.visitTown(true); - } + pickit && this.findItem(10); + }; - pickit && this.findItem(10); -}; + ClassAttack.findItemIgnoreGids = []; + ClassAttack.findItem = function (range = 10) { + if (!Config.FindItem || !Skill.canUse(sdk.skills.FindItem)) return false; -ClassAttack.findItemIgnoreGids = []; -ClassAttack.findItem = function (range = 10) { - if (!Config.FindItem || !Skill.canUse(sdk.skills.FindItem)) return false; + Config.FindItemSwitch = (me.expansion && Precast.getBetterSlot(sdk.skills.FindItem)); + let retry = false, pick = false, corpseList = []; + let orgX = me.x, orgY = me.y; - Config.FindItemSwitch = (me.expansion && Precast.getBetterSlot(sdk.skills.FindItem)); - let retry = false, pick = false, corpseList = []; - let orgX = me.x, orgY = me.y; + MainLoop: + for (let i = 0; i < 3; i++) { + let corpse = Game.getMonster(); - MainLoop: - for (let i = 0; i < 3; i++) { - let corpse = Game.getMonster(); + if (corpse) { + do { + if (corpse.dead && getDistance(corpse, orgX, orgY) <= range && this.checkCorpse(corpse)) { + corpseList.push(copyUnit(corpse)); + } + } while (corpse.getNext()); + } - if (corpse) { - do { - if (corpse.dead && getDistance(corpse, orgX, orgY) <= range && this.checkCorpse(corpse)) { - corpseList.push(copyUnit(corpse)); - } - } while (corpse.getNext()); - } + if (corpseList.length > 0) { + pick = true; - if (corpseList.length > 0) { - pick = true; + while (corpseList.length > 0) { + if (this.checkCloseMonsters(5)) { + Config.FindItemSwitch && me.switchWeapons(Attack.getPrimarySlot()); + Attack.clearPos(me.x, me.y, 10, false); + retry = true; - while (corpseList.length > 0) { - if (this.checkCloseMonsters(5)) { - Config.FindItemSwitch && me.switchWeapons(Attack.getPrimarySlot()); - Attack.clearPos(me.x, me.y, 10, false); - retry = true; + break MainLoop; + } - break MainLoop; - } + corpseList.sort(Sort.units); + corpse = corpseList.shift(); - corpseList.sort(Sort.units); - corpse = corpseList.shift(); + if (this.checkCorpse(corpse)) { + if (corpse.distance > 30 || Coords_1.isBlockedBetween(me, corpse)) { + Pather.moveToUnit(corpse); + } - if (this.checkCorpse(corpse)) { - if (corpse.distance > 30 || Coords_1.isBlockedBetween(me, corpse)) { - Pather.moveToUnit(corpse); - } + Config.FindItemSwitch && me.switchWeapons(Attack.getPrimarySlot() ^ 1); + + CorpseLoop: + for (let j = 0; j < 3; j += 1) { + Skill.cast(sdk.skills.FindItem, sdk.skills.hand.Right, corpse); - Config.FindItemSwitch && me.switchWeapons(Attack.getPrimarySlot() ^ 1); - - CorpseLoop: - for (let j = 0; j < 3; j += 1) { - Skill.cast(sdk.skills.FindItem, sdk.skills.hand.Right, corpse); + let tick = getTickCount(); - let tick = getTickCount(); + while (getTickCount() - tick < 1000) { + if (corpse.getState(sdk.states.CorpseNoSelect)) { + Pickit.fastPick(); - while (getTickCount() - tick < 1000) { - if (corpse.getState(sdk.states.CorpseNoSelect)) { - Pickit.fastPick(); + break CorpseLoop; + } - break CorpseLoop; + delay(10); } - - delay(10); } } } } } - } - if (retry) return this.findItem(me.inArea(sdk.areas.Travincal) ? 60 : 20); - Config.FindItemSwitch && me.weaponswitch === 1 && me.switchWeapons(Attack.getPrimarySlot()); - pick && Pickit.pickItems(); + if (retry) return this.findItem(me.inArea(sdk.areas.Travincal) ? 60 : 20); + Config.FindItemSwitch && me.weaponswitch === 1 && me.switchWeapons(Attack.getPrimarySlot()); + pick && Pickit.pickItems(); - return true; -}; + return true; + }; -ClassAttack.grimWard = function (range = 10) { - if (!Skill.canUse(sdk.skills.GrimWard)) return false; - let corpseList = [], orgX = me.x, orgY = me.y; + ClassAttack.grimWard = function (range = 10) { + if (!Skill.canUse(sdk.skills.GrimWard)) return false; + let corpseList = [], orgX = me.x, orgY = me.y; - for (let i = 0; i < 3; i += 1) { - let corpse = Game.getMonster(); + for (let i = 0; i < 3; i += 1) { + let corpse = Game.getMonster(); - if (corpse) { - do { - if (corpse.dead && getDistance(corpse, orgX, orgY) <= range && this.checkCorpse(corpse)) { - corpseList.push(copyUnit(corpse)); - } - } while (corpse.getNext()); - } + if (corpse) { + do { + if (corpse.dead && getDistance(corpse, orgX, orgY) <= range && this.checkCorpse(corpse)) { + corpseList.push(copyUnit(corpse)); + } + } while (corpse.getNext()); + } - if (corpseList.length > 0) { - while (corpseList.length > 0) { - corpseList.sort(Sort.units); - corpse = corpseList.shift(); + if (corpseList.length > 0) { + while (corpseList.length > 0) { + corpseList.sort(Sort.units); + corpse = corpseList.shift(); - if (this.checkCorpse(corpse)) { - if (corpse.distance > 30 || Coords_1.isBlockedBetween(me, corpse)) { - Pather.moveToUnit(corpse); - } + if (this.checkCorpse(corpse)) { + if (corpse.distance > 30 || Coords_1.isBlockedBetween(me, corpse)) { + Pather.moveToUnit(corpse); + } - CorpseLoop: - for (let j = 0; j < 3; j += 1) { - Skill.cast(sdk.skills.GrimWard, sdk.skills.hand.Right, corpse); + CorpseLoop: + for (let j = 0; j < 3; j += 1) { + Skill.cast(sdk.skills.GrimWard, sdk.skills.hand.Right, corpse); - let tick = getTickCount(); + let tick = getTickCount(); - while (getTickCount() - tick < 1000) { - if (corpse.getState(sdk.states.CorpseNoSelect)) { + while (getTickCount() - tick < 1000) { + if (corpse.getState(sdk.states.CorpseNoSelect)) { - break CorpseLoop; - } + break CorpseLoop; + } - delay(10); + delay(10); + } } } } } } - } - return true; -}; + return true; + }; +})(); diff --git a/libs/SoloPlay/Functions/ClassAttackOverrides/PaladinAttacks.js b/libs/SoloPlay/Functions/ClassAttackOverrides/PaladinAttacks.js index d8160945..0fc493e1 100644 --- a/libs/SoloPlay/Functions/ClassAttackOverrides/PaladinAttacks.js +++ b/libs/SoloPlay/Functions/ClassAttackOverrides/PaladinAttacks.js @@ -7,6 +7,10 @@ includeIfNotIncluded("core/Attacks/Paladin.js"); +const MercWatch = { + last: 0, +}; + // eslint-disable-next-line no-unused-vars ClassAttack.doAttack = function (unit = undefined, preattack = false, once = false) { if (!unit || !unit.attackable) return Attack.Result.SUCCESS; @@ -17,7 +21,8 @@ ClassAttack.doAttack = function (unit = undefined, preattack = false, once = fal let [attackSkill, aura] = [-1, -1]; const index = (unit.isSpecial || unit.isPlayer) ? 1 : 3; - if (Config.MercWatch && Town.needMerc()) { + // prevent running back to town quickly if our merc is just weak + if (Config.MercWatch && Town.needMerc() && getTickCount() - MercWatch.last > Time.seconds(5)) { console.log("mercwatch"); if (Town.visitTown()) { @@ -26,6 +31,7 @@ ClassAttack.doAttack = function (unit = undefined, preattack = false, once = fal return Attack.Result.SUCCESS; } } + MercWatch.last = getTickCount(); gold = me.gold; // reset value after town } diff --git a/libs/SoloPlay/Functions/ClassAttackOverrides/SorceressAttacks.js b/libs/SoloPlay/Functions/ClassAttackOverrides/SorceressAttacks.js index 3c13eb9e..726c9b77 100644 --- a/libs/SoloPlay/Functions/ClassAttackOverrides/SorceressAttacks.js +++ b/libs/SoloPlay/Functions/ClassAttackOverrides/SorceressAttacks.js @@ -38,6 +38,18 @@ includeIfNotIncluded("core/Attacks/Sorceress.js"); return false; }; + /** + * @param {Monster} unit + */ + const battleCryCheck = function (unit, force = false) { + // specials and dolls for now, should make dolls much less dangerous with the reduction of their damage + if (Precast.haveCTA > -1 && !unit.dead && (force || unit.isSpecial || unit.isDoll) + && unit.distance < 5 && !unit.getState(sdk.states.BattleCry) && unit.curseable) { + console.debug("BATTLECRY"); + Skill.switchCast(sdk.skills.BattleCry, { oSkill: true }); + } + }; + /** * @typedef {Object} dataObj * @property {number} skill @@ -252,6 +264,8 @@ includeIfNotIncluded("core/Attacks/Sorceress.js"); .find(unit => Attack.checkResist(unit, "lightning") && unit.hpPercent > Config.CastStatic); if (!!closeMobCheck && data.static.dmg > Math.max(data.mainTimed.dmg, data.mainUntimed.dmg, data.secondaryTimed.dmg, data.secondaryUntimed.dmg) && !Coords_1.isBlockedBetween(me, closeMobCheck)) { Developer.debugging.skills && console.log("STATIC"); + // check if we should use battle cry from cta if we have it + battleCryCheck(closeMobCheck); Skill.cast(sdk.skills.StaticField, sdk.skills.hand.Right, closeMobCheck) && Skill.cast(sdk.skills.StaticField, sdk.skills.hand.Right, closeMobCheck); } } @@ -520,6 +534,8 @@ includeIfNotIncluded("core/Attacks/Sorceress.js"); let sRetry = 0; for (let i = 0; i < 4; i++) { if (!unit.dead) { + // if we are already in close then it might be worth it to use battle cry if we have it + battleCryCheck(unit); Skill.cast(skill, Skill.getHand(skill), unit); if (!Misc.poll(() => unit.dead || unit.hp < preHealth, 200, 50)) { sRetry++; diff --git a/libs/SoloPlay/Functions/Me.js b/libs/SoloPlay/Functions/Me.js index 5ba6220d..78492b32 100644 --- a/libs/SoloPlay/Functions/Me.js +++ b/libs/SoloPlay/Functions/Me.js @@ -138,7 +138,19 @@ me.getMercEx = function () { me.getEquippedItem = function (bodyLoc) { if (!bodyLoc) return null; - return me.getItemsEx().filter(i => i.isEquipped && i.bodylocation === bodyLoc).first(); + let equippedItem = me.getItemsEx().filter(i => i.isEquipped && i.bodylocation === bodyLoc); + if (!equippedItem.length) return null; + return equippedItem.first(); +}; + +/** + * @param {number} bodyLoc + */ +me.getWeaponQuantityPercent = function (bodyLoc) { + if (!bodyLoc) return 0; + let weapon = me.getEquippedItem(bodyLoc); + if (!weapon) return 0; + return weapon.quantityPercent; }; me.getSkillTabs = function (classid = me.classid) { diff --git a/libs/SoloPlay/Functions/PrototypeOverrides.js b/libs/SoloPlay/Functions/PrototypeOverrides.js index a828d649..4f266a36 100644 --- a/libs/SoloPlay/Functions/PrototypeOverrides.js +++ b/libs/SoloPlay/Functions/PrototypeOverrides.js @@ -228,6 +228,18 @@ if (!Unit.prototype.hasOwnProperty("prettyPrint")) { }); } +if (!Unit.prototype.hasOwnProperty("quantityPercent")) { + Object.defineProperty(Unit.prototype, "quantityPercent", { + get: function () { + if (this.type !== sdk.unittype.Item) return 0; + let quantity = this.getStat(sdk.stats.Quantity); + if (!quantity) return 0; + let extraStack = this.getStat(sdk.stats.ExtraStack) || 0; + return ((quantity * 100) / (getBaseStat("items", this.classid, "maxstack") + extraStack)); + } + }); +} + /** * @param {number} difficulty */ From 3341052fdbf44527988d902508fc177deedce0a4 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Sat, 4 Feb 2023 01:04:39 -0500 Subject: [PATCH 011/263] Small bit of cleanup regarding merc hiring - Add extra check for merc hiring to see if somehow the cost got invalidated, had a report of attempting to hire without enough gold. Hoping this fixes it as I haven't seen it myself - todo is implement going to previous difficulty for merc hiring if we want one from there --- libs/SoloPlay/Functions/Mercenary.js | 13 +++++++++---- libs/SoloPlay/Modules/MercLib.js | 1 + 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/libs/SoloPlay/Functions/Mercenary.js b/libs/SoloPlay/Functions/Mercenary.js index e7f1cee8..1abe255b 100644 --- a/libs/SoloPlay/Functions/Mercenary.js +++ b/libs/SoloPlay/Functions/Mercenary.js @@ -155,9 +155,9 @@ const Mercenary = { // we don't have enough gold to hire our wanted merc switch (true) { case typeOfMerc === 1 && (myData.merc.type === "Cold Arrow" || !Misc.checkQuest(sdk.quest.id.SistersBurialGrounds, sdk.quest.states.Completed)): + case myData.merc.type === mercAuraWanted: case me.diff > mercDiff: case me.diff === mercDiff && !Pather.accessToAct(mercAct): - case myData.merc.type === mercAuraWanted: case me.diff !== mercDiff && myData.merc.type === "Defiance": case (me.charlvl > CharInfo.levelCap + 10 && Mercenary.checkMercSkill(myData.merc.type)): case me.gold < Math.round((((me.charlvl - 1) * (me.charlvl - 1)) / 2) * 7.5): @@ -168,8 +168,13 @@ const Mercenary = { // lets check what our current actually merc is let checkMyMerc = Misc.poll(() => me.getMerc(), 50, 500); const wantedSkill = (typeOfMerc === 1 - ? ["Cold Arrow", "Fire Arrow"].includes(mercAuraWanted) ? mercAuraWanted : "Cold Arrow" - : me.normal ? tmpAuraName : mercAuraWanted); + ? "Fire Arrow" === mercAuraWanted + ? mercAuraWanted + : "Cold Arrow" + : me.normal + ? tmpAuraName + : mercAuraWanted + ); if (checkMyMerc && Mercenary.checkMercSkill(wantedSkill, checkMyMerc)) { // we have our wanted merc, data file was probably erased so lets re-update it @@ -204,7 +209,7 @@ const Mercenary = { .first(); if (wantedMerc) { if (wantedMerc.cost > me.gold) { - this.minCost = wantedMerc.cost; + Mercenary.minCost = wantedMerc.cost; throw new Error(); } let oldGid_1 = (_a = me.getMerc()) === null || _a === void 0 ? void 0 : _a.gid; diff --git a/libs/SoloPlay/Modules/MercLib.js b/libs/SoloPlay/Modules/MercLib.js index 4a2ccb60..1d8b0375 100644 --- a/libs/SoloPlay/Modules/MercLib.js +++ b/libs/SoloPlay/Modules/MercLib.js @@ -190,6 +190,7 @@ Merc.prototype.hire = function () { let npc = getInteractedNPC(); if (!npc) throw new Error("To buy merc you need to interact with npc first"); + if (typeof this.cost !== "number") throw new Error("Invalid cost"); if (me.gold < this.cost) throw new Error("Merc is too expensive to buy."); let before = me.gold; while (before === me.gold) { From fe75a32d36f4b7fd0614ef94818c294908d43925 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Sat, 4 Feb 2023 01:06:27 -0500 Subject: [PATCH 012/263] Update CubingOverrides.js - Should fix keeping extra bases, and socketing a lower quality base over a higher one --- libs/SoloPlay/Functions/CubingOverrides.js | 35 +++++++++++++++++++++- 1 file changed, 34 insertions(+), 1 deletion(-) diff --git a/libs/SoloPlay/Functions/CubingOverrides.js b/libs/SoloPlay/Functions/CubingOverrides.js index 78e2bf91..8d90e609 100644 --- a/libs/SoloPlay/Functions/CubingOverrides.js +++ b/libs/SoloPlay/Functions/CubingOverrides.js @@ -429,6 +429,18 @@ Cubing.buildLists = function () { let items = me.getItemsEx() .filter(item => [sdk.items.mode.inStorage, sdk.items.mode.Equipped].includes(item.mode)) .sort((a, b) => b.ilvl - a.ilvl); + /** + * @param {ItemUnit} item + * @param {*} recipe + */ + const ingredientObj = (item, recipe) => ({ + classid: item.classid, + type: item.itemType, + quality: item.quality, + ilvl: item.ilvl, + gid: item.gid, + recipe: recipe, + }); for (let i = 0; i < this.recipes.length; i += 1) { // Set default Enabled property - true if recipe is always enabled, false otherwise @@ -443,7 +455,7 @@ Cubing.buildLists = function () { || items[k].classid === this.recipes[i].Ingredients[j]) && this.validItem(items[k], this.recipes[i])) { // push the item's info into the valid ingredients array. this will be used to find items when checking recipes - this.validIngredients.push({classid: items[k].classid, quality: items[k].quality, ilvl: items[k].ilvl, gid: items[k].gid, recipe: this.recipes[i]}); + this.validIngredients.push(ingredientObj(items[k], Cubing.recipes[i])); // Remove from item list to prevent counting the same item more than once items.splice(k, 1); @@ -595,6 +607,27 @@ Cubing.checkItem = function (unit) { return true; } } + // its an item meant for socketing so lets be sure we have the best base + if (this.validIngredients[i].recipe.Index >= Recipe.Socket.Shield && this.validIngredients[i].recipe.Index <= Recipe.Socket.Helm) { + // not the same item but the same type of item + if (!unit.isEquipped && unit.gid !== this.validIngredients[i].gid && unit.itemType === this.validIngredients[i].type + && unit.quality === this.validIngredients[i].quality) { + // console.debug(this.validIngredients[i], "\n//~~~~//\n", unit, "\n//~~~~~/\n", Item.betterThanStashed(unit, true)); + // item is better than the one we currently have, so add it to validIngredient array and remove old item + if (Item.betterThanStashed(unit, true) && this.validItem(unit, this.validIngredients[i].recipe)) { + this.validIngredients.push({ + classid: unit.classid, + type: unit.itemType, + quality: unit.quality, + ilvl: unit.ilvl, + gid: unit.gid, + recipe: this.validIngredients[i].recipe + }); + this.validIngredients.splice(i, 1); + return true; + } + } + } } if (this.keepItem(unit)) { From db3e91071d00ecdb9903728a0fa28e3be17a3dc0 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Sat, 4 Feb 2023 01:07:57 -0500 Subject: [PATCH 013/263] Increase requirement to run hellforge - I've noticed hell forge being a sticking point for a lot of characters so move it until we've done anya --- libs/SoloPlay/Functions/PickitOverrides.js | 4 +--- libs/SoloPlay/Tools/SoloIndex.js | 2 +- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/libs/SoloPlay/Functions/PickitOverrides.js b/libs/SoloPlay/Functions/PickitOverrides.js index a6100e3a..3c9e2384 100644 --- a/libs/SoloPlay/Functions/PickitOverrides.js +++ b/libs/SoloPlay/Functions/PickitOverrides.js @@ -68,9 +68,7 @@ Pickit.checkItem = function (unit) { } if (rval.result === Pickit.Result.WANTED) { - let durability = unit.getStat(sdk.stats.Durability); - - if (typeof durability === "number" && unit.getStat(sdk.stats.MaxDurability) > 0 && durability * 100 / unit.getStat(sdk.stats.MaxDurability) <= 0) { + if (unit.isBroken) { return resultObj(Pickit.Result.TRASH); } } diff --git a/libs/SoloPlay/Tools/SoloIndex.js b/libs/SoloPlay/Tools/SoloIndex.js index 6d988c9f..22c814ec 100644 --- a/libs/SoloPlay/Tools/SoloIndex.js +++ b/libs/SoloPlay/Tools/SoloIndex.js @@ -573,7 +573,7 @@ const SoloIndex = { "hellforge": { preReq: function () { const cLvl = me.charlvl; - return (Pather.accessToAct(4) && ((me.normal && cLvl >= 24) || (me.nightmare && cLvl >= 40) || (me.hell && cLvl >= 80))); + return (Pather.accessToAct(4) && (me.classic || me.anya) && ((me.normal && cLvl >= 24) || (me.nightmare && cLvl >= 40) || (me.hell && cLvl >= 80))); }, skipIf: function () { return (me.hellforge || me.getQuest(sdk.quest.id.HellsForge, sdk.quest.states.ReqComplete)); From 365377aa6a1796a75e65ed6483d93e6fd45e9f96 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Sat, 4 Feb 2023 01:09:48 -0500 Subject: [PATCH 014/263] Add Background worker versions of current threads - Change AutoBuild, EventThread, and TownChicken into background workers as I don't think they really need to be threads. It just bloats memory usage. Experimental still, option to use it is in Developer --- libs/SoloPlay/Workers/AutoBuildWorker.js | 279 ++++++++++++++ libs/SoloPlay/Workers/EventWorker.js | 163 ++++++++ libs/SoloPlay/Workers/TownChickenWorker.js | 419 +++++++++++++++++++++ 3 files changed, 861 insertions(+) create mode 100644 libs/SoloPlay/Workers/AutoBuildWorker.js create mode 100644 libs/SoloPlay/Workers/EventWorker.js create mode 100644 libs/SoloPlay/Workers/TownChickenWorker.js diff --git a/libs/SoloPlay/Workers/AutoBuildWorker.js b/libs/SoloPlay/Workers/AutoBuildWorker.js new file mode 100644 index 00000000..a90efa9b --- /dev/null +++ b/libs/SoloPlay/Workers/AutoBuildWorker.js @@ -0,0 +1,279 @@ +/** +* @filename AutoBuildWorker.js +* @author theBGuy +* @desc worker thread to handle in game events for Kolbot-SoloPlay +* +*/ + +/** + * @todo make this actually work. Issue is if we interrupt a task and peform an event function, once that is over we've desynced from + * whatever we were orignally doing. In some cases this might be fine to just end whatever it was we were running + * e.g. after receiving finishDen we could kill the den script and perform the event function. We would be able to continue with the next + * script normally afterwards. We can't do that for something like diablo lightning dodge or baal wave skipping though, in those cases it + * might be fine to just continue because in theory we shouldn't be too far from our orginal action so minor desync in physical coordinates + * but not completly different area. We wouldn't be able to do that for diablo clone though. + */ +(function() { + let debug = !!Config.AutoBuild.DebugMode, prevLevel = me.charlvl; + const usingFinalBuild = !["Start", "Stepping", "Leveling"].includes(Config.AutoBuild.Template); + const SPEND_POINTS = true; // For testing, it actually allows skill and stat point spending. + const STAT_ID_TO_NAME = [ + getLocaleString(sdk.locale.text.Strength), + getLocaleString(sdk.locale.text.Energy), + getLocaleString(sdk.locale.text.Dexterity), + getLocaleString(sdk.locale.text.Vitality) + ]; + let currAutoBuild; + + // Will check if value exists in an Array + Array.prototype.contains = (val) => this.indexOf(val) > -1; + + function skillInValidRange (id) { + switch (me.classid) { + case sdk.player.class.Amazon: + return sdk.skills.MagicArrow <= id && id <= sdk.skills.LightningFury; + case sdk.player.class.Sorceress: + return sdk.skills.FireBolt <= id && id <= sdk.skills.ColdMastery; + case sdk.player.class.Necromancer: + return sdk.skills.AmplifyDamage <= id && id <= sdk.skills.Revive; + case sdk.player.class.Paladin: + return sdk.skills.Sacrifice <= id && id <= sdk.skills.Salvation; + case sdk.player.class.Barbarian: + return sdk.skills.Bash <= id && id <= sdk.skills.BattleCommand; + case sdk.player.class.Druid: + return sdk.skills.Raven <= id && id <= sdk.skills.Hurricane; + case sdk.player.class.Assassin: + return sdk.skills.FireBlast <= id && id <= sdk.skills.PhoenixStrike; + default: + return false; + } + } + + const gainedLevels = () => me.charlvl - prevLevel; + + function canSpendPoints () { + let unusedStatPoints = me.getStat(sdk.stats.StatPts); + let haveUnusedStatpoints = unusedStatPoints >= 5; // We spend 5 stat points per level up + let unusedSkillPoints = me.getStat(sdk.stats.NewSkills); + let haveUnusedSkillpoints = unusedSkillPoints >= 1; // We spend 1 skill point per level up + debug && AutoBuild.print("Stat points:", unusedStatPoints, " Skill points:", unusedSkillPoints); + return haveUnusedStatpoints && haveUnusedSkillpoints; + } + + function spendStatPoint (id) { + let unusedStatPoints = me.getStat(sdk.stats.StatPts); + if (SPEND_POINTS) { + useStatPoint(id); + AutoBuild.print("useStatPoint(" + id + "): " + STAT_ID_TO_NAME[id]); + } else { + AutoBuild.print("Fake useStatPoint(" + id + "): " + STAT_ID_TO_NAME[id]); + } + delay(100); // TODO: How long should we wait... if at all? + return (unusedStatPoints - me.getStat(sdk.stats.StatPts) === 1); // Check if we spent one point + } + + // TODO: What do we do if it fails? report/ignore/continue? + function spendStatPoints () { + if (currAutoBuild[me.charlvl] === undefined) return true; + let stats = currAutoBuild[me.charlvl].StatPoints; + let errorMessage = "\nInvalid stat point set in build template " + getTemplateFilename() + " at level " + me.charlvl; + let spentEveryPoint = true; + let unusedStatPoints = me.getStat(sdk.stats.StatPts); + let len = stats.length; + + if (Config.AutoStat.Enabled) { + return spentEveryPoint; + } + + if (len > unusedStatPoints) { + len = unusedStatPoints; + AutoBuild.print("Warning: Number of stats specified in your build template at level " + me.charlvl + " exceeds the available unused stat points" + + "\nOnly the first " + len + " stats " + stats.slice(0, len).join(", ") + " will be added"); + } + + // We silently ignore stats set to -1 + for (let i = 0; i < len; i++) { + let id = stats[i]; + let statIsValid = (typeof id === "number") && (0 <= id && id <= 3); + + if (id === -1) { + continue; + } else if (statIsValid) { + let preStatValue = me.getStat(id); + let pointSpent = spendStatPoint(id); + if (SPEND_POINTS) { + if (!pointSpent) { + spentEveryPoint = false; + AutoBuild.print("Attempt to spend point " + (i + 1) + " in " + STAT_ID_TO_NAME[id] + " may have failed!"); + } else if (debug) { + AutoBuild.print("Stat (" + (i + 1) + "/" + len + ") Increased " + STAT_ID_TO_NAME[id] + " from " + preStatValue + " to " + me.getStat(id)); + } + } + } else { + throw new Error("Stat id must be one of the following:\n0:" + STAT_ID_TO_NAME[0] + + ",\t1:" + STAT_ID_TO_NAME[1] + ",\t2:" + STAT_ID_TO_NAME[2] + ",\t3:" + STAT_ID_TO_NAME[3] + errorMessage); + } + } + + return spentEveryPoint; + } + + function getTemplateFilename () { + let buildType = Config.AutoBuild.Template; + let className = sdk.player.class.nameOf(me.classid); + let templateFilename = "SoloPlay/BuildFiles/" + className + "/" + className + "." + buildType + "Build.js"; + return templateFilename; + } + + function getRequiredSkills (id) { + function searchSkillTree (id) { + let results = []; + let skillTreeRight = getBaseStat("skills", id, sdk.stats.PreviousSkillRight); + let skillTreeMiddle = getBaseStat("skills", id, sdk.stats.PreviousSkillMiddle); + let skillTreeLeft = getBaseStat("skills", id, sdk.stats.PreviousSkillLeft); + + results.push(skillTreeRight); + results.push(skillTreeMiddle); + results.push(skillTreeLeft); + + for (let i = 0; i < results.length; i++) { + let skill = results[i]; + let skillInValidRange = (sdk.skills.Attack < skill && skill <= sdk.skills.PhoenixStrike) && (![sdk.skills.IdentifyScroll, sdk.skills.BookofIdentify, sdk.skills.TownPortalScroll, sdk.skills.BookofTownPortal].contains(skill)); + let hardPointsInSkill = me.getSkill(skill, sdk.skills.subindex.HardPoints); + + if (skillInValidRange && !hardPointsInSkill) { + requirements.push(skill); + searchSkillTree(skill); // search children; + } + } + } + + let requirements = []; + searchSkillTree(id); + const increasing = () => a - b; + return requirements.sort(increasing); + } + + function spendSkillPoint (id) { + let unusedSkillPoints = me.getStat(sdk.stats.NewSkills); + let skillName = getSkillById(id) + " (" + id + ")"; + if (SPEND_POINTS) { + useSkillPoint(id); + AutoBuild.print("useSkillPoint(): " + skillName); + } else { + AutoBuild.print("Fake useSkillPoint(): " + skillName); + } + delay(200); // TODO: How long should we wait... if at all? + return (unusedSkillPoints - me.getStat(sdk.stats.NewSkills) === 1); // Check if we spent one point + } + + function spendSkillPoints () { + if (currAutoBuild[me.charlvl] === undefined) return true; + let skills = currAutoBuild[me.charlvl].SkillPoints; + let errInvalidSkill = "\nInvalid skill point set in build template " + getTemplateFilename() + " for level " + me.charlvl; + let spentEveryPoint = true; + let unusedSkillPoints = me.getStat(sdk.stats.NewSkills); + let len = skills.length; + + if (Config.AutoSkill.Enabled) { + return spentEveryPoint; + } + + if (len > unusedSkillPoints) { + len = unusedSkillPoints; + AutoBuild.print("Warning: Number of skills specified in your build template at level " + me.charlvl + " exceeds the available unused skill points" + + "\nOnly the first " + len + " skills " + skills.slice(0, len).join(", ") + " will be added"); + } + + // We silently ignore skills set to -1 + for (let i = 0; i < len; i++) { + let id = skills[i]; + + if (id === -1) { + continue; + } else if (!skillInValidRange(id)) { + throw new Error("Skill id " + id + " is not a skill for your character class" + errInvalidSkill); + } + + let skillName = getSkillById(id) + " (" + id + ")"; + let requiredSkills = getRequiredSkills(id); + if (requiredSkills.length > 0) { + throw new Error("You need prerequisite skills " + requiredSkills.join(", ") + " before adding " + skillName + errInvalidSkill); + } + + let requiredLevel = getBaseStat("skills", id, sdk.stats.MinimumRequiredLevel); + if (me.charlvl < requiredLevel) { + throw new Error("You need to be at least level " + requiredLevel + " before you get " + skillName + errInvalidSkill); + } + + let pointSpent = spendSkillPoint(id); + + if (SPEND_POINTS) { + if (!pointSpent) { + spentEveryPoint = false; + AutoBuild.print("Attempt to spend skill point " + (i + 1) + " in " + skillName + " may have failed!"); + } else if (debug) { + let actualSkillLevel = me.getSkill(id, sdk.skills.subindex.SoftPoints); + AutoBuild.print("Skill (" + (i + 1) + "/" + len + ") Increased " + skillName + " by one (level: ", actualSkillLevel + ")"); + } + } + + delay(200); // TODO: How long should we wait... if at all? + } + + return spentEveryPoint; + } + + // Only load this in global scope + if (getScript(true).name.toLowerCase() === "libs\\soloplay\\soloplay.js") { + const Worker = require("../../modules/Worker"); + + let waitTick = getTickCount(); + currAutoBuild = usingFinalBuild ? finalBuild.AutoBuildTemplate : build.AutoBuildTemplate; + + // Start + Worker.runInBackground.AutoBuild = function () { + if (getTickCount() - waitTick < 1000) return true; + waitTick = getTickCount(); + + try { + let levels = gainedLevels(); + + if (levels > 0 && (canSpendPoints() || Config.AutoSkill.Enabled || Config.AutoStat.Enabled)) { + scriptBroadcast("toggleQuitlist"); + AutoBuild.print("Level up detected (", prevLevel, "-->", me.charlvl, ")"); + spendSkillPoints(); + spendStatPoints(); + Config.AutoSkill.Enabled && AutoSkill.init(Config.AutoSkill.Build, Config.AutoSkill.Save); + Config.AutoStat.Enabled && AutoStat.init(Config.AutoStat.Build, Config.AutoStat.Save, Config.AutoStat.BlockChance, Config.AutoStat.UseBulk); + scriptBroadcast({event: "level up"}); + AutoBuild.applyConfigUpdates(); // scriptBroadcast() won't trigger listener on this thread. + + debug && AutoBuild.print("Incrementing cached character level to", prevLevel + 1); + + // prevLevel doesn't get set to me.charlvl because + // we may have gained multiple levels at once + prevLevel += 1; + + scriptBroadcast("toggleQuitlist"); + } + } catch (err) { + print("Something broke!"); + print("Error:" + err.toSource()); + print("Stack trace: \n" + err.stack); + + return false; + } + + return true; + }; + + try { + AutoBuild.print("Loaded helper thread"); + console.log("ÿc8Kolbot-SoloPlayÿc0: Start AutoBuildThread"); + console.log(Worker.runInBackground); + } catch (e) { + console.error(e); + } + } +})(); diff --git a/libs/SoloPlay/Workers/EventWorker.js b/libs/SoloPlay/Workers/EventWorker.js new file mode 100644 index 00000000..32110552 --- /dev/null +++ b/libs/SoloPlay/Workers/EventWorker.js @@ -0,0 +1,163 @@ +/** +* @filename EventWorker.js +* @author theBGuy +* @desc worker thread to handle in game events for Kolbot-SoloPlay +* +*/ + +/** + * @todo make this actually work. Issue is if we interrupt a task and peform an event function, once that is over we've desynced from + * whatever we were orignally doing. In some cases this might be fine to just end whatever it was we were running + * e.g. after receiving finishDen we could kill the den script and perform the event function. We would be able to continue with the next + * script normally afterwards. We can't do that for something like diablo lightning dodge or baal wave skipping though, in those cases it + * might be fine to just continue because in theory we shouldn't be too far from our orginal action so minor desync in physical coordinates + * but not completly different area. We wouldn't be able to do that for diablo clone though. + */ +(function() { + // Only load this in global scope + if (getScript(true).name.toLowerCase() === "libs\\soloplay\\soloplay.js") { + const Worker = require("../../modules/Worker"); + + let tickDelay = 0; + let [action, profiles] = [[], []]; + + const scriptEventWorker = function (msg) { + if (msg && typeof msg === "string" && msg !== "") { + switch (msg) { + case "testing": + case "finishDen": + case "dodge": + case "skip": + case "killdclone": + action.push(msg); + + break; + case "addDiaEvent": + console.log("Added dia lightning listener"); + addEventListener("gamepacket", SoloEvents.diaEvent); + + break; + case "removeDiaEvent": + console.log("Removed dia lightning listener"); + removeEventListener("gamepacket", SoloEvents.diaEvent); + + break; + case "addBaalEvent": + console.log("Added baal wave listener"); + addEventListener("gamepacket", SoloEvents.baalEvent); + + break; + case "removeBaalEvent": + console.log("Removed baal wave listener"); + removeEventListener("gamepacket", SoloEvents.baalEvent); + + break; + // ignore common scriptBroadcast messages that aren't relevent to this thread + case "mule": + case "muleTorch": + case "muleAnni": + case "torch": + case "crafting": + case "getMuleMode": + case "pingquit": + return; + } + } + }; + + const receiveCopyDataWorker = function (id, info) { + // Torch + if (id === 55) { + let { profile, ladder, torchType } = JSON.parse(info); + console.log("Mesage recived for torch...processing"); + + if (profile !== me.profile && (me.hell || (me.nightmare && me.baal)) && me.ladder === ladder) { + if (torchType === me.classid && !me.findItem(604, 0, null, 7)) { + console.log("Sent Response"); + SoloEvents.sendToProfile(profile, {profile: me.profile, level: me.charlvl, event: 604}); + } + } + + return; + } + + // Annhilus + if (id === 60) { + let { profile, ladder } = JSON.parse(info); + console.log("Mesage recived for Annhilus...processing"); + + if (profile !== me.profile && (me.hell || (me.nightmare && me.baal)) && me.ladder === ladder) { + if (!me.findItem(603, 0, null, 7)) { + console.log("Sent Response"); + SoloEvents.sendToProfile(profile, {profile: me.profile, level: me.charlvl, event: 603}); + } + } + + return; + } + + if (id === 65) { + let { profile, level, event } = JSON.parse(info); + + console.log("Sucess: profile that contacted me: " + profile + " level of char: " + level); + SoloEvents.profileResponded = true; + profiles.push({profile: profile, level: level, event: event}); + tickDelay += 1000; + } + + if (id === 70) { + Messaging.sendToScript("D2BotSoloPlay.dbj", "event"); + delay(100 + me.ping); + scriptBroadcast("quit"); + } + }; + + addEventListener("scriptmsg", scriptEventWorker); + // should this just be added to the starter? would remove needing 3 copydata event listeners (entry, default, and here) + addEventListener("copydata", receiveCopyDataWorker); + + let waitTick = getTickCount(); + + // Start + Worker.runInBackground.EventWorker = function () { + if (getTickCount() - waitTick < 100 || SoloEvents.townChicken.running) return true; + waitTick = getTickCount(); + + try { + while (action.length) { + try { + SoloEvents[action.shift()](); + } catch (e) { + console.log(e); + } + } + + if (profiles.length > 0) { + let tick = getTickCount(); + + while (getTickCount() - tick < tickDelay) { + delay(500); + } + + let lowestLevelProf = profiles.sort((a, b) => a.level - b.level).first(); + + SoloEvents.sendToProfile(lowestLevelProf.profile, lowestLevelProf.event, 70); + D2Bot.joinMe(lowestLevelProf.profile, me.gamename.toLowerCase(), "", me.gamepassword.toLowerCase(), true); + profiles = []; + } + } catch (e) { + D2Bot.printToConsole(JSON.stringify(e)); + console.log(e); + } + + return true; + }; + + try { + console.log("ÿc8Kolbot-SoloPlayÿc0: Start EventThread"); + console.log(Worker.runInBackground); + } catch (e) { + console.error(e); + } + } +})(); diff --git a/libs/SoloPlay/Workers/TownChickenWorker.js b/libs/SoloPlay/Workers/TownChickenWorker.js new file mode 100644 index 00000000..40b13af5 --- /dev/null +++ b/libs/SoloPlay/Workers/TownChickenWorker.js @@ -0,0 +1,419 @@ +/** +* @filename TownChickenWorker.js +* @author theBGuy +* @desc TownChicken background worker thread +* +*/ + +/** + * @todo + * - figure out how to deal with loss of reference to whatever it was we might of been targetting beforehand. + * - How many chickens is too many for a script? How to end a script it that amount is reached. + */ +(function() { + // Only load this in global scope + if (getScript(true).name.toLowerCase() === "libs\\soloplay\\soloplay.js") { + const Worker = require("../../modules/Worker"); + + const getNearestMonster = () => { + let gid = null; + let monster = Game.getMonster(); + let range = 30; + + if (monster) { + do { + if (monster.attackable && !monster.getParent()) { + let distance = getDistance(me, monster); + + if (distance < range) { + [range, gid] = [distance, monster.gid]; + } + } + } while (monster.getNext()); + } + + gid && (Game.getMonster(-1, -1, gid)); + + if (monster) { + console.log("ÿc9TownChickenÿc0 :: Closest monster to me: " + monster.name + " | Monster classid: " + monster.classid); + return monster.classid; + } + + return -1; + }; + + const usePortal = function (targetArea, owner, unit, dummy) { + if (targetArea && me.inArea(targetArea)) return true; + + me.cancelUIFlags(); + + const townAreaCheck = (area = 0) => sdk.areas.Towns.includes(area); + const preArea = me.area; + const leavingTown = townAreaCheck(preArea); + + for (let i = 0; i < 13; i += 1) { + if (me.dead) return false; + if (targetArea ? me.inArea(targetArea) : me.area !== preArea) return true; + + (i > 0 && owner && me.inTown) && Town.move("portalspot"); + + let portal = unit ? copyUnit(unit) : Pather.getPortal(targetArea, owner); + + if (portal && portal.area === me.area) { + const useTk = me.inTown && Skill.useTK(portal) && i < 3; + if (useTk) { + portal.distance > 21 && (me.inTown && me.act === 5 ? Town.move("portalspot") : Pather.moveNearUnit(portal, 20)); + if (Skill.cast(sdk.skills.Telekinesis, sdk.skills.hand.Right, portal) + && Misc.poll(() => targetArea ? me.inArea(targetArea) : me.area !== preArea)) { + Pather.lastPortalTick = getTickCount(); + delay(100); + + return true; + } + } else { + portal.distance > 5 && (i < 3 ? Pather.moveNearUnit(portal, 4, false) : Pather.moveToUnit(portal)); + + if (getTickCount() - Pather.lastPortalTick > (leavingTown ? 2500 : 1000)) { + i < 2 ? Packet.entityInteract(portal) : Misc.click(0, 0, portal); + } else { + // only delay if we are in town and leaving town, don't delay if we are attempting to portal from out of town since this is the chicken thread + // and we are likely being attacked + leavingTown && delay(300); + + continue; + } + } + + let tick = getTickCount(); + + while (getTickCount() - tick < 500) { + if (me.area !== preArea) { + Pather.lastPortalTick = getTickCount(); + delay(100); + + return true; + } + + delay(10); + } + // try clicking dummy portal + !!dummy && portal.area === 1 && Misc.click(0, 0, portal); + + i > 1 && (i % 3) === 0 && Packet.flash(me.gid); + } else { + console.log("Didn't find portal, retry: " + i); + i > 3 && me.inTown && Town.move("portalspot", false); + if (i === 12) { + let p = Game.getObject("portal"); + console.debug(p); + if (!!p && Misc.click(0, 0, p) && Misc.poll(() => me.area !== preArea, 1000, 100)) { + Pather.lastPortalTick = getTickCount(); + delay(100); + + return true; + } + } + Packet.flash(me.gid); + } + + delay(250); + } + + return (targetArea ? me.inArea(targetArea) : me.area !== preArea); + }; + + const makePortal = function (use = false) { + if (me.inTown) return true; + + let oldGid = -1; + + for (let i = 0; i < 5; i += 1) { + if (me.dead) return false; + + let tpTool = me.getTpTool(); + if (!tpTool) return false; + + let oldPortal = Game.getObject(sdk.objects.BluePortal); + if (oldPortal) { + do { + if (oldPortal.getParent() === me.name) { + oldGid = oldPortal.gid; + break; + } + } while (oldPortal.getNext()); + + // old portal is close to use, we should try to use it + if (oldPortal.getParent() === me.name && oldPortal.distance < 4) { + if (use) { + if (usePortal(null, null, copyUnit(oldPortal))) return true; + break; // don't spam usePortal + } else { + return copyUnit(oldPortal); + } + } + } + + let pingDelay = me.getPingDelay(); + + if (tpTool.use() || Game.getObject("portal")) { + let tick = getTickCount(); + + while (getTickCount() - tick < Math.max(500 + i * 100, pingDelay * 2 + 100)) { + const portal = getUnits(sdk.unittype.Object, "portal") + .filter((p) => p.getParent() === me.name && p.gid !== oldGid).first(); + + if (portal) { + if (use) { + if (usePortal(null, null, copyUnit(portal))) return true; + break; // don't spam usePortal + } else { + return copyUnit(portal); + } + } else { + // check dummy + let dummy = getUnits(sdk.unittype.Object, "portal").filter(p => p.name === "Dummy").first(); + if (dummy) { + console.debug(dummy); + if (use) return usePortal(null, null, dummy, true); + return copyUnit(dummy); + } + } + + delay(10); + } + } else { + console.log("Failed to use tp tool"); + Packet.flash(me.gid, pingDelay); + delay(200 + pingDelay); + } + + delay(40); + } + + return false; + }; + + const goToTown = function (act = 0, wpmenu = false) { + if (!me.inTown) { + const townArea = sdk.areas.townOf(me.act); + try { + !makePortal(true) && console.warn("Town.goToTown: Failed to make TP"); + if (!me.inTown && !usePortal(townArea, me.name)) { + console.warn("Town.goToTown: Failed to take TP"); + if (!me.inTown && !usePortal(sdk.areas.townOf(me.area))) throw new Error("Town.goToTown: Failed to take TP"); + } + } catch (e) { + let tpTool = me.getTpTool(); + if (!tpTool && Misc.getPlayerCount() <= 1) { + Misc.errorReport(new Error("Town.goToTown: Failed to go to town and no tps available. Restart.")); + scriptBroadcast("quit"); + } else { + if (!Misc.poll(() => { + if (me.inTown) return true; + let p = Game.getObject("portal"); + console.debug(p); + !!p && Misc.click(0, 0, p) && delay(100); + Misc.poll(() => me.idle, 1000, 100); + console.debug("inTown? " + me.inTown); + return me.inTown; + }, 700, 100)) { + Misc.errorReport(new Error("Town.goToTown: Failed to go to town. Quiting.")); + scriptBroadcast("quit"); + } + } + } + } + + if (!act) return true; + if (act < 1 || act > 5) throw new Error("Town.goToTown: Invalid act"); + if (act > me.highestAct) return false; + + if (act !== me.act) { + try { + Pather.useWaypoint(sdk.areas.townOfAct(act), wpmenu); + } catch (WPError) { + throw new Error("Town.goToTown: Failed use WP"); + } + } + + return true; + }; + + const visitTown = function () { + console.log("ÿc8Start ÿc0:: ÿc8visitTown"); + + const preArea = me.area; + const preAct = sdk.areas.actOf(preArea); + + if (!me.inTown && !me.getTpTool()) { + console.warn("Can't chicken to town. Quit"); + scriptBroadcast("quit"); + return false; + } + + let tick = getTickCount(); + + // not an essential function -> handle thrown errors + me.cancelUIFlags(); + try { + goToTown(); + } catch (e) { + return false; + } + + const { x, y } = me; + + Town.doChores(); + + console.debug("Current act: " + me.act + " Prev Act: " + preAct); + me.act !== preAct && goToTown(preAct); + Town.move("portalspot"); + Pather.moveTo(x, y); + + while (getTickCount() - tick < 4500) { + delay(10); + } + + if (!usePortal(preArea, me.name)) { + try { + usePortal(null, me.name); + } catch (e) { + throw new Error("Town.visitTown: Failed to go back from town"); + } + } + + console.log("ÿc8End ÿc0:: ÿc8visitTown - currentArea: " + getAreaName(me.area)); + + return me.area === preArea; + }; + + let [townCheck] = [false, false]; + + const chickenScriptEvent = function (msg) { + if (SoloEvents.townChicken.disabled) return; + if (msg && typeof msg === "string" && msg !== "") { + switch (msg) { + case "townCheck": + switch (me.area) { + case sdk.areas.ArreatSummit: + case sdk.areas.UberTristram: + console.warn("Don't tp from " + getAreaName(me.area)); + return; + default: + console.log("townCheck message recieved. First check passed."); + townCheck = true; + + return; + } + case "quit": + //quitFlag = true; + // Maybe stop townChicken thread? Would that keep us from the crash that happens when we try to leave game while townChickening + + break; + default: + break; + } + } + }; + + Misc.townCheck = function () { + return false; + }; + + addEventListener("scriptmsg", chickenScriptEvent); + + // const lastChickens = []; + const useHowl = Skill.canUse(sdk.skills.Howl); + const useTerror = Skill.canUse(sdk.skills.Terror); + + Config.DebugMode = true; + let waitTick = getTickCount(); + let potTick = getTickCount(); + + // Start + Worker.runInBackground.TownChicken = function () { + if (getTickCount() - waitTick < 100 || SoloEvents.townChicken.disabled) return true; + waitTick = getTickCount(); + if (me.inTown) return true; + + let shouldChicken = ( + (townCheck || me.hpPercent < Config.TownHP || me.mpPercent < Config.TownMP) + ); + + if (shouldChicken && !me.canTpToTown()) { + // we should probably quit? + return true; + } + + if (!shouldChicken) { + if (getTickCount() - potTick < 300) return true; + potTick = getTickCount(); + // do we need potions? + if (!Config.TownCheck) return true; + // can we chicken? + if (!me.canTpToTown()) return true; + if (me.needPotions() || (Config.OpenChests.Enabled && Town.needKeys())) { + shouldChicken = true; + } + } + + if (shouldChicken) { + let t4 = getTickCount(); + try { + // const recentChicks = lastChickens + // .slice(Math.max(lastChickens.length - 3), lastChickens.length - 1); + + // const stopCurrentScript = recentChicks.length >= 2 && recentChicks + // .every(([count]) => getTickCount() - count < Time.minutes(1)); + + // lastChickens.push([getTickCount(), me.area, me.x, me.y]); + + // if (stopCurrentScript) { + // myPrint("ÿc8TownChicken :: ÿc0Too many chickens on this script, move to next :: " + me.gold); + // goToTown(); + // // scriptBroadcast("nextScript"); + // me.emit("nextScript"); + // return true; + // } + + myPrint("ÿc8TownChicken :: ÿc0Going to town. Initial Gold :: " + me.gold); + [Attack.stopClear, SoloEvents.townChicken.running] = [true, true]; + + // determine if this is really worth it + if (useHowl || useTerror) { + if ([156, 211, 242, 243, 544, 571, 345].indexOf(getNearestMonster()) === -1) { + if (useHowl && Skill.getManaCost(130) < me.mp) { + Skill.cast(130, sdk.skills.hand.Right); + } + + if (useTerror && Skill.getManaCost(77) < me.mp) { + Skill.cast(77, sdk.skills.hand.Right, getNearestMonster()); + } + } + } + + visitTown(); + } catch (e) { + Misc.errorReport(e, "TownChicken.js"); + scriptBroadcast("quit"); + + return false; + } finally { + Packet.flash(me.gid, 100); + console.log("ÿc8TownChicken :: Took: " + Time.format(getTickCount() - t4) + " to visit town. Ending Gold :: " + me.gold); + [Attack.stopClear, SoloEvents.townChicken.running, townCheck] = [false, false, false]; + } + } + + return true; + }; + + + try { + console.log("ÿc8Kolbot-SoloPlayÿc0: Start TownChicken thread"); + console.log(Worker.runInBackground); + } catch (e) { + console.error(e); + } + } +})(); From 8d9d635f319448431cfa07df691c2672f5104414 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Sat, 4 Feb 2023 01:10:36 -0500 Subject: [PATCH 015/263] Create index.d.ts - Add start of type definitions for better intellisense of modern IDE's --- libs/SoloPlay/index.d.ts | 180 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 180 insertions(+) create mode 100644 libs/SoloPlay/index.d.ts diff --git a/libs/SoloPlay/index.d.ts b/libs/SoloPlay/index.d.ts new file mode 100644 index 00000000..9e3251cd --- /dev/null +++ b/libs/SoloPlay/index.d.ts @@ -0,0 +1,180 @@ + +declare global { + interface ItemUnit { + readonly isCharm: boolean; + readonly isGem: boolean; + readonly isInsertable: boolean; + readonly isRuneword: boolean; + readonly isBroken: boolean; + readonly isBaseType: boolean; + readonly upgradedStrReq: boolean; + readonly upgradedDexReq: boolean; + readonly upgradedLvlReq: boolean; + readonly allRes: boolean; + readonly quantityPercent: number; + + getItemType(): string; + } + + interface Monster { + readonly isStunned: boolean; + readonly isUnderCoS: boolean; + readonly isUnderLowerRes: boolean; + } + + interface Unit { + getResPenalty(difficulty: number): number; + castChargedSkillEx(...args: any[]): boolean; + castSwitchChargedSkill(...args: any[]): boolean; + haveRunes(itemInfo: number[]): boolean; + } + + interface MeType { + readonly maxNearMonsters: number; + readonly dualWielding: boolean; + readonly realFR: number; + readonly realCR: number; + readonly realPR: number; + readonly realLR: number; + readonly FR: number; + readonly CR: number; + readonly LR: number; + readonly PR: number; + readonly onFinalBuild: boolean; + + canTpToTown(): boolean; + getMercEx(): MercUnit | null; + getEquippedItem(bodyLoc: number): ItemUnit | null; + getSkillTabs(classid: number): number[]; + inDanger(checkLoc?: {x: number, y: number} | MeType, range?: number): boolean; + checkSkill(skillId: number, subId: number): boolean; + cleanUpInvoPotions(beltSize: number): boolean; + needPotions(): boolean; + getIdTool(): ItemUnit | null; + getTpTool(): ItemUnit | null; + getUnids(): ItemUnit[]; + fieldID(): boolean; + getWeaponQuantity(weaponLoc: number): number; + getItemsForRepair(repairPercent: number, chargedItems?: boolean): ItemUnit[]; + needRepair(): string[]; + needMerc(): boolean; + } + + interface Container { + /** + * A function that checks if the cube is located at { x: 0, y: 0 } in the stash and moves it there if not + * @param name + */ + CubeSpot(name: string): boolean; + + /** + * A function that sorts items with optional priority + * @param itemIdsLeft + * @param itemIdsRight + */ + SortItems(itemIdsLeft: number[], itemIdsRight: number[]): boolean; + + /** + * A function that moves an item to a location in a container + * @param item + * @param reverseX + * @param reverseY + * @param priorityClassIds + */ + MoveTo(item: ItemUnit, reverseX: boolean, reverseY: boolean, priorityClassIds: number[]): boolean + + /** + * @param item + * @param location + * @param force + */ + MakeSpot(item: ItemUnit, location: { x: number, y: number }, force: boolean): boolean; + + /** + * @param item + * @param mX + * @param mY + */ + MoveToSpot(item: ItemUnit, mX: number, mY: number): boolean; + } + + namespace Mercenary { + let minCost: number; + + function getMercSkill(merc?: MercUnit): string | false; + function getMercDifficulty(merc?: MercUnit): number; + function getMercAct(merc?: MercUnit): number; + function getMercInfo(merc?: MercUnit): { classid: number, act: number, difficulty: number, type: string | false }; + function checkMercSkill(wanted: string, merc?: MercUnit): boolean; + function hireMerc(): boolean; + } + + namespace Misc { + let townEnabled: boolean; + } + + namespace Skill { + function switchCast(skillId: number, givenSettings: { hand?: number, x?: number, y?: number, switchBack?: boolean, oSkill?: boolean }): boolean; + } + + interface charData { + initialized: boolean; + normal: { + respecUsed: boolean; + imbueUsed: boolean; + socketUsed: boolean; + }; + nightmare: { + respecUsed: boolean; + imbueUsed: boolean; + socketUsed: boolean; + }; + hell: { + respecUsed: boolean; + imbueUsed: boolean; + socketUsed: boolean; + }; + me: { + task: string; + startTime: number; + charName: string; + classid: number; + level: number; + strength: number; + dexterity: number; + currentBuild: string; + finalBuild: string; + highestDifficulty: string; + setDifficulty: string; + charms: object; + charmGids: number[]; + }; + merc: { + act: number; + classid: number; + difficulty: number; + strength: number; + dexterity: number; + type: string; + gear: number[]; + }; + } + + namespace CharData { + const filePath: string; + const threads: string[]; + + // ignoring the sub objs for now + function updateConfig(): void; + function create(): charData; + function getObj(): charData; + function getStats(): charData; + function updateData(arg: string, property: object | string, value: any): boolean; + /** @alias CharData.delete */ + function _delete(deleteMain: boolean): boolean; + } + + namespace Check { + } +} +export{}; From c20d2196df5225819d03f56f8f864a35bc51e060 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Sun, 5 Feb 2023 15:17:19 -0500 Subject: [PATCH 016/263] Fix incorrect SP login - Add another check for realm control to prevent incorrect mode bug --- D2BotSoloPlay.dbj | 9 --- libs/SoloPlay/Tools/OOGOverrides.js | 90 +++++++++++++++-------------- 2 files changed, 46 insertions(+), 53 deletions(-) diff --git a/D2BotSoloPlay.dbj b/D2BotSoloPlay.dbj index 3a3aaeaf..6e6d873d 100644 --- a/D2BotSoloPlay.dbj +++ b/D2BotSoloPlay.dbj @@ -44,15 +44,6 @@ include("SoloPlay/Tools/CharData.js"); include("SoloPlay/Tools/Tracker.js"); include("SoloPlay/Tools/OOGOverrides.js"); -// keep the namespace name but only grab the properties we need for this thread -const SoloEvents = (() => { - let { outOfGameCheck, check, gameInfo } = require("./libs/SoloPlay/Functions/SoloEvents"); - return { - check: check, - gameInfo: gameInfo, - outOfGameCheck: outOfGameCheck, - }; -})(); // is this needed? soloplay doesn't run in default.dbj anymore include("SoloPlay/Functions/ConfigOverrides.js"); diff --git a/libs/SoloPlay/Tools/OOGOverrides.js b/libs/SoloPlay/Tools/OOGOverrides.js index 41100a4a..b2d906d7 100644 --- a/libs/SoloPlay/Tools/OOGOverrides.js +++ b/libs/SoloPlay/Tools/OOGOverrides.js @@ -21,6 +21,14 @@ const locations = {}; (function() { const Controls = require("../../modules/Control"); const Overrides = require("../../modules/Override"); + const SoloEvents = (() => { + let { outOfGameCheck, check, gameInfo } = require("../Functions/SoloEvents"); + return { + check: check, + gameInfo: gameInfo, + outOfGameCheck: outOfGameCheck, + }; + })(); new Overrides.Override(Starter, Starter.receiveCopyData, function (orignal, mode, msg) { switch (mode) { @@ -262,6 +270,17 @@ const locations = {}; delay(25); } + // Wrong char select screen fix + if ([sdk.game.locations.CharSelect, sdk.game.locations.CharSelectNoChars].includes(getLocation())) { + hideConsole(); // seems to fix odd crash with single-player characters if the console is open to type in + let spCheck = Profile().type === sdk.game.profiletype.Battlenet; + let realmControl = !!Controls.CharSelectCurrentRealm.control; + if ((spCheck && !realmControl) || ((!spCheck && realmControl))) { + Controls.CharSelectExit.click(); + return false; // what about a recursive call to loginCharacter? + } + } + if (getLocation() === sdk.game.locations.CharSelectConnecting) { if (!Starter.charSelectConnecting()) { D2Bot.printToConsole("Stuck at connecting screen"); @@ -283,8 +302,8 @@ const locations = {}; if (text instanceof Array && typeof text[1] === "string") { count++; - if (text[1].toLowerCase() === info.charName.toLowerCase()) { - return true; + if (String.isEqual(text[1], info.charName)) { + return control; } } } while (count < cap && control.getNext()); @@ -296,10 +315,10 @@ const locations = {}; this.scrollDown(); let check = Controls.CharSelectCharInfo0.control; - if (!!firstCheck && !!check) { + if (firstCheck && check) { let nameCheck = check.getText(); - if (firstCheck[1].toLowerCase() === nameCheck[1].toLowerCase()) { + if (String.isEqual(firstCheck[1], nameCheck[1])) { return false; } } @@ -316,8 +335,6 @@ const locations = {}; ControlAction.loginCharacter = function (info, startFromTop = true) { me.blockMouse = true; - let count = 0; - // start from beginning of the char list startFromTop && sendKey(sdk.keys.code.Home); @@ -339,50 +356,34 @@ const locations = {}; break; case sdk.game.locations.CharSelect: - let control = Controls.CharSelectCharInfo0.control; + let control = ControlAction.findCharacter(info); if (control) { - do { - let text = control.getText(); - - if (text instanceof Array && typeof text[1] === "string") { - count++; - - if (text[1].toLowerCase() === info.charName.toLowerCase()) { - control.click(); - Controls.CreateNewAccountOk.click(); - me.blockMouse = false; - - if (getLocation() === sdk.game.locations.SelectDifficultySP) { - try { - Starter.LocationEvents.selectDifficultySP(); - Starter.locationTimeout(Time.seconds(3), sdk.game.locations.SelectDifficultySP); - } catch (err) { - break MainLoop; - } + control.click(); + Controls.CreateNewAccountOk.click(); + me.blockMouse = false; - if (me.ingame) { - return true; - } - } + if (getLocation() === sdk.game.locations.SelectDifficultySP) { + try { + Starter.LocationEvents.selectDifficultySP(); + Starter.locationTimeout(Time.seconds(3), sdk.game.locations.SelectDifficultySP); + } catch (err) { + break MainLoop; + } - return true; - } + if (me.ingame) { + return true; } - } while (control.getNext()); - } + } - // check for additional characters up to 24 - if (count === 8 || count === 16) { - Controls.CharSelectChar6.click() && this.scrollDown(); - } else { - // no further check necessary - break MainLoop; + return true; + } else if (getLocation() !== sdk.game.locations.CharSelect) { + break; } - break; + break MainLoop; case sdk.game.locations.CharSelectNoChars: - Controls.CharSelectExit.click(); + Controls.CharSelectExit.click(); // why exit rather than returning false? break; case sdk.game.locations.Disconnected: @@ -694,10 +695,11 @@ const locations = {}; } // Wrong char select screen fix - if (getLocation() === sdk.game.locations.CharSelect) { + if ([sdk.game.locations.CharSelect, sdk.game.locations.CharSelectNoChars].includes(getLocation())) { hideConsole(); // seems to fix odd crash with single-player characters if the console is open to type in - if ((Profile().type === sdk.game.profiletype.Battlenet && !Controls.CharSelectCurrentRealm.control) - || ((Profile().type !== sdk.game.profiletype.Battlenet && Controls.CharSelectCurrentRealm.control))) { + let spCheck = Profile().type === sdk.game.profiletype.Battlenet; + let realmControl = !!Controls.CharSelectCurrentRealm.control; + if ((spCheck && !realmControl) || ((!spCheck && realmControl))) { Controls.CharSelectExit.click(); return; From ef80876e96ae2e831e9f4583cd26e6c12c174bb1 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Mon, 6 Feb 2023 12:41:59 -0500 Subject: [PATCH 017/263] Update SoloPlay.js - Fix reloading autobuild thread --- libs/SoloPlay/SoloPlay.js | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/libs/SoloPlay/SoloPlay.js b/libs/SoloPlay/SoloPlay.js index df2b1da1..13d9d31c 100644 --- a/libs/SoloPlay/SoloPlay.js +++ b/libs/SoloPlay/SoloPlay.js @@ -23,7 +23,7 @@ include("SoloPlay/Functions/ConfigOverrides.js"); include("SoloPlay/Functions/Globals.js"); // main thread specific -const LocalChat = require("../modules/LocalChat"); +const LocalChat = require("../modules/LocalChat", null, false); /** * @todo @@ -179,7 +179,6 @@ function main () { } else { load("libs/SoloPlay/Threads/EventThread.js"); load("libs/SoloPlay/Threads/TownChicken.js"); - load("libs/SoloPlay/Threads/AutoBuildThread.js"); } // Load guard if we want to see the stack as it runs @@ -227,6 +226,15 @@ function main () { // Start Running Script includeIfNotIncluded("SoloPlay/Utils/Init.js"); + // log threads - track memory use + if (Config.DebugMode.Memory) { + console.log("//~~~~~~~Current Threads~~~~~~~//"); + getThreads() + .sort((a, b) => b.memory - a.memory) + .forEach(t => console.log(t)); + console.log("//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~//"); + } + // Start Developer mode - this stops the script from progressing past this point and allows running specific scripts/functions through chat commands if (Developer.developerMode.enabled) { if (Developer.developerMode.profiles.some(prof => String.isEqual(prof, me.profile))) { From e30e0cbc6263a9403a55d96a58e8697a08dc5926 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Tue, 7 Feb 2023 16:42:18 -0500 Subject: [PATCH 018/263] Update PatherOverrides.js - Missed reference to old Developer.formatTime --- libs/SoloPlay/Functions/PatherOverrides.js | 28 ++++++++++++---------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/libs/SoloPlay/Functions/PatherOverrides.js b/libs/SoloPlay/Functions/PatherOverrides.js index d9f0ea1f..a1c93d56 100644 --- a/libs/SoloPlay/Functions/PatherOverrides.js +++ b/libs/SoloPlay/Functions/PatherOverrides.js @@ -93,13 +93,14 @@ NodeAction.pickItems = function (arg = {}) { if (arg.hasOwnProperty("allowPicking") && !arg.allowPicking) return; let item = Game.getItem(); - const maxDist = Skill.haveTK ? 15 : 5; - const regPickRange = Pather.canTeleport() ? Config.PickRange : 8; - const maxRange = Math.max(maxDist, regPickRange); - const totalList = [].concat(Pickit.essentialList, Pickit.pickList); - const filterJunk = (item) => !!item && item.onGroundOrDropping; if (item) { + const maxDist = Skill.haveTK ? 15 : 5; + const regPickRange = Pather.canTeleport() ? Config.PickRange : 8; + const maxRange = Math.max(maxDist, regPickRange); + const totalList = [].concat(Pickit.essentialList, Pickit.pickList); + const filterJunk = (item) => !!item && item.onGroundOrDropping; + do { if (item.onGroundOrDropping) { const itemDist = getDistance(me, item); @@ -119,11 +120,12 @@ NodeAction.pickItems = function (arg = {}) { } } } while (item.getNext()); + + Pickit.essentialList.length > 0 && (Pickit.essentialList = Pickit.essentialList.filter(filterJunk)); + Pickit.pickList.length > 0 && (Pickit.pickList = Pickit.pickList.filter(filterJunk)); + Pickit.essentialList.length > 0 && Pickit.essessntialsPick(false, false); + Pickit.pickList.length > 0 && Pickit.pickItems(regPickRange); } - Pickit.essentialList.length > 0 && (Pickit.essentialList = Pickit.essentialList.filter(filterJunk)); - Pickit.pickList.length > 0 && (Pickit.pickList = Pickit.pickList.filter(filterJunk)); - Pickit.essentialList.length > 0 && Pickit.essessntialsPick(false, false); - Pickit.pickList.length > 0 && Pickit.pickItems(regPickRange); }; // todo - fast shrineing, if we are right next to a shrine then grab it even with mobs around @@ -332,7 +334,6 @@ Pather.clearUIFlags = function () { getUIFlag(flag) && me.cancel(); }); }; - Pather.currentWalkingPath = []; Pather.move = function (target, givenSettings = {}) { // Abort if dead @@ -467,14 +468,15 @@ Pather.move = function (target, givenSettings = {}) { // @todo check shrines/chests in proximity to old node vs next node // let otherObjects = getUnits(sdk.unittype.Object).filter(el => getDistance()); if (goBack) { - console.debug("Going back to old node"); + console.debug("Going back to old node. Distance: " + node.distance); } else if (nearestNode && nearestNode.distance > 5 && node.distance > 5 && 100 / node.distance * nearestNode.distance < 95) { - console.debug("Moving to next node"); + console.debug("Moving to next node. Distance: " + nearestNode.distance); let newIndex = path.findIndex(node => nearestNode.x === node.x && nearestNode.y === node.y); if (newIndex > -1) { console.debug("Found new path index: " + newIndex + " of currentPathLen: " + path.length); path = path.slice(newIndex); node = path.shift(); + console.debug("New path length: " + path.length); } else { console.debug("Couldn't find new path index"); } @@ -779,7 +781,7 @@ Pather.clearToExit = function (currentarea, targetarea, cleartype = true) { } while (me.area !== targetarea); } - console.log("ÿc8Kolbot-SoloPlayÿc0: End clearToExit. Time elapsed: " + Developer.formatTime(getTickCount() - tick)); + console.log("ÿc8Kolbot-SoloPlayÿc0: End clearToExit. Time elapsed: " + Tracker.formatTime(getTickCount() - tick)); return (me.area === targetarea); }; From ccdfef00aa20d7a90f2244f9511b96aff1fe1983 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Tue, 7 Feb 2023 16:49:21 -0500 Subject: [PATCH 019/263] Update tristram.js - fix old reference to Questing namespace --- libs/SoloPlay/Scripts/tristram.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libs/SoloPlay/Scripts/tristram.js b/libs/SoloPlay/Scripts/tristram.js index 6dcc42f4..75944003 100644 --- a/libs/SoloPlay/Scripts/tristram.js +++ b/libs/SoloPlay/Scripts/tristram.js @@ -72,7 +72,7 @@ function tristram () { for (let i = 0; i < stones.length; i++) { let stone = stones[i]; - if (Common.Questing.activateStone(stone)) { + if (Common.Cain.activateStone(stone)) { stones.splice(i, 1); i--; } From d4252c8b2f163ff62528bc8a70fb4a6b1d3d758e Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Wed, 8 Feb 2023 00:42:20 -0500 Subject: [PATCH 020/263] Increase hardcore level req for all classes - increased nightmare level req to 36 across all classes - increased hell req to 71 for zon, necro, paladin, and sorc --- libs/SoloPlay/BuildFiles/amazon/amazon.js | 4 ++-- libs/SoloPlay/BuildFiles/assassin/assassin.js | 2 +- libs/SoloPlay/BuildFiles/barbarian/barbarian.js | 2 +- libs/SoloPlay/BuildFiles/druid/druid.js | 2 +- libs/SoloPlay/BuildFiles/necromancer/necromancer.js | 4 ++-- libs/SoloPlay/BuildFiles/paladin/paladin.js | 4 ++-- libs/SoloPlay/BuildFiles/sorceress/sorceress.js | 4 ++-- 7 files changed, 11 insertions(+), 11 deletions(-) diff --git a/libs/SoloPlay/BuildFiles/amazon/amazon.js b/libs/SoloPlay/BuildFiles/amazon/amazon.js index 141aeda7..c73c36ea 100644 --- a/libs/SoloPlay/BuildFiles/amazon/amazon.js +++ b/libs/SoloPlay/BuildFiles/amazon/amazon.js @@ -16,8 +16,8 @@ const CharInfo = { "Hell": 100, }; const hardcoreMode = { - "Normal": me.expansion ? 33 : 33, - "Nightmare": me.expansion ? 65 : 65, + "Normal": me.expansion ? 36 : 33, + "Nightmare": me.expansion ? 71 : 65, "Hell": 100, }; diff --git a/libs/SoloPlay/BuildFiles/assassin/assassin.js b/libs/SoloPlay/BuildFiles/assassin/assassin.js index a0b4ced2..739491d4 100644 --- a/libs/SoloPlay/BuildFiles/assassin/assassin.js +++ b/libs/SoloPlay/BuildFiles/assassin/assassin.js @@ -16,7 +16,7 @@ const CharInfo = { "Hell": 100, }; const hardcoreMode = { - "Normal": 33, + "Normal": 36, "Nightmare": 65, "Hell": 100, }; diff --git a/libs/SoloPlay/BuildFiles/barbarian/barbarian.js b/libs/SoloPlay/BuildFiles/barbarian/barbarian.js index f4a39cc7..e127ea90 100644 --- a/libs/SoloPlay/BuildFiles/barbarian/barbarian.js +++ b/libs/SoloPlay/BuildFiles/barbarian/barbarian.js @@ -16,7 +16,7 @@ const CharInfo = { "Hell": 100, }; const hardcoreMode = { - "Normal": me.expansion ? 33 : 33, + "Normal": me.expansion ? 36 : 33, "Nightmare": me.expansion ? 75 : 75, "Hell": 100, }; diff --git a/libs/SoloPlay/BuildFiles/druid/druid.js b/libs/SoloPlay/BuildFiles/druid/druid.js index 6053fc15..487c4d41 100644 --- a/libs/SoloPlay/BuildFiles/druid/druid.js +++ b/libs/SoloPlay/BuildFiles/druid/druid.js @@ -16,7 +16,7 @@ const CharInfo = { "Hell": 100, }; const hardcoreMode = { - "Normal": 33, + "Normal": 36, "Nightmare": 73, "Hell": 100, }; diff --git a/libs/SoloPlay/BuildFiles/necromancer/necromancer.js b/libs/SoloPlay/BuildFiles/necromancer/necromancer.js index e637a114..0407529f 100644 --- a/libs/SoloPlay/BuildFiles/necromancer/necromancer.js +++ b/libs/SoloPlay/BuildFiles/necromancer/necromancer.js @@ -16,8 +16,8 @@ const CharInfo = { "Hell": 100, }; const hardcoreMode = { - "Normal": me.expansion ? 33 : 33, - "Nightmare": me.expansion ? 70 : 70, + "Normal": me.expansion ? 36 : 33, + "Nightmare": me.expansion ? 71 : 70, "Hell": 100, }; diff --git a/libs/SoloPlay/BuildFiles/paladin/paladin.js b/libs/SoloPlay/BuildFiles/paladin/paladin.js index 1d50d653..533fb95c 100644 --- a/libs/SoloPlay/BuildFiles/paladin/paladin.js +++ b/libs/SoloPlay/BuildFiles/paladin/paladin.js @@ -16,8 +16,8 @@ const CharInfo = { "Hell": 100, }; const hardcoreMode = { - "Normal": me.expansion ? 33 : 33, - "Nightmare": me.expansion ? 65 : 65, + "Normal": me.expansion ? 36 : 33, + "Nightmare": me.expansion ? 71 : 65, "Hell": 100, }; diff --git a/libs/SoloPlay/BuildFiles/sorceress/sorceress.js b/libs/SoloPlay/BuildFiles/sorceress/sorceress.js index 9f19bd0c..00add54e 100644 --- a/libs/SoloPlay/BuildFiles/sorceress/sorceress.js +++ b/libs/SoloPlay/BuildFiles/sorceress/sorceress.js @@ -16,8 +16,8 @@ const CharInfo = { "Hell": 100, }; const hardcoreMode = { - "Normal": me.expansion ? 33 : 33, - "Nightmare": me.expansion ? 67 : 67, + "Normal": me.expansion ? 36 : 33, + "Nightmare": me.expansion ? 71 : 67, "Hell": 100, }; From 1d0e4f9e892ec6d7be28a58749b686bf9695fe51 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Wed, 8 Feb 2023 12:24:33 -0500 Subject: [PATCH 021/263] Update PatherOverrides.js - Add some of the new core additions for getting unstuck while pathing - Add handler for weird error with excessive move retry --- libs/SoloPlay/Functions/PatherOverrides.js | 89 +++++++++++++++++++--- 1 file changed, 78 insertions(+), 11 deletions(-) diff --git a/libs/SoloPlay/Functions/PatherOverrides.js b/libs/SoloPlay/Functions/PatherOverrides.js index a1c93d56..de0aacd2 100644 --- a/libs/SoloPlay/Functions/PatherOverrides.js +++ b/libs/SoloPlay/Functions/PatherOverrides.js @@ -335,17 +335,26 @@ Pather.clearUIFlags = function () { }); }; Pather.currentWalkingPath = []; + +/** + * @param {PathNode | Unit | PresetUnit} target + * @param {pathSettings} givenSettings + * @returns {boolean} + */ Pather.move = function (target, givenSettings = {}) { // Abort if dead if (me.dead) return false; - // assign settings + /** + * assign settings + * @type {pathSettings} + */ const settings = Object.assign({}, { clearSettings: { }, allowTeleport: true, - allowPicking: true, allowClearing: true, allowTown: true, + allowPicking: true, minDist: 3, retry: 5, pop: false, @@ -365,7 +374,7 @@ Pather.move = function (target, givenSettings = {}) { !settings.allowClearing && (settings.clearSettings.allowClearing = false); !settings.allowPicking && (settings.clearSettings.allowPicking = false); - (target instanceof PresetUnit) && (target = { x: target.roomx * 5 + target.x, y: target.roomy * 5 + target.y }); + (target instanceof PresetUnit) && (target = target.realCoords()); if (settings.minDist > 3) { target = Pather.spotOnDistance(target, settings.minDist, { returnSpotOnError: settings.returnSpotOnError, reductionType: (me.inTown ? 0 : 2) }); @@ -373,7 +382,22 @@ Pather.move = function (target, givenSettings = {}) { let fail = 0; let node = { x: target.x, y: target.y }; - let [cleared, leaped, invalidCheck] = [false, false, false]; + const leaped = { + at: 0, + /** @type {PathNode} */ + from: { x: null, y: null } + }; + const whirled = { + at: 0, + /** @type {PathNode} */ + from: { x: null, y: null } + }; + const cleared = { + at: 0, + /** @type {PathNode} */ + where: { x: null, y: null } + }; + let [invalidCheck] = [false]; Pather.clearUIFlags(); @@ -392,6 +416,10 @@ Pather.move = function (target, givenSettings = {}) { // need to work on a better force clearing method but for now just have all walkers clear unless we specifically are forcing them not to (like while repositioning) settings.allowClearing && !settings.clearSettings.clearPath && !useTeleport && (settings.clearSettings.clearPath = true); + if (settings.retry <= 3 && target.distance > useTeleport ? 120 : 60) { + settings.retry = 10; + } + // for now only do this for teleporters if (useTeleport && !me.normal) { /** @type {Array} */ @@ -422,6 +450,13 @@ Pather.move = function (target, givenSettings = {}) { node = path.shift(); + if (typeof settings.callback === "function" && settings.callback()) { + console.debug("Callback function passed. Ending path."); + useTeleport && Config.TeleSwitch && me.switchWeapons(Attack.getPrimarySlot() ^ 1); + PathDebug.removeHooks(); + return true; + } + if (getDistance(me, node) > 2) { // Make life in Maggot Lair easier fail >= 3 && fail % 3 === 0 && !Attack.validSpot(node.x, node.y) && (invalidCheck = true); @@ -505,15 +540,46 @@ Pather.move = function (target, givenSettings = {}) { } if (fail > 0 && (!useTeleport || tpMana > me.mp)) { - // Don't go berserk on longer paths - if (settings.allowClearing && !cleared && me.checkForMobs({range: 10}) && Attack.clear(10)) { - console.debug("Cleared Node"); - cleared = true; + // if we are allowed to clear + if (settings.allowClearing) { + // Don't go berserk on longer paths - also check that there are even mobs blocking us + if (cleared.at === 0 || getTickCount() - cleared.at > Time.seconds(3) && cleared.where.distance > 5 && me.checkForMobs({range: 10})) { + // only set that we cleared if we actually killed at least 1 mob + if (Attack.clear(10, null, null, null, settings.allowPicking)) { + console.debug("Cleared Node"); + cleared.at = getTickCount(); + [cleared.where.x, cleared.where.y] = [node.x, node.y]; + } + } } - // Only do this once - if (!leaped && Skill.canUse(sdk.skills.LeapAttack) && Skill.cast(sdk.skills.LeapAttack, sdk.skills.hand.Right, node.x, node.y)) { - leaped = true; + // Leap can be helpful on long paths but make sure we don't spam it + if (Skill.canUse(sdk.skills.LeapAttack)) { + // we can use leapAttack, now lets see if we should - either haven't used it yet or it's been long enough since last time + if (leaped.at === 0 || getTickCount() - leaped.at > Time.seconds(3) || leaped.from.distance > 5 || me.checkForMobs({ range: 6 })) { + // alright now if we have actually casted it set the values so we know + if (Skill.cast(sdk.skills.LeapAttack, sdk.skills.hand.Right, node.x, node.y)) { + leaped.at = getTickCount(); + [leaped.from.x, leaped.from.y] = [node.x, node.y]; + } + } + } + + /** + * whirlwind can be useful as well, implement it. + * Things to consider: + * 1) Can we cast whirlwind on the node? Is it blocked by something other than monsters. + * 2) If we can't cast on that node, is there another node between us and it that would work? + */ + if (Skill.canUse(sdk.skills.Whirlwind)) { + // we can use whirlwind, now lets see if we should - either haven't used it yet or it's been long enough since last time + if (whirled.at === 0 || getTickCount() - whirled.at > Time.seconds(3) || whirled.from.distance > 5 || me.checkForMobs({ range: 6 })) { + // alright now if we have actually casted it set the values so we know + if (Skill.cast(sdk.skills.Whirlwind, sdk.skills.hand.Right, node.x, node.y)) { + whirled.at = getTickCount(); + [whirled.from.x, whirled.from.y] = [node.x, node.y]; + } + } } } } @@ -538,6 +604,7 @@ Pather.move = function (target, givenSettings = {}) { if (fail > 100) { // why? console.debug(settings); + throw new Error("Retry limit excessivly exceeded"); } fail++; } From 055a395bc158dd278db70cee4febef8b7249559d Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Wed, 8 Feb 2023 12:25:11 -0500 Subject: [PATCH 022/263] Update summoner.js - Add check for talked to jerhyn state to access palace cellar --- libs/SoloPlay/Scripts/summoner.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/libs/SoloPlay/Scripts/summoner.js b/libs/SoloPlay/Scripts/summoner.js index 09b141ed..7d89388d 100644 --- a/libs/SoloPlay/Scripts/summoner.js +++ b/libs/SoloPlay/Scripts/summoner.js @@ -71,6 +71,10 @@ function summoner () { Town.doChores(false, { fullChores: true }); myPrint("starting summoner"); + if (!Misc.checkQuest(sdk.quest.id.TheArcaneSanctuary, 3/* talked to Jerhyn */)) { + Town.npcInteract("jerhyn"); + } + Pather.checkWP(sdk.areas.ArcaneSanctuary, true) ? Pather.useWaypoint(sdk.areas.ArcaneSanctuary) : Pather.getWP(sdk.areas.ArcaneSanctuary); Precast.doPrecast(true); teleportPads(); From c4bb7d3649bb3778265e69a868d3a1c970fcf54b Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Wed, 8 Feb 2023 17:14:38 -0500 Subject: [PATCH 023/263] Fix goToDifficulty - When changing difficulty for socketing, it was just broadcasting quit then returning out of the function where the next action was setup and the difficulty got reset before it managed to exit the game causing a loop --- libs/SoloPlay/Functions/Globals.js | 7 ++++++- libs/SoloPlay/SoloPlay.js | 2 +- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/libs/SoloPlay/Functions/Globals.js b/libs/SoloPlay/Functions/Globals.js index d759a405..4080b012 100644 --- a/libs/SoloPlay/Functions/Globals.js +++ b/libs/SoloPlay/Functions/Globals.js @@ -533,11 +533,16 @@ const goToDifficulty = function (diff = undefined, reason = "") { throw new Error("Failed to set difficulty"); } scriptBroadcast("quit"); + + while (me.ingame) { + delay(3); + } } catch (e) { console.debug(e.message ? e.message : e); + return false; } - return false; + return true; }; const buildAutoBuildTempObj = (update = () => {}) => ({ diff --git a/libs/SoloPlay/SoloPlay.js b/libs/SoloPlay/SoloPlay.js index 13d9d31c..3e762318 100644 --- a/libs/SoloPlay/SoloPlay.js +++ b/libs/SoloPlay/SoloPlay.js @@ -244,7 +244,7 @@ function main () { } if (Check.brokeCheck()) return true; - Check.usePreviousSocketQuest(); // Currently only supports going back to nightmare to socket a lidless if one is equipped. + if (Check.usePreviousSocketQuest()) return true; // Currently only supports going back to nightmare to socket a lidless if one is equipped. myPrint("starting run"); Loader.run(); // we have scripts to retry so lets run them From 22bc87f6b2dd8d73565c6a10cd5c470e8947b59a Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Wed, 8 Feb 2023 17:47:19 -0500 Subject: [PATCH 024/263] Update TownOverrides.js - Don't toss base items that aren't wanted by solo but are wanted by normal pickit --- libs/SoloPlay/Functions/TownOverrides.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/libs/SoloPlay/Functions/TownOverrides.js b/libs/SoloPlay/Functions/TownOverrides.js index b8e698d6..db72cc60 100644 --- a/libs/SoloPlay/Functions/TownOverrides.js +++ b/libs/SoloPlay/Functions/TownOverrides.js @@ -1117,7 +1117,9 @@ Town.clearInventory = function () { if ([Pickit.Result.UNWANTED, Pickit.Result.TRASH].indexOf(result) === -1) { if ((item.isBaseType && item.sockets > 0) || (classItemType(item) && item.normal && item.sockets === 0)) { if (!Item.betterThanStashed(item) && !Item.betterBaseThanWearing(item, Developer.debugging.baseCheck)) { - result = 4; + if (NTIP.CheckItem(item, NTIP_CheckListNoTier) === Pickit.Result.UNWANTED) { + result = Pickit.Result.TRASH; + } } } } From 1d2eaea58fcb6a8fa1e47051cff17cb7d9d49361 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Thu, 9 Feb 2023 12:27:09 -0500 Subject: [PATCH 025/263] Update baal.js - add use of callback for hardcore characters to end script if dolls are found --- libs/SoloPlay/Scripts/baal.js | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/libs/SoloPlay/Scripts/baal.js b/libs/SoloPlay/Scripts/baal.js index 460aa87d..aac24940 100644 --- a/libs/SoloPlay/Scripts/baal.js +++ b/libs/SoloPlay/Scripts/baal.js @@ -236,7 +236,12 @@ function baal () { } // Enter throne room - Pather.moveTo(15095, 5029, 5); + const dollQuit = me.hardcore; + Pather.moveToEx(15095, 5029, { callback: () => { + if (dollQuit && Game.getMonster(sdk.monsters.SoulKiller)) { + throw new ScriptError("Unsafe for hardcore, dolls found"); + } + }}); Pather.moveTo(15113, 5040, 5); let totalTick = getTickCount(); From 91b8f37055368ed5facb8c0edb9f4a24be369d8e Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Fri, 10 Feb 2023 13:13:01 -0500 Subject: [PATCH 026/263] Update OOGOverrides.js - Fix remaking hardcore characters - If account exists when we attempt to make an account, exit out and attempt login once before making a new account --- libs/SoloPlay/Tools/OOGOverrides.js | 35 ++++++++++++++++++++--------- 1 file changed, 24 insertions(+), 11 deletions(-) diff --git a/libs/SoloPlay/Tools/OOGOverrides.js b/libs/SoloPlay/Tools/OOGOverrides.js index b2d906d7..3514397f 100644 --- a/libs/SoloPlay/Tools/OOGOverrides.js +++ b/libs/SoloPlay/Tools/OOGOverrides.js @@ -19,6 +19,9 @@ includeIfNotIncluded("OOG.js"); const locations = {}; (function() { + let joinInfo; + + Starter.Config.StopOnDeadHardcore = false; const Controls = require("../../modules/Control"); const Overrides = require("../../modules/Override"); const SoloEvents = (() => { @@ -818,6 +821,8 @@ const locations = {}; } }; + Starter.accountExists = false; + Starter.LocationEvents.loginError = function () { let string = ""; let text = Controls.LoginErrorText.getText(); @@ -839,28 +844,36 @@ const locations = {}; case getLocaleString(sdk.locale.text.AccountMustBeAtLeast): case getLocaleString(sdk.locale.text.PasswordCantBeMoreThan): case getLocaleString(sdk.locale.text.AccountCantBeMoreThan): + case getLocaleString(sdk.locale.text.InvalidPassword): D2Bot.printToConsole(string); D2Bot.stop(); - break; - case getLocaleString(sdk.locale.text.InvalidPassword): - D2Bot.updateStatus("Invalid Password"); - D2Bot.printToConsole("Invalid Password"); - ControlAction.timeoutDelay("Invalid password delay", Starter.Config.InvalidPasswordDelay * 6e4); - D2Bot.printToConsole("Invalid Password - Restart"); - D2Bot.restart(); - break; case getLocaleString(5208): // Invalid account - case getLocaleString(5239): // An account name already exists - case getLocaleString(5249): // Unable to create account D2Bot.updateStatus("Invalid Account Name"); - D2Bot.printToConsole("Invalid Account Name"); + D2Bot.printToConsole("Invalid Account Name :: " + Starter.profileInfo.account); Starter.profileInfo.account = ""; Starter.profileInfo.password = ""; D2Bot.setProfile(Starter.profileInfo.account, Starter.profileInfo.password); D2Bot.restart(true); + break; + case getLocaleString(5249): // Unable to create account + case getLocaleString(5239): // An account name already exists + if (!Starter.accountExists) { + Starter.accountExists = true; + Control.LoginErrorOk.click(); + delay(100); + Control.CreateNewAccountExit.click(); + Starter.LocationEvents.login(); + return; + } + D2Bot.updateStatus("Account name already exists :: " + Starter.profileInfo.account); + D2Bot.printToConsole("Account name already exists :: " + Starter.profileInfo.account); + Starter.profileInfo.account = ""; + Starter.profileInfo.password = ""; + D2Bot.setProfile(Starter.profileInfo.account, Starter.profileInfo.password); + break; case getLocaleString(5202): // cd key intended for another product case getLocaleString(10915): // lod key intended for another product From 3c414d84648941ed3bdffee81be222b59178873e Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Sun, 12 Feb 2023 12:08:00 -0500 Subject: [PATCH 027/263] NTIP.arrayLooping -> NTIP.buildList - renamed for clarity - Temp handler for `stringArr[i] is undefined` - Add `NTIP.buildFinalGear` still want a better way to handle items but idea is to fix tierscoring to properly recognize final gear vs regular gear --- .../BuildFiles/Runewords/AncientsPledge.js | 4 +- libs/SoloPlay/BuildFiles/Runewords/Bone.js | 2 +- .../BuildFiles/Runewords/BreathOfTheDying.js | 2 +- .../BuildFiles/Runewords/CallToArms.js | 2 +- .../BuildFiles/Runewords/ChainsOfHonor.js | 2 +- libs/SoloPlay/BuildFiles/Runewords/Chaos.js | 2 +- .../BuildFiles/Runewords/CrescentMoon.js | 2 +- .../BuildFiles/Runewords/DragonArmor.js | 2 +- .../BuildFiles/Runewords/DreamHelm.js | 2 +- .../BuildFiles/Runewords/DreamShield.js | 2 +- libs/SoloPlay/BuildFiles/Runewords/Duress.js | 2 +- libs/SoloPlay/BuildFiles/Runewords/Enigma.js | 2 +- libs/SoloPlay/BuildFiles/Runewords/Exile.js | 2 +- libs/SoloPlay/BuildFiles/Runewords/Faith.js | 2 +- .../BuildFiles/Runewords/Fortitude.js | 2 +- libs/SoloPlay/BuildFiles/Runewords/Fury.js | 2 +- libs/SoloPlay/BuildFiles/Runewords/Grief.js | 2 +- .../BuildFiles/Runewords/HandOfJustice.js | 2 +- .../BuildFiles/Runewords/HeartOfTheOak.js | 2 +- libs/SoloPlay/BuildFiles/Runewords/Honor.js | 2 +- libs/SoloPlay/BuildFiles/Runewords/Ice.js | 2 +- .../BuildFiles/Runewords/KingsGrace.js | 2 +- .../SoloPlay/BuildFiles/Runewords/LastWish.js | 2 +- .../BuildFiles/Runewords/Lawbringer.js | 2 +- libs/SoloPlay/BuildFiles/Runewords/Lore.js | 12 +-- libs/SoloPlay/BuildFiles/Runewords/Malice.js | 2 +- .../SoloPlay/BuildFiles/Runewords/MercDoom.js | 2 +- .../BuildFiles/Runewords/MercFortitude.js | 6 +- .../BuildFiles/Runewords/MercInfinity.js | 2 +- .../BuildFiles/Runewords/MercInsight.js | 2 +- .../BuildFiles/Runewords/MercPride.js | 2 +- .../BuildFiles/Runewords/MercTreachery.js | 4 +- libs/SoloPlay/BuildFiles/Runewords/Myth.js | 2 +- .../BuildFiles/Runewords/PDiamondShield.js | 2 +- .../BuildFiles/Runewords/PhoenixShield.js | 2 +- libs/SoloPlay/BuildFiles/Runewords/Rhyme.js | 2 +- .../BuildFiles/Runewords/Sanctuary.js | 2 +- libs/SoloPlay/BuildFiles/Runewords/Silence.js | 2 +- libs/SoloPlay/BuildFiles/Runewords/Smoke.js | 2 +- .../BuildFiles/Runewords/SpiritShield.js | 2 +- .../BuildFiles/Runewords/SpiritSword.js | 2 +- libs/SoloPlay/BuildFiles/Runewords/Stealth.js | 4 +- libs/SoloPlay/BuildFiles/Runewords/Steel.js | 2 +- .../BuildFiles/Runewords/Treachery.js | 2 +- .../BuildFiles/Runewords/VoiceOfReason.js | 2 +- libs/SoloPlay/BuildFiles/Runewords/White.js | 2 +- libs/SoloPlay/Config/Amazon.js | 8 +- libs/SoloPlay/Config/Assassin.js | 6 +- libs/SoloPlay/Config/Barbarian.js | 8 +- libs/SoloPlay/Config/Druid.js | 8 +- libs/SoloPlay/Config/Necromancer.js | 8 +- libs/SoloPlay/Config/Paladin.js | 8 +- libs/SoloPlay/Config/Sorceress.js | 11 +-- libs/SoloPlay/Functions/Globals.js | 2 +- libs/SoloPlay/Functions/NTIPOverrides.js | 89 +++++++++++++++---- libs/SoloPlay/Threads/TownChicken.js | 2 +- libs/SoloPlay/Utils/Init.js | 2 +- libs/SoloPlay/Workers/TownChickenWorker.js | 2 +- 58 files changed, 161 insertions(+), 105 deletions(-) diff --git a/libs/SoloPlay/BuildFiles/Runewords/AncientsPledge.js b/libs/SoloPlay/BuildFiles/Runewords/AncientsPledge.js index 7af7dd3f..2c0c7589 100644 --- a/libs/SoloPlay/BuildFiles/Runewords/AncientsPledge.js +++ b/libs/SoloPlay/BuildFiles/Runewords/AncientsPledge.js @@ -10,7 +10,7 @@ "[name] == OrtRune # # [maxquantity] == 1", "[name] == TalRune # # [maxquantity] == 1", ]; - NTIP.arrayLooping(apRunes); + NTIP.buildList(apRunes); } const apShields = [ @@ -18,7 +18,7 @@ "!me.hell && [name] == kiteshield && [flag] != ethereal && [quality] >= normal && [quality] <= superior # [sockets] == 3 # [maxquantity] == 1", "([name] == dragonshield || [name] == scutum) && [flag] != ethereal && [quality] >= normal && [quality] <= superior # [sockets] == 3 # [maxquantity] == 1", ]; - NTIP.arrayLooping(apShields); + NTIP.buildList(apShields); if (me.paladin) { NTIP.addLine("([name] == targe || [name] == rondache || [name] == heraldicshield || [name] == aerinshield || [name] == akarantarge || [name] == akaranrondache || [name] == gildedshield ||[name] == protectorshield || [name] == sacredtarge) && [flag] != ethereal && [quality] >= normal && [quality] <= superior # [fireresist] > 0 && [sockets] == 3"); diff --git a/libs/SoloPlay/BuildFiles/Runewords/Bone.js b/libs/SoloPlay/BuildFiles/Runewords/Bone.js index b7080c6a..40f5b626 100644 --- a/libs/SoloPlay/BuildFiles/Runewords/Bone.js +++ b/libs/SoloPlay/BuildFiles/Runewords/Bone.js @@ -3,7 +3,7 @@ "[name] == UmRune # # [maxquantity] == 2", "[name] == SolRune # # [maxquantity] == 1", ]; - NTIP.arrayLooping(Bone); + NTIP.buildList(Bone); // Cube to Um Rune if (Item.getQuantityOwned(me.getItem(sdk.items.runes.Um)) < 2) { diff --git a/libs/SoloPlay/BuildFiles/Runewords/BreathOfTheDying.js b/libs/SoloPlay/BuildFiles/Runewords/BreathOfTheDying.js index cc15d0d5..4e43cff7 100644 --- a/libs/SoloPlay/BuildFiles/Runewords/BreathOfTheDying.js +++ b/libs/SoloPlay/BuildFiles/Runewords/BreathOfTheDying.js @@ -8,7 +8,7 @@ "me.diff == 2 && [name] == EthRune # # [maxquantity] == 1", "[name] == colossusblade && [flag] == ethereal && [quality] >= normal && [quality] <= superior # [sockets] == 6 # [maxquantity] == 1", ]; - NTIP.arrayLooping(BoTD); + NTIP.buildList(BoTD); // Have Zod rune but do not have a base yet if (!Check.haveBase("colossusblade", 6) && me.getItem(sdk.items.runes.Zod)) { diff --git a/libs/SoloPlay/BuildFiles/Runewords/CallToArms.js b/libs/SoloPlay/BuildFiles/Runewords/CallToArms.js index 40ad013f..514b8d5b 100644 --- a/libs/SoloPlay/BuildFiles/Runewords/CallToArms.js +++ b/libs/SoloPlay/BuildFiles/Runewords/CallToArms.js @@ -6,7 +6,7 @@ "[name] == IstRune", "[name] == OhmRune", ]; - NTIP.arrayLooping(CTA); + NTIP.buildList(CTA); // Have Ohm before collecting base if (me.getItem(sdk.items.runes.Ohm)) { diff --git a/libs/SoloPlay/BuildFiles/Runewords/ChainsOfHonor.js b/libs/SoloPlay/BuildFiles/Runewords/ChainsOfHonor.js index 1c8ee74e..64ac58e1 100644 --- a/libs/SoloPlay/BuildFiles/Runewords/ChainsOfHonor.js +++ b/libs/SoloPlay/BuildFiles/Runewords/ChainsOfHonor.js @@ -5,7 +5,7 @@ "[name] == BerRune", "[name] == IstRune", ]; - NTIP.arrayLooping(CoH); + NTIP.buildList(CoH); // Cube to Ber rune if (!me.getItem(sdk.items.runes.Ber)) { diff --git a/libs/SoloPlay/BuildFiles/Runewords/Chaos.js b/libs/SoloPlay/BuildFiles/Runewords/Chaos.js index 9f43835f..b5fce4f9 100644 --- a/libs/SoloPlay/BuildFiles/Runewords/Chaos.js +++ b/libs/SoloPlay/BuildFiles/Runewords/Chaos.js @@ -6,7 +6,7 @@ "[name] == suwayyah && [quality] >= normal && [quality] <= superior # ([assassinskills]+[shadowdisciplinesskilltab]+[skillvenom]+[skilldeathsentry]+[skillfade]+[skillshadowmaster]) >= 1 && [sockets] == 3 # [maxquantity] == 1", "[name] == suwayyah && [quality] == normal # ([assassinskills]+[shadowdisciplinesskilltab]+[skillvenom]+[skilldeathsentry]+[skillfade]+[skillshadowmaster]) >= 1 && [sockets] == 0 # [maxquantity] == 1", ]; - NTIP.arrayLooping(Chaos); + NTIP.buildList(Chaos); Config.Runewords.push([Runeword.Chaos, "suwayyah"]); diff --git a/libs/SoloPlay/BuildFiles/Runewords/CrescentMoon.js b/libs/SoloPlay/BuildFiles/Runewords/CrescentMoon.js index 65e4156a..671d74c5 100644 --- a/libs/SoloPlay/BuildFiles/Runewords/CrescentMoon.js +++ b/libs/SoloPlay/BuildFiles/Runewords/CrescentMoon.js @@ -4,7 +4,7 @@ "[name] == UmRune", "[name] == TirRune # # [maxquantity] == 1", ]; - NTIP.arrayLooping(Crescent); + NTIP.buildList(Crescent); if (me.barbarian) { // Cube to Um Rune diff --git a/libs/SoloPlay/BuildFiles/Runewords/DragonArmor.js b/libs/SoloPlay/BuildFiles/Runewords/DragonArmor.js index 0fc5fd74..4f298332 100644 --- a/libs/SoloPlay/BuildFiles/Runewords/DragonArmor.js +++ b/libs/SoloPlay/BuildFiles/Runewords/DragonArmor.js @@ -4,7 +4,7 @@ "[name] == LoRune", "[name] == SolRune # # [maxquantity] == 1", ]; - NTIP.arrayLooping(DragonArmor); + NTIP.buildList(DragonArmor); // Cube to Sur rune if (!me.getItem(sdk.items.runes.Sur)) { diff --git a/libs/SoloPlay/BuildFiles/Runewords/DreamHelm.js b/libs/SoloPlay/BuildFiles/Runewords/DreamHelm.js index 166b629b..9adee6c3 100644 --- a/libs/SoloPlay/BuildFiles/Runewords/DreamHelm.js +++ b/libs/SoloPlay/BuildFiles/Runewords/DreamHelm.js @@ -5,7 +5,7 @@ "[name] == PulRune", "[name] == bonevisage && [flag] != ethereal && [quality] == superior # [enhanceddefense] >= 15 && [sockets] == 3 # [maxquantity] == 1", ]; - NTIP.arrayLooping(DreamHelm); + NTIP.buildList(DreamHelm); Config.Runewords.push([Runeword.Dream, "bonevisage"]); Config.KeepRunewords.push("[type] == helm # [holyshockaura] >= 15"); diff --git a/libs/SoloPlay/BuildFiles/Runewords/DreamShield.js b/libs/SoloPlay/BuildFiles/Runewords/DreamShield.js index 007526ba..a95ff9bc 100644 --- a/libs/SoloPlay/BuildFiles/Runewords/DreamShield.js +++ b/libs/SoloPlay/BuildFiles/Runewords/DreamShield.js @@ -5,7 +5,7 @@ "[name] == PulRune", "[name] == sacredtarge && [flag] != ethereal && [quality] >= normal && [quality] <= superior # [fireresist] >= 45 && [sockets] == 3 # [maxquantity] == 1", ]; - NTIP.arrayLooping(DreamShield); + NTIP.buildList(DreamShield); if (!Check.haveBase("sacredtarge", 3)) { NTIP.addLine("[name] == sacredtarge && [flag] != ethereal && [quality] == normal # [fireresist] >= 45 && [sockets] == 0 # [maxquantity] == 1"); diff --git a/libs/SoloPlay/BuildFiles/Runewords/Duress.js b/libs/SoloPlay/BuildFiles/Runewords/Duress.js index fa808529..c39f4252 100644 --- a/libs/SoloPlay/BuildFiles/Runewords/Duress.js +++ b/libs/SoloPlay/BuildFiles/Runewords/Duress.js @@ -4,7 +4,7 @@ "[name] == UmRune # # [maxquantity] == 1", "[name] == ThulRune # # [maxquantity] == 1", ]; - NTIP.arrayLooping(Duress); + NTIP.buildList(Duress); // Cube to Um rune if (!me.getItem(sdk.items.runes.Um)) { diff --git a/libs/SoloPlay/BuildFiles/Runewords/Enigma.js b/libs/SoloPlay/BuildFiles/Runewords/Enigma.js index 38ba82ad..e253ee4a 100644 --- a/libs/SoloPlay/BuildFiles/Runewords/Enigma.js +++ b/libs/SoloPlay/BuildFiles/Runewords/Enigma.js @@ -4,7 +4,7 @@ "[name] == IthRune # # [maxquantity] == 1", "[name] == BerRune", ]; - NTIP.arrayLooping(Enigma); + NTIP.buildList(Enigma); // Cube to Jah rune if (!me.getItem(sdk.items.runes.Jah)) { diff --git a/libs/SoloPlay/BuildFiles/Runewords/Exile.js b/libs/SoloPlay/BuildFiles/Runewords/Exile.js index 6324efcd..0293942d 100644 --- a/libs/SoloPlay/BuildFiles/Runewords/Exile.js +++ b/libs/SoloPlay/BuildFiles/Runewords/Exile.js @@ -6,7 +6,7 @@ "[name] == DolRune # # [maxquantity] == 1", "[name] == sacredtarge && [quality] >= normal && [quality] <= superior # [fireresist] >= 30 && [sockets] == 4 # [maxquantity] == 1", ]; - NTIP.arrayLooping(Exile); + NTIP.buildList(Exile); if (!Check.haveBase("sacredtarge", 4)) { NTIP.addLine("[name] == sacredtarge && [quality] == normal && [flag] == ethereal # [fireresist] >= 30 && [sockets] == 0 # [maxquantity] == 1"); diff --git a/libs/SoloPlay/BuildFiles/Runewords/Faith.js b/libs/SoloPlay/BuildFiles/Runewords/Faith.js index 4bbfb61f..e62e5766 100644 --- a/libs/SoloPlay/BuildFiles/Runewords/Faith.js +++ b/libs/SoloPlay/BuildFiles/Runewords/Faith.js @@ -5,7 +5,7 @@ "[name] == EldRune # # [maxquantity] == 1", "[name] == LemRune # # [maxquantity] == 1", ]; - NTIP.arrayLooping(FaithRunes); + NTIP.buildList(FaithRunes); // Cube to Ohm and Keep cubing to Jah rune if (Item.getQuantityOwned(me.getItem(sdk.items.runes.Ohm) > 1) && me.checkItem({name: sdk.locale.items.CalltoArms}).have) { if (!me.getItem(sdk.items.runes.Jah)) { diff --git a/libs/SoloPlay/BuildFiles/Runewords/Fortitude.js b/libs/SoloPlay/BuildFiles/Runewords/Fortitude.js index f639000e..7c77c4c1 100644 --- a/libs/SoloPlay/BuildFiles/Runewords/Fortitude.js +++ b/libs/SoloPlay/BuildFiles/Runewords/Fortitude.js @@ -5,7 +5,7 @@ "[name] == DolRune # # [maxquantity] == 1", "[name] == LoRune", ]; - NTIP.arrayLooping(Fortitude); + NTIP.buildList(Fortitude); // Have Lo rune before looking for normal base if (me.getItem(sdk.items.runes.Lo)) { diff --git a/libs/SoloPlay/BuildFiles/Runewords/Fury.js b/libs/SoloPlay/BuildFiles/Runewords/Fury.js index cd08d497..8468c59a 100644 --- a/libs/SoloPlay/BuildFiles/Runewords/Fury.js +++ b/libs/SoloPlay/BuildFiles/Runewords/Fury.js @@ -6,7 +6,7 @@ "[name] == suwayyah && [quality] >= normal && [quality] <= superior # ([assassinskills]+[shadowdisciplinesskilltab]+[skillvenom]+[skilldeathsentry]+[skillfade]+[skillshadowmaster]) >= 1 && [sockets] == 3 # [maxquantity] == 1", "[name] == suwayyah && [quality] == normal # ([assassinskills]+[shadowdisciplinesskilltab]+[skillvenom]+[skilldeathsentry]+[skillfade]+[skillshadowmaster]) >= 1 && [sockets] == 0 # [maxquantity] == 1", ]; - NTIP.arrayLooping(Fury); + NTIP.buildList(Fury); Config.Runewords.push([Runeword.Fury, "suwayyah"]); diff --git a/libs/SoloPlay/BuildFiles/Runewords/Grief.js b/libs/SoloPlay/BuildFiles/Runewords/Grief.js index c43cca08..277f8d6e 100644 --- a/libs/SoloPlay/BuildFiles/Runewords/Grief.js +++ b/libs/SoloPlay/BuildFiles/Runewords/Grief.js @@ -6,7 +6,7 @@ "[name] == MalRune # # [maxquantity] == 1", "[name] == RalRune # # [maxquantity] == 1", ]; - NTIP.arrayLooping(Grief); + NTIP.buildList(Grief); if (me.getItem(sdk.items.runes.Lo)) { NTIP.addLine("[name] == phaseblade && [quality] >= normal && [quality] <= superior # [sockets] == 5 # [maxquantity] == 1"); diff --git a/libs/SoloPlay/BuildFiles/Runewords/HandOfJustice.js b/libs/SoloPlay/BuildFiles/Runewords/HandOfJustice.js index 2c539db0..35441652 100644 --- a/libs/SoloPlay/BuildFiles/Runewords/HandOfJustice.js +++ b/libs/SoloPlay/BuildFiles/Runewords/HandOfJustice.js @@ -6,7 +6,7 @@ "[name] == LoRune", "[name] == phaseblade && [quality] >= normal && [quality] <= superior # [sockets] == 4 # [maxquantity] == 1", ]; - NTIP.arrayLooping(HoJ); + NTIP.buildList(HoJ); // Cube to Lo rune if (!me.getItem(sdk.items.runes.Lo)) { diff --git a/libs/SoloPlay/BuildFiles/Runewords/HeartOfTheOak.js b/libs/SoloPlay/BuildFiles/Runewords/HeartOfTheOak.js index 8ff82f21..6baabec9 100644 --- a/libs/SoloPlay/BuildFiles/Runewords/HeartOfTheOak.js +++ b/libs/SoloPlay/BuildFiles/Runewords/HeartOfTheOak.js @@ -5,7 +5,7 @@ "[name] == KoRune # # [maxquantity] == 1", "[name] == VexRune", ]; - NTIP.arrayLooping(HotO); + NTIP.buildList(HotO); // Have Vex rune before looking for base if (me.getItem(sdk.items.runes.Vex)) { diff --git a/libs/SoloPlay/BuildFiles/Runewords/Honor.js b/libs/SoloPlay/BuildFiles/Runewords/Honor.js index 14c79560..dbe3612e 100644 --- a/libs/SoloPlay/BuildFiles/Runewords/Honor.js +++ b/libs/SoloPlay/BuildFiles/Runewords/Honor.js @@ -6,7 +6,7 @@ "[name] == TirRune # # [maxquantity] == 1", "[name] == SolRune # # [maxquantity] == 1", ]; - NTIP.arrayLooping(Honor); + NTIP.buildList(Honor); // Cube to Amn rune if (!me.getItem(sdk.items.runes.Amn)) { diff --git a/libs/SoloPlay/BuildFiles/Runewords/Ice.js b/libs/SoloPlay/BuildFiles/Runewords/Ice.js index 6c6b1249..9ec9af83 100644 --- a/libs/SoloPlay/BuildFiles/Runewords/Ice.js +++ b/libs/SoloPlay/BuildFiles/Runewords/Ice.js @@ -5,7 +5,7 @@ "[name] == JahRune", "[name] == LoRune", ]; - NTIP.arrayLooping(IceRunes); + NTIP.buildList(IceRunes); // Cube to Lo and Keep cubing to Jah rune if (Item.getQuantityOwned(me.getItem(sdk.items.runes.Lo)) > 1 && me.checkItem({name: sdk.locale.items.ChainofHonor}).have) { diff --git a/libs/SoloPlay/BuildFiles/Runewords/KingsGrace.js b/libs/SoloPlay/BuildFiles/Runewords/KingsGrace.js index 10e8686e..16489788 100644 --- a/libs/SoloPlay/BuildFiles/Runewords/KingsGrace.js +++ b/libs/SoloPlay/BuildFiles/Runewords/KingsGrace.js @@ -6,7 +6,7 @@ "[type] == sword && [flag] != ethereal && [quality] >= normal && [quality] <= superior && [wsm] <= 10 && [strreq] <= 150 # [sockets] == 3 # [maxquantity] == 1", "([name] == legendsword || [name] == highlandblade || [name] == balrogblade || [name] == colossussword) && [flag] != ethereal && [quality] >= normal && [quality] <= superior # [sockets] == 3 # [maxquantity] == 1", ]; - NTIP.arrayLooping(KingsGrace); + NTIP.buildList(KingsGrace); Config.Runewords.push([Runeword.KingsGrace, "broadsword"]); Config.Runewords.push([Runeword.KingsGrace, "longsword"]); diff --git a/libs/SoloPlay/BuildFiles/Runewords/LastWish.js b/libs/SoloPlay/BuildFiles/Runewords/LastWish.js index fd5ac37e..fd245783 100644 --- a/libs/SoloPlay/BuildFiles/Runewords/LastWish.js +++ b/libs/SoloPlay/BuildFiles/Runewords/LastWish.js @@ -7,7 +7,7 @@ "[name] == BerRune", "[name] == phaseblade && [quality] >= normal && [quality] <= superior # [sockets] == 6 # [maxquantity] == 1", ]; - NTIP.arrayLooping(LW); + NTIP.buildList(LW); // Cube to Jah/Sur rune if (!me.getItem(sdk.items.runes.Jah) || !me.getItem(sdk.items.runes.Sur)) { if (me.checkItem({name: sdk.locale.items.CalltoArms}).have) { diff --git a/libs/SoloPlay/BuildFiles/Runewords/Lawbringer.js b/libs/SoloPlay/BuildFiles/Runewords/Lawbringer.js index 930a1dd0..428cf7fb 100644 --- a/libs/SoloPlay/BuildFiles/Runewords/Lawbringer.js +++ b/libs/SoloPlay/BuildFiles/Runewords/Lawbringer.js @@ -4,7 +4,7 @@ "[name] == LemRune", "[name] == KoRune", ]; - NTIP.arrayLooping(Lawbringer); + NTIP.buildList(Lawbringer); // Have Lem and Ko runes before looking for normal base if (me.getItem(sdk.items.runes.Lem) && me.getItem(sdk.items.runes.Ko)) { diff --git a/libs/SoloPlay/BuildFiles/Runewords/Lore.js b/libs/SoloPlay/BuildFiles/Runewords/Lore.js index d55518d1..cc85c49c 100644 --- a/libs/SoloPlay/BuildFiles/Runewords/Lore.js +++ b/libs/SoloPlay/BuildFiles/Runewords/Lore.js @@ -4,7 +4,7 @@ "[name] == OrtRune # # [maxquantity] == 1", "[name] == SolRune # # [maxquantity] == 1", ]; - NTIP.arrayLooping(loreRunes); + NTIP.buildList(loreRunes); // Cube to Sol rune if (!me.getItem(sdk.items.runes.Sol)) { @@ -32,11 +32,11 @@ "[type] == pelt && [flag] != ethereal && [quality] >= normal && [quality] <= superior # [sockets] == 2", "[type] == pelt && [quality] == normal # ([druidskills]+[elementalskilltab]+[skillcyclonearmor]+[skilltwister]+[skilltornado]+[skillhurricane]) >= 1 && [sockets] == 0", ]; - NTIP.arrayLooping(classLoreHelm); + NTIP.buildList(classLoreHelm); // Normal Helms if (Item.getEquipped(sdk.body.Head).tier < 150) { - NTIP.arrayLooping(loreHelm); + NTIP.buildList(loreHelm); } // Pelts @@ -81,11 +81,11 @@ "[type] == primalhelm && [flag] != ethereal && [quality] >= normal && [quality] <= superior && [strreq] <= 150 # [sockets] == 2", "[type] == primalhelm && [flag] != ethereal && [quality] == normal && [strreq] <= 150 # ([barbarianskills]+[barbcombatskilltab]+[skillbattleorders]+[skillfrenzy]+[skilldoubleswing]+[skillnaturalresistance]) >= 1 && [sockets] == 0", ]; - NTIP.arrayLooping(classLoreHelm); + NTIP.buildList(classLoreHelm); // Normal Helms if (Item.getEquipped(sdk.body.Head).tier < 150) { - NTIP.arrayLooping(loreHelm); + NTIP.buildList(loreHelm); } // Primal Helms @@ -119,7 +119,7 @@ } if (!me.druid && !me.barbarian) { - NTIP.arrayLooping(loreHelm); + NTIP.buildList(loreHelm); } // Normal helms diff --git a/libs/SoloPlay/BuildFiles/Runewords/Malice.js b/libs/SoloPlay/BuildFiles/Runewords/Malice.js index f25fa26f..8289fbb9 100644 --- a/libs/SoloPlay/BuildFiles/Runewords/Malice.js +++ b/libs/SoloPlay/BuildFiles/Runewords/Malice.js @@ -5,7 +5,7 @@ "[name] == EthRune # # [maxquantity] == 1", "[type] == sword && [flag] != ethereal && [quality] >= normal && [quality] <= superior && [wsm] <= 10 && [strreq] <= 150 # [sockets] == 3 # [maxquantity] == 1", ]; - NTIP.arrayLooping(Malice); + NTIP.buildList(Malice); Config.Runewords.push([Runeword.Malice, "crystalsword"]); Config.Runewords.push([Runeword.Malice, "broadsword"]); diff --git a/libs/SoloPlay/BuildFiles/Runewords/MercDoom.js b/libs/SoloPlay/BuildFiles/Runewords/MercDoom.js index 03c6ed73..56855093 100644 --- a/libs/SoloPlay/BuildFiles/Runewords/MercDoom.js +++ b/libs/SoloPlay/BuildFiles/Runewords/MercDoom.js @@ -6,7 +6,7 @@ "[name] == UmRune", "[name] == ChamRune", ]; - NTIP.arrayLooping(Doom); + NTIP.buildList(Doom); // Have Cham, Lo, and Ohm Rune before looking for normal base if (me.getItem(sdk.items.runes.Cham) && me.getItem(sdk.items.runes.Lo) && me.getItem(sdk.items.runes.Ohm)) { diff --git a/libs/SoloPlay/BuildFiles/Runewords/MercFortitude.js b/libs/SoloPlay/BuildFiles/Runewords/MercFortitude.js index 22a51270..86241770 100644 --- a/libs/SoloPlay/BuildFiles/Runewords/MercFortitude.js +++ b/libs/SoloPlay/BuildFiles/Runewords/MercFortitude.js @@ -11,15 +11,15 @@ if (["Zealer", "Smiter", "Frenzy", "Whirlwind", "Uberconc", "Wolf"].includes(SetUp.finalBuild)) { // Make Grief first, if using it for final build if (me.checkItem({name: sdk.locale.items.Grief, itemtype: sdk.items.type.Sword}).have) { - NTIP.arrayLooping(fort); + NTIP.buildList(fort); } } else if (["Blova", "Lightning"].includes(SetUp.currentBuild)) { // Make Chains of Honor first for Blova/Lightning, or already have ber so Lo isn't needed for cubing if (me.checkItem({name: sdk.locale.items.ChainsofHonor}).have || me.getItem(sdk.items.runes.Ber)) { - NTIP.arrayLooping(fort); + NTIP.buildList(fort); } } else { - NTIP.arrayLooping(fort); + NTIP.buildList(fort); } Config.Recipes.push([Recipe.Socket.Armor, "hellforgeplate"]); diff --git a/libs/SoloPlay/BuildFiles/Runewords/MercInfinity.js b/libs/SoloPlay/BuildFiles/Runewords/MercInfinity.js index e52a4c66..affa340d 100644 --- a/libs/SoloPlay/BuildFiles/Runewords/MercInfinity.js +++ b/libs/SoloPlay/BuildFiles/Runewords/MercInfinity.js @@ -6,7 +6,7 @@ "([name] == thresher || [name] == crypticaxe || [name] == greatpoleaxe || [name] == giantthresher) && [flag] == ethereal && [quality] == normal # [Sockets] == 0 # [maxquantity] == 1", "([name] == thresher || [name] == crypticaxe || [name] == greatpoleaxe || [name] == giantthresher) && [quality] >= normal && [quality] <= Superior # [Sockets] == 4 # [maxquantity] == 1", ]; - NTIP.arrayLooping(Inf); + NTIP.buildList(Inf); // Cube to Ber rune if (me.findItems(sdk.items.runes.Ber).length < 2) { diff --git a/libs/SoloPlay/BuildFiles/Runewords/MercInsight.js b/libs/SoloPlay/BuildFiles/Runewords/MercInsight.js index 5bf51f37..ccc81ae0 100644 --- a/libs/SoloPlay/BuildFiles/Runewords/MercInsight.js +++ b/libs/SoloPlay/BuildFiles/Runewords/MercInsight.js @@ -6,7 +6,7 @@ "!me.hell && " + lowTier + " && [quality] >= normal && [quality] <= superior && [flag] != runeword # [sockets] == 4 # [maxquantity] == 1", (highTier + " && [quality] >= normal && [quality] <= superior && [flag] != runeword # [sockets] == 4 # [maxquantity] == 1"), ]; - NTIP.arrayLooping(Insight); + NTIP.buildList(Insight); if (!me.hell && Item.getMercEquipped(sdk.body.RightArm).prefixnum !== sdk.locale.items.Insight && !Check.haveBase("polearm", 4)) { NTIP.addLine("[name] == voulge && [flag] != ethereal && [quality] == normal && [level] >= 26 && [level] <= 40 # [sockets] == 0 # [maxquantity] == 1"); diff --git a/libs/SoloPlay/BuildFiles/Runewords/MercPride.js b/libs/SoloPlay/BuildFiles/Runewords/MercPride.js index 3da997d7..0292006d 100644 --- a/libs/SoloPlay/BuildFiles/Runewords/MercPride.js +++ b/libs/SoloPlay/BuildFiles/Runewords/MercPride.js @@ -7,7 +7,7 @@ "([name] == thresher || [name] == crypticaxe || [name] == greatpoleaxe || [name] == giantthresher) && [flag] == ethereal && [quality] == normal # [Sockets] == 0 # [maxquantity] == 1", "([name] == thresher || [name] == crypticaxe || [name] == greatpoleaxe || [name] == giantthresher) && [quality] >= normal && [quality] <= Superior # [Sockets] == 4 # [maxquantity] == 1", ]; - NTIP.arrayLooping(Pride); + NTIP.buildList(Pride); // Cube to Sur/Lo rune if (!me.getItem(sdk.items.runes.Sur) || !me.getItem(sdk.items.runes.Lo)) { diff --git a/libs/SoloPlay/BuildFiles/Runewords/MercTreachery.js b/libs/SoloPlay/BuildFiles/Runewords/MercTreachery.js index baa468a6..9e223c7a 100644 --- a/libs/SoloPlay/BuildFiles/Runewords/MercTreachery.js +++ b/libs/SoloPlay/BuildFiles/Runewords/MercTreachery.js @@ -4,7 +4,7 @@ "[name] == ThulRune # # [maxquantity] == 1", "[name] == LemRune # # [maxquantity] == 1", ]; - NTIP.arrayLooping(treach); + NTIP.buildList(treach); const MercTreachery = [ "([name] == breastplate || [name] == mageplate || [name] == hellforgeplate || [name] == krakenshell || [name] == archonplate || [name] == balrogskin || [name] == boneweave || [name] == greathauberk || [name] == loricatedmail || [name] == diamondmail || [name] == wirefleece || [name] == scarabhusk || [name] == wyrmhide || [name] == duskshroud) && [quality] >= normal && [quality] <= superior # [sockets] == 3 # [maxquantity] == 1", @@ -13,7 +13,7 @@ // Have Shael and Lem before looking for base if (me.getItem(sdk.items.runes.Shael) && me.getItem(sdk.items.runes.Lem)) { - NTIP.arrayLooping(MercTreachery); + NTIP.buildList(MercTreachery); } Config.Recipes.push([Recipe.Socket.Armor, "hellforgeplate"]); diff --git a/libs/SoloPlay/BuildFiles/Runewords/Myth.js b/libs/SoloPlay/BuildFiles/Runewords/Myth.js index 6bfbbbfd..26335ef6 100644 --- a/libs/SoloPlay/BuildFiles/Runewords/Myth.js +++ b/libs/SoloPlay/BuildFiles/Runewords/Myth.js @@ -4,7 +4,7 @@ "[name] == AmnRune # # [maxquantity] == 1", "[name] == NefRune # # [maxquantity] == 1", ]; - NTIP.arrayLooping(Myth); + NTIP.buildList(Myth); // Cube to Hel rune if (!me.getItem(sdk.items.runes.Hel)) { diff --git a/libs/SoloPlay/BuildFiles/Runewords/PDiamondShield.js b/libs/SoloPlay/BuildFiles/Runewords/PDiamondShield.js index aa6fc36e..ad43c331 100644 --- a/libs/SoloPlay/BuildFiles/Runewords/PDiamondShield.js +++ b/libs/SoloPlay/BuildFiles/Runewords/PDiamondShield.js @@ -3,7 +3,7 @@ "[name] == perfectdiamond # # [maxquantity] == 3", "[name] == towershield && [quality] >= normal && [quality] <= superior # [sockets] == 3 # [maxquantity] == 1", ]; - NTIP.arrayLooping(PDiamondShield); + NTIP.buildList(PDiamondShield); // cube to Pdiamonds if (Item.getQuantityOwned(me.getItem(sdk.items.gems.Perfect.Diamond)) < 3) { diff --git a/libs/SoloPlay/BuildFiles/Runewords/PhoenixShield.js b/libs/SoloPlay/BuildFiles/Runewords/PhoenixShield.js index 2c91c9ce..1c5b0758 100644 --- a/libs/SoloPlay/BuildFiles/Runewords/PhoenixShield.js +++ b/libs/SoloPlay/BuildFiles/Runewords/PhoenixShield.js @@ -5,7 +5,7 @@ "[name] == JahRune", "[name] == monarch && [flag] != ethereal && [quality] >= normal && [quality] <= superior # [sockets] == 4 # [maxquantity] == 1" ]; - NTIP.arrayLooping(PhoenixRunes); + NTIP.buildList(PhoenixRunes); // Cube to vex and Keep cubing to Jah rune if (!me.getItem(sdk.items.runes.Jah)) { if (Item.getQuantityOwned(me.getItem(sdk.items.runes.Vex)) < 2) { diff --git a/libs/SoloPlay/BuildFiles/Runewords/Rhyme.js b/libs/SoloPlay/BuildFiles/Runewords/Rhyme.js index 10e6340f..8a1088f9 100644 --- a/libs/SoloPlay/BuildFiles/Runewords/Rhyme.js +++ b/libs/SoloPlay/BuildFiles/Runewords/Rhyme.js @@ -5,7 +5,7 @@ "[type] == voodooheads && [quality] >= normal && [quality] <= superior # [sockets] == 2 # [maxquantity] == 1", "[type] == voodooheads && [quality] == normal # ([necromancerskills]+[poisonandboneskilltab]+[skillbonespear]+[skillbonespirit]+[skillteeth]+[skillbonewall]+[skillboneprison]+[skillamplifydamage]) >= 1 && [sockets] == 0 # [maxquantity] == 1", ]; - NTIP.arrayLooping(rhyme); + NTIP.buildList(rhyme); Config.Runewords.push([Runeword.Rhyme, "preservedhead"]); Config.Runewords.push([Runeword.Rhyme, "zombiehead"]); diff --git a/libs/SoloPlay/BuildFiles/Runewords/Sanctuary.js b/libs/SoloPlay/BuildFiles/Runewords/Sanctuary.js index 6b3534ca..ba825ec7 100644 --- a/libs/SoloPlay/BuildFiles/Runewords/Sanctuary.js +++ b/libs/SoloPlay/BuildFiles/Runewords/Sanctuary.js @@ -4,7 +4,7 @@ "[name] == MalRune", "[name] == hyperion && [flag] != ethereal && [quality] >= normal && [quality] <= superior # [sockets] == 3 # [maxquantity] == 1", ]; - NTIP.arrayLooping(Sanctuary); + NTIP.buildList(Sanctuary); // Cube to Mal rune if (!me.getItem(sdk.items.runes.Mal)) { diff --git a/libs/SoloPlay/BuildFiles/Runewords/Silence.js b/libs/SoloPlay/BuildFiles/Runewords/Silence.js index 585df05f..cf8ead05 100644 --- a/libs/SoloPlay/BuildFiles/Runewords/Silence.js +++ b/libs/SoloPlay/BuildFiles/Runewords/Silence.js @@ -7,7 +7,7 @@ "[name] == IstRune", "[name] == VexRune", ]; - NTIP.arrayLooping(Silence); + NTIP.buildList(Silence); // Have Vex before collecting base if (me.getItem(sdk.items.runes.Vex)) { diff --git a/libs/SoloPlay/BuildFiles/Runewords/Smoke.js b/libs/SoloPlay/BuildFiles/Runewords/Smoke.js index f50fbc24..00353992 100644 --- a/libs/SoloPlay/BuildFiles/Runewords/Smoke.js +++ b/libs/SoloPlay/BuildFiles/Runewords/Smoke.js @@ -9,7 +9,7 @@ "[name] == NefRune # # [maxquantity] == 1", "[name] == LumRune # # [maxquantity] == 1", ]; - NTIP.arrayLooping(smokeRunes); + NTIP.buildList(smokeRunes); } // Have Lum rune before looking for base diff --git a/libs/SoloPlay/BuildFiles/Runewords/SpiritShield.js b/libs/SoloPlay/BuildFiles/Runewords/SpiritShield.js index 09eceecf..b0af22d8 100644 --- a/libs/SoloPlay/BuildFiles/Runewords/SpiritShield.js +++ b/libs/SoloPlay/BuildFiles/Runewords/SpiritShield.js @@ -5,7 +5,7 @@ "[name] == OrtRune # # [maxquantity] == 1", "[name] == AmnRune # # [maxquantity] == 1", ]; - NTIP.arrayLooping(SpiritRunes); + NTIP.buildList(SpiritRunes); if (me.paladin) { NTIP.addLine("([name] == targe || [name] == rondache || [name] == heraldicshield || [name] == aerinshield || [name] == akarantarge || [name] == akaranrondache || [name] == gildedshield ||[name] == protectorshield || [name] == sacredtarge) && [flag] != ethereal && [quality] >= normal && [quality] <= superior # [fireresist] > 0 && [sockets] == 4"); diff --git a/libs/SoloPlay/BuildFiles/Runewords/SpiritSword.js b/libs/SoloPlay/BuildFiles/Runewords/SpiritSword.js index 7af8dcf4..973db829 100644 --- a/libs/SoloPlay/BuildFiles/Runewords/SpiritSword.js +++ b/libs/SoloPlay/BuildFiles/Runewords/SpiritSword.js @@ -6,7 +6,7 @@ "[name] == OrtRune # # [maxquantity] == 1", "[name] == AmnRune # # [maxquantity] == 1", ]; - NTIP.arrayLooping(SpiritSword); + NTIP.buildList(SpiritSword); // Cube to Amn Rune if (!me.getItem(sdk.items.runes.Amn)) { diff --git a/libs/SoloPlay/BuildFiles/Runewords/Stealth.js b/libs/SoloPlay/BuildFiles/Runewords/Stealth.js index d5ecc3a0..cfb3bc23 100644 --- a/libs/SoloPlay/BuildFiles/Runewords/Stealth.js +++ b/libs/SoloPlay/BuildFiles/Runewords/Stealth.js @@ -4,14 +4,14 @@ "[name] == TalRune # # [maxquantity] == 1", "[name] == EthRune # # [maxquantity] == 1", ]; - NTIP.arrayLooping(stealthRunes); + NTIP.buildList(stealthRunes); } const stealthArmor = [ "!me.hell && ([name] == studdedleather || [name] == lightplate) && [flag] != ethereal && [quality] >= normal && [quality] <= superior # [sockets] == 2 # [maxquantity] == 1", "([name] == ghostarmor || [name] == serpentskinarmor || [name] == mageplate) && [flag] != ethereal && [quality] >= normal && [quality] <= superior # [sockets] == 2 # [maxquantity] == 1", ]; - NTIP.arrayLooping(stealthArmor); + NTIP.buildList(stealthArmor); if (Item.getEquipped(sdk.body.Armor).tier < 200) { NTIP.addLine("[name] == breastplate && [flag] != ethereal && [quality] >= normal && [quality] <= superior # [sockets] == 2 # [maxquantity] == 1"); diff --git a/libs/SoloPlay/BuildFiles/Runewords/Steel.js b/libs/SoloPlay/BuildFiles/Runewords/Steel.js index ba13920c..bc8558f3 100644 --- a/libs/SoloPlay/BuildFiles/Runewords/Steel.js +++ b/libs/SoloPlay/BuildFiles/Runewords/Steel.js @@ -3,7 +3,7 @@ "[name] == TirRune # # [maxquantity] == 2", "[name] == ElRune # # [maxquantity] == 2", ]; - NTIP.arrayLooping(Steel); + NTIP.buildList(Steel); if (Item.getEquipped(sdk.body.LeftArm).tier < 500 && Item.getEquipped(sdk.body.LeftArm).tier > 395) { NTIP.addLine("[type] == sword && [flag] != ethereal && [quality] >= normal && [quality] <= superior && [wsm] <= 10 && [strreq] <= 150 && [class] == elite # [sockets] == 2 # [maxquantity] == 1"); diff --git a/libs/SoloPlay/BuildFiles/Runewords/Treachery.js b/libs/SoloPlay/BuildFiles/Runewords/Treachery.js index 8450258e..805550d1 100644 --- a/libs/SoloPlay/BuildFiles/Runewords/Treachery.js +++ b/libs/SoloPlay/BuildFiles/Runewords/Treachery.js @@ -4,7 +4,7 @@ "[name] == ThulRune # # [maxquantity] == 1", "[name] == LemRune # # [maxquantity] == 1", ]; - NTIP.arrayLooping(treach); + NTIP.buildList(treach); // Cube to Lem rune if (!me.getItem(sdk.items.runes.Lem)) { diff --git a/libs/SoloPlay/BuildFiles/Runewords/VoiceOfReason.js b/libs/SoloPlay/BuildFiles/Runewords/VoiceOfReason.js index f5bc95db..54669c49 100644 --- a/libs/SoloPlay/BuildFiles/Runewords/VoiceOfReason.js +++ b/libs/SoloPlay/BuildFiles/Runewords/VoiceOfReason.js @@ -5,7 +5,7 @@ "[name] == ElRune # # [maxquantity] == 1", "[name] == EldRune # # [maxquantity] == 1", ]; - NTIP.arrayLooping(VoiceofReason); + NTIP.buildList(VoiceofReason); if (me.barbarian) { // Have Lem and Ko runes before looking for normal base diff --git a/libs/SoloPlay/BuildFiles/Runewords/White.js b/libs/SoloPlay/BuildFiles/Runewords/White.js index 9fa1813a..04c6dd89 100644 --- a/libs/SoloPlay/BuildFiles/Runewords/White.js +++ b/libs/SoloPlay/BuildFiles/Runewords/White.js @@ -5,7 +5,7 @@ "[type] == wand && ([name] != wand && [name] != yewwand && [name] != burntwand) && [quality] >= normal && [quality] <= superior # [sockets] == 2 # [maxquantity] == 1", "[type] == wand && ([name] != wand && [name] != yewwand && [name] != burntwand) && [quality] == normal # ([necromancerskills]+[poisonandboneskilltab]+[skillbonespear]+[skillbonespirit]+[skillteeth]+[skillbonewall]+[skillboneprison]+[skillamplifydamage]) >= 1 && [sockets] == 0 # [maxquantity] == 1", ]; - NTIP.arrayLooping(white); + NTIP.buildList(white); // Cube to Io rune if (!me.getItem(sdk.items.runes.Io)) { diff --git a/libs/SoloPlay/Config/Amazon.js b/libs/SoloPlay/Config/Amazon.js index 3237f980..96105e7a 100644 --- a/libs/SoloPlay/Config/Amazon.js +++ b/libs/SoloPlay/Config/Amazon.js @@ -97,8 +97,8 @@ "[name] == grandcharm && [quality] == magic # # [invoquantity] == 2 && [charmtier] == charmscore(item)", ]; - NTIP.arrayLooping(levelingTiers); - me.expansion && NTIP.arrayLooping(expansionTiers); + NTIP.buildList(levelingTiers); + me.expansion && NTIP.buildList(expansionTiers); if (["Witchyzon", "Wfzon", "Faithbowzon"].indexOf(SetUp.currentBuild) === -1) { NTIP.addLine("[type] == shield && ([quality] >= magic || [flag] == runeword) && [flag] != ethereal # [itemchargedskill] >= 0 # [tier] == tierscore(item)"); @@ -119,7 +119,7 @@ /* Gear */ let finalGear = Check.finalBuild().finalGear; - !!finalGear && NTIP.arrayLooping(finalGear); + !!finalGear && NTIP.buildList(finalGear); Config.imbueables = [ {name: sdk.items.MaidenJavelin, condition: () => me.normal && me.expansion}, @@ -132,7 +132,7 @@ let imbueArr = SetUp.imbueItems(); - !me.smith && NTIP.arrayLooping(imbueArr); + !me.smith && NTIP.buildList(imbueArr); if (Item.getEquipped(sdk.body.RightArm).tier < 100000) { Config.GambleItems.push("Javelin"); diff --git a/libs/SoloPlay/Config/Assassin.js b/libs/SoloPlay/Config/Assassin.js index 91e7d4c8..2a4bc65f 100644 --- a/libs/SoloPlay/Config/Assassin.js +++ b/libs/SoloPlay/Config/Assassin.js @@ -95,7 +95,7 @@ "[name] == grandcharm && [quality] == magic # # [invoquantity] == 2 && [charmtier] == charmscore(item)", ]; - NTIP.arrayLooping(levelingTiers); + NTIP.buildList(levelingTiers); /* Attack configuration. */ Config.AttackSkill = [0, 0, 0, 0, 0, 0, 0]; @@ -124,7 +124,7 @@ /* Gear */ let finalGear = Check.finalBuild().finalGear; - !!finalGear && NTIP.arrayLooping(finalGear); + !!finalGear && NTIP.buildList(finalGear); Config.imbueables = [ {name: sdk.items.Claws, condition: () => (me.normal)}, @@ -137,7 +137,7 @@ let imbueArr = SetUp.imbueItems(); - !me.smith && NTIP.arrayLooping(imbueArr); + !me.smith && NTIP.buildList(imbueArr); const { basicSocketables, addSocketableObj } = require("../Utils/General"); diff --git a/libs/SoloPlay/Config/Barbarian.js b/libs/SoloPlay/Config/Barbarian.js index 2e95b90e..3dda8ca0 100644 --- a/libs/SoloPlay/Config/Barbarian.js +++ b/libs/SoloPlay/Config/Barbarian.js @@ -95,8 +95,8 @@ "[name] == grandcharm && [quality] == magic # # [invoquantity] == 2 && [charmtier] == charmscore(item)", ]; - NTIP.arrayLooping(levelingTiers); - me.expansion && NTIP.arrayLooping(expansionTiers); + NTIP.buildList(levelingTiers); + me.expansion && NTIP.buildList(expansionTiers); /* Attack configuration. */ Config.AttackSkill = [-1, 0, 0, 0, 0]; @@ -112,7 +112,7 @@ /* Gear */ let finalGear = Check.finalBuild().finalGear; - !!finalGear && NTIP.arrayLooping(finalGear); + !!finalGear && NTIP.buildList(finalGear); Config.imbueables = [ {name: sdk.items.AvengerGuard, condition: () => (me.normal && me.expansion)}, @@ -125,7 +125,7 @@ let imbueArr = SetUp.imbueItems(); - !me.smith && NTIP.arrayLooping(imbueArr); + !me.smith && NTIP.buildList(imbueArr); switch (me.gametype) { case sdk.game.gametype.Classic: diff --git a/libs/SoloPlay/Config/Druid.js b/libs/SoloPlay/Config/Druid.js index 733a3ee4..5ddeaa02 100644 --- a/libs/SoloPlay/Config/Druid.js +++ b/libs/SoloPlay/Config/Druid.js @@ -97,7 +97,7 @@ "[name] == grandcharm && [quality] == magic # # [invoquantity] == 2 && [charmtier] == charmscore(item)", ]; - NTIP.arrayLooping(levelingTiers); + NTIP.buildList(levelingTiers); let switchTiers = (["Wolf", "Plaguewolf"].includes(SetUp.currentBuild) ? [ @@ -109,7 +109,7 @@ "[name] == beardedaxe && [quality] == unique # [itemchargedskill] == 87 # [secondarytier] == 50000", // Spellsteel Decrepify charged axe ]); - NTIP.arrayLooping(switchTiers); + NTIP.buildList(switchTiers); /* Attack configuration. */ Config.AttackSkill = [0, 0, 0, 0, 0, 0, 0]; @@ -131,7 +131,7 @@ /* Gear */ let finalGear = Check.finalBuild().finalGear; - !!finalGear && NTIP.arrayLooping(finalGear); + !!finalGear && NTIP.buildList(finalGear); Config.imbueables = [ {name: sdk.items.SpiritMask, condition: () => (me.normal)}, @@ -144,7 +144,7 @@ let imbueArr = SetUp.imbueItems(); - !me.smith && NTIP.arrayLooping(imbueArr); + !me.smith && NTIP.buildList(imbueArr); const { basicSocketables, addSocketableObj } = require("../Utils/General"); diff --git a/libs/SoloPlay/Config/Necromancer.js b/libs/SoloPlay/Config/Necromancer.js index 6b2a44a4..6f48e2bc 100644 --- a/libs/SoloPlay/Config/Necromancer.js +++ b/libs/SoloPlay/Config/Necromancer.js @@ -100,8 +100,8 @@ "[name] == grandcharm && [quality] == magic # # [invoquantity] == 2 && [charmtier] == charmscore(item)", ]; - NTIP.arrayLooping(levelingTiers); - me.expansion && NTIP.arrayLooping(expansionTiers); + NTIP.buildList(levelingTiers); + me.expansion && NTIP.buildList(expansionTiers); /* Attack configuration. */ Skill.usePvpRange = true; @@ -131,7 +131,7 @@ /* Gear */ let finalGear = Check.finalBuild().finalGear; - !!finalGear && NTIP.arrayLooping(finalGear); + !!finalGear && NTIP.buildList(finalGear); Config.imbueables = [ {name: sdk.items.DemonHead, condition: () => (me.normal && me.expansion)}, @@ -144,7 +144,7 @@ let imbueArr = SetUp.imbueItems(); - !me.smith && NTIP.arrayLooping(imbueArr); + !me.smith && NTIP.buildList(imbueArr); switch (me.gametype) { case sdk.game.gametype.Classic: diff --git a/libs/SoloPlay/Config/Paladin.js b/libs/SoloPlay/Config/Paladin.js index 5e234268..22195709 100644 --- a/libs/SoloPlay/Config/Paladin.js +++ b/libs/SoloPlay/Config/Paladin.js @@ -112,8 +112,8 @@ "[name] == grandcharm && [quality] == magic # # [invoquantity] == 2 && [charmtier] == charmscore(item)", ]; - NTIP.arrayLooping(levelingTiers); - me.expansion && NTIP.arrayLooping(expansionTiers); + NTIP.buildList(levelingTiers); + me.expansion && NTIP.buildList(expansionTiers); /* Attack configuration. */ Config.AttackSkill = [0, 0, 0, 0, 0, 0, 0]; @@ -131,7 +131,7 @@ /* Gear */ let finalGear = Check.finalBuild().finalGear; - !!finalGear && NTIP.arrayLooping(finalGear); + !!finalGear && NTIP.buildList(finalGear); // Maybe add auric shield? Config.imbueables = [ @@ -145,7 +145,7 @@ let imbueArr = SetUp.imbueItems(); - !me.smith && NTIP.arrayLooping(imbueArr); + !me.smith && NTIP.buildList(imbueArr); switch (me.gametype) { case sdk.game.gametype.Classic: diff --git a/libs/SoloPlay/Config/Sorceress.js b/libs/SoloPlay/Config/Sorceress.js index c58df758..e5a689f6 100644 --- a/libs/SoloPlay/Config/Sorceress.js +++ b/libs/SoloPlay/Config/Sorceress.js @@ -111,9 +111,6 @@ NTIP.addLine("([type] == orb || [type] == wand || [type] == sword || [type] == knife) && ([quality] >= magic || [flag] == runeword) && [flag] == ethereal && [2handed] == 0 # [itemchargedskill] >= 0 # [tier] == tierscore(item)"); } - NTIP.arrayLooping(levelingTiers); - me.expansion && NTIP.arrayLooping(expansionTiers); - /* Attack configuration. */ Skill.usePvpRange = true; Config.AttackSkill = [0, 0, 0, 0, 0, 0, 0]; @@ -136,8 +133,12 @@ Config.CastStatic = me.classic ? 15 : [25, 33, 50][me.diff]; /* Gear */ + NTIP.buildList(levelingTiers); + me.expansion && NTIP.buildList(expansionTiers); let finalGear = Check.finalBuild().finalGear; - !!finalGear && NTIP.arrayLooping(finalGear); + NTIP.buildList(finalGear); + NTIP.buildFinalGear(finalGear); + Config.imbueables = [ {name: sdk.items.JaredsStone, condition: () => (me.normal && me.expansion)}, @@ -150,7 +151,7 @@ let imbueArr = SetUp.imbueItems(); - !me.smith && NTIP.arrayLooping(imbueArr); + !me.smith && NTIP.buildList(imbueArr); switch (me.gametype) { case sdk.game.gametype.Classic: diff --git a/libs/SoloPlay/Functions/Globals.js b/libs/SoloPlay/Functions/Globals.js index 4080b012..6e0ae2e6 100644 --- a/libs/SoloPlay/Functions/Globals.js +++ b/libs/SoloPlay/Functions/Globals.js @@ -386,7 +386,7 @@ const SetUp = { // A2 Guard "me.mercid === 338 && ([type] == polearm || [type] == spear) && ([quality] >= magic || [flag] == runeword) # [itemchargedskill] >= 0 # [Merctier] == mercscore(item)", ]; - NTIP.arrayLooping(expansionExtras); + NTIP.buildList(expansionExtras); this.bowQuiver(); } diff --git a/libs/SoloPlay/Functions/NTIPOverrides.js b/libs/SoloPlay/Functions/NTIPOverrides.js index c71a79cd..90a7c2b5 100644 --- a/libs/SoloPlay/Functions/NTIPOverrides.js +++ b/libs/SoloPlay/Functions/NTIPOverrides.js @@ -23,6 +23,10 @@ NTIP.RuntimeStringArray = []; NTIP.SoloCheckList = []; NTIP.SoloCheckListNoTier = []; NTIP.SoloStringArray = []; +NTIP.FinalGear = { + list: [], + strArray: [], +}; NTIP.generateTierFunc = function (tierType) { return function (item) { @@ -73,14 +77,28 @@ NTIP.generateTierFunc = function (tierType) { }; }; -/**@function - * @param item */ +/** + * @function + * @param item + */ NTIP.GetTier = NTIP.generateTierFunc("Tier"); -/**@function - * @param item */ +/** + * @function + * @param item + */ NTIP.GetMercTier = NTIP.generateTierFunc("Merctier"); + +/** + * @function + * @param item + */ NTIP.GetCharmTier = NTIP.generateTierFunc("Charmtier"); + +/** + * @function + * @param item + */ NTIP.GetSecondaryTier = NTIP.generateTierFunc("Secondarytier"); NTIP.addLine = function (itemString) { @@ -106,6 +124,29 @@ NTIP.addLine = function (itemString) { return true; }; +NTIP.buildFinalGear = function (arr) { + for (let i = 0; i < arr.length; i++) { + const info = { + line: NTIP.FinalGear.list.length + 1, + file: "Kolbot-SoloPlay", + string: arr[i] + }; + + const line = NTIP.ParseLineInt(arr[i], info); + + if (line) { + if (!arr[i].toLowerCase().includes("tier")) { + continue; + } + + NTIP.FinalGear.list.push(line); + NTIP.FinalGear.strArray.push(info); + } + } + + return true; +}; + // currently just using for quiver's but if that changes need to figure out way to seperate out sections // so things can be deleted without affecting the entire list NTIP.addToRuntime = function (itemString) { @@ -130,7 +171,7 @@ NTIP.resetRuntimeList = () => { NTIP.RuntimeStringArray.length = 0; }; -NTIP.arrayLooping = function (...arraystoloop) { +NTIP.buildList = function (...arraystoloop) { for (let arr of arraystoloop) { if (Array.isArray(arr)) { for (let i = 0; i < arr.length; i++) { @@ -263,6 +304,12 @@ NTIP.CheckItem = function (item, entryList, verbose = false) { let result = 0; const identified = item.getFlag(sdk.items.flags.Identified); + /** + * + * @param {any[]} list + * @param {string[]} stringArr + * @returns + */ const iterateList = (list, stringArr) => { let i, num; @@ -298,7 +345,7 @@ NTIP.CheckItem = function (item, entryList, verbose = false) { } else if (!identified && result === 0 || !identified && result === 1) { result = -1; - if (verbose) { + if (verbose && stringArr[i] !== undefined) { rval.line = stringArr[i].file + " #" + stringArr[i].line; } } @@ -350,7 +397,7 @@ NTIP.CheckItem = function (item, entryList, verbose = false) { } else if (!identified && result === 0 || !identified && result === 1) { result = -1; - if (verbose) { + if (verbose && stringArr[i] !== undefined) { rval.line = stringArr[i].file + " #" + stringArr[i].line; } } @@ -371,17 +418,21 @@ NTIP.CheckItem = function (item, entryList, verbose = false) { } if (verbose) { - switch (result) { - case -1: - break; - case 1: - rval.line = stringArr[i].file + " #" + stringArr[i].line; + try { + switch (result) { + case -1: + break; + case 1: + rval.line = stringArr[i].file + " #" + stringArr[i].line; - break; - default: - rval.line = null; + break; + default: + rval.line = null; - break; + break; + } + } catch (e) { + rval.line = null; } rval.result = result; @@ -396,7 +447,11 @@ NTIP.CheckItem = function (item, entryList, verbose = false) { return result; }; - const listOfLists = [[NTIP.SoloCheckList, NTIP.SoloStringArray], [NTIP_CheckList, stringArray], [NTIP.RuntimeCheckList, NTIP.RuntimeStringArray]]; + const listOfLists = [ + [NTIP.SoloCheckList, NTIP.SoloStringArray], + [NTIP_CheckList, stringArray], + [NTIP.RuntimeCheckList, NTIP.RuntimeStringArray] + ]; if (Array.isArray(entryList)) return iterateList(entryList, stringArray); for (let i = 0; i < listOfLists.length; i++) { diff --git a/libs/SoloPlay/Threads/TownChicken.js b/libs/SoloPlay/Threads/TownChicken.js index 977c4dda..1383ead3 100644 --- a/libs/SoloPlay/Threads/TownChicken.js +++ b/libs/SoloPlay/Threads/TownChicken.js @@ -399,7 +399,7 @@ function main() { const useHowl = Skill.canUse(sdk.skills.Howl); const useTerror = Skill.canUse(sdk.skills.Terror); - Config.DebugMode = true; + Config.DebugMode.Stack = true; while (true) { if (!me.inTown && (townCheck || fastTown diff --git a/libs/SoloPlay/Utils/Init.js b/libs/SoloPlay/Utils/Init.js index ea7af0b3..8e96e76d 100644 --- a/libs/SoloPlay/Utils/Init.js +++ b/libs/SoloPlay/Utils/Init.js @@ -10,7 +10,7 @@ if (getScript(true).name.toLowerCase() === "libs\\soloplay\\soloplay.js") { myPrint("start setup"); const { nipItems, impossibleClassicBuilds, impossibleNonLadderBuilds } = require("../Utils/General"); - NTIP.arrayLooping(nipItems.Quest, nipItems.General); + NTIP.buildList(nipItems.Quest, nipItems.General); try { if (impossibleClassicBuilds.includes(SetUp.finalBuild) && me.classic) { diff --git a/libs/SoloPlay/Workers/TownChickenWorker.js b/libs/SoloPlay/Workers/TownChickenWorker.js index 40b13af5..37cd8994 100644 --- a/libs/SoloPlay/Workers/TownChickenWorker.js +++ b/libs/SoloPlay/Workers/TownChickenWorker.js @@ -326,7 +326,7 @@ const useHowl = Skill.canUse(sdk.skills.Howl); const useTerror = Skill.canUse(sdk.skills.Terror); - Config.DebugMode = true; + Config.DebugMode.Stack = true; let waitTick = getTickCount(); let potTick = getTickCount(); From be311ca0bb3317cb9e7661f5cf5e9dff0a3ccd1f Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Sun, 12 Feb 2023 12:10:25 -0500 Subject: [PATCH 028/263] Small classattack updates - Barb - Remove recursive findItem call, add some more handling in case we fail to hork corpse - Sorc - Handle situation where we can't find a valid skill to use --- .../ClassAttackOverrides/AmazonAttacks.js | 13 +++- .../ClassAttackOverrides/BarbarianAttacks.js | 60 +++++++++++++------ .../ClassAttackOverrides/SorceressAttacks.js | 1 + 3 files changed, 56 insertions(+), 18 deletions(-) diff --git a/libs/SoloPlay/Functions/ClassAttackOverrides/AmazonAttacks.js b/libs/SoloPlay/Functions/ClassAttackOverrides/AmazonAttacks.js index 8ac9008f..a5674997 100644 --- a/libs/SoloPlay/Functions/ClassAttackOverrides/AmazonAttacks.js +++ b/libs/SoloPlay/Functions/ClassAttackOverrides/AmazonAttacks.js @@ -15,6 +15,12 @@ includeIfNotIncluded("core/Attacks/Amazon.js"); ClassAttack.decoyTick = getTickCount(); +/** + * @param {Monster} unit + * @param {boolean} preattack + * @param {boolean} once + * @returns {AttackResult} + */ ClassAttack.doAttack = function (unit, preattack, once) { // unit became invalidated if (!unit || !unit.attackable) return Attack.Result.SUCCESS; @@ -335,7 +341,12 @@ ClassAttack.afterAttack = function () { this.lightFuryTick = 0; }; -// Returns: 0 - fail, 1 - success, 2 - no valid attack skills +/** + * @param {Monster} unit + * @param {number} timedSkill + * @param {number} untimedSkill + * @returns {AttackResult} + */ ClassAttack.doCast = function (unit, timedSkill, untimedSkill) { // No valid skills can be found if (timedSkill < 0 && untimedSkill < 0) return Attack.Result.CANTATTACK; diff --git a/libs/SoloPlay/Functions/ClassAttackOverrides/BarbarianAttacks.js b/libs/SoloPlay/Functions/ClassAttackOverrides/BarbarianAttacks.js index b543741e..336c49c5 100644 --- a/libs/SoloPlay/Functions/ClassAttackOverrides/BarbarianAttacks.js +++ b/libs/SoloPlay/Functions/ClassAttackOverrides/BarbarianAttacks.js @@ -104,7 +104,12 @@ includeIfNotIncluded("core/Attacks/Barbarian.js"); } }; - ClassAttack.doAttack = function (unit = undefined, preattack = false) { + /** + * @param {Monster} unit + * @param {boolean} preattack + * @returns {AttackResult} + */ + ClassAttack.doAttack = function (unit, preattack = false) { if (unit === undefined || !unit || unit.dead) return true; let gid = unit.gid; @@ -326,8 +331,9 @@ includeIfNotIncluded("core/Attacks/Barbarian.js"); if (!Config.FindItem || !Skill.canUse(sdk.skills.FindItem)) return false; Config.FindItemSwitch = (me.expansion && Precast.getBetterSlot(sdk.skills.FindItem)); - let retry = false, pick = false, corpseList = []; - let orgX = me.x, orgY = me.y; + let pick = false; + let corpseList = []; + const { x: orgX, y: orgY } = me; MainLoop: for (let i = 0; i < 3; i++) { @@ -345,46 +351,66 @@ includeIfNotIncluded("core/Attacks/Barbarian.js"); pick = true; while (corpseList.length > 0) { - if (this.checkCloseMonsters(5)) { + if (this.checkCloseMonsters(10)) { Config.FindItemSwitch && me.switchWeapons(Attack.getPrimarySlot()); Attack.clearPos(me.x, me.y, 10, false); - retry = true; - break MainLoop; + continue MainLoop; } corpseList.sort(Sort.units); - corpse = corpseList.shift(); + const check = corpseList.shift(); + let attempted = false; + let invalidated = false; + // get the actual corpse rather than the copied unit + corpse = Game.getMonster(check.classid, sdk.monsters.mode.Dead, check.gid); if (this.checkCorpse(corpse)) { if (corpse.distance > 30 || Coords_1.isBlockedBetween(me, corpse)) { - Pather.moveToUnit(corpse); + Pather.moveNearUnit(corpse, 5); } Config.FindItemSwitch && me.switchWeapons(Attack.getPrimarySlot() ^ 1); CorpseLoop: for (let j = 0; j < 3; j += 1) { - Skill.cast(sdk.skills.FindItem, sdk.skills.hand.Right, corpse); + // sometimes corpse can become invalidated - necro summoned from it or baal wave clearing, ect + // this still doesn't seem to capture baal wave clearing + if (j > 0) { + corpse = Game.getMonster(check.classid, sdk.monsters.mode.Dead, check.gid); + if (!this.checkCorpse(corpse)) { + invalidated = true; + break; + } + } + // see if we can find a new position if we failed the first time - sometimes findItem is bugged + j > 0 && Attack.getIntoPosition(corpse, 5, sdk.collision.BlockWall, Pather.useTeleport(), true); + // only delay if we actually casted the skill + if (Skill.cast(sdk.skills.FindItem, sdk.skills.hand.Right, corpse)) { + let tick = getTickCount(); + attempted = true; - let tick = getTickCount(); + while (getTickCount() - tick < 1000) { + if (corpse.getState(sdk.states.CorpseNoSelect)) { + Config.FastPick ? Pickit.fastPick() : Pickit.pickItems(range); - while (getTickCount() - tick < 1000) { - if (corpse.getState(sdk.states.CorpseNoSelect)) { - Pickit.fastPick(); + break CorpseLoop; + } - break CorpseLoop; + delay(10); } - - delay(10); } } } + + if (attempted && !invalidated && corpse && !corpse.getState(sdk.states.CorpseNoSelect)) { + !me.inArea(sdk.areas.ThroneofDestruction) && D2Bot.printToConsole("Failed to hork " + JSON.stringify(corpse) + " at " + getAreaName(me.area)); + console.debug("Failed to hork " + JSON.stringify(corpse) + " at " + getAreaName(me.area)); + } } } } - if (retry) return this.findItem(me.inArea(sdk.areas.Travincal) ? 60 : 20); Config.FindItemSwitch && me.weaponswitch === 1 && me.switchWeapons(Attack.getPrimarySlot()); pick && Pickit.pickItems(); diff --git a/libs/SoloPlay/Functions/ClassAttackOverrides/SorceressAttacks.js b/libs/SoloPlay/Functions/ClassAttackOverrides/SorceressAttacks.js index 726c9b77..c4478a94 100644 --- a/libs/SoloPlay/Functions/ClassAttackOverrides/SorceressAttacks.js +++ b/libs/SoloPlay/Functions/ClassAttackOverrides/SorceressAttacks.js @@ -280,6 +280,7 @@ includeIfNotIncluded("core/Attacks/Sorceress.js"); .filter(k => typeof data[k] === "object" && data[k].have && me.mp > data[k].mana && (!data[k].timed || !me.skillDelay) && (data[k].skill !== sdk.skills.StaticField || !recheckSkill)) .sort((a, b) => data[b].dmg - data[a].dmg); + if (!sortedList.length) return Attack.Result.FAILED; let skillCheck = data[sortedList[0]].skill === sdk.skills.StaticField && unit.distance > data.static.range && me.inDanger(unit, 15) ? sortedList.at(1) : sortedList.at(0); From 289d2d5dba63190a2558426f9ee2916ff3fada9c Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Sun, 12 Feb 2023 12:11:23 -0500 Subject: [PATCH 029/263] Update ItemUtilities.js - First iteration of checking bases against what our merc is currently using --- libs/SoloPlay/Functions/ItemUtilities.js | 37 +++++++++++++++++++++++- 1 file changed, 36 insertions(+), 1 deletion(-) diff --git a/libs/SoloPlay/Functions/ItemUtilities.js b/libs/SoloPlay/Functions/ItemUtilities.js index ceac0519..ab41bd71 100644 --- a/libs/SoloPlay/Functions/ItemUtilities.js +++ b/libs/SoloPlay/Functions/ItemUtilities.js @@ -26,6 +26,11 @@ includeIfNotIncluded("core/Item.js"); return generalScore; }; + /** + * @param {ItemUnit} base + * @param {boolean} verbose + * @returns {boolean} + */ Item.betterBaseThanWearing = function (base = undefined, verbose = Developer.debugging.baseCheck) { if (!base || !base.isBaseType) return false; @@ -67,7 +72,37 @@ includeIfNotIncluded("core/Item.js"); }; // @todo - betterThanMercUsing check for now just keep merc items - if ([sdk.items.type.Polearm, sdk.items.type.Spear].includes(base.itemType) || ([sdk.items.type.Armor].includes(base.itemType) && base.ethereal)) return true; + if ([sdk.items.type.Polearm, sdk.items.type.Spear].includes(base.itemType) || ([sdk.items.type.Armor].includes(base.itemType) && base.ethereal)) { + let merc = me.getMercEx(); + if (merc) { + bodyLoc = Item.getBodyLocMerc(base); + let eqItem = merc.getItemsEx().filter(i => i.isEquipped && bodyLoc.includes(i.bodylocation)); + if (!eqItem || !eqItem.runeword || NTIP.GetMercTier(eqItem) >= NTIP.MAX_TIER) return true; + name = getLocaleString(eqItem.prefixnum); + // todo logic checking before this to ensure we aren't keeping extra merc stuff + if (base.sockets === 0) return true; + switch (equippedItem.prefixnum) { + case sdk.locale.items.Insight: + [itemsTotalDmg, baseDmg] = [getRealDmg(equippedItem, 260), getDmg(base)]; + if (baseDmg !== itemsTotalDmg && dmgCheck(itemsTotalDmg, baseDmg)) return true; + + break; + case sdk.locale.items.Infinity: + [itemsTotalDmg, baseDmg] = [getRealDmg(equippedItem, 325), getDmg(base)]; + if (baseDmg !== itemsTotalDmg && dmgCheck(itemsTotalDmg, baseDmg)) return true; + + break; + case sdk.locale.items.Treachery: + [itemsDefense, baseDefense] = [getRealDef(equippedItem), getDef(base)]; + if (baseDefense !== itemsDefense && defCheck(itemsDefense, baseDefense)) return true; + + break; + default: + return true; + } + return false; + } + } // Can't use so its worse then what we already have if ((Check.finalBuild().maxStr < base.strreq || Check.finalBuild().maxDex < base.dexreq)) { console.log("ÿc9BetterThanWearingCheckÿc0 :: " + base.name + " has to high stat requirments strReq: " + base.strreq + " dexReq " + base.dexreq); From 9174d2b5f5bf729e63c0a79fcbf4f846958363ff Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Sun, 12 Feb 2023 12:12:32 -0500 Subject: [PATCH 030/263] Update Quest.js - Add cube into `Quest.preReqs` --- libs/SoloPlay/Functions/Quest.js | 25 +++++++++++++++++-------- 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/libs/SoloPlay/Functions/Quest.js b/libs/SoloPlay/Functions/Quest.js index 023f7d98..aa311176 100644 --- a/libs/SoloPlay/Functions/Quest.js +++ b/libs/SoloPlay/Functions/Quest.js @@ -8,17 +8,26 @@ const Quest = { preReqs: function () { - // horadric staff - if (Pather.accessToAct(2) && !me.staff && !me.horadricstaff) { - if (!me.amulet) { - for (let getAmmy = 0; !me.amulet && getAmmy < 5; getAmmy++) { - Loader.runScript("amulet"); + if (Pather.accessToAct(2)) { + // cube + if (!me.cube) { + for (let getCube = 0; !me.cube && getCube < 5; getCube++) { + Loader.runScript("cube"); } } - if (!me.shaft) { - for (let getStaff = 0; !me.shaft && getStaff < 5; getStaff++) { - Loader.runScript("staff"); + // horadric staff + if (!me.staff && !me.horadricstaff) { + if (!me.amulet) { + for (let getAmmy = 0; !me.amulet && getAmmy < 5; getAmmy++) { + Loader.runScript("amulet"); + } + } + + if (!me.shaft) { + for (let getStaff = 0; !me.shaft && getStaff < 5; getStaff++) { + Loader.runScript("staff"); + } } } } From 6bf5b2701742da730c59411a3aa25dace19d383d Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Sun, 12 Feb 2023 13:03:27 -0500 Subject: [PATCH 031/263] Add Attack.Result.NOOP - Better pathing, Attack.clear was returning true even if we didn't clear anything which resulted occasionally in an endless loop in areas like maggot lair. Now if nothing is cleared it returns NOOP which tells pather not to continue to next iteration --- libs/SoloPlay/Functions/AttackOverrides.js | 54 +++++++++++++++++----- libs/SoloPlay/Functions/PatherOverrides.js | 14 ++++-- 2 files changed, 52 insertions(+), 16 deletions(-) diff --git a/libs/SoloPlay/Functions/AttackOverrides.js b/libs/SoloPlay/Functions/AttackOverrides.js index 3a2eb42a..a1c9450f 100644 --- a/libs/SoloPlay/Functions/AttackOverrides.js +++ b/libs/SoloPlay/Functions/AttackOverrides.js @@ -9,6 +9,8 @@ includeIfNotIncluded("core/Attack.js"); Attack.stopClear = false; +Attack.Result.NOOP = 4; + Attack.init = function () { const CLASSNAME = sdk.player.class.nameOf(me.classid); if (Config.Wereform) { @@ -567,12 +569,20 @@ Attack.clearLevelUntilLevel = function (charlvl = undefined, spectype = 0) { return true; }; -// Clear monsters in a section based on range and spectype or clear monsters around a boss monster -// probably going to change to passing an object -// accepts object for bossId or classid, gid sometimes works depends if the gid is > 999 -// should stop clearing after boss is killed if we are using bossid -// @todo if we are skipping a certain monster because of enchant or aura we should skip any monsters within the area of that scary monster -// @todo maybe include refresh call every x amount of attacks in case the we've changed position and the monster we are targetting isn't actually the right one anymore +/** + * @description Clear monsters in a section based on range and spectype or clear monsters around a boss monster + * @param {number} range - area radius to clear around + * @param {number} spectype - type of monsters to clear + * @param {number | Monster} bossId - gid, classid, or Monster unit to clear + * @param {Function} sortfunc - how to sort the monsters we are clearing, defaults to Attack.sortMonsters + * @param {boolean} pickit - Are we allowed to pick items when we are done + * @returns {AttackResult} - (0) on failure, (1) on success, (4) on no operation performed + * @todo + * - change to passing an object + * - if we are skipping a certain monster because of enchant or aura we should skip any monsters within the area of that scary monster + * - maybe include refresh call every x amount of attacks in case the we've changed position and the monster we are targetting isn't actually the right one anymore + * - should we stop clearing after boss is killed if we are using bossid? + */ Attack.clear = function (range = 25, spectype = 0, bossId = false, sortfunc = undefined, pickit = true) { while (!me.gameReady) { delay(40); @@ -588,6 +598,7 @@ Attack.clear = function (range = 25, spectype = 0, bossId = false, sortfunc = un let tick = getTickCount(); let [killedBoss, logged] = [false, false]; let [retry, attackCount] = [0, 0]; + let clearResult = Attack.Result.NOOP; if (bossId) { boss = Misc.poll(function () { @@ -670,14 +681,14 @@ Attack.clear = function (range = 25, spectype = 0, bossId = false, sortfunc = un } } - (i === gidAttack.length) && gidAttack.push({gid: target.gid, attacks: 0, name: target.name}); + (i === gidAttack.length) && gidAttack.push({ gid: target.gid, attacks: 0, name: target.name }); gidAttack[i].attacks += 1; attackCount += 1; let isSpecial = target.isSpecial; let secAttack = me.barbarian ? (isSpecial ? 2 : 4) : 5; if (Config.AttackSkill[secAttack] > -1 && (!Attack.checkResist(target, Config.AttackSkill[isSpecial ? 1 : 3]) - || (me.paladin && Config.AttackSkill[isSpecial ? 1 : 3] === sdk.skills.BlessedHammer && !ClassAttack.getHammerPosition(target)))) { + || (me.paladin && Config.AttackSkill[isSpecial ? 1 : 3] === sdk.skills.BlessedHammer && !ClassAttack.getHammerPosition(target)))) { skillCheck = Config.AttackSkill[secAttack]; } else { skillCheck = Config.AttackSkill[isSpecial ? 1 : 3]; @@ -710,13 +721,16 @@ Attack.clear = function (range = 25, spectype = 0, bossId = false, sortfunc = un // we cleared this monster so subtract amount of attacks from current attack count in order to prevent pre-maturely ending clear if (target.dead) { + clearResult = Attack.Result.SUCCESS; if (boss && boss.gid === target.gid) { killedBoss = true; console.log("ÿc7Cleared ÿc0:: " + (!!target.name ? target.name : bossId) + "ÿc0 - ÿc7Duration: ÿc0" + Time.format(getTickCount() - tick)); } attackCount -= gidAttack[i].attacks; } - (target.dead || Config.FastPick || attackCount % 5 === 0) && Config.FastPick ? Pickit.fastPick() : Pickit.essessntialsPick(false, true); + if (target.dead || Config.FastPick || attackCount % 5 === 0) { + Config.FastPick ? Pickit.fastPick() : Pickit.essessntialsPick(false, true); + } } else { if (Coords_1.isBlockedBetween(me, target)) { let collCheck = Coords_1.getCollisionBetweenCoords(me.x, me.y, target.x, target.y); @@ -745,15 +759,16 @@ Attack.clear = function (range = 25, spectype = 0, bossId = false, sortfunc = un if (boss && !killedBoss) { // check if boss corpse is around + // sometimes this fails, need better check for it if (boss.dead) { console.log("ÿc7Cleared ÿc0:: " + (!!boss.name ? boss.name : bossId) + "ÿc0 - ÿc7Duration: ÿc0" + Time.format(getTickCount() - tick)); } else { console.log("ÿc7Clear ÿc0:: ÿc1Failed to clear ÿc0:: " + (!!boss.name ? boss.name : bossId)); - return false; + return Attack.Result.FAILED; } } - return true; + return clearResult; }; // Take a array of coords - path and clear @@ -1288,6 +1303,15 @@ Attack.deploy = function (unit, distance = 10, spread = 5, range = 9) { return typeof index === "number" ? Pather.moveTo(grid[index].x, grid[index].y, 0) : false; }; +/** + * Attempt to find non blocked position to attack from + * @param {Monster} unit + * @param {number} distance + * @param {number} coll + * @param {boolean} walk + * @param {boolean} force + * @returns {boolean} + */ Attack.getIntoPosition = function (unit = false, distance = 0, coll = 0, walk = false, force = false) { if (!unit || !unit.x || !unit.y) return false; Developer.debugging.pathing && console.time("getIntoPosition"); @@ -1295,6 +1319,12 @@ Attack.getIntoPosition = function (unit = false, distance = 0, coll = 0, walk = walk === true && (walk = 1); if (distance < 4 && (!unit.hasOwnProperty("mode") || !unit.dead)) { + /** + * if we are surrounded by monsters it can be near impossible to get into position + * what would be good is if we are surrounded either pick an AoE skill and cast or + * just attack whatever monster is the nearest to us, this would also be a good place + * for necro's use of terror and barbs use of howl/leap/leapAttack/whirlwind + */ // we are actually able to walk to where we want to go, hopefully prevent wall hugging if (walk && (unit.distance < 8 || !CollMap.checkColl(me, unit, sdk.collision.WallOrRanged | sdk.collision.Objects | sdk.collision.IsOnFloor))) { Pather.walkTo(unit.x, unit.y, 3); @@ -1318,7 +1348,7 @@ Attack.getIntoPosition = function (unit = false, distance = 0, coll = 0, walk = let caster = (force && !me.inTown); - //let t = getTickCount(); + // let t = getTickCount(); for (let n = 0; n < 3; n += 1) { const nearMobs = getUnits(sdk.unittype.Monster).filter(m => m.getStat(sdk.stats.Alignment) !== 2); diff --git a/libs/SoloPlay/Functions/PatherOverrides.js b/libs/SoloPlay/Functions/PatherOverrides.js index de0aacd2..cf3cce0e 100644 --- a/libs/SoloPlay/Functions/PatherOverrides.js +++ b/libs/SoloPlay/Functions/PatherOverrides.js @@ -530,10 +530,16 @@ Pather.move = function (target, givenSettings = {}) { } } else { if (!me.inTown) { - if (!useTeleport && settings.allowClearing - && me.checkForMobs({range: (annoyingArea ? 5 : 10), coll: sdk.collision.BlockWalk}) && Attack.clear((annoyingArea ? 5 : 10))) { - console.debug("Cleared Node"); - continue; + if (!useTeleport && settings.allowClearing) { + let tempRange = (annoyingArea ? 5 : 10); + // allowed to clear so lets see if any mobs are around us + if (me.checkForMobs({ range: tempRange, coll: sdk.collision.BlockWalk })) { + // there are at least some, but lets only continue to next iteration if we actually killed something + if (Attack.clear(tempRange, null, null, null, settings.allowPicking) === Attack.Result.SUCCESS) { + console.debug("Cleared Node"); + continue; + } + } } if (!useTeleport && (Pather.openDoors(node.x, node.y) || Pather.kickBarrels(node.x, node.y))) { continue; From 328361a9f41b2f449b7463a8b690c0ea70a9bf1f Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Sun, 12 Feb 2023 13:49:44 -0500 Subject: [PATCH 032/263] Update OOGOverrides.js - Add back handler for cd key in use --- libs/SoloPlay/Tools/OOGOverrides.js | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/libs/SoloPlay/Tools/OOGOverrides.js b/libs/SoloPlay/Tools/OOGOverrides.js index 3514397f..6d876632 100644 --- a/libs/SoloPlay/Tools/OOGOverrides.js +++ b/libs/SoloPlay/Tools/OOGOverrides.js @@ -874,6 +874,21 @@ const locations = {}; Starter.profileInfo.password = ""; D2Bot.setProfile(Starter.profileInfo.account, Starter.profileInfo.password); + break; + case getLocaleString(sdk.locale.text.CdKeyInUseBy): + string += (" " + Controls.LoginCdKeyInUseBy.getText()); + D2Bot.printToConsole(Starter.gameInfo.mpq + " " + string, sdk.colors.D2Bot.Gold); + D2Bot.CDKeyInUse(); + + if (Starter.gameInfo.switchKeys) { + cdkeyError = true; + } else { + Controls.UnableToConnectOk.click(); + ControlAction.timeoutDelay("LoD key in use", Starter.Config.CDKeyInUseDelay * 6e4); + + return; + } + break; case getLocaleString(5202): // cd key intended for another product case getLocaleString(10915): // lod key intended for another product From 78f98e4897258f9df837b194eb696ce8c0b0cf10 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Sun, 12 Feb 2023 17:36:47 -0500 Subject: [PATCH 033/263] Update TownChicken.js - broke togglePause up into pause an resume, both with extra checks to ensure that soloplay main thread hasn't crashed --- libs/SoloPlay/Threads/TownChicken.js | 60 ++++++++++++++++------------ 1 file changed, 35 insertions(+), 25 deletions(-) diff --git a/libs/SoloPlay/Threads/TownChicken.js b/libs/SoloPlay/Threads/TownChicken.js index 1383ead3..356c0d49 100644 --- a/libs/SoloPlay/Threads/TownChicken.js +++ b/libs/SoloPlay/Threads/TownChicken.js @@ -1,6 +1,6 @@ /** * @filename TownChicken.js -* @author kolton, theBGuy (modified for Kolbot-SoloPlay) +* @author kolton, theBGuy * @desc modified TownChicken for use with Kolbot-SoloPlay * */ @@ -292,32 +292,43 @@ function main() { return me.area === preArea; }; - this.togglePause = function () { - let scripts = ["libs/SoloPlay/SoloPlay.js", "threads/antihostile.js"]; + const pause = function () { + let script = getScript("libs/SoloPlay/SoloPlay.js"); - for (let i = 0; i < scripts.length; i++) { - let script = getScript(scripts[i]); + if (!script) { + !!getScript("libs/SoloPlay/threads/toolsthread.js") ? scriptBroadcast("quit") : quit(); + } - if (script) { - if (script.running) { - scripts[i] === "libs/SoloPlay/SoloPlay.js" && console.log("ÿc8TownChicken:: ÿc1Pausing " + scripts[i]); + if (script && script.running) { + script.pause(); + console.log("ÿc8TownChicken:: ÿc1Pausing SoloPlay"); - script.pause(); - } else { - if (scripts[i] === "libs/SoloPlay/SoloPlay.js") { - // don't resume if dclone walked - if (!SoloEvents.cloneWalked) { - console.log("ÿc8TownChicken :: ÿc2Resuming threads"); - script.resume(); - } - } else { - script.resume(); - } + return true; + } + + return false; + }; + + const resume = function () { + let script = getScript("libs/SoloPlay/SoloPlay.js"); + + // resume only if clonekilla isn't running + if (!SoloEvents.cloneWalked) { + if (script && !script.running) { + console.log("ÿc8TownChicken :: ÿc2Resuming threads"); + script.resume(); + + return true; + } else { + if (!script) { + // soloplay has crashed? We shouldn't be running then. Is toolsthread still up? + // if yes try to still quit normally, otherwise quit from here + !!getScript("libs/SoloPlay/threads/toolsthread.js") ? scriptBroadcast("quit") : quit(); } } } - return true; + return false; }; this.scriptEvent = function (msg) { @@ -389,7 +400,7 @@ function main() { addEventListener("scriptmsg", this.scriptEvent); let tGuard = getScript("libs/SoloPlay/Modules/TownGuard.js"); !!tGuard && tGuard.running && tGuard.stop(); - Developer.debugging.showStack.profiles.some(profile => profile.toLowerCase() === "all" || profile.toLowerCase() === me.profile.toLowerCase()) && require("../Modules/TownGuard"); + Developer.debugging.showStack.profiles.some(prof => String.isEqual(prof, me.profile) || String.isEqual(prof, "all")) && require("../Modules/TownGuard"); // START // test for getUnit bug @@ -411,7 +422,7 @@ function main() { continue; } - this.togglePause(); + pause(); while (!me.gameReady) { if (me.dead) { @@ -447,11 +458,10 @@ function main() { return false; } finally { - Packet.flash(me.gid, 100); - console.log("ÿc8TownChicken :: Took: " + Time.format(getTickCount() - t4) + " to visit town"); - this.togglePause(); + resume(); Messaging.sendToScript("libs/SoloPlay/Threads/EventThread.js", "townchickenOff"); [Attack.stopClear, SoloEvents.townChicken.running, townCheck, fastTown] = [false, false, false, false]; + console.log("ÿc8TownChicken :: Took: " + Time.format(getTickCount() - t4) + " to visit town"); } } From c82474be240cacd4e6f2985fb5d0ad87229a9db8 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Sun, 12 Feb 2023 19:57:49 -0500 Subject: [PATCH 034/263] Update Globals.js - Fix typo --- libs/SoloPlay/Functions/Globals.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libs/SoloPlay/Functions/Globals.js b/libs/SoloPlay/Functions/Globals.js index 6e0ae2e6..387d7981 100644 --- a/libs/SoloPlay/Functions/Globals.js +++ b/libs/SoloPlay/Functions/Globals.js @@ -229,7 +229,7 @@ const SetUp = { // sort all inventory potions from the right sdk.items.RejuvenationPotion, sdk.items.FullRejuvenationPotion, sdk.items.MinorHealingPotion, sdk.items.LightHealingPotion, sdk.items.HealingPotion, sdk.items.GreaterHealingPotion, sdk.items.SuperHealingPotion, - sdk.items.MinorManaPotion, sdk.items.LightManaPotion, sdk.items.ManaPotion, sdk.items.GreaterManaPotion, sdk.items.SuperHealingPotion + sdk.items.MinorManaPotion, sdk.items.LightManaPotion, sdk.items.ManaPotion, sdk.items.GreaterManaPotion, sdk.items.SuperManaPotion ], PrioritySorting: true, ItemsSortedFromLeftPriority: [/*605, 604, 603, 519, 518*/], // (NOTE: the earlier in the index, the further to the Left) From 76cac2c0c7005f05870b8cde5c50c50cefef9f85 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Thu, 16 Feb 2023 20:27:02 -0500 Subject: [PATCH 035/263] Update Sorceress.js - Check if we have any maxed magefist in storage as well as being equipped before adding the recipe for cubing --- libs/SoloPlay/Config/Sorceress.js | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/libs/SoloPlay/Config/Sorceress.js b/libs/SoloPlay/Config/Sorceress.js index e5a689f6..acd0333b 100644 --- a/libs/SoloPlay/Config/Sorceress.js +++ b/libs/SoloPlay/Config/Sorceress.js @@ -170,9 +170,15 @@ Config.Recipes.push([Recipe.Caster.Amulet]); } - if (Item.getEquipped(sdk.body.Gloves).tier < 110000) { - Config.Recipes.push([Recipe.Unique.Armor.ToExceptional, "Light Gauntlets", Roll.NonEth]); - ["Blova", "Lightning"].includes(SetUp.finalBuild) && Config.Recipes.push([Recipe.Unique.Armor.ToElite, "Battle Gauntlets", Roll.NonEth, "magefist"]); + { + let maxedMage = me.getItemsEx() + .filter(i => i.itemType === sdk.items.type.Gloves) + .find(i => NTIP.GetTier(i) >= 110000); + + if (!maxedMage) { + Config.Recipes.push([Recipe.Unique.Armor.ToExceptional, "Light Gauntlets", Roll.NonEth]); + ["Blova", "Lightning"].includes(SetUp.finalBuild) && Config.Recipes.push([Recipe.Unique.Armor.ToElite, "Battle Gauntlets", Roll.NonEth, "magefist"]); + } } Config.socketables = Config.socketables.concat(basicSocketables.caster, basicSocketables.all); From ceb9e7195fc549fcb8e560d582779ebcf61342a6 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Sat, 18 Feb 2023 12:56:02 -0500 Subject: [PATCH 036/263] Param change for tierscore + Town cleanup - Add parameter to pass in an initial value for tierscore, this way it simplifies the final build lines and there isn't a discrepancy between the tierscore ane NTIP.GetTier value of an item - Broke up Town and created NPCActions.js, town was kinda messy with what was there. - Removed Town.sortInventory and added me.sortInventory - Removed Town.checkColumns and added Storage.Belt.checkColumns - Removed Town.clearBelt and added me.clearBelt - Added invo sort if we can't fit an item while picking and there are no monsters around, todo: need to have a fast sort and full sort option --- .../amazon/amazon.FaithbowzonBuild.js | 14 +- .../amazon/amazon.FrostmaidenBuild.js | 6 +- .../BuildFiles/amazon/amazon.JavazonBuild.js | 12 +- .../BuildFiles/amazon/amazon.WfzonBuild.js | 12 +- .../amazon/amazon.WitchyzonBuild.js | 10 +- .../assassin/assassin.TrapsinBuild.js | 14 +- .../assassin/assassin.WhirlsinBuild.js | 8 +- .../barbarian/barbarian.FrenzyBuild.js | 8 +- .../barbarian/barbarian.ImmortalwhirlBuild.js | 2 +- .../barbarian/barbarian.SingerBuild.js | 8 +- .../barbarian/barbarian.UberconcBuild.js | 8 +- .../barbarian/barbarian.WhirlwindBuild.js | 10 +- .../BuildFiles/druid/druid.ElementalBuild.js | 18 +- .../BuildFiles/druid/druid.FirewolfBuild.js | 12 +- .../BuildFiles/druid/druid.PlaguewolfBuild.js | 8 +- .../BuildFiles/druid/druid.StormbearBuild.js | 14 +- .../BuildFiles/druid/druid.WindBuild.js | 16 +- .../BuildFiles/druid/druid.WolfBuild.js | 16 +- .../necromancer/necromancer.BoneBuild.js | 28 +- .../necromancer/necromancer.PoisonBuild.js | 28 +- .../necromancer/necromancer.SummonBuild.js | 28 +- .../paladin/paladin.AuradinBuild.js | 4 +- .../paladin/paladin.ClassicauradinBuild.js | 12 +- .../paladin/paladin.HammerdinBuild.js | 22 +- .../paladin/paladin.HammershockBuild.js | 18 +- .../paladin/paladin.SancdreamerBuild.js | 4 +- .../BuildFiles/paladin/paladin.SmiterBuild.js | 10 +- .../paladin/paladin.TorchadinBuild.js | 8 +- .../BuildFiles/paladin/paladin.ZealerBuild.js | 10 +- .../sorceress/sorceress.BlizzballerBuild.js | 20 +- .../sorceress/sorceress.BlovaBuild.js | 20 +- .../sorceress/sorceress.ColdBuild.js | 20 +- .../sorceress/sorceress.EsorbBuild.js | 32 +- .../sorceress/sorceress.LightningBuild.js | 24 +- .../sorceress/sorceress.MeteorbBuild.js | 32 +- libs/SoloPlay/Functions/CubingOverrides.js | 2 +- libs/SoloPlay/Functions/DynamicTiers.js | 8 +- libs/SoloPlay/Functions/ItemOverrides.js | 30 +- libs/SoloPlay/Functions/Me.js | 54 +- libs/SoloPlay/Functions/Mercenary.js | 2 +- libs/SoloPlay/Functions/MiscOverrides.js | 2 +- libs/SoloPlay/Functions/NPCAction.js | 721 +++++++++++++++ libs/SoloPlay/Functions/PickitOverrides.js | 17 +- libs/SoloPlay/Functions/Quest.js | 4 +- libs/SoloPlay/Functions/StorageOverrides.js | 23 +- libs/SoloPlay/Functions/TownOverrides.js | 872 +++--------------- libs/SoloPlay/Scripts/ancients.js | 2 +- libs/SoloPlay/Scripts/cows.js | 4 +- libs/SoloPlay/Scripts/den.js | 2 +- libs/SoloPlay/Scripts/orgtorch.js | 2 +- libs/SoloPlay/Threads/ToolsThread.js | 2 +- libs/SoloPlay/index.d.ts | 26 + 52 files changed, 1247 insertions(+), 1042 deletions(-) create mode 100644 libs/SoloPlay/Functions/NPCAction.js diff --git a/libs/SoloPlay/BuildFiles/amazon/amazon.FaithbowzonBuild.js b/libs/SoloPlay/BuildFiles/amazon/amazon.FaithbowzonBuild.js index 586fb61f..46b532f1 100644 --- a/libs/SoloPlay/BuildFiles/amazon/amazon.FaithbowzonBuild.js +++ b/libs/SoloPlay/BuildFiles/amazon/amazon.FaithbowzonBuild.js @@ -34,19 +34,19 @@ const finalBuild = { ], autoEquipTiers: [ // autoequip final gear // Final Weapon - Faith - "([type] == amazonbow || [type] == bow) && [flag] == runeword # [fanaticismaura] >= 12 && [itemallskills] >= 1 # [tier] == 100000 + tierscore(item)", + "([type] == amazonbow || [type] == bow) && [flag] == runeword # [fanaticismaura] >= 12 && [itemallskills] >= 1 # [tier] == tierscore(item, 100000)", // Weapon - WitchWild String up'd - "[name] == diamondbow && [quality] == unique # [fireresist] == 40 # [tier] == 90000 + tierscore(item)", + "[name] == diamondbow && [quality] == unique # [fireresist] == 40 # [tier] == tierscore(item, 90000)", // Helmet - Vampz Gaze - "[name] == grimhelm && [quality] == unique && [flag] != ethereal # [manaleech] >= 6 && [lifeleech] >= 6 && [damageresist] >= 15 # [tier] == 100000 + tierscore(item)", + "[name] == grimhelm && [quality] == unique && [flag] != ethereal # [manaleech] >= 6 && [lifeleech] >= 6 && [damageresist] >= 15 # [tier] == tierscore(item, 100000)", // Boots - War Traveler - "[name] == battleboots && [quality] == unique && [flag] != ethereal # [itemmagicbonus] >= 35 # [tier] == 100000 + tierscore(item)", + "[name] == battleboots && [quality] == unique && [flag] != ethereal # [itemmagicbonus] >= 35 # [tier] == tierscore(item, 100000)", // Belt - Nosferatu's Coil - "[name] == vampirefangbelt && [quality] == unique && [flag] != ethereal # [lifeleech] >= 5 # [tier] == 100000 + tierscore(item)", + "[name] == vampirefangbelt && [quality] == unique && [flag] != ethereal # [lifeleech] >= 5 # [tier] == tierscore(item, 100000)", // Armor - CoH "[type] == armor && [flag] == runeword && [flag] != ethereal # [fireresist] == 65 && [hpregen] == 7 # [tier] == 100000", // Gloves - Lava Gout - "[name] == battlegauntlets && [quality] == unique && [flag] != ethereal # [ias] == 20 && [enhanceddefense] >= 150 # [tier] == 3000 + tierscore(item)", + "[name] == battlegauntlets && [quality] == unique && [flag] != ethereal # [ias] == 20 && [enhanceddefense] >= 150 # [tier] == tierscore(item, 3000)", // Final Amulet - Atma's Scarab "[type] == amulet && [quality] == unique # [poisonresist] == 75 # [tier] == 110000", // Amulet - Highlords @@ -62,7 +62,7 @@ const finalBuild = { // Switch Temporary Weapon - Life Tap charged wand "[type] == wand && [quality] >= normal # [itemchargedskill] == 82 # [secondarytier] == 75000 + chargeditemscore(item, 82)", // Switch Shield - Any 1+ all skill - "[type] == shield # [itemallskills] >= 1 # [secondarytier] == 100000 + tierscore(item)", + "[type] == shield # [itemallskills] >= 1 # [secondarytier] == tierscore(item, 100000)", // Merc Armor - Fortitude "[type] == armor && [flag] == runeword # [enhanceddefense] >= 200 && [enhanceddamage] >= 300 # [merctier] == 100000", // Merc Final Helmet - Eth Andy's diff --git a/libs/SoloPlay/BuildFiles/amazon/amazon.FrostmaidenBuild.js b/libs/SoloPlay/BuildFiles/amazon/amazon.FrostmaidenBuild.js index c9c389f0..817c0990 100644 --- a/libs/SoloPlay/BuildFiles/amazon/amazon.FrostmaidenBuild.js +++ b/libs/SoloPlay/BuildFiles/amazon/amazon.FrostmaidenBuild.js @@ -29,13 +29,13 @@ const finalBuild = { ], autoEquipTiers: [ // autoequip final gear // Weapon - Brand - "[name] == grandmatronbow && [flag] == runeword # [enhanceddamage] >= 260 && [itemknockback] == 1 && [itemdeadlystrike] == 20 # [tier] == 100000 + tierscore(item)", + "[name] == grandmatronbow && [flag] == runeword # [enhanceddamage] >= 260 && [itemknockback] == 1 && [itemdeadlystrike] == 20 # [tier] == tierscore(item, 100000)", // Helmet - Dream "[type] == helm && [flag] == runeword # [holyshockaura] >= 15 # [tier] == 110000", // Boots - War Traveler "[name] == battleboots && [quality] == unique && [flag] != ethereal # [itemmagicbonus] >= 30 # [tier] == 100000", // Belt - Nosferatu's Coil - "[name] == sharkskinbelt && [quality] == unique && [flag] != ethereal # [dexterity] == 15 # [tier] == 100000 + tierscore(item)", + "[name] == sharkskinbelt && [quality] == unique && [flag] != ethereal # [dexterity] == 15 # [tier] == tierscore(item, 100000)", // Armor - Chains of Honor "[type] == armor && [flag] == runeword && [flag] != ethereal # [fireresist] == 65 # [tier] == 100000", // Amulet - Highlords @@ -48,7 +48,7 @@ const finalBuild = { // Switch Temporary Weapon - Life Tap charged wand "[type] == wand && [quality] >= normal # [itemchargedskill] == 82 # [secondarytier] == 75000 + chargeditemscore(item, 82)", // Switch Shield - Any 1+ all skill - "[type] == shield # [itemallskills] >= 1 # [secondarytier] == 100000 + tierscore(item)", + "[type] == shield # [itemallskills] >= 1 # [secondarytier] == tierscore(item, 100000)", // Merc Armor - Fortitude "[type] == armor && [flag] == runeword # [enhanceddefense] >= 200 && [enhanceddamage] >= 300 # [merctier] == 100000", // Merc Final Helmet - Eth Andy's diff --git a/libs/SoloPlay/BuildFiles/amazon/amazon.JavazonBuild.js b/libs/SoloPlay/BuildFiles/amazon/amazon.JavazonBuild.js index cb571924..5ca01bee 100644 --- a/libs/SoloPlay/BuildFiles/amazon/amazon.JavazonBuild.js +++ b/libs/SoloPlay/BuildFiles/amazon/amazon.JavazonBuild.js @@ -55,7 +55,7 @@ const finalBuild = { ], classicTiers: [ // Helm - Tarnhelm - "[name] == skullcap && [quality] == unique # [itemallskills] == 1 && [itemmagicbonus] >= 25 # [tier] == 100000 + tierscore(item)", + "[name] == skullcap && [quality] == unique # [itemallskills] == 1 && [itemmagicbonus] >= 25 # [tier] == tierscore(item, 100000)", // Armor - Twitchthroe "[name] == studdedleather && [quality] == unique # [ias] == 20 && [fhr] == 20 # [tier] == 100000", // Belt - Death's Guard Sash @@ -67,17 +67,17 @@ const finalBuild = { ], expansionTiers: [ // Weapon - Titan's Revenge - "[name] == ceremonialjavelin && [quality] == unique # [itemchargedskill] >= 0 # [tier] == 100000 + tierscore(item)", + "[name] == ceremonialjavelin && [quality] == unique # [itemchargedskill] >= 0 # [tier] == tierscore(item, 100000)", // Helmet - Harlequin's Crest - "[name] == shako && [quality] == unique && [flag] != ethereal # [itemallskills] == 2 # [tier] == 100000 + tierscore(item)", + "[name] == shako && [quality] == unique && [flag] != ethereal # [itemallskills] == 2 # [tier] == tierscore(item, 100000)", // Boots - Sandstorm Treks - "[name] == scarabshellboots && [quality] == unique # [strength]+[vitality] >= 20 # [tier] == 100000 + tierscore(item)", + "[name] == scarabshellboots && [quality] == unique # [strength]+[vitality] >= 20 # [tier] == tierscore(item, 100000)", // Belt - Thundergod's Vigor - "[name] == warbelt && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 160 # [tier] == 110000 + tierscore(item)", + "[name] == warbelt && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 160 # [tier] == tierscore(item, 110000)", // Armor - CoH "[type] == armor && [flag] == runeword && [flag] != ethereal # [fireresist] == 65 && [hpregen] == 7 # [tier] == 110000", // Shield - Spirit - "[type] == shield # [fcr] >= 25 && [maxmana] >= 89 # [tier] == 110000 + tierscore(item)", + "[type] == shield # [fcr] >= 25 && [maxmana] >= 89 # [tier] == tierscore(item, 110000)", // Amulet - Highlords "[type] == amulet && [quality] == unique # [lightresist] == 35 # [tier] == 110000", // Final Rings - Perfect Raven Frost & Wisp diff --git a/libs/SoloPlay/BuildFiles/amazon/amazon.WfzonBuild.js b/libs/SoloPlay/BuildFiles/amazon/amazon.WfzonBuild.js index 92a9220e..500fb97e 100644 --- a/libs/SoloPlay/BuildFiles/amazon/amazon.WfzonBuild.js +++ b/libs/SoloPlay/BuildFiles/amazon/amazon.WfzonBuild.js @@ -31,19 +31,19 @@ const finalBuild = { ], autoEquipTiers: [ // autoequip final gear // Final Weapon - Windforce - "[name] == hydrabow && [quality] == unique # [manaleech] >= 6 # [tier] == 100000 + tierscore(item)", + "[name] == hydrabow && [quality] == unique # [manaleech] >= 6 # [tier] == tierscore(item, 100000)", // Weapon - WitchWild String up'd - "[name] == diamondbow && [quality] == unique # [fireresist] == 40 # [tier] == 50000 + tierscore(item)", + "[name] == diamondbow && [quality] == unique # [fireresist] == 40 # [tier] == tierscore(item, 50000)", // Helmet - Vampz Gaze - "[name] == grimhelm && [quality] == unique && [flag] != ethereal # [manaleech] >= 6 && [lifeleech] >= 6 && [damageresist] >= 15 # [tier] == 100000 + tierscore(item)", + "[name] == grimhelm && [quality] == unique && [flag] != ethereal # [manaleech] >= 6 && [lifeleech] >= 6 && [damageresist] >= 15 # [tier] == tierscore(item, 100000)", // Boots - War Traveler "[name] == battleboots && [quality] == unique && [flag] != ethereal # [itemmagicbonus] >= 30 # [tier] == 100000", // Belt - Nosferatu's Coil - "[name] == vampirefangbelt && [quality] == unique && [flag] != ethereal # [lifeleech] >= 5 # [tier] == 100000 + tierscore(item)", + "[name] == vampirefangbelt && [quality] == unique && [flag] != ethereal # [lifeleech] >= 5 # [tier] == tierscore(item, 100000)", // Armor - CoH "[type] == armor && [flag] == runeword && [flag] != ethereal # [fireresist] == 65 && [hpregen] == 7 # [tier] == 100000", // Gloves - Lava Gout - "[name] == battlegauntlets && [quality] == unique && [flag] != ethereal # [ias] == 20 && [enhanceddefense] >= 150 # [tier] == 3000 + tierscore(item)", + "[name] == battlegauntlets && [quality] == unique && [flag] != ethereal # [ias] == 20 && [enhanceddefense] >= 150 # [tier] == tierscore(item, 3000)", // Final Amulet - Atma's Scarab "[type] == amulet && [quality] == unique # [poisonresist] == 75 # [tier] == 110000", // Amulet - Highlords @@ -60,7 +60,7 @@ const finalBuild = { // Switch Final Shield - Spirit "[name] == monarch && [flag] == runeword # [fcr] >= 25 && [maxmana] >= 89 # [secondarytier] == 110000", // Switch Temporary Shield - Any 1+ all skill - "[type] == shield # [itemallskills] >= 1 # [secondarytier] == 100000 + tierscore(item)", + "[type] == shield # [itemallskills] >= 1 # [secondarytier] == tierscore(item, 100000)", // Merc Armor - Fortitude "[type] == armor && [flag] == runeword # [enhanceddefense] >= 200 && [enhanceddamage] >= 300 # [merctier] == 100000", // Merc Helmet - Eth Andy's diff --git a/libs/SoloPlay/BuildFiles/amazon/amazon.WitchyzonBuild.js b/libs/SoloPlay/BuildFiles/amazon/amazon.WitchyzonBuild.js index 1242746c..4c4e8505 100644 --- a/libs/SoloPlay/BuildFiles/amazon/amazon.WitchyzonBuild.js +++ b/libs/SoloPlay/BuildFiles/amazon/amazon.WitchyzonBuild.js @@ -33,13 +33,13 @@ const finalBuild = { ], autoEquipTiers: [ // autoequip final gear // Weapon - WitchWild String up'd - "[name] == diamondbow && [quality] == unique # [fireresist] == 40 # [tier] == 100000 + tierscore(item)", + "[name] == diamondbow && [quality] == unique # [fireresist] == 40 # [tier] == tierscore(item, 100000)", // Helmet - Vampz Gaze - "[name] == grimhelm && [quality] == unique && [flag] != ethereal # [manaleech] >= 6 && [lifeleech] >= 6 && [damageresist] >= 15 # [tier] == 100000 + tierscore(item)", + "[name] == grimhelm && [quality] == unique && [flag] != ethereal # [manaleech] >= 6 && [lifeleech] >= 6 && [damageresist] >= 15 # [tier] == tierscore(item, 100000)", // Boots - War Traveler - "[name] == battleboots && [quality] == unique && [flag] != ethereal # [itemmagicbonus] >= 35 # [tier] == 100000 + tierscore(item)", + "[name] == battleboots && [quality] == unique && [flag] != ethereal # [itemmagicbonus] >= 35 # [tier] == tierscore(item, 100000)", // Belt - Nosferatu's Coil - "[name] == vampirefangbelt && [quality] == unique && [flag] != ethereal # [lifeleech] >= 5 # [tier] == 100000 + tierscore(item)", + "[name] == vampirefangbelt && [quality] == unique && [flag] != ethereal # [lifeleech] >= 5 # [tier] == tierscore(item, 100000)", // Armor - CoH "[type] == armor && [flag] == runeword && [flag] != ethereal # [fireresist] == 65 && [hpregen] == 7 # [tier] == 100000", // Amulet - Cat's Eye @@ -55,7 +55,7 @@ const finalBuild = { // Switch Temporary Weapon - Life Tap charged wand "[type] == wand && [quality] >= normal # [itemchargedskill] == 82 # [secondarytier] == 75000 + chargeditemscore(item, 82)", // Switch Shield - Any 1+ all skill - "[type] == shield # [itemallskills] >= 1 # [secondarytier] == 100000 + tierscore(item)", + "[type] == shield # [itemallskills] >= 1 # [secondarytier] == tierscore(item, 100000)", // Merc Armor - Fortitude "[type] == armor && [flag] == runeword # [enhanceddefense] >= 200 && [enhanceddamage] >= 300 # [merctier] == 100000", // Merc Final Helmet - Eth Andy's diff --git a/libs/SoloPlay/BuildFiles/assassin/assassin.TrapsinBuild.js b/libs/SoloPlay/BuildFiles/assassin/assassin.TrapsinBuild.js index 90c67b4c..4bd22db0 100644 --- a/libs/SoloPlay/BuildFiles/assassin/assassin.TrapsinBuild.js +++ b/libs/SoloPlay/BuildFiles/assassin/assassin.TrapsinBuild.js @@ -44,19 +44,19 @@ const finalBuild = { // Temporary Weapon - HotO "[type] == mace && [flag] == runeword # [itemallskills] == 3 # [tier] == 100000", // Helmet - Andy's - "[name] == demonhead && [quality] == unique && [flag] != ethereal # [strength] >= 25 && [enhanceddefense] >= 100 # [tier] == 100000 + tierscore(item)", + "[name] == demonhead && [quality] == unique && [flag] != ethereal # [strength] >= 25 && [enhanceddefense] >= 100 # [tier] == tierscore(item, 100000)", // Belt - Arach's - "[name] == spiderwebsash && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 90 # [tier] == 100000 + tierscore(item)", + "[name] == spiderwebsash && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 90 # [tier] == tierscore(item, 100000)", // Boots - Waterwalks - "[name] == sharkskinboots && [quality] == unique && [flag] != ethereal # [maxhp] >= 65 # [tier] == 100000 + tierscore(item)", + "[name] == sharkskinboots && [quality] == unique && [flag] != ethereal # [maxhp] >= 65 # [tier] == tierscore(item, 100000)", // Armor - Enigma "[type] == armor && [flag] != ethereal && [flag] == runeword # [itemallskills] == 2 # [tier] == 100000", // Shield - Spirit - "[name] == monarch && [flag] == runeword # [fcr] >= 25 && [maxmana] >= 89 # [tier] == 110000 + tierscore(item)", + "[name] == monarch && [flag] == runeword # [fcr] >= 25 && [maxmana] >= 89 # [tier] == tierscore(item, 110000)", // Gloves - Lava Gout - "[name] == battlegauntlets && [quality] == unique && [flag] != ethereal # [ias] == 20 # [tier] == 100000 + tierscore(item)", + "[name] == battlegauntlets && [quality] == unique && [flag] != ethereal # [ias] == 20 # [tier] == tierscore(item, 100000)", // Amulet - Maras - "[type] == amulet && [quality] == unique # [strength] == 5 && [coldresist] >= 30 # [tier] == 100000 + tierscore(item)", + "[type] == amulet && [quality] == unique # [strength] == 5 && [coldresist] >= 30 # [tier] == tierscore(item, 100000)", // Final Rings - SoJ & Perfect Raven Frost "[type] == ring && [quality] == unique # [itemmaxmanapercent] == 25 # [tier] == 100000", "[type] == ring && [quality] == unique # [dexterity] == 20 # [tier] == 110000", @@ -67,7 +67,7 @@ const finalBuild = { // Switch Final Shield - Spirit "[name] == monarch && [flag] == runeword # [fcr] >= 25 && [maxmana] >= 89 # [secondarytier] == 110000", // Switch Temporary Shield - Any 1+ all skill - "[type] == shield # [itemallskills] >= 1 # [secondarytier] == 50000 + tierscore(item)", + "[type] == shield # [itemallskills] >= 1 # [secondarytier] == tierscore(item, 50000)", // Merc Armor - Fortitude "[type] == armor && [flag] == runeword # [enhanceddefense] >= 200 && [enhanceddamage] >= 300 # [merctier] == 100000", // Merc Final Helmet - Eth Andy's diff --git a/libs/SoloPlay/BuildFiles/assassin/assassin.WhirlsinBuild.js b/libs/SoloPlay/BuildFiles/assassin/assassin.WhirlsinBuild.js index c674a2ee..c51fdd66 100644 --- a/libs/SoloPlay/BuildFiles/assassin/assassin.WhirlsinBuild.js +++ b/libs/SoloPlay/BuildFiles/assassin/assassin.WhirlsinBuild.js @@ -34,11 +34,11 @@ const finalBuild = { "[type] == assassinclaw && [flag] == runeword # [plusskillwhirlwind] == 1 # [tier] == 100000", "[type] == assassinclaw && [flag] == runeword # [itemallskills] == 2 && [ias] == 40 && [itemdeadlystrike] == 33 # [tier] == 200000", // Helmet - GFace - "[name] == wingedhelm && [quality] == set && [flag] != ethereal # [fhr] >= 30 # [tier] == 100000 + tierscore(item)", + "[name] == wingedhelm && [quality] == set && [flag] != ethereal # [fhr] >= 30 # [tier] == tierscore(item, 100000)", // Belt - Verdungos - "[name] == mithrilcoil && [quality] == unique && [flag] != ethereal # [damageresist] == 15 # [tier] == 110000 + tierscore(item)", + "[name] == mithrilcoil && [quality] == unique && [flag] != ethereal # [damageresist] == 15 # [tier] == tierscore(item, 110000)", // Boots - Gore Rider - "[name] == warboots && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 160 # [tier] == 110000 + tierscore(item)", + "[name] == warboots && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 160 # [tier] == tierscore(item, 110000)", // Armor - Fortitude "[type] == armor && [flag] == runeword # [enhanceddefense] >= 200 && [enhanceddamage] >= 300 # [tier] == 100000", // Gloves - Trang-Ouls @@ -56,7 +56,7 @@ const finalBuild = { // Switch Final Shield - Spirit "[name] == monarch && [flag] == runeword # [fcr] >= 25 && [maxmana] >= 89 # [secondarytier] == 110000", // Switch Temporary Shield - Any 1+ all skill - "[type] == shield # [itemallskills] >= 1 # [secondarytier] == 50000 + tierscore(item)", + "[type] == shield # [itemallskills] >= 1 # [secondarytier] == tierscore(item, 50000)", // Merc Armor - Fortitude "[type] == armor && [flag] == runeword # [enhanceddefense] >= 200 && [enhanceddamage] >= 300 # [merctier] == 100000", // Merc Final Helmet - Eth Andy's diff --git a/libs/SoloPlay/BuildFiles/barbarian/barbarian.FrenzyBuild.js b/libs/SoloPlay/BuildFiles/barbarian/barbarian.FrenzyBuild.js index 6b777691..0e62b4f3 100644 --- a/libs/SoloPlay/BuildFiles/barbarian/barbarian.FrenzyBuild.js +++ b/libs/SoloPlay/BuildFiles/barbarian/barbarian.FrenzyBuild.js @@ -35,17 +35,17 @@ const finalBuild = { "[name] == phaseblade && [flag] == runeword # [ias] >= 30 # [tier] == 100000", "[name] == colossusblade && [flag] == runeword # [ias] >= 60 && [enhanceddamage] >= 350 # [tier] == 100000", // Helmet - Arreat's Face - "[name] == slayerguard && [quality] == unique && [flag] != ethereal # [barbarianskills] == 2 # [tier] == 100000 + tierscore(item)", + "[name] == slayerguard && [quality] == unique && [flag] != ethereal # [barbarianskills] == 2 # [tier] == tierscore(item, 100000)", // Belt - Dungo's - "[name] == mithrilcoil && [quality] == unique && [flag] != ethereal # [damageresist] >= 10 && [vitality] >= 30 # [tier] == 100000 + tierscore(item)", + "[name] == mithrilcoil && [quality] == unique && [flag] != ethereal # [damageresist] >= 10 && [vitality] >= 30 # [tier] == tierscore(item, 100000)", // Boots - Gore Rider - "[name] == warboots && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 160 # [tier] == 100000 + tierscore(item)", + "[name] == warboots && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 160 # [tier] == tierscore(item, 100000)", // Armor - Fortitude "[type] == armor && [flag] != ethereal && [flag] == runeword # [enhanceddefense] >= 200 && [enhanceddamage] >= 30 # [tier] == 100000", // Gloves - Laying of Hands "[name] == bramblemitts && [quality] == set && [flag] != ethereal # [ias] == 20 # [tier] == 100000", // Amulet - Atma's - "[type] == amulet && [quality] == unique # [poisonresist] == 75 # [tier] == 100000 + tierscore(item)", + "[type] == amulet && [quality] == unique # [poisonresist] == 75 # [tier] == tierscore(item, 100000)", // Final Rings - Perfect Raven Frost & Bul-Kathos' Wedding Band "[type] == ring && [quality] == unique # [dexterity] == 20 && [tohit] == 250 # [tier] == 110000", "[type] == ring && [quality] == unique # [maxstamina] == 50 && [lifeleech] == 5 # [tier] == 110000", diff --git a/libs/SoloPlay/BuildFiles/barbarian/barbarian.ImmortalwhirlBuild.js b/libs/SoloPlay/BuildFiles/barbarian/barbarian.ImmortalwhirlBuild.js index 65331d9a..88945c89 100644 --- a/libs/SoloPlay/BuildFiles/barbarian/barbarian.ImmortalwhirlBuild.js +++ b/libs/SoloPlay/BuildFiles/barbarian/barbarian.ImmortalwhirlBuild.js @@ -41,7 +41,7 @@ const finalBuild = { // Gloves - IK Gauntlets "[name] == wargauntlets && [quality] == set && [flag] != ethereal # [strength] >= 20 && [dexterity] >= 20 # [tier] == 110000", // Amulet - Metalgrid - "[type] == amulet && [quality] == unique # [defense] >= 300 # [tier] == 110000 + tierscore(item)", + "[type] == amulet && [quality] == unique # [defense] >= 300 # [tier] == tierscore(item, 110000)", // Final Rings - Perfect Raven Frost & Bul-Kathos' Wedding Band "[type] == ring && [quality] == unique # [dexterity] == 20 && [tohit] == 250 # [tier] == 110000", "[type] == ring && [quality] == unique # [maxstamina] == 50 && [lifeleech] == 5 # [tier] == 110000", diff --git a/libs/SoloPlay/BuildFiles/barbarian/barbarian.SingerBuild.js b/libs/SoloPlay/BuildFiles/barbarian/barbarian.SingerBuild.js index 624337fc..392ab699 100644 --- a/libs/SoloPlay/BuildFiles/barbarian/barbarian.SingerBuild.js +++ b/libs/SoloPlay/BuildFiles/barbarian/barbarian.SingerBuild.js @@ -32,17 +32,17 @@ const finalBuild = { // Weapon - HotO x2 dual wield "[type] == mace && [flag] == runeword # [itemallskills] == 3 # [tier] == 100000", // Helmet - Harlequin's Crest - "[name] == shako && [quality] == unique && [flag] != ethereal # [damageresist] == 10 # [tier] == 100000 + tierscore(item)", + "[name] == shako && [quality] == unique && [flag] != ethereal # [damageresist] == 10 # [tier] == tierscore(item, 100000)", // Belt - Arach's - "[name] == spiderwebsash && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 90 # [tier] == 100000 + tierscore(item)", + "[name] == spiderwebsash && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 90 # [tier] == tierscore(item, 100000)", // Boots - Silkweave - "[name] == meshboots && [quality] == unique && [flag] != ethereal # [frw] >= 30 # [tier] == 100000 + tierscore(item)", + "[name] == meshboots && [quality] == unique && [flag] != ethereal # [frw] >= 30 # [tier] == tierscore(item, 100000)", // Armor - Enigma "[type] == armor && [flag] != ethereal && [flag] == runeword # [itemallskills] == 2 # [tier] == 100000", // Gloves - Frostburns "[name] == gauntlets && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 10 # [tier] == 100000", // Amulet - Maras - "[type] == amulet && [quality] == unique # [strength] == 5 && [coldresist] >= 30 # [tier] == 100000 + tierscore(item)", + "[type] == amulet && [quality] == unique # [strength] == 5 && [coldresist] >= 30 # [tier] == tierscore(item, 100000)", // Rings - SoJ "[type] == ring && [quality] == unique # [itemmaxmanapercent] == 25 # [tier] == 100000", // Switch - BO sticks diff --git a/libs/SoloPlay/BuildFiles/barbarian/barbarian.UberconcBuild.js b/libs/SoloPlay/BuildFiles/barbarian/barbarian.UberconcBuild.js index 000430a1..b6609cb1 100644 --- a/libs/SoloPlay/BuildFiles/barbarian/barbarian.UberconcBuild.js +++ b/libs/SoloPlay/BuildFiles/barbarian/barbarian.UberconcBuild.js @@ -34,15 +34,15 @@ const finalBuild = { // Weapon - Grief "[type] == sword && [flag] == runeword # [ias] >= 30 && [itemdeadlystrike] == 20 && [passivepoispierce] >= 20 # [tier] == 100000", // Helmet - Arreat's Face - "[name] == slayerguard && [quality] == unique && [flag] != ethereal # [barbarianskills] == 2 # [tier] == 100000 + tierscore(item)", + "[name] == slayerguard && [quality] == unique && [flag] != ethereal # [barbarianskills] == 2 # [tier] == tierscore(item, 100000)", // Belt - Dungo's - "[name] == mithrilcoil && [quality] == unique && [flag] != ethereal # [damageresist] >= 10 && [vitality] >= 30 # [tier] == 100000 + tierscore(item)", + "[name] == mithrilcoil && [quality] == unique && [flag] != ethereal # [damageresist] >= 10 && [vitality] >= 30 # [tier] == tierscore(item, 100000)", // Boots - Gore Rider's - "[name] == warboots && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 160 # [tier] == 100000 + tierscore(item)", + "[name] == warboots && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 160 # [tier] == tierscore(item, 100000)", // Armor - CoH "[type] == armor && [flag] == runeword && [flag] != ethereal # [fireresist] == 65 && [hpregen] == 7 # [tier] == 100000", // Gloves - Drac's - "[name] == vampirebonegloves && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 100 && [strength] >= 12 && [lifeleech] >= 9 # [tier] == 100000 + tierscore(item)", + "[name] == vampirebonegloves && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 100 && [strength] >= 12 && [lifeleech] >= 9 # [tier] == tierscore(item, 100000)", // Amulet - Highlords "[type] == amulet && [quality] == unique # [lightresist] == 35 # [tier] == 100000", // Final Rings - Perfect Raven Frost & Bul-Kathos' Wedding Band diff --git a/libs/SoloPlay/BuildFiles/barbarian/barbarian.WhirlwindBuild.js b/libs/SoloPlay/BuildFiles/barbarian/barbarian.WhirlwindBuild.js index de6df9ec..504df7d8 100644 --- a/libs/SoloPlay/BuildFiles/barbarian/barbarian.WhirlwindBuild.js +++ b/libs/SoloPlay/BuildFiles/barbarian/barbarian.WhirlwindBuild.js @@ -33,19 +33,19 @@ const finalBuild = { // Weapon - Grief x2 dual wield "[type] == sword && [flag] == runeword # [ias] >= 30 && [itemdeadlystrike] == 20 && [passivepoispierce] >= 20 # [tier] == 100000", // Final Helmet - Upp'ed Arreat's Face - "[name] == guardiancrown && [quality] == unique && [flag] != ethereal # [barbarianskills] == 2 && [fhr] >= 30 # [tier] == 150000 + tierscore(item)", + "[name] == guardiancrown && [quality] == unique && [flag] != ethereal # [barbarianskills] == 2 && [fhr] >= 30 # [tier] == tierscore(item, 150000)", // Helmet - Arreat's Face - "[name] == slayerguard && [quality] == unique && [flag] != ethereal # [barbarianskills] == 2 && [fhr] >= 30 # [tier] == 100000 + tierscore(item)", + "[name] == slayerguard && [quality] == unique && [flag] != ethereal # [barbarianskills] == 2 && [fhr] >= 30 # [tier] == tierscore(item, 100000)", // Belt - Dungo's - "[name] == mithrilcoil && [quality] == unique && [flag] != ethereal # [damageresist] >= 10 && [vitality] >= 30 # [tier] == 100000 + tierscore(item)", + "[name] == mithrilcoil && [quality] == unique && [flag] != ethereal # [damageresist] >= 10 && [vitality] >= 30 # [tier] == tierscore(item, 100000)", // Boots - Gore Rider - "[name] == warboots && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 160 # [tier] == 100000 + tierscore(item)", + "[name] == warboots && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 160 # [tier] == tierscore(item, 100000)", // Armor - Fortitude "[type] == armor && [flag] != ethereal && [flag] == runeword # [enhanceddefense] >= 200 && [enhanceddamage] >= 30 # [tier] == 100000", // Gloves - Laying of Hands "[name] == bramblemitts && [quality] == set && [flag] != ethereal # [ias] == 20 # [tier] == 100000", // Amulet - Atma's - "[type] == amulet && [quality] == unique # [poisonresist] == 75 # [tier] == 100000 + tierscore(item)", + "[type] == amulet && [quality] == unique # [poisonresist] == 75 # [tier] == tierscore(item, 100000)", // Final Rings - Perfect Raven Frost & Bul-Kathos' Wedding Band "[type] == ring && [quality] == unique # [dexterity] == 20 && [tohit] == 250 # [tier] == 110000", "[type] == ring && [quality] == unique # [maxstamina] == 50 && [lifeleech] == 5 # [tier] == 110000", diff --git a/libs/SoloPlay/BuildFiles/druid/druid.ElementalBuild.js b/libs/SoloPlay/BuildFiles/druid/druid.ElementalBuild.js index ee193074..9ba135d0 100644 --- a/libs/SoloPlay/BuildFiles/druid/druid.ElementalBuild.js +++ b/libs/SoloPlay/BuildFiles/druid/druid.ElementalBuild.js @@ -36,27 +36,27 @@ const finalBuild = { // Weapon - HotO "[type] == mace && [flag] == runeword # [itemallskills] == 3 # [tier] == 100000", // Helmet - Ravenlore - "[name] == skyspirit && [quality] == unique # [passivefirepierce] >= 10 # [tier] == 100000 + tierscore(item)", + "[name] == skyspirit && [quality] == unique # [passivefirepierce] >= 10 # [tier] == tierscore(item, 100000)", // Belt - Arach's - "[name] == spiderwebsash && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 90 # [tier] == 100000 + tierscore(item)", + "[name] == spiderwebsash && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 90 # [tier] == tierscore(item, 100000)", // Final Boots - Sandstorm Treks - "[name] == scarabshellboots && [quality] == unique # [strength]+[vitality] >= 20 # [tier] == 100000 + tierscore(item)", + "[name] == scarabshellboots && [quality] == unique # [strength]+[vitality] >= 20 # [tier] == tierscore(item, 100000)", // Boots - War Traveler - "[name] == battleboots && [quality] == unique && [flag] != ethereal # [itemmagicbonus] >= 50 # [tier] == 5000 + tierscore(item)", + "[name] == battleboots && [quality] == unique && [flag] != ethereal # [itemmagicbonus] >= 50 # [tier] == tierscore(item, 5000)", // Armor - Enigma "[type] == armor && [flag] != ethereal && [flag] == runeword # [itemallskills] == 2 # [tier] == 100000", // Shield - Phoenix - "[name] == monarch && [flag] != ethereal && [flag] == runeword # [passivefirepierce] >= 28 # [tier] == 100000 + tierscore(item)", + "[name] == monarch && [flag] != ethereal && [flag] == runeword # [passivefirepierce] >= 28 # [tier] == tierscore(item, 100000)", // Final Gloves - Perfect 2x Upp'ed Magefist "[name] == crusadergauntlets && [quality] == unique && [flag] != ethereal # [enhanceddefense] == 30 && [addfireskills] == 1 # [tier] == 110000", // Gloves - 2x Upp'ed Magefist - "[name] == crusadergauntlets && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 20 && [addfireskills] == 1 # [tier] == 100000 + tierscore(item)", + "[name] == crusadergauntlets && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 20 && [addfireskills] == 1 # [tier] == tierscore(item, 100000)", // Gloves - Upp'ed Magefist - "[name] == battlegauntlets && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 20 && [addfireskills] == 1 # [tier] == 100000 + tierscore(item)", + "[name] == battlegauntlets && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 20 && [addfireskills] == 1 # [tier] == tierscore(item, 100000)", // Gloves - Magefist - "[name] == lightgauntlets && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 20 && [addfireskills] == 1 # [tier] == 100000 + tierscore(item)", + "[name] == lightgauntlets && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 20 && [addfireskills] == 1 # [tier] == tierscore(item, 100000)", // Amulet - Maras - "[type] == amulet && [quality] == unique # [strength] == 5 && [coldresist] >= 30 # [tier] == 100000 + tierscore(item)", + "[type] == amulet && [quality] == unique # [strength] == 5 && [coldresist] >= 30 # [tier] == tierscore(item, 100000)", // Rings - SoJ "[type] == ring && [quality] == unique # [itemmaxmanapercent] == 25 # [tier] == 100000", // Switch - CTA diff --git a/libs/SoloPlay/BuildFiles/druid/druid.FirewolfBuild.js b/libs/SoloPlay/BuildFiles/druid/druid.FirewolfBuild.js index c5ac9c49..03cb3f92 100644 --- a/libs/SoloPlay/BuildFiles/druid/druid.FirewolfBuild.js +++ b/libs/SoloPlay/BuildFiles/druid/druid.FirewolfBuild.js @@ -37,21 +37,21 @@ const finalBuild = { // Weapon - Ice "[name] == demoncrossbow && [flag] == runeword # [holyfreezeaura] == 18 # [tier] == 110000", // Helmet - Jalal's Mane - "[name] == totemicmask && [quality] == unique # [druidskills] == 2 && [shapeshiftingskilltab] == 2 # [tier] == 110000 + tierscore(item)", + "[name] == totemicmask && [quality] == unique # [druidskills] == 2 && [shapeshiftingskilltab] == 2 # [tier] == tierscore(item, 110000)", // Belt - Verdungo's - "[name] == mithrilcoil && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 90 # [tier] == 110000 + tierscore(item)", + "[name] == mithrilcoil && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 90 # [tier] == tierscore(item, 110000)", // Armor - Chains of Honor "[type] == armor && [flag] == runeword && [flag] != ethereal # [fireresist] == 65 && [hpregen] == 7 # [tier] == 110000", // Gloves - Dracul's Grasp - "[name] == vampirebonegloves && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 90 # [tier] == 110000 + tierscore(item)", + "[name] == vampirebonegloves && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 90 # [tier] == tierscore(item, 110000)", // Boots - Goblin Toes - "[name] == lightplatedboots && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 50 # [tier] == 100000 + tierscore(item)", + "[name] == lightplatedboots && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 50 # [tier] == tierscore(item, 100000)", // Amulet - Metalgrid - "[type] == amulet && [quality] == unique # [tohit] >= 400 && [coldresist] >= 25 # [tier] == 110000 + tierscore(item)", + "[type] == amulet && [quality] == unique # [tohit] >= 400 && [coldresist] >= 25 # [tier] == tierscore(item, 110000)", // Ring 1 - Ravenfrost "[type] == ring && [quality] == unique # [tohit] >= 180 && [dexterity] >= 15 # [tier] == 100000", // Ring 2 - Carrion Wind - "[name] == ring && [quality] == unique # [poisonresist] == 55 && [lifeleech] >= 6 # [tier] == 110000 + tierscore(item)", + "[name] == ring && [quality] == unique # [poisonresist] == 55 && [lifeleech] >= 6 # [tier] == tierscore(item, 110000)", // Merc // Final Armor - Fortitude "[type] == armor && [flag] == runeword # [enhanceddefense] >= 200 && [enhanceddamage] >= 300 # [merctier] == 100000", diff --git a/libs/SoloPlay/BuildFiles/druid/druid.PlaguewolfBuild.js b/libs/SoloPlay/BuildFiles/druid/druid.PlaguewolfBuild.js index a05c8399..2ae3f266 100644 --- a/libs/SoloPlay/BuildFiles/druid/druid.PlaguewolfBuild.js +++ b/libs/SoloPlay/BuildFiles/druid/druid.PlaguewolfBuild.js @@ -34,15 +34,15 @@ const finalBuild = { // Shield - Stormshield "[name] == monarch && [quality] == unique # [damageresist] >= 35 # [tier] == 110000", // Helmet - Jalal's Mane - "[name] == totemicmask && [quality] == unique # [druidskills] == 2 && [shapeshiftingskilltab] == 2 # [tier] == 110000 + tierscore(item)", + "[name] == totemicmask && [quality] == unique # [druidskills] == 2 && [shapeshiftingskilltab] == 2 # [tier] == tierscore(item, 110000)", // Belt - Verdungo's - "[name] == mithrilcoil && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 90 # [tier] == 110000 + tierscore(item)", + "[name] == mithrilcoil && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 90 # [tier] == tierscore(item, 110000)", // Armor - CoH "[type] == armor && [flag] == runeword && [flag] != ethereal # [fireresist] == 65 && [hpregen] == 7 # [tier] == 110000", // Gloves - Dracul's - "[name] == vampirebonegloves && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 90 # [tier] == 110000 + tierscore(item)", + "[name] == vampirebonegloves && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 90 # [tier] == tierscore(item, 110000)", // Amulet - Maras - "[type] == amulet && [quality] == unique # [strength] == 5 && [coldresist] >= 30 # [tier] == 110000 + tierscore(item)", + "[type] == amulet && [quality] == unique # [strength] == 5 && [coldresist] >= 30 # [tier] == tierscore(item, 110000)", // Final Rings - Perfect Wisp & Bul-Kathos' Wedding Band "[type] == ring && [quality] == unique # [itemabsorblightpercent] == 20 # [tier] == 110000", "[type] == ring && [quality] == unique # [maxstamina] == 50 && [lifeleech] == 5 # [tier] == 110000", diff --git a/libs/SoloPlay/BuildFiles/druid/druid.StormbearBuild.js b/libs/SoloPlay/BuildFiles/druid/druid.StormbearBuild.js index 211d09fa..5659170c 100644 --- a/libs/SoloPlay/BuildFiles/druid/druid.StormbearBuild.js +++ b/libs/SoloPlay/BuildFiles/druid/druid.StormbearBuild.js @@ -35,23 +35,23 @@ const finalBuild = { "[name] == phaseblade && [flag] == runeword # [enhanceddamage] >= 350 && [itemdeadlystrike] == 20 && [itemcrushingblow] == 20 # [tier] == 110000", // Shield - Sanctuary "[name] == hyperion # [fhr] >= 20 && [enhanceddefense] >= 130 && [fireresist] >= 70 # [tier] == 110000", - "[name] == hyperion # [fhr] >= 20 && [enhanceddefense] >= 130 && [fireresist] >= 50 # [tier] == 100000 + tierscore(item)", + "[name] == hyperion # [fhr] >= 20 && [enhanceddefense] >= 130 && [fireresist] >= 50 # [tier] == tierscore(item, 100000)", // Helmet - Jalal's Mane - "[name] == totemicmask && [quality] == unique # [druidskills] == 2 && [shapeshiftingskilltab] == 2 # [tier] == 110000 + tierscore(item)", + "[name] == totemicmask && [quality] == unique # [druidskills] == 2 && [shapeshiftingskilltab] == 2 # [tier] == tierscore(item, 110000)", // Belt - Verdungo's - "[name] == mithrilcoil && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 90 # [tier] == 110000 + tierscore(item)", + "[name] == mithrilcoil && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 90 # [tier] == tierscore(item, 110000)", // Armor - Chains of Honor "[type] == armor && [flag] == runeword && [flag] != ethereal # [fireresist] == 65 && [hpregen] == 7 # [tier] == 110000", // Gloves - Dracul's Grasp - "[name] == vampirebonegloves && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 90 # [tier] == 110000 + tierscore(item)", + "[name] == vampirebonegloves && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 90 # [tier] == tierscore(item, 110000)", // Boots - Gore Rider - "[name] == warboots && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 160 # [tier] == 110000 + tierscore(item)", + "[name] == warboots && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 160 # [tier] == tierscore(item, 110000)", // Amulet - Metalgrid - "[type] == amulet && [quality] == unique # [tohit] >= 400 && [coldresist] >= 25 # [tier] == 110000 + tierscore(item)", + "[type] == amulet && [quality] == unique # [tohit] >= 400 && [coldresist] >= 25 # [tier] == tierscore(item, 110000)", // Ring 1 - Ravenfrost "[type] == ring && [quality] == unique # [tohit] >= 180 && [dexterity] >= 15 # [tier] == 100000", // Ring 2 - Carrion Wind - "[name] == ring && [quality] == unique # [poisonresist] == 55 && [lifeleech] >= 6 # [tier] == 110000 + tierscore(item)", + "[name] == ring && [quality] == unique # [poisonresist] == 55 && [lifeleech] >= 6 # [tier] == tierscore(item, 110000)", // Merc // Final Armor - Fortitude "[type] == armor && [flag] == runeword # [enhanceddefense] >= 200 && [enhanceddamage] >= 300 # [merctier] == 100000", diff --git a/libs/SoloPlay/BuildFiles/druid/druid.WindBuild.js b/libs/SoloPlay/BuildFiles/druid/druid.WindBuild.js index 9aadb53d..018c0f77 100644 --- a/libs/SoloPlay/BuildFiles/druid/druid.WindBuild.js +++ b/libs/SoloPlay/BuildFiles/druid/druid.WindBuild.js @@ -31,13 +31,13 @@ const finalBuild = { // Weapon - HotO "[type] == mace && [flag] == runeword # [itemallskills] == 3 # [tier] == 100000", // Helmet - Jalal's mane - "[name] == totemicmask && [quality] == unique # [druidskills] == 2 && [shapeshiftingskilltab] == 2 # [tier] == 110000 + tierscore(item)", + "[name] == totemicmask && [quality] == unique # [druidskills] == 2 && [shapeshiftingskilltab] == 2 # [tier] == tierscore(item, 110000)", // Belt - Arach's - "[name] == spiderwebsash && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 90 # [tier] == 100000 + tierscore(item)", + "[name] == spiderwebsash && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 90 # [tier] == tierscore(item, 100000)", // Final Boots - Sandstorm Treks - "[name] == scarabshellboots && [quality] == unique # [strength]+[vitality] >= 20 # [tier] == 100000 + tierscore(item)", + "[name] == scarabshellboots && [quality] == unique # [strength]+[vitality] >= 20 # [tier] == tierscore(item, 100000)", // Boots - War Traveler - "[name] == battleboots && [quality] == unique && [flag] != ethereal # [itemmagicbonus] >= 50 # [tier] == 5000 + tierscore(item)", + "[name] == battleboots && [quality] == unique && [flag] != ethereal # [itemmagicbonus] >= 50 # [tier] == tierscore(item, 5000)", // Armor - Enigma "[type] == armor && [flag] != ethereal && [flag] == runeword # [itemallskills] == 2 # [tier] == 100000", // Shield - Spirit @@ -45,13 +45,13 @@ const finalBuild = { // Final Gloves - Perfect 2x Upp'ed Magefist "[name] == crusadergauntlets && [quality] == unique && [flag] != ethereal # [enhanceddefense] == 30 && [addfireskills] == 1 # [tier] == 110000", // Gloves - 2x Upp'ed Magefist - "[name] == crusadergauntlets && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 20 && [addfireskills] == 1 # [tier] == 100000 + tierscore(item)", + "[name] == crusadergauntlets && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 20 && [addfireskills] == 1 # [tier] == tierscore(item, 100000)", // Gloves - Upp'ed Magefist - "[name] == battlegauntlets && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 20 && [addfireskills] == 1 # [tier] == 100000 + tierscore(item)", + "[name] == battlegauntlets && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 20 && [addfireskills] == 1 # [tier] == tierscore(item, 100000)", // Gloves - Magefist - "[name] == lightgauntlets && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 20 && [addfireskills] == 1 # [tier] == 100000 + tierscore(item)", + "[name] == lightgauntlets && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 20 && [addfireskills] == 1 # [tier] == tierscore(item, 100000)", // Amulet - Maras - "[type] == amulet && [quality] == unique # [strength] == 5 && [coldresist] >= 30 # [tier] == 100000 + tierscore(item)", + "[type] == amulet && [quality] == unique # [strength] == 5 && [coldresist] >= 30 # [tier] == tierscore(item, 100000)", // Rings - SoJ "[type] == ring && [quality] == unique # [itemmaxmanapercent] == 25 # [tier] == 100000", // Switch - CTA diff --git a/libs/SoloPlay/BuildFiles/druid/druid.WolfBuild.js b/libs/SoloPlay/BuildFiles/druid/druid.WolfBuild.js index eeac3e6c..77f17f73 100644 --- a/libs/SoloPlay/BuildFiles/druid/druid.WolfBuild.js +++ b/libs/SoloPlay/BuildFiles/druid/druid.WolfBuild.js @@ -30,21 +30,21 @@ const finalBuild = { // Weapon - Upp'ed Ribcracker "[name] == stalagmite && [quality] == unique # [enhanceddamage] >= 300 && [ias] >= 50 # [tier] == 110000", // Helmet - Jalal's Mane - "[name] == totemicmask && [quality] == unique # [druidskills] == 2 && [shapeshiftingskilltab] == 2 # [tier] == 110000 + tierscore(item)", + "[name] == totemicmask && [quality] == unique # [druidskills] == 2 && [shapeshiftingskilltab] == 2 # [tier] == tierscore(item, 110000)", // Belt - Verdungo's - "[name] == mithrilcoil && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 90 # [tier] == 110000 + tierscore(item)", + "[name] == mithrilcoil && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 90 # [tier] == tierscore(item, 110000)", // Armor - CoH "[type] == armor && [flag] == runeword && [flag] != ethereal # [fireresist] == 65 && [hpregen] == 7 # [tier] == 110000", // Gloves - Dracul's - "[name] == vampirebonegloves && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 90 # [tier] == 110000 + tierscore(item)", + "[name] == vampirebonegloves && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 90 # [tier] == tierscore(item, 110000)", // Amulet - Maras - "[type] == amulet && [quality] == unique # [strength] == 5 && [coldresist] >= 30 # [tier] == 110000 + tierscore(item)", + "[type] == amulet && [quality] == unique # [strength] == 5 && [coldresist] >= 30 # [tier] == tierscore(item, 110000)", // Final Rings - Perfect Wisp & Bul-Kathos' Wedding Band - "[type] == ring && [quality] == unique # [itemabsorblightpercent] == 20 # [tier] == 110000 + tierscore(item)", - "[type] == ring && [quality] == unique # [maxstamina] == 50 && [lifeleech] == 5 # [tier] == 110000 + tierscore(item)", + "[type] == ring && [quality] == unique # [itemabsorblightpercent] == 20 # [tier] == tierscore(item, 110000)", + "[type] == ring && [quality] == unique # [maxstamina] == 50 && [lifeleech] == 5 # [tier] == tierscore(item, 110000)", // Rings - Wisp & Bul-Kathos' Wedding Band - "[type] == ring && [quality] == unique # [itemabsorblightpercent] >= 10 # [tier] == 110000 + tierscore(item)", - "[type] == ring && [quality] == unique # [maxstamina] == 50 && [lifeleech] >= 3 # [tier] == 110000 + tierscore(item)", + "[type] == ring && [quality] == unique # [itemabsorblightpercent] >= 10 # [tier] == tierscore(item, 110000)", + "[type] == ring && [quality] == unique # [maxstamina] == 50 && [lifeleech] >= 3 # [tier] == tierscore(item, 110000)", // Merc Final Armor - Fortitude "[type] == armor && [flag] == runeword # [enhanceddefense] >= 200 && [enhanceddamage] >= 300 # [merctier] == 100000", // Merc Armor - Treachery diff --git a/libs/SoloPlay/BuildFiles/necromancer/necromancer.BoneBuild.js b/libs/SoloPlay/BuildFiles/necromancer/necromancer.BoneBuild.js index 4a8a1672..11144bc1 100644 --- a/libs/SoloPlay/BuildFiles/necromancer/necromancer.BoneBuild.js +++ b/libs/SoloPlay/BuildFiles/necromancer/necromancer.BoneBuild.js @@ -37,31 +37,31 @@ const finalBuild = { // Weapon - Spectral Shard "[name] == blade && [quality] == unique # [fcr] == 20 && [allres] == 10 # [tier] == 100000", // Helm - Tarnhelm - "[name] == skullcap && [quality] == unique # [itemallskills] == 1 && [itemmagicbonus] >= 25 # [tier] == 100000 + tierscore(item)", + "[name] == skullcap && [quality] == unique # [itemallskills] == 1 && [itemmagicbonus] >= 25 # [tier] == tierscore(item, 100000)", // Shield - "[type] == shield && [quality] >= magic # [necromancerskills] == 2 && [allres] >= 16 # [tier] == 100000 + tierscore(item)", + "[type] == shield && [quality] >= magic # [necromancerskills] == 2 && [allres] >= 16 # [tier] == tierscore(item, 100000)", // Rings - SoJ "[type] == ring && [quality] == unique # [itemmaxmanapercent] == 25 # [tier] == 100000", // Amulet - "[type] == amulet && [quality] >= magic # [necromancerskills] == 2 && [fcr] == 10 # [tier] == 100000 + tierscore(item)", + "[type] == amulet && [quality] >= magic # [necromancerskills] == 2 && [fcr] == 10 # [tier] == tierscore(item, 100000)", // Boots - "[type] == boots && [quality] >= magic # [frw] >= 20 && [fhr] == 10 && [coldresist]+[lightresist] >= 10 # [tier] == 100000 + tierscore(item)", + "[type] == boots && [quality] >= magic # [frw] >= 20 && [fhr] == 10 && [coldresist]+[lightresist] >= 10 # [tier] == tierscore(item, 100000)", // Belt - "[type] == belt && [quality] >= magic # [fhr] >= 20 && [maxhp] >= 40 && [fireresist]+[lightresist] >= 20 # [tier] == 100000 + tierscore(item)", + "[type] == belt && [quality] >= magic # [fhr] >= 20 && [maxhp] >= 40 && [fireresist]+[lightresist] >= 20 # [tier] == tierscore(item, 100000)", // Gloves - Magefist - "[name] == lightgauntlets && [quality] == unique # [fcr] >= 20 && [addfireskills] == 1 # [tier] == 100000 + tierscore(item)", + "[name] == lightgauntlets && [quality] == unique # [fcr] >= 20 && [addfireskills] == 1 # [tier] == tierscore(item, 100000)", ], expansionTiers: [ // Weapon "([type] == wand || [type] == sword && ([quality] >= magic || [flag] == runeword) || [type] == knife && [quality] >= magic) && [flag] != ethereal # [secondarymindamage] == 0 && [itemchargedskill] >= 0 # [tier] == tierscore(item)", // Helmet - Harlequin's Crest - "[name] == shako && [quality] == unique && [flag] != ethereal # [damageresist] == 10 # [tier] == 100000 + tierscore(item)", + "[name] == shako && [quality] == unique && [flag] != ethereal # [damageresist] == 10 # [tier] == tierscore(item, 100000)", // Belt - Arach's - "[name] == spiderwebsash && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 90 # [tier] == 100000 + tierscore(item)", + "[name] == spiderwebsash && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 90 # [tier] == tierscore(item, 100000)", // Final Boots - Sandstorm Treks - "[name] == scarabshellboots && [quality] == unique # [strength]+[vitality] >= 20 # [tier] == 100000 + tierscore(item)", + "[name] == scarabshellboots && [quality] == unique # [strength]+[vitality] >= 20 # [tier] == tierscore(item, 100000)", // Boots - War Traveler - "[name] == battleboots && [quality] == unique && [flag] != ethereal # [itemmagicbonus] >= 50 # [tier] == 5000 + tierscore(item)", + "[name] == battleboots && [quality] == unique && [flag] != ethereal # [itemmagicbonus] >= 50 # [tier] == tierscore(item, 5000)", // Armor - Enigma "[type] == armor && [flag] != ethereal && [flag] == runeword # [itemallskills] == 2 # [tier] == 100000", // Shield @@ -69,13 +69,13 @@ const finalBuild = { // Final Gloves - Perfect 2x Upp'ed Magefist "[name] == crusadergauntlets && [quality] == unique && [flag] != ethereal # [enhanceddefense] == 30 && [addfireskills] == 1 # [tier] == 110000", // Gloves - 2x Upp'ed Magefist - "[name] == crusadergauntlets && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 20 && [addfireskills] == 1 # [tier] == 100000 + tierscore(item)", + "[name] == crusadergauntlets && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 20 && [addfireskills] == 1 # [tier] == tierscore(item, 100000)", // Gloves - Upp'ed Magefist - "[name] == battlegauntlets && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 20 && [addfireskills] == 1 # [tier] == 100000 + tierscore(item)", + "[name] == battlegauntlets && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 20 && [addfireskills] == 1 # [tier] == tierscore(item, 100000)", // Gloves - Magefist - "[name] == lightgauntlets && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 20 && [addfireskills] == 1 # [tier] == 100000 + tierscore(item)", + "[name] == lightgauntlets && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 20 && [addfireskills] == 1 # [tier] == tierscore(item, 100000)", // Amulet - Maras - "[type] == amulet && [quality] == unique # [strength] == 5 && [coldresist] >= 30 # [tier] == 100000 + tierscore(item)", + "[type] == amulet && [quality] == unique # [strength] == 5 && [coldresist] >= 30 # [tier] == tierscore(item, 100000)", // Rings - Dwarf Star & SoJ "[type] == ring && [quality] == unique # [maxhp] >= 40 && [magicdamagereduction] >= 12 # [tier] == 99000", "[type] == ring && [quality] == unique # [itemmaxmanapercent] == 25 # [tier] == 100000", diff --git a/libs/SoloPlay/BuildFiles/necromancer/necromancer.PoisonBuild.js b/libs/SoloPlay/BuildFiles/necromancer/necromancer.PoisonBuild.js index db1c9403..93ceae86 100644 --- a/libs/SoloPlay/BuildFiles/necromancer/necromancer.PoisonBuild.js +++ b/libs/SoloPlay/BuildFiles/necromancer/necromancer.PoisonBuild.js @@ -38,31 +38,31 @@ const finalBuild = { // Weapon - Blackbog's Sharp "[name] == cinquedeas && [quality] == unique # [ias] == 30 && [skillpoisonnova] == 4 # [tier] == 100000", // Helm - Tarnhelm - "[name] == skullcap && [quality] == unique # [itemallskills] == 1 && [itemmagicbonus] >= 25 # [tier] == 100000 + tierscore(item)", + "[name] == skullcap && [quality] == unique # [itemallskills] == 1 && [itemmagicbonus] >= 25 # [tier] == tierscore(item, 100000)", // Shield - "[type] == shield && [quality] >= magic # [necromancerskills] == 2 && [allres] >= 16 # [tier] == 100000 + tierscore(item)", + "[type] == shield && [quality] >= magic # [necromancerskills] == 2 && [allres] >= 16 # [tier] == tierscore(item, 100000)", // Rings - SoJ "[type] == ring && [quality] == unique # [itemmaxmanapercent] == 25 # [tier] == 100000", // Amulet - "[type] == amulet && [quality] >= magic # [necromancerskills] == 2 && [fcr] == 10 # [tier] == 100000 + tierscore(item)", + "[type] == amulet && [quality] >= magic # [necromancerskills] == 2 && [fcr] == 10 # [tier] == tierscore(item, 100000)", // Boots - "[type] == boots && [quality] >= magic # [frw] >= 20 && [fhr] == 10 && [coldresist]+[lightresist] >= 10 # [tier] == 100000 + tierscore(item)", + "[type] == boots && [quality] >= magic # [frw] >= 20 && [fhr] == 10 && [coldresist]+[lightresist] >= 10 # [tier] == tierscore(item, 100000)", // Belt - "[type] == belt && [quality] >= magic # [fhr] >= 20 && [maxhp] >= 40 && [fireresist]+[lightresist] >= 20 # [tier] == 100000 + tierscore(item)", + "[type] == belt && [quality] >= magic # [fhr] >= 20 && [maxhp] >= 40 && [fireresist]+[lightresist] >= 20 # [tier] == tierscore(item, 100000)", // Gloves - Magefist - "[name] == lightgauntlets && [quality] == unique # [fcr] >= 20 && [addfireskills] == 1 # [tier] == 100000 + tierscore(item)", + "[name] == lightgauntlets && [quality] == unique # [fcr] >= 20 && [addfireskills] == 1 # [tier] == tierscore(item, 100000)", ], expansionTiers: [ // Weapon "([type] == wand || [type] == sword && ([quality] >= magic || [flag] == runeword) || [type] == knife && [quality] >= magic) && [flag] != ethereal # [secondarymindamage] == 0 && [itemchargedskill] >= 0 # [tier] == tierscore(item)", // Helmet - Harlequin's Crest - "[name] == shako && [quality] == unique && [flag] != ethereal # [damageresist] == 10 # [tier] == 100000 + tierscore(item)", + "[name] == shako && [quality] == unique && [flag] != ethereal # [damageresist] == 10 # [tier] == tierscore(item, 100000)", // Belt - Arach's - "[name] == spiderwebsash && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 90 # [tier] == 100000 + tierscore(item)", + "[name] == spiderwebsash && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 90 # [tier] == tierscore(item, 100000)", // Final Boots - Sandstorm Treks - "[name] == scarabshellboots && [quality] == unique # [strength]+[vitality] >= 20 # [tier] == 100000 + tierscore(item)", + "[name] == scarabshellboots && [quality] == unique # [strength]+[vitality] >= 20 # [tier] == tierscore(item, 100000)", // Boots - War Traveler - "[name] == battleboots && [quality] == unique && [flag] != ethereal # [itemmagicbonus] >= 50 # [tier] == 5000 + tierscore(item)", + "[name] == battleboots && [quality] == unique && [flag] != ethereal # [itemmagicbonus] >= 50 # [tier] == tierscore(item, 5000)", // Armor - Enigma "[type] == armor && [flag] != ethereal && [flag] == runeword # [itemallskills] == 2 # [tier] == 100000", // Shield @@ -70,13 +70,13 @@ const finalBuild = { // Final Gloves - Perfect 2x Upp'ed Magefist "[name] == crusadergauntlets && [quality] == unique && [flag] != ethereal # [enhanceddefense] == 30 && [addfireskills] == 1 # [tier] == 110000", // Gloves - 2x Upp'ed Magefist - "[name] == crusadergauntlets && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 20 && [addfireskills] == 1 # [tier] == 100000 + tierscore(item)", + "[name] == crusadergauntlets && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 20 && [addfireskills] == 1 # [tier] == tierscore(item, 100000)", // Gloves - Upp'ed Magefist - "[name] == battlegauntlets && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 20 && [addfireskills] == 1 # [tier] == 100000 + tierscore(item)", + "[name] == battlegauntlets && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 20 && [addfireskills] == 1 # [tier] == tierscore(item, 100000)", // Gloves - Magefist - "[name] == lightgauntlets && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 20 && [addfireskills] == 1 # [tier] == 100000 + tierscore(item)", + "[name] == lightgauntlets && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 20 && [addfireskills] == 1 # [tier] == tierscore(item, 100000)", // Amulet - "[type] == amulet && [quality] == unique # [strength] == 5 && [coldresist] >= 30 # [tier] == 100000 + tierscore(item)", // Maras + "[type] == amulet && [quality] == unique # [strength] == 5 && [coldresist] >= 30 # [tier] == tierscore(item, 100000)", // Maras // Rings - Dwarf Star & SoJ "[name] == ring && [quality] == unique # [maxhp] >= 40 && [magicdamagereduction] >= 12 # [tier] == 99000", "[type] == ring && [quality] == unique # [itemmaxmanapercent] == 25 # [tier] == 100000", diff --git a/libs/SoloPlay/BuildFiles/necromancer/necromancer.SummonBuild.js b/libs/SoloPlay/BuildFiles/necromancer/necromancer.SummonBuild.js index a19d58ef..1037d373 100644 --- a/libs/SoloPlay/BuildFiles/necromancer/necromancer.SummonBuild.js +++ b/libs/SoloPlay/BuildFiles/necromancer/necromancer.SummonBuild.js @@ -48,31 +48,31 @@ const finalBuild = { // Weapon - Blackbog's Sharp "[name] == cinquedeas && [quality] == unique # [ias] == 30 && [skillpoisonnova] == 4 # [tier] == 100000", // Helm - Tarnhelm - "[name] == skullcap && [quality] == unique # [itemallskills] == 1 && [itemmagicbonus] >= 25 # [tier] == 100000 + tierscore(item)", + "[name] == skullcap && [quality] == unique # [itemallskills] == 1 && [itemmagicbonus] >= 25 # [tier] == tierscore(item, 100000)", // Shield - "[type] == shield && [quality] >= magic # [necromancerskills] == 2 && [allres] >= 16 # [tier] == 100000 + tierscore(item)", + "[type] == shield && [quality] >= magic # [necromancerskills] == 2 && [allres] >= 16 # [tier] == tierscore(item, 100000)", // Rings - SoJ "[type] == ring && [quality] == unique # [itemmaxmanapercent] == 25 # [tier] == 100000", // Amulet - "[type] == amulet && [quality] >= magic # [necromancerskills] == 2 && [fcr] == 10 # [tier] == 100000 + tierscore(item)", + "[type] == amulet && [quality] >= magic # [necromancerskills] == 2 && [fcr] == 10 # [tier] == tierscore(item, 100000)", // Boots - "[type] == boots && [quality] >= magic # [frw] >= 20 && [fhr] == 10 && [coldresist]+[lightresist] >= 10 # [tier] == 100000 + tierscore(item)", + "[type] == boots && [quality] >= magic # [frw] >= 20 && [fhr] == 10 && [coldresist]+[lightresist] >= 10 # [tier] == tierscore(item, 100000)", // Belt - "[type] == belt && [quality] >= magic # [fhr] >= 20 && [maxhp] >= 40 && [fireresist]+[lightresist] >= 20 # [tier] == 100000 + tierscore(item)", + "[type] == belt && [quality] >= magic # [fhr] >= 20 && [maxhp] >= 40 && [fireresist]+[lightresist] >= 20 # [tier] == tierscore(item, 100000)", // Gloves - Magefist - "[name] == lightgauntlets && [quality] == unique # [fcr] >= 20 && [addfireskills] == 1 # [tier] == 100000 + tierscore(item)", + "[name] == lightgauntlets && [quality] == unique # [fcr] >= 20 && [addfireskills] == 1 # [tier] == tierscore(item, 100000)", ], expansionTiers: [ // Weapon "([type] == wand || [type] == sword && ([quality] >= magic || [flag] == runeword) || [type] == knife && [quality] >= magic) && [flag] != ethereal # [secondarymindamage] == 0 && [itemchargedskill] >= 0 # [tier] == tierscore(item)", // Helmet - Harlequin's Crest - "[name] == shako && [quality] == unique && [flag] != ethereal # [damageresist] == 10 # [tier] == 100000 + tierscore(item)", + "[name] == shako && [quality] == unique && [flag] != ethereal # [damageresist] == 10 # [tier] == tierscore(item, 100000)", // Belt - Arach's - "[name] == spiderwebsash && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 90 # [tier] == 100000 + tierscore(item)", + "[name] == spiderwebsash && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 90 # [tier] == tierscore(item, 100000)", // Boots - Marrowalk - "[name] == boneweaveboots && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 170 # [tier] == 100000 + tierscore(item)", + "[name] == boneweaveboots && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 170 # [tier] == tierscore(item, 100000)", // Boots - War Traveler - "[name] == battleboots && [quality] == unique && [flag] != ethereal # [itemmagicbonus] >= 50 # [tier] == 5000 + tierscore(item)", + "[name] == battleboots && [quality] == unique && [flag] != ethereal # [itemmagicbonus] >= 50 # [tier] == tierscore(item, 5000)", // Armor - Enigma "[type] == armor && [flag] != ethereal && [flag] == runeword # [itemallskills] == 2 # [tier] == 100000", // Shield @@ -80,13 +80,13 @@ const finalBuild = { // Final Gloves - Perfect 2x Upp'ed Magefist "[name] == crusadergauntlets && [quality] == unique && [flag] != ethereal # [enhanceddefense] == 30 && [addfireskills] == 1 # [tier] == 110000", // Gloves - 2x Upp'ed Magefist - "[name] == crusadergauntlets && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 20 && [addfireskills] == 1 # [tier] == 100000 + tierscore(item)", + "[name] == crusadergauntlets && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 20 && [addfireskills] == 1 # [tier] == tierscore(item, 100000)", // Gloves - Upp'ed Magefist - "[name] == battlegauntlets && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 20 && [addfireskills] == 1 # [tier] == 100000 + tierscore(item)", + "[name] == battlegauntlets && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 20 && [addfireskills] == 1 # [tier] == tierscore(item, 100000)", // Gloves - Magefist - "[name] == lightgauntlets && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 20 && [addfireskills] == 1 # [tier] == 100000 + tierscore(item)", + "[name] == lightgauntlets && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 20 && [addfireskills] == 1 # [tier] == tierscore(item, 100000)", // Amulet - Maras - "[type] == amulet && [quality] == unique # [strength] == 5 && [coldresist] >= 30 # [tier] == 100000 + tierscore(item)", + "[type] == amulet && [quality] == unique # [strength] == 5 && [coldresist] >= 30 # [tier] == tierscore(item, 100000)", // Final Rings - SoJ & Perfect Raven Frost "[type] == ring && [quality] == unique # [itemmaxmanapercent] == 25 # [tier] == 100000", "[type] == ring && [quality] == unique # [dexterity] >= 20 # [tier] == 100000", diff --git a/libs/SoloPlay/BuildFiles/paladin/paladin.AuradinBuild.js b/libs/SoloPlay/BuildFiles/paladin/paladin.AuradinBuild.js index e2910724..4bece3a2 100644 --- a/libs/SoloPlay/BuildFiles/paladin/paladin.AuradinBuild.js +++ b/libs/SoloPlay/BuildFiles/paladin/paladin.AuradinBuild.js @@ -37,9 +37,9 @@ const finalBuild = { // Helm - Dream "[type] == helm && [flag] == runeword # [holyshockaura] >= 15 # [tier] == 110000", // Belt - TGods - "[name] == warbelt && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 160 # [tier] == 110000 + tierscore(item)", + "[name] == warbelt && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 160 # [tier] == tierscore(item, 110000)", // Boots - Gore Rider - "[name] == warboots && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 160 # [tier] == 110000 + tierscore(item)", + "[name] == warboots && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 160 # [tier] == tierscore(item, 110000)", // Armor - Dragon "[type] == armor && [flag] != ethereal && [flag] == runeword # [holyfireaura] >= 14 # [tier] == 110000", // Shield - Dream diff --git a/libs/SoloPlay/BuildFiles/paladin/paladin.ClassicauradinBuild.js b/libs/SoloPlay/BuildFiles/paladin/paladin.ClassicauradinBuild.js index 7c86d559..3cb8d92c 100644 --- a/libs/SoloPlay/BuildFiles/paladin/paladin.ClassicauradinBuild.js +++ b/libs/SoloPlay/BuildFiles/paladin/paladin.ClassicauradinBuild.js @@ -33,15 +33,15 @@ const finalBuild = { ], classicTiers: [ // Weapon - "[name] == warscepter && [quality] >= magic # [paladinskills] == 2 && [ias] == 40 && [skillholyshock] >= 1 # [tier] == 100000 + tierscore(item)", + "[name] == warscepter && [quality] >= magic # [paladinskills] == 2 && [ias] == 40 && [skillholyshock] >= 1 # [tier] == tierscore(item, 100000)", // Helm - Tarnhelm - "[name] == skullcap && [quality] == unique # [itemallskills] == 1 && [itemmagicbonus] >= 25 # [tier] == 100000 + tierscore(item)", + "[name] == skullcap && [quality] == unique # [itemallskills] == 1 && [itemmagicbonus] >= 25 # [tier] == tierscore(item, 100000)", // Shield - "[type] == shield && [quality] >= magic # [paladinskills] == 2 && [allres] >= 16 # [tier] == 100000 + tierscore(item)", + "[type] == shield && [quality] >= magic # [paladinskills] == 2 && [allres] >= 16 # [tier] == tierscore(item, 100000)", // Rings - SoJ "[type] == ring && [quality] == unique # [itemmaxmanapercent] == 25 # [tier] == 100000", // Amulet - "[type] == amulet && [quality] >= magic # [paladinskills] == 2 && [allres] >= 16 # [tier] == 100000 + tierscore(item)", + "[type] == amulet && [quality] >= magic # [paladinskills] == 2 && [allres] >= 16 # [tier] == tierscore(item, 100000)", // Boots - Hsaru's Iron Heel "[name] == chainboots && [quality] == set # [frw] == 20 # [tier] == 100000", // Belt - Hsaru's Iron Stay @@ -60,9 +60,9 @@ const finalBuild = { // Helm - Dream "[type] == helm && [flag] == runeword # [holyshockaura] >= 15 # [tier] == 110000", // Belt - TGods - "[name] == warbelt && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 160 # [tier] == 110000 + tierscore(item)", + "[name] == warbelt && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 160 # [tier] == tierscore(item, 110000)", // Boots - Gore Rider - "[name] == warboots && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 160 # [tier] == 110000 + tierscore(item)", + "[name] == warboots && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 160 # [tier] == tierscore(item, 110000)", // Armor - Dragon "[type] == armor && [flag] != ethereal && [flag] == runeword # [holyfireaura] >= 14 # [tier] == 110000", // Shield - Dream diff --git a/libs/SoloPlay/BuildFiles/paladin/paladin.HammerdinBuild.js b/libs/SoloPlay/BuildFiles/paladin/paladin.HammerdinBuild.js index 7daaf285..a561b450 100644 --- a/libs/SoloPlay/BuildFiles/paladin/paladin.HammerdinBuild.js +++ b/libs/SoloPlay/BuildFiles/paladin/paladin.HammerdinBuild.js @@ -46,35 +46,35 @@ const finalBuild = { // Rings - SoJ "[type] == ring && [quality] == unique # [itemmaxmanapercent] == 25 # [tier] == 100000", // Gloves - Magefist - "[name] == lightgauntlets && [quality] == unique # [fcr] >= 20 && [addfireskills] == 1 # [tier] == 100000 + tierscore(item)", + "[name] == lightgauntlets && [quality] == unique # [fcr] >= 20 && [addfireskills] == 1 # [tier] == tierscore(item, 100000)", ], expansionTiers: [ // Weapon - HotO "[type] == mace && [flag] == runeword # [fcr] == 40 # [tier] == 100000", // Helm - Harlequin's Crest - "[name] == shako && [quality] == unique && [flag] != ethereal # [damageresist] == 10 # [tier] == 100000 + tierscore(item)", + "[name] == shako && [quality] == unique && [flag] != ethereal # [damageresist] == 10 # [tier] == tierscore(item, 100000)", // Belt - Arach's - "[name] == spiderwebsash && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 90 # [tier] == 100000 + tierscore(item)", + "[name] == spiderwebsash && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 90 # [tier] == tierscore(item, 100000)", // Final Boots - Sandstorm Treks - "[name] == scarabshellboots && [quality] == unique # [strength]+[vitality] >= 20 # [tier] == 100000 + tierscore(item)", + "[name] == scarabshellboots && [quality] == unique # [strength]+[vitality] >= 20 # [tier] == tierscore(item, 100000)", // Boots - War Traveler - "[name] == battleboots && [quality] == unique && [flag] != ethereal # [itemmagicbonus] >= 50 # [tier] == 5000 + tierscore(item)", + "[name] == battleboots && [quality] == unique && [flag] != ethereal # [itemmagicbonus] >= 50 # [tier] == tierscore(item, 5000)", // Armor - Enigma "[type] == armor && [flag] != ethereal && [flag] == runeword # [itemallskills] == 2 # [tier] == 100000", // Final Shield - Spirit "[type] == auricshields && [flag] == runeword # [fcr] == 35 && [maxmana] == 112 && [coldresist] == 80 # [tier] == 110000", // Shield - Spirit - "[type] == auricshields && [flag] == runeword # [fcr] >= 25 && [maxmana] >= 89 && [coldresist] == 80 # [tier] == 100000 + tierscore(item)", + "[type] == auricshields && [flag] == runeword # [fcr] >= 25 && [maxmana] >= 89 && [coldresist] == 80 # [tier] == tierscore(item, 100000)", // Shield - HoZ - "[name] == gildedshield && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 150 # [tier] == 50000 + tierscore(item)", + "[name] == gildedshield && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 150 # [tier] == tierscore(item, 50000)", // Final Gloves - Perfect Upp'ed Magefist - "[name] == battlegauntlets && [quality] == unique && [flag] != ethereal # [enhanceddefense] == 30 && [addfireskills] == 1 # [tier] == 110000", + "[name] == battlegauntlets && [quality] == unique && [flag] != ethereal # [enhanceddefense] == 30 && [addfireskills] == 1 && [defense] == 71 # [tier] == 110000", // Gloves - Upp'ed Magefist - "[name] == battlegauntlets && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 20 && [addfireskills] == 1 # [tier] == 100000 + tierscore(item)", + "[name] == battlegauntlets && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 20 && [addfireskills] == 1 # [tier] == tierscore(item, 100000)", // Gloves - Magefist - "[name] == lightgauntlets && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 20 && [addfireskills] == 1 # [tier] == 100000 + tierscore(item)", + "[name] == lightgauntlets && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 20 && [addfireskills] == 1 # [tier] == tierscore(item, 100000)", // Amulet - Maras - "[type] == amulet && [quality] == unique # [strength] == 5 && [coldresist] >= 30 # [tier] == 100000 + tierscore(item)", + "[type] == amulet && [quality] == unique # [strength] == 5 && [coldresist] >= 30 # [tier] == tierscore(item, 100000)", // Final Rings - SoJ & Perfect Raven Frost "[type] == ring && [quality] == unique # [itemmaxmanapercent] == 25 # [tier] == 100000", "[type] == ring && [quality] == unique # [dexterity] >= 20 # [tier] == 100000", diff --git a/libs/SoloPlay/BuildFiles/paladin/paladin.HammershockBuild.js b/libs/SoloPlay/BuildFiles/paladin/paladin.HammershockBuild.js index 43369425..f5f52902 100644 --- a/libs/SoloPlay/BuildFiles/paladin/paladin.HammershockBuild.js +++ b/libs/SoloPlay/BuildFiles/paladin/paladin.HammershockBuild.js @@ -37,15 +37,15 @@ const finalBuild = { ], classicTiers: [ // Weapon - "[name] == warscepter && [quality] >= magic # [paladinskills] == 2 && [ias] == 40 && [skillholyshock] >= 1 # [tier] == 100000 + tierscore(item)", + "[name] == warscepter && [quality] >= magic # [paladinskills] == 2 && [ias] == 40 && [skillholyshock] >= 1 # [tier] == tierscore(item, 100000)", // Helm - Tarnhelm - "[name] == skullcap && [quality] == unique # [itemallskills] == 1 && [itemmagicbonus] >= 25 # [tier] == 100000 + tierscore(item)", + "[name] == skullcap && [quality] == unique # [itemallskills] == 1 && [itemmagicbonus] >= 25 # [tier] == tierscore(item, 100000)", // Shield - "[type] == shield && [quality] >= magic # [paladinskills] == 2 && [allres] >= 16 # [tier] == 100000 + tierscore(item)", + "[type] == shield && [quality] >= magic # [paladinskills] == 2 && [allres] >= 16 # [tier] == tierscore(item, 100000)", // Rings - SoJ "[type] == ring && [quality] == unique # [itemmaxmanapercent] == 25 # [tier] == 100000", // Amulet - "[type] == amulet && [quality] >= magic # [paladinskills] == 2 && [allres] >= 16 # [tier] == 100000 + tierscore(item)", + "[type] == amulet && [quality] >= magic # [paladinskills] == 2 && [allres] >= 16 # [tier] == tierscore(item, 100000)", // Boots - Hsaru's Iron Heel "[name] == chainboots && [quality] == set # [frw] == 20 # [tier] == 100000", // Belt - Hsaru's Iron Stay @@ -53,19 +53,19 @@ const finalBuild = { ], expansionTiers: [ // Weapon - Heaven's Light - "[type] == scepter && [quality] == unique && [flag] != ethereal # [paladinskills] >= 2 && [enhanceddamage] >= 250 # [tier] == 100000 + tierscore(item)", + "[type] == scepter && [quality] == unique && [flag] != ethereal # [paladinskills] >= 2 && [enhanceddamage] >= 250 # [tier] == tierscore(item, 100000)", // Helm - Harlequin's Crest - "[name] == shako && [quality] == unique && [flag] != ethereal # [damageresist] == 10 # [tier] == 100000 + tierscore(item)", + "[name] == shako && [quality] == unique && [flag] != ethereal # [damageresist] == 10 # [tier] == tierscore(item, 100000)", // Belt - TGods - "[name] == warbelt && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 160 # [tier] == 110000 + tierscore(item)", + "[name] == warbelt && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 160 # [tier] == tierscore(item, 110000)", // Boots - Gore Rider - "[name] == warboots && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 160 # [tier] == 110000 + tierscore(item)", + "[name] == warboots && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 160 # [tier] == tierscore(item, 110000)", // Armor - Enigma "[type] == armor && [flag] != ethereal && [flag] == runeword # [itemallskills] == 2 # [tier] == 100000", // Final Shield - Exile "[type] == auricshields && [flag] == runeword # [defianceaura] >= 13 # [tier] == 110000", // Shield - HoZ - "[name] == gildedshield && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 150 # [tier] == 50000 + tierscore(item)", + "[name] == gildedshield && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 150 # [tier] == tierscore(item, 50000)", // Gloves - Laying of Hand's "[name] == bramblemitts && [quality] == set && [flag] != ethereal # [ias] >= 20 # [tier] == 110000", // Amulet - Highlords diff --git a/libs/SoloPlay/BuildFiles/paladin/paladin.SancdreamerBuild.js b/libs/SoloPlay/BuildFiles/paladin/paladin.SancdreamerBuild.js index 6b2d57b8..525501d5 100644 --- a/libs/SoloPlay/BuildFiles/paladin/paladin.SancdreamerBuild.js +++ b/libs/SoloPlay/BuildFiles/paladin/paladin.SancdreamerBuild.js @@ -40,9 +40,9 @@ const finalBuild = { // Helm - Dream "[type] == helm && [flag] == runeword # [holyshockaura] >= 15 # [tier] == 110000", // Belt - Verdungos - "[name] == mithrilcoil && [quality] == unique && [flag] != ethereal # [damageresist] == 15 # [tier] == 110000 + tierscore(item)", + "[name] == mithrilcoil && [quality] == unique && [flag] != ethereal # [damageresist] == 15 # [tier] == tierscore(item, 110000)", // Boots - Gore Rider - "[name] == warboots && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 160 # [tier] == 110000 + tierscore(item)", + "[name] == warboots && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 160 # [tier] == tierscore(item, 110000)", // Armor - CoH "[type] == armor && [flag] == runeword && [flag] != ethereal # [fireresist] == 65 && [hpregen] == 7 # [tier] == 100000", // Shield - Dream diff --git a/libs/SoloPlay/BuildFiles/paladin/paladin.SmiterBuild.js b/libs/SoloPlay/BuildFiles/paladin/paladin.SmiterBuild.js index 7bf3905a..14aa4cc2 100644 --- a/libs/SoloPlay/BuildFiles/paladin/paladin.SmiterBuild.js +++ b/libs/SoloPlay/BuildFiles/paladin/paladin.SmiterBuild.js @@ -31,17 +31,17 @@ const finalBuild = { // Weapon - Grief "[type] == sword && [flag] == runeword # [ias] >= 30 && [itemdeadlystrike] == 20 && [passivepoispierce] >= 20 # [tier] == 100000", // Helm - GFace - "[name] == wingedhelm && [quality] == set && [flag] != ethereal # [fhr] >= 30 # [tier] == 100000 + tierscore(item)", + "[name] == wingedhelm && [quality] == set && [flag] != ethereal # [fhr] >= 30 # [tier] == tierscore(item, 100000)", // Belt - Tgods - "[name] == warbelt && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 160 # [tier] == 100000 + tierscore(item)", + "[name] == warbelt && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 160 # [tier] == tierscore(item, 100000)", // Boots - Goblin Toes - "[name] == lightplatedboots && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 50 # [tier] == 100000 + tierscore(item)", + "[name] == lightplatedboots && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 50 # [tier] == tierscore(item, 100000)", // Armor - Enigma "[type] == armor && [flag] != ethereal && [flag] == runeword # [itemallskills] == 2 # [tier] == 100000", // Shield - HoZ - "[name] == gildedshield && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 150 # [tier] == 100000 + tierscore(item)", + "[name] == gildedshield && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 150 # [tier] == tierscore(item, 100000)", // Gloves - Drac's - "[name] == vampirebonegloves && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 100 && [strength] >= 12 && [lifeleech] >= 9 # [tier] == 100000 + tierscore(item)", + "[name] == vampirebonegloves && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 100 && [strength] >= 12 && [lifeleech] >= 9 # [tier] == tierscore(item, 100000)", // Amulet - Highlords "[type] == amulet && [quality] == unique # [lightresist] == 35 # [tier] == 100000", // Final Rings - Perfect Raven Frost & Bul-Kathos' Wedding Band diff --git a/libs/SoloPlay/BuildFiles/paladin/paladin.TorchadinBuild.js b/libs/SoloPlay/BuildFiles/paladin/paladin.TorchadinBuild.js index ebe21a74..b34b9b07 100644 --- a/libs/SoloPlay/BuildFiles/paladin/paladin.TorchadinBuild.js +++ b/libs/SoloPlay/BuildFiles/paladin/paladin.TorchadinBuild.js @@ -37,13 +37,13 @@ const finalBuild = { // Temporary Weapon - Voice of Reason "[type] == sword && [flag] == runeword # [passivecoldpierce] >= 24 # [tier] == 102500", // Final Helm - Upp'ed Vamp Gaze - "[name] == bonevisage && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 100 && [lifeleech] >= 6 # [tier] == 100000 + tierscore(item)", + "[name] == bonevisage && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 100 && [lifeleech] >= 6 # [tier] == tierscore(item, 100000)", // Helm - Vamp Gaze - "[name] == grimhelm && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 100 # [tier] == 100000 + tierscore(item)", + "[name] == grimhelm && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 100 # [tier] == tierscore(item, 100000)", // Belt - TGods - "[name] == warbelt && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 160 # [tier] == 110000 + tierscore(item)", + "[name] == warbelt && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 160 # [tier] == tierscore(item, 110000)", // Boots - Gore Rider - "[name] == warboots && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 160 # [tier] == 110000 + tierscore(item)", + "[name] == warboots && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 160 # [tier] == tierscore(item, 110000)", // Armor - Dragon "[type] == armor && [flag] != ethereal && [flag] == runeword # [holyfireaura] >= 14 # [tier] == 110000", // Shield - Exile diff --git a/libs/SoloPlay/BuildFiles/paladin/paladin.ZealerBuild.js b/libs/SoloPlay/BuildFiles/paladin/paladin.ZealerBuild.js index 8763baa9..922c8bfe 100644 --- a/libs/SoloPlay/BuildFiles/paladin/paladin.ZealerBuild.js +++ b/libs/SoloPlay/BuildFiles/paladin/paladin.ZealerBuild.js @@ -33,19 +33,19 @@ const finalBuild = { // Weapon - Grief "[type] == sword && [flag] == runeword # [ias] >= 30 && [itemdeadlystrike] == 20 && [passivepoispierce] >= 20 # [tier] == 100000", // Final Helm - Upp'ed Vamp Gaze - "[name] == bonevisage && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 100 && [lifeleech] >= 6 # [tier] == 100000 + tierscore(item)", + "[name] == bonevisage && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 100 && [lifeleech] >= 6 # [tier] == tierscore(item, 100000)", // Helm -Vamp Gaze - "[name] == grimhelm && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 100 # [tier] == 100000 + tierscore(item)", + "[name] == grimhelm && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 100 # [tier] == tierscore(item, 100000)", // Belt - TGods - "[name] == warbelt && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 160 # [tier] == 110000 + tierscore(item)", + "[name] == warbelt && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 160 # [tier] == tierscore(item, 110000)", // Boots - Gore Rider - "[name] == warboots && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 160 # [tier] == 110000 + tierscore(item)", + "[name] == warboots && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 160 # [tier] == tierscore(item, 110000)", // Armor - Fortitude "[type] == armor && [flag] != ethereal && [flag] == runeword # [enhanceddefense] >= 200 && [enhanceddamage] >= 300 # [tier] == 110000", // Final Shield - Exile "[type] == auricshields && [flag] == runeword # [defianceaura] >= 13 # [tier] == 110000", // Shield - HoZ - "[name] == gildedshield && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 150 # [tier] == 100000 + tierscore(item)", + "[name] == gildedshield && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 150 # [tier] == tierscore(item, 100000)", // Gloves - Laying of Hand's "[name] == bramblemitts && [quality] == set && [flag] != ethereal # [ias] >= 20 # [tier] == 110000", // Amulet - Highlords diff --git a/libs/SoloPlay/BuildFiles/sorceress/sorceress.BlizzballerBuild.js b/libs/SoloPlay/BuildFiles/sorceress/sorceress.BlizzballerBuild.js index 36b840f2..117caae5 100644 --- a/libs/SoloPlay/BuildFiles/sorceress/sorceress.BlizzballerBuild.js +++ b/libs/SoloPlay/BuildFiles/sorceress/sorceress.BlizzballerBuild.js @@ -37,27 +37,27 @@ const finalBuild = { ], autoEquipTiers: [ // autoequip final gear // Weapon - Tals Orb - "[name] == swirlingcrystal && [quality] == set && [flag] != ethereal # [skilllightningmastery]+[skillfiremastery]+[skillcoldmastery] >= 3 # [tier] == 100000 + tierscore(item)", + "[name] == swirlingcrystal && [quality] == set && [flag] != ethereal # [skilllightningmastery]+[skillfiremastery]+[skillcoldmastery] >= 3 # [tier] == tierscore(item, 100000)", // Helmet - Tals Mask - "[name] == deathmask && [quality] == set && [flag] != ethereal # [coldresist]+[lightresist]+[fireresist]+[poisonresist] >= 60 # [tier] == 100000 + tierscore(item)", + "[name] == deathmask && [quality] == set && [flag] != ethereal # [coldresist]+[lightresist]+[fireresist]+[poisonresist] >= 60 # [tier] == tierscore(item, 100000)", // Belt - Tals Belt - "[name] == meshbelt && [quality] == set && [flag] != ethereal # [itemmagicbonus] >= 10 # [tier] == 100000 + tierscore(item)", + "[name] == meshbelt && [quality] == set && [flag] != ethereal # [itemmagicbonus] >= 10 # [tier] == tierscore(item, 100000)", // Final Boots - Sandstorm Treks - "[name] == scarabshellboots && [quality] == unique # [strength]+[vitality] >= 20 # [tier] == 100000 + tierscore(item)", + "[name] == scarabshellboots && [quality] == unique # [strength]+[vitality] >= 20 # [tier] == tierscore(item, 100000)", // Boots - War Traveler - "[name] == battleboots && [quality] == unique && [flag] != ethereal # [itemmagicbonus] >= 50 # [tier] == 5000 + tierscore(item)", + "[name] == battleboots && [quality] == unique && [flag] != ethereal # [itemmagicbonus] >= 50 # [tier] == tierscore(item, 5000)", // Armor - Tals Armor "[name] == lacqueredplate && [quality] == set # [coldresist] >= 1 # [tier] == 100000", // Final Shield - Sanctuary "[name] == hyperion && [flag] == runeword # [fhr] >= 20 && [enhanceddefense] >= 130 && [fireresist] >= 50 # [tier] == 100000", // Shield - Mosers - "[name] == roundshield && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 180 # [tier] == 50000 + tierscore(item)", + "[name] == roundshield && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 180 # [tier] == tierscore(item, 50000)", // Final Gloves - Perfect Upp'ed Magefist - "[name] == battlegauntlets && [quality] == unique && [flag] != ethereal # [enhanceddefense] == 30 && [addfireskills] == 1 # [tier] == 110000", + "[name] == battlegauntlets && [quality] == unique && [flag] != ethereal # [enhanceddefense] == 30 && [addfireskills] == 1 && [defense] == 71 # [tier] == 110000", // Gloves - Upp'ed Magefist - "[name] == battlegauntlets && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 20 && [addfireskills] == 1 # [tier] == 100000 + tierscore(item)", + "[name] == battlegauntlets && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 20 && [addfireskills] == 1 # [tier] == tierscore(item, 100000)", // Gloves - Magefist - "[name] == lightgauntlets && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 20 && [addfireskills] == 1 # [tier] == 100000 + tierscore(item)", + "[name] == lightgauntlets && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 20 && [addfireskills] == 1 # [tier] == tierscore(item, 100000)", // Amulet - Tals Ammy "[name] == amulet && [quality] == set # [lightresist] == 33 # [tier] == 100000", // Final Rings - Perfect Raven Frost & Nagelring @@ -68,7 +68,7 @@ const finalBuild = { // Switch - CTA "[minimumsockets] >= 5 && [flag] == runeword # [plusskillbattleorders] >= 1 # [secondarytier] == 100000", // Switch Shield - 1+ all skill - "[type] == shield # [itemallskills] >= 1 # [secondarytier] == 100000 + tierscore(item)", + "[type] == shield # [itemallskills] >= 1 # [secondarytier] == tierscore(item, 100000)", // Merc Armor - Fortitude "[type] == armor && [flag] == runeword # [enhanceddefense] >= 200 && [enhanceddamage] >= 300 # [merctier] == 100000", // Merc Final Helmet - Eth Andy's diff --git a/libs/SoloPlay/BuildFiles/sorceress/sorceress.BlovaBuild.js b/libs/SoloPlay/BuildFiles/sorceress/sorceress.BlovaBuild.js index b2c64a4b..c984841f 100644 --- a/libs/SoloPlay/BuildFiles/sorceress/sorceress.BlovaBuild.js +++ b/libs/SoloPlay/BuildFiles/sorceress/sorceress.BlovaBuild.js @@ -35,25 +35,25 @@ const finalBuild = { // Weapon - HotO "[type] == mace && [flag] == runeword # [itemallskills] == 3 # [tier] == 100000", // Helmet - Griffons - "[name] == diadem && [quality] == unique && [flag] != ethereal # [fcr] == 25 # [tier] == 100000 + tierscore(item)", + "[name] == diadem && [quality] == unique && [flag] != ethereal # [fcr] == 25 # [tier] == tierscore(item, 100000)", // Belt - Arach's - "[name] == spiderwebsash && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 90 # [tier] == 100000 + tierscore(item)", + "[name] == spiderwebsash && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 90 # [tier] == tierscore(item, 100000)", // Final Boots - Sandstorm Treks - "[name] == scarabshellboots && [quality] == unique # [strength]+[vitality] >= 20 # [tier] == 100000 + tierscore(item)", + "[name] == scarabshellboots && [quality] == unique # [strength]+[vitality] >= 20 # [tier] == tierscore(item, 100000)", // Boots - War Traveler - "[name] == battleboots && [quality] == unique && [flag] != ethereal # [itemmagicbonus] >= 50 # [tier] == 5000 + tierscore(item)", + "[name] == battleboots && [quality] == unique && [flag] != ethereal # [itemmagicbonus] >= 50 # [tier] == tierscore(item, 5000)", // Armor - CoH "[type] == armor && [flag] == runeword && [flag] != ethereal # [fireresist] == 65 && [hpregen] == 7 # [tier] == 100000", // Shield - Spirit - "[type] == shield # [fcr] >= 25 && [maxmana] >= 89 # [tier] == 100000 + tierscore(item)", + "[type] == shield # [fcr] >= 25 && [maxmana] >= 89 # [tier] == tierscore(item, 100000)", // Final Gloves - Perfect Upp'ed Magefist - "[name] == battlegauntlets && [quality] == unique && [flag] != ethereal # [enhanceddefense] == 30 && [addfireskills] == 1 # [tier] == 110000", + "[name] == battlegauntlets && [quality] == unique && [flag] != ethereal # [enhanceddefense] == 30 && [addfireskills] == 1 && [defense] == 71 # [tier] == 110000", // Gloves - Upp'ed Magefist - "[name] == battlegauntlets && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 20 && [addfireskills] == 1 # [tier] == 100000 + tierscore(item)", + "[name] == battlegauntlets && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 20 && [addfireskills] == 1 # [tier] == tierscore(item, 100000)", // Gloves - Magefist - "[name] == lightgauntlets && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 20 && [addfireskills] == 1 # [tier] == 100000 + tierscore(item)", + "[name] == lightgauntlets && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 20 && [addfireskills] == 1 # [tier] == tierscore(item, 100000)", // Amulet - Maras - "[type] == amulet && [quality] == unique # [strength] == 5 && [coldresist] >= 30 # [tier] == 100000 + tierscore(item)", + "[type] == amulet && [quality] == unique # [strength] == 5 && [coldresist] >= 30 # [tier] == tierscore(item, 100000)", // Final Rings - SoJ & Perfect Bul-Kathos' Wedding Band "[type] == ring && [quality] == unique # [itemmaxmanapercent] == 25 # [tier] == 100000", "[name] == ring && [quality] == unique # [maxstamina] == 50 && [lifeleech] == 5 # [tier] == 100000", @@ -64,7 +64,7 @@ const finalBuild = { // Switch Final Shield - Spirit "[name] == monarch && [flag] == runeword # [fcr] >= 25 && [maxmana] >= 89 # [secondarytier] == 110000", // Switch Temporary Shield - 1+ all skill - "[type] == shield # [itemallskills] >= 1 # [secondarytier] == 50000 + tierscore(item)", + "[type] == shield # [itemallskills] >= 1 # [secondarytier] == tierscore(item, 50000)", // Merc Armor - Fortitude "[type] == armor && [flag] == runeword # [enhanceddefense] >= 200 && [enhanceddamage] >= 300 # [merctier] == 100000", // Merc Final Helmet - Eth Andy's diff --git a/libs/SoloPlay/BuildFiles/sorceress/sorceress.ColdBuild.js b/libs/SoloPlay/BuildFiles/sorceress/sorceress.ColdBuild.js index 2905228e..528c7410 100644 --- a/libs/SoloPlay/BuildFiles/sorceress/sorceress.ColdBuild.js +++ b/libs/SoloPlay/BuildFiles/sorceress/sorceress.ColdBuild.js @@ -33,27 +33,27 @@ const finalBuild = { ], autoEquipTiers: [ // autoequip final gear // Weapon - Tals Orb - "[name] == swirlingcrystal && [quality] == set && [flag] != ethereal # [skilllightningmastery]+[skillfiremastery]+[skillcoldmastery] >= 3 # [tier] == 100000 + tierscore(item)", + "[name] == swirlingcrystal && [quality] == set && [flag] != ethereal # [skilllightningmastery]+[skillfiremastery]+[skillcoldmastery] >= 3 # [tier] == tierscore(item, 100000)", // Helmet - Tals Mask - "[name] == deathmask && [quality] == set && [flag] != ethereal # [coldresist]+[lightresist]+[fireresist]+[poisonresist] >= 60 # [tier] == 100000 + tierscore(item)", + "[name] == deathmask && [quality] == set && [flag] != ethereal # [coldresist]+[lightresist]+[fireresist]+[poisonresist] >= 60 # [tier] == tierscore(item, 100000)", // Belt - Tals Belt - "[name] == meshbelt && [quality] == set && [flag] != ethereal # [itemmagicbonus] >= 10 # [tier] == 100000 + tierscore(item)", + "[name] == meshbelt && [quality] == set && [flag] != ethereal # [itemmagicbonus] >= 10 # [tier] == tierscore(item, 100000)", // Final Boots - Sandstorm Treks - "[name] == scarabshellboots && [quality] == unique # [strength]+[vitality] >= 20 # [tier] == 100000 + tierscore(item)", + "[name] == scarabshellboots && [quality] == unique # [strength]+[vitality] >= 20 # [tier] == tierscore(item, 100000)", // Boots - War Traveler - "[name] == battleboots && [quality] == unique && [flag] != ethereal # [itemmagicbonus] >= 50 # [tier] == 5000 + tierscore(item)", + "[name] == battleboots && [quality] == unique && [flag] != ethereal # [itemmagicbonus] >= 50 # [tier] == tierscore(item, 5000)", // Armor - Tals Armor "[name] == lacqueredplate && [quality] == set # [coldresist] >= 1 # [tier] == 100000", // Final Shield - Sanctuary "[name] == hyperion && [flag] == runeword # [fhr] >= 20 && [enhanceddefense] >= 130 && [fireresist] >= 50 # [tier] == 100000", // Shield - Mosers - "[name] == roundshield && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 180 # [tier] == 50000 + tierscore(item)", + "[name] == roundshield && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 180 # [tier] == tierscore(item, 50000)", // Final Gloves - Perfect Upp'ed Magefist - "[name] == battlegauntlets && [quality] == unique && [flag] != ethereal # [enhanceddefense] == 30 && [addfireskills] == 1 # [tier] == 110000", + "[name] == battlegauntlets && [quality] == unique && [flag] != ethereal # [enhanceddefense] == 30 && [addfireskills] == 1 && [defense] == 71 # [tier] == 110000", // Gloves - Upp'ed Magefist - "[name] == battlegauntlets && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 20 && [addfireskills] == 1 # [tier] == 100000 + tierscore(item)", + "[name] == battlegauntlets && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 20 && [addfireskills] == 1 # [tier] == tierscore(item, 100000)", // Gloves - Magefist - "[name] == lightgauntlets && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 20 && [addfireskills] == 1 # [tier] == 100000 + tierscore(item)", + "[name] == lightgauntlets && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 20 && [addfireskills] == 1 # [tier] == tierscore(item, 100000)", // Amulet - Tals Ammy "[name] == amulet && [quality] == set # [lightresist] == 33 # [tier] == 100000", // Final Rings - Perfect Raven Frost & Nagelring @@ -64,7 +64,7 @@ const finalBuild = { // Switch - CTA "[minimumsockets] >= 5 && [flag] == runeword # [plusskillbattleorders] >= 1 # [secondarytier] == 100000", // Switch Shield - 1+ all skill - "[type] == shield # [itemallskills] >= 1 # [secondarytier] == 100000 + tierscore(item)", + "[type] == shield # [itemallskills] >= 1 # [secondarytier] == tierscore(item, 100000)", // Merc Armor - Fortitude "[type] == armor && [flag] == runeword # [enhanceddefense] >= 200 && [enhanceddamage] >= 300 # [merctier] == 100000", // Merc Final Helmet - Eth Andy's diff --git a/libs/SoloPlay/BuildFiles/sorceress/sorceress.EsorbBuild.js b/libs/SoloPlay/BuildFiles/sorceress/sorceress.EsorbBuild.js index 0fb039ba..6a1f1b65 100644 --- a/libs/SoloPlay/BuildFiles/sorceress/sorceress.EsorbBuild.js +++ b/libs/SoloPlay/BuildFiles/sorceress/sorceress.EsorbBuild.js @@ -38,43 +38,43 @@ const finalBuild = { // Weapon - Spectral Shard "[name] == blade && [quality] == unique # [fcr] == 20 && [allres] == 10 # [tier] == 100000", // Helm - Tarnhelm - "[name] == skullcap && [quality] == unique # [itemallskills] == 1 && [itemmagicbonus] >= 25 # [tier] == 100000 + tierscore(item)", + "[name] == skullcap && [quality] == unique # [itemallskills] == 1 && [itemmagicbonus] >= 25 # [tier] == tierscore(item, 100000)", // Shield - "[type] == shield && [quality] >= magic # [sorceressskills] == 2 && [allres] >= 16 # [tier] == 100000 + tierscore(item)", + "[type] == shield && [quality] >= magic # [sorceressskills] == 2 && [allres] >= 16 # [tier] == tierscore(item, 100000)", // Rings - SoJ "[type] == ring && [quality] == unique # [itemmaxmanapercent] == 25 # [tier] == 100000", // Amulet - "[type] == amulet && [quality] >= magic # [sorceressskills] == 2 && [fcr] == 10 # [tier] == 100000 + tierscore(item)", + "[type] == amulet && [quality] >= magic # [sorceressskills] == 2 && [fcr] == 10 # [tier] == tierscore(item, 100000)", // Boots - "[type] == boots && [quality] >= magic # [frw] >= 20 && [fhr] == 10 && [coldresist]+[lightresist] >= 10 # [tier] == 100000 + tierscore(item)", + "[type] == boots && [quality] >= magic # [frw] >= 20 && [fhr] == 10 && [coldresist]+[lightresist] >= 10 # [tier] == tierscore(item, 100000)", // Belt - "[type] == belt && [quality] >= magic # [fhr] >= 20 && [maxhp] >= 40 && [fireresist]+[lightresist] >= 20 # [tier] == 100000 + tierscore(item)", + "[type] == belt && [quality] >= magic # [fhr] >= 20 && [maxhp] >= 40 && [fireresist]+[lightresist] >= 20 # [tier] == tierscore(item, 100000)", // Gloves - Magefist - "[name] == lightgauntlets && [quality] == unique # [fcr] >= 20 && [addfireskills] == 1 # [tier] == 100000 + tierscore(item)", + "[name] == lightgauntlets && [quality] == unique # [fcr] >= 20 && [addfireskills] == 1 # [tier] == tierscore(item, 100000)", ], expansionTiers: [ // Weapon - Tals Orb - "[name] == swirlingcrystal && [quality] == set && [flag] != ethereal # [skilllightningmastery]+[skillfiremastery]+[skillcoldmastery] >= 3 # [tier] == 100000 + tierscore(item)", + "[name] == swirlingcrystal && [quality] == set && [flag] != ethereal # [skilllightningmastery]+[skillfiremastery]+[skillcoldmastery] >= 3 # [tier] == tierscore(item, 100000)", // Helmet - Tals Mask - "[name] == deathmask && [quality] == set && [flag] != ethereal # [coldresist]+[lightresist]+[fireresist]+[poisonresist] >= 60 # [tier] == 100000 + tierscore(item)", + "[name] == deathmask && [quality] == set && [flag] != ethereal # [coldresist]+[lightresist]+[fireresist]+[poisonresist] >= 60 # [tier] == tierscore(item, 100000)", // Belt - Tals Belt - "[name] == meshbelt && [quality] == set && [flag] != ethereal # [itemmagicbonus] >= 10 # [tier] == 100000 + tierscore(item)", + "[name] == meshbelt && [quality] == set && [flag] != ethereal # [itemmagicbonus] >= 10 # [tier] == tierscore(item, 100000)", // Final Boots - Sandstorm Treks - "[name] == scarabshellboots && [quality] == unique # [strength]+[vitality] >= 20 # [tier] == 100000 + tierscore(item)", + "[name] == scarabshellboots && [quality] == unique # [strength]+[vitality] >= 20 # [tier] == tierscore(item, 100000)", // Boots - War Traveler - "[name] == battleboots && [quality] == unique && [flag] != ethereal # [itemmagicbonus] >= 50 # [tier] == 5000 + tierscore(item)", + "[name] == battleboots && [quality] == unique && [flag] != ethereal # [itemmagicbonus] >= 50 # [tier] == tierscore(item, 5000)", // Armor - Tals Armor "[name] == lacqueredplate && [quality] == set # [coldresist] >= 1 # [tier] == 100000", // Final Shield - Sanctuary "[name] == hyperion && [flag] == runeword # [fhr] >= 20 && [enhanceddefense] >= 130 && [fireresist] >= 50 # [tier] == 100000", // Shield - Mosers - "[name] == roundshield && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 180 # [tier] == 50000 + tierscore(item)", + "[name] == roundshield && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 180 # [tier] == tierscore(item, 50000)", // Final Gloves - Perfect Upp'ed Magefist - "[name] == battlegauntlets && [quality] == unique && [flag] != ethereal # [enhanceddefense] == 30 && [addfireskills] == 1 # [tier] == 110000", + "[name] == battlegauntlets && [quality] == unique && [flag] != ethereal # [enhanceddefense] == 30 && [addfireskills] == 1 && [defense] == 71 # [tier] == 110000", // Gloves - Upp'ed Magefist - "[name] == battlegauntlets && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 20 && [addfireskills] == 1 # [tier] == 100000 + tierscore(item)", + "[name] == battlegauntlets && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 20 && [addfireskills] == 1 # [tier] == tierscore(item, 100000)", // Gloves - Magefist - "[name] == lightgauntlets && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 20 && [addfireskills] == 1 # [tier] == 100000 + tierscore(item)", + "[name] == lightgauntlets && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 20 && [addfireskills] == 1 # [tier] == tierscore(item, 100000)", // Amulet - Tals Ammy "[name] == amulet && [quality] == set # [lightresist] == 33 # [tier] == 100000", // Final Rings - Perfect Raven Frost & Nagelring @@ -85,7 +85,7 @@ const finalBuild = { // Switch - CTA "[minimumsockets] >= 5 && [flag] == runeword # [plusskillbattleorders] >= 1 # [secondarytier] == 100000", // Switch Shield - 1+ all skill - "[type] == shield # [itemallskills] >= 1 # [secondarytier] == 100000 + tierscore(item)", + "[type] == shield # [itemallskills] >= 1 # [secondarytier] == tierscore(item, 100000)", // Merc Armor - Fortitude "[type] == armor && [flag] == runeword # [enhanceddefense] >= 200 && [enhanceddamage] >= 300 # [merctier] == 100000", // Merc Final Helmet - Eth Andy's diff --git a/libs/SoloPlay/BuildFiles/sorceress/sorceress.LightningBuild.js b/libs/SoloPlay/BuildFiles/sorceress/sorceress.LightningBuild.js index 29168b57..f8af58d1 100644 --- a/libs/SoloPlay/BuildFiles/sorceress/sorceress.LightningBuild.js +++ b/libs/SoloPlay/BuildFiles/sorceress/sorceress.LightningBuild.js @@ -35,37 +35,37 @@ const finalBuild = { // Weapon - HotO "[type] == mace && [flag] == runeword # [fcr] == 40 # [tier] == 100000", // Helmet - Harlequin's Crest - "[name] == shako && [quality] == unique && [flag] != ethereal # [damageresist] >= 10 # [tier] == 100000 + tierscore(item)", + "[name] == shako && [quality] == unique && [flag] != ethereal # [damageresist] >= 10 # [tier] == tierscore(item, 100000)", // Belt - Arach's - "[name] == spiderwebsash && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 90 # [tier] == 100000 + tierscore(item)", + "[name] == spiderwebsash && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 90 # [tier] == tierscore(item, 100000)", // Boots - "[name] == battleboots && [quality] == unique && [flag] != ethereal # [itemmagicbonus] >= 50 # [tier] == 5000 + tierscore(item)", // War Traveler - "[name] == scarabshellboots && [quality] == unique # [strength]+[vitality] >= 20 # [tier] == 100000 + tierscore(item)", // Sandstorm Treks + "[name] == battleboots && [quality] == unique && [flag] != ethereal # [itemmagicbonus] >= 50 # [tier] == tierscore(item, 5000)", // War Traveler + "[name] == scarabshellboots && [quality] == unique # [strength]+[vitality] >= 20 # [tier] == tierscore(item, 100000)", // Sandstorm Treks // Armor - CoH "[type] == armor && [flag] == runeword && [flag] != ethereal # [fireresist] == 65 && [hpregen] == 7 # [tier] == 100000", // Shield - Spirit - "[type] == shield # [fcr] >= 25 && [maxmana] >= 89 # [tier] == 100000 + tierscore(item)", + "[type] == shield # [fcr] >= 25 && [maxmana] >= 89 # [tier] == tierscore(item, 100000)", // Final Gloves - Perfect 2x Upp'ed Magefist "[name] == crusadergauntlets && [quality] == unique && [flag] != ethereal # [enhanceddefense] == 30 && [addfireskills] == 1 # [tier] == 110000", // Gloves - 2x Upp'ed Magefist - "[name] == crusadergauntlets && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 20 && [addfireskills] == 1 # [tier] == 100000 + tierscore(item)", + "[name] == crusadergauntlets && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 20 && [addfireskills] == 1 # [tier] == tierscore(item, 100000)", // Gloves - Upp'ed Magefist - "[name] == battlegauntlets && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 20 && [addfireskills] == 1 # [tier] == 100000 + tierscore(item)", + "[name] == battlegauntlets && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 20 && [addfireskills] == 1 # [tier] == tierscore(item, 100000)", // Gloves - Magefist - "[name] == lightgauntlets && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 20 && [addfireskills] == 1 # [tier] == 100000 + tierscore(item)", + "[name] == lightgauntlets && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 20 && [addfireskills] == 1 # [tier] == tierscore(item, 100000)", // Amulet - Maras - "[type] == amulet && [quality] == unique # [strength] == 5 && [coldresist] >= 30 # [tier] == 100000 + tierscore(item)", + "[type] == amulet && [quality] == unique # [strength] == 5 && [coldresist] >= 30 # [tier] == tierscore(item, 100000)", // Final Rings - SoJ & Perfect Wisp "[type] == ring && [quality] == unique # [itemmaxmanapercent] == 25 # [tier] == 100000", - "[name] == ring && [quality] == unique # [itemabsorblightpercent] == 20 # [tier] == 100000 + tierscore(item)", + "[name] == ring && [quality] == unique # [itemabsorblightpercent] == 20 # [tier] == tierscore(item, 100000)", // Rings - Wisp - "[name] == ring && [quality] == unique # [itemabsorblightpercent] >= 10 # [tier] == 90000 + tierscore(item)", + "[name] == ring && [quality] == unique # [itemabsorblightpercent] >= 10 # [tier] == tierscore(item, 90000)", // Switch - CTA "[minimumsockets] >= 5 && [flag] == runeword # [plusskillbattleorders] >= 1 # [secondarytier] == 100000", // Switch Final Shield - Spirit "[name] == monarch && [flag] == runeword # [fcr] >= 25 && [maxmana] >= 89 # [secondarytier] == 110000", // Switch Temporary Shield - 1+ all skill - "[type] == shield # [itemallskills] >= 1 # [secondarytier] == 50000 + tierscore(item)", + "[type] == shield # [itemallskills] >= 1 # [secondarytier] == tierscore(item, 50000)", // Merc Armor - Fortitude "[type] == armor && [flag] == runeword # [enhanceddefense] >= 200 && [enhanceddamage] >= 300 # [merctier] == 100000", // Merc Final Helmet - Eth Andy's diff --git a/libs/SoloPlay/BuildFiles/sorceress/sorceress.MeteorbBuild.js b/libs/SoloPlay/BuildFiles/sorceress/sorceress.MeteorbBuild.js index de40d1c7..c74f30ed 100644 --- a/libs/SoloPlay/BuildFiles/sorceress/sorceress.MeteorbBuild.js +++ b/libs/SoloPlay/BuildFiles/sorceress/sorceress.MeteorbBuild.js @@ -41,43 +41,43 @@ const finalBuild = { // Weapon - Spectral Shard "[name] == blade && [quality] == unique # [fcr] == 20 && [allres] == 10 # [tier] == 100000", // Helm - Tarnhelm - "[name] == skullcap && [quality] == unique # [itemallskills] == 1 && [itemmagicbonus] >= 25 # [tier] == 100000 + tierscore(item)", + "[name] == skullcap && [quality] == unique # [itemallskills] == 1 && [itemmagicbonus] >= 25 # [tier] == tierscore(item, 100000)", // Shield - "[type] == shield && [quality] >= magic # [sorceressskills] == 2 && [allres] >= 16 # [tier] == 100000 + tierscore(item)", + "[type] == shield && [quality] >= magic # [sorceressskills] == 2 && [allres] >= 16 # [tier] == tierscore(item, 100000)", // Rings - SoJ "[type] == ring && [quality] == unique # [itemmaxmanapercent] == 25 # [tier] == 100000", // Amulet - "[type] == amulet && [quality] >= magic # [sorceressskills] == 2 && [fcr] == 10 # [tier] == 100000 + tierscore(item)", + "[type] == amulet && [quality] >= magic # [sorceressskills] == 2 && [fcr] == 10 # [tier] == tierscore(item, 100000)", // Boots - "[type] == boots && [quality] >= magic # [frw] >= 20 && [fhr] == 10 && [coldresist]+[lightresist] >= 10 # [tier] == 100000 + tierscore(item)", + "[type] == boots && [quality] >= magic # [frw] >= 20 && [fhr] == 10 && [coldresist]+[lightresist] >= 10 # [tier] == tierscore(item, 100000)", // Belt - "[type] == belt && [quality] >= magic # [fhr] >= 20 && [maxhp] >= 40 && [fireresist]+[lightresist] >= 20 # [tier] == 100000 + tierscore(item)", + "[type] == belt && [quality] >= magic # [fhr] >= 20 && [maxhp] >= 40 && [fireresist]+[lightresist] >= 20 # [tier] == tierscore(item, 100000)", // Gloves - Magefist - "[name] == lightgauntlets && [quality] == unique # [fcr] >= 20 && [addfireskills] == 1 # [tier] == 100000 + tierscore(item)", + "[name] == lightgauntlets && [quality] == unique # [fcr] >= 20 && [addfireskills] == 1 # [tier] == tierscore(item, 100000)", ], expansionTiers: [ // Weapon - Tals Orb - "[name] == swirlingcrystal && [quality] == set && [flag] != ethereal # [skilllightningmastery]+[skillfiremastery]+[skillcoldmastery] >= 3 # [tier] == 100000 + tierscore(item)", + "[name] == swirlingcrystal && [quality] == set && [flag] != ethereal # [skilllightningmastery]+[skillfiremastery]+[skillcoldmastery] >= 3 # [tier] == tierscore(item, 100000)", // Helmet - Tals Mask - "[name] == deathmask && [quality] == set && [flag] != ethereal # [coldresist]+[lightresist]+[fireresist]+[poisonresist] >= 60 # [tier] == 100000 + tierscore(item)", + "[name] == deathmask && [quality] == set && [flag] != ethereal # [coldresist]+[lightresist]+[fireresist]+[poisonresist] >= 60 # [tier] == tierscore(item, 100000)", // Belt - Tals Belt - "[name] == meshbelt && [quality] == set && [flag] != ethereal # [itemmagicbonus] >= 10 # [tier] == 100000 + tierscore(item)", + "[name] == meshbelt && [quality] == set && [flag] != ethereal # [itemmagicbonus] >= 10 # [tier] == tierscore(item, 100000)", // Final Boots - Sandstorm Treks - "[name] == scarabshellboots && [quality] == unique # [strength]+[vitality] >= 20 # [tier] == 100000 + tierscore(item)", + "[name] == scarabshellboots && [quality] == unique # [strength]+[vitality] >= 20 # [tier] == tierscore(item, 100000)", // Boots - War Traveler - "[name] == battleboots && [quality] == unique && [flag] != ethereal # [itemmagicbonus] >= 50 # [tier] == 5000 + tierscore(item)", + "[name] == battleboots && [quality] == unique && [flag] != ethereal # [itemmagicbonus] >= 50 # [tier] == tierscore(item, 5000)", // Armor - Tals Armor "[name] == lacqueredplate && [quality] == set # [coldresist] >= 1 # [tier] == 100000", // Final Shield - Sanctuary "[name] == hyperion && [flag] == runeword # [fhr] >= 20 && [enhanceddefense] >= 130 && [fireresist] >= 50 # [tier] == 100000", // Shield - Mosers - "[name] == roundshield && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 180 # [tier] == 50000 + tierscore(item)", + "[name] == roundshield && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 180 # [tier] == tierscore(item, 50000)", // Final Gloves - Perfect Upp'ed Magefist - "[name] == battlegauntlets && [quality] == unique && [flag] != ethereal # [enhanceddefense] == 30 && [addfireskills] == 1 # [tier] == 110000", + "[name] == battlegauntlets && [quality] == unique && [flag] != ethereal # [enhanceddefense] == 30 && [addfireskills] == 1 && [defense] == 71 # [tier] == 110000", // Gloves - Upp'ed Magefist - "[name] == battlegauntlets && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 20 && [addfireskills] == 1 # [tier] == 100000 + tierscore(item)", + "[name] == battlegauntlets && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 20 && [addfireskills] == 1 # [tier] == tierscore(item, 100000)", // Gloves - Magefist - "[name] == lightgauntlets && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 20 && [addfireskills] == 1 # [tier] == 100000 + tierscore(item)", + "[name] == lightgauntlets && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 20 && [addfireskills] == 1 # [tier] == tierscore(item, 100000)", // Amulet - Tals Ammy "[name] == amulet && [quality] == set # [lightresist] == 33 # [tier] == 100000", // Final Rings - Perfect Raven Frost & Nagelring @@ -88,7 +88,7 @@ const finalBuild = { // Switch - CTA "[minimumsockets] >= 5 && [flag] == runeword # [plusskillbattleorders] >= 1 # [secondarytier] == 100000", // Switch Shield - 1+ all skill - "[type] == shield # [itemallskills] >= 1 # [secondarytier] == 100000 + tierscore(item)", + "[type] == shield # [itemallskills] >= 1 # [secondarytier] == tierscore(item, 100000)", // Merc Final Armor - Fortitude "[type] == armor && [flag] == runeword # [enhanceddefense] >= 200 && [enhanceddamage] >= 300 # [merctier] == 100000", // Merc Final Helmet - Eth Andy's diff --git a/libs/SoloPlay/Functions/CubingOverrides.js b/libs/SoloPlay/Functions/CubingOverrides.js index 8d90e609..ea9a28ae 100644 --- a/libs/SoloPlay/Functions/CubingOverrides.js +++ b/libs/SoloPlay/Functions/CubingOverrides.js @@ -579,7 +579,7 @@ Cubing.emptyCube = function () { if (!Storage.Stash.MoveTo(items[0]) && !Storage.Inventory.MoveTo(items[0])) { Town.clearInventory(); - Town.sortInventory(); + me.sortInventory(); if (!Storage.Stash.MoveTo(items[0]) && !Storage.Inventory.MoveTo(items[0])) { return false; diff --git a/libs/SoloPlay/Functions/DynamicTiers.js b/libs/SoloPlay/Functions/DynamicTiers.js index 8e6e52d7..0e2fc94f 100644 --- a/libs/SoloPlay/Functions/DynamicTiers.js +++ b/libs/SoloPlay/Functions/DynamicTiers.js @@ -338,7 +338,7 @@ * @param {ItemUnit} item * @param {number} [bodyloc] */ - const tierscore = function (item, bodyloc) { + const tierscore = function (item, tier, bodyloc) { const buildInfo = Check.currentBuild(); const generalScore = () => { @@ -591,7 +591,7 @@ return ctcRating; }; - let tier = 1; // set to 1 for native autoequip to use items. + tier === undefined && (tier = 1); // set to 1 for native autoequip to use items. tier += generalScore(); tier += resistScore(); tier += buildScore(); @@ -606,6 +606,10 @@ } } + // if (tier > 1 && tier < NTIP.MAX_TIER && NTIP.CheckItem(item, NTIP.FinalGear.list) === Pickit.Result.WANTED) { + // tier += NTIP.MAX_TIER; + // } + return item.questItem ? -1 : Math.max(1, tier); }; diff --git a/libs/SoloPlay/Functions/ItemOverrides.js b/libs/SoloPlay/Functions/ItemOverrides.js index f61c0f9d..426609a3 100644 --- a/libs/SoloPlay/Functions/ItemOverrides.js +++ b/libs/SoloPlay/Functions/ItemOverrides.js @@ -26,7 +26,7 @@ Item.helmTypes = [ * @param {ItemUnit} item * @param {boolean} [skipSameItem] */ -Item.getQuantityOwned = function (item = undefined, skipSameItem = false) { +Item.getQuantityOwned = function (item, skipSameItem = false) { if (!item) return 0; return me.getItemsEx() @@ -118,7 +118,7 @@ Item.getEquipped = function (bodyLoc = -1) { itemType: item.itemType, quality: item.quality, tier: NTIP.GetTier(item), - tierScore: tierscore(item, bodyLoc), + tierScore: tierscore(item, 1, bodyLoc), secondarytier: NTIP.GetSecondaryTier(item), str: item.getStatEx(sdk.stats.Strength), dex: item.getStatEx(sdk.stats.Dexterity), @@ -175,11 +175,9 @@ Item.autoEquipCheck = function (item, basicCheck = false) { let equippedItem = Item.getEquipped(bodyLoc[i]); // rings are special - // first check if its a final item - can't use tierscore value as it doesn't count the bloated value - if (item.isInStorage && item.itemType === sdk.items.type.Ring - && ((tier < NTIP.MAX_TIER && !equippedItem.finalItem) || (equippedItem.finalItem && tier >= NTIP.MAX_TIER))) { + if (item.isInStorage && item.itemType === sdk.items.type.Ring) { // have to pass in the specific location - tier = tierscore(item, bodyLoc[i]); + tier = tierscore(item, 1, bodyLoc[i]); if (tier > equippedItem.tierScore) { return true; @@ -193,6 +191,12 @@ Item.autoEquipCheck = function (item, basicCheck = false) { if (Item.getEquipped(sdk.body.RightArm).twoHanded && tier < Item.getEquipped(sdk.body.RightArm).tier) return false; } + // lets double check that this is the highest tied'd item of this type in our storage + let betterItem = me.getItemsEx() + .filter(el => el.isInStorage && el.gid !== item.gid && Item.getBodyLoc(el).includes(bodyLoc[i])) + .some(el => NTIP.GetTier(el) > tier); + console.debug("Higher tier'd item? " + betterItem); + return true; } } @@ -263,7 +267,7 @@ Item.autoEquip = function (task = "") { // stash'd unid check let unids = items.filter(item => !item.identified && item.isInStash); - if (unids.length && Town.fillTome(sdk.items.TomeofIdentify, true)) { + if (unids.length && NPCAction.fillTome(sdk.items.TomeofIdentify, true)) { unids.forEach(item => Item.identify(item)); } @@ -289,10 +293,10 @@ Item.autoEquip = function (task = "") { for (let j = 0; j < bodyLoc.length; j += 1) { const equippedItem = Item.getEquipped(bodyLoc[j]); // rings are special - if (item.isInStorage && item.itemType === sdk.items.type.Ring && (tier < NTIP.MAX_TIER || (equippedItem.finalItem && tier >= NTIP.MAX_TIER))) { + if (item.isInStorage && item.itemType === sdk.items.type.Ring) { Item.identify(item); // have to pass in the specific location - tier = tierscore(item, bodyLoc[j]); + tier = tierscore(item, 1, bodyLoc[j]); if (tier > equippedItem.tierScore) { if (!runEquip(item, bodyLoc[j], tier)) { @@ -364,8 +368,8 @@ Item.equip = function (item, bodyLoc) { let checkScore = 0; switch (cursorItem.itemType) { case sdk.items.type.Ring: - checkScore = tierscore(cursorItem, bodyLoc); - if (checkScore > justEquipped.tierScore && !justEquipped.finalItem) { + checkScore = tierscore(cursorItem, 1, bodyLoc); + if (checkScore > justEquipped.tierScore) { console.debug("ROLLING BACK TO OLD ITEM BECAUSE IT WAS BETTER"); console.debug("OldItem: " + checkScore + " Just Equipped Item: " + Item.getEquipped(bodyLoc).tierScore); clickItemAndWait(sdk.clicktypes.click.item.Left, bodyLoc); @@ -492,7 +496,7 @@ Item.secondaryEquip = function (item, bodyLoc) { } if (Item.hasDependancy(item) && me.needRepair() && me.inTown) { - Town.repair(true); + NPCAction.repair(true); } return true; @@ -1583,7 +1587,7 @@ const AutoEquip = { return Item.autoEquipCheckMerc(item, true) || Item.autoEquipCheck(item, true) || Item.autoEquipCheckSecondary(item); }, - runAutoEquip: function () { + run: function () { Item.autoEquip(); Item.autoEquipSecondary(); Item.autoEquipCharms(); diff --git a/libs/SoloPlay/Functions/Me.js b/libs/SoloPlay/Functions/Me.js index 78492b32..47378bf9 100644 --- a/libs/SoloPlay/Functions/Me.js +++ b/libs/SoloPlay/Functions/Me.js @@ -204,7 +204,7 @@ me.cleanUpInvoPotions = function (beltSize) { */ const beltCapRef = [(0 + beltMax), (1 + beltMax), (2 + beltMax), (3 + beltMax)]; // check if we have empty belt slots - let needCleanup = Town.checkColumns(beltSize).some(slot => slot > 0); + let needCleanup = Storage.Belt.checkColumns(beltSize).some(slot => slot > 0); if (needCleanup) { const potsInInventory = me.getItemsEx() @@ -216,7 +216,7 @@ me.cleanUpInvoPotions = function (beltSize) { beltSize > 1 && potsInInventory.forEach(function (p) { let moved = false; // get free space in each slot of our belt - let freeSpace = Town.checkColumns(beltSize); + let freeSpace = Storage.Belt.checkColumns(beltSize); for (let i = 0; i < 4 && !moved; i += 1) { // checking that current potion matches what we want in our belt if (freeSpace[i] > 0 && p.code && p.code.startsWith(Config.BeltColumn[i])) { @@ -229,7 +229,7 @@ me.cleanUpInvoPotions = function (beltSize) { clickItemAndWait(sdk.clicktypes.click.item.ShiftLeft, p.x, p.y, p.location); } Misc.poll(() => !me.itemoncursor, 300, 30); - moved = Town.checkColumns(beltSize)[i] === freeSpace[i] - 1; + moved = Storage.Belt.checkColumns(beltSize)[i] === freeSpace[i] - 1; } Cubing.cursorCheck(); } @@ -296,6 +296,47 @@ me.needPotions = function () { return false; }; +me.clearBelt = function () { + let item = me.getItem(-1, sdk.items.mode.inBelt); + let clearList = []; + + if (item) { + do { + switch (item.itemType) { + case sdk.items.type.HealingPotion: + if (Config.BeltColumn[item.x % 4] !== "hp") { + clearList.push(copyUnit(item)); + } + + break; + case sdk.items.type.ManaPotion: + if (Config.BeltColumn[item.x % 4] !== "mp") { + clearList.push(copyUnit(item)); + } + + break; + case sdk.items.type.RejuvPotion: + if (Config.BeltColumn[item.x % 4] !== "rv") { + clearList.push(copyUnit(item)); + } + + break; + case sdk.items.type.StaminaPotion: + case sdk.items.type.AntidotePotion: + case sdk.items.type.ThawingPotion: + clearList.push(copyUnit(item)); + } + } while (item.getNext()); + + while (clearList.length > 0) { + clearList.shift().interact(); + delay(200); + } + } + + return true; +}; + me.getIdTool = function () { let items = me.getItemsEx().filter((i) => i.isInInventory && [sdk.items.ScrollofIdentify, sdk.items.TomeofIdentify].includes(i.classid)); let scroll = items.find((i) => i.isInInventory && i.classid === sdk.items.ScrollofIdentify); @@ -488,3 +529,10 @@ me.needMerc = function () { // In case we never had a merc and Config.UseMerc is still set to true for some odd reason return true; }; + +me.sortInventory = function () { + return Storage.Inventory.SortItems( + SetUp.sortSettings.ItemsSortedFromLeft, + SetUp.sortSettings.ItemsSortedFromRight + ); +}; diff --git a/libs/SoloPlay/Functions/Mercenary.js b/libs/SoloPlay/Functions/Mercenary.js index 1abe255b..6c7e7632 100644 --- a/libs/SoloPlay/Functions/Mercenary.js +++ b/libs/SoloPlay/Functions/Mercenary.js @@ -198,7 +198,7 @@ const Mercenary = { Town.goToTown(typeOfMerc); myPrint("ÿc9Mercenaryÿc0 :: getting merc"); Town.move(Town.tasks[me.act - 1].Merc); - Town.sortInventory(); + me.sortInventory(); Item.removeItemsMerc(); // strip temp merc gear delay(500 + me.ping); addEventListener("gamepacket", MercLib_1.mercPacket); diff --git a/libs/SoloPlay/Functions/MiscOverrides.js b/libs/SoloPlay/Functions/MiscOverrides.js index 1dc0b40b..32b7549f 100644 --- a/libs/SoloPlay/Functions/MiscOverrides.js +++ b/libs/SoloPlay/Functions/MiscOverrides.js @@ -487,7 +487,7 @@ Misc.addSocketablesToItem = function (item, runes = []) { bodyLoc = item.bodylocation; if (!Storage.Inventory.CanFit(item)) { - Town.sortInventory(); + me.sortInventory(); if (!Storage.Inventory.CanFit(item) && !Storage.Inventory.MoveTo(item)) { console.log("ÿc8AddSocketableToItemÿc0 :: No space to get item back"); diff --git a/libs/SoloPlay/Functions/NPCAction.js b/libs/SoloPlay/Functions/NPCAction.js new file mode 100644 index 00000000..bdb4b888 --- /dev/null +++ b/libs/SoloPlay/Functions/NPCAction.js @@ -0,0 +1,721 @@ +/** +* @filename NPCAction.js +* @author theBGuy +* @desc NPC related functions +* +*/ + +// ugly but should handle scope issues if I decide to add this to the core in which case I can come back and remove this +// but won't get immeadiate issues of trying to redefine a const +(function (NPCAction) { + NPCAction.buyPotions = function () { + if (me.gold < 450 || !me.getItem(sdk.items.TomeofTownPortal)) return false; + + me.clearBelt(); + const buffer = { hp: 0, mp: 0 }; + const beltSize = Storage.BeltSize(); + let [needPots, needBuffer, specialCheck] = [false, true, false]; + let col = Storage.Belt.checkColumns(beltSize); + + const getNeededBuffer = () => { + [buffer.hp, buffer.mp] = [0, 0]; + me.getItemsEx().filter(function (p) { + return p.isInInventory && [sdk.items.type.HealingPotion, sdk.items.type.ManaPotion].includes(p.itemType); + }).forEach(function (p) { + switch (p.itemType) { + case sdk.items.type.HealingPotion: + return (buffer.hp++); + case sdk.items.type.ManaPotion: + return (buffer.mp++); + } + return false; + }); + }; + + // HP/MP Buffer + (Config.HPBuffer > 0 || Config.MPBuffer > 0) && getNeededBuffer(); + + // Check if we need to buy potions based on Config.MinColumn + if (Config.BeltColumn.some((c, i) => ["hp", "mp"].includes(c) && col[i] > (beltSize - Math.min(Config.MinColumn[i], beltSize)))) { + needPots = true; + } + + // Check if we need any potions for buffers + if (buffer.mp < Config.MPBuffer || buffer.hp < Config.HPBuffer) { + if (Config.BeltColumn.some((c, i) => col[i] >= beltSize && (!needPots || c === "rv"))) { + specialCheck = true; + } + } + + // We have enough potions in inventory + (buffer.mp >= Config.MPBuffer && buffer.hp >= Config.HPBuffer) && (needBuffer = false); + + // No columns to fill + if (!needPots && !needBuffer) return true; + // todo: buy the cheaper potions if we are low on gold or don't need the higher ones i.e have low mana/health pool + // why buy potion that heals 225 (greater mana) if we only have sub 100 mana + let [wantedHpPot, wantedMpPot] = [5, 5]; + // only do this if we are low on gold in the first place + if (me.normal && me.gold < Config.LowGold) { + const mpPotsEffects = PotData.getMpPots().map(el => el.effect[me.classid]); + const hpPotsEffects = PotData.getHpPots().map(el => el.effect[me.classid]); + + wantedHpPot = (hpPotsEffects.findIndex(eff => me.hpmax / 2 < eff) + 1 || hpPotsEffects.length - 1); + wantedMpPot = (mpPotsEffects.findIndex(eff => me.mpmax / 2 < eff) + 1 || mpPotsEffects.length - 1); + console.debug("Wanted hpPot: " + wantedHpPot + " Wanted mpPot: " + wantedMpPot); + } + + if (me.normal && me.highestAct >= 4) { + let pAct = Math.max(wantedHpPot, wantedMpPot); + pAct >= 4 ? me.act < 4 && Town.goToTown(4) : pAct > me.act && Town.goToTown(pAct); + } + + let npc = Town.initNPC("Shop", "buyPotions"); + if (!npc) return false; + + // special check, sometimes our rejuv slot is empty but we do still need buffer. Check if we can buy something to slot there + if (specialCheck && Config.BeltColumn.some((c, i) => c === "rv" && col[i] >= beltSize)) { + let pots = [sdk.items.ThawingPotion, sdk.items.AntidotePotion, sdk.items.StaminaPotion]; + Config.BeltColumn.forEach((c, i) => { + if (c === "rv" && col[i] >= beltSize && pots.length) { + let usePot = pots[0]; + let pot = npc.getItem(usePot); + if (pot) { + Storage.Inventory.CanFit(pot) && Packet.buyItem(pot, false); + pot = me.getItemsEx(usePot, sdk.items.mode.inStorage).filter(i => i.isInInventory).first(); + !!pot && Packet.placeInBelt(pot, i); + pots.shift(); + } else { + needBuffer = false; // we weren't able to find any pots to buy + } + } + }); + } + + for (let i = 0; i < 4; i += 1) { + if (col[i] > 0) { + const useShift = Town.shiftCheck(col, beltSize); + const wantedPot = Config.BeltColumn[i] === "hp" ? wantedHpPot : wantedMpPot; + let pot = Town.getPotion(npc, Config.BeltColumn[i], wantedPot); + + if (pot) { + // print("ÿc2column ÿc0" + i + "ÿc2 needs ÿc0" + col[i] + " ÿc2potions"); + // Shift+buy will trigger if there's no empty columns or if only the current column is empty + if (useShift) { + pot.buy(true); + } else { + for (let j = 0; j < col[i]; j += 1) { + pot.buy(false); + } + } + } + } + + col = Storage.Belt.checkColumns(beltSize); // Re-initialize columns (needed because 1 shift-buy can fill multiple columns) + } + + // re-check + !needBuffer && (Config.HPBuffer > 0 || Config.MPBuffer > 0) && getNeededBuffer(); + + const buyHPBuffers = () => { + if (needBuffer && buffer.hp < Config.HPBuffer) { + for (let i = 0; i < Config.HPBuffer - buffer.hp; i += 1) { + let pot = Town.getPotion(npc, "hp", wantedHpPot); + !!pot && Storage.Inventory.CanFit(pot) && pot.buy(false); + } + } + return true; + }; + const buyMPBuffers = () => { + if (needBuffer && buffer.mp < Config.MPBuffer) { + for (let i = 0; i < Config.MPBuffer - buffer.mp; i += 1) { + let pot = Town.getPotion(npc, "mp", wantedMpPot); + !!pot && Storage.Inventory.CanFit(pot) && pot.buy(false); + } + } + return true; + }; + // priortize mana pots if caster + Check.currentBuild().caster ? buyMPBuffers() && buyHPBuffers() : buyHPBuffers() && buyMPBuffers(); + + // keep cold/pois res high with potions + if (me.gold > 50000 && npc.getItem(sdk.items.ThawingPotion)) { + CharData.buffData.thawing.need() && Town.buyPots(12, "thawing", true); + CharData.buffData.antidote.need() && Town.buyPots(12, "antidote", true); + } + + return true; + }; + + NPCAction.fillTome = function (classid, force = false) { + if (me.gold < 450) return false; + const have = Town.checkScrolls(classid, force); + if (have >= (me.charlvl < 12 ? 5 : 13)) return true; + + let scroll; + const scrollId = (classid === sdk.items.TomeofTownPortal ? sdk.items.ScrollofTownPortal : sdk.items.ScrollofIdentify); + let npc = Town.initNPC("Shop", "fillTome"); + if (!npc) return false; + + delay(500); + + if (!me.findItem(classid, sdk.items.mode.inStorage, sdk.storage.Inventory)) { + let tome = npc.getItem(classid); + + try { + if (!tome || !Storage.Inventory.CanFit(tome)) throw new Error("Can't buy tome"); + tome.buy(); + } catch (e) { + // couldn't buy tome, lets see if we can just buy a single scroll + if (me.getItem(scrollId)) return true; + scroll = npc.getItem(scrollId); + if (!scroll || !Storage.Inventory.CanFit(scroll)) return false; + try { + scroll.buy(); + } catch (e) { + console.error(e); + return false; + } + return true; + } + } + + scroll = npc.getItem(scrollId); + if (!scroll) return false; + + try { + if (me.gold < 5000) { + let myTome = me.getItem(classid); + if (myTome) { + while (myTome.getStat(sdk.stats.Quantity) < 5 && me.gold > 500) { + scroll = npc.getItem(scrollId); + scroll && Packet.buyScroll(scroll, myTome, false); + delay(50); + } + } + } else { + scroll.buy(true); + } + } catch (e2) { + console.error(e2); + + return false; + } + + return true; + }; + + NPCAction.cainID = function (force = false) { + if ((!Config.CainID.Enable && !force) || !Misc.checkQuest(sdk.quest.id.TheSearchForCain, sdk.quest.states.Completed)) return false; + + let npc = getInteractedNPC(); + + // Check if we're already in a shop. It would be pointless to go to Cain if so. + if (npc && npc.name.toLowerCase() === Town.tasks[me.act - 1].Shop) return false; + // Check if we may use Cain - minimum gold + if (me.gold < Config.CainID.MinGold && !force) return false; + + me.cancel(); + Town.stash(false); + + let unids = me.getUnids(); + + if (unids) { + // Check if we may use Cain - number of unid items + if (unids.length < Config.CainID.MinUnids && !force) return false; + + let cain = Town.initNPC("CainID", "cainID"); + if (!cain) return false; + + if (force) { + npc = Town.initNPC("Shop", "clearInventory"); + if (!npc) return false; + } + + for (let i = 0; i < unids.length; i += 1) { + let item = unids[i]; + let result = Pickit.checkItem(item); + // Force ID for unid items matching autoEquip/cubing criteria + Town.needForceID(item) && (result.result = -1); + + switch (result.result) { + case Pickit.Result.TRASH: + try { + console.log("sell " + item.name); + Town.initNPC("Shop", "clearInventory"); + Item.logger("Sold", item); + item.sell(); + } catch (e) { + console.error(e); + } + + break; + case Pickit.Result.WANTED: + case Pickit.Result.SOLOWANTS: + Item.logger("Kept", item); + Item.logItem("Kept", item, result.line); + + break; + case Pickit.Result.CUBING: + Item.logger("Kept", item, "Cubing-Town"); + Cubing.update(); + + break; + case Pickit.Result.RUNEWORD: // (doesn't trigger normally) + break; + case Pickit.Result.CRAFTING: + Item.logger("Kept", item, "CraftSys-Town"); + CraftingSystem.update(item); + + break; + case Pickit.Result.SOLOSYSTEM: + Item.logger("Kept", item, "SoloWants-Town"); + SoloWants.update(item); + + break; + default: + if ((getUIFlag(sdk.uiflags.Shop) || getUIFlag(sdk.uiflags.NPCMenu)) && (item.getItemCost(sdk.items.cost.ToSell) <= 1 || !item.sellable)) { + continue; + } + + Town.initNPC("Shop", "clearInventory"); + + // Might as well sell the item if already in shop + if (getUIFlag(sdk.uiflags.Shop) || (Config.PacketShopping && getInteractedNPC() && getInteractedNPC().itemcount > 0)) { + console.log("clearInventory sell " + item.name); + + switch (true) { + case (Developer.debugging.smallCharm && item.classid === sdk.items.SmallCharm): + case (Developer.debugging.largeCharm && item.classid === sdk.items.LargeCharm): + case (Developer.debugging.grandCharm && item.classid === sdk.items.GrandCharm): + Item.logItem("Sold", item); + + break; + default: + Item.logger("Dropped", item, "clearInventory"); + + break; + } + + item.sell(); + } else { + console.log("clearInventory dropped " + item.name); + Item.logger("Dropped", item, "clearInventory"); + item.drop(); + } + + let timer = getTickCount() - Town.sellTimer; // shop speedup test + + if (timer > 0 && timer < 500) { + delay(timer); + } + + break; + case Pickit.Result.UNID: + if (tome) { + Town.identifyItem(item, tome); + } else { + let scroll = npc.getItem(sdk.items.ScrollofIdentify); + + if (scroll) { + if (!Storage.Inventory.CanFit(scroll)) { + let tpTome = me.findItem(sdk.items.TomeofTownPortal, sdk.items.mode.inStorage, sdk.storage.Inventory); + !!tpTome && tpTome.sell() && delay(500); + } + + delay(500); + Storage.Inventory.CanFit(scroll) && scroll.buy(); + } + + scroll = me.findItem(sdk.items.ScrollofIdentify, sdk.items.mode.inStorage, sdk.storage.Inventory); + if (!scroll) continue; + + Town.identifyItem(item, scroll); + } + + // roll back to now check against other criteria + if (item.identified) { + i--; + } + + break; + } + } + } + + return true; + }; + + // todo - allow earlier shopping, mainly to get a belt + NPCAction.shopItems = function (force = false) { + if (!Config.MiniShopBot) return true; + // todo - better gold scaling + let goldLimit = [10000, 20000, 30000][me.diff]; + let itemTypes = []; + let lowLevelShop = false; + if (me.gold < goldLimit && me.charlvl > 6) { + return false; + } else if (me.charlvl < 6 && me.gold > 200) { + lowLevelShop = true; + Storage.BeltSize() === 1 && itemTypes.push(sdk.items.type.Belt); + !CharData.skillData.bowData.bowOnSwitch && itemTypes.push(sdk.items.type.Bow, sdk.items.type.Crossbow); + if (!itemTypes.length) return true; + goldLimit = 200; + } + + let npc = getInteractedNPC(); + if (!npc || !npc.itemcount) { + // for now we only do force shop on low level + if (force && itemTypes.length) { + console.debug("Attempt force shopping"); + Town.initNPC("Repair", "shopItems"); + npc = getInteractedNPC(); + if (!npc || !npc.itemcount) return false; + } else { + return false; + } + } + + let items = npc.getItemsEx() + .filter((item) => !Town.ignoreType(item.itemType) && (itemTypes.length === 0 || itemTypes.includes(item.itemType))) + .sort((a, b) => NTIP.GetTier(b) - NTIP.GetTier(a)); + if (!items.length) return false; + if (getTickCount() - Town.lastShopped.tick < Time.seconds(3) && Town.lastShopped.who === npc.name) return false; + + console.time("shopItems"); + + let bought = 0; + const haveMerc = (!me.classic && Config.UseMerc && !me.mercrevivecost && Misc.poll(() => !!me.getMerc(), 500, 100)); + console.info(true, "ÿc4MiniShopBotÿc0: Scanning " + npc.itemcount + " items."); + + /** + * @param {ItemUnit} item + * @param {string} action + * @param {{ result: PickitResult, line: string }} result + * @param {number | string} tierInfo + */ + const shopReport = (item, action, result, tierInfo) => { + action === undefined && (action = ""); + tierInfo === undefined && (tierInfo = ""); + console.log("ÿc8Kolbot-SoloPlayÿc0: " + action + (tierInfo ? " " + tierInfo : "")); + Item.logger(action, item); + Developer.debugging.autoEquip && Item.logItem("Shopped " + action, item, result.line !== undefined ? result.line : "null"); + }; + + for (let i = 0; i < items.length; i++) { + const item = items[i]; + const myGold = me.gold; + const itemCost = item.getItemCost(sdk.items.cost.ToBuy); + if (myGold < itemCost) continue; + const result = Pickit.checkItem(item); + + // no tier'ed items + if (!lowLevelShop && result.result === Pickit.Result.SOLOWANTS && NTIP.CheckItem(item, NTIP.SoloCheckListNoTier, true).result !== Pickit.Result.UNWANTED) { + try { + if (Storage.Inventory.CanFit(item) && myGold >= itemCost && (myGold - itemCost > goldLimit)) { + if (item.isBaseType) { + if (Item.betterThanStashed(item) && Item.betterBaseThanWearing(item, Developer.debugging.baseCheck)) { + shopReport(item, "better base", result.line); + item.buy() && bought++; + + continue; + } + } else { + shopReport(item, "NoTier", result.line); + item.buy() && bought++; + + continue; + } + } + } catch (e) { + console.error(e); + } + } + + // tier'ed items + if (result.result === Pickit.Result.SOLOWANTS && AutoEquip.wanted(item)) { + const checkDependancy = (item) => { + let check = Item.hasDependancy(item); + if (check) { + let el = npc.getItem(check); + !!el && el.buy(); + } + }; + try { + if (Storage.Inventory.CanFit(item) && myGold >= itemCost && (myGold - itemCost > goldLimit)) { + if (Item.hasTier(item) && Item.autoEquipCheck(item)) { + try { + shopReport(item, "AutoEquip", result.line, (item.fname + " Tier: " + NTIP.GetTier(item))); + item.buy() && bought++; + Item.autoEquip("InShop"); + } catch (e) { + console.error(e); + } + + checkDependancy(item); + + continue; + } + + if (Item.hasSecondaryTier(item) && Item.autoEquipCheckSecondary(item)) { + try { + shopReport(item, "AutoEquip Switch Shopped", result.line, (item.fname + " SecondaryTier: " + NTIP.GetSecondaryTier(item))); + item.buy() && bought++; + Item.autoEquip("InShop"); + } catch (e) { + console.error(e); + } + + checkDependancy(item); + } + } + } catch (e) { + console.error(e); + } + } + + delay(2); + } + + // merc tier'ed items + if (haveMerc && !lowLevelShop) { + items = npc.getItemsEx() + .filter((item) => !Town.ignoreType(item.itemType) && (itemTypes.length === 0 || itemTypes.includes(item.itemType) && NTIP.GetMercTier(item) > 0)) + .sort((a, b) => NTIP.GetMercTier(b) - NTIP.GetMercTier(a)) + .forEach(item => { + const myGold = me.gold; + const itemCost = item.getItemCost(sdk.items.cost.ToBuy); + if (myGold < itemCost) return; + const result = Pickit.checkItem(item); + + if (result.result === Pickit.Result.SOLOWANTS && AutoEquip.wanted(item)) { + try { + if (Storage.Inventory.CanFit(item) && myGold >= itemCost && (myGold - itemCost > goldLimit)) { + if (Item.hasMercTier(item) && Item.autoEquipCheckMerc(item)) { + shopReport(item, "AutoEquipMerc", result.line, (item.fname + " Tier: " + NTIP.GetMercTier(item))); + item.buy() && bought++; + } + } + } catch (e) { + console.error(e); + } + } + + delay(2); + }); + } + + Town.lastShopped.tick = getTickCount(); + Town.lastShopped.who = npc.name; + + console.info(false, "Bought " + bought + " items", "shopItems"); + + return true; + }; + + NPCAction.gamble = function () { + if (!Town.needGamble() || Config.GambleItems.length === 0) return true; + + let list = []; + + if (Town.gambleIds.length === 0) { + // change text to classid + for (let i = 0; i < Config.GambleItems.length; i += 1) { + if (isNaN(Config.GambleItems[i])) { + if (NTIPAliasClassID.hasOwnProperty(Config.GambleItems[i].replace(/\s+/g, "").toLowerCase())) { + Town.gambleIds.push(NTIPAliasClassID[Config.GambleItems[i].replace(/\s+/g, "").toLowerCase()]); + } else { + Misc.errorReport("ÿc1Invalid gamble entry:ÿc0 " + Config.GambleItems[i]); + } + } else { + Town.gambleIds.push(Config.GambleItems[i]); + } + } + } + + if (Town.gambleIds.length === 0) return true; + + // avoid Alkor + me.act === 3 && Town.goToTown(Pather.accessToAct(4) ? 4 : 2); + + let npc = Town.initNPC("Gamble", "gamble"); + if (!npc) return false; + + let items = me.findItems(-1, sdk.items.mode.inStorage, sdk.storage.Inventory); + + while (items && items.length > 0) { + list.push(items.shift().gid); + } + + while (me.gold >= Config.GambleGoldStop) { + !getInteractedNPC() && npc.startTrade("Gamble"); + + let item = npc.getItem(); + items = []; + + if (item) { + do { + Town.gambleIds.includes(item.classid) && items.push(copyUnit(item)); + } while (item.getNext()); + + for (let i = 0; i < items.length; i += 1) { + if (!Storage.Inventory.CanFit(items[i])) return false; + + items[i].buy(false, true); + + let newItem = Town.getGambledItem(list); + + if (newItem) { + let result = Pickit.checkItem(newItem); + + switch (result.result) { + case Pickit.Result.WANTED: + Item.logger("Gambled", newItem); + Item.logItem("Gambled", newItem, result.line); + list.push(newItem.gid); + + break; + case Pickit.Result.CUBING: + list.push(newItem.gid); + Cubing.update(); + + break; + case Pickit.Result.CRAFTING: + CraftingSystem.update(newItem); + + break; + default: + Item.logger("Sold", newItem, "Gambling"); + newItem.sell(); + + if (!Config.PacketShopping) { + delay(500); + } + + break; + } + } + } + } + + me.cancel(); + } + + return true; + }; + + NPCAction.repair = function (force = false) { + if (Town.cubeRepair()) return true; + + let npc; + let repairAction = Town.needRepair(); + force && repairAction.indexOf("repair") === -1 && repairAction.push("repair"); + if (!repairAction || !repairAction.length) return false; + + for (let i = 0; i < repairAction.length; i += 1) { + switch (repairAction[i]) { + case "repair": + me.act === 3 && Town.goToTown(Pather.accessToAct(4) ? 4 : 2); + npc = Town.initNPC("Repair", "repair"); + if (!npc) return false; + me.repair(); + + break; + case "buyQuiver": + let bowCheck = Attack.usingBow(); + let switchBowCheck = CharData.skillData.bowData.bowOnSwitch; + !bowCheck && switchBowCheck && (bowCheck = (() => { + switch (CharData.skillData.bowData.bowType) { + case sdk.items.type.Bow: + case sdk.items.type.AmazonBow: + return "bow"; + case sdk.items.type.Crossbow: + return "crossbow"; + default: + return ""; + } + })()); + + if (bowCheck) { + let quiver = bowCheck === "bow" ? "aqv" : "cqv"; + switchBowCheck && me.switchWeapons(sdk.player.slot.Secondary); + let myQuiver = me.getItem(quiver, sdk.items.mode.Equipped); + !!myQuiver && myQuiver.drop(); + + npc = Town.initNPC("Repair", "buyQuiver"); + if (!npc) return false; + + quiver = npc.getItem(quiver); + !!quiver && quiver.buy(); + switchBowCheck && me.switchWeapons(sdk.player.slot.Main); + } + + break; + } + } + + NPCAction.shopItems(); + + return true; + }; + + NPCAction.reviveMerc = function () { + if (!me.needMerc()) return true; + let preArea = me.area; + + // avoid Aheara + me.act === 3 && Town.goToTown(Pather.accessToAct(4) ? 4 : 2); + + let npc = Town.initNPC("Merc", "reviveMerc"); + if (!npc) return false; + + MainLoop: + for (let i = 0; i < 3; i += 1) { + let dialog = getDialogLines(); + if (!dialog) continue; + + for (let lines = 0; lines < dialog.length; lines += 1) { + if (dialog[lines].text.match(":", "gi")) { + dialog[lines].handler(); + delay(Math.max(750, me.ping * 2)); + } + + // "You do not have enough gold for that." + if (dialog[lines].text.match(getLocaleString(sdk.locale.dialog.youDoNotHaveEnoughGoldForThat), "gi")) { + dialog.find(line => !line.text.match(getLocaleString(sdk.locale.dialog.youDoNotHaveEnoughGoldForThat), "gi")).handler(); + delay(Math.max(750, me.ping * 2)); + me.cancelUIFlags(); + console.error("Could not revive merc: My current gold: " + me.gold + ", current mercrevivecost: " + me.mercrevivecost); + return false; + } + } + + let tick = getTickCount(); + + while (getTickCount() - tick < 2000) { + if (me.getMercEx()) { + delay(Math.max(750, me.ping * 2)); + + break MainLoop; + } + + delay(200); + } + } + + Attack.checkInfinity(); + + if (me.getMercEx()) { + // Cast BO on merc so he doesn't just die again. Only do this is you are a barb or actually have a cta. Otherwise its just a waste of time. + if (Config.MercWatch && Precast.needOutOfTownCast()) { + console.log("MercWatch precast"); + Precast.doRandomPrecast(true, preArea); + } + + return true; + } + + return false; + }; + +})(global.NPCAction = global.NPCAction || {}); diff --git a/libs/SoloPlay/Functions/PickitOverrides.js b/libs/SoloPlay/Functions/PickitOverrides.js index 3c9e2384..a09636e2 100644 --- a/libs/SoloPlay/Functions/PickitOverrides.js +++ b/libs/SoloPlay/Functions/PickitOverrides.js @@ -158,7 +158,7 @@ Pickit.amountOfPotsNeeded = function () { needed[pot.itemType][pot.location] -= 1; }); } - let missing = Town.checkColumns(Pickit.beltSize); + let missing = Storage.Belt.checkColumns(Pickit.beltSize); Config.BeltColumn.forEach(function (column, index) { if (column === "hp") {needed[sdk.items.type.HealingPotion][sdk.storage.Belt] = missing[index];} if (column === "mp") {needed[sdk.items.type.ManaPotion][sdk.storage.Belt] = missing[index];} @@ -718,12 +718,26 @@ Pickit.pickItems = function (range = Config.PickRange, once = false) { me.fieldID() && (canFit = (currItem.gid !== undefined && Storage.Inventory.CanFit(currItem))); } + if (!canFit && !me.checkForMobs({ range: 10 })) { + me.sortInventory(); + canFit = (Storage.Inventory.CanFit(currItem) || Pickit.canFit(currItem)); + } + // Try to make room by selling items in town if (!canFit) { // Check if any of the current inventory items can be stashed or need to be identified and eventually sold to make room if (this.canMakeRoom()) { console.log("ÿc7Trying to make room for " + Item.color(currItem) + currItem.name); + /** + * @todo + * - Try to sort inventory if it is safe to do so + * - Check to see if we can clear up enough buffer potions (exlcuding rejuvs) in order to pick the item + * - If all this fails after visiting town to clear inventory and stash items then globally ignore this item + * and potentially any other items it's size until our used space changes. Only way we should get to this point + * is the use of an additonal pickit file without muling setup. + */ + // Go to town and do town chores if (Town.visitTown()) { // Recursive check after going to town. We need to remake item list because gids can change. @@ -737,7 +751,6 @@ Pickit.pickItems = function (range = Config.PickRange, once = false) { return false; } - // Can't make room - trigger automule if (copyUnit(currItem).x !== undefined) { Item.logger("No room for", currItem); diff --git a/libs/SoloPlay/Functions/Quest.js b/libs/SoloPlay/Functions/Quest.js index aa311176..4ab13db2 100644 --- a/libs/SoloPlay/Functions/Quest.js +++ b/libs/SoloPlay/Functions/Quest.js @@ -12,7 +12,7 @@ const Quest = { // cube if (!me.cube) { for (let getCube = 0; !me.cube && getCube < 5; getCube++) { - Loader.runScript("cube"); + Loader.runScript("amulet"); } } @@ -124,7 +124,7 @@ const Quest = { if (!Storage.Inventory.CanFit(hstaff)) { Town.clearJunk(); - Town.sortInventory(); + me.sortInventory(); } hstaff.isInStash && Town.openStash(); diff --git a/libs/SoloPlay/Functions/StorageOverrides.js b/libs/SoloPlay/Functions/StorageOverrides.js index f5f81b0c..7f38a522 100644 --- a/libs/SoloPlay/Functions/StorageOverrides.js +++ b/libs/SoloPlay/Functions/StorageOverrides.js @@ -227,7 +227,7 @@ includeIfNotIncluded("SoloPlay/Tools/Developer.js"); } } - //this.Dump(); + // this.Dump(); return true; }; @@ -588,6 +588,27 @@ includeIfNotIncluded("SoloPlay/Tools/Developer.js"); this.TradeScreen = new Container("Inventory", 10, 4, 5); this.Stash = new Container("Stash", (Developer.plugyMode ? 10 : 6), this.StashY, 7); this.Belt = new Container("Belt", 4 * this.BeltSize(), 1, 2); + + /** + * @description Return column status (needed potions in each column) + * @param {0 | 1 | 2 | 3 | 4} beltSize + * @returns {[number, number, number, number]} + */ + this.Belt.checkColumns = function (beltSize) { + beltSize === undefined && (beltSize = this.width / 4); + let col = [beltSize, beltSize, beltSize, beltSize]; + let pot = me.getItem(-1, sdk.items.mode.inBelt); + + // No potions + if (!pot) return col; + + do { + col[pot.x % 4] -= 1; + } while (pot.getNext()); + + return col; + }; + this.Cube = new Container("Horadric Cube", 3, 4, 6); this.InvRef = []; diff --git a/libs/SoloPlay/Functions/TownOverrides.js b/libs/SoloPlay/Functions/TownOverrides.js index db72cc60..8ffbd30a 100644 --- a/libs/SoloPlay/Functions/TownOverrides.js +++ b/libs/SoloPlay/Functions/TownOverrides.js @@ -56,6 +56,13 @@ Town.needPotions = () => me.needPotions(); Town.fieldID = () => me.fieldID(); Town.getItemsForRepair = (repairPercent, chargedItems) => me.getItemsForRepair(repairPercent, chargedItems); Town.needRepair = () => me.needRepair(); +Town.buyPotions = () => NPCAction.buyPotions(); +Town.fillTome = (classid, force = false) => NPCAction.fillTome(classid, force); +Town.cainID = (force = false) => NPCAction.cainID(force); +Town.lastShopped = { who: "", tick: 0 }; +// todo - allow earlier shopping, mainly to get a belt +Town.shopItems = (force = false) => NPCAction.shopItems(force); +Town.gamble = () => NPCAction.gamble(); Town.sell = []; // Removed Missle Potions for easy gold @@ -67,160 +74,41 @@ Town.ignoredItemTypes = [ sdk.items.type.AntidotePotion, sdk.items.type.ThawingPotion ]; +/** + * Check if any of the systems need this item + * @param {ItemUnit} item + * @returns {boolean} + */ Town.systemsKeep = function (item) { return (AutoEquip.wanted(item) || Cubing.keepItem(item) || Runewords.keepItem(item) || CraftingSystem.keepItem(item) || SoloWants.keepItem(item)); }; +/** + * @param {ItemUnit} item + * @returns {boolean} + */ Town.needForceID = function (item) { const result = Pickit.checkItem(item); return ([Pickit.Result.WANTED, Pickit.Result.CUBING].includes(result.result) && !item.identified && AutoEquip.hasTier(item)); }; Town.haveItemsToSell = function () { - Town.sell = Town.sell.filter(i => i && i.isInStorage); - return Town.sell.length; -}; - -Town.buyPotions = function () { - // Ain't got money fo' dat shyt - if (me.gold < 450 || !me.getItem(sdk.items.TomeofTownPortal)) return false; - - this.clearBelt(); - const buffer = { hp: 0, mp: 0 }; - const beltSize = Storage.BeltSize(); - let [needPots, needBuffer, specialCheck] = [false, true, false]; - let col = this.checkColumns(beltSize); - - const getNeededBuffer = () => { - [buffer.hp, buffer.mp] = [0, 0]; - me.getItemsEx().filter(function (p) { - return p.isInInventory && [sdk.items.type.HealingPotion, sdk.items.type.ManaPotion].includes(p.itemType); - }).forEach(function (p) { - switch (p.itemType) { - case sdk.items.type.HealingPotion: - return (buffer.hp++); - case sdk.items.type.ManaPotion: - return (buffer.mp++); - } - return false; - }); - }; - - // HP/MP Buffer - (Config.HPBuffer > 0 || Config.MPBuffer > 0) && getNeededBuffer(); - - // Check if we need to buy potions based on Config.MinColumn - if (Config.BeltColumn.some((c, i) => ["hp", "mp"].includes(c) && col[i] > (beltSize - Math.min(Config.MinColumn[i], beltSize)))) { - needPots = true; - } - - // Check if we need any potions for buffers - if (buffer.mp < Config.MPBuffer || buffer.hp < Config.HPBuffer) { - if (Config.BeltColumn.some((c, i) => col[i] >= beltSize && (!needPots || c === "rv"))) { - specialCheck = true; - } - } - - // We have enough potions in inventory - (buffer.mp >= Config.MPBuffer && buffer.hp >= Config.HPBuffer) && (needBuffer = false); - - // No columns to fill - if (!needPots && !needBuffer) return true; - // todo: buy the cheaper potions if we are low on gold or don't need the higher ones i.e have low mana/health pool - // why buy potion that heals 225 (greater mana) if we only have sub 100 mana - let [wantedHpPot, wantedMpPot] = [5, 5]; - // only do this if we are low on gold in the first place - if (me.normal && me.gold < Config.LowGold) { - const mpPotsEffects = PotData.getMpPots().map(el => el.effect[me.classid]); - const hpPotsEffects = PotData.getHpPots().map(el => el.effect[me.classid]); - - wantedHpPot = (hpPotsEffects.findIndex(eff => me.hpmax / 2 < eff) + 1 || hpPotsEffects.length - 1); - wantedMpPot = (mpPotsEffects.findIndex(eff => me.mpmax / 2 < eff) + 1 || mpPotsEffects.length - 1); - console.debug("Wanted hpPot: " + wantedHpPot + " Wanted mpPot: " + wantedMpPot); - } - - if (me.normal && me.highestAct >= 4) { - let pAct = Math.max(wantedHpPot, wantedMpPot); - pAct >= 4 ? me.act < 4 && this.goToTown(4) : pAct > me.act && this.goToTown(pAct); - } - - let npc = this.initNPC("Shop", "buyPotions"); - if (!npc) return false; - - // special check, sometimes our rejuv slot is empty but we do still need buffer. Check if we can buy something to slot there - if (specialCheck && Config.BeltColumn.some((c, i) => c === "rv" && col[i] >= beltSize)) { - let pots = [sdk.items.ThawingPotion, sdk.items.AntidotePotion, sdk.items.StaminaPotion]; - Config.BeltColumn.forEach((c, i) => { - if (c === "rv" && col[i] >= beltSize && pots.length) { - let usePot = pots[0]; - let pot = npc.getItem(usePot); - if (pot) { - Storage.Inventory.CanFit(pot) && Packet.buyItem(pot, false); - pot = me.getItemsEx(usePot, sdk.items.mode.inStorage).filter(i => i.isInInventory).first(); - !!pot && Packet.placeInBelt(pot, i); - pots.shift(); - } else { - needBuffer = false; // we weren't able to find any pots to buy - } - } - }); - } - - for (let i = 0; i < 4; i += 1) { - if (col[i] > 0) { - const useShift = this.shiftCheck(col, beltSize); - const wantedPot = Config.BeltColumn[i] === "hp" ? wantedHpPot : wantedMpPot; - let pot = this.getPotion(npc, Config.BeltColumn[i], wantedPot); - - if (pot) { - // print("ÿc2column ÿc0" + i + "ÿc2 needs ÿc0" + col[i] + " ÿc2potions"); - // Shift+buy will trigger if there's no empty columns or if only the current column is empty - if (useShift) { - pot.buy(true); - } else { - for (let j = 0; j < col[i]; j += 1) { - pot.buy(false); - } - } - } + let temp = []; + while (Town.sell.length) { + let i = Town.sell.shift(); + if (i && i.isInStorage && !Town.ignoreType(i.itemType) && i.sellable && !Town.systemsKeep(i)) { + temp.push(i); } - - col = this.checkColumns(beltSize); // Re-initialize columns (needed because 1 shift-buy can fill multiple columns) } - - // re-check - !needBuffer && (Config.HPBuffer > 0 || Config.MPBuffer > 0) && getNeededBuffer(); - - const buyHPBuffers = () => { - if (needBuffer && buffer.hp < Config.HPBuffer) { - for (let i = 0; i < Config.HPBuffer - buffer.hp; i += 1) { - let pot = this.getPotion(npc, "hp", wantedHpPot); - !!pot && Storage.Inventory.CanFit(pot) && pot.buy(false); - } - } - return true; - }; - const buyMPBuffers = () => { - if (needBuffer && buffer.mp < Config.MPBuffer) { - for (let i = 0; i < Config.MPBuffer - buffer.mp; i += 1) { - let pot = this.getPotion(npc, "mp", wantedMpPot); - !!pot && Storage.Inventory.CanFit(pot) && pot.buy(false); - } - } - return true; - }; - // priortize mana pots if caster - Check.currentBuild().caster ? buyMPBuffers() && buyHPBuffers() : buyHPBuffers() && buyMPBuffers(); - - // keep cold/pois res high with potions - if (me.gold > 50000 && npc.getItem(sdk.items.ThawingPotion)) { - CharData.buffData.thawing.need() && Town.buyPots(12, "thawing", true); - CharData.buffData.antidote.need() && Town.buyPots(12, "antidote", true); - } - - return true; + Town.sell = temp.slice(0); + + return Town.sell.length; }; +/** + * @param {ItemUnit[]} itemList + * @returns {boolean} + */ Town.sellItems = function (itemList = []) { !itemList.length && (itemList = Town.sell); @@ -231,7 +119,7 @@ Town.sellItems = function (itemList = []) { try { if (getUIFlag(sdk.uiflags.Shop) || getUIFlag(sdk.uiflags.NPCMenu)) { - console.log("sell " + item.name); + console.log("sell " + item.prettyPrint); Item.logger("Sold", item); item.sell(); delay(100); @@ -262,64 +150,13 @@ Town.checkScrolls = function (id, force = false) { return tome.getStat(sdk.stats.Quantity); }; -Town.fillTome = function (classid, force = false) { - if (me.gold < 450) return false; - const have = this.checkScrolls(classid, force); - if (have >= (me.charlvl < 12 ? 5 : 13)) return true; - - let scroll; - const scrollId = (classid === sdk.items.TomeofTownPortal ? sdk.items.ScrollofTownPortal : sdk.items.ScrollofIdentify); - let npc = this.initNPC("Shop", "fillTome"); - if (!npc) return false; - - delay(500); - - if (!me.findItem(classid, sdk.items.mode.inStorage, sdk.storage.Inventory)) { - let tome = npc.getItem(classid); - - try { - if (!tome || !Storage.Inventory.CanFit(tome)) throw new Error("Can't buy tome"); - tome.buy(); - } catch (e) { - // couldn't buy tome, lets see if we can just buy a single scroll - if (me.getItem(scrollId)) return true; - scroll = npc.getItem(scrollId); - if (!scroll || !Storage.Inventory.CanFit(scroll)) return false; - try { - scroll.buy(); - return true; - } catch (e) { - console.error(e); - return false; - } - } - } - - scroll = npc.getItem(scrollId); - if (!scroll) return false; - - try { - if (me.gold < 5000) { - let myTome = me.getItem(classid); - if (myTome) { - while (myTome.getStat(sdk.stats.Quantity) < 5 && me.gold > 500) { - scroll = npc.getItem(scrollId); - scroll && Packet.buyScroll(scroll, myTome, false); - delay(50); - } - } - } else { - scroll.buy(true); - } - } catch (e2) { - console.error(e2); - - return false; - } - - return true; -}; - +/** + * @param {ItemUnit} item + * @param {{ result: PickitResult, line: string }} result + * @param {string} system + * @param {boolean} sell + * @returns {void} + */ Town.itemResult = function (item, result, system = "", sell = false) { let timer = 0; sell && !getInteractedNPC() && (sell = false); @@ -384,149 +221,11 @@ Town.itemResult = function (item, result, system = "", sell = false) { } }; -Town.cainID = function (force = false) { - if ((!Config.CainID.Enable && !force) || !Misc.checkQuest(sdk.quest.id.TheSearchForCain, sdk.quest.states.Completed)) return false; - - let npc = getInteractedNPC(); - - // Check if we're already in a shop. It would be pointless to go to Cain if so. - if (npc && npc.name.toLowerCase() === this.tasks[me.act - 1].Shop) return false; - // Check if we may use Cain - minimum gold - if (me.gold < Config.CainID.MinGold && !force) return false; - - me.cancel(); - this.stash(false); - - let unids = me.getUnids(); - - if (unids) { - // Check if we may use Cain - number of unid items - if (unids.length < Config.CainID.MinUnids && !force) return false; - - let cain = this.initNPC("CainID", "cainID"); - if (!cain) return false; - - if (force) { - npc = this.initNPC("Shop", "clearInventory"); - if (!npc) return false; - } - - for (let i = 0; i < unids.length; i += 1) { - let item = unids[i]; - let result = Pickit.checkItem(item); - // Force ID for unid items matching autoEquip/cubing criteria - Town.needForceID(item) && (result.result = -1); - - switch (result.result) { - case Pickit.Result.TRASH: - try { - console.log("sell " + item.name); - this.initNPC("Shop", "clearInventory"); - Item.logger("Sold", item); - item.sell(); - } catch (e) { - console.error(e); - } - - break; - case Pickit.Result.WANTED: - case Pickit.Result.SOLOWANTS: - Item.logger("Kept", item); - Item.logItem("Kept", item, result.line); - - break; - case Pickit.Result.CUBING: - Item.logger("Kept", item, "Cubing-Town"); - Cubing.update(); - - break; - case Pickit.Result.RUNEWORD: // (doesn't trigger normally) - break; - case Pickit.Result.CRAFTING: - Item.logger("Kept", item, "CraftSys-Town"); - CraftingSystem.update(item); - - break; - case Pickit.Result.SOLOSYSTEM: - Item.logger("Kept", item, "SoloWants-Town"); - SoloWants.update(item); - - break; - default: - if ((getUIFlag(sdk.uiflags.Shop) || getUIFlag(sdk.uiflags.NPCMenu)) && (item.getItemCost(sdk.items.cost.ToSell) <= 1 || !item.sellable)) { - continue; - } - - this.initNPC("Shop", "clearInventory"); - - // Might as well sell the item if already in shop - if (getUIFlag(sdk.uiflags.Shop) || (Config.PacketShopping && getInteractedNPC() && getInteractedNPC().itemcount > 0)) { - console.log("clearInventory sell " + item.name); - - switch (true) { - case (Developer.debugging.smallCharm && item.classid === sdk.items.SmallCharm): - case (Developer.debugging.largeCharm && item.classid === sdk.items.LargeCharm): - case (Developer.debugging.grandCharm && item.classid === sdk.items.GrandCharm): - Item.logItem("Sold", item); - - break; - default: - Item.logger("Dropped", item, "clearInventory"); - - break; - } - - item.sell(); - } else { - console.log("clearInventory dropped " + item.name); - Item.logger("Dropped", item, "clearInventory"); - item.drop(); - } - - let timer = getTickCount() - this.sellTimer; // shop speedup test - - if (timer > 0 && timer < 500) { - delay(timer); - } - - break; - case Pickit.Result.UNID: - if (tome) { - this.identifyItem(item, tome); - } else { - let scroll = npc.getItem(sdk.items.ScrollofIdentify); - - if (scroll) { - if (!Storage.Inventory.CanFit(scroll)) { - let tpTome = me.findItem(sdk.items.TomeofTownPortal, sdk.items.mode.inStorage, sdk.storage.Inventory); - !!tpTome && tpTome.sell() && delay(500); - } - - delay(500); - Storage.Inventory.CanFit(scroll) && scroll.buy(); - } - - scroll = me.findItem(sdk.items.ScrollofIdentify, sdk.items.mode.inStorage, sdk.storage.Inventory); - if (!scroll) continue; - - this.identifyItem(item, scroll); - } - - // roll back to now check against other criteria - if (item.identified) { - i--; - } - - break; - } - } - } - - return true; -}; - Town.identify = function () { - if (me.gold < 5000 && this.cainID(true)) return true; + /** + * @todo use cain we are closer to him than our shop npc + */ + if (me.gold < 15000 && NPCAction.cainID(true)) return true; let list = (Storage.Inventory.Compare(Config.Inventory) || []); if (!list.length) return false; @@ -535,27 +234,28 @@ Town.identify = function () { // Only unid items or sellable junk (low level) should trigger a NPC visit if (!list.some(item => { let identified = item.identified; - return (!identified && ([Pickit.Result.UNID, Pickit.Result.TRASH].includes(Pickit.checkItem(item).result) || (!identified && AutoEquip.hasTier(item)))); + if (!identified && AutoEquip.hasTier(item)) return true; + return ([Pickit.Result.UNID, Pickit.Result.TRASH].includes(Pickit.checkItem(item).result)); })) { return false; } - let npc = this.initNPC("Shop", "identify"); + let npc = Town.initNPC("Shop", "identify"); if (!npc) return false; - let tome = me.findItem(sdk.items.TomeofIdentify, sdk.items.mode.inStorage, sdk.storage.Inventory); - tome && tome.getStat(sdk.stats.Quantity) < list.length && this.fillTome(sdk.items.TomeofIdentify); + let tome = me.getTome(sdk.items.TomeofIdentify); + tome && tome.getStat(sdk.stats.Quantity) < list.length && NPCAction.fillTome(sdk.items.TomeofIdentify); MainLoop: while (list.length > 0) { - let item = list.shift(); + const item = list.shift(); - if (!this.ignoredItemTypes.includes(item.itemType) && item.location === sdk.storage.Inventory && !item.identified) { - let result = Pickit.checkItem(item); + if (!Town.ignoreType(item.itemType) && item.isInInventory && !item.identified) { + let result = Pickit.checkItem(item).result; // Force ID for unid items matching autoEquip/cubing criteria - Town.needForceID(item) && (result.result = -1); + Town.needForceID(item) && (result = -1); - switch (result.result) { + switch (result) { // Items for gold, will sell magics, etc. w/o id, but at low levels // magics are often not worth iding. case Pickit.Result.TRASH: @@ -573,7 +273,7 @@ Town.identify = function () { if (scroll) { if (!Storage.Inventory.CanFit(scroll)) { - let tpTome = me.findItem(sdk.items.TomeofTownPortal, sdk.items.mode.inStorage, sdk.storage.Inventory); + let tpTome = me.getTome(sdk.items.TomeofTownPortal); !!tpTome && tpTome.sell() && delay(500); } @@ -598,241 +298,7 @@ Town.identify = function () { } } - this.fillTome(sdk.items.TomeofTownPortal); // Check for TP tome in case it got sold for ID scrolls - - return true; -}; - -Town.lastShopped = { who: "", tick: 0 }; - -// todo - allow earlier shopping, mainly to get a belt -Town.shopItems = function (force = false) { - if (!Config.MiniShopBot) return true; - // todo - better gold scaling - let goldLimit = [10000, 20000, 30000][me.diff]; - let itemTypes = []; - let lowLevelShop = false; - if (me.gold < goldLimit && me.charlvl > 6) { - return false; - } else if (me.charlvl < 6 && me.gold > 200) { - lowLevelShop = true; - Storage.BeltSize() === 1 && itemTypes.push(sdk.items.type.Belt); - !CharData.skillData.bowData.bowOnSwitch && itemTypes.push(sdk.items.type.Bow, sdk.items.type.Crossbow); - if (!itemTypes.length) return true; - goldLimit = 200; - } - - let npc = getInteractedNPC(); - if (!npc || !npc.itemcount) { - // for now we only do force shop on low level - if (force && itemTypes.length) { - console.debug("Attempt force shopping"); - Town.initNPC("Repair", "shopItems"); - npc = getInteractedNPC(); - if (!npc || !npc.itemcount) return false; - } else { - return false; - } - } - - let items = npc.getItemsEx() - .filter((item) => Town.ignoredItemTypes.indexOf(item.itemType) === -1 && (itemTypes.length === 0 || itemTypes.includes(item.itemType))) - .sort((a, b) => NTIP.GetTier(b) - NTIP.GetTier(a)); - if (!items.length) return false; - if (getTickCount() - Town.lastShopped.tick < Time.seconds(3) && Town.lastShopped.who === npc.name) return false; - - console.time("shopItems"); - let bought = 0; - const haveMerc = (!me.classic && Config.UseMerc && !me.mercrevivecost && Misc.poll(() => !!me.getMerc(), 500, 100)); - console.info(true, "ÿc4MiniShopBotÿc0: Scanning " + npc.itemcount + " items."); - - const shopReport = (item, action, result, tierInfo) => { - action === undefined && (action = ""); - tierInfo === undefined && (tierInfo = ""); - console.log("ÿc8Kolbot-SoloPlayÿc0: " + action + (tierInfo ? " " + tierInfo : "")); - Item.logger(action, item); - Developer.debugging.autoEquip && Item.logItem("Shopped " + action, item, result.line !== undefined ? result.line : "null"); - }; - - for (let i = 0; i < items.length; i++) { - const item = items[i]; - const myGold = me.gold; - const itemCost = item.getItemCost(sdk.items.cost.ToBuy); - if (myGold < itemCost) continue; - const result = Pickit.checkItem(item); - - // no tier'ed items - if (!lowLevelShop && result.result === Pickit.Result.SOLOWANTS && NTIP.CheckItem(item, NTIP.SoloCheckListNoTier, true).result !== Pickit.Result.UNWANTED) { - try { - if (Storage.Inventory.CanFit(item) && myGold >= itemCost && (myGold - itemCost > goldLimit)) { - if (item.isBaseType) { - if (Item.betterThanStashed(item) && Item.betterBaseThanWearing(item, Developer.debugging.baseCheck)) { - shopReport(item, "better base", result.line); - item.buy() && bought++; - - continue; - } - } else { - shopReport(item, "NoTier", result.line); - item.buy() && bought++; - - continue; - } - } - } catch (e) { - console.error(e); - } - } - - // tier'ed items - if (result.result === Pickit.Result.SOLOWANTS && AutoEquip.wanted(item)) { - const checkDependancy = (item) => { - let check = Item.hasDependancy(item); - if (check) { - let el = npc.getItem(check); - !!el && el.buy(); - } - }; - try { - if (Storage.Inventory.CanFit(item) && myGold >= itemCost && (myGold - itemCost > goldLimit)) { - if (Item.hasTier(item) && Item.autoEquipCheck(item)) { - try { - shopReport(item, "AutoEquip", result.line, (item.fname + " Tier: " + NTIP.GetTier(item))); - item.buy() && bought++; - Item.autoEquip("InShop"); - } catch (e) { - console.error(e); - } - - checkDependancy(item); - - continue; - } - - if (!lowLevelShop && haveMerc && Item.hasMercTier(item) && Item.autoEquipCheckMerc(item)) { - shopReport(item, "AutoEquipMerc", result.line, (item.fname + " Tier: " + NTIP.GetMercTier(item))); - item.buy() && bought++; - - continue; - } - - if (Item.hasSecondaryTier(item) && Item.autoEquipCheckSecondary(item)) { - try { - shopReport(item, "AutoEquip Switch Shopped", result.line, (item.fname + " SecondaryTier: " + NTIP.GetSecondaryTier(item))); - item.buy() && bought++; - Item.autoEquip("InShop"); - } catch (e) { - console.error(e); - } - - checkDependancy(item); - - continue; - } - } - } catch (e) { - console.error(e); - } - } - - delay(2); - } - - Town.lastShopped.tick = getTickCount(); - Town.lastShopped.who = npc.name; - - console.info(false, "Bought " + bought + " items", "shopItems"); - - return true; -}; - -Town.gamble = function () { - if (!this.needGamble() || Config.GambleItems.length === 0) return true; - - let list = []; - - if (this.gambleIds.length === 0) { - // change text to classid - for (let i = 0; i < Config.GambleItems.length; i += 1) { - if (isNaN(Config.GambleItems[i])) { - if (NTIPAliasClassID.hasOwnProperty(Config.GambleItems[i].replace(/\s+/g, "").toLowerCase())) { - this.gambleIds.push(NTIPAliasClassID[Config.GambleItems[i].replace(/\s+/g, "").toLowerCase()]); - } else { - Misc.errorReport("ÿc1Invalid gamble entry:ÿc0 " + Config.GambleItems[i]); - } - } else { - this.gambleIds.push(Config.GambleItems[i]); - } - } - } - - if (this.gambleIds.length === 0) return true; - - // avoid Alkor - me.act === 3 && this.goToTown(Pather.accessToAct(4) ? 4 : 2); - - let npc = this.initNPC("Gamble", "gamble"); - if (!npc) return false; - - let items = me.findItems(-1, sdk.items.mode.inStorage, sdk.storage.Inventory); - - while (items && items.length > 0) { - list.push(items.shift().gid); - } - - while (me.gold >= Config.GambleGoldStop) { - !getInteractedNPC() && npc.startTrade("Gamble"); - - let item = npc.getItem(); - items = []; - - if (item) { - do { - this.gambleIds.includes(item.classid) && items.push(copyUnit(item)); - } while (item.getNext()); - - for (let i = 0; i < items.length; i += 1) { - if (!Storage.Inventory.CanFit(items[i])) return false; - - items[i].buy(false, true); - - let newItem = this.getGambledItem(list); - - if (newItem) { - let result = Pickit.checkItem(newItem); - - switch (result.result) { - case Pickit.Result.WANTED: - Item.logger("Gambled", newItem); - Item.logItem("Gambled", newItem, result.line); - list.push(newItem.gid); - - break; - case Pickit.Result.CUBING: - list.push(newItem.gid); - Cubing.update(); - - break; - case Pickit.Result.CRAFTING: - CraftingSystem.update(newItem); - - break; - default: - Item.logger("Sold", newItem, "Gambling"); - newItem.sell(); - - if (!Config.PacketShopping) { - delay(500); - } - - break; - } - } - } - } - - me.cancel(); - } + NPCAction.fillTome(sdk.items.TomeofTownPortal); // Check for TP tome in case it got sold for ID scrolls return true; }; @@ -886,130 +352,9 @@ Town.stash = function (stashGold = true) { return true; }; -Town.sortInventory = function () { - Storage.Inventory.SortItems(SetUp.sortSettings.ItemsSortedFromLeft, SetUp.sortSettings.ItemsSortedFromRight); - return true; -}; - Town.sortStash = function (force = false) { if (Storage.Stash.UsedSpacePercent() < 50 && !force) return true; - Storage.Stash.SortItems(); - - return true; -}; - -Town.repair = function (force = false) { - if (this.cubeRepair()) return true; - - let npc; - let repairAction = this.needRepair(); - force && repairAction.indexOf("repair") === -1 && repairAction.push("repair"); - if (!repairAction || !repairAction.length) return false; - - for (let i = 0; i < repairAction.length; i += 1) { - switch (repairAction[i]) { - case "repair": - me.act === 3 && this.goToTown(Pather.accessToAct(4) ? 4 : 2); - npc = this.initNPC("Repair", "repair"); - if (!npc) return false; - me.repair(); - - break; - case "buyQuiver": - let bowCheck = Attack.usingBow(); - let switchBowCheck = CharData.skillData.bowData.bowOnSwitch; - !bowCheck && switchBowCheck && (bowCheck = (() => { - switch (CharData.skillData.bowData.bowType) { - case sdk.items.type.Bow: - case sdk.items.type.AmazonBow: - return "bow"; - case sdk.items.type.Crossbow: - return "crossbow"; - default: - return ""; - } - })()); - - if (bowCheck) { - let quiver = bowCheck === "bow" ? "aqv" : "cqv"; - switchBowCheck && me.switchWeapons(sdk.player.slot.Secondary); - let myQuiver = me.getItem(quiver, sdk.items.mode.Equipped); - !!myQuiver && myQuiver.drop(); - - npc = this.initNPC("Repair", "buyQuiver"); - if (!npc) return false; - - quiver = npc.getItem(quiver); - !!quiver && quiver.buy(); - switchBowCheck && me.switchWeapons(sdk.player.slot.Main); - } - - break; - } - } - - Town.shopItems(); - - return true; -}; - -Town.reviveMerc = function () { - if (!me.needMerc()) return true; - let preArea = me.area; - - // avoid Aheara - me.act === 3 && this.goToTown(Pather.accessToAct(4) ? 4 : 2); - - let npc = this.initNPC("Merc", "reviveMerc"); - if (!npc) return false; - - MainLoop: - for (let i = 0; i < 3; i += 1) { - let dialog = getDialogLines(); - if (!dialog) continue; - - for (let lines = 0; lines < dialog.length; lines += 1) { - if (dialog[lines].text.match(":", "gi")) { - dialog[lines].handler(); - delay(Math.max(750, me.ping * 2)); - } - - // "You do not have enough gold for that." - if (dialog[lines].text.match(getLocaleString(sdk.locale.dialog.youDoNotHaveEnoughGoldForThat), "gi")) { - dialog.find(line => !line.text.match(getLocaleString(sdk.locale.dialog.youDoNotHaveEnoughGoldForThat), "gi")).handler(); - delay(Math.max(750, me.ping * 2)); - me.cancelUIFlags(); - console.error("Could not revive merc: My current gold: " + me.gold + ", current mercrevivecost: " + me.mercrevivecost); - return false; - } - } - - let tick = getTickCount(); - - while (getTickCount() - tick < 2000) { - if (me.getMercEx()) { - delay(Math.max(750, me.ping * 2)); - - break MainLoop; - } - - delay(200); - } - } - - Attack.checkInfinity(); - - if (me.getMercEx()) { - // Cast BO on merc so he doesn't just die again. Only do this is you are a barb or actually have a cta. Otherwise its just a waste of time. - if (Config.MercWatch && Precast.needOutOfTownCast()) { - console.log("MercWatch precast"); - Precast.doRandomPrecast(true, preArea); - } - - return true; - } - - return false; + return Storage.Stash.SortItems(); }; Town.clearInventory = function () { @@ -1027,7 +372,7 @@ Town.clearInventory = function () { } // Remove potions in the wrong slot of our belt - this.clearBelt(); + me.clearBelt(); // Return potions from inventory to belt me.cleanUpInvoPotions(); @@ -1042,25 +387,25 @@ Town.clearInventory = function () { ].includes(p.itemType)); if (potsInInventory.length > 0) { - let hp = [], mp = [], rv = [], specials = []; - potsInInventory.forEach(function (p) { - if (!p || p === undefined) return false; - - switch (p.itemType) { - case sdk.items.type.HealingPotion: - return (hp.push(p)); - case sdk.items.type.ManaPotion: - return (mp.push(p)); - case sdk.items.type.RejuvPotion: - return (rv.push(p)); - case sdk.items.type.ThawingPotion: - case sdk.items.type.AntidotePotion: - case sdk.items.type.StaminaPotion: - return (specials.push(p)); - } - - return false; - }); + let [hp, mp, rv, specials] = [[], [], [], []]; + + while (potsInInventory.length) { + (function (p) { + switch (p.itemType) { + case sdk.items.type.HealingPotion: + return (hp.push(p)); + case sdk.items.type.ManaPotion: + return (mp.push(p)); + case sdk.items.type.RejuvPotion: + return (rv.push(p)); + case sdk.items.type.ThawingPotion: + case sdk.items.type.AntidotePotion: + case sdk.items.type.StaminaPotion: + default: // shuts d2bs up + return (specials.push(p)); + } + })(potsInInventory.shift()); + } // Cleanup healing potions while (hp.length > Config.HPBuffer) { @@ -1106,7 +451,15 @@ Town.clearInventory = function () { return false; }); + /** + * @type {ItemUnit[]} + */ let sell = []; + + /** + * @param {ItemUnit} item + * @returns {boolean} + */ const classItemType = (item) => [ sdk.items.type.Wand, sdk.items.type.VoodooHeads, sdk.items.type.AuricShields, sdk.items.type.PrimalHelm, sdk.items.type.Pelt ].includes(item.itemType); @@ -1132,7 +485,7 @@ Town.clearInventory = function () { sell.length > 0 && this.initNPC("Shop", "clearInventory") && sell.forEach(function (item) { try { if (getUIFlag(sdk.uiflags.Shop) || getUIFlag(sdk.uiflags.NPCMenu)) { - console.log("clearInventory sell " + item.name); + console.log("clearInventory sell " + item.prettyPrint); Item.logger("Sold", item); item.sell(); delay(100); @@ -1149,15 +502,24 @@ Town.clearInventory = function () { Town.clearJunk = function () { let junkItems = me.getItemsEx() - .filter(i => i.isInStorage && !Town.ignoredItemTypes.includes(i.itemType) && i.sellable && !Town.systemsKeep(i)); + .filter(i => i.isInStorage && !Town.ignoreType(i.itemType) && i.sellable && !Town.systemsKeep(i)); if (!junkItems.length) return false; + /** + * @type {ItemUnit[][]} + */ let [totalJunk, junkToSell, junkToDrop] = [[], [], []]; + + /** + * @param {string} str + * @param {ItemUnit} item + * @returns {boolean} + */ const getToItem = (str = "", item = null) => { if (!getUIFlag(sdk.uiflags.Stash) && item.isInStash && !Town.openStash()) { - throw new Error("ÿc9" + str + "ÿc0 :: Failed to get " + item.name + " from stash"); + throw new Error("ÿc9" + str + "ÿc0 :: Failed to get " + item.prettyPrint + " from stash"); } - if (item.isInCube && !Cubing.emptyCube()) throw new Error("ÿc9" + str + "ÿc0 :: Failed to remove " + item.name + " from cube"); + if (item.isInCube && !Cubing.emptyCube()) throw new Error("ÿc9" + str + "ÿc0 :: Failed to remove " + item.prettyPrint + " from cube"); return true; }; @@ -1167,7 +529,7 @@ Town.clearJunk = function () { try { if ([Pickit.Result.UNWANTED, Pickit.Result.TRASH].includes(pickitResult)) { - console.log("ÿc9JunkCheckÿc0 :: Junk: " + junk.name + " Pickit Result: " + pickitResult); + console.log("ÿc9JunkCheckÿc0 :: Junk: " + junk.prettyPrint + " Pickit Result: " + pickitResult); getToItem("JunkCheck", junk) && totalJunk.push(junk); continue; @@ -1175,7 +537,7 @@ Town.clearJunk = function () { if (pickitResult !== Pickit.Result.WANTED) { if (!junk.identified && !Cubing.keepItem(junk) && !CraftingSystem.keepItem(junk) && junk.quality < sdk.items.quality.Set) { - console.log("ÿc9UnidJunkCheckÿc0 :: Junk: " + junk.name + " Junk type: " + junk.itemType + " Pickit Result: " + pickitResult); + console.log("ÿc9UnidJunkCheckÿc0 :: Junk: " + junk.prettyPrint + " Junk type: " + junk.itemType + " Pickit Result: " + pickitResult); getToItem("UnidJunkCheck", junk) && totalJunk.push(junk); continue; @@ -1183,7 +545,7 @@ Town.clearJunk = function () { } if (junk.isRuneword && !AutoEquip.wanted(junk)) { - console.log("ÿc9AutoEquipJunkCheckÿc0 :: Junk: " + junk.name + " Junk type: " + junk.itemType + " Pickit Result: " + pickitResult); + console.log("ÿc9AutoEquipJunkCheckÿc0 :: Junk: " + junk.prettyPrint + " Junk type: " + junk.itemType + " Pickit Result: " + pickitResult); getToItem("AutoEquipJunkCheck", junk) && totalJunk.push(junk); continue; @@ -1191,14 +553,14 @@ Town.clearJunk = function () { if (junk.isBaseType && pickitResult === Pickit.Result.SOLOWANTS) { if (!Item.betterThanStashed(junk)) { - console.log("ÿc9BetterThanStashedCheckÿc0 :: Base: " + junk.name + " Junk type: " + junk.itemType + " Pickit Result: " + pickitResult); + console.log("ÿc9BetterThanStashedCheckÿc0 :: Base: " + junk.prettyPrint + " Junk type: " + junk.itemType + " Pickit Result: " + pickitResult); getToItem("BetterThanStashedCheck", junk) && totalJunk.push(junk); continue; } if (!Item.betterBaseThanWearing(junk, Developer.debugging.baseCheck)) { - console.log("ÿc9BetterThanWearingCheckÿc0 :: Base: " + junk.name + " Junk type: " + junk.itemType + " Pickit Result: " + pickitResult); + console.log("ÿc9BetterThanWearingCheckÿc0 :: Base: " + junk.prettyPrint + " Junk type: " + junk.itemType + " Pickit Result: " + pickitResult); getToItem("BetterThanWearingCheck", junk) && totalJunk.push(junk); continue; @@ -1227,7 +589,7 @@ Town.clearJunk = function () { if (getUIFlag(sdk.uiflags.Shop) || (Config.PacketShopping && getInteractedNPC() && getInteractedNPC().itemcount > 0)) { for (let i = 0; i < junkToSell.length; i++) { - console.log("ÿc9JunkCheckÿc0 :: Sell " + junkToSell[i].name); + console.log("ÿc9JunkCheckÿc0 :: Sell " + junkToSell[i].prettyPrint); Item.logger("Sold", junkToSell[i]); Developer.debugging.junkCheck && Item.logItem("JunkCheck Sold", junkToSell[i]); @@ -1239,7 +601,7 @@ Town.clearJunk = function () { me.cancelUIFlags(); for (let i = 0; i < junkToDrop.length; i++) { - console.log("ÿc9JunkCheckÿc0 :: Drop " + junkToDrop[i].name); + console.log("ÿc9JunkCheckÿc0 :: Drop " + junkToDrop[i].prettyPrint); Item.logger("Sold", junkToDrop[i]); Developer.debugging.junkCheck && Item.logItem("JunkCheck Sold", junkToDrop[i]); @@ -1251,6 +613,12 @@ Town.clearJunk = function () { return true; }; +/** + * @override + * @param {boolean} repair + * @param {extraTasks} givenTasks + * @returns {boolean} + */ Town.doChores = function (repair = false, givenTasks = {}) { const extraTasks = Object.assign({}, { thawing: false, @@ -1276,38 +644,38 @@ Town.doChores = function (repair = false, givenTasks = {}) { me.switchWeapons(Attack.getPrimarySlot()); extraTasks.fullChores && Quest.unfinishedQuests(); - me.getUnids().length && me.gold < 5000 && this.cainID(true); + me.getUnids().length && me.gold < 5000 && NPCAction.cainID(true); this.heal(); this.identify(); this.clearInventory(); - this.fillTome(sdk.items.TomeofTownPortal); - Config.FieldID.Enabled && this.fillTome(sdk.items.TomeofIdentify); + NPCAction.fillTome(sdk.items.TomeofTownPortal); + Config.FieldID.Enabled && NPCAction.fillTome(sdk.items.TomeofIdentify); !!me.getItem(sdk.items.TomeofTownPortal) && this.clearScrolls(); - this.buyPotions(); + NPCAction.buyPotions(); this.buyKeys(); extraTasks.thawing && CharData.buffData.thawing.need() && Town.buyPots(12, "Thawing", true); extraTasks.antidote && CharData.buffData.antidote.need() && Town.buyPots(12, "Antidote", true); extraTasks.stamina && Town.buyPots(12, "Stamina", true); - this.shopItems(); - this.repair(repair) && this.shopItems(true); - this.reviveMerc(); - this.gamble(); + NPCAction.shopItems(); + NPCAction.repair(repair) && NPCAction.shopItems(true); + NPCAction.reviveMerc(); + NPCAction.gamble(); Cubing.emptyCube(); Runewords.makeRunewords(); Cubing.doCubing(); Runewords.makeRunewords(); - AutoEquip.runAutoEquip(); + AutoEquip.run(); Mercenary.hireMerc(); Item.autoEquipMerc(); Town.haveItemsToSell() && Town.sellItems() && me.cancelUIFlags(); this.clearJunk(); this.stash(); // check pots again, we might have enough gold now if we didn't before - me.needPotions() && this.buyPotions() && me.cancelUIFlags(); + me.needPotions() && NPCAction.buyPotions() && me.cancelUIFlags(); // check repair again, we might have enough gold now if we didn't before - me.needRepair() && this.repair() && me.cancelUIFlags(); + me.needRepair() && NPCAction.repair() && me.cancelUIFlags(); - this.sortInventory(); + me.sortInventory(); extraTasks.fullChores && this.sortStash(); Quest.characterRespec(); @@ -1324,7 +692,7 @@ Town.doChores = function (repair = false, givenTasks = {}) { delay(300); console.debug("doChores Ending Gold :: " + me.gold); console.info(false, null, "doChores"); - Town.lastInteractedNPC.reset(); // unassign + // Town.lastInteractedNPC.reset(); // unassign return true; }; diff --git a/libs/SoloPlay/Scripts/ancients.js b/libs/SoloPlay/Scripts/ancients.js index 580bc92c..70fb2602 100644 --- a/libs/SoloPlay/Scripts/ancients.js +++ b/libs/SoloPlay/Scripts/ancients.js @@ -35,7 +35,7 @@ function ancients () { CharData.updateConfig(); me.overhead("updated settings"); - Town.buyPotions(); + NPCAction.buyPotions(); if (!Pather.usePortal(sdk.areas.ArreatSummit, me.name)) { console.log("ÿc8Kolbot-SoloPlayÿc0: Failed to take portal back to Arreat Summit"); Pather.clearToExit(sdk.areas.AncientsWay, sdk.areas.ArreatSummit, true); // enter Arreat Summit diff --git a/libs/SoloPlay/Scripts/cows.js b/libs/SoloPlay/Scripts/cows.js index 6777cd94..e7dea7fe 100644 --- a/libs/SoloPlay/Scripts/cows.js +++ b/libs/SoloPlay/Scripts/cows.js @@ -105,7 +105,7 @@ function cows () { } me.cancel(); - Town.sortInventory(); + me.sortInventory(); return true; }; @@ -120,7 +120,7 @@ function cows () { Town.doChores(); openPortal(sdk.areas.MooMooFarm, sdk.items.quest.WirtsLeg, sdk.items.TomeofTownPortal); - Town.fillTome(sdk.items.TomeofTownPortal); + NPCAction.fillTome(sdk.items.TomeofTownPortal); // when does this become not worth it if (Pather.canTeleport()) { diff --git a/libs/SoloPlay/Scripts/den.js b/libs/SoloPlay/Scripts/den.js index 9e68d0a1..4f968696 100644 --- a/libs/SoloPlay/Scripts/den.js +++ b/libs/SoloPlay/Scripts/den.js @@ -19,7 +19,7 @@ function den () { myPrint("starting den"); - me.gold > 500 && Town.initNPC("repair", "shopItems") && Town.shopItems(500, [sdk.items.type.Bow, sdk.items.type.Crossbow, sdk.items.type.Belt]); + me.gold > 500 && Town.initNPC("repair", "shopItems") && NPCAction.shopItems(500, [sdk.items.type.Bow, sdk.items.type.Crossbow, sdk.items.type.Belt]); me.gold > 1000 && Town.buyPots(12, "stamina", true); if (!Pather.checkWP(sdk.areas.ColdPlains) || me.charlvl < 4) { diff --git a/libs/SoloPlay/Scripts/orgtorch.js b/libs/SoloPlay/Scripts/orgtorch.js index 2ae18228..da97d182 100644 --- a/libs/SoloPlay/Scripts/orgtorch.js +++ b/libs/SoloPlay/Scripts/orgtorch.js @@ -259,7 +259,7 @@ function orgtorch() { this.juvCheck = function () { let i, needJuvs = 0, - col = Town.checkColumns(Storage.BeltSize()); + col = Storage.Belt.checkColumns(Storage.BeltSize()); for (i = 0; i < 4; i += 1) { if (Config.BeltColumn[i] === "rv") { diff --git a/libs/SoloPlay/Threads/ToolsThread.js b/libs/SoloPlay/Threads/ToolsThread.js index 367b60a6..67d09db9 100644 --- a/libs/SoloPlay/Threads/ToolsThread.js +++ b/libs/SoloPlay/Threads/ToolsThread.js @@ -351,7 +351,7 @@ function main () { if (!!itemToCheck) { let special = ""; if (itemToCheck.itemType === sdk.items.type.Ring) { - special = (" | ÿc4TierLHS: ÿc0" + tierscore(itemToCheck, sdk.body.RingRight) + " | ÿc4TierRHS: ÿc0" + tierscore(itemToCheck, sdk.body.RingLeft)); + special = (" | ÿc4TierLHS: ÿc0" + tierscore(itemToCheck, 1, sdk.body.RingRight) + " | ÿc4TierRHS: ÿc0" + tierscore(itemToCheck, 1, sdk.body.RingLeft)); } itemString = "ÿc4MaxQuantity: ÿc0" + NTIP.getMaxQuantity(itemToCheck) + " | ÿc4ItemsOwned: ÿc0" + Item.getQuantityOwned(itemToCheck) + " | ÿc4Tier: ÿc0" + NTIP.GetTier(itemToCheck) + (special ? special : "") + " | ÿc4SecondaryTier: ÿc0" + NTIP.GetSecondaryTier(itemToCheck) + " | ÿc4MercTier: ÿc0" + NTIP.GetMercTier(itemToCheck) + "\n" diff --git a/libs/SoloPlay/index.d.ts b/libs/SoloPlay/index.d.ts index 9e3251cd..68cb097c 100644 --- a/libs/SoloPlay/index.d.ts +++ b/libs/SoloPlay/index.d.ts @@ -58,6 +58,8 @@ declare global { getItemsForRepair(repairPercent: number, chargedItems?: boolean): ItemUnit[]; needRepair(): string[]; needMerc(): boolean; + clearBelt(): boolean; + sortInventory(): boolean; } interface Container { @@ -176,5 +178,29 @@ declare global { namespace Check { } + + namespace NPCAction { + function buyPotions(): boolean; + function fillTome(classid: number, force?: boolean): boolean; + function cainID(force?: boolean): boolean; + function shopItems(force?: boolean): boolean; + function gamble(): boolean; + function repair(force?: boolean): boolean; + function reviveMerc(): boolean; + } + + namespace AutoEquip { + } + + type extraTasks = { + thawing?: boolean, + antidote?: boolean, + stamina?: boolean, + fullChores?: boolean, + }; + + namespace Town { + function doChores(repair?: boolean, givenTasks?: extraTasks): boolean; + } } export{}; From 326427a79460893083b6d6f2c0cd63ef2fcdf3a6 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Sat, 18 Feb 2023 12:56:59 -0500 Subject: [PATCH 037/263] Minor formatting --- .../ClassAttackOverrides/BarbarianAttacks.js | 4 +++- .../ClassAttackOverrides/DruidAttacks.js | 16 +++++++++++++--- libs/SoloPlay/Functions/PatherOverrides.js | 2 +- 3 files changed, 17 insertions(+), 5 deletions(-) diff --git a/libs/SoloPlay/Functions/ClassAttackOverrides/BarbarianAttacks.js b/libs/SoloPlay/Functions/ClassAttackOverrides/BarbarianAttacks.js index 336c49c5..c44e8ea6 100644 --- a/libs/SoloPlay/Functions/ClassAttackOverrides/BarbarianAttacks.js +++ b/libs/SoloPlay/Functions/ClassAttackOverrides/BarbarianAttacks.js @@ -213,7 +213,9 @@ includeIfNotIncluded("core/Attacks/Barbarian.js"); } if (unit.distance < data.battleCry.range) { - data.switchCast ? Skill.switchCast(sdk.skills.BattleCry, {hand: 0, switchBack: !data.warCry.have}) : Skill.cast(sdk.skills.BattleCry, sdk.skills.hand.Right, unit); + data.switchCast + ? Skill.switchCast(sdk.skills.BattleCry, { hand: sdk.skills.hand.Right, switchBack: !data.warCry.have }) + : Skill.cast(sdk.skills.BattleCry, sdk.skills.hand.Right); } } } diff --git a/libs/SoloPlay/Functions/ClassAttackOverrides/DruidAttacks.js b/libs/SoloPlay/Functions/ClassAttackOverrides/DruidAttacks.js index 80fc14ea..c742231a 100644 --- a/libs/SoloPlay/Functions/ClassAttackOverrides/DruidAttacks.js +++ b/libs/SoloPlay/Functions/ClassAttackOverrides/DruidAttacks.js @@ -12,6 +12,11 @@ includeIfNotIncluded("core/Attacks/Druid.js"); +/** + * @param {Monster} unit + * @param {boolean} preattack + * @returns {AttackResult} + */ ClassAttack.doAttack = function (unit, preattack) { if (!unit) return Attack.Result.SUCCESS; let gid = unit.gid; @@ -171,12 +176,18 @@ ClassAttack.doAttack = function (unit, preattack) { return result; }; +/** + * @param {Monster} unit + * @param {number} timedSkill + * @param {number} untimedSkill + * @returns {AttackResult} + */ ClassAttack.doCast = function (unit, timedSkill, untimedSkill) { - let walk; - // No valid skills can be found if (timedSkill < 0 && untimedSkill < 0) return Attack.Result.CANTATTACK; + let walk; + // Rebuff Hurricane Skill.canUse(sdk.skills.Hurricane) && !me.getState(sdk.states.Hurricane) && Skill.cast(sdk.skills.Hurricane, sdk.skills.hand.Right); @@ -234,4 +245,3 @@ ClassAttack.doCast = function (unit, timedSkill, untimedSkill) { return Attack.Result.SUCCESS; }; - diff --git a/libs/SoloPlay/Functions/PatherOverrides.js b/libs/SoloPlay/Functions/PatherOverrides.js index cf3cce0e..73fc9e11 100644 --- a/libs/SoloPlay/Functions/PatherOverrides.js +++ b/libs/SoloPlay/Functions/PatherOverrides.js @@ -633,7 +633,7 @@ Pather.moveNear = function (x, y, minDist, givenSettings = {}) { return Pather.move({ x: x, y: y }, Object.assign({ minDist: minDist }, givenSettings)); }; -Pather.moveTo = function (x = undefined, y = undefined, retry = undefined, clearPath = true, pop = false) { +Pather.moveTo = function (x, y, retry, clearPath = true, pop = false) { return Pather.move({ x: x, y: y }, { retry: retry, pop: pop, clearSettings: { clearPath: clearPath } }); }; From 4b16323791b640d4e04b523311904bb4fe632e6d Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Sat, 18 Feb 2023 21:40:54 -0500 Subject: [PATCH 038/263] Town.needMerc -> me.needMerc --- .../Functions/ClassAttackOverrides/AmazonAttacks-WIP.js | 4 ++-- libs/SoloPlay/Functions/ClassAttackOverrides/AmazonAttacks.js | 4 ++-- .../Functions/ClassAttackOverrides/AssassinAttacks.js | 2 +- .../Functions/ClassAttackOverrides/BarbarianAttacks.js | 2 +- libs/SoloPlay/Functions/ClassAttackOverrides/DruidAttacks.js | 4 ++-- .../Functions/ClassAttackOverrides/NecromancerAttacks.js | 4 ++-- .../SoloPlay/Functions/ClassAttackOverrides/PaladinAttacks.js | 4 ++-- 7 files changed, 12 insertions(+), 12 deletions(-) diff --git a/libs/SoloPlay/Functions/ClassAttackOverrides/AmazonAttacks-WIP.js b/libs/SoloPlay/Functions/ClassAttackOverrides/AmazonAttacks-WIP.js index 1aa78253..018df3bf 100644 --- a/libs/SoloPlay/Functions/ClassAttackOverrides/AmazonAttacks-WIP.js +++ b/libs/SoloPlay/Functions/ClassAttackOverrides/AmazonAttacks-WIP.js @@ -64,7 +64,7 @@ ClassAttack.doAttack = function (unit) { let gid = unit.gid; let needRepair = me.charlvl < 5 ? [] : me.needRepair(); - if ((Config.MercWatch && Town.needMerc()) || needRepair.length > 0) { + if ((Config.MercWatch && me.needMerc()) || needRepair.length > 0) { console.log("towncheck"); if (Town.visitTown(!!needRepair.length)) { @@ -300,7 +300,7 @@ ClassAttack.doAttack = function (unit) { if (!unit) return Attack.Result.SUCCESS; - if (Town.needMerc()) { + if (me.needMerc()) { if (Config.MercWatch && mercRevive++ < 1) { Town.visitTown(); } else { diff --git a/libs/SoloPlay/Functions/ClassAttackOverrides/AmazonAttacks.js b/libs/SoloPlay/Functions/ClassAttackOverrides/AmazonAttacks.js index a5674997..0c2b90cd 100644 --- a/libs/SoloPlay/Functions/ClassAttackOverrides/AmazonAttacks.js +++ b/libs/SoloPlay/Functions/ClassAttackOverrides/AmazonAttacks.js @@ -28,7 +28,7 @@ ClassAttack.doAttack = function (unit, preattack, once) { let gid = unit.gid; let needRepair = me.charlvl < 5 ? [] : me.needRepair(); - if ((Config.MercWatch && Town.needMerc()) || needRepair.length > 0) { + if ((Config.MercWatch && me.needMerc()) || needRepair.length > 0) { console.log("towncheck"); if (Town.visitTown(!!needRepair.length)) { @@ -297,7 +297,7 @@ ClassAttack.doAttack = function (unit, preattack, once) { if (!unit) return Attack.Result.SUCCESS; - if (Town.needMerc()) { + if (me.needMerc()) { if (Config.MercWatch && mercRevive++ < 1) { Town.visitTown(); } else { diff --git a/libs/SoloPlay/Functions/ClassAttackOverrides/AssassinAttacks.js b/libs/SoloPlay/Functions/ClassAttackOverrides/AssassinAttacks.js index 5370393e..82e443d0 100644 --- a/libs/SoloPlay/Functions/ClassAttackOverrides/AssassinAttacks.js +++ b/libs/SoloPlay/Functions/ClassAttackOverrides/AssassinAttacks.js @@ -225,7 +225,7 @@ ClassAttack.doAttack = function (unit, preattack) { if (!unit) return Attack.Result.SUCCESS; - if (Town.needMerc()) { + if (me.needMerc()) { if (Config.MercWatch && mercRevive++ < 1) { Town.visitTown(); } else { diff --git a/libs/SoloPlay/Functions/ClassAttackOverrides/BarbarianAttacks.js b/libs/SoloPlay/Functions/ClassAttackOverrides/BarbarianAttacks.js index c44e8ea6..80449126 100644 --- a/libs/SoloPlay/Functions/ClassAttackOverrides/BarbarianAttacks.js +++ b/libs/SoloPlay/Functions/ClassAttackOverrides/BarbarianAttacks.js @@ -116,7 +116,7 @@ includeIfNotIncluded("core/Attacks/Barbarian.js"); let needRepair = [], gold = me.gold; me.charlvl >= 5 && (needRepair = me.needRepair()); - if ((Config.MercWatch && Town.needMerc()) || needRepair.length > 0) { + if ((Config.MercWatch && me.needMerc()) || needRepair.length > 0) { console.log("towncheck"); if (Town.visitTown(!!needRepair.length)) { diff --git a/libs/SoloPlay/Functions/ClassAttackOverrides/DruidAttacks.js b/libs/SoloPlay/Functions/ClassAttackOverrides/DruidAttacks.js index c742231a..89aac027 100644 --- a/libs/SoloPlay/Functions/ClassAttackOverrides/DruidAttacks.js +++ b/libs/SoloPlay/Functions/ClassAttackOverrides/DruidAttacks.js @@ -21,7 +21,7 @@ ClassAttack.doAttack = function (unit, preattack) { if (!unit) return Attack.Result.SUCCESS; let gid = unit.gid; - if (Config.MercWatch && Town.needMerc()) { + if (Config.MercWatch && me.needMerc()) { console.log("mercwatch"); if (Town.visitTown()) { @@ -149,7 +149,7 @@ ClassAttack.doAttack = function (unit, preattack) { if (!unit) return Attack.Result.SUCCESS; - if (Town.needMerc()) { + if (me.needMerc()) { if (Config.MercWatch && mercRevive++ < 1) { Town.visitTown(); } else { diff --git a/libs/SoloPlay/Functions/ClassAttackOverrides/NecromancerAttacks.js b/libs/SoloPlay/Functions/ClassAttackOverrides/NecromancerAttacks.js index a3414dfb..13c55508 100644 --- a/libs/SoloPlay/Functions/ClassAttackOverrides/NecromancerAttacks.js +++ b/libs/SoloPlay/Functions/ClassAttackOverrides/NecromancerAttacks.js @@ -149,7 +149,7 @@ ClassAttack.doAttack = function (unit, preattack, once) { if (!unit) return Attack.Result.SUCCESS; let gid = unit.gid; - if (Config.MercWatch && Town.needMerc()) { + if (Config.MercWatch && me.needMerc()) { console.log("mercwatch"); if (Town.visitTown()) { @@ -264,7 +264,7 @@ ClassAttack.doAttack = function (unit, preattack, once) { if (!unit) return Attack.Result.SUCCESS; - if (Town.needMerc()) { + if (me.needMerc()) { if (Config.MercWatch && mercRevive++ < 1) { Town.visitTown(); } else { diff --git a/libs/SoloPlay/Functions/ClassAttackOverrides/PaladinAttacks.js b/libs/SoloPlay/Functions/ClassAttackOverrides/PaladinAttacks.js index 0fc493e1..e198e61e 100644 --- a/libs/SoloPlay/Functions/ClassAttackOverrides/PaladinAttacks.js +++ b/libs/SoloPlay/Functions/ClassAttackOverrides/PaladinAttacks.js @@ -22,7 +22,7 @@ ClassAttack.doAttack = function (unit = undefined, preattack = false, once = fal const index = (unit.isSpecial || unit.isPlayer) ? 1 : 3; // prevent running back to town quickly if our merc is just weak - if (Config.MercWatch && Town.needMerc() && getTickCount() - MercWatch.last > Time.seconds(5)) { + if (Config.MercWatch && me.needMerc() && getTickCount() - MercWatch.last > Time.seconds(5)) { console.log("mercwatch"); if (Town.visitTown()) { @@ -162,7 +162,7 @@ ClassAttack.doAttack = function (unit = undefined, preattack = false, once = fal if (!unit) return Attack.Result.SUCCESS; - if (Town.needMerc()) { + if (me.needMerc()) { if (Config.MercWatch && mercRevive++ < 1) { Town.visitTown(); } else { From 5485a88cd791cad85afc858913e7101f5f3ee5a7 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Sat, 18 Feb 2023 21:41:52 -0500 Subject: [PATCH 039/263] Build finalGear list for NTIP --- libs/SoloPlay/BuildFiles/amazon/amazon.FaithbowzonBuild.js | 2 +- .../SoloPlay/BuildFiles/sorceress/sorceress.LightningBuild.js | 4 ++-- libs/SoloPlay/Config/Amazon.js | 1 + libs/SoloPlay/Config/Assassin.js | 1 + libs/SoloPlay/Config/Barbarian.js | 1 + libs/SoloPlay/Config/Druid.js | 1 + libs/SoloPlay/Config/Necromancer.js | 1 + libs/SoloPlay/Config/Paladin.js | 1 + 8 files changed, 9 insertions(+), 3 deletions(-) diff --git a/libs/SoloPlay/BuildFiles/amazon/amazon.FaithbowzonBuild.js b/libs/SoloPlay/BuildFiles/amazon/amazon.FaithbowzonBuild.js index 46b532f1..1af98a9f 100644 --- a/libs/SoloPlay/BuildFiles/amazon/amazon.FaithbowzonBuild.js +++ b/libs/SoloPlay/BuildFiles/amazon/amazon.FaithbowzonBuild.js @@ -36,7 +36,7 @@ const finalBuild = { // Final Weapon - Faith "([type] == amazonbow || [type] == bow) && [flag] == runeword # [fanaticismaura] >= 12 && [itemallskills] >= 1 # [tier] == tierscore(item, 100000)", // Weapon - WitchWild String up'd - "[name] == diamondbow && [quality] == unique # [fireresist] == 40 # [tier] == tierscore(item, 90000)", + "[name] == diamondbow && [quality] == unique # [fireresist] == 40 # [tier] == tierscore(item, 100000)", // Helmet - Vampz Gaze "[name] == grimhelm && [quality] == unique && [flag] != ethereal # [manaleech] >= 6 && [lifeleech] >= 6 && [damageresist] >= 15 # [tier] == tierscore(item, 100000)", // Boots - War Traveler diff --git a/libs/SoloPlay/BuildFiles/sorceress/sorceress.LightningBuild.js b/libs/SoloPlay/BuildFiles/sorceress/sorceress.LightningBuild.js index f8af58d1..b95558ee 100644 --- a/libs/SoloPlay/BuildFiles/sorceress/sorceress.LightningBuild.js +++ b/libs/SoloPlay/BuildFiles/sorceress/sorceress.LightningBuild.js @@ -57,9 +57,9 @@ const finalBuild = { "[type] == amulet && [quality] == unique # [strength] == 5 && [coldresist] >= 30 # [tier] == tierscore(item, 100000)", // Final Rings - SoJ & Perfect Wisp "[type] == ring && [quality] == unique # [itemmaxmanapercent] == 25 # [tier] == 100000", - "[name] == ring && [quality] == unique # [itemabsorblightpercent] == 20 # [tier] == tierscore(item, 100000)", + "[name] == ring && [quality] == unique # [itemabsorblightpercent] == 20 # [tier] == tierscore(item, 110000)", // Rings - Wisp - "[name] == ring && [quality] == unique # [itemabsorblightpercent] >= 10 # [tier] == tierscore(item, 90000)", + "[name] == ring && [quality] == unique # [itemabsorblightpercent] >= 10 # [tier] == tierscore(item, 100000)", // Switch - CTA "[minimumsockets] >= 5 && [flag] == runeword # [plusskillbattleorders] >= 1 # [secondarytier] == 100000", // Switch Final Shield - Spirit diff --git a/libs/SoloPlay/Config/Amazon.js b/libs/SoloPlay/Config/Amazon.js index 96105e7a..b9996263 100644 --- a/libs/SoloPlay/Config/Amazon.js +++ b/libs/SoloPlay/Config/Amazon.js @@ -120,6 +120,7 @@ /* Gear */ let finalGear = Check.finalBuild().finalGear; !!finalGear && NTIP.buildList(finalGear); + NTIP.buildFinalGear(finalGear); Config.imbueables = [ {name: sdk.items.MaidenJavelin, condition: () => me.normal && me.expansion}, diff --git a/libs/SoloPlay/Config/Assassin.js b/libs/SoloPlay/Config/Assassin.js index 2a4bc65f..7330fc2e 100644 --- a/libs/SoloPlay/Config/Assassin.js +++ b/libs/SoloPlay/Config/Assassin.js @@ -125,6 +125,7 @@ /* Gear */ let finalGear = Check.finalBuild().finalGear; !!finalGear && NTIP.buildList(finalGear); + NTIP.buildFinalGear(finalGear); Config.imbueables = [ {name: sdk.items.Claws, condition: () => (me.normal)}, diff --git a/libs/SoloPlay/Config/Barbarian.js b/libs/SoloPlay/Config/Barbarian.js index 3dda8ca0..a4a2818c 100644 --- a/libs/SoloPlay/Config/Barbarian.js +++ b/libs/SoloPlay/Config/Barbarian.js @@ -113,6 +113,7 @@ /* Gear */ let finalGear = Check.finalBuild().finalGear; !!finalGear && NTIP.buildList(finalGear); + NTIP.buildFinalGear(finalGear); Config.imbueables = [ {name: sdk.items.AvengerGuard, condition: () => (me.normal && me.expansion)}, diff --git a/libs/SoloPlay/Config/Druid.js b/libs/SoloPlay/Config/Druid.js index 5ddeaa02..720c19f3 100644 --- a/libs/SoloPlay/Config/Druid.js +++ b/libs/SoloPlay/Config/Druid.js @@ -132,6 +132,7 @@ /* Gear */ let finalGear = Check.finalBuild().finalGear; !!finalGear && NTIP.buildList(finalGear); + NTIP.buildFinalGear(finalGear); Config.imbueables = [ {name: sdk.items.SpiritMask, condition: () => (me.normal)}, diff --git a/libs/SoloPlay/Config/Necromancer.js b/libs/SoloPlay/Config/Necromancer.js index 6f48e2bc..54ab6de4 100644 --- a/libs/SoloPlay/Config/Necromancer.js +++ b/libs/SoloPlay/Config/Necromancer.js @@ -132,6 +132,7 @@ /* Gear */ let finalGear = Check.finalBuild().finalGear; !!finalGear && NTIP.buildList(finalGear); + NTIP.buildFinalGear(finalGear); Config.imbueables = [ {name: sdk.items.DemonHead, condition: () => (me.normal && me.expansion)}, diff --git a/libs/SoloPlay/Config/Paladin.js b/libs/SoloPlay/Config/Paladin.js index 22195709..f7bab559 100644 --- a/libs/SoloPlay/Config/Paladin.js +++ b/libs/SoloPlay/Config/Paladin.js @@ -132,6 +132,7 @@ /* Gear */ let finalGear = Check.finalBuild().finalGear; !!finalGear && NTIP.buildList(finalGear); + NTIP.buildFinalGear(finalGear); // Maybe add auric shield? Config.imbueables = [ From 49e16d06accc302257b204a92e6af3eb542122eb Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Sat, 18 Feb 2023 21:44:16 -0500 Subject: [PATCH 040/263] Check if item is wanted by our final gear list - trying to fix bug where tierscore evaluates as one thing and NTIP.GetTier evaluates as another - Need to do this a better way still, so dual wield items and rings can be properly tier'd and equipped --- libs/SoloPlay/Functions/DynamicTiers.js | 7 ++++--- libs/SoloPlay/Functions/ItemOverrides.js | 10 +++++----- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/libs/SoloPlay/Functions/DynamicTiers.js b/libs/SoloPlay/Functions/DynamicTiers.js index 0e2fc94f..bbb77257 100644 --- a/libs/SoloPlay/Functions/DynamicTiers.js +++ b/libs/SoloPlay/Functions/DynamicTiers.js @@ -606,9 +606,10 @@ } } - // if (tier > 1 && tier < NTIP.MAX_TIER && NTIP.CheckItem(item, NTIP.FinalGear.list) === Pickit.Result.WANTED) { - // tier += NTIP.MAX_TIER; - // } + if (tier > 1 && tier < NTIP.MAX_TIER && NTIP.CheckItem(item, NTIP.FinalGear.list) === Pickit.Result.WANTED) { + // console.debug(item.prettyPrint + "~~~" + tier); + tier += NTIP.MAX_TIER; + } return item.questItem ? -1 : Math.max(1, tier); }; diff --git a/libs/SoloPlay/Functions/ItemOverrides.js b/libs/SoloPlay/Functions/ItemOverrides.js index 426609a3..ee4d593f 100644 --- a/libs/SoloPlay/Functions/ItemOverrides.js +++ b/libs/SoloPlay/Functions/ItemOverrides.js @@ -192,10 +192,10 @@ Item.autoEquipCheck = function (item, basicCheck = false) { } // lets double check that this is the highest tied'd item of this type in our storage - let betterItem = me.getItemsEx() - .filter(el => el.isInStorage && el.gid !== item.gid && Item.getBodyLoc(el).includes(bodyLoc[i])) - .some(el => NTIP.GetTier(el) > tier); - console.debug("Higher tier'd item? " + betterItem); + // let betterItem = me.getItemsEx() + // .filter(el => el.isInStorage && el.gid !== item.gid && Item.getBodyLoc(el).includes(bodyLoc[i])) + // .some(el => NTIP.GetTier(el) > tier); + // console.debug("Higher tier'd item? " + betterItem); return true; } @@ -272,7 +272,7 @@ Item.autoEquip = function (task = "") { } // ring check - sometimes a higher tier ring ends up on the wrong finger causing a rollback loop - if (Item.getEquipped(sdk.body.RingLeft).tierScore > Item.getEquipped(sdk.body.RingRight).tierScore) { + if (Item.getEquipped(sdk.body.RingLeft).tier > Item.getEquipped(sdk.body.RingRight).tier) { console.log("ÿc9" + task + "ÿc0 :: Swapping rings, higher tier ring is on the wrong finger"); clickItemAndWait(sdk.clicktypes.click.item.Left, sdk.body.RingLeft); delay(200); From a15b5b64b75b22a501e0c3c35966f91c8b2e0ef5 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Mon, 20 Feb 2023 01:21:16 -0500 Subject: [PATCH 041/263] Safer anya rescue - Check where frozenstein is instead of just running right to anya, need to implement a clearWhile function and lure him away but for now just attempt clearing - Fix bug where we didn't correctly find anya the first time and when we interacted the second time and were waiting for her to be free we crashed because of the dialog - Fix attempting to grab lam essens tome when we already completed it --- libs/SoloPlay/Scripts/anya.js | 37 +++++++++++++++++++++++++++---- libs/SoloPlay/Scripts/lamessen.js | 10 +++++---- libs/SoloPlay/index.d.ts | 32 ++++++++++++++++++++++++++ 3 files changed, 71 insertions(+), 8 deletions(-) diff --git a/libs/SoloPlay/Scripts/anya.js b/libs/SoloPlay/Scripts/anya.js index f1fdd5cd..1c79b9b7 100644 --- a/libs/SoloPlay/Scripts/anya.js +++ b/libs/SoloPlay/Scripts/anya.js @@ -14,20 +14,45 @@ function anya () { Precast.doPrecast(true); Pather.clearToExit(sdk.areas.CrystalizedPassage, sdk.areas.FrozenRiver, Pather.useTeleport()); - if (!Pather.moveToPreset(me.area, sdk.unittype.Object, sdk.objects.FrozenAnyasPlatform)) { + if (!Pather.moveToPresetObject(me.area, sdk.objects.FrozenAnyasPlatform, { callback: () => { + let fStein = Game.getMonster(getLocaleString(sdk.locale.monsters.Frozenstein)); + // let frozenanya = Game.getObject(sdk.objects.FrozenAnya); + return (fStein && fStein.distance < 30) /*&& /* (frozenanya && frozenanya.distance < 35) */; + }})) { console.log("ÿc8Kolbot-SoloPlayÿc0: Failed to move to Anya"); return false; } + let presetLoc = Game.getPresetObject(me.area, sdk.objects.FrozenAnyasPlatform).realCoords(); + let fStein = Game.getMonster(getLocaleString(sdk.locale.monsters.Frozenstein)); + console.debug("dist anya-stein :: " + getDistance(presetLoc, fStein)); + console.debug(presetLoc, fStein); + + if (presetLoc && fStein && getDistance(presetLoc, fStein) < 15) { + // need to write a clearWhile function + Attack.clear(15, sdk.monsters.spectype.All, fStein); + } + let frozenanya = Game.getObject(sdk.objects.FrozenAnya); + + if (!frozenanya) { + Pather.moveToEx(presetLoc.x, presetLoc.y, { callback: () => Game.getObject(sdk.objects.FrozenAnya)}); + frozenanya = Game.getObject(sdk.objects.FrozenAnya); + } + // todo - tele char can lure frozenstein away from anya as he can be hard to kill // aggro the pack then move back until there isn't any monster around anya (note) we can only detect mobs around 40 yards of us // then should use a static location behind anya as our destination to tele to if (frozenanya) { - Pather.moveToUnit(frozenanya); - Packet.entityInteract(frozenanya); + if (me.sorceress && Skill.haveTK) { + Attack.getIntoPosition(frozenanya, 15, sdk.collision.LineOfSight, Pather.canTeleport(), true); + Packet.telekinesis(frozenanya); + } else { + Pather.moveToUnit(frozenanya); + Packet.entityInteract(frozenanya); + } Misc.poll(() => getIsTalkingNPC(), 2000, 50); - me.cancel(); + me.cancel() && me.cancel(); } Town.npcInteract("malah"); @@ -47,6 +72,10 @@ function anya () { me.cancel() && me.cancel(); break; } + if (getIsTalkingNPC()) { + // in case we failed to interact the first time this prevent us from crashing if her dialog is going + me.cancel() && me.cancel(); + } Attack.clearPos(frozenanya.x, frozenanya.y, 15); } } diff --git a/libs/SoloPlay/Scripts/lamessen.js b/libs/SoloPlay/Scripts/lamessen.js index 0b8a2d68..bdaec097 100644 --- a/libs/SoloPlay/Scripts/lamessen.js +++ b/libs/SoloPlay/Scripts/lamessen.js @@ -1,7 +1,7 @@ /** * @filename lamessen.js -* @author isid0re, theBGuy -* @desc get the lam essen's tome +* @author theBGuy +* @desc Get lamessen's tome * */ @@ -12,11 +12,13 @@ function lamessen () { Pather.checkWP(sdk.areas.KurastBazaar, true) ? Pather.useWaypoint(sdk.areas.KurastBazaar) : Pather.getWP(sdk.areas.KurastBazaar); Precast.doPrecast(true); - if (!Pather.moveToExit(sdk.areas.RuinedTemple, true) || !Pather.moveToPreset(me.area, sdk.unittype.Object, sdk.quest.chest.LamEsensTomeHolder)) { + if (!Pather.moveToExit(sdk.areas.RuinedTemple, true) || !Pather.moveToPresetObject(me.area, sdk.quest.chest.LamEsensTomeHolder)) { throw new Error("Failed to move to LamEssen Tome"); } - Quest.collectItem(sdk.items.quest.LamEsensTome, sdk.quest.chest.LamEsensTomeHolder); + if (!Misc.checkQuest(sdk.quest.id.LamEsensTome, sdk.quest.states.Completed)) { + Quest.collectItem(sdk.items.quest.LamEsensTome, sdk.quest.chest.LamEsensTomeHolder); + } Quest.unfinishedQuests(); return true; diff --git a/libs/SoloPlay/index.d.ts b/libs/SoloPlay/index.d.ts index 68cb097c..be9808a9 100644 --- a/libs/SoloPlay/index.d.ts +++ b/libs/SoloPlay/index.d.ts @@ -176,7 +176,39 @@ declare global { function _delete(deleteMain: boolean): boolean; } + namespace SetUp { + let mercEnabled: boolean; + const currentBuild: string; + const finalBuild: string; + const stopAtLevel: number | false; + + function init(): void; + function include(): void; + function finalRespec(): number; + function getTemplate(): { buildType: string, template: string }; + function specPush(specType: string): number[]; + function makeNext(): void; + function belt(): void; + function buffers(): void; + function bowQuiver(): void; + function imbueItems(): string[]; + function config(): void; + } + namespace Check { + let lowGold: boolean; + + function gold(): boolean; + function brokeAf(): boolean; + function broken(): 0 | 1 | 2; + function brokeCheck(): boolean; + function resistance(): { Status: boolean, FR: number, CR: number, LR: number, PR: number }; + function nextDifficulty(announce: boolean): string | false; + function runes(): boolean; + function haveItem(type: string | number, flag?: string | number, iName?: string): boolean; + } + + namespace SoloWants { } namespace NPCAction { From b467d7e5b2bd9991397fa00a4d724ac458719afe Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Mon, 20 Feb 2023 01:23:33 -0500 Subject: [PATCH 042/263] Little bit of cleanup --- libs/SoloPlay/Functions/CubingOverrides.js | 6 +++--- libs/SoloPlay/Functions/Globals.js | 4 ++-- libs/SoloPlay/Functions/ItemOverrides.js | 11 +++++------ libs/SoloPlay/Functions/NPCAction.js | 6 +++--- 4 files changed, 13 insertions(+), 14 deletions(-) diff --git a/libs/SoloPlay/Functions/CubingOverrides.js b/libs/SoloPlay/Functions/CubingOverrides.js index ea9a28ae..617cff0a 100644 --- a/libs/SoloPlay/Functions/CubingOverrides.js +++ b/libs/SoloPlay/Functions/CubingOverrides.js @@ -669,7 +669,7 @@ Cubing.validItem = function (unit, recipe) { // START let valid = true; const ntipResult = NTIP.CheckItem(unit); - const ntipToTierResult = NTIP.CheckItem(unit, NTIP_CheckListNoTier); + const ntipNoTierResult = NTIP.CheckItem(unit, NTIP_CheckListNoTier); if (recipe.Index >= Recipe.HitPower.Helm && recipe.Index <= Recipe.Safety.Weapon) { if (Math.floor(me.charlvl / 2) + Math.floor(unit.ilvl / 2) < recipe.Level) { @@ -772,13 +772,13 @@ Cubing.validItem = function (unit, recipe) { if (unit.magic && unit.ilvl >= recipe.Level) { if (ntipResult === Pickit.Result.UNWANTED) return true; // should allow for charms that aren't immeaditly wanted by equip and not nip wanted - if (unit.isCharm && !Item.autoEquipCharmCheck(unit) && ntipToTierResult === Pickit.Result.UNWANTED) return true; + if (unit.isCharm && !Item.autoEquipCharmCheck(unit) && ntipNoTierResult === Pickit.Result.UNWANTED) return true; return true; } return false; } else if (recipe.Index === Recipe.Reroll.Charm) { - if (unit.isCharm && unit.magic && (ntipResult === Pickit.Result.UNWANTED || (!Item.autoEquipCharmCheck(unit) && ntipToTierResult === Pickit.Result.UNWANTED))) { + if (unit.isCharm && unit.magic && (ntipResult === Pickit.Result.UNWANTED || (!Item.autoEquipCharmCheck(unit) && ntipNoTierResult === Pickit.Result.UNWANTED))) { switch (unit.itemType) { case sdk.items.type.SmallCharm: if (unit.ilvl >= recipe.Level[unit.code].ilvl) { diff --git a/libs/SoloPlay/Functions/Globals.js b/libs/SoloPlay/Functions/Globals.js index 387d7981..4cee9cd7 100644 --- a/libs/SoloPlay/Functions/Globals.js +++ b/libs/SoloPlay/Functions/Globals.js @@ -621,7 +621,7 @@ const Check = { return false; case me.normal: case !meleeChar && me.nightmare: - this.lowGold = myGold < repairCost; + Check.lowGold = myGold < repairCost; return false; case meleeChar && !me.normal: // check how broke we are - only for melee chars since casters don't care about weapons @@ -725,7 +725,7 @@ const Check = { }, // todo: need to finish up adding locale string ids to sdk so I can remove this in favor of better me.checkItem prototype - haveItem: function (type, flag, iName = undefined) { + haveItem: function (type, flag, iName) { let [isClassID, itemCHECK, typeCHECK] = [false, false, false]; flag && typeof flag === "string" && (flag = flag.capitalize(true)); diff --git a/libs/SoloPlay/Functions/ItemOverrides.js b/libs/SoloPlay/Functions/ItemOverrides.js index ee4d593f..f98669f8 100644 --- a/libs/SoloPlay/Functions/ItemOverrides.js +++ b/libs/SoloPlay/Functions/ItemOverrides.js @@ -192,12 +192,11 @@ Item.autoEquipCheck = function (item, basicCheck = false) { } // lets double check that this is the highest tied'd item of this type in our storage - // let betterItem = me.getItemsEx() - // .filter(el => el.isInStorage && el.gid !== item.gid && Item.getBodyLoc(el).includes(bodyLoc[i])) - // .some(el => NTIP.GetTier(el) > tier); - // console.debug("Higher tier'd item? " + betterItem); + let betterItem = me.getItemsEx() + .filter(el => el.isInStorage && el.gid !== item.gid && el.identified && Item.getBodyLoc(el).includes(bodyLoc[i])) + .some(el => NTIP.GetTier(el) > tier); - return true; + return !betterItem; } } } @@ -272,7 +271,7 @@ Item.autoEquip = function (task = "") { } // ring check - sometimes a higher tier ring ends up on the wrong finger causing a rollback loop - if (Item.getEquipped(sdk.body.RingLeft).tier > Item.getEquipped(sdk.body.RingRight).tier) { + if (Item.getEquipped(sdk.body.RingLeft).tierScore > Item.getEquipped(sdk.body.RingRight).tierScore) { console.log("ÿc9" + task + "ÿc0 :: Swapping rings, higher tier ring is on the wrong finger"); clickItemAndWait(sdk.clicktypes.click.item.Left, sdk.body.RingLeft); delay(200); diff --git a/libs/SoloPlay/Functions/NPCAction.js b/libs/SoloPlay/Functions/NPCAction.js index bdb4b888..ccfa83c6 100644 --- a/libs/SoloPlay/Functions/NPCAction.js +++ b/libs/SoloPlay/Functions/NPCAction.js @@ -220,7 +220,7 @@ let unids = me.getUnids(); - if (unids) { + if (unids.length) { // Check if we may use Cain - number of unid items if (unids.length < Config.CainID.MinUnids && !force) return false; @@ -299,7 +299,7 @@ item.sell(); } else { - console.log("clearInventory dropped " + item.name); + console.log("clearInventory dropped " + item.prettyPrint); Item.logger("Dropped", item, "clearInventory"); item.drop(); } @@ -319,7 +319,7 @@ if (scroll) { if (!Storage.Inventory.CanFit(scroll)) { - let tpTome = me.findItem(sdk.items.TomeofTownPortal, sdk.items.mode.inStorage, sdk.storage.Inventory); + let tpTome = me.getTome(sdk.items.TomeofTownPortal); !!tpTome && tpTome.sell() && delay(500); } From 2b60bc952ea05087aed3fe765ff24fd05713b10b Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Mon, 20 Feb 2023 01:25:08 -0500 Subject: [PATCH 043/263] Rebuild sorc classattack - More efficiently decide what skills to use and don't have to rebuild the base data structure every time. --- .../ClassAttackOverrides/SorceressAttacks.js | 384 ++++++++++-------- 1 file changed, 219 insertions(+), 165 deletions(-) diff --git a/libs/SoloPlay/Functions/ClassAttackOverrides/SorceressAttacks.js b/libs/SoloPlay/Functions/ClassAttackOverrides/SorceressAttacks.js index c4478a94..8dd3db78 100644 --- a/libs/SoloPlay/Functions/ClassAttackOverrides/SorceressAttacks.js +++ b/libs/SoloPlay/Functions/ClassAttackOverrides/SorceressAttacks.js @@ -7,7 +7,13 @@ includeIfNotIncluded("core/Attacks/Sorceress.js"); -(function() { +(function () { + /** + * Can we slow this monster + * @param {Monster} unit + * @param {boolean} freezeable + * @returns {boolean} + */ const slowable = function (unit, freezeable = false) { return (!!unit && unit.attackable // those that we can attack && Attack.checkResist(unit, "cold") @@ -17,13 +23,6 @@ includeIfNotIncluded("core/Attacks/Sorceress.js"); }; const frostNovaCheck = function () { - // return getUnits(sdk.unittype.Monster).some(function(el) { - // return !!el && el.distance < 7 && el.attackable - // && ![sdk.monsters.Andariel].includes(el.classid) - // && !el.isChilled && Attack.checkResist(el, 'cold') - // && !checkCollision(me, el, Coords_1.Collision.BLOCK_MISSILE); - // }); - // don't build whole list - since we are just trying if at least one passes the test // todo - test to time difference between these two methods let mob = Game.getMonster(); @@ -51,49 +50,117 @@ includeIfNotIncluded("core/Attacks/Sorceress.js"); }; /** - * @typedef {Object} dataObj - * @property {number} skill - * @property {number} reqLvl - * @property {number} range - * @property {boolean} have - * @property {number} mana - * @property {boolean} timed - * @property {number} dmg - * @property {Function} assignValues - * @property {Function} calcDmg + * @constructor + * @param {number} skillId + * @param {number} reqLvl + * @param {number} range */ + function ClassData (skillId = -1, range = 0) { + this.have = false; + this.skill = skillId; + this.range = range ? range : Skill.getRange(skillId); + this.mana = Infinity; + this.dmg = 0; + this.timed = Skill.isTimed(skillId); + this.reqLvl = getBaseStat("skills", skillId, "reqlevel"); + } /** - * @param {number} skillId - * @param {number} [reqLvl] - * @param {number} [range] - * @returns {dataObj} + * Initialize data values + * @param {number} [range] + * @returns {void} */ - const buildDataObj = (skillId = -1, reqLvl = 1, range = 0) => ({ - have: false, skill: skillId, range: range ? range : Infinity, mana: Infinity, timed: false, reqLvl: reqLvl, dmg: 0, - assignValues: function (range) { - this.have = Skill.canUse(this.skill); - if (!this.have) return; - this.range = range || Skill.getRange(this.skill); - this.mana = Skill.getManaCost(this.skill); - this.timed = Skill.isTimed(this.skill); - }, - calcDmg: function (unit) { - if (!this.have) return; - this.dmg = GameData.avgSkillDamage(this.skill, unit); - } - }); + ClassData.prototype.assignValues = function (range) { + this.have = Skill.canUse(this.skill); + if (!this.have) return; + this.range = range || Skill.getRange(this.skill); + this.mana = Skill.getManaCost(this.skill); + }; + + /** + * Calculate effective damage for a certain monster unit + * @param {Monster} unit + * @returns {void} + */ + ClassData.prototype.calcDmg = function (unit) { + if (!this.have) return; + this.dmg = GameData.avgSkillDamage(this.skill, unit); + }; + + const AttackData = { + "Attack": new ClassData(sdk.skills.Attack, 4), + "FireBolt": new ClassData(sdk.skills.FireBolt), + "ChargedBolt": new ClassData(sdk.skills.ChargedBolt), + "IceBolt": new ClassData(sdk.skills.IceBolt), + "Inferno": new ClassData(sdk.skills.Inferno), + "Telekinesis": new ClassData(sdk.skills.Telekinesis, 20), + "StaticField": new ClassData(sdk.skills.StaticField), + "IceBlast": new ClassData(sdk.skills.IceBlast), + "FrostNova": new ClassData(sdk.skills.FrostNova), + "FireBall": new ClassData(sdk.skills.FireBall), + // Blaze: new ClassData(sdk.skills.Blaze), + "Lightning": new ClassData(sdk.skills.Lightning), + "Nova": new ClassData(sdk.skills.Nova), + "FireWall": new ClassData(sdk.skills.FireWall), + "ChainLightning": new ClassData(sdk.skills.ChainLightning), + "GlacialSpike": new ClassData(sdk.skills.GlacialSpike), + "Meteor": new ClassData(sdk.skills.Meteor), + // ThunderStorm: new ClassData(sdk.skills.ThunderStorm), + "Blizzard": new ClassData(sdk.skills.Blizzard), + "Hydra": new ClassData(sdk.skills.Hydra), + "FrozenOrb": new ClassData(sdk.skills.FrozenOrb), + }; /** - * @param {dataObj} main - * @param {dataObj} check + * The keys never change so this makes it easier to iterate without calling Object.keys each time + * @type {Array} + */ + const AttackDataKeys = Object.keys(AttackData); + + /** + * Helper function to re-init AttackData + * @param {number} currLvl + * @todo decide when AttackData need to be re-initialized becasue doing it every attack is a waste + */ + const initAttackData = (currLvl = me.charlvl) => { + AttackDataKeys.forEach(sk => { + if (currLvl >= AttackData[sk].reqLvl) { + AttackData[sk].assignValues(); + } + }); + }; + + /** + * Helper function to init damage value for unit + * @param {Monster} unit + */ + const setDamageValues = (unit) => { + AttackDataKeys.forEach(sk => { + if (AttackData[sk].have) { + AttackData[sk].calcDmg(unit); + } + }); + }; + + /** + * Check if this skill is the most damaging + * @param {ClassData} skill * @returns {boolean} */ - const compareDamage = (main, check) => { - if (main.skill === check.skill) return false; - return check.dmg > main.dmg; + const isHighestDmg = (skill) => { + for (let key of AttackDataKeys) { + if (AttackData[key].dmg > skill.dmg) { + return false; + } + } + return true; }; + /** + * Used to handle times when there isn't a valid skill we can use, to prevent throwing error + */ + const DummyData = new ClassData(-1, -1); + /** * @param {Monster} unit * @param {boolean} force @@ -135,30 +202,40 @@ includeIfNotIncluded("core/Attacks/Sorceress.js"); }; /** - * @param {Unit} unit + * @param {Monster} unit * @returns {dataObj} */ ClassAttack.decideDistanceSkill = function (unit) { - const data = {}; const currLvl = me.charlvl; - data.iceBlast = buildDataObj(sdk.skills.IceBlast, 6, 20); - data.fireBall = buildDataObj(sdk.skills.FireBall, 12, 20); - data.lightning = buildDataObj(sdk.skills.Lightning, 12); - data.glacialSpike = buildDataObj(sdk.skills.GlacialSpike, 18, 25); - data.blizzard = buildDataObj(sdk.skills.Blizzard, 24, 40); - data.meteor = buildDataObj(sdk.skills.Meteor, 24, 40); - data.frozenOrb = buildDataObj(sdk.skills.FrozenOrb, 30); - data.hydra = buildDataObj(sdk.skills.Hydra, 30, 40); - Object.keys(data).forEach(k => typeof data[k] === "object" && currLvl >= data[k].reqLvl && data[k].assignValues()); - Object.keys(data).forEach(k => typeof data[k] === "object" && data[k].have && data[k].calcDmg(unit)); - - let skillCheck = Object.keys(data) - .filter(k => typeof data[k] === "object" && data[k].have && me.mp > data[k].mana - && (!data[k].timed || !me.skillDelay)) - .sort((a, b) => data[b].dmg - data[a].dmg).first(); - return typeof data[skillCheck] === "object" ? data[skillCheck] : buildDataObj(-1); + let selected = AttackDataKeys + .filter(sk => { + if (currLvl < AttackData[sk].reqLvl || AttackData[sk].range < 20) return false; + AttackData[sk].assignValues(); + if (!AttackData[sk].have) return false; + AttackData[sk].calcDmg(unit); + /** + * For now, no skill delay check. + * Things to consider: + * 1) If the skill we choose is timed and we are in skillDelay, how long is left to wait? + * 2) If not long then what is the damage difference between the skill we choose and the runner up non-timed skill + * 3) If the non-timed skill will do enough damage to kill this monster then use it, or if we have more than 1-2 seconds to wait + * and we don't need to move to cast the non-timed skill. + * 4) Anything else? + */ + return AttackData[sk].dmg > 0 /* && (!AttackData[k].timed || !me.skillDelay) */; + }) + .sort((a, b) => AttackData[b].dmg - AttackData[a].dmg) + .first(); + return typeof AttackData[selected] === "object" ? AttackData[selected] : DummyData; }; + /** + * @override + * @param {Monster} unit + * @param {boolean} recheckSkill + * @param {boolean} once + * @returns {AttackResult} + */ ClassAttack.doAttack = function (unit, recheckSkill = false, once = false) { Developer.debugging.skills && console.log(sdk.colors.Green + "Test Start-----------------------------------------//"); // unit became invalidated @@ -170,7 +247,7 @@ includeIfNotIncluded("core/Attacks/Sorceress.js"); let tick = getTickCount(); let gold = me.gold; - if (Config.MercWatch && Town.needMerc() && gold > me.mercrevivecost * 3) { + if (Config.MercWatch && me.needMerc() && gold > me.mercrevivecost * 3) { console.debug("mercwatch"); if (Town.visitTown()) { @@ -183,44 +260,18 @@ includeIfNotIncluded("core/Attacks/Sorceress.js"); } } - // Keep Energy Shield active - Skill.canUse(sdk.skills.EnergyShield) && !me.getState(sdk.states.EnergyShield) && Skill.cast(sdk.skills.EnergyShield, sdk.skills.hand.Right); - // Keep Thunder-Storm active - Skill.canUse(sdk.skills.ThunderStorm) && !me.getState(sdk.states.ThunderStorm) && Skill.cast(sdk.skills.ThunderStorm, sdk.skills.hand.Right); + // maybe every couple attacks or just the first one? + Precast.doPrecast(); // Handle Charge skill casting if (index === 1 && me.expansion && !unit.dead) { ClassAttack.switchCurse(unit); } - const data = {}; - data.static = buildDataObj(sdk.skills.StaticField, 6); - data.frostNova = buildDataObj(sdk.skills.FrostNova, 6, 7); - data.iceBlast = buildDataObj(sdk.skills.IceBlast, 6, 15); - data.nova = buildDataObj(sdk.skills.Nova, 12); - data.fireBall = buildDataObj(sdk.skills.FireBall, 12); - data.lightning = buildDataObj(sdk.skills.Lightning, 12); - data.glacialSpike = buildDataObj(sdk.skills.GlacialSpike, 18, 15); - data.frozenOrb = buildDataObj(sdk.skills.FrozenOrb, 30); - data.hydra = buildDataObj(sdk.skills.Hydra, 30); - data.customTimed = buildDataObj(-1); - data.customUntimed = buildDataObj(-1); - // @todo handle if these are already include in the above list, or should I just build all used skils instead and ignore these? - data.mainTimed = buildDataObj(Config.AttackSkill[index]); - data.mainUntimed = buildDataObj(Config.AttackSkill[index + 1]); - data.secondaryTimed = buildDataObj(Config.AttackSkill[5]); - data.secondaryUntimed = buildDataObj(Config.AttackSkill[6]); - - if (Attack.getCustomAttack(unit)) { - let [ts, uts] = Attack.getCustomAttack(unit); - ts > 0 && (data.customTimed = buildDataObj(ts)); - uts > 0 && (data.customUntimed = buildDataObj(uts)); - } - - Object.keys(data).forEach(k => typeof data[k] === "object" && currLvl >= data[k].reqLvl && data[k].assignValues()); + initAttackData(currLvl); - if (data.frostNova.have) { - if (me.mp > data.frostNova.mana) { + if (AttackData.FrostNova.have) { + if (me.mp > AttackData.FrostNova.mana) { frostNovaCheck() && Skill.cast(sdk.skills.FrostNova, sdk.skills.hand.Right); let ticktwo = getTickCount(); // if the nova cause the death of any monsters around us, its worth it @@ -231,12 +282,12 @@ includeIfNotIncluded("core/Attacks/Sorceress.js"); } } - if (data.glacialSpike.have) { - if (me.mp > data.glacialSpike.mana * 2) { + if (AttackData.GlacialSpike.have) { + if (me.mp > AttackData.GlacialSpike.mana * 2) { let shouldSpike = unit && unit.distance < 10 && - getUnits(sdk.unittype.Monster).filter(function (el) { - return getDistance(el, unit) < 4 && slowable(el, true); - }).length > 1; + getUnits(sdk.unittype.Monster).filter(function (el) { + return getDistance(el, unit) < 4 && slowable(el, true); + }).length > 1; if (shouldSpike && !Coords_1.isBlockedBetween(me, unit)) { Developer.debugging.skills && console.log("SPIKE"); Skill.cast(sdk.skills.GlacialSpike, sdk.skills.hand.Right, unit); @@ -249,87 +300,79 @@ includeIfNotIncluded("core/Attacks/Sorceress.js"); // Set damage values // redo gamedata to be more efficent - Object.keys(data).forEach(k => typeof data[k] === "object" && data[k].have && data[k].calcDmg(unit)); + setDamageValues(unit); // log damage values - if (Developer.debugging.skills) { - Object.keys(data).forEach(k => typeof data[k] === "object" && console.log(getSkillById(data[k].skill) + " : " + data[k].dmg)); - } + // if (Developer.debugging.skills) { + // Object.keys(data).forEach(k => typeof data[k] === "object" && console.log(getSkillById(data[k].skill) + " : " + data[k].dmg)); + // } + + let rebuild = false; // If we have enough mana for Static and it will do more damage than our other skills then duh use it // should this return afterwards since the calulations will now be different? - if (data.static.have && (data.static.mana * 3) < me.mp) { + if (AttackData.StaticField.have && (AttackData.StaticField.mana * 3) < me.mp) { let closeMobCheck = getUnits(sdk.unittype.Monster) - .filter(unit => !!unit && unit.attackable && unit.distance < data.static.range) + .filter(unit => !!unit && unit.attackable && unit.distance < AttackData.StaticField.range) .find(unit => Attack.checkResist(unit, "lightning") && unit.hpPercent > Config.CastStatic); - if (!!closeMobCheck && data.static.dmg > Math.max(data.mainTimed.dmg, data.mainUntimed.dmg, data.secondaryTimed.dmg, data.secondaryUntimed.dmg) && !Coords_1.isBlockedBetween(me, closeMobCheck)) { + if (!!closeMobCheck && isHighestDmg(AttackData.StaticField) && !Coords_1.isBlockedBetween(me, closeMobCheck)) { Developer.debugging.skills && console.log("STATIC"); // check if we should use battle cry from cta if we have it battleCryCheck(closeMobCheck); Skill.cast(sdk.skills.StaticField, sdk.skills.hand.Right, closeMobCheck) && Skill.cast(sdk.skills.StaticField, sdk.skills.hand.Right, closeMobCheck); + rebuild = true; } } // We lost track of the mob or killed it (recheck after using static) - if (unit === undefined || !unit || !unit.attackable) return true; + if (unit === undefined || !unit || !unit.attackable) return Attack.Result.SUCCESS; + + rebuild && setDamageValues(unit); /** * @todo static field is a good skill but if we are currently out of range, check how dangerous it is to tele to spot before choosing that as our skill */ - let sortedList = Object.keys(data) - .filter(k => typeof data[k] === "object" && data[k].have && me.mp > data[k].mana - && (!data[k].timed || !me.skillDelay) && (data[k].skill !== sdk.skills.StaticField || !recheckSkill)) - .sort((a, b) => data[b].dmg - data[a].dmg); + let sortedList = AttackDataKeys + .filter(k => AttackData[k].have && me.mp > AttackData[k].mana + && (!AttackData[k].timed || !me.skillDelay) && (AttackData[k].skill !== sdk.skills.StaticField || !recheckSkill)) + .sort((a, b) => AttackData[b].dmg - AttackData[a].dmg); if (!sortedList.length) return Attack.Result.FAILED; - let skillCheck = data[sortedList[0]].skill === sdk.skills.StaticField && unit.distance > data.static.range && me.inDanger(unit, 15) + + // A bit ugly but handle static and charged bolt here + let skillCheck = ( + (AttackData[sortedList[0]].skill === sdk.skills.StaticField && unit.distance > AttackData.StaticField.range && me.inDanger(unit, 15)) + || (AttackData[sortedList[0]].skill === sdk.skills.ChargedBolt && recheckSkill) + ) ? sortedList.at(1) : sortedList.at(0); - let timedSkill = typeof data[skillCheck] === "object" ? data[skillCheck] : buildDataObj(-1); - - // throw in another attack when using charged bolt as sometimes it misses - const lowManaData = {}; - lowManaData.fBolt = buildDataObj(sdk.skills.FireBolt); - lowManaData.cBolt = buildDataObj(sdk.skills.ChargedBolt); - lowManaData.iBolt = buildDataObj(sdk.skills.IceBolt); - lowManaData.iBlast = buildDataObj(sdk.skills.IceBlast); - lowManaData.tk = buildDataObj(sdk.skills.Telekinesis, 6, 20); - lowManaData.attack = buildDataObj(sdk.skills.Attack, 1, 4); - if (timedSkill.skill === sdk.skills.ChargedBolt && recheckSkill) { - let temp = timedSkill; - Object.keys(data).forEach(k => { - if (typeof data[k] === "object" && data[k].have && compareDamage(temp, data[k])) { - temp = data[k]; - } - }); - if (temp.skill !== timedSkill.skill) { - timedSkill = temp; + + /** @type {ClassData} */ + let selectedSkill = typeof AttackData[skillCheck] === "object" ? AttackData[skillCheck] : DummyData; + + switch (selectedSkill.skill) { + case sdk.skills.ChargedBolt: + if (selectedSkill.skill === sdk.skills.ChargedBolt && AttackData.IceBolt.have && slowable(unit)) { + selectedSkill = AttackData.IceBolt; } - } - if (!timedSkill.have || timedSkill.mana > me.mp) { - Developer.debugging.skills && console.log("Choosing lower mana skill, Was I not able to use one of my better skills? (" + (!timedSkill.have) + "). Did I not have enough mana? " + (timedSkill.mana > me.mp)); - Object.keys(lowManaData).forEach(k => typeof lowManaData[k] === "object" && currLvl >= lowManaData[k].reqLvl && lowManaData[k].assignValues() && lowManaData[k].calcDmg(unit)); - const timedSkillCheck = Object.keys(lowManaData) - .filter(k => typeof lowManaData[k] === "object" && lowManaData[k].have && me.mp > lowManaData[k].mana) - .sort((a, b) => lowManaData[b].dmg - lowManaData[a].dmg).first(); - console.debug(timedSkillCheck); - timedSkill = (() => { - switch (true) { - case !!timedSkillCheck && [lowManaData.tk.skill, lowManaData.attack.skill].indexOf(lowManaData[timedSkillCheck].skill) === -1: - return lowManaData[timedSkillCheck]; - case !!timedSkillCheck && lowManaData[timedSkillCheck].skill === lowManaData.tk.skill && me.normal: - return lowManaData.tk; - default: - if (me.charlvl < 5) return lowManaData.attack; - return (me.normal && me.checkForMobs({range: 10, coll: (sdk.collision.BlockWall | sdk.collision.Objects | sdk.collision.ClosedDoor)}) ? lowManaData.attack : buildDataObj(-1)); - } - })(); - Object.assign(data, lowManaData); - } - if (timedSkill.skill === sdk.skills.ChargedBolt && data.secondaryUntimed.skill === sdk.skills.IceBolt && data.secondaryUntimed.have && slowable(unit)) { - timedSkill = data.secondaryUntimed; + break; + case sdk.skills.Telekinesis: + // maybe check if we are able to telestomp? + if (!me.normal) { + selectedSkill = DummyData; + } + + break; + case sdk.skills.Attack: + if (!me.normal || (me.charlvl > 6 && !me.checkForMobs({ range: 10, coll: (sdk.collision.BlockWall | sdk.collision.ClosedDoor) }))) { + selectedSkill = DummyData; + } } + /** + * @param {Monster} unit + * @returns {AttackResult} + */ const switchBowAttack = (unit) => { if (Attack.getIntoPosition(unit, 20, sdk.collision.Ranged)) { try { @@ -360,18 +403,22 @@ includeIfNotIncluded("core/Attacks/Sorceress.js"); if (CharData.skillData.bowData.bowOnSwitch && (index !== 1 || !unit.name.includes(getLocaleString(sdk.locale.text.Ghostly))) - && ([-1, sdk.skills.Attack].includes(timedSkill.skill) - || timedSkill.mana > me.mp - || (timedSkill.mana * 3 > me.mp && [sdk.skills.FireBolt, sdk.skills.ChargedBolt].includes(timedSkill.skill)))) { + && ([-1, sdk.skills.Attack].includes(selectedSkill.skill) + || selectedSkill.mana > me.mp + || (selectedSkill.mana * 3 > me.mp && [sdk.skills.FireBolt, sdk.skills.ChargedBolt].includes(selectedSkill.skill)))) { if (switchBowAttack(unit) === Attack.Result.SUCCESS) return Attack.Result.SUCCESS; } - switch (ClassAttack.doCast(unit, timedSkill, data)) { + let result = ClassAttack.doCast(unit, selectedSkill); + + switch (result) { case Attack.Result.FAILED: Developer.debugging.skills && console.log(sdk.colors.Red + "Fail Test End----Time elasped[" + ((getTickCount() - tick) / 1000) + " seconds]----------------------//"); - break; + + return Attack.Result.FAILED; case Attack.Result.SUCCESS: Developer.debugging.skills && console.log(sdk.colors.Red + "Sucess Test End----Time elasped[" + ((getTickCount() - tick) / 1000) + " seconds]----------------------//"); + return Attack.Result.SUCCESS; case Attack.Result.CANTATTACK: // Try to telestomp if (Pather.canTeleport() && Attack.checkResist(unit, "physical") && !!me.getMerc() @@ -390,7 +437,7 @@ includeIfNotIncluded("core/Attacks/Sorceress.js"); if (!unit) return Attack.Result.SUCCESS; - if (Town.needMerc()) { + if (me.needMerc()) { if (Config.MercWatch && mercRevive < 3) { Town.visitTown() && (mercRevive++); } else { @@ -407,24 +454,30 @@ includeIfNotIncluded("core/Attacks/Sorceress.js"); !!spot && Pather.walkTo(spot.x, spot.y); } - if (Attack.checkResist(unit, "lightning") && data.static.have && unit.hpPercent > Config.CastStatic) { + if (Attack.checkResist(unit, "lightning") && AttackData.StaticField.have && unit.hpPercent > Config.CastStatic) { Skill.cast(sdk.skills.StaticField, sdk.skills.hand.Right); } - let closeMob = Attack.getNearestMonster({skipGid: gid}); - !!closeMob ? this.doCast(closeMob, timedSkill, data) : haveTK && Skill.cast(sdk.skills.Telekinesis, sdk.skills.hand.Right, unit); + let closeMob = Attack.getNearestMonster({ skipGid: gid }); + !!closeMob ? this.doCast(closeMob, selectedSkill) : haveTK && Skill.cast(sdk.skills.Telekinesis, sdk.skills.hand.Right, unit); } return Attack.Result.SUCCESS; } - break; + return Attack.Result.CANTATTACK; + default: + return result; } - - return Attack.Result.FAILED; }; - ClassAttack.doCast = function (unit, choosenSkill, data) { + /** + * @override + * @param {Monster} unit + * @param {ClassData} choosenSkill + * @returns {AttackResult} + */ + ClassAttack.doCast = function (unit, choosenSkill) { let noMana = false; let { skill, range, mana, timed } = choosenSkill; // unit became invalidated @@ -503,6 +556,7 @@ includeIfNotIncluded("core/Attacks/Sorceress.js"); if ([sdk.skills.FireBolt, sdk.skills.IceBolt].includes(skill)) { range = 12; } + if (unit.distance > range || Coords_1.isBlockedBetween(me, unit)) { // Allow short-distance walking for melee skills let walk = (range < 4 || (skill === sdk.skills.ChargedBolt && range === 7)) && unit.distance < 10 && !checkCollision(me, unit, Coords_1.BlockBits.BlockWall); @@ -545,7 +599,7 @@ includeIfNotIncluded("core/Attacks/Sorceress.js"); !unit.dead && Skill.cast(skill, Skill.getHand(skill), unit); } - if (data.frostNova.have && me.mp > data.frostNova.mana) { + if (AttackData.FrostNova.have && me.mp > AttackData.FrostNova.mana) { frostNovaCheck() && Skill.cast(sdk.skills.FrostNova, sdk.skills.hand.Right); } From 32ef6ea356940b7fa816018dda8c634e379125e7 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Mon, 20 Feb 2023 14:00:23 -0500 Subject: [PATCH 044/263] Minor cleanup - Add path callback to hellforge to stop once we locate heph - remove debug statements from anya - Add quit hotkey to toolsthread, press delete to quickly exit the game --- libs/SoloPlay/Scripts/anya.js | 4 +--- libs/SoloPlay/Scripts/hellforge.js | 5 ++++- libs/SoloPlay/Threads/ToolsThread.js | 20 +++++++++----------- 3 files changed, 14 insertions(+), 15 deletions(-) diff --git a/libs/SoloPlay/Scripts/anya.js b/libs/SoloPlay/Scripts/anya.js index 1c79b9b7..d998018b 100644 --- a/libs/SoloPlay/Scripts/anya.js +++ b/libs/SoloPlay/Scripts/anya.js @@ -1,6 +1,6 @@ /** * @filename anya.js -* @author isid0re, theBGuy +* @author theBGuy * @desc Anya rescue from frozen river * */ @@ -25,8 +25,6 @@ function anya () { let presetLoc = Game.getPresetObject(me.area, sdk.objects.FrozenAnyasPlatform).realCoords(); let fStein = Game.getMonster(getLocaleString(sdk.locale.monsters.Frozenstein)); - console.debug("dist anya-stein :: " + getDistance(presetLoc, fStein)); - console.debug(presetLoc, fStein); if (presetLoc && fStein && getDistance(presetLoc, fStein) < 15) { // need to write a clearWhile function diff --git a/libs/SoloPlay/Scripts/hellforge.js b/libs/SoloPlay/Scripts/hellforge.js index c481a5dd..c7493bbd 100644 --- a/libs/SoloPlay/Scripts/hellforge.js +++ b/libs/SoloPlay/Scripts/hellforge.js @@ -23,7 +23,10 @@ function hellforge () { * - Generate path and use callback to stop after we detect heph in range instead of moving all the way to the forge */ - if (!Pather.moveToPreset(me.area, sdk.unittype.Object, sdk.quest.chest.HellForge)) { + if (!Pather.moveToPresetObject(me.area, sdk.quest.chest.HellForge, { callback: () => { + let heph = Game.getMonster(getLocaleString(sdk.locale.monsters.HephastoTheArmorer)); + return (heph && heph.distance < 30); + }})) { console.warn("ÿc8Kolbot-SoloPlayÿc0: Failed to move to Hephasto"); } diff --git a/libs/SoloPlay/Threads/ToolsThread.js b/libs/SoloPlay/Threads/ToolsThread.js index 67d09db9..512e8950 100644 --- a/libs/SoloPlay/Threads/ToolsThread.js +++ b/libs/SoloPlay/Threads/ToolsThread.js @@ -291,6 +291,10 @@ function main () { D2Bot.printToConsole(me.profile + " - end run " + me.gamename); D2Bot.stop(me.profile, true); + break; + case sdk.keys.Delete: // quit current game + this.exit(); + break; case sdk.keys.Insert: // reveal level me.overhead("Revealing " + getAreaName(me.area)); @@ -561,13 +565,7 @@ function main () { addEventListener("scriptmsg", this.scriptEvent); addEventListener("scriptmsg", Tracker.logLeveling); - // Load Fastmod - patched - // Packet.changeStat(105, Config.FCR); - // Packet.changeStat(99, Config.FHR); - // Packet.changeStat(102, Config.FBR); - // Packet.changeStat(93, Config.IAS); - - Config.QuitListMode > 0 && this.initQuitList(); + Config.QuitListMode > 0 && Common.Toolsthread.initQuitList(); let myAct = me.act; @@ -590,7 +588,7 @@ function main () { !Developer.hideChickens && D2Bot.printToConsole("Life Chicken (" + me.hp + "/" + me.hpmax + ")" + Attack.getNearestMonster() + " in " + getAreaName(me.area) + ". Ping: " + me.ping, sdk.colors.D2Bot.Red); this.exit(true); - break; + return true; } Config.UseMP > 0 && me.mpPercent < Config.UseMP && this.drinkPotion(Common.Toolsthread.pots.Mana); @@ -604,7 +602,7 @@ function main () { !Developer.hideChickens && D2Bot.printToConsole("Mana Chicken: (" + me.mp + "/" + me.mpmax + ") in " + getAreaName(me.area), sdk.colors.D2Bot.Red); this.exit(true); - break; + return true; } if (Config.IronGolemChicken > 0 && me.necromancer) { @@ -618,7 +616,7 @@ function main () { !Developer.hideChickens && D2Bot.printToConsole("Irom Golem Chicken in " + getAreaName(me.area), sdk.colors.D2Bot.Red); this.exit(true); - break; + return true; } } } @@ -633,7 +631,7 @@ function main () { !Developer.hideChickens && D2Bot.printToConsole("Merc Chicken in " + getAreaName(me.area), sdk.colors.D2Bot.Red); this.exit(true); - break; + return true; } mercHP < Config.UseMercHP && this.drinkPotion(Common.Toolsthread.pots.MercHealth); From d4fa02485beb525d339fea2bb9d6f16ce89d10b7 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Fri, 24 Feb 2023 00:20:04 -0500 Subject: [PATCH 045/263] Update Me.js - Move items to our inventory if we have room when clearingBelt slots --- libs/SoloPlay/Functions/Me.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/libs/SoloPlay/Functions/Me.js b/libs/SoloPlay/Functions/Me.js index 47378bf9..907cbbcf 100644 --- a/libs/SoloPlay/Functions/Me.js +++ b/libs/SoloPlay/Functions/Me.js @@ -329,7 +329,8 @@ me.clearBelt = function () { } while (item.getNext()); while (clearList.length > 0) { - clearList.shift().interact(); + let pot = clearList.shift(); + (Storage.Inventory.CanFit(pot) && Storage.Inventory.MoveTo(pot)) || pot.interact(); delay(200); } } From b9ad5dd8fbb93f7a9d56300dbdd96ed2e91009d9 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Mon, 27 Feb 2023 00:46:42 -0500 Subject: [PATCH 046/263] Update countess.js - If doing countess for quest, wait a bit for the chest to stop dropping items --- libs/SoloPlay/Scripts/countess.js | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/libs/SoloPlay/Scripts/countess.js b/libs/SoloPlay/Scripts/countess.js index f2b94fc8..789cc029 100644 --- a/libs/SoloPlay/Scripts/countess.js +++ b/libs/SoloPlay/Scripts/countess.js @@ -16,6 +16,7 @@ function countess () { Pather.checkWP(sdk.areas.BlackMarsh, true) ? Pather.useWaypoint(sdk.areas.BlackMarsh) : Pather.getWP(sdk.areas.BlackMarsh); Precast.doPrecast(true); + let forQuest = !!Misc.checkQuest(sdk.quest.id.ForgottenTower, sdk.quest.states.Completed); if (me.charlvl < 12) { // @todo - low level, lets take a scenic route and kill those hawk nests @@ -33,6 +34,11 @@ function countess () { Pather.moveToPreset(me.area, sdk.unittype.Object, sdk.objects.SuperChest); Attack.killTarget(getLocaleString(sdk.locale.monsters.TheCountess)); + + if (forQuest) { + Pather.moveToPreset(me.area, sdk.unittype.Object, sdk.objects.SuperChest); + delay(3000); + } } catch (err) { console.log("ÿc8Kolbot-SoloPlayÿc0: Failed to kill Countess: " + err); } From 498fe282439e04541e08a4d39b1c968134d383ab Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Mon, 27 Feb 2023 11:47:16 -0500 Subject: [PATCH 047/263] Update CubingOverrides.js - Fix hoarding classitems for cubing --- libs/SoloPlay/Functions/CubingOverrides.js | 202 +++++++++++---------- 1 file changed, 103 insertions(+), 99 deletions(-) diff --git a/libs/SoloPlay/Functions/CubingOverrides.js b/libs/SoloPlay/Functions/CubingOverrides.js index 617cff0a..24cc6446 100644 --- a/libs/SoloPlay/Functions/CubingOverrides.js +++ b/libs/SoloPlay/Functions/CubingOverrides.js @@ -25,243 +25,243 @@ Cubing.buildRecipes = function () { switch (Config.Recipes[i][0]) { case Recipe.Gem: - this.recipes.push({Ingredients: [Config.Recipes[i][1], Config.Recipes[i][1], Config.Recipes[i][1]], Index: Recipe.Gem, AlwaysEnabled: true}); + this.recipes.push({ Ingredients: [Config.Recipes[i][1], Config.Recipes[i][1], Config.Recipes[i][1]], Index: Recipe.Gem, AlwaysEnabled: true }); break; // Crafting Recipes----------------------------------------------------------------------------------------------------------------------------------// case Recipe.HitPower.Helm: - this.recipes.push({Ingredients: [Config.Recipes[i][1], sdk.items.runes.Ith, sdk.items.Jewel, sdk.items.gems.Perfect.Sapphire], Level: 84, Index: Recipe.HitPower.Helm}); + this.recipes.push({ Ingredients: [Config.Recipes[i][1], sdk.items.runes.Ith, sdk.items.Jewel, sdk.items.gems.Perfect.Sapphire], Level: 84, Index: Recipe.HitPower.Helm }); break; case Recipe.HitPower.Boots: - this.recipes.push({Ingredients: [Config.Recipes[i][1], sdk.items.runes.Ral, sdk.items.Jewel, sdk.items.gems.Perfect.Sapphire], Level: 71, Index: Recipe.HitPower.Boots}); + this.recipes.push({ Ingredients: [Config.Recipes[i][1], sdk.items.runes.Ral, sdk.items.Jewel, sdk.items.gems.Perfect.Sapphire], Level: 71, Index: Recipe.HitPower.Boots }); break; case Recipe.HitPower.Gloves: - this.recipes.push({Ingredients: [Config.Recipes[i][1], sdk.items.runes.Ort, sdk.items.Jewel, sdk.items.gems.Perfect.Sapphire], Level: 79, Index: Recipe.HitPower.Gloves}); + this.recipes.push({ Ingredients: [Config.Recipes[i][1], sdk.items.runes.Ort, sdk.items.Jewel, sdk.items.gems.Perfect.Sapphire], Level: 79, Index: Recipe.HitPower.Gloves }); break; case Recipe.HitPower.Belt: - this.recipes.push({Ingredients: [Config.Recipes[i][1], sdk.items.runes.Tal, sdk.items.Jewel, sdk.items.gems.Perfect.Sapphire], Level: 71, Index: Recipe.HitPower.Belt}); + this.recipes.push({ Ingredients: [Config.Recipes[i][1], sdk.items.runes.Tal, sdk.items.Jewel, sdk.items.gems.Perfect.Sapphire], Level: 71, Index: Recipe.HitPower.Belt }); break; case Recipe.HitPower.Shield: - this.recipes.push({Ingredients: [Config.Recipes[i][1], sdk.items.runes.Eth, sdk.items.Jewel, sdk.items.gems.Perfect.Sapphire], Level: 82, Index: Recipe.HitPower.Shield}); + this.recipes.push({ Ingredients: [Config.Recipes[i][1], sdk.items.runes.Eth, sdk.items.Jewel, sdk.items.gems.Perfect.Sapphire], Level: 82, Index: Recipe.HitPower.Shield }); break; case Recipe.HitPower.Body: - this.recipes.push({Ingredients: [Config.Recipes[i][1], sdk.items.runes.Nef, sdk.items.Jewel, sdk.items.gems.Perfect.Sapphire], Level: 85, Index: Recipe.HitPower.Body}); + this.recipes.push({ Ingredients: [Config.Recipes[i][1], sdk.items.runes.Nef, sdk.items.Jewel, sdk.items.gems.Perfect.Sapphire], Level: 85, Index: Recipe.HitPower.Body }); break; case Recipe.HitPower.Amulet: - this.recipes.push({Ingredients: [sdk.items.Amulet, sdk.items.runes.Thul, sdk.items.Jewel, sdk.items.gems.Perfect.Sapphire], Level: 90, Index: Recipe.HitPower.Amulet}); + this.recipes.push({ Ingredients: [sdk.items.Amulet, sdk.items.runes.Thul, sdk.items.Jewel, sdk.items.gems.Perfect.Sapphire], Level: 90, Index: Recipe.HitPower.Amulet }); break; case Recipe.HitPower.Ring: - this.recipes.push({Ingredients: [sdk.items.Ring, sdk.items.runes.Amn, sdk.items.Jewel, sdk.items.gems.Perfect.Sapphire], Level: 77, Index: Recipe.HitPower.Ring}); + this.recipes.push({ Ingredients: [sdk.items.Ring, sdk.items.runes.Amn, sdk.items.Jewel, sdk.items.gems.Perfect.Sapphire], Level: 77, Index: Recipe.HitPower.Ring }); break; case Recipe.HitPower.Weapon: - this.recipes.push({Ingredients: [Config.Recipes[i][1], sdk.items.runes.Tir, sdk.items.Jewel, sdk.items.gems.Perfect.Sapphire], Level: 85, Index: Recipe.HitPower.Weapon}); + this.recipes.push({ Ingredients: [Config.Recipes[i][1], sdk.items.runes.Tir, sdk.items.Jewel, sdk.items.gems.Perfect.Sapphire], Level: 85, Index: Recipe.HitPower.Weapon }); break; case Recipe.Blood.Helm: - this.recipes.push({Ingredients: [Config.Recipes[i][1], sdk.items.runes.Ral, sdk.items.Jewel, sdk.items.gems.Perfect.Ruby], Level: 84, Index: Recipe.Blood.Helm}); + this.recipes.push({ Ingredients: [Config.Recipes[i][1], sdk.items.runes.Ral, sdk.items.Jewel, sdk.items.gems.Perfect.Ruby], Level: 84, Index: Recipe.Blood.Helm }); break; case Recipe.Blood.Boots: - this.recipes.push({Ingredients: [Config.Recipes[i][1], sdk.items.runes.Eth, sdk.items.Jewel, sdk.items.gems.Perfect.Ruby], Level: 71, Index: Recipe.Blood.Boots}); + this.recipes.push({ Ingredients: [Config.Recipes[i][1], sdk.items.runes.Eth, sdk.items.Jewel, sdk.items.gems.Perfect.Ruby], Level: 71, Index: Recipe.Blood.Boots }); break; case Recipe.Blood.Gloves: - this.recipes.push({Ingredients: [Config.Recipes[i][1], sdk.items.runes.Nef, sdk.items.Jewel, sdk.items.gems.Perfect.Ruby], Level: 79, Index: Recipe.Blood.Gloves}); + this.recipes.push({ Ingredients: [Config.Recipes[i][1], sdk.items.runes.Nef, sdk.items.Jewel, sdk.items.gems.Perfect.Ruby], Level: 79, Index: Recipe.Blood.Gloves }); break; case Recipe.Blood.Belt: - this.recipes.push({Ingredients: [Config.Recipes[i][1], sdk.items.runes.Tal, sdk.items.Jewel, sdk.items.gems.Perfect.Ruby], Level: 71, Index: Recipe.Blood.Belt}); + this.recipes.push({ Ingredients: [Config.Recipes[i][1], sdk.items.runes.Tal, sdk.items.Jewel, sdk.items.gems.Perfect.Ruby], Level: 71, Index: Recipe.Blood.Belt }); break; case Recipe.Blood.Shield: - this.recipes.push({Ingredients: [Config.Recipes[i][1], sdk.items.runes.Ith, sdk.items.Jewel, sdk.items.gems.Perfect.Ruby], Level: 82, Index: Recipe.Blood.Shield}); + this.recipes.push({ Ingredients: [Config.Recipes[i][1], sdk.items.runes.Ith, sdk.items.Jewel, sdk.items.gems.Perfect.Ruby], Level: 82, Index: Recipe.Blood.Shield }); break; case Recipe.Blood.Body: - this.recipes.push({Ingredients: [Config.Recipes[i][1], sdk.items.runes.Thul, sdk.items.Jewel, sdk.items.gems.Perfect.Ruby], Level: 85, Index: Recipe.Blood.Body}); + this.recipes.push({ Ingredients: [Config.Recipes[i][1], sdk.items.runes.Thul, sdk.items.Jewel, sdk.items.gems.Perfect.Ruby], Level: 85, Index: Recipe.Blood.Body }); break; case Recipe.Blood.Amulet: - this.recipes.push({Ingredients: [sdk.items.Amulet, sdk.items.runes.Amn, sdk.items.Jewel, sdk.items.gems.Perfect.Ruby], Level: 90, Index: Recipe.Blood.Amulet}); + this.recipes.push({ Ingredients: [sdk.items.Amulet, sdk.items.runes.Amn, sdk.items.Jewel, sdk.items.gems.Perfect.Ruby], Level: 90, Index: Recipe.Blood.Amulet }); break; case Recipe.Blood.Ring: - this.recipes.push({Ingredients: [sdk.items.Ring, sdk.items.runes.Sol, sdk.items.Jewel, sdk.items.gems.Perfect.Ruby], Level: 77, Index: Recipe.Blood.Ring}); + this.recipes.push({ Ingredients: [sdk.items.Ring, sdk.items.runes.Sol, sdk.items.Jewel, sdk.items.gems.Perfect.Ruby], Level: 77, Index: Recipe.Blood.Ring }); break; case Recipe.Blood.Weapon: - this.recipes.push({Ingredients: [Config.Recipes[i][1], sdk.items.runes.Ort, sdk.items.Jewel, sdk.items.gems.Perfect.Ruby], Level: 85, Index: Recipe.Blood.Weapon}); + this.recipes.push({ Ingredients: [Config.Recipes[i][1], sdk.items.runes.Ort, sdk.items.Jewel, sdk.items.gems.Perfect.Ruby], Level: 85, Index: Recipe.Blood.Weapon }); break; case Recipe.Caster.Helm: - this.recipes.push({Ingredients: [Config.Recipes[i][1], sdk.items.runes.Nef, sdk.items.Jewel, sdk.items.gems.Perfect.Amethyst], Level: 84, Index: Recipe.Caster.Helm}); + this.recipes.push({ Ingredients: [Config.Recipes[i][1], sdk.items.runes.Nef, sdk.items.Jewel, sdk.items.gems.Perfect.Amethyst], Level: 84, Index: Recipe.Caster.Helm }); break; case Recipe.Caster.Boots: - this.recipes.push({Ingredients: [Config.Recipes[i][1], sdk.items.runes.Thul, sdk.items.Jewel, sdk.items.gems.Perfect.Amethyst], Level: 71, Index: Recipe.Caster.Boots}); + this.recipes.push({ Ingredients: [Config.Recipes[i][1], sdk.items.runes.Thul, sdk.items.Jewel, sdk.items.gems.Perfect.Amethyst], Level: 71, Index: Recipe.Caster.Boots }); break; case Recipe.Caster.Gloves: - this.recipes.push({Ingredients: [Config.Recipes[i][1], sdk.items.runes.Ort, sdk.items.Jewel, sdk.items.gems.Perfect.Amethyst], Level: 79, Index: Recipe.Caster.Gloves}); + this.recipes.push({ Ingredients: [Config.Recipes[i][1], sdk.items.runes.Ort, sdk.items.Jewel, sdk.items.gems.Perfect.Amethyst], Level: 79, Index: Recipe.Caster.Gloves }); break; case Recipe.Caster.Belt: - this.recipes.push({Ingredients: [Config.Recipes[i][1], sdk.items.runes.Ith, sdk.items.Jewel, sdk.items.gems.Perfect.Amethyst], Level: 71, Index: Recipe.Caster.Belt}); + this.recipes.push({ Ingredients: [Config.Recipes[i][1], sdk.items.runes.Ith, sdk.items.Jewel, sdk.items.gems.Perfect.Amethyst], Level: 71, Index: Recipe.Caster.Belt }); break; case Recipe.Caster.Shield: - this.recipes.push({Ingredients: [Config.Recipes[i][1], sdk.items.runes.Eth, sdk.items.Jewel, sdk.items.gems.Perfect.Amethyst], Level: 82, Index: Recipe.Caster.Shield}); + this.recipes.push({ Ingredients: [Config.Recipes[i][1], sdk.items.runes.Eth, sdk.items.Jewel, sdk.items.gems.Perfect.Amethyst], Level: 82, Index: Recipe.Caster.Shield }); break; case Recipe.Caster.Body: - this.recipes.push({Ingredients: [Config.Recipes[i][1], sdk.items.runes.Tal, sdk.items.Jewel, sdk.items.gems.Perfect.Amethyst], Level: 85, Index: Recipe.Caster.Body}); + this.recipes.push({ Ingredients: [Config.Recipes[i][1], sdk.items.runes.Tal, sdk.items.Jewel, sdk.items.gems.Perfect.Amethyst], Level: 85, Index: Recipe.Caster.Body }); break; case Recipe.Caster.Amulet: - this.recipes.push({Ingredients: [sdk.items.Amulet, sdk.items.runes.Ral, sdk.items.Jewel, sdk.items.gems.Perfect.Amethyst], Level: 90, Index: Recipe.Caster.Amulet}); + this.recipes.push({ Ingredients: [sdk.items.Amulet, sdk.items.runes.Ral, sdk.items.Jewel, sdk.items.gems.Perfect.Amethyst], Level: 90, Index: Recipe.Caster.Amulet }); break; case Recipe.Caster.Ring: - this.recipes.push({Ingredients: [sdk.items.Ring, sdk.items.runes.Amn, sdk.items.Jewel, sdk.items.gems.Perfect.Amethyst], Level: 77, Index: Recipe.Caster.Ring}); + this.recipes.push({ Ingredients: [sdk.items.Ring, sdk.items.runes.Amn, sdk.items.Jewel, sdk.items.gems.Perfect.Amethyst], Level: 77, Index: Recipe.Caster.Ring }); break; case Recipe.Caster.Weapon: - this.recipes.push({Ingredients: [Config.Recipes[i][1], sdk.items.runes.Tir, sdk.items.Jewel, sdk.items.gems.Perfect.Amethyst], Level: 85, Index: Recipe.Caster.Weapon}); + this.recipes.push({ Ingredients: [Config.Recipes[i][1], sdk.items.runes.Tir, sdk.items.Jewel, sdk.items.gems.Perfect.Amethyst], Level: 85, Index: Recipe.Caster.Weapon }); break; case Recipe.Safety.Helm: - this.recipes.push({Ingredients: [Config.Recipes[i][1], sdk.items.runes.Ith, sdk.items.Jewel, sdk.items.gems.Perfect.Emerald], Level: 84, Index: Recipe.Safety.Helm}); + this.recipes.push({ Ingredients: [Config.Recipes[i][1], sdk.items.runes.Ith, sdk.items.Jewel, sdk.items.gems.Perfect.Emerald], Level: 84, Index: Recipe.Safety.Helm }); break; case Recipe.Safety.Boots: - this.recipes.push({Ingredients: [Config.Recipes[i][1], sdk.items.runes.Ort, sdk.items.Jewel, sdk.items.gems.Perfect.Emerald], Level: 71, Index: Recipe.Safety.Boots}); + this.recipes.push({ Ingredients: [Config.Recipes[i][1], sdk.items.runes.Ort, sdk.items.Jewel, sdk.items.gems.Perfect.Emerald], Level: 71, Index: Recipe.Safety.Boots }); break; case Recipe.Safety.Gloves: - this.recipes.push({Ingredients: [Config.Recipes[i][1], sdk.items.runes.Ral, sdk.items.Jewel, sdk.items.gems.Perfect.Emerald], Level: 79, Index: Recipe.Safety.Gloves}); + this.recipes.push({ Ingredients: [Config.Recipes[i][1], sdk.items.runes.Ral, sdk.items.Jewel, sdk.items.gems.Perfect.Emerald], Level: 79, Index: Recipe.Safety.Gloves }); break; case Recipe.Safety.Belt: - this.recipes.push({Ingredients: [Config.Recipes[i][1], sdk.items.runes.Tal, sdk.items.Jewel, sdk.items.gems.Perfect.Emerald], Level: 71, Index: Recipe.Safety.Belt}); + this.recipes.push({ Ingredients: [Config.Recipes[i][1], sdk.items.runes.Tal, sdk.items.Jewel, sdk.items.gems.Perfect.Emerald], Level: 71, Index: Recipe.Safety.Belt }); break; case Recipe.Safety.Shield: - this.recipes.push({Ingredients: [Config.Recipes[i][1], sdk.items.runes.Nef, sdk.items.Jewel, sdk.items.gems.Perfect.Emerald], Level: 82, Index: Recipe.Safety.Shield}); + this.recipes.push({ Ingredients: [Config.Recipes[i][1], sdk.items.runes.Nef, sdk.items.Jewel, sdk.items.gems.Perfect.Emerald], Level: 82, Index: Recipe.Safety.Shield }); break; case Recipe.Safety.Body: - this.recipes.push({Ingredients: [Config.Recipes[i][1], sdk.items.runes.Eth, sdk.items.Jewel, sdk.items.gems.Perfect.Emerald], Level: 85, Index: Recipe.Safety.Body}); + this.recipes.push({ Ingredients: [Config.Recipes[i][1], sdk.items.runes.Eth, sdk.items.Jewel, sdk.items.gems.Perfect.Emerald], Level: 85, Index: Recipe.Safety.Body }); break; case Recipe.Safety.Amulet: - this.recipes.push({Ingredients: [sdk.items.Amulet, sdk.items.runes.Thul, sdk.items.Jewel, sdk.items.gems.Perfect.Emerald], Level: 90, Index: Recipe.Safety.Amulet}); + this.recipes.push({ Ingredients: [sdk.items.Amulet, sdk.items.runes.Thul, sdk.items.Jewel, sdk.items.gems.Perfect.Emerald], Level: 90, Index: Recipe.Safety.Amulet }); break; case Recipe.Safety.Ring: - this.recipes.push({Ingredients: [sdk.items.Ring, sdk.items.runes.Amn, sdk.items.Jewel, sdk.items.gems.Perfect.Emerald], Level: 77, Index: Recipe.Safety.Ring}); + this.recipes.push({ Ingredients: [sdk.items.Ring, sdk.items.runes.Amn, sdk.items.Jewel, sdk.items.gems.Perfect.Emerald], Level: 77, Index: Recipe.Safety.Ring }); break; case Recipe.Safety.Weapon: - this.recipes.push({Ingredients: [Config.Recipes[i][1], sdk.items.runes.Sol, sdk.items.Jewel, sdk.items.gems.Perfect.Emerald], Level: 85, Index: Recipe.Safety.Weapon}); + this.recipes.push({ Ingredients: [Config.Recipes[i][1], sdk.items.runes.Sol, sdk.items.Jewel, sdk.items.gems.Perfect.Emerald], Level: 85, Index: Recipe.Safety.Weapon }); break; // Upgrading Recipes----------------------------------------------------------------------------------------------------------------------------------// case Recipe.Unique.Weapon.ToExceptional: - this.recipes.push({Ingredients: [Config.Recipes[i][1], sdk.items.runes.Ral, sdk.items.runes.Sol, sdk.items.gems.Perfect.Emerald], Index: Recipe.Unique.Weapon.ToExceptional, Ethereal: Config.Recipes[i][2]}); + this.recipes.push({ Ingredients: [Config.Recipes[i][1], sdk.items.runes.Ral, sdk.items.runes.Sol, sdk.items.gems.Perfect.Emerald], Index: Recipe.Unique.Weapon.ToExceptional, Ethereal: Config.Recipes[i][2] }); break; case Recipe.Unique.Weapon.ToElite: // Ladder only if (me.ladder || Developer.addLadderRW) { - this.recipes.push({Ingredients: [Config.Recipes[i][1], sdk.items.runes.Lum, sdk.items.runes.Pul, sdk.items.gems.Perfect.Emerald], Index: Recipe.Unique.Weapon.ToElite, Ethereal: Config.Recipes[i][2]}); + this.recipes.push({ Ingredients: [Config.Recipes[i][1], sdk.items.runes.Lum, sdk.items.runes.Pul, sdk.items.gems.Perfect.Emerald], Index: Recipe.Unique.Weapon.ToElite, Ethereal: Config.Recipes[i][2] }); } break; case Recipe.Unique.Armor.ToExceptional: - this.recipes.push({Ingredients: [Config.Recipes[i][1], sdk.items.runes.Tal, sdk.items.runes.Shael, sdk.items.gems.Perfect.Diamond], Index: Recipe.Unique.Armor.ToExceptional, Ethereal: Config.Recipes[i][2]}); + this.recipes.push({ Ingredients: [Config.Recipes[i][1], sdk.items.runes.Tal, sdk.items.runes.Shael, sdk.items.gems.Perfect.Diamond], Index: Recipe.Unique.Armor.ToExceptional, Ethereal: Config.Recipes[i][2] }); break; case Recipe.Unique.Armor.ToElite: // Ladder only if (me.ladder || Developer.addLadderRW) { - this.recipes.push({Ingredients: [Config.Recipes[i][1], sdk.items.runes.Lem, sdk.items.runes.Ko, sdk.items.gems.Perfect.Diamond], Index: Recipe.Unique.Armor.ToElite, Ethereal: Config.Recipes[i][2]}); + this.recipes.push({ Ingredients: [Config.Recipes[i][1], sdk.items.runes.Lem, sdk.items.runes.Ko, sdk.items.gems.Perfect.Diamond], Index: Recipe.Unique.Armor.ToElite, Ethereal: Config.Recipes[i][2] }); } break; case Recipe.Rare.Weapon.ToExceptional: - this.recipes.push({Ingredients: [Config.Recipes[i][1], sdk.items.runes.Ort, sdk.items.runes.Amn, sdk.items.gems.Perfect.Sapphire], Index: Recipe.Rare.Weapon.ToExceptional, Ethereal: Config.Recipes[i][2]}); + this.recipes.push({ Ingredients: [Config.Recipes[i][1], sdk.items.runes.Ort, sdk.items.runes.Amn, sdk.items.gems.Perfect.Sapphire], Index: Recipe.Rare.Weapon.ToExceptional, Ethereal: Config.Recipes[i][2] }); break; case Recipe.Rare.Weapon.ToElite: - this.recipes.push({Ingredients: [Config.Recipes[i][1], sdk.items.runes.Fal, sdk.items.runes.Um, sdk.items.gems.Perfect.Sapphire], Index: Recipe.Rare.Weapon.ToElite, Ethereal: Config.Recipes[i][2]}); + this.recipes.push({ Ingredients: [Config.Recipes[i][1], sdk.items.runes.Fal, sdk.items.runes.Um, sdk.items.gems.Perfect.Sapphire], Index: Recipe.Rare.Weapon.ToElite, Ethereal: Config.Recipes[i][2] }); break; case Recipe.Rare.Armor.ToExceptional: - this.recipes.push({Ingredients: [Config.Recipes[i][1], sdk.items.runes.Ral, sdk.items.runes.Thul, sdk.items.gems.Perfect.Amethyst], Index: Recipe.Rare.Armor.ToExceptional, Ethereal: Config.Recipes[i][2]}); + this.recipes.push({ Ingredients: [Config.Recipes[i][1], sdk.items.runes.Ral, sdk.items.runes.Thul, sdk.items.gems.Perfect.Amethyst], Index: Recipe.Rare.Armor.ToExceptional, Ethereal: Config.Recipes[i][2] }); break; case Recipe.Rare.Armor.ToElite: - this.recipes.push({Ingredients: [Config.Recipes[i][1], sdk.items.runes.Ko, sdk.items.runes.Pul, sdk.items.gems.Perfect.Amethyst], Index: Recipe.Rare.Armor.ToElite, Ethereal: Config.Recipes[i][2]}); + this.recipes.push({ Ingredients: [Config.Recipes[i][1], sdk.items.runes.Ko, sdk.items.runes.Pul, sdk.items.gems.Perfect.Amethyst], Index: Recipe.Rare.Armor.ToElite, Ethereal: Config.Recipes[i][2] }); break; // Socketing Recipes----------------------------------------------------------------------------------------------------------------------------------// case Recipe.Socket.Shield: - this.recipes.push({Ingredients: [Config.Recipes[i][1], sdk.items.runes.Tal, sdk.items.runes.Amn, sdk.items.gems.Perfect.Ruby], Index: Recipe.Socket.Shield, Ethereal: Config.Recipes[i][2]}); + this.recipes.push({ Ingredients: [Config.Recipes[i][1], sdk.items.runes.Tal, sdk.items.runes.Amn, sdk.items.gems.Perfect.Ruby], Index: Recipe.Socket.Shield, Ethereal: Config.Recipes[i][2] }); break; case Recipe.Socket.Weapon: - this.recipes.push({Ingredients: [Config.Recipes[i][1], sdk.items.runes.Ral, sdk.items.runes.Amn, sdk.items.gems.Perfect.Amethyst], Index: Recipe.Socket.Weapon, Ethereal: Config.Recipes[i][2]}); + this.recipes.push({ Ingredients: [Config.Recipes[i][1], sdk.items.runes.Ral, sdk.items.runes.Amn, sdk.items.gems.Perfect.Amethyst], Index: Recipe.Socket.Weapon, Ethereal: Config.Recipes[i][2] }); break; case Recipe.Socket.Armor: - this.recipes.push({Ingredients: [Config.Recipes[i][1], sdk.items.runes.Tal, sdk.items.runes.Thul, sdk.items.gems.Perfect.Topaz], Index: Recipe.Socket.Armor, Ethereal: Config.Recipes[i][2]}); + this.recipes.push({ Ingredients: [Config.Recipes[i][1], sdk.items.runes.Tal, sdk.items.runes.Thul, sdk.items.gems.Perfect.Topaz], Index: Recipe.Socket.Armor, Ethereal: Config.Recipes[i][2] }); break; case Recipe.Socket.Helm: - this.recipes.push({Ingredients: [Config.Recipes[i][1], sdk.items.runes.Ral, sdk.items.runes.Thul, sdk.items.gems.Perfect.Sapphire], Index: Recipe.Socket.Helm, Ethereal: Config.Recipes[i][2]}); + this.recipes.push({ Ingredients: [Config.Recipes[i][1], sdk.items.runes.Ral, sdk.items.runes.Thul, sdk.items.gems.Perfect.Sapphire], Index: Recipe.Socket.Helm, Ethereal: Config.Recipes[i][2] }); break; case Recipe.Socket.LowMagic: - this.recipes.push({Ingredients: [Config.Recipes[i][1], "cgem", "cgem", "cgem"], Level: 25, Index: Recipe.Socket.LowMagic}); + this.recipes.push({ Ingredients: [Config.Recipes[i][1], "cgem", "cgem", "cgem"], Level: 25, Index: Recipe.Socket.LowMagic }); break; case Recipe.Socket.HighMagic: - this.recipes.push({Ingredients: [Config.Recipes[i][1], "fgem", "fgem", "fgem"], Level: 30, Index: Recipe.Socket.HighMagic}); + this.recipes.push({ Ingredients: [Config.Recipes[i][1], "fgem", "fgem", "fgem"], Level: 30, Index: Recipe.Socket.HighMagic }); break; case Recipe.Socket.Rare: - this.recipes.push({Ingredients: [Config.Recipes[i][1], sdk.items.Ring, sdk.items.gems.Perfect.Skull, sdk.items.gems.Perfect.Skull, sdk.items.gems.Perfect.Skull], Index: Recipe.Socket.Rare}); + this.recipes.push({ Ingredients: [Config.Recipes[i][1], sdk.items.Ring, sdk.items.gems.Perfect.Skull, sdk.items.gems.Perfect.Skull, sdk.items.gems.Perfect.Skull], Index: Recipe.Socket.Rare }); break; // Re-rolling Recipes----------------------------------------------------------------------------------------------------------------------------------// case Recipe.Reroll.Magic: // Hacky solution ftw - this.recipes.push({Ingredients: [Config.Recipes[i][1], "pgem", "pgem", "pgem"], Level: 91, Index: Recipe.Reroll.Magic}); + this.recipes.push({ Ingredients: [Config.Recipes[i][1], "pgem", "pgem", "pgem"], Level: 91, Index: Recipe.Reroll.Magic }); break; case Recipe.Reroll.Charm: - this.recipes.push({Ingredients: [Config.Recipes[i][1], "pgem", "pgem", "pgem"], Level: Object.assign({"cm1": 95, "cm2": 91, "cm3": 91}, Config.Recipes[i][2]), Index: Recipe.Reroll.Charm}); + this.recipes.push({ Ingredients: [Config.Recipes[i][1], "pgem", "pgem", "pgem"], Level: Object.assign({ "cm1": 95, "cm2": 91, "cm3": 91 }, Config.Recipes[i][2]), Index: Recipe.Reroll.Charm }); break; case Recipe.Reroll.Rare: - this.recipes.push({Ingredients: [Config.Recipes[i][1], sdk.items.gems.Perfect.Skull, sdk.items.gems.Perfect.Skull, sdk.items.gems.Perfect.Skull, sdk.items.gems.Perfect.Skull, sdk.items.gems.Perfect.Skull, sdk.items.gems.Perfect.Skull], Index: Recipe.Reroll.Rare}); + this.recipes.push({ Ingredients: [Config.Recipes[i][1], sdk.items.gems.Perfect.Skull, sdk.items.gems.Perfect.Skull, sdk.items.gems.Perfect.Skull, sdk.items.gems.Perfect.Skull, sdk.items.gems.Perfect.Skull, sdk.items.gems.Perfect.Skull], Index: Recipe.Reroll.Rare }); break; case Recipe.Reroll.HighRare: - this.recipes.push({Ingredients: [Config.Recipes[i][1], sdk.items.gems.Perfect.Skull, sdk.items.Ring], Index: Recipe.Reroll.HighRare, Enabled: false}); + this.recipes.push({ Ingredients: [Config.Recipes[i][1], sdk.items.gems.Perfect.Skull, sdk.items.Ring], Index: Recipe.Reroll.HighRare, Enabled: false }); break; case Recipe.LowToNorm.Weapon: - this.recipes.push({Ingredients: [Config.Recipes[i][1], sdk.items.runes.Eld, "cgem"], Index: Recipe.LowToNorm.Weapon}); + this.recipes.push({ Ingredients: [Config.Recipes[i][1], sdk.items.runes.Eld, "cgem"], Index: Recipe.LowToNorm.Weapon }); break; case Recipe.LowToNorm.Armor: - this.recipes.push({Ingredients: [Config.Recipes[i][1], sdk.items.runes.El, "cgem"], Index: Recipe.LowToNorm.Armor}); + this.recipes.push({ Ingredients: [Config.Recipes[i][1], sdk.items.runes.El, "cgem"], Index: Recipe.LowToNorm.Armor }); break; // Rune Recipes----------------------------------------------------------------------------------------------------------------------------------// @@ -276,136 +276,136 @@ Cubing.buildRecipes = function () { case sdk.items.runes.Tal: case sdk.items.runes.Ral: case sdk.items.runes.Ort: - this.recipes.push({Ingredients: [Config.Recipes[i][1], Config.Recipes[i][1], Config.Recipes[i][1]], Index: Recipe.Rune, AlwaysEnabled: true}); + this.recipes.push({ Ingredients: [Config.Recipes[i][1], Config.Recipes[i][1], Config.Recipes[i][1]], Index: Recipe.Rune, AlwaysEnabled: true }); break; case sdk.items.runes.Thul: // thul->amn - this.recipes.push({Ingredients: [sdk.items.runes.Thul, sdk.items.runes.Thul, sdk.items.runes.Thul, sdk.items.gems.Chipped.Topaz], Index: Recipe.Rune}); + this.recipes.push({ Ingredients: [sdk.items.runes.Thul, sdk.items.runes.Thul, sdk.items.runes.Thul, sdk.items.gems.Chipped.Topaz], Index: Recipe.Rune }); break; case sdk.items.runes.Amn: // amn->sol - this.recipes.push({Ingredients: [sdk.items.runes.Amn, sdk.items.runes.Amn, sdk.items.runes.Amn, sdk.items.gems.Chipped.Amethyst], Index: Recipe.Rune}); + this.recipes.push({ Ingredients: [sdk.items.runes.Amn, sdk.items.runes.Amn, sdk.items.runes.Amn, sdk.items.gems.Chipped.Amethyst], Index: Recipe.Rune }); break; case sdk.items.runes.Sol: // sol->shael - this.recipes.push({Ingredients: [sdk.items.runes.Sol, sdk.items.runes.Sol, sdk.items.runes.Sol, sdk.items.gems.Chipped.Sapphire], Index: Recipe.Rune}); + this.recipes.push({ Ingredients: [sdk.items.runes.Sol, sdk.items.runes.Sol, sdk.items.runes.Sol, sdk.items.gems.Chipped.Sapphire], Index: Recipe.Rune }); break; case sdk.items.runes.Shael: // shael->dol - this.recipes.push({Ingredients: [sdk.items.runes.Shael, sdk.items.runes.Shael, sdk.items.runes.Shael, sdk.items.gems.Chipped.Ruby], Index: Recipe.Rune}); + this.recipes.push({ Ingredients: [sdk.items.runes.Shael, sdk.items.runes.Shael, sdk.items.runes.Shael, sdk.items.gems.Chipped.Ruby], Index: Recipe.Rune }); break; case sdk.items.runes.Dol: // dol->hel if (me.ladder || Developer.addLadderRW) { - this.recipes.push({Ingredients: [sdk.items.runes.Dol, sdk.items.runes.Dol, sdk.items.runes.Dol, sdk.items.gems.Chipped.Emerald], Index: Recipe.Rune}); + this.recipes.push({ Ingredients: [sdk.items.runes.Dol, sdk.items.runes.Dol, sdk.items.runes.Dol, sdk.items.gems.Chipped.Emerald], Index: Recipe.Rune }); } break; case sdk.items.runes.Hel: // hel->io if (me.ladder || Developer.addLadderRW) { - this.recipes.push({Ingredients: [sdk.items.runes.Hel, sdk.items.runes.Hel, sdk.items.runes.Hel, sdk.items.gems.Chipped.Diamond], Index: Recipe.Rune}); + this.recipes.push({ Ingredients: [sdk.items.runes.Hel, sdk.items.runes.Hel, sdk.items.runes.Hel, sdk.items.gems.Chipped.Diamond], Index: Recipe.Rune }); } break; case sdk.items.runes.Io: // io->lum if (me.ladder || Developer.addLadderRW) { - this.recipes.push({Ingredients: [sdk.items.runes.Io, sdk.items.runes.Io, sdk.items.runes.Io, sdk.items.gems.Flawed.Topaz], Index: Recipe.Rune}); + this.recipes.push({ Ingredients: [sdk.items.runes.Io, sdk.items.runes.Io, sdk.items.runes.Io, sdk.items.gems.Flawed.Topaz], Index: Recipe.Rune }); } break; case sdk.items.runes.Lum: // lum->ko if (me.ladder || Developer.addLadderRW) { - this.recipes.push({Ingredients: [sdk.items.runes.Lum, sdk.items.runes.Lum, sdk.items.runes.Lum, sdk.items.gems.Flawed.Amethyst], Index: Recipe.Rune}); + this.recipes.push({ Ingredients: [sdk.items.runes.Lum, sdk.items.runes.Lum, sdk.items.runes.Lum, sdk.items.gems.Flawed.Amethyst], Index: Recipe.Rune }); } break; case sdk.items.runes.Ko: // ko->fal if (me.ladder || Developer.addLadderRW) { - this.recipes.push({Ingredients: [sdk.items.runes.Ko, sdk.items.runes.Ko, sdk.items.runes.Ko, sdk.items.gems.Flawed.Sapphire], Index: Recipe.Rune}); + this.recipes.push({ Ingredients: [sdk.items.runes.Ko, sdk.items.runes.Ko, sdk.items.runes.Ko, sdk.items.gems.Flawed.Sapphire], Index: Recipe.Rune }); } break; case sdk.items.runes.Fal: // fal->lem if (me.ladder || Developer.addLadderRW) { - this.recipes.push({Ingredients: [sdk.items.runes.Fal, sdk.items.runes.Fal, sdk.items.runes.Fal, sdk.items.gems.Flawed.Ruby], Index: Recipe.Rune}); + this.recipes.push({ Ingredients: [sdk.items.runes.Fal, sdk.items.runes.Fal, sdk.items.runes.Fal, sdk.items.gems.Flawed.Ruby], Index: Recipe.Rune }); } break; case sdk.items.runes.Lem: // lem->pul if (me.ladder || Developer.addLadderRW) { - this.recipes.push({Ingredients: [sdk.items.runes.Lem, sdk.items.runes.Lem, sdk.items.runes.Lem, sdk.items.gems.Flawed.Emerald], Index: Recipe.Rune}); + this.recipes.push({ Ingredients: [sdk.items.runes.Lem, sdk.items.runes.Lem, sdk.items.runes.Lem, sdk.items.gems.Flawed.Emerald], Index: Recipe.Rune }); } break; case sdk.items.runes.Pul: // pul->um if (me.ladder || Developer.addLadderRW) { - this.recipes.push({Ingredients: [sdk.items.runes.Pul, sdk.items.runes.Pul, sdk.items.gems.Flawed.Diamond], Index: Recipe.Rune}); + this.recipes.push({ Ingredients: [sdk.items.runes.Pul, sdk.items.runes.Pul, sdk.items.gems.Flawed.Diamond], Index: Recipe.Rune }); } break; case sdk.items.runes.Um: // um->mal if (me.ladder || Developer.addLadderRW) { - this.recipes.push({Ingredients: [sdk.items.runes.Um, sdk.items.runes.Um, sdk.items.gems.Normal.Topaz], Index: Recipe.Rune}); + this.recipes.push({ Ingredients: [sdk.items.runes.Um, sdk.items.runes.Um, sdk.items.gems.Normal.Topaz], Index: Recipe.Rune }); } break; case sdk.items.runes.Mal: // mal->ist if (me.ladder || Developer.addLadderRW) { - this.recipes.push({Ingredients: [sdk.items.runes.Mal, sdk.items.runes.Mal, sdk.items.gems.Normal.Amethyst], Index: Recipe.Rune}); + this.recipes.push({ Ingredients: [sdk.items.runes.Mal, sdk.items.runes.Mal, sdk.items.gems.Normal.Amethyst], Index: Recipe.Rune }); } break; case sdk.items.runes.Ist: // ist->gul if (me.ladder || Developer.addLadderRW) { - this.recipes.push({Ingredients: [sdk.items.runes.Ist, sdk.items.runes.Ist, sdk.items.gems.Normal.Sapphire], Index: Recipe.Rune}); + this.recipes.push({ Ingredients: [sdk.items.runes.Ist, sdk.items.runes.Ist, sdk.items.gems.Normal.Sapphire], Index: Recipe.Rune }); } break; case sdk.items.runes.Gul: // gul->vex if (me.ladder || Developer.addLadderRW) { - this.recipes.push({Ingredients: [sdk.items.runes.Gul, sdk.items.runes.Gul, sdk.items.gems.Normal.Ruby], Index: Recipe.Rune}); + this.recipes.push({ Ingredients: [sdk.items.runes.Gul, sdk.items.runes.Gul, sdk.items.gems.Normal.Ruby], Index: Recipe.Rune }); } break; case sdk.items.runes.Vex: // vex->ohm if (me.ladder || Developer.addLadderRW) { - this.recipes.push({Ingredients: [sdk.items.runes.Vex, sdk.items.runes.Vex, sdk.items.gems.Normal.Emerald], Index: Recipe.Rune}); + this.recipes.push({ Ingredients: [sdk.items.runes.Vex, sdk.items.runes.Vex, sdk.items.gems.Normal.Emerald], Index: Recipe.Rune }); } break; case sdk.items.runes.Ohm: // ohm->lo if (me.ladder || Developer.addLadderRW) { - this.recipes.push({Ingredients: [sdk.items.runes.Ohm, sdk.items.runes.Ohm, sdk.items.gems.Normal.Diamond], Index: Recipe.Rune}); + this.recipes.push({ Ingredients: [sdk.items.runes.Ohm, sdk.items.runes.Ohm, sdk.items.gems.Normal.Diamond], Index: Recipe.Rune }); } break; case sdk.items.runes.Lo: // lo->sur if (me.ladder || Developer.addLadderRW) { - this.recipes.push({Ingredients: [sdk.items.runes.Lo, sdk.items.runes.Lo, sdk.items.gems.Flawless.Topaz], Index: Recipe.Rune}); + this.recipes.push({ Ingredients: [sdk.items.runes.Lo, sdk.items.runes.Lo, sdk.items.gems.Flawless.Topaz], Index: Recipe.Rune }); } break; case sdk.items.runes.Sur: // sur->ber if (me.ladder || Developer.addLadderRW) { - this.recipes.push({Ingredients: [sdk.items.runes.Sur, sdk.items.runes.Sur, sdk.items.gems.Flawless.Amethyst], Index: Recipe.Rune}); + this.recipes.push({ Ingredients: [sdk.items.runes.Sur, sdk.items.runes.Sur, sdk.items.gems.Flawless.Amethyst], Index: Recipe.Rune }); } break; case sdk.items.runes.Ber: // ber->jah if (me.ladder || Developer.addLadderRW) { - this.recipes.push({Ingredients: [sdk.items.runes.Ber, sdk.items.runes.Ber, sdk.items.gems.Flawless.Sapphire], Index: Recipe.Rune}); + this.recipes.push({ Ingredients: [sdk.items.runes.Ber, sdk.items.runes.Ber, sdk.items.gems.Flawless.Sapphire], Index: Recipe.Rune }); } break; case sdk.items.runes.Jah: // jah->cham if (me.ladder || Developer.addLadderRW) { - this.recipes.push({Ingredients: [sdk.items.runes.Jah, sdk.items.runes.Jah, sdk.items.gems.Flawless.Ruby], Index: Recipe.Rune}); + this.recipes.push({ Ingredients: [sdk.items.runes.Jah, sdk.items.runes.Jah, sdk.items.gems.Flawless.Ruby], Index: Recipe.Rune }); } break; case sdk.items.runes.Cham: // cham->zod if (me.ladder || Developer.addLadderRW) { - this.recipes.push({Ingredients: [sdk.items.runes.Cham, sdk.items.runes.Cham, sdk.items.gems.Flawless.Emerald], Index: Recipe.Rune}); + this.recipes.push({ Ingredients: [sdk.items.runes.Cham, sdk.items.runes.Cham, sdk.items.gems.Flawless.Emerald], Index: Recipe.Rune }); } break; @@ -413,7 +413,7 @@ Cubing.buildRecipes = function () { break; case Recipe.Token: - this.recipes.push({Ingredients: [sdk.quest.item.TwistedEssenceofSuffering, sdk.quest.item.ChargedEssenceofHatred, sdk.quest.item.BurningEssenceofTerror, sdk.quest.item.FesteringEssenceofDestruction], Index: Recipe.Token, AlwaysEnabled: true}); + this.recipes.push({ Ingredients: [sdk.quest.item.TwistedEssenceofSuffering, sdk.quest.item.ChargedEssenceofHatred, sdk.quest.item.BurningEssenceofTerror, sdk.quest.item.FesteringEssenceofDestruction], Index: Recipe.Token, AlwaysEnabled: true }); break; } @@ -473,7 +473,7 @@ Cubing.buildLists = function () { } // add the item to needed list - enable pickup - this.neededIngredients.push({classid: this.recipes[i].Ingredients[j], recipe: this.recipes[i]}); + this.neededIngredients.push({ classid: this.recipes[i].Ingredients[j], recipe: this.recipes[i] }); // skip flawless gems adding if we don't have the main item (Recipe.Gem and Recipe.Rune for el-ort are always enabled) if (!this.recipes[i].Enabled) { @@ -483,85 +483,85 @@ Cubing.buildLists = function () { // if the recipe is enabled (we have the main item), add flawless gem recipes (if needed) if (this.subRecipes.indexOf(sdk.items.gems.Perfect.Amethyst) === -1 && (this.recipes[i].Ingredients[j] === sdk.items.gems.Perfect.Amethyst || (this.recipes[i].Ingredients[j] === "pgem" && this.gemList.indexOf(sdk.items.gems.Perfect.Amethyst) > -1))) { - this.recipes.push({Ingredients: [sdk.items.gems.Flawless.Amethyst, sdk.items.gems.Flawless.Amethyst, sdk.items.gems.Flawless.Amethyst], Index: Recipe.Gem, AlwaysEnabled: true, MainRecipe: this.recipes[i].Index}); + this.recipes.push({ Ingredients: [sdk.items.gems.Flawless.Amethyst, sdk.items.gems.Flawless.Amethyst, sdk.items.gems.Flawless.Amethyst], Index: Recipe.Gem, AlwaysEnabled: true, MainRecipe: this.recipes[i].Index }); this.subRecipes.push(sdk.items.gems.Perfect.Amethyst); } // Make flawless amethyst if (this.subRecipes.indexOf(sdk.items.gems.Flawless.Amethyst) === -1 && (this.recipes[i].Ingredients[j] === sdk.items.gems.Flawless.Amethyst || (this.recipes[i].Ingredients[j] === "fgem" && this.gemList.indexOf(sdk.items.gems.Flawless.Amethyst) > -1))) { - this.recipes.push({Ingredients: [sdk.items.gems.Normal.Amethyst, sdk.items.gems.Normal.Amethyst, sdk.items.gems.Normal.Amethyst], Index: Recipe.Gem, AlwaysEnabled: true, MainRecipe: this.recipes[i].Index}); + this.recipes.push({ Ingredients: [sdk.items.gems.Normal.Amethyst, sdk.items.gems.Normal.Amethyst, sdk.items.gems.Normal.Amethyst], Index: Recipe.Gem, AlwaysEnabled: true, MainRecipe: this.recipes[i].Index }); this.subRecipes.push(sdk.items.gems.Flawless.Amethyst); } // Make perf topaz if (this.subRecipes.indexOf(sdk.items.gems.Perfect.Topaz) === -1 && (this.recipes[i].Ingredients[j] === sdk.items.gems.Perfect.Topaz || (this.recipes[i].Ingredients[j] === "pgem" && this.gemList.indexOf(sdk.items.gems.Perfect.Topaz) > -1))) { - this.recipes.push({Ingredients: [sdk.items.gems.Flawless.Topaz, sdk.items.gems.Flawless.Topaz, sdk.items.gems.Flawless.Topaz], Index: Recipe.Gem, AlwaysEnabled: true, MainRecipe: this.recipes[i].Index}); + this.recipes.push({ Ingredients: [sdk.items.gems.Flawless.Topaz, sdk.items.gems.Flawless.Topaz, sdk.items.gems.Flawless.Topaz], Index: Recipe.Gem, AlwaysEnabled: true, MainRecipe: this.recipes[i].Index }); this.subRecipes.push(sdk.items.gems.Perfect.Topaz); } // Make flawless topaz if (this.subRecipes.indexOf(sdk.items.gems.Flawless.Topaz) === -1 && (this.recipes[i].Ingredients[j] === sdk.items.gems.Flawless.Topaz || (this.recipes[i].Ingredients[j] === "fgem" && this.gemList.indexOf(sdk.items.gems.Flawless.Topaz) > -1))) { - this.recipes.push({Ingredients: [sdk.items.gems.Normal.Topaz, sdk.items.gems.Normal.Topaz, sdk.items.gems.Normal.Topaz], Index: Recipe.Gem, AlwaysEnabled: true, MainRecipe: this.recipes[i].Index}); + this.recipes.push({ Ingredients: [sdk.items.gems.Normal.Topaz, sdk.items.gems.Normal.Topaz, sdk.items.gems.Normal.Topaz], Index: Recipe.Gem, AlwaysEnabled: true, MainRecipe: this.recipes[i].Index }); this.subRecipes.push(sdk.items.gems.Flawless.Topaz); } // Make perf sapphire if (this.subRecipes.indexOf(sdk.items.gems.Perfect.Sapphire) === -1 && (this.recipes[i].Ingredients[j] === sdk.items.gems.Perfect.Sapphire || (this.recipes[i].Ingredients[j] === "pgem" && this.gemList.indexOf(sdk.items.gems.Perfect.Sapphire) > -1))) { - this.recipes.push({Ingredients: [sdk.items.gems.Flawless.Sapphire, sdk.items.gems.Flawless.Sapphire, sdk.items.gems.Flawless.Sapphire], Index: Recipe.Gem, AlwaysEnabled: true, MainRecipe: this.recipes[i].Index}); + this.recipes.push({ Ingredients: [sdk.items.gems.Flawless.Sapphire, sdk.items.gems.Flawless.Sapphire, sdk.items.gems.Flawless.Sapphire], Index: Recipe.Gem, AlwaysEnabled: true, MainRecipe: this.recipes[i].Index }); this.subRecipes.push(sdk.items.gems.Perfect.Sapphire); } // Make flawless sapphire if (this.subRecipes.indexOf(sdk.items.gems.Flawless.Sapphire) === -1 && (this.recipes[i].Ingredients[j] === sdk.items.gems.Flawless.Sapphire || (this.recipes[i].Ingredients[j] === "fgem" && this.gemList.indexOf(sdk.items.gems.Flawless.Sapphire) > -1))) { - this.recipes.push({Ingredients: [sdk.items.gems.Normal.Sapphire, sdk.items.gems.Normal.Sapphire, sdk.items.gems.Normal.Sapphire], Index: Recipe.Gem, AlwaysEnabled: true, MainRecipe: this.recipes[i].Index}); + this.recipes.push({ Ingredients: [sdk.items.gems.Normal.Sapphire, sdk.items.gems.Normal.Sapphire, sdk.items.gems.Normal.Sapphire], Index: Recipe.Gem, AlwaysEnabled: true, MainRecipe: this.recipes[i].Index }); this.subRecipes.push(sdk.items.gems.Flawless.Sapphire); } // Make perf emerald if (this.subRecipes.indexOf(sdk.items.gems.Perfect.Emerald) === -1 && (this.recipes[i].Ingredients[j] === sdk.items.gems.Perfect.Emerald || (this.recipes[i].Ingredients[j] === "pgem" && this.gemList.indexOf(sdk.items.gems.Perfect.Emerald) > -1))) { - this.recipes.push({Ingredients: [sdk.items.gems.Flawless.Emerald, sdk.items.gems.Flawless.Emerald, sdk.items.gems.Flawless.Emerald], Index: Recipe.Gem, AlwaysEnabled: true, MainRecipe: this.recipes[i].Index}); + this.recipes.push({ Ingredients: [sdk.items.gems.Flawless.Emerald, sdk.items.gems.Flawless.Emerald, sdk.items.gems.Flawless.Emerald], Index: Recipe.Gem, AlwaysEnabled: true, MainRecipe: this.recipes[i].Index }); this.subRecipes.push(sdk.items.gems.Perfect.Emerald); } // Make flawless emerald if (this.subRecipes.indexOf(sdk.items.gems.Flawless.Emerald) === -1 && (this.recipes[i].Ingredients[j] === sdk.items.gems.Flawless.Emerald || (this.recipes[i].Ingredients[j] === "fgem" && this.gemList.indexOf(sdk.items.gems.Flawless.Emerald) > -1))) { - this.recipes.push({Ingredients: [sdk.items.gems.Normal.Emerald, sdk.items.gems.Normal.Emerald, sdk.items.gems.Normal.Emerald], Index: Recipe.Gem, AlwaysEnabled: true, MainRecipe: this.recipes[i].Index}); + this.recipes.push({ Ingredients: [sdk.items.gems.Normal.Emerald, sdk.items.gems.Normal.Emerald, sdk.items.gems.Normal.Emerald], Index: Recipe.Gem, AlwaysEnabled: true, MainRecipe: this.recipes[i].Index }); this.subRecipes.push(sdk.items.gems.Flawless.Emerald); } // Make perf ruby if (this.subRecipes.indexOf(sdk.items.gems.Perfect.Ruby) === -1 && (this.recipes[i].Ingredients[j] === sdk.items.gems.Perfect.Ruby || (this.recipes[i].Ingredients[j] === "pgem" && this.gemList.indexOf(sdk.items.gems.Perfect.Ruby) > -1))) { - this.recipes.push({Ingredients: [sdk.items.gems.Flawless.Ruby, sdk.items.gems.Flawless.Ruby, sdk.items.gems.Flawless.Ruby], Index: Recipe.Gem, AlwaysEnabled: true, MainRecipe: this.recipes[i].Index}); + this.recipes.push({ Ingredients: [sdk.items.gems.Flawless.Ruby, sdk.items.gems.Flawless.Ruby, sdk.items.gems.Flawless.Ruby], Index: Recipe.Gem, AlwaysEnabled: true, MainRecipe: this.recipes[i].Index }); this.subRecipes.push(sdk.items.gems.Perfect.Ruby); } // Make flawless ruby if (this.subRecipes.indexOf(sdk.items.gems.Flawless.Ruby) === -1 && (this.recipes[i].Ingredients[j] === sdk.items.gems.Flawless.Ruby || (this.recipes[i].Ingredients[j] === "fgem" && this.gemList.indexOf(sdk.items.gems.Flawless.Ruby) > -1))) { - this.recipes.push({Ingredients: [sdk.items.gems.Normal.Ruby, sdk.items.gems.Normal.Ruby, sdk.items.gems.Normal.Ruby], Index: Recipe.Gem, AlwaysEnabled: true, MainRecipe: this.recipes[i].Index}); + this.recipes.push({ Ingredients: [sdk.items.gems.Normal.Ruby, sdk.items.gems.Normal.Ruby, sdk.items.gems.Normal.Ruby], Index: Recipe.Gem, AlwaysEnabled: true, MainRecipe: this.recipes[i].Index }); this.subRecipes.push(sdk.items.gems.Flawless.Ruby); } // Make perf diamond if (this.subRecipes.indexOf(sdk.items.gems.Perfect.Diamond) === -1 && (this.recipes[i].Ingredients[j] === sdk.items.gems.Perfect.Diamond || (this.recipes[i].Ingredients[j] === "pgem" && this.gemList.indexOf(sdk.items.gems.Perfect.Diamond) > -1))) { - this.recipes.push({Ingredients: [sdk.items.gems.Flawless.Diamond, sdk.items.gems.Flawless.Diamond, sdk.items.gems.Flawless.Diamond], Index: Recipe.Gem, AlwaysEnabled: true, MainRecipe: this.recipes[i].Index}); + this.recipes.push({ Ingredients: [sdk.items.gems.Flawless.Diamond, sdk.items.gems.Flawless.Diamond, sdk.items.gems.Flawless.Diamond], Index: Recipe.Gem, AlwaysEnabled: true, MainRecipe: this.recipes[i].Index }); this.subRecipes.push(sdk.items.gems.Perfect.Diamond); } // Make flawless diamond if (this.subRecipes.indexOf(sdk.items.gems.Flawless.Diamond) === -1 && (this.recipes[i].Ingredients[j] === sdk.items.gems.Flawless.Diamond || (this.recipes[i].Ingredients[j] === "fgem" && this.gemList.indexOf(sdk.items.gems.Flawless.Diamond) > -1))) { - this.recipes.push({Ingredients: [sdk.items.gems.Normal.Diamond, sdk.items.gems.Normal.Diamond, sdk.items.gems.Normal.Diamond], Index: Recipe.Gem, AlwaysEnabled: true, MainRecipe: this.recipes[i].Index}); + this.recipes.push({ Ingredients: [sdk.items.gems.Normal.Diamond, sdk.items.gems.Normal.Diamond, sdk.items.gems.Normal.Diamond], Index: Recipe.Gem, AlwaysEnabled: true, MainRecipe: this.recipes[i].Index }); this.subRecipes.push(sdk.items.gems.Flawless.Diamond); } // Make perf skull if (this.subRecipes.indexOf(sdk.items.gems.Perfect.Skull) === -1 && (this.recipes[i].Ingredients[j] === sdk.items.gems.Perfect.Skull || (this.recipes[i].Ingredients[j] === "pgem" && this.gemList.indexOf(sdk.items.gems.Perfect.Skull) > -1))) { - this.recipes.push({Ingredients: [sdk.items.gems.Flawless.Skull, sdk.items.gems.Flawless.Skull, sdk.items.gems.Flawless.Skull], Index: Recipe.Gem, AlwaysEnabled: true, MainRecipe: this.recipes[i].Index}); + this.recipes.push({ Ingredients: [sdk.items.gems.Flawless.Skull, sdk.items.gems.Flawless.Skull, sdk.items.gems.Flawless.Skull], Index: Recipe.Gem, AlwaysEnabled: true, MainRecipe: this.recipes[i].Index }); this.subRecipes.push(sdk.items.gems.Perfect.Skull); } // Make flawless skull if (this.subRecipes.indexOf(sdk.items.gems.Flawless.Skull) === -1 && (this.recipes[i].Ingredients[j] === sdk.items.gems.Flawless.Skull || (this.recipes[i].Ingredients[j] === "fgem" && this.gemList.indexOf(sdk.items.gems.Flawless.Skull) > -1))) { - this.recipes.push({Ingredients: [sdk.items.gems.Normal.Skull, sdk.items.gems.Normal.Skull, sdk.items.gems.Normal.Skull], Index: Recipe.Gem, AlwaysEnabled: true, MainRecipe: this.recipes[i].Index}); + this.recipes.push({ Ingredients: [sdk.items.gems.Normal.Skull, sdk.items.gems.Normal.Skull, sdk.items.gems.Normal.Skull], Index: Recipe.Gem, AlwaysEnabled: true, MainRecipe: this.recipes[i].Index }); this.subRecipes.push(sdk.items.gems.Flawless.Skull); } } @@ -602,7 +602,7 @@ Cubing.checkItem = function (unit) { && unit.classid === this.validIngredients[i].classid && unit.quality === this.validIngredients[i].quality) { // item is better than the one we currently have, so add it to validIngredient array and remove old item if (unit.ilvl > this.validIngredients[i].ilvl && this.validItem(unit, this.validIngredients[i].recipe)) { - this.validIngredients.push({classid: unit.classid, quality: unit.quality, ilvl: unit.ilvl, gid: unit.gid, recipe: this.validIngredients[i].recipe}); + this.validIngredients.push({ classid: unit.classid, quality: unit.quality, ilvl: unit.ilvl, gid: unit.gid, recipe: this.validIngredients[i].recipe }); this.validIngredients.splice(i, 1); return true; } @@ -756,6 +756,10 @@ Cubing.validItem = function (unit, recipe) { } else if (recipe.Index >= Recipe.Socket.Shield && recipe.Index <= Recipe.Socket.Helm) { // Normal item matching pickit entry, no sockets if (unit.normal && unit.sockets === 0) { + if (Pickit.Result.WANTED === ntipResult + && [sdk.items.type.Wand, sdk.items.type.VoodooHeads, sdk.items.type.AuricShields, sdk.items.type.PrimalHelm, sdk.items.type.Pelt].includes(unit.itemType)) { + if (!Item.betterThanStashed(unit)) return false; + } switch (recipe.Ethereal) { case Roll.All: case undefined: From f01c82a858bc89d843c127adef64bab9a6051f33 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Mon, 27 Feb 2023 14:39:37 -0500 Subject: [PATCH 048/263] Clearer steps for anya - Check the quest states as we go, as to prevent wasted effort --- libs/SoloPlay/Functions/Quest.js | 8 ++- libs/SoloPlay/Scripts/anya.js | 101 +++++++++++++++++++++---------- 2 files changed, 76 insertions(+), 33 deletions(-) diff --git a/libs/SoloPlay/Functions/Quest.js b/libs/SoloPlay/Functions/Quest.js index 4ab13db2..c852ad52 100644 --- a/libs/SoloPlay/Functions/Quest.js +++ b/libs/SoloPlay/Functions/Quest.js @@ -280,7 +280,7 @@ const Quest = { let questTool = me.getItem(tool); while (me.getItem(tool)) { - smashable.distance > 4 && Pather.moveToEx(smashable.x, smashable.y, {clearSettings: {allowClearing: false}}); + smashable.distance > 4 && Pather.moveToEx(smashable.x, smashable.y, { clearSettings: { allowClearing: false } }); Skill.cast(sdk.skills.Attack, sdk.skills.hand.Right, smashable); smashable.interact(); @@ -657,6 +657,12 @@ const Quest = { sor.isInStash && Town.openStash() && delay(300); sor.use() && console.log("ÿc8Kolbot-SoloPlayÿc0: used scroll of resistance"); } + + if (Misc.checkQuest(sdk.quest.id.PrisonofIce, 7/** Used the scroll */) + && !Misc.checkQuest(sdk.quest.id.PrisonofIce, sdk.quest.states.Completed)) { + // never talked to anya after drinking potion, lets do that + Town.npcInteract("anya"); + } } Misc.checkSocketables(); diff --git a/libs/SoloPlay/Scripts/anya.js b/libs/SoloPlay/Scripts/anya.js index d998018b..b6716d12 100644 --- a/libs/SoloPlay/Scripts/anya.js +++ b/libs/SoloPlay/Scripts/anya.js @@ -7,10 +7,17 @@ function anya () { Town.doChores(false, { fullChores: true }); + // double check after chores, as that calls Quest.unfinishedQuests + if (Misc.checkQuest(sdk.quest.id.PrisonofIce, sdk.quest.states.Completed)) return true; + Town.goToTown(5); myPrint("starting anya"); - Pather.checkWP(sdk.areas.CrystalizedPassage, true) ? Pather.useWaypoint(sdk.areas.CrystalizedPassage) : Pather.getWP(sdk.areas.CrystalizedPassage); + Pather.checkWP(sdk.areas.CrystalizedPassage, true) + ? Pather.useWaypoint(sdk.areas.CrystalizedPassage) + : Pather.getWP(sdk.areas.CrystalizedPassage); + + // Move to anya's platform Precast.doPrecast(true); Pather.clearToExit(sdk.areas.CrystalizedPassage, sdk.areas.FrozenRiver, Pather.useTeleport()); @@ -18,11 +25,12 @@ function anya () { let fStein = Game.getMonster(getLocaleString(sdk.locale.monsters.Frozenstein)); // let frozenanya = Game.getObject(sdk.objects.FrozenAnya); return (fStein && fStein.distance < 30) /*&& /* (frozenanya && frozenanya.distance < 35) */; - }})) { + } })) { console.log("ÿc8Kolbot-SoloPlayÿc0: Failed to move to Anya"); return false; } + // Making sure it's safe, needs work let presetLoc = Game.getPresetObject(me.area, sdk.objects.FrozenAnyasPlatform).realCoords(); let fStein = Game.getMonster(getLocaleString(sdk.locale.monsters.Frozenstein)); @@ -34,13 +42,18 @@ function anya () { let frozenanya = Game.getObject(sdk.objects.FrozenAnya); if (!frozenanya) { - Pather.moveToEx(presetLoc.x, presetLoc.y, { callback: () => Game.getObject(sdk.objects.FrozenAnya)}); + Pather.moveToEx(presetLoc.x, presetLoc.y, { callback: () => Game.getObject(sdk.objects.FrozenAnya) }); frozenanya = Game.getObject(sdk.objects.FrozenAnya); } - // todo - tele char can lure frozenstein away from anya as he can be hard to kill - // aggro the pack then move back until there isn't any monster around anya (note) we can only detect mobs around 40 yards of us - // then should use a static location behind anya as our destination to tele to + /** + * Here we have issues sometimes + * Including a check for her unfreezing in case we already have malah's potion + * @todo + * - tele char can lure frozenstein away from anya as he can be hard to kill + * aggro the pack then move back until there isn't any monster around anya (note) we can only detect mobs around 40 yards of us + * then should use a static location behind anya as our destination to tele to + */ if (frozenanya) { if (me.sorceress && Skill.haveTK) { Attack.getIntoPosition(frozenanya, 15, sdk.collision.LineOfSight, Pather.canTeleport(), true); @@ -49,40 +62,64 @@ function anya () { Pather.moveToUnit(frozenanya); Packet.entityInteract(frozenanya); } - Misc.poll(() => getIsTalkingNPC(), 2000, 50); + Misc.poll(() => getIsTalkingNPC() || frozenanya.mode, 2000, 50); me.cancel() && me.cancel(); } Town.npcInteract("malah"); - Town.doChores(); - if (!Misc.poll(() => { - Pather.usePortal(sdk.areas.FrozenRiver, me.name); - return me.inArea(sdk.areas.FrozenRiver); - }, Time.seconds(30), 1000)) throw new Error("Anya quest failed - Failed to return to frozen river"); - - frozenanya = Game.getObject(sdk.objects.FrozenAnya); // Check again in case she's no longer there from first intereaction - if (frozenanya) { - for (let i = 0; i < 3; i++) { - frozenanya.distance > 5 && Pather.moveToUnit(frozenanya, 1, 2); - Packet.entityInteract(frozenanya); - if (Misc.poll(() => frozenanya.mode, Time.seconds(2), 50)) { - me.cancel() && me.cancel(); - break; - } - if (getIsTalkingNPC()) { - // in case we failed to interact the first time this prevent us from crashing if her dialog is going - me.cancel() && me.cancel(); + /** + * Now this should prevent us from re-entering if we either failed to interact with anya in the first place + * or if we had malah's potion because this is our second attempt and we managed to unfreeze her + */ + if (me.getItem(sdk.quest.item.MalahsPotion)) { + console.log("Got potion, lets go unfreeze anya"); + + if (!Misc.poll(() => { + Pather.usePortal(sdk.areas.FrozenRiver, me.name); + return me.inArea(sdk.areas.FrozenRiver); + }, Time.seconds(30), 1000)) throw new Error("Anya quest failed - Failed to return to frozen river"); + + frozenanya = Game.getObject(sdk.objects.FrozenAnya); // Check again in case she's no longer there from first intereaction + + if (frozenanya) { + for (let i = 0; i < 3; i++) { + frozenanya.distance > 5 && Pather.moveToUnit(frozenanya, 1, 2); + Packet.entityInteract(frozenanya); + if (Misc.poll(() => frozenanya.mode, Time.seconds(2), 50)) { + me.cancel() && me.cancel(); + break; + } + if (getIsTalkingNPC()) { + // in case we failed to interact the first time this prevent us from crashing if her dialog is going + me.cancel() && me.cancel(); + } + Attack.clearPos(frozenanya.x, frozenanya.y, 15); } - Attack.clearPos(frozenanya.x, frozenanya.y, 15); } } - Town.goToTown(5); - Town.npcInteract("malah"); - Quest.unfinishedQuests(); - Town.doChores(); - Town.npcInteract("anya"); + /** + * Now lets handle completing the quest as we have freed anya + */ + if (Misc.checkQuest(sdk.quest.id.PrisonofIce, sdk.quest.states.ReqComplete)) { + /** + * Here we haven't talked to malah to recieve the scroll yet so lets do that + */ + if (!Misc.checkQuest(sdk.quest.id.PrisonofIce, 8/** Recieved the scroll */)) { + Town.npcInteract("malah"); + } + + /** + * Here we haven't talked to anya to open the red portal + */ + if (!Misc.checkQuest(sdk.quest.id.PrisonofIce, 9/** Talk to anya in town */)) { + Town.npcInteract("anya"); + } + + /** Handles using the scroll, no need to repeat the same code here */ + Quest.unfinishedQuests(); + } - return true; + return !!Misc.checkQuest(sdk.quest.id.PrisonofIce, sdk.quest.states.Completed); } From 9b7b51d44373bdd4c685728ca07894f1ea49db32 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Mon, 27 Feb 2023 15:49:07 -0500 Subject: [PATCH 049/263] Update OOGOverrides.js - Fix R/D check causing restart when we never made it to the charselect screen --- libs/SoloPlay/Tools/OOGOverrides.js | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/libs/SoloPlay/Tools/OOGOverrides.js b/libs/SoloPlay/Tools/OOGOverrides.js index 6d876632..8ee395ca 100644 --- a/libs/SoloPlay/Tools/OOGOverrides.js +++ b/libs/SoloPlay/Tools/OOGOverrides.js @@ -125,6 +125,8 @@ const locations = {}; "\nÿc8ThreadData ::\n", getScript(true), "\nÿc8GlobalVariabls ::\n", Object.keys(global), "\n" + sdk.colors.Red + "//-----------DataDump End-----------//"); + } else if (msg === "deleteAndRemake") { + Starter.deadCheck = true; } else { orignal(msg); } @@ -542,9 +544,8 @@ const locations = {}; CharData.updateData("me", "finalBuild", Starter.profileInfo.tag); Developer.logPerformance && Tracker.initialize(); D2Bot.printToConsole("Deleted: " + info.charName + ". Now remaking...", sdk.colors.D2Bot.Gold); - ControlAction.makeCharacter(Starter.profileInfo); - return true; + return ControlAction.makeCharacter(Starter.profileInfo); }; ControlAction.saveInfo = function (info) { @@ -987,12 +988,21 @@ const locations = {}; if (Starter.deadCheck && ControlAction.deleteAndRemakeChar(Starter.profileInfo)) { Starter.deadCheck = false; + + return; + } + + if (!Controls.CharSelectCreate.control) { + // We aren't in the right place + return; } if (Object.keys(Starter.profileInfo).length) { if (!ControlAction.findCharacter(Starter.profileInfo)) { + let currLoc = getLocation(); if (Starter.profileInfo.charName === DataFile.getObj().name - && getLocation() !== sdk.game.locations.CharSelectNoChars && ControlAction.getCharacters().length === 0) { + && currLoc !== sdk.game.locations.CharSelectNoChars + && ControlAction.getCharacters().length === 0) { ControlAction.timeoutDelay("[R/D] Character not found ", 18e4); D2Bot.printToConsole("Avoid Creating New Character - Restart"); D2Bot.restart(); From 6f0d9acc2ad4c85c3cef7335a9f1e70943923e00 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Mon, 27 Feb 2023 16:15:21 -0500 Subject: [PATCH 050/263] Update TownOverrides.js - Sort stash before we actually stash items --- libs/SoloPlay/Functions/TownOverrides.js | 49 ++++++++++++++++-------- 1 file changed, 32 insertions(+), 17 deletions(-) diff --git a/libs/SoloPlay/Functions/TownOverrides.js b/libs/SoloPlay/Functions/TownOverrides.js index 8ffbd30a..21eb0259 100644 --- a/libs/SoloPlay/Functions/TownOverrides.js +++ b/libs/SoloPlay/Functions/TownOverrides.js @@ -324,21 +324,32 @@ Town.stash = function (stashGold = true) { let items = (Storage.Inventory.Compare(Config.Inventory) || []); - items.length > 0 && items.forEach(item => { - if (this.canStash(item)) { - const pickResult = Pickit.checkItem(item).result; - switch (true) { - case pickResult !== Pickit.Result.UNWANTED && pickResult !== Pickit.Result.TRASH: - case Town.systemsKeep(item): - case AutoEquip.wanted(item) && pickResult === Pickit.Result.UNWANTED: // wanted but can't use yet - case !item.sellable: // quest/essences/keys/ect - Storage.Stash.MoveTo(item) && Item.logger("Stashed", item); - break; - default: - break; + if (items.length > 0) { + Storage.Stash.SortItems(); + + items.forEach(item => { + if (this.canStash(item)) { + const pickResult = Pickit.checkItem(item).result; + switch (true) { + case pickResult !== Pickit.Result.UNWANTED && pickResult !== Pickit.Result.TRASH: + case Town.systemsKeep(item): + case AutoEquip.wanted(item) && pickResult === Pickit.Result.UNWANTED: // wanted but can't use yet + case !item.sellable: // quest/essences/keys/ect + if ([sdk.quest.item.PotofLife, sdk.quest.item.ScrollofResistance].includes(item.classid)) { + // don't stash item, use it + let refName = item.prettyPrint; + if (item.use()) { + console.log("Used " + refName); + return; + } + } + Storage.Stash.MoveTo(item) && Item.logger("Stashed", item); + + break; + } } - } - }); + }); + } // Stash gold if (stashGold) { @@ -364,6 +375,7 @@ Town.clearInventory = function () { // If we are at an npc already, open the window otherwise moving potions around fails if (getUIFlag(sdk.uiflags.NPCMenu) && !getUIFlag(sdk.uiflags.Shop)) { try { + console.debug("Open npc menu"); !!getInteractedNPC() && Misc.useMenu(sdk.menu.Trade); } catch (e) { console.error(e); @@ -505,6 +517,9 @@ Town.clearJunk = function () { .filter(i => i.isInStorage && !Town.ignoreType(i.itemType) && i.sellable && !Town.systemsKeep(i)); if (!junkItems.length) return false; + console.log("ÿc8Start ÿc0:: ÿc8clearJunk"); + let clearJunkTick = getTickCount(); + /** * @type {ItemUnit[][]} */ @@ -551,7 +566,7 @@ Town.clearJunk = function () { continue; } - if (junk.isBaseType && pickitResult === Pickit.Result.SOLOWANTS) { + if (junk.isBaseType && [Pickit.Result.CUBING, Pickit.Result.SOLOWANTS].includes(pickitResult)) { if (!Item.betterThanStashed(junk)) { console.log("ÿc9BetterThanStashedCheckÿc0 :: Base: " + junk.prettyPrint + " Junk type: " + junk.itemType + " Pickit Result: " + pickitResult); getToItem("BetterThanStashedCheck", junk) && totalJunk.push(junk); @@ -610,6 +625,8 @@ Town.clearJunk = function () { } } + console.log("ÿc8Exit clearJunk ÿc0- ÿc7Duration: ÿc0" + Time.format(getTickCount() - clearJunkTick)); + return true; }; @@ -676,7 +693,6 @@ Town.doChores = function (repair = false, givenTasks = {}) { me.needRepair() && NPCAction.repair() && me.cancelUIFlags(); me.sortInventory(); - extraTasks.fullChores && this.sortStash(); Quest.characterRespec(); me.act !== preAct && this.goToTown(preAct); @@ -692,7 +708,6 @@ Town.doChores = function (repair = false, givenTasks = {}) { delay(300); console.debug("doChores Ending Gold :: " + me.gold); console.info(false, null, "doChores"); - // Town.lastInteractedNPC.reset(); // unassign return true; }; From 83d3413dc3320c82c1b3870edc60561326c9081e Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Tue, 28 Feb 2023 16:00:48 -0500 Subject: [PATCH 051/263] Linting mostly --- libs/SoloPlay/Functions/AttackOverrides.js | 74 ++++++++++++----- .../ClassAttackOverrides/AmazonAttacks-WIP.js | 6 +- .../ClassAttackOverrides/AmazonAttacks.js | 4 +- .../ClassAttackOverrides/AssassinAttacks.js | 2 +- .../ClassAttackOverrides/BarbarianAttacks.js | 2 +- .../NecromancerAttacks.js | 2 +- .../ClassAttackOverrides/PaladinAttacks.js | 6 +- libs/SoloPlay/Functions/ItemPrototypes.js | 4 +- libs/SoloPlay/Functions/LoaderOverrides.js | 4 +- libs/SoloPlay/Functions/Me.js | 3 +- libs/SoloPlay/Functions/PatherOverrides.js | 4 +- libs/SoloPlay/Functions/PickitOverrides.js | 18 ++++- libs/SoloPlay/Functions/Polyfills.js | 2 +- libs/SoloPlay/Functions/PrecastOverrides.js | 4 +- libs/SoloPlay/Functions/PrototypeOverrides.js | 6 +- libs/SoloPlay/Functions/SoloEvents.js | 6 +- libs/SoloPlay/Functions/StorageOverrides.js | 14 ++-- libs/SoloPlay/Scripts/andariel.js | 10 +-- libs/SoloPlay/Scripts/baal.js | 8 +- libs/SoloPlay/Scripts/cave.js | 80 +++++++++---------- libs/SoloPlay/Scripts/corpsefire.js | 2 +- libs/SoloPlay/Scripts/diablo.js | 6 +- libs/SoloPlay/Scripts/duriel.js | 2 +- libs/SoloPlay/Scripts/eye.js | 2 +- libs/SoloPlay/Scripts/hellforge.js | 4 +- libs/SoloPlay/Scripts/hephasto.js | 2 +- libs/SoloPlay/Scripts/izual.js | 2 +- libs/SoloPlay/Scripts/jail.js | 4 +- libs/SoloPlay/Scripts/mephisto.js | 2 +- libs/SoloPlay/Scripts/orgtorch.js | 8 +- libs/SoloPlay/Scripts/river.js | 2 +- libs/SoloPlay/Scripts/tristram.js | 17 ++-- libs/SoloPlay/Scripts/worldstone.js | 2 +- libs/SoloPlay/Threads/AutoBuildThread.js | 4 +- libs/SoloPlay/Threads/EventThread.js | 8 +- libs/SoloPlay/Threads/ToolsThread.js | 8 +- libs/SoloPlay/Threads/TownChicken.js | 4 +- libs/SoloPlay/Tools/CharData.js | 2 +- libs/SoloPlay/Tools/SoloIndex.js | 20 ++--- libs/SoloPlay/Utils/General.js | 4 +- libs/SoloPlay/index.d.ts | 21 +++++ 41 files changed, 230 insertions(+), 155 deletions(-) diff --git a/libs/SoloPlay/Functions/AttackOverrides.js b/libs/SoloPlay/Functions/AttackOverrides.js index a1c9450f..b6cd1860 100644 --- a/libs/SoloPlay/Functions/AttackOverrides.js +++ b/libs/SoloPlay/Functions/AttackOverrides.js @@ -92,7 +92,14 @@ Attack.getLowerResistPercent = function () { return 0; }; -Attack.checkResist = function (unit = undefined, val = -1, maxres = 100) { +/** + * Check if monster is immmune to a damagetype + * @param {Monster} unit + * @param {number} val + * @param {number} maxres + * @returns {boolean} true if they are not immune + */ +Attack.checkResist = function (unit, val = -1, maxres = 100) { if (!unit || !unit.type || unit.type === sdk.unittype.Player) return true; const damageType = typeof val === "number" ? this.getSkillElement(val) : val; @@ -144,8 +151,12 @@ Attack.checkResist = function (unit = undefined, val = -1, maxres = 100) { return this.getResist(unit, damageType) < maxres; }; -// Maybe make this a prototype and use game data to also check if should attack not just can based on effort? -Attack.canAttack = function (unit = undefined) { +/** + * @param {Monster} unit + * @returns {boolean} If we have a valid skill to use on this monster + * @todo Maybe make this a prototype and use game data to also check if should attack not just can based on effort? + */ +Attack.canAttack = function (unit) { if (!unit) return false; if (unit.isMonster) { // Unique/Champion @@ -167,8 +178,15 @@ Attack.canAttack = function (unit = undefined) { return false; }; -Attack.openChests = function (range = 10, x = undefined, y = undefined) { +/** + * @param {number} range + * @param {number} x + * @param {number} y + * @returns {boolean} + */ +Attack.openChests = function (range, x, y) { if (!Config.OpenChests.Enabled || !Misc.openChestsEnabled) return false; + range === undefined && (range = 10); x === undefined && (x = me.x); y === undefined && (y = me.y); @@ -196,8 +214,11 @@ Attack.openChests = function (range = 10, x = undefined, y = undefined) { return true; }; -// this might be depreciated now -Attack.killTarget = function (name = undefined) { +/** + * @param {Monster | string | number} name + * @returns {boolean} + */ +Attack.killTarget = function (name) { if (!name || Config.AttackSkill[1] < 0) return false; typeof name === "string" && (name = name.toLowerCase()); let target = (typeof name === "object" ? name : Misc.poll(() => Game.getMonster(name), 2000, 100)); @@ -235,7 +256,7 @@ Attack.killTarget = function (name = undefined) { const gid = target.gid; let errorInfo = ""; let [retry, attackCount] = [0, 0]; - let lastLoc = {x: me.x, y: me.y}; + let lastLoc = { x: me.x, y: me.y }; let tick = getTickCount(); try { @@ -280,7 +301,7 @@ Attack.killTarget = function (name = undefined) { retry = 0; } - lastLoc = {x: me.x, y: me.y}; + lastLoc = { x: me.x, y: me.y }; attackCount++; if (target.dead || Config.FastPick || (attackCount > 0 && attackCount % 5 === 0)) { @@ -316,6 +337,14 @@ Attack.clearLocations = function (list = []) { return true; }; +/** + * Clear around a certain node + * @param {number} x + * @param {number} y + * @param {number} range + * @param {boolean} pickit + * @returns {boolean} + */ Attack.clearPos = function (x, y, range = 15, pickit = true) { while (!me.gameReady) { delay(40); @@ -374,7 +403,7 @@ Attack.clearPos = function (x, y, range = 15, pickit = true) { } } - (i === gidAttack.length) && gidAttack.push({gid: target.gid, attacks: 0, name: target.name}); + (i === gidAttack.length) && gidAttack.push({ gid: target.gid, attacks: 0, name: target.name }); gidAttack[i].attacks += 1; attackCount += 1; let isSpecial = target.isSpecial; @@ -617,10 +646,9 @@ Attack.clear = function (range = 25, spectype = 0, bossId = false, sortfunc = un return Attack.clear(10); } - ({orgx, orgy} = {orgx: boss.x, orgy: boss.y}); - Config.MFLeader && !!bossId && Pather.makePortal() && say("clear " + bossId); + ({ orgx, orgy } = { orgx: boss.x, orgy: boss.y }); } else { - ({orgx, orgy} = {orgx: me.x, orgy: me.y}); + ({ orgx, orgy } = { orgx: me.x, orgy: me.y }); } let monsterList = []; @@ -646,7 +674,7 @@ Attack.clear = function (range = 25, spectype = 0, bossId = false, sortfunc = un while (start && monsterList.length > 0 && attackCount < 300) { if (me.dead || Attack.stopClear) return false; - boss && (({orgx, orgy} = {orgx: boss.x, orgy: boss.y})); + boss && (({ orgx, orgy } = { orgx: boss.x, orgy: boss.y })); monsterList.sort(sortfunc); target = copyUnit(monsterList[0]); @@ -920,7 +948,7 @@ Attack.castCharges = function (skillId = undefined, unit = undefined) { return true; }; -Attack.switchCastCharges = function (skillId = undefined, unit = undefined) { +Attack.switchCastCharges = function (skillId, unit) { if (!skillId || !unit || !Skill.wereFormCheck(skillId) || (me.inTown && !Skill.townSkill(skillId))) { return false; } @@ -931,14 +959,22 @@ Attack.switchCastCharges = function (skillId = undefined, unit = undefined) { return true; }; -Attack.dollAvoid = function (unit = undefined) { +/** + * Position ourselves further from a doll to attack + * @param {Monster} unit + * @returns {boolean} + */ +Attack.dollAvoid = function (unit) { if (!unit) return false; let distance = 14; for (let i = 0; i < 2 * Math.PI; i += Math.PI / 6) { let cx = Math.round(Math.cos(i) * distance); let cy = Math.round(Math.sin(i) * distance); - if (Attack.validSpot(unit.x + cx, unit.y + cy)) return Pather.moveTo(unit.x + cx, unit.y + cy); + if (Attack.validSpot(unit.x + cx, unit.y + cy)) { + // don't clear while trying to reposition + return Pather.moveToEx(unit.x + cx, unit.y + cy, { clearSettings: { allowClearing: false } }); + } } return false; @@ -1109,7 +1145,7 @@ Attack.pwnDia = function () { return Game.getMonster(sdk.monsters.Diablo); }; { - let nearSpot = Pather.spotOnDistance({ x: 7792, y: 5292 }, 35, {returnSpotOnError: false}); + let nearSpot = Pather.spotOnDistance({ x: 7792, y: 5292 }, 35, { returnSpotOnError: false }); Pather.moveToUnit(nearSpot); } @@ -1286,7 +1322,7 @@ Attack.deploy = function (unit, distance = 10, spread = 5, range = 9) { }); for (let i = 0; i < grid.length; i += 1) { - if (!(CollMap.getColl(grid[i].x, grid[i].y, true) & sdk.collision.BlockWall) && !CollMap.checkColl(unit, {x: grid[i].x, y: grid[i].y}, sdk.collision.Ranged)) { + if (!(CollMap.getColl(grid[i].x, grid[i].y, true) & sdk.collision.BlockWall) && !CollMap.checkColl(unit, { x: grid[i].x, y: grid[i].y }, sdk.collision.Ranged)) { currCount = this.getMonsterCount(grid[i].x, grid[i].y, range, monList); if (currCount < count) { @@ -1367,7 +1403,7 @@ Attack.getIntoPosition = function (unit = false, distance = 0, coll = 0, walk = if (!force) { for (let i = 0; i < coords.length; i += 1) { if ((getDistance(me, coords[i].x, coords[i].y) < 1 - && !CollMap.checkColl(unit, {x: coords[i].x, y: coords[i].y}, sdk.collision.WallOrRanged | sdk.collision.Objects | sdk.collision.IsOnFloor, 1)) + && !CollMap.checkColl(unit, { x: coords[i].x, y: coords[i].y }, sdk.collision.WallOrRanged | sdk.collision.Objects | sdk.collision.IsOnFloor, 1)) || (getDistance(me, coords[i].x, coords[i].y) <= 5 && me.getMobCount(6) > 2)) { return true; } diff --git a/libs/SoloPlay/Functions/ClassAttackOverrides/AmazonAttacks-WIP.js b/libs/SoloPlay/Functions/ClassAttackOverrides/AmazonAttacks-WIP.js index 018df3bf..d63d9acb 100644 --- a/libs/SoloPlay/Functions/ClassAttackOverrides/AmazonAttacks-WIP.js +++ b/libs/SoloPlay/Functions/ClassAttackOverrides/AmazonAttacks-WIP.js @@ -23,7 +23,7 @@ let inDanger = function (unit) { }; ClassAttack.decideSkill = function (unit) { - let skills = {timed: -1, untimed: -1}; + let skills = { timed: -1, untimed: -1 }; if (!unit) return skills; let index = (unit.isSpecial || unit.isPlayer) ? 1 : 3; @@ -192,7 +192,7 @@ ClassAttack.doAttack = function (unit) { // specials and dolls for now, should make dolls much less dangerous with the reduction of their damage if (Precast.haveCTA > -1 && unit.curseable && (index === 1 || unit.isDoll) && unit.distance < 5 && !unit.getState(sdk.states.BattleCry) && unit.curseable) { - Skill.switchCast(sdk.skills.BattleCry, {oSkill: true}); + Skill.switchCast(sdk.skills.BattleCry, { oSkill: true }); } if (data.decoy.use()) { @@ -317,7 +317,7 @@ ClassAttack.doAttack = function (unit) { !!spot && Pather.walkTo(spot.x, spot.y); } - let closeMob = Attack.getNearestMonster({skipGid: gid}); + let closeMob = Attack.getNearestMonster({ skipGid: gid }); if (!!closeMob) { let findSkill = this.decideSkill(closeMob); diff --git a/libs/SoloPlay/Functions/ClassAttackOverrides/AmazonAttacks.js b/libs/SoloPlay/Functions/ClassAttackOverrides/AmazonAttacks.js index 0c2b90cd..952151e5 100644 --- a/libs/SoloPlay/Functions/ClassAttackOverrides/AmazonAttacks.js +++ b/libs/SoloPlay/Functions/ClassAttackOverrides/AmazonAttacks.js @@ -123,7 +123,7 @@ ClassAttack.doAttack = function (unit, preattack, once) { // specials and dolls for now, should make dolls much less dangerous with the reduction of their damage if (Precast.haveCTA > -1 && unit.curseable && (index === 1 || unit.isDoll) && unit.distance < 5 && !unit.getState(sdk.states.BattleCry) && unit.curseable) { - Skill.switchCast(sdk.skills.BattleCry, {oSkill: true}); + Skill.switchCast(sdk.skills.BattleCry, { oSkill: true }); } if (useSkills.Decoy) { @@ -314,7 +314,7 @@ ClassAttack.doAttack = function (unit, preattack, once) { !!spot && Pather.walkTo(spot.x, spot.y); } - let closeMob = Attack.getNearestMonster({skipGid: gid}); + let closeMob = Attack.getNearestMonster({ skipGid: gid }); if (!!closeMob) { let findSkill = this.decideSkill(closeMob); diff --git a/libs/SoloPlay/Functions/ClassAttackOverrides/AssassinAttacks.js b/libs/SoloPlay/Functions/ClassAttackOverrides/AssassinAttacks.js index 82e443d0..c8a7db52 100644 --- a/libs/SoloPlay/Functions/ClassAttackOverrides/AssassinAttacks.js +++ b/libs/SoloPlay/Functions/ClassAttackOverrides/AssassinAttacks.js @@ -242,7 +242,7 @@ ClassAttack.doAttack = function (unit, preattack) { !!spot && Pather.walkTo(spot.x, spot.y); } - let closeMob = Attack.getNearestMonster({skipGid: gid}); + let closeMob = Attack.getNearestMonster({ skipGid: gid }); !!closeMob && this.doCast(closeMob, skills.timed, skills.untimed); } diff --git a/libs/SoloPlay/Functions/ClassAttackOverrides/BarbarianAttacks.js b/libs/SoloPlay/Functions/ClassAttackOverrides/BarbarianAttacks.js index 80449126..5fc2904f 100644 --- a/libs/SoloPlay/Functions/ClassAttackOverrides/BarbarianAttacks.js +++ b/libs/SoloPlay/Functions/ClassAttackOverrides/BarbarianAttacks.js @@ -222,7 +222,7 @@ includeIfNotIncluded("core/Attacks/Barbarian.js"); // TODO: write GameData.killableSummonsByWarCry if (data.warCry.have && data.warCry.mana < me.mp && !me.skillDelay && warCryCheck()) { - data.switchCast ? Skill.switchCast(sdk.skills.WarCry, {hand: 0}) : Skill.cast(sdk.skills.WarCry, sdk.skills.hand.Right, unit); + data.switchCast ? Skill.switchCast(sdk.skills.WarCry, { hand: 0 }) : Skill.cast(sdk.skills.WarCry, sdk.skills.hand.Right, unit); this.warCryTick = getTickCount(); } diff --git a/libs/SoloPlay/Functions/ClassAttackOverrides/NecromancerAttacks.js b/libs/SoloPlay/Functions/ClassAttackOverrides/NecromancerAttacks.js index 13c55508..5509823a 100644 --- a/libs/SoloPlay/Functions/ClassAttackOverrides/NecromancerAttacks.js +++ b/libs/SoloPlay/Functions/ClassAttackOverrides/NecromancerAttacks.js @@ -284,7 +284,7 @@ ClassAttack.doAttack = function (unit, preattack, once) { Config.ActiveSummon && this.raiseArmy(); this.explodeCorpses(unit); this.smartCurse(unit); - let closeMob = Attack.getNearestMonster({skipGid: gid}); + let closeMob = Attack.getNearestMonster({ skipGid: gid }); if (!!closeMob) { let findSkill = Attack.decideSkill(closeMob); (this.doCast(closeMob, findSkill.timed, findSkill.untimed) === Attack.Result.SUCCESS) diff --git a/libs/SoloPlay/Functions/ClassAttackOverrides/PaladinAttacks.js b/libs/SoloPlay/Functions/ClassAttackOverrides/PaladinAttacks.js index e198e61e..130dadbc 100644 --- a/libs/SoloPlay/Functions/ClassAttackOverrides/PaladinAttacks.js +++ b/libs/SoloPlay/Functions/ClassAttackOverrides/PaladinAttacks.js @@ -67,7 +67,7 @@ ClassAttack.doAttack = function (unit = undefined, preattack = false, once = fal // specials and dolls for now, should make dolls much less dangerous with the reduction of their damage if (Precast.haveCTA > -1 && !unit.dead && (index === 1 || unit.isDoll) && unit.distance < 5 && !unit.getState(sdk.states.BattleCry) && unit.curseable) { - Skill.switchCast(sdk.skills.BattleCry, {oSkill: true}); + Skill.switchCast(sdk.skills.BattleCry, { oSkill: true }); } if (Attack.getCustomAttack(unit)) { @@ -179,7 +179,7 @@ ClassAttack.doAttack = function (unit = undefined, preattack = false, once = fal !!spot && Pather.walkTo(spot.x, spot.y); } - let closeMob = Attack.getNearestMonster({skipGid: gid}); + let closeMob = Attack.getNearestMonster({ skipGid: gid }); !!closeMob && this.doCast(closeMob, attackSkill, aura); } @@ -391,7 +391,7 @@ ClassAttack.afterAttack = function () { Precast.doPrecast(false); if (Skill.canUse(sdk.skills.Cleansing) && me.hpPercent < 85 && me.getState(sdk.states.Poison) - && !me.checkForMobs({range: 12, coll: Coords_1.BlockBits.BlockWall}) && Skill.setSkill(sdk.skills.Cleansing, sdk.skills.hand.Right)) { + && !me.checkForMobs({ range: 12, coll: Coords_1.BlockBits.BlockWall }) && Skill.setSkill(sdk.skills.Cleansing, sdk.skills.hand.Right)) { me.overhead("Delaying for a second to get rid of Poison"); Misc.poll(() => (!me.getState(sdk.states.Poison) || me.mode === sdk.player.mode.GettingHit), 1500, 50); } diff --git a/libs/SoloPlay/Functions/ItemPrototypes.js b/libs/SoloPlay/Functions/ItemPrototypes.js index c850d2df..38bbd3f0 100644 --- a/libs/SoloPlay/Functions/ItemPrototypes.js +++ b/libs/SoloPlay/Functions/ItemPrototypes.js @@ -155,7 +155,7 @@ Unit.prototype.autoEquipCheck = function (checkCanEquip = true) { if (tier > 0 && bodyLoc.length) { for (let i = 0; i < bodyLoc.length; i += 1) { if (tier > Item.getEquipped(bodyLoc[i]).tier - && ((checkCanEquip ? this.canEquip() : true) || !this.identified)) { + && ((checkCanEquip ? this.canEquip() : true) || !this.identified)) { if (this.twoHanded && !me.barbarian) { if (tier < Item.getEquipped(sdk.body.RightArm).tier + Item.getEquipped(sdk.body.LeftArm).tier) return false; } @@ -182,7 +182,7 @@ Unit.prototype.autoEquipCheckSecondary = function (checkCanEquip = true) { if (tier > 0 && bodyLoc.length) { for (let i = 0; i < bodyLoc.length; i += 1) { if (tier > Item.getEquipped(bodyLoc[i]).secondarytier - && ((checkCanEquip ? this.canEquip() : true) || !this.identified)) { + && ((checkCanEquip ? this.canEquip() : true) || !this.identified)) { if (this.twoHanded && !me.barbarian) { if (tier < Item.getEquipped(sdk.body.RightArmSecondary).secondarytier + Item.getEquipped(sdk.body.LeftArmSecondary).secondarytier) return false; } diff --git a/libs/SoloPlay/Functions/LoaderOverrides.js b/libs/SoloPlay/Functions/LoaderOverrides.js index 1047131c..300ba5f0 100644 --- a/libs/SoloPlay/Functions/LoaderOverrides.js +++ b/libs/SoloPlay/Functions/LoaderOverrides.js @@ -57,7 +57,7 @@ Loader.run = function () { tick = getTickCount(); currentExp = me.getStat(sdk.stats.Experience); - Messaging.sendToScript("libs/SoloPlay/Threads/ToolsThread.js", JSON.stringify({currScript: scriptName})); + Messaging.sendToScript("libs/SoloPlay/Threads/ToolsThread.js", JSON.stringify({ currScript: scriptName })); DataFile.updateStats("lastScript", scriptName); for (j = 0; j < 5; j += 1) { @@ -140,7 +140,7 @@ Loader.runScript = function (script, configOverride) { let mainScriptStr = (mainScript !== script ? buildScriptMsg() : ""); this.tempList.push(script); console.log(mainScriptStr + "ÿc2Starting script: ÿc9" + script); - Messaging.sendToScript("libs/SoloPlay/Threads/ToolsThread.js", JSON.stringify({currScript: script})); + Messaging.sendToScript("libs/SoloPlay/Threads/ToolsThread.js", JSON.stringify({ currScript: script })); DataFile.updateStats("lastScript", script); if (typeof configOverride === "function") { diff --git a/libs/SoloPlay/Functions/Me.js b/libs/SoloPlay/Functions/Me.js index 907cbbcf..56f64899 100644 --- a/libs/SoloPlay/Functions/Me.js +++ b/libs/SoloPlay/Functions/Me.js @@ -184,7 +184,6 @@ me.inDanger = function (checkLoc, range) { }; /** - * * @param {number} skillId * @param {number} subId * @returns boolean @@ -195,8 +194,8 @@ me.checkSkill = (skillId = 0, subId = 0) => !!me.getSkill(skillId, subId); me.cleanUpInvoPotions = function (beltSize) { beltSize === undefined && (beltSize = Storage.BeltSize()); const beltMax = (beltSize * 4); - // belt 4x4 locations /** + * belt 4x4 locations * 12 13 14 15 * 8 9 10 11 * 4 5 6 7 diff --git a/libs/SoloPlay/Functions/PatherOverrides.js b/libs/SoloPlay/Functions/PatherOverrides.js index 73fc9e11..d13f898b 100644 --- a/libs/SoloPlay/Functions/PatherOverrides.js +++ b/libs/SoloPlay/Functions/PatherOverrides.js @@ -162,7 +162,7 @@ Pather.forceRun = false; return mon.attackable && getDistance(x, y, mon.x, mon.y) < settings.range && (!settings.type || (settings.type & mon.spectype)) && (settings.ignoreClassids.indexOf(mon.classid) === -1) - && !CollMap.checkColl({x: x, y: y}, mon, settings.coll, 1); + && !CollMap.checkColl({ x: x, y: y }, mon, settings.coll, 1); }).length; } }); @@ -549,7 +549,7 @@ Pather.move = function (target, givenSettings = {}) { // if we are allowed to clear if (settings.allowClearing) { // Don't go berserk on longer paths - also check that there are even mobs blocking us - if (cleared.at === 0 || getTickCount() - cleared.at > Time.seconds(3) && cleared.where.distance > 5 && me.checkForMobs({range: 10})) { + if (cleared.at === 0 || getTickCount() - cleared.at > Time.seconds(3) && cleared.where.distance > 5 && me.checkForMobs({ range: 10 })) { // only set that we cleared if we actually killed at least 1 mob if (Attack.clear(10, null, null, null, settings.allowPicking)) { console.debug("Cleared Node"); diff --git a/libs/SoloPlay/Functions/PickitOverrides.js b/libs/SoloPlay/Functions/PickitOverrides.js index a09636e2..ba829bb9 100644 --- a/libs/SoloPlay/Functions/PickitOverrides.js +++ b/libs/SoloPlay/Functions/PickitOverrides.js @@ -167,6 +167,10 @@ Pickit.amountOfPotsNeeded = function () { return needed; }; +/** + * @param {ItemUnit} item + * @returns {boolean} + */ Pickit.canFit = function (item) { switch (item.itemType) { case sdk.items.type.Gold: @@ -192,6 +196,10 @@ Pickit.canFit = function (item) { } }; +/** + * @param {ItemUnit} unit + * @returns {boolean} + */ Pickit.canPick = function (unit) { if (!unit) return false; if (sdk.quest.items.includes(unit.classid) && me.getItem(unit.classid)) return false; @@ -329,7 +337,7 @@ Pickit.canPick = function (unit) { } } - return (needPots > 0); + return (needPots > 0 || (me.charlvl < 10 && Storage.Inventory.CanFit(unit))); case undefined: // Yes, it does happen console.warn("undefined item (!?)"); @@ -419,11 +427,13 @@ Pickit.pickItem = function (unit, status, keptLine, clearBeforePick = true) { let checkItem = false; const maxDist = (Config.FastPick || i < 1) ? 8 : 5; if (item.distance > maxDist || checkCollision(me, item, sdk.collision.BlockWall)) { - if (!clearBeforePick && me.checkForMobs({range: 5, coll: (sdk.collision.BlockWall | sdk.collision.Objects | sdk.collision.ClosedDoor)})) { + let coll = (sdk.collision.BlockWall | sdk.collision.Objects | sdk.collision.ClosedDoor); + + if (!clearBeforePick && me.checkForMobs({ range: 5, coll: coll })) { continue; } - if (clearBeforePick && item.checkForMobs({range: 8, coll: (sdk.collision.BlockWall | sdk.collision.Objects | sdk.collision.ClosedDoor)})) { + if (clearBeforePick && item.checkForMobs({ range: 8, coll: coll })) { try { console.log("ÿc8PickItemÿc0 :: Clearing area around item I want to pick"); Pickit.enabled = false; // Don't pick while trying to clear @@ -693,7 +703,7 @@ Pickit.pickItems = function (range = Config.PickRange, once = false) { } if (Pickit.pickList.some(i => [sdk.items.type.HealingPotion, sdk.items.type.ManaPotion, sdk.items.type.RejuvPotion].includes(i.itemType))) { - Town.clearBelt(); + me.clearBelt(); Pickit.beltSize = Storage.BeltSize(); } diff --git a/libs/SoloPlay/Functions/Polyfills.js b/libs/SoloPlay/Functions/Polyfills.js index 94cdb54f..db4dd1a6 100644 --- a/libs/SoloPlay/Functions/Polyfills.js +++ b/libs/SoloPlay/Functions/Polyfills.js @@ -212,5 +212,5 @@ if (!Array.prototype.equals) { return true; }; // Hide method from for-in loops - Object.defineProperty(Array.prototype, "equals", {enumerable: false}); + Object.defineProperty(Array.prototype, "equals", { enumerable: false }); } diff --git a/libs/SoloPlay/Functions/PrecastOverrides.js b/libs/SoloPlay/Functions/PrecastOverrides.js index d816facb..38b0df13 100644 --- a/libs/SoloPlay/Functions/PrecastOverrides.js +++ b/libs/SoloPlay/Functions/PrecastOverrides.js @@ -40,7 +40,7 @@ new Overrides.Override(Precast, Precast.doPrecast, function (orignal, force) { if (needShout || needBo || needBc) { let primary = Attack.getPrimarySlot(); - let {x, y} = me; + let { x, y } = me; (needBo || needBc) && me.switchWeapons(this.getBetterSlot(sdk.skills.BattleOrders)); needBc && Precast.cast(sdk.skills.BattleCommand, x, y, true); @@ -75,7 +75,7 @@ Precast.summon = function (skillId, minionType) { while (me.getMinionCount(minionType) < count) { rv = true; let coord = CollMap.getRandCoordinate(me.x, -3, 3, me.y, -3, 3); // Get a random coordinate to summon using - let unit = Attack.getNearestMonster({skipImmune: false}); + let unit = Attack.getNearestMonster({ skipImmune: false }); if (unit && [sdk.summons.type.Golem, sdk.summons.type.Grizzly, sdk.summons.type.Shadow].includes(minionType) && unit.distance < 20 && !checkCollision(me, unit, sdk.collision.Ranged)) { try { diff --git a/libs/SoloPlay/Functions/PrototypeOverrides.js b/libs/SoloPlay/Functions/PrototypeOverrides.js index 4f266a36..899b4a1e 100644 --- a/libs/SoloPlay/Functions/PrototypeOverrides.js +++ b/libs/SoloPlay/Functions/PrototypeOverrides.js @@ -434,7 +434,6 @@ Unit.prototype.castChargedSkillEx = function (...args) { Unit.prototype.castSwitchChargedSkill = function (...args) { let skillId, x, y, unit, chargedItem; - let chargedItems = []; switch (args.length) { case 0: // item.castChargedSkill() @@ -475,7 +474,10 @@ Unit.prototype.castSwitchChargedSkill = function (...args) { if (this === me) { if (!skillId) throw Error("Must supply skillId on me.castChargedSkill"); - chargedItems = []; + /** + * @type {{ charge: number, level: number, item: ItemUnit }[]} + */ + let chargedItems = []; CharData.skillData.chargedSkillsOnSwitch.forEach(chargeSkill => { if (chargeSkill.skill === skillId) { diff --git a/libs/SoloPlay/Functions/SoloEvents.js b/libs/SoloPlay/Functions/SoloEvents.js index ad240c65..df2e6337 100644 --- a/libs/SoloPlay/Functions/SoloEvents.js +++ b/libs/SoloPlay/Functions/SoloEvents.js @@ -179,7 +179,7 @@ if (!charm || charm === undefined) return false; D2Bot.printToConsole("Kolbot-SoloPlay :: Dropping " + charm.name, sdk.colors.D2Bot.Orange); - let orginalLocation = {act: me.act, area: me.area, x: me.x, y: me.y}; + let orginalLocation = { act: me.act, area: me.area, x: me.x, y: me.y }; if (!me.inTown) { Town.goToTown(1); @@ -204,7 +204,7 @@ // end the current script but insert it to be continued after dclone is dead killdclone: function () { D2Bot.printToConsole("Kolbot-SoloPlay :: Trying to kill DClone.", sdk.colors.D2Bot.Orange); - let orginalLocation = {area: me.area, x: me.x, y: me.y}; + let orginalLocation = { area: me.area, x: me.x, y: me.y }; !me.inTown && Town.goToTown(); @@ -231,7 +231,7 @@ let oldAnni = me.findItem(sdk.items.SmallCharm, sdk.items.mode.inStorage, -1, sdk.items.quality.Unique); if (newAnni && oldAnni) { - this.sendToList({profile: me.profile, ladder: me.ladder}, 60); + this.sendToList({ profile: me.profile, ladder: me.ladder }, 60); let tick = getTickCount(); diff --git a/libs/SoloPlay/Functions/StorageOverrides.js b/libs/SoloPlay/Functions/StorageOverrides.js index 7f38a522..43cef8c6 100644 --- a/libs/SoloPlay/Functions/StorageOverrides.js +++ b/libs/SoloPlay/Functions/StorageOverrides.js @@ -129,7 +129,7 @@ includeIfNotIncluded("SoloPlay/Tools/Developer.js"); return true; } - let makeCubeSpot = this.MakeSpot(cube, {x: 0, y: 0}, true); // NOTE: passing these in buffer order [h/x][w/y] + let makeCubeSpot = this.MakeSpot(cube, { x: 0, y: 0 }, true); // NOTE: passing these in buffer order [h/x][w/y] if (makeCubeSpot) { // this item cannot be moved @@ -142,8 +142,8 @@ includeIfNotIncluded("SoloPlay/Tools/Developer.js"); }; /** - * @param {ItemUnit} item - */ + * @param {ItemUnit} item + */ Container.prototype.CanFit = function (item) { return (!!this.FindSpot(item)); }; @@ -285,14 +285,14 @@ includeIfNotIncluded("SoloPlay/Tools/Developer.js"); && !this.IsLocked(this.itemList[this.buffer[x][y] - 1], Config.Inventory) // don't try to make a spot by moving locked items! TODO: move this to the start of loop && (priorityClassIds.indexOf(bufferItemClass) === -1 || priorityClassIds.indexOf(item.classid) < priorityClassIds.indexOf(bufferItemClass))) { // item in this spot needs to move! - makeSpot = this.MakeSpot(item, {x: x, y: y}); // NOTE: passing these in buffer order [h/x][w/y] + makeSpot = this.MakeSpot(item, { x: x, y: y }); // NOTE: passing these in buffer order [h/x][w/y] if (item.classid !== bufferItemClass // higher priority item || (item.classid === bufferItemClass && item.quality > bufferItemQuality) // same class, higher quality item || (item.classid === bufferItemClass && item.quality === bufferItemQuality && item.gfx > bufferItemGfx) // same quality, higher graphic item || (Config.AutoEquip && item.classid === bufferItemClass && item.quality === bufferItemQuality && item.gfx === bufferItemGfx // same graphic, higher tier item && NTIP.GetTier(item) > NTIP.GetTier(this.itemList[this.buffer[x][y] - 1]))) { - makeSpot = this.MakeSpot(item, {x: x, y: y}); // NOTE: passing these in buffer order [h/x][w/y] + makeSpot = this.MakeSpot(item, { x: x, y: y }); // NOTE: passing these in buffer order [h/x][w/y] if (makeSpot) { // this item cannot be moved @@ -323,7 +323,7 @@ includeIfNotIncluded("SoloPlay/Tools/Developer.js"); } } - return ({x: x, y: y}); + return ({ x: x, y: y }); } } @@ -402,7 +402,7 @@ includeIfNotIncluded("SoloPlay/Tools/Developer.js"); } //D2Bot.printToConsole("MakeSpot success! " + item.name + " can now be placed at " + location.y + "," + location.x, sdk.colors.D2Bot.Gold); - return ({x: location.x, y: location.y}); + return ({ x: location.x, y: location.y }); }; /** diff --git a/libs/SoloPlay/Scripts/andariel.js b/libs/SoloPlay/Scripts/andariel.js index 6ebe6c8c..968c626b 100644 --- a/libs/SoloPlay/Scripts/andariel.js +++ b/libs/SoloPlay/Scripts/andariel.js @@ -23,8 +23,8 @@ function andariel () { Pather.moveToExit([sdk.areas.CatacombsLvl3, sdk.areas.CatacombsLvl4], true); if (me.poisonRes < 75) { - Town.doChores(true, {thawing: me.coldRes < 75, antidote: true}); - Pather.usePortal(sdk.areas.CatacombsLvl4, me.name); + Town.doChores(true, { thawing: me.coldRes < 75, antidote: true }); + Pather.usePortal(sdk.areas.CatacombsLvl4); } Precast.doPrecast(true); @@ -39,9 +39,9 @@ function andariel () { } let coords = [ - {x: 22572, y: 9635}, {x: 22554, y: 9618}, - {x: 22542, y: 9600}, {x: 22572, y: 9582}, - {x: 22554, y: 9566} + { x: 22572, y: 9635 }, { x: 22554, y: 9618 }, + { x: 22542, y: 9600 }, { x: 22572, y: 9582 }, + { x: 22554, y: 9566 } ]; if (Pather.useTeleport()) { diff --git a/libs/SoloPlay/Scripts/baal.js b/libs/SoloPlay/Scripts/baal.js index aac24940..2b217e0c 100644 --- a/libs/SoloPlay/Scripts/baal.js +++ b/libs/SoloPlay/Scripts/baal.js @@ -50,10 +50,10 @@ function baal () { break; case sdk.player.class.Assassin: if (Config.UseTraps) { - let check = ClassAttack.checkTraps({x: 15093, y: 5029}); + let check = ClassAttack.checkTraps({ x: 15093, y: 5029 }); if (check) { - ClassAttack.placeTraps({x: 15093, y: 5029}, 5); + ClassAttack.placeTraps({ x: 15093, y: 5029 }, 5); return true; } @@ -241,7 +241,7 @@ function baal () { if (dollQuit && Game.getMonster(sdk.monsters.SoulKiller)) { throw new ScriptError("Unsafe for hardcore, dolls found"); } - }}); + } }); Pather.moveTo(15113, 5040, 5); let totalTick = getTickCount(); @@ -259,7 +259,7 @@ function baal () { Common.Baal.clearThrone(); if (me.coldRes < 75 || me.poisonRes < 75) { - Town.doChores(null, {thawing: me.coldRes < 75, antidote: me.poisonRes < 75}); + Town.doChores(null, { thawing: me.coldRes < 75, antidote: me.poisonRes < 75 }); Town.move("portalspot"); Pather.usePortal(sdk.areas.ThroneofDestruction, me.name); } diff --git a/libs/SoloPlay/Scripts/cave.js b/libs/SoloPlay/Scripts/cave.js index d9d2869e..1c8e2162 100644 --- a/libs/SoloPlay/Scripts/cave.js +++ b/libs/SoloPlay/Scripts/cave.js @@ -11,46 +11,46 @@ function cave () { // coords from sonic const clearCoords = [ - {"x": 7549, "y": 12554, "radius": 10}, - {"x": 7560, "y": 12551, "radius": 10}, - {"x": 7573, "y": 12550, "radius": 10}, - {"x": 7576, "y": 12563, "radius": 10}, - {"x": 7586, "y": 12564, "radius": 10}, - {"x": 7596, "y": 12567, "radius": 10}, - {"x": 7596, "y": 12578, "radius": 10}, - {"x": 7606, "y": 12559, "radius": 10}, - {"x": 7612, "y": 12549, "radius": 10}, - {"x": 7611, "y": 12540, "radius": 10}, - {"x": 7608, "y": 12528, "radius": 10}, - {"x": 7595, "y": 12529, "radius": 10}, - {"x": 7588, "y": 12519, "radius": 10}, - {"x": 7574, "y": 12520, "radius": 10}, - {"x": 7564, "y": 12523, "radius": 10}, - {"x": 7568, "y": 12567, "radius": 10}, - {"x": 7565, "y": 12574, "radius": 10}, - {"x": 7560, "y": 12583, "radius": 10}, - {"x": 7554, "y": 12578, "radius": 10}, - {"x": 7546, "y": 12573, "radius": 10}, - {"x": 7537, "y": 12573, "radius": 10}, - {"x": 7528, "y": 12574, "radius": 10}, - {"x": 7519, "y": 12575, "radius": 10}, - {"x": 7510, "y": 12566, "radius": 10}, - {"x": 7510, "y": 12584, "radius": 10}, - {"x": 7514, "y": 12593, "radius": 10}, - {"x": 7521, "y": 12595, "radius": 10}, - {"x": 7526, "y": 12600, "radius": 10}, - {"x": 7525, "y": 12606, "radius": 10}, - {"x": 7535, "y": 12596, "radius": 10}, - {"x": 7543, "y": 12596, "radius": 10}, - {"x": 7550, "y": 12596, "radius": 10}, - {"x": 7557, "y": 12595, "radius": 10}, - {"x": 7556, "y": 12605, "radius": 10}, - {"x": 7556, "y": 12611, "radius": 10}, - {"x": 7566, "y": 12608, "radius": 10}, - {"x": 7580, "y": 12613, "radius": 10}, - {"x": 7589, "y": 12610, "radius": 10}, - {"x": 7594, "y": 12601, "radius": 10}, - {"x": 7600, "y": 12601, "radius": 10} + { "x": 7549, "y": 12554, "radius": 10 }, + { "x": 7560, "y": 12551, "radius": 10 }, + { "x": 7573, "y": 12550, "radius": 10 }, + { "x": 7576, "y": 12563, "radius": 10 }, + { "x": 7586, "y": 12564, "radius": 10 }, + { "x": 7596, "y": 12567, "radius": 10 }, + { "x": 7596, "y": 12578, "radius": 10 }, + { "x": 7606, "y": 12559, "radius": 10 }, + { "x": 7612, "y": 12549, "radius": 10 }, + { "x": 7611, "y": 12540, "radius": 10 }, + { "x": 7608, "y": 12528, "radius": 10 }, + { "x": 7595, "y": 12529, "radius": 10 }, + { "x": 7588, "y": 12519, "radius": 10 }, + { "x": 7574, "y": 12520, "radius": 10 }, + { "x": 7564, "y": 12523, "radius": 10 }, + { "x": 7568, "y": 12567, "radius": 10 }, + { "x": 7565, "y": 12574, "radius": 10 }, + { "x": 7560, "y": 12583, "radius": 10 }, + { "x": 7554, "y": 12578, "radius": 10 }, + { "x": 7546, "y": 12573, "radius": 10 }, + { "x": 7537, "y": 12573, "radius": 10 }, + { "x": 7528, "y": 12574, "radius": 10 }, + { "x": 7519, "y": 12575, "radius": 10 }, + { "x": 7510, "y": 12566, "radius": 10 }, + { "x": 7510, "y": 12584, "radius": 10 }, + { "x": 7514, "y": 12593, "radius": 10 }, + { "x": 7521, "y": 12595, "radius": 10 }, + { "x": 7526, "y": 12600, "radius": 10 }, + { "x": 7525, "y": 12606, "radius": 10 }, + { "x": 7535, "y": 12596, "radius": 10 }, + { "x": 7543, "y": 12596, "radius": 10 }, + { "x": 7550, "y": 12596, "radius": 10 }, + { "x": 7557, "y": 12595, "radius": 10 }, + { "x": 7556, "y": 12605, "radius": 10 }, + { "x": 7556, "y": 12611, "radius": 10 }, + { "x": 7566, "y": 12608, "radius": 10 }, + { "x": 7580, "y": 12613, "radius": 10 }, + { "x": 7589, "y": 12610, "radius": 10 }, + { "x": 7594, "y": 12601, "radius": 10 }, + { "x": 7600, "y": 12601, "radius": 10 } ]; !me.inArea(sdk.areas.CaveLvl2) && Pather.journeyTo(sdk.areas.CaveLvl2); diff --git a/libs/SoloPlay/Scripts/corpsefire.js b/libs/SoloPlay/Scripts/corpsefire.js index 92735cf8..850f6cc1 100644 --- a/libs/SoloPlay/Scripts/corpsefire.js +++ b/libs/SoloPlay/Scripts/corpsefire.js @@ -7,7 +7,7 @@ function corpsefire() { myPrint("starting corpsefire"); - Town.doChores(null, {thawing: me.coldRes < 75, antidote: me.poisonRes < 75}); + Town.doChores(null, { thawing: me.coldRes < 75, antidote: me.poisonRes < 75 }); Pather.checkWP(sdk.areas.ColdPlains, true) ? Pather.useWaypoint(sdk.areas.ColdPlains) : Pather.getWP(sdk.areas.ColdPlains); Precast.doPrecast(true); diff --git a/libs/SoloPlay/Scripts/diablo.js b/libs/SoloPlay/Scripts/diablo.js index b50962c3..55d9cbaa 100644 --- a/libs/SoloPlay/Scripts/diablo.js +++ b/libs/SoloPlay/Scripts/diablo.js @@ -53,8 +53,8 @@ function diablo () { break; case sdk.player.class.Assassin: if (Config.UseTraps) { - let trapCheck = ClassAttack.checkTraps({x: 7793, y: 5293}); - trapCheck && ClassAttack.placeTraps({x: 7793, y: 5293, classid: sdk.monsters.Diablo}, trapCheck); + let trapCheck = ClassAttack.checkTraps({ x: 7793, y: 5293 }); + trapCheck && ClassAttack.placeTraps({ x: 7793, y: 5293, classid: sdk.monsters.Diablo }, trapCheck); } Config.AttackSkill[1] === sdk.skills.ShockWeb && Skill.cast(Config.AttackSkill[1], sdk.skills.hand.Right, 7793, 5293); @@ -93,7 +93,7 @@ function diablo () { } if (me.coldRes < 75 || me.poisonRes < 75) { - Town.doChores(null, {thawing: me.coldRes < 75, antidote: me.poisonRes < 75}); + Town.doChores(null, { thawing: me.coldRes < 75, antidote: me.poisonRes < 75 }); Town.move("portalspot"); Pather.usePortal(sdk.areas.ChaosSanctuary, me.name); Misc.poll(() => { diff --git a/libs/SoloPlay/Scripts/duriel.js b/libs/SoloPlay/Scripts/duriel.js index 70466383..81697afd 100644 --- a/libs/SoloPlay/Scripts/duriel.js +++ b/libs/SoloPlay/Scripts/duriel.js @@ -22,7 +22,7 @@ function duriel () { // quest-prep let preArea = me.area; - Town.doChores(null, {thawing: me.coldRes < 75}); + Town.doChores(null, { thawing: me.coldRes < 75 }); let oldMercWatch = Config.MercWatch; Config.MercWatch = false; diff --git a/libs/SoloPlay/Scripts/eye.js b/libs/SoloPlay/Scripts/eye.js index 5cb6c092..978ee4e7 100644 --- a/libs/SoloPlay/Scripts/eye.js +++ b/libs/SoloPlay/Scripts/eye.js @@ -21,7 +21,7 @@ function eye () { } } - Town.doChores(null, {antidote: me.poisonRes < 75}); + Town.doChores(null, { antidote: me.poisonRes < 75 }); Pather.usePortal(sdk.areas.SpiderCavern, me.name); Pather.moveToPreset(me.area, sdk.unittype.Object, sdk.quest.chest.KhalimsEyeChest); Attack.clear(0x7); diff --git a/libs/SoloPlay/Scripts/hellforge.js b/libs/SoloPlay/Scripts/hellforge.js index c7493bbd..934dc196 100644 --- a/libs/SoloPlay/Scripts/hellforge.js +++ b/libs/SoloPlay/Scripts/hellforge.js @@ -11,7 +11,7 @@ function hellforge () { if (Misc.poll(() => Misc.checkQuest(sdk.quest.id.HellsForge, sdk.quest.states.Completed), 2000, 500)) return true; } myPrint("starting hellforge"); - Town.doChores(false, {thawing: me.coldRes < 75, antidote: me.poisonRes < 75, fullChores: true}); + Town.doChores(false, { thawing: me.coldRes < 75, antidote: me.poisonRes < 75, fullChores: true }); Pather.checkWP(sdk.areas.RiverofFlame, true) ? Pather.useWaypoint(sdk.areas.RiverofFlame) : Pather.getWP(sdk.areas.RiverofFlame); Precast.doPrecast(true); @@ -26,7 +26,7 @@ function hellforge () { if (!Pather.moveToPresetObject(me.area, sdk.quest.chest.HellForge, { callback: () => { let heph = Game.getMonster(getLocaleString(sdk.locale.monsters.HephastoTheArmorer)); return (heph && heph.distance < 30); - }})) { + } })) { console.warn("ÿc8Kolbot-SoloPlayÿc0: Failed to move to Hephasto"); } diff --git a/libs/SoloPlay/Scripts/hephasto.js b/libs/SoloPlay/Scripts/hephasto.js index 52599be6..f01e2865 100644 --- a/libs/SoloPlay/Scripts/hephasto.js +++ b/libs/SoloPlay/Scripts/hephasto.js @@ -7,7 +7,7 @@ function hephasto() { myPrint("starting hephasto"); - Town.doChores(null, {thawing: me.coldRes < 75, antidote: me.poisonRes < 75}); + Town.doChores(null, { thawing: me.coldRes < 75, antidote: me.poisonRes < 75 }); Pather.checkWP(sdk.areas.RiverofFlame, true) ? Pather.useWaypoint(sdk.areas.RiverofFlame) : Pather.getWP(sdk.areas.RiverofFlame); Precast.doPrecast(true); diff --git a/libs/SoloPlay/Scripts/izual.js b/libs/SoloPlay/Scripts/izual.js index 0ec09826..914a87ae 100644 --- a/libs/SoloPlay/Scripts/izual.js +++ b/libs/SoloPlay/Scripts/izual.js @@ -8,7 +8,7 @@ function izual () { myPrint("starting izual"); - Town.doChores(false, {thawing: true, antidote: true, stamina: true, fullChores: true}); + Town.doChores(false, { thawing: true, antidote: true, stamina: true, fullChores: true }); Pather.checkWP(sdk.areas.CityoftheDamned, true) ? Pather.useWaypoint(sdk.areas.CityoftheDamned) : Pather.getWP(sdk.areas.CityoftheDamned); Precast.doPrecast(true); diff --git a/libs/SoloPlay/Scripts/jail.js b/libs/SoloPlay/Scripts/jail.js index 0e2063ad..e9842856 100644 --- a/libs/SoloPlay/Scripts/jail.js +++ b/libs/SoloPlay/Scripts/jail.js @@ -16,7 +16,7 @@ function jail () { myPrint("clearing jail level " + i); Precast.doPrecast(true); - Attack.clearLevelEx({quitWhen: function () { + Attack.clearLevelEx({ quitWhen: function () { if (!me.hell) return false; // don't quit let dangerMob = Game.getMonster(); @@ -30,7 +30,7 @@ function jail () { } return false; - }}); + } }); Pather.clearToExit(me.area, levels[i], true); } diff --git a/libs/SoloPlay/Scripts/mephisto.js b/libs/SoloPlay/Scripts/mephisto.js index 8925ce6c..5ecb6b39 100644 --- a/libs/SoloPlay/Scripts/mephisto.js +++ b/libs/SoloPlay/Scripts/mephisto.js @@ -42,7 +42,7 @@ function mephisto () { // Town stuff if (me.coldRes < 75 || me.poisonRes < 75) { - Town.doChores(null, {thawing: me.coldRes < 75, antidote: me.poisonRes < 75}); + Town.doChores(null, { thawing: me.coldRes < 75, antidote: me.poisonRes < 75 }); // Re-enter portal Pather.usePortal(sdk.areas.DuranceofHateLvl3, me.name); Precast.doPrecast(true); diff --git a/libs/SoloPlay/Scripts/orgtorch.js b/libs/SoloPlay/Scripts/orgtorch.js index da97d182..3b718a37 100644 --- a/libs/SoloPlay/Scripts/orgtorch.js +++ b/libs/SoloPlay/Scripts/orgtorch.js @@ -29,7 +29,7 @@ function orgtorch() { for (let i = 0; i < 7; i++) { if (torch.getStat(sdk.stats.AddClassSkills, i)) { - SoloEvents.sendToList({profile: me.profile, ladder: me.ladder, torchType: i}); + SoloEvents.sendToList({ profile: me.profile, ladder: me.ladder, torchType: i }); return true; } @@ -90,9 +90,9 @@ function orgtorch() { // Get fade in River of Flames this.getFade = function () { if (!me.getState(sdk.states.Fade) - && (me.checkItem({name: sdk.locale.items.Treachery, equipped: true}).have - || me.checkItem({name: sdk.locale.items.LastWish, equipped: true}).have - || me.checkItem({name: sdk.locale.items.SpiritWard, equipped: true}).have)) { + && (me.checkItem({ name: sdk.locale.items.Treachery, equipped: true }).have + || me.checkItem({ name: sdk.locale.items.LastWish, equipped: true }).have + || me.checkItem({ name: sdk.locale.items.SpiritWard, equipped: true }).have)) { if (!me.getState(sdk.states.Fade)) { myPrint(sdk.colors.Orange + "OrgTorch :: " + sdk.colors.White + "Getting Fade"); Pather.useWaypoint(sdk.states.RiverofFlame); diff --git a/libs/SoloPlay/Scripts/river.js b/libs/SoloPlay/Scripts/river.js index c2194551..f3f528e6 100644 --- a/libs/SoloPlay/Scripts/river.js +++ b/libs/SoloPlay/Scripts/river.js @@ -8,7 +8,7 @@ function river() { myPrint("starting river"); - Town.doChores(null, {thawing: me.coldRes < 75, antidote: me.poisonRes < 75}); + Town.doChores(null, { thawing: me.coldRes < 75, antidote: me.poisonRes < 75 }); Pather.checkWP(sdk.areas.CityoftheDamned, true) ? Pather.useWaypoint(sdk.areas.CityoftheDamned) : Pather.getWP(sdk.areas.CityoftheDamned); Precast.doPrecast(true); diff --git a/libs/SoloPlay/Scripts/tristram.js b/libs/SoloPlay/Scripts/tristram.js index 75944003..42e931b0 100644 --- a/libs/SoloPlay/Scripts/tristram.js +++ b/libs/SoloPlay/Scripts/tristram.js @@ -57,6 +57,9 @@ function tristram () { if (!Misc.checkQuest(sdk.quest.id.TheSearchForCain, 4) && me.getItem(sdk.items.quest.KeytotheCairnStones)) { try { + /** + * @todo I know there is a way to read the correct stone order from the packet response, need to figure that out + */ include("core/Common/Cain.js"); const stoneIds = [ @@ -109,13 +112,13 @@ function tristram () { if (me.inArea(sdk.areas.Tristram)) { if (!me.tristram) { let clearCoords = [ - {"x": 25166, "y": 5108, "radius": 10}, - {"x": 25164, "y": 5115, "radius": 10}, - {"x": 25163, "y": 5121, "radius": 10}, - {"x": 25158, "y": 5126, "radius": 10}, - {"x": 25151, "y": 5125, "radius": 10}, - {"x": 25145, "y": 5129, "radius": 10}, - {"x": 25142, "y": 5135, "radius": 10} + { "x": 25166, "y": 5108, "radius": 10 }, + { "x": 25164, "y": 5115, "radius": 10 }, + { "x": 25163, "y": 5121, "radius": 10 }, + { "x": 25158, "y": 5126, "radius": 10 }, + { "x": 25151, "y": 5125, "radius": 10 }, + { "x": 25145, "y": 5129, "radius": 10 }, + { "x": 25142, "y": 5135, "radius": 10 } ]; Attack.clearCoordList(clearCoords); diff --git a/libs/SoloPlay/Scripts/worldstone.js b/libs/SoloPlay/Scripts/worldstone.js index 27ffef78..9e31ba23 100644 --- a/libs/SoloPlay/Scripts/worldstone.js +++ b/libs/SoloPlay/Scripts/worldstone.js @@ -8,7 +8,7 @@ function worldstone() { myPrint("starting worldstone"); - Town.doChores(null, {thawing: me.coldRes < 75, antidote: me.poisonRes < 75}); + Town.doChores(null, { thawing: me.coldRes < 75, antidote: me.poisonRes < 75 }); Pather.useWaypoint(sdk.areas.WorldstoneLvl2); Precast.doPrecast(true); diff --git a/libs/SoloPlay/Threads/AutoBuildThread.js b/libs/SoloPlay/Threads/AutoBuildThread.js index d44d7150..5efe2694 100644 --- a/libs/SoloPlay/Threads/AutoBuildThread.js +++ b/libs/SoloPlay/Threads/AutoBuildThread.js @@ -8,7 +8,7 @@ js_strict(true); include("critical.js"); // globals needed for core gameplay -includeCoreLibs({ exclude: ["Storage.js"]}); +includeCoreLibs({ exclude: ["Storage.js"] }); // needed for this thread include("core/Auto/AutoSkill.js"); include("core/Auto/AutoStat.js"); @@ -260,7 +260,7 @@ function main () { spendStatPoints(); Config.AutoSkill.Enabled && AutoSkill.init(Config.AutoSkill.Build, Config.AutoSkill.Save); Config.AutoStat.Enabled && AutoStat.init(Config.AutoStat.Build, Config.AutoStat.Save, Config.AutoStat.BlockChance, Config.AutoStat.UseBulk); - scriptBroadcast({event: "level up"}); + scriptBroadcast({ event: "level up" }); AutoBuild.applyConfigUpdates(); // scriptBroadcast() won't trigger listener on this thread. debug && AutoBuild.print("Incrementing cached character level to", prevLevel + 1); diff --git a/libs/SoloPlay/Threads/EventThread.js b/libs/SoloPlay/Threads/EventThread.js index 243f9f89..8a6c9d6d 100644 --- a/libs/SoloPlay/Threads/EventThread.js +++ b/libs/SoloPlay/Threads/EventThread.js @@ -8,7 +8,7 @@ js_strict(true); include("critical.js"); // globals needed for core gameplay -includeCoreLibs({ exclude: ["Storage.js"]}); +includeCoreLibs({ exclude: ["Storage.js"] }); // system libs includeSystemLibs(); @@ -170,7 +170,7 @@ function main () { if (profile !== me.profile && (me.hell || (me.nightmare && me.baal)) && me.ladder === ladder) { if (torchType === me.classid && !me.findItem(604, 0, null, 7)) { console.log("Sent Response"); - SoloEvents.sendToProfile(profile, {profile: me.profile, level: me.charlvl, event: 604}); + SoloEvents.sendToProfile(profile, { profile: me.profile, level: me.charlvl, event: 604 }); } } @@ -185,7 +185,7 @@ function main () { if (profile !== me.profile && (me.hell || (me.nightmare && me.baal)) && me.ladder === ladder) { if (!me.findItem(603, 0, null, 7)) { console.log("Sent Response"); - SoloEvents.sendToProfile(profile, {profile: me.profile, level: me.charlvl, event: 603}); + SoloEvents.sendToProfile(profile, { profile: me.profile, level: me.charlvl, event: 603 }); } } @@ -197,7 +197,7 @@ function main () { console.log("Sucess: profile that contacted me: " + profile + " level of char: " + level); SoloEvents.profileResponded = true; - profiles.push({profile: profile, level: level, event: event}); + profiles.push({ profile: profile, level: level, event: event }); tickDelay += 1000; } diff --git a/libs/SoloPlay/Threads/ToolsThread.js b/libs/SoloPlay/Threads/ToolsThread.js index 512e8950..daa53dc1 100644 --- a/libs/SoloPlay/Threads/ToolsThread.js +++ b/libs/SoloPlay/Threads/ToolsThread.js @@ -8,7 +8,7 @@ js_strict(true); include("critical.js"); // globals needed for core gameplay -includeCoreLibs({ exclude: ["Storage.js"]}); +includeCoreLibs({ exclude: ["Storage.js"] }); include("core/Common/Tools.js"); // system libs @@ -35,7 +35,7 @@ function main () { let debugInfo = { area: 0, currScript: "no entry" }; new Overrides.Override(Attack, Attack.getNearestMonster, function (orignal) { - let monster = orignal({skipBlocked: false, skipImmune: false}); + let monster = orignal({ skipBlocked: false, skipImmune: false }); return (monster ? " to " + monster.name : ""); }).apply(); @@ -478,6 +478,10 @@ function main () { if (msg && typeof msg === "string" && msg !== "") { let updated = false; switch (true) { + case msg === "deleteAndRemake" && Developer.testingMode.enabled: + quitFlag = true; + + break; case msg.substring(0, 8) === "config--": console.debug("update config"); Config = JSON.parse(msg.split("config--")[1]); diff --git a/libs/SoloPlay/Threads/TownChicken.js b/libs/SoloPlay/Threads/TownChicken.js index 356c0d49..81bd91f2 100644 --- a/libs/SoloPlay/Threads/TownChicken.js +++ b/libs/SoloPlay/Threads/TownChicken.js @@ -8,7 +8,7 @@ js_strict(true); include("critical.js"); // globals needed for core gameplay -includeCoreLibs({ exclude: ["Storage.js"]}); +includeCoreLibs({ exclude: ["Storage.js"] }); // system libs includeSystemLibs(); @@ -446,7 +446,7 @@ function main() { } if (useTerror && Skill.getManaCost(77) < me.mp) { - Skill.cast(77, sdk.skills.hand.Right, Attack.getNearestMonster({skipImmune: false})); + Skill.cast(77, sdk.skills.hand.Right, Attack.getNearestMonster({ skipImmune: false })); } } } diff --git a/libs/SoloPlay/Tools/CharData.js b/libs/SoloPlay/Tools/CharData.js index 48cd9ecc..6b28b35b 100644 --- a/libs/SoloPlay/Tools/CharData.js +++ b/libs/SoloPlay/Tools/CharData.js @@ -55,7 +55,7 @@ const CharData = { loginData: { filePath: "libs/SoloPlay/Data/" + me.profile + "/" + me.profile + "-LoginData.json", - default: {Acc: "", Pass: "", Char: "", existing: false}, + default: { Acc: "", Pass: "", Char: "", existing: false }, create: function () { let obj = Object.assign({}, this.default); diff --git a/libs/SoloPlay/Tools/SoloIndex.js b/libs/SoloPlay/Tools/SoloIndex.js index 22c814ec..1cf0501c 100644 --- a/libs/SoloPlay/Tools/SoloIndex.js +++ b/libs/SoloPlay/Tools/SoloIndex.js @@ -102,9 +102,9 @@ const SoloIndex = { skipIf: function () { switch (me.classid) { case sdk.player.class.Paladin: - return Pather.accessToAct(3) || Attack.auradin || me.checkItem({name: sdk.locale.items.Enigma}).have; + return Pather.accessToAct(3) || Attack.auradin || me.checkItem({ name: sdk.locale.items.Enigma }).have; case sdk.player.class.Barbarian: - return Pather.accessToAct(3) || me.checkItem({name: sdk.locale.items.Lawbringer}).have; + return Pather.accessToAct(3) || me.checkItem({ name: sdk.locale.items.Lawbringer }).have; default: if (me.hell && me.charlvl > 72 && Pather.accessToAct(2)) return true; return false; @@ -125,7 +125,7 @@ const SoloIndex = { return (me.hell && !Pather.accessToAct(3)); }, skipIf: function () { - return !me.paladin || Attack.auradin || me.checkItem({name: sdk.locale.items.Enigma}).have; + return !me.paladin || Attack.auradin || me.checkItem({ name: sdk.locale.items.Enigma }).have; }, shouldRun: function () { if (!this.preReq() || this.skipIf()) return false; @@ -141,7 +141,7 @@ const SoloIndex = { let needRunes = Check.runes(); switch (true) { case (me.normal && (needRunes || Check.brokeAf())): // todo - better determination for low gold - case (me.barbarian && me.hell && me.checkItem({name: sdk.locale.items.Lawbringer}).have): + case (me.barbarian && me.hell && me.checkItem({ name: sdk.locale.items.Lawbringer }).have): case (!me.normal && (Pather.canTeleport() || me.charlvl < 60)): return true; } @@ -238,7 +238,7 @@ const SoloIndex = { skipIf: function () { if (me.barbarian && (!me.hell || Pather.accessToAct(3) || (Item.getEquipped(sdk.body.LeftArm).tier > 1270 - || me.checkItem({name: sdk.locale.items.Lawbringer}).have))) { + || me.checkItem({ name: sdk.locale.items.Lawbringer }).have))) { return true; } @@ -452,7 +452,7 @@ const SoloIndex = { }, shouldRun: function () { if (!this.preReq() || this.skipIf()) return false; - return (me.nightmare && me.charlvl >= 50 && !me.checkItem({name: sdk.locale.items.VoiceofReason}).have); + return (me.nightmare && me.charlvl >= 50 && !me.checkItem({ name: sdk.locale.items.VoiceofReason }).have); } }, "heart": { @@ -491,8 +491,8 @@ const SoloIndex = { switch (true) { case !me.travincal: case (me.charlvl < 25 || (me.charlvl >= 25 && me.normal && !me.diffCompleted && Check.brokeAf())): - case (me.nightmare && !me.diablo && me.barbarian && !me.checkItem({name: sdk.locale.items.Lawbringer}).have): - case (me.hell && me.paladin && me.charlvl > 85 && (!Attack.auradin || !me.checkItem({name: sdk.locale.items.Enigma}).have)): + case (me.nightmare && !me.diablo && me.barbarian && !me.checkItem({ name: sdk.locale.items.Lawbringer }).have): + case (me.hell && me.paladin && me.charlvl > 85 && (!Attack.auradin || !me.checkItem({ name: sdk.locale.items.Enigma }).have)): return true; } return false; @@ -546,7 +546,7 @@ const SoloIndex = { shouldRun: function () { if (!this.preReq() || this.skipIf()) return false; switch (true) { - case (me.barbarian && !me.checkItem({name: sdk.locale.items.Lawbringer}).have): + case (me.barbarian && !me.checkItem({ name: sdk.locale.items.Lawbringer }).have): case (me.sorceress && me.classic): return true; } @@ -564,7 +564,7 @@ const SoloIndex = { shouldRun: function () { if (!this.preReq() || this.skipIf()) return false; switch (true) { - case (me.charlvl <= 70 && !me.checkItem({name: sdk.locale.items.Lawbringer}).have): + case (me.charlvl <= 70 && !me.checkItem({ name: sdk.locale.items.Lawbringer }).have): return true; } return false; diff --git a/libs/SoloPlay/Utils/General.js b/libs/SoloPlay/Utils/General.js index b9b2941a..4e77d10e 100644 --- a/libs/SoloPlay/Utils/General.js +++ b/libs/SoloPlay/Utils/General.js @@ -91,12 +91,12 @@ )); // spirit base basicSocketables.caster.push(addSocketableObj(sdk.items.BroadSword, [], [], true, (item) => - me.normal && !Check.haveBase("sword", 4) && !me.checkItem({name: sdk.locale.items.Spirit, itemtype: sdk.items.type.Sword}).have + me.normal && !Check.haveBase("sword", 4) && !me.checkItem({ name: sdk.locale.items.Spirit, itemtype: sdk.items.type.Sword }).have && item.ilvl >= 26 && item.isBaseType && !item.ethereal )); // spirit base basicSocketables.caster.push(addSocketableObj(sdk.items.CrystalSword, [], [], true, (item) => - me.normal && !Check.haveBase("sword", 4) && !me.checkItem({name: sdk.locale.items.Spirit, itemtype: sdk.items.type.Sword}).have + me.normal && !Check.haveBase("sword", 4) && !me.checkItem({ name: sdk.locale.items.Spirit, itemtype: sdk.items.type.Sword }).have && item.ilvl >= 26 && item.ilvl <= 40 && item.isBaseType && !item.ethereal )); // Lidless diff --git a/libs/SoloPlay/index.d.ts b/libs/SoloPlay/index.d.ts index be9808a9..bd405173 100644 --- a/libs/SoloPlay/index.d.ts +++ b/libs/SoloPlay/index.d.ts @@ -119,6 +119,10 @@ declare global { function switchCast(skillId: number, givenSettings: { hand?: number, x?: number, y?: number, switchBack?: boolean, oSkill?: boolean }): boolean; } + namespace Attack { + function clearPos(x: number, y: number, range: number, pickit: boolean): boolean; + } + interface charData { initialized: boolean; normal: { @@ -209,6 +213,16 @@ declare global { } namespace SoloWants { + const needList: any[]; + const validGids: number[]; + + function checkItem(item: ItemUnit): boolean; + function keepItem(item: ItemUnit): boolean; + function buildList(): void; + function addToList(item: ItemUnit): boolean; + function update(item: ItemUnit): boolean; + function ensureList(): void; + function checkSubrecipes(): boolean; } namespace NPCAction { @@ -224,6 +238,13 @@ declare global { namespace AutoEquip { } + namespace SoloEvents { + namespace townChicken { + let disabled: boolean; + let running: boolean; + } + } + type extraTasks = { thawing?: boolean, antidote?: boolean, From f39299523ddd7fc4dd784e763c3e1f1888efe037 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Tue, 28 Feb 2023 16:03:41 -0500 Subject: [PATCH 052/263] Update SorceressAttacks.js - During low levels don't run in to attack if the area is dangerous - If our skill is ranged and we are on top of the target, move to a safer distance --- .../ClassAttackOverrides/SorceressAttacks.js | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/libs/SoloPlay/Functions/ClassAttackOverrides/SorceressAttacks.js b/libs/SoloPlay/Functions/ClassAttackOverrides/SorceressAttacks.js index 8dd3db78..119de2c0 100644 --- a/libs/SoloPlay/Functions/ClassAttackOverrides/SorceressAttacks.js +++ b/libs/SoloPlay/Functions/ClassAttackOverrides/SorceressAttacks.js @@ -408,6 +408,11 @@ includeIfNotIncluded("core/Attacks/Sorceress.js"); || (selectedSkill.mana * 3 > me.mp && [sdk.skills.FireBolt, sdk.skills.ChargedBolt].includes(selectedSkill.skill)))) { if (switchBowAttack(unit) === Attack.Result.SUCCESS) return Attack.Result.SUCCESS; } + + if (selectedSkill === sdk.skills.Attack && me.inDanger(unit, 10)) { + // try to stay safer for now, probably should see if there are any easy targets we can pick off + return Attack.Result.CANTATTACK; + } let result = ClassAttack.doCast(unit, selectedSkill); @@ -505,6 +510,8 @@ includeIfNotIncluded("core/Attacks/Sorceress.js"); | Coords_1.BlockBits.Objects, false, true); } else if (me.inDanger()) { Attack.getIntoPosition(unit, range + 1, Coords_1.Collision.BLOCK_MISSILE, true); + } else if (unit.distance < 3 && range > 4) { + Attack.getIntoPosition(unit, range, Coords_1.Collision.BLOCK_MISSILE, true); } } @@ -516,7 +523,7 @@ includeIfNotIncluded("core/Attacks/Sorceress.js"); } if (skill === sdk.skills.Attack) { - if (me.hpPercent < 50 && me.mode !== sdk.player.mode.GettingHit && !me.checkForMobs({range: 12})) { + if (me.hpPercent < 50 && me.mode !== sdk.player.mode.GettingHit && !me.checkForMobs({ range: 12 })) { console.log("Low health but safe right now, going to delay a bit"); let tick = getTickCount(); const howLongToDelay = Config.AttackSkill.some(sk => sk > 1 && Skill.canUse(sk)) ? Time.seconds(2) : Time.seconds(1); @@ -537,7 +544,7 @@ includeIfNotIncluded("core/Attacks/Sorceress.js"); if (range < 4 && !Attack.validSpot(unit.x, unit.y)) return Attack.Result.FAILED; // Only delay if there are no mobs in our immediate area - if (mana > me.mp && !me.checkForMobs({range: 12})) { + if (mana > me.mp && !me.checkForMobs({ range: 12 })) { let tick = getTickCount(); while (getTickCount() - tick < 750) { From 1195c0f7ca343f68cf5b8fb12108095d44369244 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Tue, 28 Feb 2023 16:37:44 -0500 Subject: [PATCH 053/263] Include betterBaseThanWearing check for cubing - Don't cube a base if it's no good for us --- libs/SoloPlay/Functions/CubingOverrides.js | 2 +- libs/SoloPlay/Functions/ItemUtilities.js | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/libs/SoloPlay/Functions/CubingOverrides.js b/libs/SoloPlay/Functions/CubingOverrides.js index 24cc6446..c6b41837 100644 --- a/libs/SoloPlay/Functions/CubingOverrides.js +++ b/libs/SoloPlay/Functions/CubingOverrides.js @@ -758,7 +758,7 @@ Cubing.validItem = function (unit, recipe) { if (unit.normal && unit.sockets === 0) { if (Pickit.Result.WANTED === ntipResult && [sdk.items.type.Wand, sdk.items.type.VoodooHeads, sdk.items.type.AuricShields, sdk.items.type.PrimalHelm, sdk.items.type.Pelt].includes(unit.itemType)) { - if (!Item.betterThanStashed(unit)) return false; + if (!Item.betterThanStashed(unit) || !Item.betterBaseThanWearing(unit)) return false; } switch (recipe.Ethereal) { case Roll.All: diff --git a/libs/SoloPlay/Functions/ItemUtilities.js b/libs/SoloPlay/Functions/ItemUtilities.js index ab41bd71..efe33b07 100644 --- a/libs/SoloPlay/Functions/ItemUtilities.js +++ b/libs/SoloPlay/Functions/ItemUtilities.js @@ -6,7 +6,7 @@ */ includeIfNotIncluded("core/Item.js"); -(function() { +(function () { // TODO: clean this up (sigh) - 8/10/22 - update refactored alot, still think more can be done though const baseSkillsScore = (item, buildInfo) => { buildInfo === undefined && (buildInfo = Check.currentBuild()); @@ -31,7 +31,7 @@ includeIfNotIncluded("core/Item.js"); * @param {boolean} verbose * @returns {boolean} */ - Item.betterBaseThanWearing = function (base = undefined, verbose = Developer.debugging.baseCheck) { + Item.betterBaseThanWearing = function (base, verbose = Developer.debugging.baseCheck) { if (!base || !base.isBaseType) return false; let name = ""; From bf4088777d405513d584b11650be66b466bb8038 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Wed, 1 Mar 2023 16:48:19 -0500 Subject: [PATCH 054/263] Update NTIPOverrides.js - Fix other tier'd items being included in finalGear check --- libs/SoloPlay/Functions/NTIPOverrides.js | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/libs/SoloPlay/Functions/NTIPOverrides.js b/libs/SoloPlay/Functions/NTIPOverrides.js index 90a7c2b5..b59bfdb4 100644 --- a/libs/SoloPlay/Functions/NTIPOverrides.js +++ b/libs/SoloPlay/Functions/NTIPOverrides.js @@ -11,7 +11,8 @@ includeIfNotIncluded("SoloPlay/Functions/PrototypeOverrides.js"); /** * @todo - * - need a way to build and update/remove elements from checklists during gameplay + * - need a way to build and update/remove elements from checklists during gameplay + * - Build parser, takes obj then coverts to normal nip string */ NTIPAliasStat["addfireskills"] = [sdk.stats.ElemSkill, 1]; @@ -124,6 +125,10 @@ NTIP.addLine = function (itemString) { return true; }; +/** + * @param {string[]} arr + * @returns {boolean} + */ NTIP.buildFinalGear = function (arr) { for (let i = 0; i < arr.length; i++) { const info = { @@ -132,10 +137,17 @@ NTIP.buildFinalGear = function (arr) { string: arr[i] }; + /** @type {string} */ const line = NTIP.ParseLineInt(arr[i], info); if (line) { - if (!arr[i].toLowerCase().includes("tier")) { + let lineCheck = arr[i].toLowerCase(); + + switch (true) { + case !lineCheck.includes("tier"): + case line.includes("merctier"): + case line.includes("secondarytier"): + case line.includes("charmtier"): continue; } From 4a84c3c15165a8eebc15b6561164c24eeb5b88d9 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Thu, 2 Mar 2023 15:24:36 -0500 Subject: [PATCH 055/263] Fix TCPHost mode - Really no reason to use it but fixing it anyway so it doesn't stay stuck in an endless loop - Fix typo in SoloEvents --- libs/SoloPlay/Functions/SoloEvents.js | 4 ++-- libs/SoloPlay/Tools/OOGOverrides.js | 12 ++++++++---- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/libs/SoloPlay/Functions/SoloEvents.js b/libs/SoloPlay/Functions/SoloEvents.js index df2e6337..8c771317 100644 --- a/libs/SoloPlay/Functions/SoloEvents.js +++ b/libs/SoloPlay/Functions/SoloEvents.js @@ -7,7 +7,7 @@ (function (root, factory) { if (typeof module === "object" && typeof module.exports === "object") { - const v = factory(); + let v = factory(); if (v !== undefined) module.exports = v; } else if (typeof define === "function" && define.amd) { define([], factory); @@ -136,7 +136,7 @@ //realm = "useast"; // testing purposes if (!FileTools.exists("logs/Kolbot-SoloPlay/" + realm)) { - return profileList; + return charList; } let files = dopen("logs/Kolbot-SoloPlay/" + realm + "/").getFiles(); diff --git a/libs/SoloPlay/Tools/OOGOverrides.js b/libs/SoloPlay/Tools/OOGOverrides.js index 8ee395ca..3dfe85fd 100644 --- a/libs/SoloPlay/Tools/OOGOverrides.js +++ b/libs/SoloPlay/Tools/OOGOverrides.js @@ -692,8 +692,10 @@ const locations = {}; Starter.LocationEvents.login = function () { Starter.inGame && (Starter.inGame = false); + let pType = Profile().type; + if (getLocation() === sdk.game.locations.MainMenu && Starter.firstRun - && Profile().type === sdk.game.profiletype.SinglePlayer + && pType === sdk.game.profiletype.SinglePlayer && Controls.SinglePlayer.click()) { return; } @@ -701,7 +703,7 @@ const locations = {}; // Wrong char select screen fix if ([sdk.game.locations.CharSelect, sdk.game.locations.CharSelectNoChars].includes(getLocation())) { hideConsole(); // seems to fix odd crash with single-player characters if the console is open to type in - let spCheck = Profile().type === sdk.game.profiletype.Battlenet; + let spCheck = pType === sdk.game.profiletype.Battlenet; let realmControl = !!Controls.CharSelectCurrentRealm.control; if ((spCheck && !realmControl) || ((!spCheck && realmControl))) { Controls.CharSelectExit.click(); @@ -790,8 +792,10 @@ const locations = {}; } else { // SP/TCP characters try { - if (getLocation() === sdk.game.locations.MainMenu && Profile().type === sdk.game.profiletype.SinglePlayer) { - Controls.SinglePlayer.click(); + if (getLocation() === sdk.game.locations.MainMenu) { + pType === sdk.game.profiletype.SinglePlayer + ? Controls.SinglePlayer.click() + : ControlAction.loginOtherMultiplayer(); } Starter.checkDifficulty(); Starter.LocationEvents.charSelect(getLocation()); From a8645683424dfe7e0a4537c29b4a67086d581fc9 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Wed, 8 Mar 2023 12:51:52 -0500 Subject: [PATCH 056/263] Mostly linting and formatting - Fixed occasional bug in ntip where stringArray was undefined --- D2BotSoloPlay.dbj | 2 +- .../BuildFiles/Runewords/AncientsPledge.js | 2 +- .../BuildFiles/Runewords/CallToArms.js | 2 +- .../BuildFiles/Runewords/ChainsOfHonor.js | 4 +- libs/SoloPlay/BuildFiles/Runewords/Faith.js | 4 +- libs/SoloPlay/BuildFiles/Runewords/Grief.js | 2 +- .../BuildFiles/Runewords/HandOfJustice.js | 2 +- libs/SoloPlay/BuildFiles/Runewords/Ice.js | 4 +- .../SoloPlay/BuildFiles/Runewords/LastWish.js | 2 +- .../SoloPlay/BuildFiles/Runewords/MercDoom.js | 8 +- .../BuildFiles/Runewords/MercFortitude.js | 4 +- .../BuildFiles/Runewords/MercInfinity.js | 2 +- .../BuildFiles/Runewords/MercPride.js | 2 +- .../BuildFiles/Runewords/PhoenixShield.js | 4 +- .../amazon/amazon.FaithbowzonBuild.js | 2 +- .../amazon/amazon.FrostmaidenBuild.js | 2 +- .../assassin/assassin.WhirlsinBuild.js | 2 +- .../barbarian/barbarian.FrenzyBuild.js | 2 +- .../barbarian/barbarian.SingerBuild.js | 2 +- .../barbarian/barbarian.UberconcBuild.js | 2 +- .../barbarian/barbarian.WhirlwindBuild.js | 2 +- .../BuildFiles/druid/druid.FirewolfBuild.js | 2 +- .../BuildFiles/druid/druid.PlaguewolfBuild.js | 2 +- .../BuildFiles/druid/druid.StormbearBuild.js | 2 +- .../paladin/paladin.AuradinBuild.js | 4 +- .../paladin/paladin.ClassicauradinBuild.js | 2 +- .../paladin/paladin.SancdreamerBuild.js | 4 +- .../BuildFiles/paladin/paladin.SmiterBuild.js | 2 +- .../paladin/paladin.TorchadinBuild.js | 2 +- .../BuildFiles/paladin/paladin.ZealerBuild.js | 2 +- libs/SoloPlay/Config/Amazon.js | 20 ++-- libs/SoloPlay/Config/Assassin.js | 26 ++--- libs/SoloPlay/Config/Barbarian.js | 34 +++--- libs/SoloPlay/Config/Druid.js | 30 ++--- libs/SoloPlay/Config/Necromancer.js | 20 ++-- libs/SoloPlay/Config/Paladin.js | 104 +++++++++--------- libs/SoloPlay/Config/Sorceress.js | 24 ++-- libs/SoloPlay/Functions/Globals.js | 20 ++-- libs/SoloPlay/Functions/ItemOverrides.js | 1 + libs/SoloPlay/Functions/ItemPrototypes.js | 6 +- libs/SoloPlay/Functions/ItemUtilities.js | 7 +- libs/SoloPlay/Functions/Me.js | 12 +- libs/SoloPlay/Functions/NTIPOverrides.js | 21 +--- libs/SoloPlay/Functions/PrototypeOverrides.js | 4 +- libs/SoloPlay/Threads/ToolsThread.js | 102 +++++++++-------- 45 files changed, 261 insertions(+), 250 deletions(-) diff --git a/D2BotSoloPlay.dbj b/D2BotSoloPlay.dbj index 6e6d873d..2340702d 100644 --- a/D2BotSoloPlay.dbj +++ b/D2BotSoloPlay.dbj @@ -126,7 +126,7 @@ function main () { Starter.BNET && D2Bot.updateRuns(); } - DataFile.updateStats("debugInfo", JSON.stringify({currScript: "none", area: "out of game"})); + DataFile.updateStats("debugInfo", JSON.stringify({ currScript: "none", area: "out of game" })); while (!Object.keys(Starter.profileInfo).length) { D2Bot.getProfile(); diff --git a/libs/SoloPlay/BuildFiles/Runewords/AncientsPledge.js b/libs/SoloPlay/BuildFiles/Runewords/AncientsPledge.js index 2c0c7589..b1ed2008 100644 --- a/libs/SoloPlay/BuildFiles/Runewords/AncientsPledge.js +++ b/libs/SoloPlay/BuildFiles/Runewords/AncientsPledge.js @@ -1,5 +1,5 @@ (function() { - if (!me.checkItem({name: sdk.locale.items.AncientsPledge}).have && !me.hell) { + if (!me.checkItem({ name: sdk.locale.items.AncientsPledge }).have && !me.hell) { // Cube to Ort rune if (me.normal && !me.getItem(sdk.items.runes.Ort)) { Config.Recipes.push([Recipe.Rune, "Ral Rune"]); diff --git a/libs/SoloPlay/BuildFiles/Runewords/CallToArms.js b/libs/SoloPlay/BuildFiles/Runewords/CallToArms.js index 514b8d5b..8de2ceda 100644 --- a/libs/SoloPlay/BuildFiles/Runewords/CallToArms.js +++ b/libs/SoloPlay/BuildFiles/Runewords/CallToArms.js @@ -33,7 +33,7 @@ Config.Recipes.push([Recipe.Rune, "Ist Rune"]); Config.Recipes.push([Recipe.Rune, "Gul Rune"]); - if (me.checkItem({name: sdk.locale.items.HeartoftheOak}).have || ["Zealer", "Smiter", "Auradin", "Meteorb", "Blizzballer", "Cold"].includes(SetUp.finalBuild)) { + if (me.checkItem({ name: sdk.locale.items.HeartoftheOak }).have || ["Zealer", "Smiter", "Auradin", "Meteorb", "Blizzballer", "Cold"].includes(SetUp.finalBuild)) { Config.Recipes.push([Recipe.Rune, "Vex Rune"]); } } diff --git a/libs/SoloPlay/BuildFiles/Runewords/ChainsOfHonor.js b/libs/SoloPlay/BuildFiles/Runewords/ChainsOfHonor.js index 64ac58e1..b91a16c0 100644 --- a/libs/SoloPlay/BuildFiles/Runewords/ChainsOfHonor.js +++ b/libs/SoloPlay/BuildFiles/Runewords/ChainsOfHonor.js @@ -9,7 +9,7 @@ // Cube to Ber rune if (!me.getItem(sdk.items.runes.Ber)) { - if (me.checkItem({name: sdk.locale.items.CalltoArms}).have || ["Plaguewolf", "Wolf", "Uberconc"].includes(SetUp.finalBuild)) { + if (me.checkItem({ name: sdk.locale.items.CalltoArms }).have || ["Plaguewolf", "Wolf", "Uberconc"].includes(SetUp.finalBuild)) { Config.Recipes.push([Recipe.Rune, "Mal Rune"]); Config.Recipes.push([Recipe.Rune, "Ist Rune"]); Config.Recipes.push([Recipe.Rune, "Gul Rune"]); @@ -17,7 +17,7 @@ Config.Recipes.push([Recipe.Rune, "Ohm Rune"]); } - if (me.checkItem({name: sdk.locale.items.Grief}).have || ["Uberconc"].indexOf(SetUp.finalBuild) === -1) { + if (me.checkItem({ name: sdk.locale.items.Grief }).have || ["Uberconc"].indexOf(SetUp.finalBuild) === -1) { Config.Recipes.push([Recipe.Rune, "Lo Rune"]); } diff --git a/libs/SoloPlay/BuildFiles/Runewords/Faith.js b/libs/SoloPlay/BuildFiles/Runewords/Faith.js index e62e5766..2366d182 100644 --- a/libs/SoloPlay/BuildFiles/Runewords/Faith.js +++ b/libs/SoloPlay/BuildFiles/Runewords/Faith.js @@ -7,7 +7,7 @@ ]; NTIP.buildList(FaithRunes); // Cube to Ohm and Keep cubing to Jah rune - if (Item.getQuantityOwned(me.getItem(sdk.items.runes.Ohm) > 1) && me.checkItem({name: sdk.locale.items.CalltoArms}).have) { + if (Item.getQuantityOwned(me.getItem(sdk.items.runes.Ohm) > 1) && me.checkItem({ name: sdk.locale.items.CalltoArms }).have) { if (!me.getItem(sdk.items.runes.Jah)) { Config.Recipes.push([Recipe.Rune, "Mal Rune"]); Config.Recipes.push([Recipe.Rune, "Ist Rune"]); @@ -17,7 +17,7 @@ !me.getItem(sdk.items.runes.Jah) && Config.Recipes.push([Recipe.Rune, "Ohm Rune"]); } // Cube to Jah rune - if (!me.getItem(sdk.items.runes.Jah) && me.checkItem({name: sdk.locale.items.ChainofHonor}).have) { + if (!me.getItem(sdk.items.runes.Jah) && me.checkItem({ name: sdk.locale.items.ChainofHonor }).have) { Config.Recipes.push([Recipe.Rune, "Ohm Rune"]); Config.Recipes.push([Recipe.Rune, "Lo Rune"]); Config.Recipes.push([Recipe.Rune, "Sur Rune"]); diff --git a/libs/SoloPlay/BuildFiles/Runewords/Grief.js b/libs/SoloPlay/BuildFiles/Runewords/Grief.js index 277f8d6e..aaccf200 100644 --- a/libs/SoloPlay/BuildFiles/Runewords/Grief.js +++ b/libs/SoloPlay/BuildFiles/Runewords/Grief.js @@ -25,7 +25,7 @@ Config.Recipes.push([Recipe.Rune, "Gul Rune"]); Config.Recipes.push([Recipe.Rune, "Vex Rune"]); - if (me.checkItem({name: sdk.locale.items.CalltoArms}).have || ["Smiter", "Zealer"].indexOf(SetUp.finalBuild) === -1) { + if (me.checkItem({ name: sdk.locale.items.CalltoArms }).have || ["Smiter", "Zealer"].indexOf(SetUp.finalBuild) === -1) { Config.Recipes.push([Recipe.Rune, "Ohm Rune"]); } } diff --git a/libs/SoloPlay/BuildFiles/Runewords/HandOfJustice.js b/libs/SoloPlay/BuildFiles/Runewords/HandOfJustice.js index 35441652..3e5751d9 100644 --- a/libs/SoloPlay/BuildFiles/Runewords/HandOfJustice.js +++ b/libs/SoloPlay/BuildFiles/Runewords/HandOfJustice.js @@ -15,7 +15,7 @@ Config.Recipes.push([Recipe.Rune, "Gul Rune"]); Config.Recipes.push([Recipe.Rune, "Vex Rune"]); - if (me.checkItem({name: sdk.locale.items.CalltoArms}).have) { + if (me.checkItem({ name: sdk.locale.items.CalltoArms }).have) { Config.Recipes.push([Recipe.Rune, "Ohm Rune"]); } } diff --git a/libs/SoloPlay/BuildFiles/Runewords/Ice.js b/libs/SoloPlay/BuildFiles/Runewords/Ice.js index 9ec9af83..2f1a52bd 100644 --- a/libs/SoloPlay/BuildFiles/Runewords/Ice.js +++ b/libs/SoloPlay/BuildFiles/Runewords/Ice.js @@ -8,7 +8,7 @@ NTIP.buildList(IceRunes); // Cube to Lo and Keep cubing to Jah rune - if (Item.getQuantityOwned(me.getItem(sdk.items.runes.Lo)) > 1 && me.checkItem({name: sdk.locale.items.ChainofHonor}).have) { + if (Item.getQuantityOwned(me.getItem(sdk.items.runes.Lo)) > 1 && me.checkItem({ name: sdk.locale.items.ChainofHonor }).have) { if (!me.getItem(sdk.items.runes.Jah)) { Config.Recipes.push([Recipe.Rune, "Mal Rune"]); Config.Recipes.push([Recipe.Rune, "Ist Rune"]); @@ -19,7 +19,7 @@ !me.getItem(sdk.items.runes.Jah) && Config.Recipes.push([Recipe.Rune, "Lo Rune"]); } // Cube to Jah rune - if (!me.getItem(sdk.items.runes.Jah) && me.checkItem({name: sdk.locale.items.ChainofHonor}).have) { + if (!me.getItem(sdk.items.runes.Jah) && me.checkItem({ name: sdk.locale.items.ChainofHonor }).have) { Config.Recipes.push([Recipe.Rune, "Lo Rune"]); Config.Recipes.push([Recipe.Rune, "Sur Rune"]); Config.Recipes.push([Recipe.Rune, "Ber Rune"]); diff --git a/libs/SoloPlay/BuildFiles/Runewords/LastWish.js b/libs/SoloPlay/BuildFiles/Runewords/LastWish.js index fd245783..f2265e2c 100644 --- a/libs/SoloPlay/BuildFiles/Runewords/LastWish.js +++ b/libs/SoloPlay/BuildFiles/Runewords/LastWish.js @@ -10,7 +10,7 @@ NTIP.buildList(LW); // Cube to Jah/Sur rune if (!me.getItem(sdk.items.runes.Jah) || !me.getItem(sdk.items.runes.Sur)) { - if (me.checkItem({name: sdk.locale.items.CalltoArms}).have) { + if (me.checkItem({ name: sdk.locale.items.CalltoArms }).have) { Config.Recipes.push([Recipe.Rune, "Mal Rune"]); Config.Recipes.push([Recipe.Rune, "Ist Rune"]); Config.Recipes.push([Recipe.Rune, "Gul Rune"]); diff --git a/libs/SoloPlay/BuildFiles/Runewords/MercDoom.js b/libs/SoloPlay/BuildFiles/Runewords/MercDoom.js index 56855093..3da876fd 100644 --- a/libs/SoloPlay/BuildFiles/Runewords/MercDoom.js +++ b/libs/SoloPlay/BuildFiles/Runewords/MercDoom.js @@ -24,8 +24,8 @@ Config.Recipes.push([Recipe.Rune, "Gul Rune"]); Config.Recipes.push([Recipe.Rune, "Vex Rune"]); Config.Recipes.push([Recipe.Rune, "Ohm Rune"]); - if ((me.barbarian && me.haveAll([{name: sdk.locale.items.Grief}, {name: sdk.locale.items.Fortitude}])) - || (["Witchyzon", "Wfzon"].includes(SetUp.finalBuild) && me.checkItem({name: sdk.locale.items.ChainsofHonor}).have) + if ((me.barbarian && me.haveAll([{ name: sdk.locale.items.Grief }, { name: sdk.locale.items.Fortitude }])) + || (["Witchyzon", "Wfzon"].includes(SetUp.finalBuild) && me.checkItem({ name: sdk.locale.items.ChainsofHonor }).have) || (SetUp.currentBuild === "Faithbowzon")) { Config.Recipes.push([Recipe.Rune, "Lo Rune"]); } @@ -35,8 +35,8 @@ } // Cube to Lo if (!me.getItem(sdk.items.runes.Lo)) { - if ((me.barbarian) || (SetUp.currentBuild === "Faithbowzon" && me.checkItem({name: sdk.locale.items.CalltoArms}).have) - || (["Witchyzon", "Wfzon"].includes(SetUp.finalBuild) && me.haveAll([{name: sdk.locale.items.ChainsofHonor}, {name: sdk.locale.items.CalltoArms}]))) { + if ((me.barbarian) || (SetUp.currentBuild === "Faithbowzon" && me.checkItem({ name: sdk.locale.items.CalltoArms }).have) + || (["Witchyzon", "Wfzon"].includes(SetUp.finalBuild) && me.haveAll([{ name: sdk.locale.items.ChainsofHonor }, { name: sdk.locale.items.CalltoArms }]))) { Config.Recipes.push([Recipe.Rune, "Mal Rune"]); Config.Recipes.push([Recipe.Rune, "Ist Rune"]); Config.Recipes.push([Recipe.Rune, "Gul Rune"]); diff --git a/libs/SoloPlay/BuildFiles/Runewords/MercFortitude.js b/libs/SoloPlay/BuildFiles/Runewords/MercFortitude.js index 86241770..70af0a5d 100644 --- a/libs/SoloPlay/BuildFiles/Runewords/MercFortitude.js +++ b/libs/SoloPlay/BuildFiles/Runewords/MercFortitude.js @@ -10,12 +10,12 @@ if (["Zealer", "Smiter", "Frenzy", "Whirlwind", "Uberconc", "Wolf"].includes(SetUp.finalBuild)) { // Make Grief first, if using it for final build - if (me.checkItem({name: sdk.locale.items.Grief, itemtype: sdk.items.type.Sword}).have) { + if (me.checkItem({ name: sdk.locale.items.Grief, itemtype: sdk.items.type.Sword }).have) { NTIP.buildList(fort); } } else if (["Blova", "Lightning"].includes(SetUp.currentBuild)) { // Make Chains of Honor first for Blova/Lightning, or already have ber so Lo isn't needed for cubing - if (me.checkItem({name: sdk.locale.items.ChainsofHonor}).have || me.getItem(sdk.items.runes.Ber)) { + if (me.checkItem({ name: sdk.locale.items.ChainsofHonor }).have || me.getItem(sdk.items.runes.Ber)) { NTIP.buildList(fort); } } else { diff --git a/libs/SoloPlay/BuildFiles/Runewords/MercInfinity.js b/libs/SoloPlay/BuildFiles/Runewords/MercInfinity.js index affa340d..1e183a15 100644 --- a/libs/SoloPlay/BuildFiles/Runewords/MercInfinity.js +++ b/libs/SoloPlay/BuildFiles/Runewords/MercInfinity.js @@ -10,7 +10,7 @@ // Cube to Ber rune if (me.findItems(sdk.items.runes.Ber).length < 2) { - if (me.checkItem({name: sdk.locale.items.CalltoArms}).have || me.barbarian) { + if (me.checkItem({ name: sdk.locale.items.CalltoArms }).have || me.barbarian) { Config.Recipes.push([Recipe.Rune, "Mal Rune"]); Config.Recipes.push([Recipe.Rune, "Ist Rune"]); Config.Recipes.push([Recipe.Rune, "Gul Rune"]); diff --git a/libs/SoloPlay/BuildFiles/Runewords/MercPride.js b/libs/SoloPlay/BuildFiles/Runewords/MercPride.js index 0292006d..b9525352 100644 --- a/libs/SoloPlay/BuildFiles/Runewords/MercPride.js +++ b/libs/SoloPlay/BuildFiles/Runewords/MercPride.js @@ -11,7 +11,7 @@ // Cube to Sur/Lo rune if (!me.getItem(sdk.items.runes.Sur) || !me.getItem(sdk.items.runes.Lo)) { - if (me.checkItem({name: sdk.locale.items.CalltoArms}).have || me.barbarian) { + if (me.checkItem({ name: sdk.locale.items.CalltoArms }).have || me.barbarian) { Config.Recipes.push([Recipe.Rune, "Mal Rune"]); Config.Recipes.push([Recipe.Rune, "Ist Rune"]); Config.Recipes.push([Recipe.Rune, "Gul Rune"]); diff --git a/libs/SoloPlay/BuildFiles/Runewords/PhoenixShield.js b/libs/SoloPlay/BuildFiles/Runewords/PhoenixShield.js index 1c5b0758..b939ddd1 100644 --- a/libs/SoloPlay/BuildFiles/Runewords/PhoenixShield.js +++ b/libs/SoloPlay/BuildFiles/Runewords/PhoenixShield.js @@ -16,7 +16,7 @@ (Item.getQuantityOwned(me.getItem(sdk.items.runes.Vex)) > 1 && !me.getItem(sdk.items.runes.Jah) && Config.Recipes.push([Recipe.Rune, "Vex Rune"])); } // Cube to Lo and Keep cubing to Jah rune - if (Item.getQuantityOwned(me.getItem(sdk.items.runes.Lo)) > 1 && me.checkItem({name: sdk.locale.items.HeartoftheOak}).have) { + if (Item.getQuantityOwned(me.getItem(sdk.items.runes.Lo)) > 1 && me.checkItem({ name: sdk.locale.items.HeartoftheOak }).have) { if (!me.getItem(sdk.items.runes.Jah)) { Config.Recipes.push([Recipe.Rune, "Vex Rune"]); Config.Recipes.push([Recipe.Rune, "Ohm Rune"]); @@ -24,7 +24,7 @@ !me.getItem(sdk.items.runes.Jah) && Config.Recipes.push([Recipe.Rune, "Lo Rune"]); } // Cube to Jah rune - if (me.checkItem({name: sdk.locale.items.Enigma}).have && !me.getItem(sdk.items.runes.Jah)) { + if (me.checkItem({ name: sdk.locale.items.Enigma }).have && !me.getItem(sdk.items.runes.Jah)) { Config.Recipes.push([Recipe.Rune, "Lo Rune"]); Config.Recipes.push([Recipe.Rune, "Sur Rune"]); Config.Recipes.push([Recipe.Rune, "Ber Rune"]); diff --git a/libs/SoloPlay/BuildFiles/amazon/amazon.FaithbowzonBuild.js b/libs/SoloPlay/BuildFiles/amazon/amazon.FaithbowzonBuild.js index 1af98a9f..7d9da63c 100644 --- a/libs/SoloPlay/BuildFiles/amazon/amazon.FaithbowzonBuild.js +++ b/libs/SoloPlay/BuildFiles/amazon/amazon.FaithbowzonBuild.js @@ -135,7 +135,7 @@ const finalBuild = { if (me.classic) { return false; } else { - return (me.checkItem({name: sdk.locale.items.Faith}).have || Check.haveItem("diamondbow", "unique", "Witchwild String")); + return (me.checkItem({ name: sdk.locale.items.Faith }).have || Check.haveItem("diamondbow", "unique", "Witchwild String")); } }, diff --git a/libs/SoloPlay/BuildFiles/amazon/amazon.FrostmaidenBuild.js b/libs/SoloPlay/BuildFiles/amazon/amazon.FrostmaidenBuild.js index 817c0990..ba47b24f 100644 --- a/libs/SoloPlay/BuildFiles/amazon/amazon.FrostmaidenBuild.js +++ b/libs/SoloPlay/BuildFiles/amazon/amazon.FrostmaidenBuild.js @@ -119,7 +119,7 @@ const finalBuild = { if (me.classic) { return false; } else { - return me.haveAll([{name: sdk.locale.items.Dream, itemtype: sdk.items.type.Helm}, {name: sdk.locale.items.Brand}, {name: sdk.locale.items.ChainsofHonor}]); + return me.haveAll([{ name: sdk.locale.items.Dream, itemtype: sdk.items.type.Helm }, { name: sdk.locale.items.Brand }, { name: sdk.locale.items.ChainsofHonor }]); } }, diff --git a/libs/SoloPlay/BuildFiles/assassin/assassin.WhirlsinBuild.js b/libs/SoloPlay/BuildFiles/assassin/assassin.WhirlsinBuild.js index c51fdd66..33bfdf85 100644 --- a/libs/SoloPlay/BuildFiles/assassin/assassin.WhirlsinBuild.js +++ b/libs/SoloPlay/BuildFiles/assassin/assassin.WhirlsinBuild.js @@ -127,7 +127,7 @@ const finalBuild = { }, respec: function () { - return me.haveAll([{name: sdk.locale.items.Chaos}, {name: sdk.locale.items.Fury}]); + return me.haveAll([{ name: sdk.locale.items.Chaos }, { name: sdk.locale.items.Fury }]); }, active: function () { diff --git a/libs/SoloPlay/BuildFiles/barbarian/barbarian.FrenzyBuild.js b/libs/SoloPlay/BuildFiles/barbarian/barbarian.FrenzyBuild.js index 0e62b4f3..4a13668c 100644 --- a/libs/SoloPlay/BuildFiles/barbarian/barbarian.FrenzyBuild.js +++ b/libs/SoloPlay/BuildFiles/barbarian/barbarian.FrenzyBuild.js @@ -127,7 +127,7 @@ const finalBuild = { if (me.classic) { return me.charlvl >= 75 && me.diablo; } else { - return me.haveAll([{name: sdk.locale.items.Grief}, {name: sdk.locale.items.BreathoftheDying}]); + return me.haveAll([{ name: sdk.locale.items.Grief }, { name: sdk.locale.items.BreathoftheDying }]); } }, diff --git a/libs/SoloPlay/BuildFiles/barbarian/barbarian.SingerBuild.js b/libs/SoloPlay/BuildFiles/barbarian/barbarian.SingerBuild.js index 392ab699..8e2460d1 100644 --- a/libs/SoloPlay/BuildFiles/barbarian/barbarian.SingerBuild.js +++ b/libs/SoloPlay/BuildFiles/barbarian/barbarian.SingerBuild.js @@ -109,7 +109,7 @@ const finalBuild = { if (me.classic) { return me.charlvl >= 75 && me.diablo; } else { - return me.haveAll([{name: sdk.locale.items.Enigma}, {name: sdk.locale.items.HeartoftheOak}]); + return me.haveAll([{ name: sdk.locale.items.Enigma }, { name: sdk.locale.items.HeartoftheOak }]); } }, diff --git a/libs/SoloPlay/BuildFiles/barbarian/barbarian.UberconcBuild.js b/libs/SoloPlay/BuildFiles/barbarian/barbarian.UberconcBuild.js index b6609cb1..8ce5c680 100644 --- a/libs/SoloPlay/BuildFiles/barbarian/barbarian.UberconcBuild.js +++ b/libs/SoloPlay/BuildFiles/barbarian/barbarian.UberconcBuild.js @@ -105,7 +105,7 @@ const finalBuild = { if (me.classic) { return me.charlvl >= 75 && me.diablo; } else { - return me.checkItem({name: sdk.locale.items.Grief, itemtype: sdk.items.type.Sword}).have && Check.haveItem("monarch", "unique", "Stormshield"); + return me.checkItem({ name: sdk.locale.items.Grief, itemtype: sdk.items.type.Sword }).have && Check.haveItem("monarch", "unique", "Stormshield"); } }, diff --git a/libs/SoloPlay/BuildFiles/barbarian/barbarian.WhirlwindBuild.js b/libs/SoloPlay/BuildFiles/barbarian/barbarian.WhirlwindBuild.js index 504df7d8..76c505dc 100644 --- a/libs/SoloPlay/BuildFiles/barbarian/barbarian.WhirlwindBuild.js +++ b/libs/SoloPlay/BuildFiles/barbarian/barbarian.WhirlwindBuild.js @@ -118,7 +118,7 @@ const finalBuild = { return me.charlvl >= 75 && me.diablo; } else { // TODO: figure out how to make sure we have two, or determine if that even matters - return me.checkItem({name: sdk.locale.items.Grief, itemtype: sdk.items.type.Sword}).have; + return me.checkItem({ name: sdk.locale.items.Grief, itemtype: sdk.items.type.Sword }).have; } }, diff --git a/libs/SoloPlay/BuildFiles/druid/druid.FirewolfBuild.js b/libs/SoloPlay/BuildFiles/druid/druid.FirewolfBuild.js index 03cb3f92..e8eef02c 100644 --- a/libs/SoloPlay/BuildFiles/druid/druid.FirewolfBuild.js +++ b/libs/SoloPlay/BuildFiles/druid/druid.FirewolfBuild.js @@ -117,7 +117,7 @@ const finalBuild = { }, respec: function () { - return me.haveAll([{name: sdk.locale.items.Ice}, {name: sdk.locale.items.ChainsofHonor}]); + return me.haveAll([{ name: sdk.locale.items.Ice }, { name: sdk.locale.items.ChainsofHonor }]); }, active: function () { diff --git a/libs/SoloPlay/BuildFiles/druid/druid.PlaguewolfBuild.js b/libs/SoloPlay/BuildFiles/druid/druid.PlaguewolfBuild.js index 2ae3f266..4a81dc5c 100644 --- a/libs/SoloPlay/BuildFiles/druid/druid.PlaguewolfBuild.js +++ b/libs/SoloPlay/BuildFiles/druid/druid.PlaguewolfBuild.js @@ -106,7 +106,7 @@ const finalBuild = { }, respec: function () { - return me.haveAll([{name: sdk.locale.items.Grief}, {name: sdk.locale.items.ChainsofHonor}]); + return me.haveAll([{ name: sdk.locale.items.Grief }, { name: sdk.locale.items.ChainsofHonor }]); }, active: function () { diff --git a/libs/SoloPlay/BuildFiles/druid/druid.StormbearBuild.js b/libs/SoloPlay/BuildFiles/druid/druid.StormbearBuild.js index 5659170c..49813574 100644 --- a/libs/SoloPlay/BuildFiles/druid/druid.StormbearBuild.js +++ b/libs/SoloPlay/BuildFiles/druid/druid.StormbearBuild.js @@ -117,7 +117,7 @@ const finalBuild = { }, respec: function () { - return me.haveAll([{name: sdk.locale.items.Destruction}, {name: sdk.locale.items.Dragon, itemtype: sdk.items.type.Armor}]); + return me.haveAll([{ name: sdk.locale.items.Destruction }, { name: sdk.locale.items.Dragon, itemtype: sdk.items.type.Armor }]); }, active: function () { diff --git a/libs/SoloPlay/BuildFiles/paladin/paladin.AuradinBuild.js b/libs/SoloPlay/BuildFiles/paladin/paladin.AuradinBuild.js index 4bece3a2..5be86b18 100644 --- a/libs/SoloPlay/BuildFiles/paladin/paladin.AuradinBuild.js +++ b/libs/SoloPlay/BuildFiles/paladin/paladin.AuradinBuild.js @@ -103,7 +103,7 @@ const finalBuild = { Config.AttackSkill = [-1, sdk.skills.Zeal, sdk.skills.Conviction, sdk.skills.Zeal, sdk.skills.Conviction, -1, -1]; Config.LowManaSkill = [-1, -1]; - if (!me.haveSome([{name: sdk.locale.items.Dragon, itemtype: sdk.items.type.Armor}, {name: sdk.locale.items.HandofJustice}])) { + if (!me.haveSome([{ name: sdk.locale.items.Dragon, itemtype: sdk.items.type.Armor }, { name: sdk.locale.items.HandofJustice }])) { Config.SkipImmune = ["lightning and physical"]; } else { Config.SkipImmune = ["lightning and fire and physical"]; // Don't think this ever happens but should skip if it does @@ -119,7 +119,7 @@ const finalBuild = { if (me.classic) { return false; } else { - return me.haveAll([{name: sdk.locale.items.Dream, itemtype: sdk.items.type.AuricShields}, {name: sdk.locale.items.Dream, itemtype: sdk.items.type.Helm}]); + return me.haveAll([{ name: sdk.locale.items.Dream, itemtype: sdk.items.type.AuricShields }, { name: sdk.locale.items.Dream, itemtype: sdk.items.type.Helm }]); } }, diff --git a/libs/SoloPlay/BuildFiles/paladin/paladin.ClassicauradinBuild.js b/libs/SoloPlay/BuildFiles/paladin/paladin.ClassicauradinBuild.js index 3cb8d92c..a6e17c32 100644 --- a/libs/SoloPlay/BuildFiles/paladin/paladin.ClassicauradinBuild.js +++ b/libs/SoloPlay/BuildFiles/paladin/paladin.ClassicauradinBuild.js @@ -138,7 +138,7 @@ const finalBuild = { if (me.classic) { return me.charlvl >= 75 && me.diablo; } else { - return me.haveAll([{name: sdk.locale.items.Dream, itemtype: sdk.items.type.AuricShields}, {name: sdk.locale.items.Dream, itemtype: sdk.items.type.Helm}]); + return me.haveAll([{ name: sdk.locale.items.Dream, itemtype: sdk.items.type.AuricShields }, { name: sdk.locale.items.Dream, itemtype: sdk.items.type.Helm }]); } }, diff --git a/libs/SoloPlay/BuildFiles/paladin/paladin.SancdreamerBuild.js b/libs/SoloPlay/BuildFiles/paladin/paladin.SancdreamerBuild.js index 525501d5..64aa3e36 100644 --- a/libs/SoloPlay/BuildFiles/paladin/paladin.SancdreamerBuild.js +++ b/libs/SoloPlay/BuildFiles/paladin/paladin.SancdreamerBuild.js @@ -117,8 +117,8 @@ const finalBuild = { if (me.classic) { return false; } else { - return me.haveAll([{name: sdk.locale.items.Dream, itemtype: sdk.items.type.AuricShields}, {name: sdk.locale.items.Dream, itemtype: sdk.items.type.Helm}, - {name: sdk.locale.items.LastWish}]); + return me.haveAll([{ name: sdk.locale.items.Dream, itemtype: sdk.items.type.AuricShields }, { name: sdk.locale.items.Dream, itemtype: sdk.items.type.Helm }, + { name: sdk.locale.items.LastWish }]); } }, diff --git a/libs/SoloPlay/BuildFiles/paladin/paladin.SmiterBuild.js b/libs/SoloPlay/BuildFiles/paladin/paladin.SmiterBuild.js index 14aa4cc2..dd022329 100644 --- a/libs/SoloPlay/BuildFiles/paladin/paladin.SmiterBuild.js +++ b/libs/SoloPlay/BuildFiles/paladin/paladin.SmiterBuild.js @@ -105,7 +105,7 @@ const finalBuild = { if (me.classic) { return me.charlvl >= 75 && me.diablo; } else { - return me.checkItem({name: sdk.locale.items.Grief, itemtype: sdk.items.type.Sword}).have; + return me.checkItem({ name: sdk.locale.items.Grief, itemtype: sdk.items.type.Sword }).have; } }, diff --git a/libs/SoloPlay/BuildFiles/paladin/paladin.TorchadinBuild.js b/libs/SoloPlay/BuildFiles/paladin/paladin.TorchadinBuild.js index b34b9b07..2d3821ff 100644 --- a/libs/SoloPlay/BuildFiles/paladin/paladin.TorchadinBuild.js +++ b/libs/SoloPlay/BuildFiles/paladin/paladin.TorchadinBuild.js @@ -117,7 +117,7 @@ const finalBuild = { if (me.classic) { return false; } else { - return me.haveAll([{name: sdk.locale.items.HandofJustice}, {name: sdk.locale.items.Dragon, itemtype: sdk.items.type.Armor}]); + return me.haveAll([{ name: sdk.locale.items.HandofJustice }, { name: sdk.locale.items.Dragon, itemtype: sdk.items.type.Armor }]); } }, diff --git a/libs/SoloPlay/BuildFiles/paladin/paladin.ZealerBuild.js b/libs/SoloPlay/BuildFiles/paladin/paladin.ZealerBuild.js index 922c8bfe..bebd93ab 100644 --- a/libs/SoloPlay/BuildFiles/paladin/paladin.ZealerBuild.js +++ b/libs/SoloPlay/BuildFiles/paladin/paladin.ZealerBuild.js @@ -113,7 +113,7 @@ const finalBuild = { if (me.classic) { return me.charlvl >= 75 && me.diablo; } else { - return me.checkItem({name: sdk.locale.items.Grief, itemtype: sdk.items.type.Sword}).have; + return me.checkItem({ name: sdk.locale.items.Grief, itemtype: sdk.items.type.Sword }).have; } }, diff --git a/libs/SoloPlay/Config/Amazon.js b/libs/SoloPlay/Config/Amazon.js index b9996263..19c07773 100644 --- a/libs/SoloPlay/Config/Amazon.js +++ b/libs/SoloPlay/Config/Amazon.js @@ -111,7 +111,7 @@ Config.MaxAttackCount = 1000; Config.BossPriority = false; Config.ClearType = 0; - Config.ClearPath = {Range: (Pather.canTeleport() ? 30 : 20), Spectype: sdk.monsters.spectype.All}; + Config.ClearPath = { Range: (Pather.canTeleport() ? 30 : 20), Spectype: sdk.monsters.spectype.All }; // Class specific config Config.LightningFuryDelay = 10; // Lightning fury interval in seconds. LF is treated as timed skill. @@ -123,12 +123,12 @@ NTIP.buildFinalGear(finalGear); Config.imbueables = [ - {name: sdk.items.MaidenJavelin, condition: () => me.normal && me.expansion}, - {name: sdk.items.CeremonialJavelin, condition: () => !me.normal && (me.charlvl < 48 || me.trueStr < 107 || me.trueDex < 151) && me.expansion}, - {name: sdk.items.MatriarchalJavelin, condition: () => (Item.getEquipped(sdk.body.RightArm).tier < 100000 && me.trueStr >= 107 && me.trueDex >= 151 && me.expansion)}, - {name: sdk.items.Belt, condition: () => (me.normal && (Item.getEquipped(sdk.body.RightArm).tier > 100000 || me.classic))}, - {name: sdk.items.MeshBelt, condition: () => (!me.normal && me.charlvl < 46 && me.trueStr > 58 && (Item.getEquipped(sdk.body.RightArm).tier > 100000 || me.classic))}, - {name: sdk.items.SpiderwebSash, condition: () => (!me.normal && me.trueStr > 50 && (Item.getEquipped(sdk.body.RightArm).tier > 100000 || me.classic))}, + { name: sdk.items.MaidenJavelin, condition: () => me.normal && me.expansion }, + { name: sdk.items.CeremonialJavelin, condition: () => !me.normal && (me.charlvl < 48 || me.trueStr < 107 || me.trueDex < 151) && me.expansion }, + { name: sdk.items.MatriarchalJavelin, condition: () => (Item.getEquipped(sdk.body.RightArm).tier < 100000 && me.trueStr >= 107 && me.trueDex >= 151 && me.expansion) }, + { name: sdk.items.Belt, condition: () => (me.normal && (Item.getEquipped(sdk.body.RightArm).tier > 100000 || me.classic)) }, + { name: sdk.items.MeshBelt, condition: () => (!me.normal && me.charlvl < 46 && me.trueStr > 58 && (Item.getEquipped(sdk.body.RightArm).tier > 100000 || me.classic)) }, + { name: sdk.items.SpiderwebSash, condition: () => (!me.normal && me.trueStr > 50 && (Item.getEquipped(sdk.body.RightArm).tier > 100000 || me.classic)) }, ]; let imbueArr = SetUp.imbueItems(); @@ -192,7 +192,7 @@ )); } - if ((SetUp.finalBuild === "Faithbowzon") && !me.checkItem({name: sdk.locale.items.Faith, classid: sdk.items.GrandMatronBow}).have) { + if ((SetUp.finalBuild === "Faithbowzon") && !me.checkItem({ name: sdk.locale.items.Faith, classid: sdk.items.GrandMatronBow }).have) { includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/Faith.js"); } @@ -246,12 +246,12 @@ } // Call to Arms - if (!me.checkItem({name: sdk.locale.items.CalltoArms}).have) { + if (!me.checkItem({ name: sdk.locale.items.CalltoArms }).have) { includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/CallToArms.js"); } // Chains of Honor - if (!me.checkItem({name: sdk.locale.items.ChainsofHonor}).have) { + if (!me.checkItem({ name: sdk.locale.items.ChainsofHonor }).have) { includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/ChainsOfHonor.js"); } diff --git a/libs/SoloPlay/Config/Assassin.js b/libs/SoloPlay/Config/Assassin.js index 7330fc2e..bff3e3f6 100644 --- a/libs/SoloPlay/Config/Assassin.js +++ b/libs/SoloPlay/Config/Assassin.js @@ -103,7 +103,7 @@ Config.MaxAttackCount = 1000; Config.BossPriority = me.normal; Config.ClearType = 0; - Config.ClearPath = {Range: (Pather.canTeleport() ? 30 : 20), Spectype: 0}; + Config.ClearPath = { Range: (Pather.canTeleport() ? 30 : 20), Spectype: 0 }; /* Class specific configuration. */ Config.UseTraps = true; @@ -128,12 +128,12 @@ NTIP.buildFinalGear(finalGear); Config.imbueables = [ - {name: sdk.items.Claws, condition: () => (me.normal)}, - {name: sdk.items.HandScythe, condition: () => (!me.normal && Item.getEquipped(sdk.body.RightArm).tier < 777 && (me.trueStr < 79 || me.trueDex < 79))}, - {name: sdk.items.GreaterTalons, condition: () => (Item.getEquipped(sdk.body.RightArm).tier < 777 && me.trueStr >= 79 && me.trueDex >= 79)}, - {name: sdk.items.Belt, condition: () => (me.normal && (Item.getEquipped(sdk.body.RightArm).tier > 777))}, - {name: sdk.items.MeshBelt, condition: () => (!me.normal && me.charlvl < 46 && me.trueStr > 58 && (Item.getEquipped(sdk.body.RightArm).tier > 777))}, - {name: sdk.items.SpiderwebSash, condition: () => (!me.normal && me.trueStr > 50 && (Item.getEquipped(sdk.body.RightArm).tier > 777))}, + { name: sdk.items.Claws, condition: () => (me.normal) }, + { name: sdk.items.HandScythe, condition: () => (!me.normal && Item.getEquipped(sdk.body.RightArm).tier < 777 && (me.trueStr < 79 || me.trueDex < 79)) }, + { name: sdk.items.GreaterTalons, condition: () => (Item.getEquipped(sdk.body.RightArm).tier < 777 && me.trueStr >= 79 && me.trueDex >= 79) }, + { name: sdk.items.Belt, condition: () => (me.normal && (Item.getEquipped(sdk.body.RightArm).tier > 777)) }, + { name: sdk.items.MeshBelt, condition: () => (!me.normal && me.charlvl < 46 && me.trueStr > 58 && (Item.getEquipped(sdk.body.RightArm).tier > 777)) }, + { name: sdk.items.SpiderwebSash, condition: () => (!me.normal && me.trueStr > 50 && (Item.getEquipped(sdk.body.RightArm).tier > 777)) }, ].filter((item) => item.condition()); let imbueArr = SetUp.imbueItems(); @@ -162,17 +162,17 @@ } // Chaos - if (!me.checkItem({name: sdk.locale.items.Chaos}).have) { + if (!me.checkItem({ name: sdk.locale.items.Chaos }).have) { includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/Chaos.js"); } // Fury - if (!me.checkItem({name: sdk.locale.items.Fury}).have) { + if (!me.checkItem({ name: sdk.locale.items.Fury }).have) { includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/Fury.js"); } // Fortitude - if ((me.ladder || Developer.addLadderRW) && !me.checkItem({name: sdk.locale.items.Fortitude, itemtype: sdk.items.type.Armor}).have) { + if ((me.ladder || Developer.addLadderRW) && !me.checkItem({ name: sdk.locale.items.Fortitude, itemtype: sdk.items.type.Armor }).have) { includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/Fortitude.js"); } @@ -188,12 +188,12 @@ } // Heart of the Oak - if (!me.checkItem({name: sdk.locale.items.HeartoftheOak}).have) { + if (!me.checkItem({ name: sdk.locale.items.HeartoftheOak }).have) { includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/HeartOfTheOak.js"); } // Enigma - if (!me.checkItem({name: sdk.locale.items.Enigma}).have) { + if (!me.checkItem({ name: sdk.locale.items.Enigma }).have) { includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/Enigma.js"); } @@ -213,7 +213,7 @@ } // Call to Arms - if (!me.checkItem({name: sdk.locale.items.CalltoArms}).have) { + if (!me.checkItem({ name: sdk.locale.items.CalltoArms }).have) { includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/CallToArms.js"); } diff --git a/libs/SoloPlay/Config/Barbarian.js b/libs/SoloPlay/Config/Barbarian.js index a4a2818c..ab59f1ae 100644 --- a/libs/SoloPlay/Config/Barbarian.js +++ b/libs/SoloPlay/Config/Barbarian.js @@ -104,7 +104,7 @@ Config.MaxAttackCount = 1000; Config.BossPriority = me.normal; Config.ClearType = 0; - Config.ClearPath = {Range: (Pather.canTeleport() ? 30 : 10), Spectype: 0}; + Config.ClearPath = { Range: (Pather.canTeleport() ? 30 : 10), Spectype: 0 }; // Class specific config Config.FindItem = true; // Use Find Item skill on corpses after clearing. @@ -116,12 +116,12 @@ NTIP.buildFinalGear(finalGear); Config.imbueables = [ - {name: sdk.items.AvengerGuard, condition: () => (me.normal && me.expansion)}, - {name: sdk.items.SlayerGuard, condition: () => (!me.normal && me.trueStr >= 118 && me.expansion)}, - {name: sdk.items.CarnageHelm, condition: () => (Item.getEquipped(sdk.body.Head).tier < 100000 && me.trueStr >= 106 && me.expansion)}, - {name: sdk.items.Belt, condition: () => (me.normal && (Item.getEquipped(sdk.body.Head).tier > 100000 || me.classic))}, - {name: sdk.items.MeshBelt, condition: () => (!me.normal && me.charlvl < 46 && me.trueStr > 58 && (Item.getEquipped(sdk.body.RightArm).tier > 100000 || me.classic))}, - {name: sdk.items.SpiderwebSash, condition: () => (!me.normal && me.trueStr > 50 && (Item.getEquipped(sdk.body.RightArm).tier > 100000 || me.classic))}, + { name: sdk.items.AvengerGuard, condition: () => (me.normal && me.expansion) }, + { name: sdk.items.SlayerGuard, condition: () => (!me.normal && me.trueStr >= 118 && me.expansion) }, + { name: sdk.items.CarnageHelm, condition: () => (Item.getEquipped(sdk.body.Head).tier < 100000 && me.trueStr >= 106 && me.expansion) }, + { name: sdk.items.Belt, condition: () => (me.normal && (Item.getEquipped(sdk.body.Head).tier > 100000 || me.classic)) }, + { name: sdk.items.MeshBelt, condition: () => (!me.normal && me.charlvl < 46 && me.trueStr > 58 && (Item.getEquipped(sdk.body.RightArm).tier > 100000 || me.classic)) }, + { name: sdk.items.SpiderwebSash, condition: () => (!me.normal && me.trueStr > 50 && (Item.getEquipped(sdk.body.RightArm).tier > 100000 || me.classic)) }, ].filter((item) => item.condition()); let imbueArr = SetUp.imbueItems(); @@ -137,10 +137,10 @@ Config.socketables = Config.socketables.concat(basicSocketables.all); Config.socketables.push(addSocketableObj(sdk.items.Flamberge, [], [], - true, (item) => me.normal && Item.getEquipped(sdk.body.LeftArm).tier < 600 && !Check.haveBase("sword", 5) && !me.checkItem({name: sdk.locale.items.Honor}).have && item.ilvl >= 41 && item.isBaseType && !item.ethereal + true, (item) => me.normal && Item.getEquipped(sdk.body.LeftArm).tier < 600 && !Check.haveBase("sword", 5) && !me.checkItem({ name: sdk.locale.items.Honor }).have && item.ilvl >= 41 && item.isBaseType && !item.ethereal )); Config.socketables.push(addSocketableObj(sdk.items.Zweihander, [], [], - true, (item) => Item.getEquipped(sdk.body.LeftArm).tier < 1000 && !Check.haveBase("sword", 5) && !me.checkItem({name: sdk.locale.items.Honor}).have && item.ilvl >= 41 && item.isBaseType && !item.ethereal + true, (item) => Item.getEquipped(sdk.body.LeftArm).tier < 1000 && !Check.haveBase("sword", 5) && !me.checkItem({ name: sdk.locale.items.Honor }).have && item.ilvl >= 41 && item.isBaseType && !item.ethereal )); if (SetUp.finalBuild !== "Immortalwhirl") { @@ -151,12 +151,12 @@ if (["Immortalwhirl", "Singer"].indexOf(SetUp.finalBuild) === -1) { // Grief - if ((me.ladder || Developer.addLadderRW) && (!me.checkItem({name: sdk.locale.items.Grief}).have || (SetUp.finalBuild === "Whirlwind" && Item.getEquipped(sdk.body.LeftArm).prefixnum !== sdk.locale.items.Grief))) { + if ((me.ladder || Developer.addLadderRW) && (!me.checkItem({ name: sdk.locale.items.Grief }).have || (SetUp.finalBuild === "Whirlwind" && Item.getEquipped(sdk.body.LeftArm).prefixnum !== sdk.locale.items.Grief))) { includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/Grief.js"); } // Fortitude - if ((me.ladder || Developer.addLadderRW) && SetUp.finalBuild !== "Uberconc" && me.checkItem({name: sdk.locale.items.Grief}).have && !me.checkItem({name: sdk.locale.items.Fortitude, itemtype: sdk.items.type.Armor}).have) { + if ((me.ladder || Developer.addLadderRW) && SetUp.finalBuild !== "Uberconc" && me.checkItem({ name: sdk.locale.items.Grief }).have && !me.checkItem({ name: sdk.locale.items.Fortitude, itemtype: sdk.items.type.Armor }).have) { includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/Fortitude.js"); } @@ -169,32 +169,32 @@ // FinalBuild specific setup switch (SetUp.finalBuild) { case "Uberconc": - if (me.checkItem({name: sdk.locale.items.Grief}).have && SetUp.finalBuild === "Uberconc") { + if (me.checkItem({ name: sdk.locale.items.Grief }).have && SetUp.finalBuild === "Uberconc") { // Add Stormshield NTIP.addLine("[name] == monarch && [quality] == unique && [flag] != ethereal # [damageresist] >= 35 # [tier] == 100000"); } // Chains of Honor - if (!me.checkItem({name: sdk.locale.items.ChainsofHonor}).have) { + if (!me.checkItem({ name: sdk.locale.items.ChainsofHonor }).have) { includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/ChainsOfHonor.js"); } break; case "Frenzy": // Breathe of the Dying - if (!me.checkItem({name: sdk.locale.items.BreathoftheDying}).have) { + if (!me.checkItem({ name: sdk.locale.items.BreathoftheDying }).have) { includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/BreathoftheDying.js"); } break; case "Singer": // Heart of the Oak - if (Item.getEquipped(sdk.body.LeftArm).prefixnum !== sdk.locale.items.HeartoftheOak && me.checkItem({name: sdk.locale.items.Enigma}).have) { + if (Item.getEquipped(sdk.body.LeftArm).prefixnum !== sdk.locale.items.HeartoftheOak && me.checkItem({ name: sdk.locale.items.Enigma }).have) { includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/HeartOfTheOak.js"); } // Enigma - if (!me.checkItem({name: sdk.locale.items.Enigma}).have) { + if (!me.checkItem({ name: sdk.locale.items.Enigma }).have) { includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/Enigma.js"); } @@ -337,7 +337,7 @@ } // Duress - if (Item.getEquipped(sdk.body.Armor).tier < 600 && (me.checkItem({name: sdk.locale.items.CrescentMoon}).have || Item.getEquipped(sdk.body.LeftArm).tier > 900)) { + if (Item.getEquipped(sdk.body.Armor).tier < 600 && (me.checkItem({ name: sdk.locale.items.CrescentMoon }).have || Item.getEquipped(sdk.body.LeftArm).tier > 900)) { includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/Duress.js"); } diff --git a/libs/SoloPlay/Config/Druid.js b/libs/SoloPlay/Config/Druid.js index 720c19f3..0e3085aa 100644 --- a/libs/SoloPlay/Config/Druid.js +++ b/libs/SoloPlay/Config/Druid.js @@ -117,7 +117,7 @@ Config.MaxAttackCount = 1000; Config.BossPriority = me.normal; Config.ClearType = 0; - Config.ClearPath = {Range: (Pather.canTeleport() ? 30 : 10), Spectype: 0}; + Config.ClearPath = { Range: (Pather.canTeleport() ? 30 : 10), Spectype: 0 }; /* Class specific configuration. */ /* Wereform */ @@ -135,12 +135,12 @@ NTIP.buildFinalGear(finalGear); Config.imbueables = [ - {name: sdk.items.SpiritMask, condition: () => (me.normal)}, - {name: sdk.items.TotemicMask, condition: () => (!me.normal && Item.getEquipped(sdk.body.Head).tier < 100000 && (me.charlvl < 66 || me.trueStr < 118))}, - {name: sdk.items.DreamSpirit, condition: () => (Item.getEquipped(sdk.body.Head).tier < 100000 && me.trueStr >= 118)}, - {name: sdk.items.Belt, condition: () => (me.normal && (Item.getEquipped(sdk.body.Head).tier > 100000))}, - {name: sdk.items.MeshBelt, condition: () => (!me.normal && me.charlvl < 46 && me.trueStr > 58 && (Item.getEquipped(sdk.body.Head).tier > 100000))}, - {name: sdk.items.SpiderwebSash, condition: () => (!me.normal && me.trueStr > 50 && (Item.getEquipped(sdk.body.Head).tier > 100000))}, + { name: sdk.items.SpiritMask, condition: () => (me.normal) }, + { name: sdk.items.TotemicMask, condition: () => (!me.normal && Item.getEquipped(sdk.body.Head).tier < 100000 && (me.charlvl < 66 || me.trueStr < 118)) }, + { name: sdk.items.DreamSpirit, condition: () => (Item.getEquipped(sdk.body.Head).tier < 100000 && me.trueStr >= 118) }, + { name: sdk.items.Belt, condition: () => (me.normal && (Item.getEquipped(sdk.body.Head).tier > 100000)) }, + { name: sdk.items.MeshBelt, condition: () => (!me.normal && me.charlvl < 46 && me.trueStr > 58 && (Item.getEquipped(sdk.body.Head).tier > 100000)) }, + { name: sdk.items.SpiderwebSash, condition: () => (!me.normal && me.trueStr > 50 && (Item.getEquipped(sdk.body.Head).tier > 100000)) }, ].filter((item) => item.condition()); let imbueArr = SetUp.imbueItems(); @@ -160,7 +160,7 @@ true, (item) => item.unique && !item.ethereal )); - if (me.checkItem({name: sdk.locale.items.CalltoArms}).have) { + if (me.checkItem({ name: sdk.locale.items.CalltoArms }).have) { // Spirit on swap NTIP.addLine("[name] == monarch && [flag] == runeword # [fcr] >= 25 && [maxmana] >= 89 # [secondarytier] == 110000"); } @@ -179,7 +179,7 @@ case "Wind": case "Elemental": // Call to Arms - if (!me.checkItem({name: sdk.locale.items.CalltoArms}).have) { + if (!me.checkItem({ name: sdk.locale.items.CalltoArms }).have) { includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/CallToArms.js"); } @@ -189,18 +189,18 @@ )); // Phoenix Shield - if ((me.ladder || Developer.addLadderRW) && SetUp.finalBuild === "Elemental" && me.checkItem({name: sdk.locale.items.Enigma}).have && !me.checkItem({name: sdk.locale.items.Phoenix, itemtype: sdk.items.type.Shield}).have) { + if ((me.ladder || Developer.addLadderRW) && SetUp.finalBuild === "Elemental" && me.checkItem({ name: sdk.locale.items.Enigma }).have && !me.checkItem({ name: sdk.locale.items.Phoenix, itemtype: sdk.items.type.Shield }).have) { includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/PhoneixShield.js"); } } // Heart of the Oak - if (!me.checkItem({name: sdk.locale.items.HeartoftheOak}).have) { + if (!me.checkItem({ name: sdk.locale.items.HeartoftheOak }).have) { includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/HeartOfTheOak.js"); } // Enigma - if (!me.checkItem({name: sdk.locale.items.Enigma}).have) { + if (!me.checkItem({ name: sdk.locale.items.Enigma }).have) { includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/Enigma.js"); } @@ -214,18 +214,18 @@ case "Wolf": case "Plaguewolf": // Chains of Honor - if (!me.checkItem({name: sdk.locale.items.ChainsofHonor}).have) { + if (!me.checkItem({ name: sdk.locale.items.ChainsofHonor }).have) { includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/ChainsOfHonor.js"); } if (SetUp.finalBuild === "Plaguewolf") { // Grief - if (!me.checkItem({name: sdk.locale.items.Grief}).have) { + if (!me.checkItem({ name: sdk.locale.items.Grief }).have) { includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/Grief.js"); } } else { // Make sure to have CoH first - if (me.checkItem({name: sdk.locale.items.ChainsofHonor}).have) { + if (me.checkItem({ name: sdk.locale.items.ChainsofHonor }).have) { // Upgrade Ribcracker to Elite Config.Recipes.push([Recipe.Unique.Weapon.ToElite, "quarterstaff", Roll.NonEth]); } diff --git a/libs/SoloPlay/Config/Necromancer.js b/libs/SoloPlay/Config/Necromancer.js index 54ab6de4..6b7e50c0 100644 --- a/libs/SoloPlay/Config/Necromancer.js +++ b/libs/SoloPlay/Config/Necromancer.js @@ -110,7 +110,7 @@ Config.MaxAttackCount = 1000; Config.BossPriority = me.normal; Config.ClearType = 0; - Config.ClearPath = {Range: (Pather.canTeleport() ? 30 : 20), Spectype: 0}; + Config.ClearPath = { Range: (Pather.canTeleport() ? 30 : 20), Spectype: 0 }; /* Class specific configuration. */ Config.Dodge = Check.haveItem("armor", "runeword", "Enigma"); @@ -135,12 +135,12 @@ NTIP.buildFinalGear(finalGear); Config.imbueables = [ - {name: sdk.items.DemonHead, condition: () => (me.normal && me.expansion)}, - {name: sdk.items.HierophantTrophy, condition: () => (!me.normal && (me.charlvl < 66 || me.trueStr < 106) && me.expansion)}, - {name: sdk.items.BloodlordSkull, condition: () => (Item.getEquipped(sdk.body.LeftArm).tier < 1000 && me.expansion)}, - {name: sdk.items.Belt, condition: () => (me.normal && (Item.getEquipped(sdk.body.LeftArm).tier > 1000 || me.classic))}, - {name: sdk.items.MeshBelt, condition: () => (!me.normal && me.charlvl < 46 && me.trueStr > 58 && (Item.getEquipped(sdk.body.LeftArm).tier > 1000 || me.classic))}, - {name: sdk.items.SpiderwebSash, condition: () => (!me.normal && me.trueStr > 50 && (Item.getEquipped(sdk.body.LeftArm).tier > 1000 || me.classic))}, + { name: sdk.items.DemonHead, condition: () => (me.normal && me.expansion) }, + { name: sdk.items.HierophantTrophy, condition: () => (!me.normal && (me.charlvl < 66 || me.trueStr < 106) && me.expansion) }, + { name: sdk.items.BloodlordSkull, condition: () => (Item.getEquipped(sdk.body.LeftArm).tier < 1000 && me.expansion) }, + { name: sdk.items.Belt, condition: () => (me.normal && (Item.getEquipped(sdk.body.LeftArm).tier > 1000 || me.classic)) }, + { name: sdk.items.MeshBelt, condition: () => (!me.normal && me.charlvl < 46 && me.trueStr > 58 && (Item.getEquipped(sdk.body.LeftArm).tier > 1000 || me.classic)) }, + { name: sdk.items.SpiderwebSash, condition: () => (!me.normal && me.trueStr > 50 && (Item.getEquipped(sdk.body.LeftArm).tier > 1000 || me.classic)) }, ].filter((item) => item.condition()); let imbueArr = SetUp.imbueItems(); @@ -191,7 +191,7 @@ } // Call to Arms - if (!me.checkItem({name: sdk.locale.items.CalltoArms}).have) { + if (!me.checkItem({ name: sdk.locale.items.CalltoArms }).have) { includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/CallToArms.js"); } @@ -206,7 +206,7 @@ } // Enigma - if (!me.checkItem({name: sdk.locale.items.Enigma}).have) { + if (!me.checkItem({ name: sdk.locale.items.Enigma }).have) { includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/Enigma.js"); } @@ -225,7 +225,7 @@ includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/MercInsight.js"); } - if (!me.haveSome([{name: sdk.locale.items.Enigma}, {name: sdk.locale.items.Bone}]) && Item.getEquipped(sdk.body.Armor).tier < 650) { + if (!me.haveSome([{ name: sdk.locale.items.Enigma }, { name: sdk.locale.items.Bone }]) && Item.getEquipped(sdk.body.Armor).tier < 650) { includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/Bone.js"); } diff --git a/libs/SoloPlay/Config/Paladin.js b/libs/SoloPlay/Config/Paladin.js index f7bab559..72cbff72 100644 --- a/libs/SoloPlay/Config/Paladin.js +++ b/libs/SoloPlay/Config/Paladin.js @@ -121,7 +121,7 @@ Config.MaxAttackCount = 1000; Config.BossPriority = me.normal; Config.ClearType = 0; - Config.ClearPath = {Range: (Pather.canTeleport() ? 30 : 10), Spectype: 0}; + Config.ClearPath = { Range: (Pather.canTeleport() ? 30 : 10), Spectype: 0 }; /* Class specific configuration. */ Config.AvoidDolls = true; @@ -136,12 +136,12 @@ // Maybe add auric shield? Config.imbueables = [ - {name: sdk.items.WarScepter, condition: () => me.normal}, - {name: sdk.items.DivineScepter, condition: () => (!me.normal && (me.trueStr < 125 || me.trueDex < 60))}, - {name: sdk.items.MightyScepter, condition: () => (Item.getEquipped(sdk.body.RightArm).tier < 777 && (me.trueStr >= 125 || me.trueDex >= 60))}, - {name: sdk.items.Belt, condition: () => (me.normal && (Item.getEquipped(sdk.body.RightArm).tier > 777 || me.classic))}, - {name: sdk.items.MeshBelt, condition: () => (!me.normal && me.charlvl < 46 && me.trueStr > 58 && (Item.getEquipped(sdk.body.RightArm).tier > 777 || me.classic))}, - {name: sdk.items.SpiderwebSash, condition: () => (!me.normal && me.trueStr > 50 && (Item.getEquipped(sdk.body.RightArm).tier > 777 || me.classic))}, + { name: sdk.items.WarScepter, condition: () => me.normal }, + { name: sdk.items.DivineScepter, condition: () => (!me.normal && (me.trueStr < 125 || me.trueDex < 60)) }, + { name: sdk.items.MightyScepter, condition: () => (Item.getEquipped(sdk.body.RightArm).tier < 777 && (me.trueStr >= 125 || me.trueDex >= 60)) }, + { name: sdk.items.Belt, condition: () => (me.normal && (Item.getEquipped(sdk.body.RightArm).tier > 777 || me.classic)) }, + { name: sdk.items.MeshBelt, condition: () => (!me.normal && me.charlvl < 46 && me.trueStr > 58 && (Item.getEquipped(sdk.body.RightArm).tier > 777 || me.classic)) }, + { name: sdk.items.SpiderwebSash, condition: () => (!me.normal && me.trueStr > 50 && (Item.getEquipped(sdk.body.RightArm).tier > 777 || me.classic)) }, ]; let imbueArr = SetUp.imbueItems(); @@ -178,7 +178,7 @@ Config.Recipes.push([Recipe.Unique.Armor.ToExceptional, "Light Gauntlets", Roll.NonEth]); } - if (me.checkItem({name: sdk.locale.items.CalltoArms}).have) { + if (me.checkItem({ name: sdk.locale.items.CalltoArms }).have) { // Spirit on swap NTIP.addLine("[type] == auricshields && [flag] == runeword # [fcr] >= 25 && [maxmana] >= 89 # [secondarytier] == 110000"); } @@ -190,7 +190,7 @@ case "Smiter": case "Zealer": // Grief - if ((me.ladder || Developer.addLadderRW) && !me.checkItem({name: sdk.locale.items.Grief}).have) { + if ((me.ladder || Developer.addLadderRW) && !me.checkItem({ name: sdk.locale.items.Grief }).have) { includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/Grief.js"); } @@ -211,12 +211,12 @@ } // Exile - if (!me.checkItem({name: sdk.locale.items.Exile, itemtype: sdk.items.type.AuricShields}).have) { + if (!me.checkItem({ name: sdk.locale.items.Exile, itemtype: sdk.items.type.AuricShields }).have) { includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/Exile.js"); } // Fortitude - if ((me.ladder || Developer.addLadderRW) && !me.checkItem({name: sdk.locale.items.Fortitude, itemtype: sdk.items.type.Armor}).have) { + if ((me.ladder || Developer.addLadderRW) && !me.checkItem({ name: sdk.locale.items.Fortitude, itemtype: sdk.items.type.Armor }).have) { includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/Fortitude.js"); } } @@ -224,7 +224,7 @@ break; case "Hammerdin": // Heart of the Oak - if (!me.checkItem({name: sdk.locale.items.HeartoftheOak}).have) { + if (!me.checkItem({ name: sdk.locale.items.HeartoftheOak }).have) { includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/HeartOfTheOak.js"); } @@ -235,22 +235,22 @@ break; case "Auradin": - dreamerCheck = me.haveAll([{name: sdk.locale.items.Dream, itemtype: sdk.items.type.AuricShields}, {name: sdk.locale.items.Dream, itemtype: sdk.items.type.Helm}]); + dreamerCheck = me.haveAll([{ name: sdk.locale.items.Dream, itemtype: sdk.items.type.AuricShields }, { name: sdk.locale.items.Dream, itemtype: sdk.items.type.Helm }]); // Dream Shield - if ((me.ladder || Developer.addLadderRW) && !me.checkItem({name: sdk.locale.items.Dream, itemtype: sdk.items.type.AuricShields}).have) { + if ((me.ladder || Developer.addLadderRW) && !me.checkItem({ name: sdk.locale.items.Dream, itemtype: sdk.items.type.AuricShields }).have) { includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/DreamShield.js"); } // Dream Helm - if ((me.ladder || Developer.addLadderRW) && !me.checkItem({name: sdk.locale.items.Dream, itemtype: sdk.items.type.Helm}).have) { + if ((me.ladder || Developer.addLadderRW) && !me.checkItem({ name: sdk.locale.items.Dream, itemtype: sdk.items.type.Helm }).have) { includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/DreamHelm.js"); } if ((me.ladder || Developer.addLadderRW) && !dreamerCheck) { // Cube to Jah rune if (!me.getItem(sdk.items.runes.Jah)) { - if (me.checkItem({name: sdk.locale.items.CalltoArms}).have) { + if (me.checkItem({ name: sdk.locale.items.CalltoArms }).have) { Config.Recipes.push([Recipe.Rune, "Mal Rune"]); Config.Recipes.push([Recipe.Rune, "Ist Rune"]); Config.Recipes.push([Recipe.Rune, "Gul Rune"]); @@ -265,7 +265,7 @@ } - if (!me.checkItem({name: sdk.locale.items.CalltoArms}).have) { + if (!me.checkItem({ name: sdk.locale.items.CalltoArms }).have) { // Cube to Mal rune if (!me.getItem(sdk.items.runes.Mal) && Item.getEquipped(sdk.body.RightArm).tier >= 110000) { Config.Recipes.push([Recipe.Rune, "Um Rune"]); @@ -279,52 +279,52 @@ } // Dragon Armor - if ((me.ladder || Developer.addLadderRW) && !me.checkItem({name: sdk.locale.items.Dragon, itemtype: sdk.items.type.Armor}).have && dreamerCheck) { + if ((me.ladder || Developer.addLadderRW) && !me.checkItem({ name: sdk.locale.items.Dragon, itemtype: sdk.items.type.Armor }).have && dreamerCheck) { includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/DragonArmor.js"); } - if (!me.checkItem({name: sdk.locale.items.HandofJustice}).have && dreamerCheck) { + if (!me.checkItem({ name: sdk.locale.items.HandofJustice }).have && dreamerCheck) { includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/HandOfJustice.js"); // Azurewrath NTIP.addLine("[name] == phaseblade && [flag] != ethereal && [quality] == unique # [enhanceddamage] >= 230 && [sanctuaryaura] >= 10 # [tier] == 115000"); } - if (!me.haveAll([{name: sdk.locale.items.CrescentMoon}, {name: sdk.locale.items.HandofJustice}]) && dreamerCheck) { + if (!me.haveAll([{ name: sdk.locale.items.CrescentMoon }, { name: sdk.locale.items.HandofJustice }]) && dreamerCheck) { includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/CrescentMoon.js"); // Lightsabre NTIP.addLine("[name] == phaseblade && [flag] != ethereal && [quality] == unique # [enhanceddamage] >= 150 && [itemabsorblightpercent] == 25 # [tier] == 105000"); } - if (!me.checkItem({name: sdk.locale.items.VoiceofReason}).have && !me.haveSome([{name: sdk.locale.items.CrescentMoon}, {name: sdk.locale.items.HandofJustice}]) && dreamerCheck) { + if (!me.checkItem({ name: sdk.locale.items.VoiceofReason }).have && !me.haveSome([{ name: sdk.locale.items.CrescentMoon }, { name: sdk.locale.items.HandofJustice }]) && dreamerCheck) { includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/VoiceOfReason.js"); } if ((me.ladder || Developer.addLadderRW) && Item.getMercEquipped(sdk.body.Armor).prefixnum !== sdk.locale.items.Fortitude - && me.haveAll([{name: sdk.locale.items.HandofJustice}, {name: sdk.locale.items.Dragon, itemtype: sdk.items.type.Armor}, - {name: sdk.locale.items.Dream, itemtype: sdk.items.type.AuricShields}, {name: sdk.locale.items.Dream, itemtype: sdk.items.type.Helm}])) { + && me.haveAll([{ name: sdk.locale.items.HandofJustice }, { name: sdk.locale.items.Dragon, itemtype: sdk.items.type.Armor }, + { name: sdk.locale.items.Dream, itemtype: sdk.items.type.AuricShields }, { name: sdk.locale.items.Dream, itemtype: sdk.items.type.Helm }])) { includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/MercFortitude.js"); } break; case "Sancdreamer": - dreamerCheck = me.haveAll([{name: sdk.locale.items.Dream, itemtype: sdk.items.type.AuricShields}, {name: sdk.locale.items.Dream, itemtype: sdk.items.type.Helm}]); + dreamerCheck = me.haveAll([{ name: sdk.locale.items.Dream, itemtype: sdk.items.type.AuricShields }, { name: sdk.locale.items.Dream, itemtype: sdk.items.type.Helm }]); // Dream Shield - if ((me.ladder || Developer.addLadderRW) && !me.checkItem({name: sdk.locale.items.Dream, itemtype: sdk.items.type.AuricShields}).have) { + if ((me.ladder || Developer.addLadderRW) && !me.checkItem({ name: sdk.locale.items.Dream, itemtype: sdk.items.type.AuricShields }).have) { includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/DreamShield.js"); } // Dream Helm - if ((me.ladder || Developer.addLadderRW) && !me.checkItem({name: sdk.locale.items.Dream, itemtype: sdk.items.type.Helm}).have) { + if ((me.ladder || Developer.addLadderRW) && !me.checkItem({ name: sdk.locale.items.Dream, itemtype: sdk.items.type.Helm }).have) { includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/DreamHelm.js"); } if ((me.ladder || Developer.addLadderRW) && !dreamerCheck) { // Cube to Jah rune if (!me.getItem(sdk.items.runes.Jah)) { - if (me.checkItem({name: sdk.locale.items.CalltoArms}).have) { + if (me.checkItem({ name: sdk.locale.items.CalltoArms }).have) { Config.Recipes.push([Recipe.Rune, "Mal Rune"]); Config.Recipes.push([Recipe.Rune, "Ist Rune"]); Config.Recipes.push([Recipe.Rune, "Gul Rune"]); @@ -338,7 +338,7 @@ } } - if (!me.checkItem({name: sdk.locale.items.CalltoArms}).have) { + if (!me.checkItem({ name: sdk.locale.items.CalltoArms }).have) { // Cube to Mal rune if (!me.getItem(sdk.items.runes.Mal) && Item.getEquipped(sdk.body.RightArm).tier >= 110000) { Config.Recipes.push([Recipe.Rune, "Um Rune"]); @@ -352,27 +352,27 @@ } // Chains of Honor - if (!me.checkItem({name: sdk.locale.items.ChainsofHonor}).have) { + if (!me.checkItem({ name: sdk.locale.items.ChainsofHonor }).have) { includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/ChainsOfHonor.js"); } - if (!me.checkItem({name: sdk.locale.items.LastWish}).have && dreamerCheck) { + if (!me.checkItem({ name: sdk.locale.items.LastWish }).have && dreamerCheck) { includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/LastWish.js"); } - if (!me.haveAll([{name: sdk.locale.items.CrescentMoon}, {name: sdk.locale.items.LastWish}]) && dreamerCheck) { + if (!me.haveAll([{ name: sdk.locale.items.CrescentMoon }, { name: sdk.locale.items.LastWish }]) && dreamerCheck) { includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/CrescentMoon.js"); // Lightsabre NTIP.addLine("[name] == phaseblade && [flag] != ethereal && [quality] == unique # [enhanceddamage] >= 150 && [itemabsorblightpercent] == 25 # [tier] == 105000"); } - if (!me.checkItem({name: sdk.locale.items.VoiceofReason}).have && !me.haveSome([{name: sdk.locale.items.CrescentMoon}, {name: sdk.locale.items.LastWish}]) && dreamerCheck) { + if (!me.checkItem({ name: sdk.locale.items.VoiceofReason }).have && !me.haveSome([{ name: sdk.locale.items.CrescentMoon }, { name: sdk.locale.items.LastWish }]) && dreamerCheck) { includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/VoiceOfReason.js"); } - if ((me.ladder || Developer.addLadderRW) && me.haveAll([{name: sdk.locale.items.LastWish}, {name: sdk.locale.items.ChainsofHonor}, - {name: sdk.locale.items.Dream, itemtype: sdk.items.type.AuricShields}, {name: sdk.locale.items.Dream, itemtype: sdk.items.type.Helm}])) { + if ((me.ladder || Developer.addLadderRW) && me.haveAll([{ name: sdk.locale.items.LastWish }, { name: sdk.locale.items.ChainsofHonor }, + { name: sdk.locale.items.Dream, itemtype: sdk.items.type.AuricShields }, { name: sdk.locale.items.Dream, itemtype: sdk.items.type.Helm }])) { // Infinity if ((me.ladder || Developer.addLadderRW) && Item.getMercEquipped(sdk.body.RightArm).prefixnum !== sdk.locale.items.Infinity) { if (!isIncluded("SoloPlay/BuildFiles/Runewords/MercInfinity.js")) { @@ -390,11 +390,11 @@ break; case "Torchadin": // Dragon Armor - if ((me.ladder || Developer.addLadderRW) && !me.checkItem({name: sdk.locale.items.Dragon, itemtype: sdk.items.type.Armor}).have) { + if ((me.ladder || Developer.addLadderRW) && !me.checkItem({ name: sdk.locale.items.Dragon, itemtype: sdk.items.type.Armor }).have) { includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/DragonArmor.js"); } - if (!me.checkItem({name: sdk.locale.items.HandofJustice}).have) { + if (!me.checkItem({ name: sdk.locale.items.HandofJustice }).have) { includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/HandOfJustice.js"); // Azurewrath @@ -402,31 +402,31 @@ } // Exile - if ((me.ladder || Developer.addLadderRW) && !me.checkItem({name: sdk.locale.items.Exile, itemtype: sdk.items.type.AuricShields}).have) { + if ((me.ladder || Developer.addLadderRW) && !me.checkItem({ name: sdk.locale.items.Exile, itemtype: sdk.items.type.AuricShields }).have) { includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/Exile.js"); } - if ((me.ladder || Developer.addLadderRW) && !me.haveAll([{name: sdk.locale.items.HandofJustice}, - {name: sdk.locale.items.Exile, itemtype: sdk.items.type.AuricShields}, {name: sdk.locale.items.Dragon, itemtype: sdk.items.type.Armor}])) { + if ((me.ladder || Developer.addLadderRW) && !me.haveAll([{ name: sdk.locale.items.HandofJustice }, + { name: sdk.locale.items.Exile, itemtype: sdk.items.type.AuricShields }, { name: sdk.locale.items.Dragon, itemtype: sdk.items.type.Armor }])) { // Cube to Cham rune if (!me.getItem(sdk.items.runes.Cham) || !me.getItem(sdk.items.runes.Sur) || !me.getItem(sdk.items.runes.Lo)) { - if (me.checkItem({name: sdk.locale.items.CalltoArms}).have) { + if (me.checkItem({ name: sdk.locale.items.CalltoArms }).have) { Config.Recipes.push([Recipe.Rune, "Mal Rune"]); Config.Recipes.push([Recipe.Rune, "Ist Rune"]); Config.Recipes.push([Recipe.Rune, "Gul Rune"]); - if (me.checkItem({name: sdk.locale.items.Exile, itemtype: sdk.items.type.AuricShields}).have) { + if (me.checkItem({ name: sdk.locale.items.Exile, itemtype: sdk.items.type.AuricShields }).have) { Config.Recipes.push([Recipe.Rune, "Vex Rune"]); Config.Recipes.push([Recipe.Rune, "Ohm Rune"]); - } else if (!me.checkItem({name: sdk.locale.items.Exile, itemtype: sdk.items.type.AuricShields}).have && !me.getItem(sdk.items.runes.Ohm)) { + } else if (!me.checkItem({ name: sdk.locale.items.Exile, itemtype: sdk.items.type.AuricShields }).have && !me.getItem(sdk.items.runes.Ohm)) { Config.Recipes.push([Recipe.Rune, "Vex Rune"]); } } - if (me.checkItem({name: sdk.locale.items.Dragon, itemtype: sdk.items.type.Armor}).have) { + if (me.checkItem({ name: sdk.locale.items.Dragon, itemtype: sdk.items.type.Armor }).have) { Config.Recipes.push([Recipe.Rune, "Lo Rune"]); Config.Recipes.push([Recipe.Rune, "Sur Rune"]); - } else if ((!me.haveAll([{name: sdk.locale.items.Dragon, itemtype: sdk.items.type.Armor}, {name: sdk.locale.items.HandofJustice}]) && !me.getItem(sdk.items.runes.Sur))) { + } else if ((!me.haveAll([{ name: sdk.locale.items.Dragon, itemtype: sdk.items.type.Armor }, { name: sdk.locale.items.HandofJustice }]) && !me.getItem(sdk.items.runes.Sur))) { Config.Recipes.push([Recipe.Rune, "Lo Rune"]); } @@ -435,7 +435,7 @@ } } - if (!me.checkItem({name: sdk.locale.items.CalltoArms}).have) { + if (!me.checkItem({ name: sdk.locale.items.CalltoArms }).have) { // Cube to Mal rune if (!me.getItem(sdk.items.runes.Mal) && Item.getEquipped(sdk.body.RightArm).tier >= 110000) { Config.Recipes.push([Recipe.Rune, "Um Rune"]); @@ -448,19 +448,19 @@ } } - if (!me.haveAll([{name: sdk.locale.items.CrescentMoon}, {name: sdk.locale.items.HandofJustice}])) { + if (!me.haveAll([{ name: sdk.locale.items.CrescentMoon }, { name: sdk.locale.items.HandofJustice }])) { includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/CrescentMoon.js"); // Lightsabre NTIP.addLine("[name] == phaseblade && [flag] != ethereal && [quality] == unique # [enhanceddamage] >= 150 && [itemabsorblightpercent] == 25 # [tier] == 105000"); } - if (!me.checkItem({name: sdk.locale.items.VoiceofReason}).have && !me.haveSome([{name: sdk.locale.items.CrescentMoon}, {name: sdk.locale.items.HandofJustice}])) { + if (!me.checkItem({ name: sdk.locale.items.VoiceofReason }).have && !me.haveSome([{ name: sdk.locale.items.CrescentMoon }, { name: sdk.locale.items.HandofJustice }])) { includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/VoiceOfReason.js"); } if ((me.ladder || Developer.addLadderRW) && Item.getMercEquipped(sdk.body.Armor).prefixnum !== sdk.locale.items.Fortitude - && me.haveAll([{name: sdk.locale.items.HandofJustice}, {name: sdk.locale.items.Dragon, itemtype: sdk.items.type.Armor}, {name: sdk.locale.items.Exile, itemtype: sdk.items.type.AuricShields}])) { + && me.haveAll([{ name: sdk.locale.items.HandofJustice }, { name: sdk.locale.items.Dragon, itemtype: sdk.items.type.Armor }, { name: sdk.locale.items.Exile, itemtype: sdk.items.type.AuricShields }])) { includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/MercFortitude.js"); } break; @@ -472,12 +472,12 @@ Check.itemSockables(sdk.items.Shako, "unique", "Harlequin Crest"); // Call to Arms - if (!me.checkItem({name: sdk.locale.items.CalltoArms}).have) { + if (!me.checkItem({ name: sdk.locale.items.CalltoArms }).have) { includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/CallToArms.js"); } // Enigma - Don't make if not Smiter or Hammerdin - if (!me.checkItem({name: sdk.locale.items.Enigma}).have && ["Hammerdin", "Smiter"].includes(SetUp.finalBuild)) { + if (!me.checkItem({ name: sdk.locale.items.Enigma }).have && ["Hammerdin", "Smiter"].includes(SetUp.finalBuild)) { includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/Enigma.js"); } @@ -501,6 +501,10 @@ includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/Lore.js"); } + /** + * @todo Rhyme and Splendor + */ + // Ancients' Pledge if (Item.getEquipped(sdk.body.LeftArm).tier < 500) { includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/AncientsPledge.js"); diff --git a/libs/SoloPlay/Config/Sorceress.js b/libs/SoloPlay/Config/Sorceress.js index acd0333b..9373924a 100644 --- a/libs/SoloPlay/Config/Sorceress.js +++ b/libs/SoloPlay/Config/Sorceress.js @@ -118,7 +118,7 @@ Config.MaxAttackCount = 1000; Config.BossPriority = false; Config.ClearType = 0; - Config.ClearPath = {Range: 30, Spectype: (me.hell && Pather.canTeleport() ? 0xF : 0)}; + Config.ClearPath = { Range: 30, Spectype: (me.hell && Pather.canTeleport() ? 0xF : 0) }; /* Monster skip configuration. */ Pather.canTeleport() && me.lightRes < 75 && Config.SkipEnchant.push("lightning enchanted"); @@ -141,12 +141,12 @@ Config.imbueables = [ - {name: sdk.items.JaredsStone, condition: () => (me.normal && me.expansion)}, - {name: sdk.items.SwirlingCrystal, condition: () => (!me.normal && me.charlvl < 66 && me.expansion)}, - {name: sdk.items.DimensionalShard, condition: () => (Item.getEquipped(sdk.body.RightArm).tier < 777 && me.expansion)}, - {name: sdk.items.Belt, condition: () => (me.normal && (Item.getEquipped(sdk.body.RightArm).tier > 777 || me.classic))}, - {name: sdk.items.MeshBelt, condition: () => (!me.normal && me.charlvl < 46 && me.trueStr > 58 && (Item.getEquipped(sdk.body.RightArm).tier > 777 || me.classic))}, - {name: sdk.items.SpiderwebSash, condition: () => (!me.normal && me.trueStr > 50 && (Item.getEquipped(sdk.body.RightArm).tier > 777 || me.classic))}, + { name: sdk.items.JaredsStone, condition: () => (me.normal && me.expansion) }, + { name: sdk.items.SwirlingCrystal, condition: () => (!me.normal && me.charlvl < 66 && me.expansion) }, + { name: sdk.items.DimensionalShard, condition: () => (Item.getEquipped(sdk.body.RightArm).tier < 777 && me.expansion) }, + { name: sdk.items.Belt, condition: () => (me.normal && (Item.getEquipped(sdk.body.RightArm).tier > 777 || me.classic)) }, + { name: sdk.items.MeshBelt, condition: () => (!me.normal && me.charlvl < 46 && me.trueStr > 58 && (Item.getEquipped(sdk.body.RightArm).tier > 777 || me.classic)) }, + { name: sdk.items.SpiderwebSash, condition: () => (!me.normal && me.trueStr > 50 && (Item.getEquipped(sdk.body.RightArm).tier > 777 || me.classic)) }, ].filter((item) => item.condition()); let imbueArr = SetUp.imbueItems(); @@ -198,12 +198,12 @@ } // Chains of Honor - if (!me.checkItem({name: sdk.locale.items.ChainsofHonor}).have) { + if (!me.checkItem({ name: sdk.locale.items.ChainsofHonor }).have) { includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/ChainsOfHonor.js"); } // Heart of the Oak - if (!me.checkItem({name: sdk.locale.items.HeartoftheOak}).have) { + if (!me.checkItem({ name: sdk.locale.items.HeartoftheOak }).have) { includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/HeartOfTheOak.js"); } @@ -239,7 +239,7 @@ } // Go ahead and keep two P-diamonds prior to finding a moser's unless already using a better shield - if (!Check.haveItem("shield", "unique", "Moser's Blessed Circle") && !me.haveSome([{name: sdk.locale.items.Sanctuary}, {name: sdk.locale.items.Spirit, itemtype: sdk.items.type.Shield}])) { + if (!Check.haveItem("shield", "unique", "Moser's Blessed Circle") && !me.haveSome([{ name: sdk.locale.items.Sanctuary }, { name: sdk.locale.items.Spirit, itemtype: sdk.items.type.Shield }])) { NTIP.addLine("[name] == perfectdiamond # # [maxquantity] == 2"); if (Item.getQuantityOwned(me.getItem(sdk.items.gems.Perfect.Diamond)) < 2) { @@ -250,12 +250,12 @@ Check.itemSockables(sdk.items.RoundShield, "unique", "Moser's Blessed Circle"); // Sanctuary - if (!me.haveSome([{name: sdk.locale.items.Sanctuary}, {name: sdk.locale.items.Spirit, itemtype: sdk.items.type.Shield}]) && ["Blova", "Lightning"].indexOf(SetUp.currentBuild) === -1) { + if (!me.haveSome([{ name: sdk.locale.items.Sanctuary }, { name: sdk.locale.items.Spirit, itemtype: sdk.items.type.Shield }]) && ["Blova", "Lightning"].indexOf(SetUp.currentBuild) === -1) { includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/Sanctuary.js"); } // Call to Arms - if (!me.checkItem({name: sdk.locale.items.CalltoArms}).have) { + if (!me.checkItem({ name: sdk.locale.items.CalltoArms }).have) { includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/CallToArms.js"); } diff --git a/libs/SoloPlay/Functions/Globals.js b/libs/SoloPlay/Functions/Globals.js index 4cee9cd7..fd604a4b 100644 --- a/libs/SoloPlay/Functions/Globals.js +++ b/libs/SoloPlay/Functions/Globals.js @@ -397,7 +397,7 @@ const SetUp = { Config.PacketShopping = true; Config.TownCheck = true; Config.LogExperience = false; - Config.PingQuit = [{Ping: 600, Duration: 10}]; + Config.PingQuit = [{ Ping: 600, Duration: 10 }]; Config.Silence = true; Config.OpenChests.Enabled = true; Config.LowGold = me.normal ? 25000 : me.nightmare ? 50000 : 100000; @@ -697,18 +697,18 @@ const Check = { switch (me.diff) { case sdk.difficulty.Normal: // Have runes or stealth and ancients pledge - if (me.haveRunes([sdk.items.runes.Tal, sdk.items.runes.Eth]) || me.checkItem({name: sdk.locale.items.Stealth}).have) { + if (me.haveRunes([sdk.items.runes.Tal, sdk.items.runes.Eth]) || me.checkItem({ name: sdk.locale.items.Stealth }).have) { needRunes = false; } break; case sdk.difficulty.Nightmare: if ((me.haveRunes([sdk.items.runes.Tal, sdk.items.runes.Thul, sdk.items.runes.Ort, sdk.items.runes.Amn]) && Check.currentBuild().caster) - || (!me.paladin && me.checkItem({name: sdk.locale.items.Spirit, itemtype: sdk.items.type.Sword}).have) - || (me.paladin && me.haveAll([{name: sdk.locale.items.Spirit, itemtype: sdk.items.type.Sword}, {name: sdk.locale.items.Spirit, itemtype: sdk.items.type.AuricShields}])) - || (me.necromancer && me.checkItem({name: sdk.locale.items.White}).have - && (me.checkItem({name: sdk.locale.items.Rhyme, itemtype: sdk.items.type.VoodooHeads}).have || Item.getEquipped(sdk.body.LeftArm).tier > 800)) - || (me.barbarian && (me.checkItem({name: sdk.locale.items.Lawbringer}).have || me.baal))) { + || (!me.paladin && me.checkItem({ name: sdk.locale.items.Spirit, itemtype: sdk.items.type.Sword }).have) + || (me.paladin && me.haveAll([{ name: sdk.locale.items.Spirit, itemtype: sdk.items.type.Sword }, { name: sdk.locale.items.Spirit, itemtype: sdk.items.type.AuricShields }])) + || (me.necromancer && me.checkItem({ name: sdk.locale.items.White }).have + && (me.checkItem({ name: sdk.locale.items.Rhyme, itemtype: sdk.items.type.VoodooHeads }).have || Item.getEquipped(sdk.body.LeftArm).tier > 800)) + || (me.barbarian && (me.checkItem({ name: sdk.locale.items.Lawbringer }).have || me.baal))) { needRunes = false; } @@ -1149,9 +1149,9 @@ const SoloWants = { let hel = me.getItemsEx(sdk.items.runes.Hel, sdk.items.mode.inStorage); // we don't have any hel runes and its not already in our needList if ((!hel || hel.length === 0)) { - SoloWants.needList.push({classid: sdk.items.runes.Hel, needed: [sdk.items.runes.Hel]}); + SoloWants.needList.push({ classid: sdk.items.runes.Hel, needed: [sdk.items.runes.Hel] }); } else if (!hel.some(check => SoloWants.validGids.includes(check.gid))) { - SoloWants.needList.push({classid: sdk.items.runes.Hel, needed: [sdk.items.runes.Hel]}); + SoloWants.needList.push({ classid: sdk.items.runes.Hel, needed: [sdk.items.runes.Hel] }); } } } @@ -1175,7 +1175,7 @@ const SoloWants = { } // add to our needList so we pick the items - return list.length > 0 ? this.needList.push({classid: item.classid, needed: list}) : false; + return list.length > 0 ? this.needList.push({ classid: item.classid, needed: list }) : false; }, update: function (item) { diff --git a/libs/SoloPlay/Functions/ItemOverrides.js b/libs/SoloPlay/Functions/ItemOverrides.js index f98669f8..61a1723c 100644 --- a/libs/SoloPlay/Functions/ItemOverrides.js +++ b/libs/SoloPlay/Functions/ItemOverrides.js @@ -489,6 +489,7 @@ Item.secondaryEquip = function (item, bodyLoc) { equipped = true; [sdk.items.Arrows, sdk.items.Bolts].includes(item.classid) && CharData.skillData.bowData.setArrowInfo(item); [sdk.items.type.Bow, sdk.items.type.AmazonBow, sdk.items.type.Crossbow].includes(item.itemType) && CharData.skillData.bowData.setBowInfo(item); + if (getCursorType() === 3) { let cursorItem = Game.getCursorUnit(); !!cursorItem && !cursorItem.shouldKeep() && cursorItem.drop(); diff --git a/libs/SoloPlay/Functions/ItemPrototypes.js b/libs/SoloPlay/Functions/ItemPrototypes.js index 38bbd3f0..9126ec9e 100644 --- a/libs/SoloPlay/Functions/ItemPrototypes.js +++ b/libs/SoloPlay/Functions/ItemPrototypes.js @@ -154,8 +154,7 @@ Unit.prototype.autoEquipCheck = function (checkCanEquip = true) { if (tier > 0 && bodyLoc.length) { for (let i = 0; i < bodyLoc.length; i += 1) { - if (tier > Item.getEquipped(bodyLoc[i]).tier - && ((checkCanEquip ? this.canEquip() : true) || !this.identified)) { + if (tier > Item.getEquipped(bodyLoc[i]).tier && ((checkCanEquip ? this.canEquip() : true) || !this.identified)) { if (this.twoHanded && !me.barbarian) { if (tier < Item.getEquipped(sdk.body.RightArm).tier + Item.getEquipped(sdk.body.LeftArm).tier) return false; } @@ -181,8 +180,7 @@ Unit.prototype.autoEquipCheckSecondary = function (checkCanEquip = true) { if (tier > 0 && bodyLoc.length) { for (let i = 0; i < bodyLoc.length; i += 1) { - if (tier > Item.getEquipped(bodyLoc[i]).secondarytier - && ((checkCanEquip ? this.canEquip() : true) || !this.identified)) { + if (tier > Item.getEquipped(bodyLoc[i]).secondarytier && ((checkCanEquip ? this.canEquip() : true) || !this.identified)) { if (this.twoHanded && !me.barbarian) { if (tier < Item.getEquipped(sdk.body.RightArmSecondary).secondarytier + Item.getEquipped(sdk.body.LeftArmSecondary).secondarytier) return false; } diff --git a/libs/SoloPlay/Functions/ItemUtilities.js b/libs/SoloPlay/Functions/ItemUtilities.js index efe33b07..6760568e 100644 --- a/libs/SoloPlay/Functions/ItemUtilities.js +++ b/libs/SoloPlay/Functions/ItemUtilities.js @@ -7,7 +7,12 @@ includeIfNotIncluded("core/Item.js"); (function () { - // TODO: clean this up (sigh) - 8/10/22 - update refactored alot, still think more can be done though + /** + * @param {ItemUnit} item + * @param {*} buildInfo + * @returns {number} + * @todo clean this up (sigh) - 8/10/22 - update refactored alot, still think more can be done though + */ const baseSkillsScore = (item, buildInfo) => { buildInfo === undefined && (buildInfo = Check.currentBuild()); let generalScore = 0; diff --git a/libs/SoloPlay/Functions/Me.js b/libs/SoloPlay/Functions/Me.js index 56f64899..6ffaecf5 100644 --- a/libs/SoloPlay/Functions/Me.js +++ b/libs/SoloPlay/Functions/Me.js @@ -195,12 +195,12 @@ me.cleanUpInvoPotions = function (beltSize) { beltSize === undefined && (beltSize = Storage.BeltSize()); const beltMax = (beltSize * 4); /** - * belt 4x4 locations - * 12 13 14 15 - * 8 9 10 11 - * 4 5 6 7 - * 0 1 2 3 - */ + * belt 4x4 locations + * 12 13 14 15 + * 8 9 10 11 + * 4 5 6 7 + * 0 1 2 3 + */ const beltCapRef = [(0 + beltMax), (1 + beltMax), (2 + beltMax), (3 + beltMax)]; // check if we have empty belt slots let needCleanup = Storage.Belt.checkColumns(beltSize).some(slot => slot > 0); diff --git a/libs/SoloPlay/Functions/NTIPOverrides.js b/libs/SoloPlay/Functions/NTIPOverrides.js index b59bfdb4..b9452bb0 100644 --- a/libs/SoloPlay/Functions/NTIPOverrides.js +++ b/libs/SoloPlay/Functions/NTIPOverrides.js @@ -430,24 +430,11 @@ NTIP.CheckItem = function (item, entryList, verbose = false) { } if (verbose) { - try { - switch (result) { - case -1: - break; - case 1: - rval.line = stringArr[i].file + " #" + stringArr[i].line; - - break; - default: - rval.line = null; - - break; - } - } catch (e) { - rval.line = null; - } - rval.result = result; + rval.line = (() => { + if (stringArr[i] === undefined) return null; + return result === 1 ? stringArr[i].file + " #" + stringArr[i].line : null; + })(); if (!identified && result === 1) { rval.result = -1; diff --git a/libs/SoloPlay/Functions/PrototypeOverrides.js b/libs/SoloPlay/Functions/PrototypeOverrides.js index 899b4a1e..e22324a3 100644 --- a/libs/SoloPlay/Functions/PrototypeOverrides.js +++ b/libs/SoloPlay/Functions/PrototypeOverrides.js @@ -433,7 +433,7 @@ Unit.prototype.castChargedSkillEx = function (...args) { }; Unit.prototype.castSwitchChargedSkill = function (...args) { - let skillId, x, y, unit, chargedItem; + let skillId, x, y, unit; switch (args.length) { case 0: // item.castChargedSkill() @@ -498,7 +498,7 @@ Unit.prototype.castSwitchChargedSkill = function (...args) { me.weaponswitch === 0 && me.switchWeapons(1); - chargedItem = chargedItems.sort((a, b) => a.charge.level - b.charge.level).first().item; + let chargedItem = chargedItems.sort((a, b) => a.charge.level - b.charge.level).first().item; return chargedItem.castChargedSkillEx.apply(chargedItem, args); } diff --git a/libs/SoloPlay/Threads/ToolsThread.js b/libs/SoloPlay/Threads/ToolsThread.js index daa53dc1..1706ac75 100644 --- a/libs/SoloPlay/Threads/ToolsThread.js +++ b/libs/SoloPlay/Threads/ToolsThread.js @@ -62,7 +62,7 @@ function main () { // General functions this.togglePause = function () { - let scripts = ["libs/SoloPlay/SoloPlay.js", "libs/SoloPlay/Threads/TownChicken.js", "threads/antihostile.js", "threads/party.js"]; + let scripts = ["libs/SoloPlay/SoloPlay.js", "libs/SoloPlay/Threads/TownChicken.js", "threads/party.js"]; for (let l = 0; l < scripts.length; l += 1) { let script = getScript(scripts[l]); @@ -269,8 +269,15 @@ function main () { return false; }; - // Event functions - this.keyEvent = function (key) { + // ~~~~~~~~~~~~~~~ // + // Event functions // + // ~~~~~~~~~~~~~~~ // + + /** + * Handle keyUp events + * @param {number} key + */ + const keyEvent = function (key) { switch (key) { case sdk.keys.PauseBreak: // pause default.dbj this.togglePause(); @@ -397,18 +404,28 @@ function main () { } }; - this.gameEvent = function (mode, param1, param2, name1, name2) { + /** + * Handle game events + * @param {number} mode + * @param {number} [param1] + * @param {number} [param2] + * @param {string} [name1] + * @param {string} [name2] + */ + const gameEvent = function (mode, param1, param2, name1, name2) { switch (mode) { case 0x00: // "%Name1(%Name2) dropped due to time out." case 0x01: // "%Name1(%Name2) dropped due to errors." case 0x03: // "%Name1(%Name2) left our world. Diablo's minions weaken." - if ((typeof Config.QuitList === "string" && Config.QuitList.toLowerCase() === "any") - || (Config.QuitList instanceof Array && Config.QuitList.indexOf (name1) > -1)) { + Config.DebugMode.Stack && mode === 0 && D2Bot.printToConsole(name1 + " timed out, check their logs"); + + if (Config.QuitList.includes(name1) || Config.QuitList.some(str => String.isEqual(str, "all"))) { console.log(name1 + (mode === 0 ? " timed out" : " left")); - if (typeof Config.QuitListDelay !== "undefined" && typeof quitListDelayTime === "undefined" && Config.QuitListDelay.length > 0) { - Config.QuitListDelay.sort((a, b) => a - b); - quitListDelayTime = getTickCount() + rand(Config.QuitListDelay[0] * 1e3, Config.QuitListDelay[1] * 1e3); + if (typeof quitListDelayTime === "undefined" && Config.QuitListDelay.length > 0) { + let [min, max] = Config.QuitListDelay.sort((a, b) => a - b).map(s => Time.seconds(s)); + + quitListDelayTime = getTickCount() + rand(min, max); } else { quitListDelayTime = getTickCount(); } @@ -444,7 +461,7 @@ function main () { // Only do this in expansion if (Config.SoJWaitTime && !me.classic) { !!me.gameserverip && D2Bot.printToConsole(param1 + " Stones of Jordan Sold to Merchants on IP " + me.gameserverip.split(".")[3], sdk.colors.D2Bot.DarkGold); - Messaging.sendToScript("default.dbj", "soj"); + Messaging.sendToScript("libs/SoloPlay/SoloPlay.js", "soj"); } break; @@ -472,51 +489,38 @@ function main () { } }; - this.scriptEvent = function (msg) { + /** + * Handle script/thread communications + * @param {string} msg + * @returns {void} + */ + const scriptEvent = function (msg) { + if (!msg || typeof msg !== "string") return; + let obj; - if (msg && typeof msg === "string" && msg !== "") { - let updated = false; - switch (true) { - case msg === "deleteAndRemake" && Developer.testingMode.enabled: - quitFlag = true; + if (msg.includes("--")) { + let sub = msg.match(/\w+?--/gm).first(); - break; - case msg.substring(0, 8) === "config--": + switch (sub) { + case "config--": console.debug("update config"); Config = JSON.parse(msg.split("config--")[1]); - updated = true; - break; - case msg.substring(0, 7) === "skill--": + return; + case "skill--": console.debug("update skillData"); obj = JSON.parse(msg.split("skill--")[1]); Misc.updateRecursively(CharData.skillData, obj); - updated = true; - break; - case msg.substring(0, 6) === "data--": + return; + case "data--": console.debug("update myData"); obj = JSON.parse(msg.split("data--")[1]); Misc.updateRecursively(myData, obj); - updated = true; - break; - case msg.toLowerCase() === "test": - { - console.debug(sdk.colors.Green + "//-----------DataDump Start-----------//", - "\nÿc8ThreadData ::\n", getScript(true), - "\nÿc8MainData ::\n", myData, - "\nÿc8BuffData ::\n", CharData.buffData, - "\nÿc8SkillData ::\n", CharData.skillData, - "\nÿc8GlobalVariabls ::\n", Object.keys(global), - "\n" + sdk.colors.Red + "//-----------DataDump End-----------//"); - } - - break; + return; } - - if (updated) return; } switch (msg) { @@ -531,6 +535,17 @@ function main () { case "restart": restart = true; + break; + case "test": + { + console.debug(sdk.colors.Green + "//-----------DataDump Start-----------//", + "\nÿc8ThreadData ::\n", getScript(true), + "\nÿc8MainData ::\n", myData, + "\nÿc8BuffData ::\n", CharData.buffData, + "\nÿc8SkillData ::\n", CharData.skillData, + "\nÿc8GlobalVariabls ::\n", Object.keys(global), + "\n" + sdk.colors.Red + "//-----------DataDump End-----------//"); + } break; // ignore common scriptBroadcast messages that aren't relevent to this thread case "mule": @@ -564,12 +579,13 @@ function main () { Config = copyObj(Config); tick = getTickCount(); - addEventListener("keyup", this.keyEvent); - addEventListener("gameevent", this.gameEvent); - addEventListener("scriptmsg", this.scriptEvent); + addEventListener("keyup", keyEvent); + addEventListener("gameevent", gameEvent); + addEventListener("scriptmsg", scriptEvent); addEventListener("scriptmsg", Tracker.logLeveling); Config.QuitListMode > 0 && Common.Toolsthread.initQuitList(); + !Array.isArray(Config.QuitList) && (Config.QuitList = [Config.QuitList]); let myAct = me.act; From 7006a61d365dcc7974fd4f14b6ccb1bd91264895 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Wed, 8 Mar 2023 12:59:01 -0500 Subject: [PATCH 057/263] Update StorageOverrides.js - Fix CanFit check when using an object instead of a item --- libs/SoloPlay/Functions/StorageOverrides.js | 24 +++++++++++++++------ 1 file changed, 17 insertions(+), 7 deletions(-) diff --git a/libs/SoloPlay/Functions/StorageOverrides.js b/libs/SoloPlay/Functions/StorageOverrides.js index 43cef8c6..129f8904 100644 --- a/libs/SoloPlay/Functions/StorageOverrides.js +++ b/libs/SoloPlay/Functions/StorageOverrides.js @@ -157,7 +157,7 @@ includeIfNotIncluded("SoloPlay/Tools/Developer.js"); this.cubeSpot(this.name); - let x, y, item, nPos; + let x, y, nPos; for (y = this.width - 1; y >= 0; y--) { for (x = this.height - 1; x >= 0; x--) { @@ -168,7 +168,7 @@ includeIfNotIncluded("SoloPlay/Tools/Developer.js"); continue; // nothing on this spot } - item = this.itemList[this.buffer[x][y] - 1]; + let item = this.itemList[this.buffer[x][y] - 1]; if (item.classid === sdk.quest.item.Cube && item.isInStash && item.x === 0 && item.y === 0) { continue; // dont touch the cube @@ -247,12 +247,22 @@ includeIfNotIncluded("SoloPlay/Tools/Developer.js"); */ let x, y, nx, ny, makeSpot; - let startX, startY, endX, endY, xDir = 1, yDir = 1; + let xDir = 1, yDir = 1; - startX = 0; - startY = 0; - endX = this.width - (item.sizex - 1); - endY = this.height - (item.sizey - 1); + let startX = 0; + let startY = 0; + let endX = this.width - (item.sizex - 1); + let endY = this.height - (item.sizey - 1); + + Storage.Reload(); + + if (item.sizex && item.sizey && item.gid === undefined) { + // fake item we are checking if we can fit a certain sized item so mock some props to it + item.gid = -1; + item.classid = -1; + item.quality = -1; + item.gfx = -1; + } Storage.Reload(); From eb9b7fc2aa28e680e059c3705e9d4102ba3c7635 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Wed, 8 Mar 2023 13:09:42 -0500 Subject: [PATCH 058/263] A lot of town sequence cleanup - Better checks for using cain based on gold and distance to him - Fix repetitive shopping - Better cleanup sequence for scrolls - Utilize IdTome if we have it before running to shop npc, helps to reduce running around town - Keep track of when last chores was performed, not used yet but todo is skip chores if we don't actually need to do anything and/or we've done them within a certain time period - Add `NPCAction.shopAt`, its not used yet but makes it easier to shop at a certain npc rather than rely on other town functions - Prior to filling our tome if we have one, store any loose scrolls so we don't fill it completely just to turn around and sell scrolls we already had - Clean up cainId function - Change shopItems to instead of using a hard gold limit, it uses a percentage of our total gold that we cannot drop below - Better shopItem sort - More logging information printed about items bought - Cleaner bow/switch bow check for needing arrows --- libs/SoloPlay/Functions/NPCAction.js | 342 +++++++++++------------ libs/SoloPlay/Functions/TownOverrides.js | 39 ++- 2 files changed, 197 insertions(+), 184 deletions(-) diff --git a/libs/SoloPlay/Functions/NPCAction.js b/libs/SoloPlay/Functions/NPCAction.js index ccfa83c6..ed61513d 100644 --- a/libs/SoloPlay/Functions/NPCAction.js +++ b/libs/SoloPlay/Functions/NPCAction.js @@ -8,6 +8,52 @@ // ugly but should handle scope issues if I decide to add this to the core in which case I can come back and remove this // but won't get immeadiate issues of trying to redefine a const (function (NPCAction) { + /** + * Easier shopping done at a specific npc + * @param {string} npcName - NPC.NameOfNPC + * @returns {boolean} + */ + NPCAction.shopAt = function (npcName) { + if (!me.inTown) return false; + + // check for invaid npcs to shop at + if ([NPC.Kashya, NPC.Warriv, NPC.Meshif, NPC.Atma, NPC.Greiz, NPC.Tyrael, NPC.Qual_Kehk, NPC.Cain].includes(npcName.toLowerCase())) { + console.warn(npcName + " is an invalid npc to shop at"); + + return false; + } + + // keep track of where we start from + const origAct = me.act; + const npcAct = NPC.getAct(npcName); + if (!npcAct.length) return false; + + try { + if (!npcAct.includes(origAct)) { + Town.goToTown(npcAct[0]); + } + + Town.move(npcName); + let npc = Game.getNPC(npcName); + + if (!npc && Town.move(npcName)) { + npc = Game.getNPC(npcName); + } + + if (!getUIFlag(sdk.uiflags.Shop) && !npc.startTrade("Shop")) { + throw new Error("Failed to shop at " + npc.name); + } + + return Town.shopItems(); + } catch (e) { + console.error(e); + + return false; + } finally { + me.act !== origAct && Town.goToTown(origAct); + } + }; + NPCAction.buyPotions = function () { if (me.gold < 450 || !me.getItem(sdk.items.TomeofTownPortal)) return false; @@ -147,45 +193,64 @@ return true; }; + /** + * @param {number} classid + * @param {boolean} force + * @returns {boolean} + */ NPCAction.fillTome = function (classid, force = false) { - if (me.gold < 450) return false; + const scrollId = (classid === sdk.items.TomeofTownPortal ? sdk.items.ScrollofTownPortal : sdk.items.ScrollofIdentify); const have = Town.checkScrolls(classid, force); - if (have >= (me.charlvl < 12 ? 5 : 13)) return true; + let myTome = me.getTome(classid); + let invoScrolls = 0; + + if (have > 0 && have < 20) { + // lets see if we have scrolls to cleanup + invoScrolls = me.cleanUpScrolls(myTome, scrollId); + } + + if (have + invoScrolls >= (me.charlvl < 12 ? 5 : 13)) return true; + if (me.gold < 450) return false; - let scroll; - const scrollId = (classid === sdk.items.TomeofTownPortal ? sdk.items.ScrollofTownPortal : sdk.items.ScrollofIdentify); let npc = Town.initNPC("Shop", "fillTome"); if (!npc) return false; delay(500); - if (!me.findItem(classid, sdk.items.mode.inStorage, sdk.storage.Inventory)) { + + if (!myTome) { let tome = npc.getItem(classid); try { - if (!tome || !Storage.Inventory.CanFit(tome)) throw new Error("Can't buy tome"); - tome.buy(); - } catch (e) { - // couldn't buy tome, lets see if we can just buy a single scroll - if (me.getItem(scrollId)) return true; - scroll = npc.getItem(scrollId); - if (!scroll || !Storage.Inventory.CanFit(scroll)) return false; - try { + if (tome) { + Storage.Inventory.CanFit(tome) && tome.buy(); + } else { + // couldn't buy tome, lets see if we can just buy a single scroll + if (me.getItem(scrollId)) return true; + let scroll = npc.getItem(scrollId); + if (!scroll || !Storage.Inventory.CanFit(scroll)) return false; scroll.buy(); - } catch (e) { - console.error(e); - return false; + + return true; } - return true; + } catch (e) { + console.error(e); + + return false; } } - scroll = npc.getItem(scrollId); + let scroll = npc.getItem(scrollId); if (!scroll) return false; + if (!myTome && !(myTome = me.getTome(classid))) return false; + + // place scrolls in tome if we have any now that we know we have a tome (possibly just bought one) + me.cleanUpScrolls(myTome, scrollId); try { if (me.gold < 5000) { - let myTome = me.getItem(classid); + myTome = me.getTome(classid); + if (myTome) { while (myTome.getStat(sdk.stats.Quantity) < 5 && me.gold > 500) { scroll = npc.getItem(scrollId); @@ -216,7 +281,6 @@ if (me.gold < Config.CainID.MinGold && !force) return false; me.cancel(); - Town.stash(false); let unids = me.getUnids(); @@ -227,33 +291,21 @@ let cain = Town.initNPC("CainID", "cainID"); if (!cain) return false; - if (force) { - npc = Town.initNPC("Shop", "clearInventory"); - if (!npc) return false; - } - - for (let i = 0; i < unids.length; i += 1) { - let item = unids[i]; - let result = Pickit.checkItem(item); - // Force ID for unid items matching autoEquip/cubing criteria - Town.needForceID(item) && (result.result = -1); + me.cancelUIFlags(); + + while (unids.length) { + const item = unids.shift(); + const { result, line } = Pickit.checkItem(item); - switch (result.result) { + switch (result) { case Pickit.Result.TRASH: - try { - console.log("sell " + item.name); - Town.initNPC("Shop", "clearInventory"); - Item.logger("Sold", item); - item.sell(); - } catch (e) { - console.error(e); - } + Town.sell.push(item); break; case Pickit.Result.WANTED: case Pickit.Result.SOLOWANTS: Item.logger("Kept", item); - Item.logItem("Kept", item, result.line); + Item.logItem("Kept", item, line); break; case Pickit.Result.CUBING: @@ -261,7 +313,10 @@ Cubing.update(); break; - case Pickit.Result.RUNEWORD: // (doesn't trigger normally) + case Pickit.Result.RUNEWORD: + Item.logger("Kept", item, "Runewords-Town"); + Runewords.update(item.classid, item.gid); + break; case Pickit.Result.CRAFTING: Item.logger("Kept", item, "CraftSys-Town"); @@ -272,72 +327,9 @@ Item.logger("Kept", item, "SoloWants-Town"); SoloWants.update(item); - break; - default: - if ((getUIFlag(sdk.uiflags.Shop) || getUIFlag(sdk.uiflags.NPCMenu)) && (item.getItemCost(sdk.items.cost.ToSell) <= 1 || !item.sellable)) { - continue; - } - - Town.initNPC("Shop", "clearInventory"); - - // Might as well sell the item if already in shop - if (getUIFlag(sdk.uiflags.Shop) || (Config.PacketShopping && getInteractedNPC() && getInteractedNPC().itemcount > 0)) { - console.log("clearInventory sell " + item.name); - - switch (true) { - case (Developer.debugging.smallCharm && item.classid === sdk.items.SmallCharm): - case (Developer.debugging.largeCharm && item.classid === sdk.items.LargeCharm): - case (Developer.debugging.grandCharm && item.classid === sdk.items.GrandCharm): - Item.logItem("Sold", item); - - break; - default: - Item.logger("Dropped", item, "clearInventory"); - - break; - } - - item.sell(); - } else { - console.log("clearInventory dropped " + item.prettyPrint); - Item.logger("Dropped", item, "clearInventory"); - item.drop(); - } - - let timer = getTickCount() - Town.sellTimer; // shop speedup test - - if (timer > 0 && timer < 500) { - delay(timer); - } - break; case Pickit.Result.UNID: - if (tome) { - Town.identifyItem(item, tome); - } else { - let scroll = npc.getItem(sdk.items.ScrollofIdentify); - - if (scroll) { - if (!Storage.Inventory.CanFit(scroll)) { - let tpTome = me.getTome(sdk.items.TomeofTownPortal); - !!tpTome && tpTome.sell() && delay(500); - } - - delay(500); - Storage.Inventory.CanFit(scroll) && scroll.buy(); - } - - scroll = me.findItem(sdk.items.ScrollofIdentify, sdk.items.mode.inStorage, sdk.storage.Inventory); - if (!scroll) continue; - - Town.identifyItem(item, scroll); - } - - // roll back to now check against other criteria - if (item.identified) { - i--; - } - + default: break; } } @@ -349,24 +341,26 @@ // todo - allow earlier shopping, mainly to get a belt NPCAction.shopItems = function (force = false) { if (!Config.MiniShopBot) return true; + if (!me.getTome(sdk.items.TomeofTownPortal)) return false; + // todo - better gold scaling - let goldLimit = [10000, 20000, 30000][me.diff]; + // let goldLimit = [10000, 20000, 30000][me.diff]; + const startingGold = me.gold; + let goldLimit = Math.floor(startingGold * 0.60); let itemTypes = []; let lowLevelShop = false; - if (me.gold < goldLimit && me.charlvl > 6) { - return false; - } else if (me.charlvl < 6 && me.gold > 200) { - lowLevelShop = true; + + if (me.charlvl < 6 && startingGold > 200) { Storage.BeltSize() === 1 && itemTypes.push(sdk.items.type.Belt); !CharData.skillData.bowData.bowOnSwitch && itemTypes.push(sdk.items.type.Bow, sdk.items.type.Crossbow); if (!itemTypes.length) return true; - goldLimit = 200; + lowLevelShop = true; } let npc = getInteractedNPC(); if (!npc || !npc.itemcount) { // for now we only do force shop on low level - if (force && itemTypes.length) { + if ((force || lowLevelShop) && itemTypes.length) { console.debug("Attempt force shopping"); Town.initNPC("Repair", "shopItems"); npc = getInteractedNPC(); @@ -376,16 +370,28 @@ } } + if (getTickCount() - Town.lastShopped.tick < Time.seconds(3) && Town.lastShopped.who === npc.name) return false; let items = npc.getItemsEx() - .filter((item) => !Town.ignoreType(item.itemType) && (itemTypes.length === 0 || itemTypes.includes(item.itemType))) - .sort((a, b) => NTIP.GetTier(b) - NTIP.GetTier(a)); + .filter((item) => !Town.ignoreType(item.itemType) + && (itemTypes.length === 0 || itemTypes.includes(item.itemType)) + && (NTIP.CheckItem(item) !== Pickit.Result.UNWANTED) + && (startingGold - item.getItemCost(sdk.items.cost.ToBuy) > goldLimit)) + .sort((a, b) => { + let priorityA = itemTypes.includes(a.itemType); + let priorityB = itemTypes.includes(b.itemType); + if (priorityA && priorityB) return NTIP.GetTier(b) - NTIP.GetTier(a); + if (priorityA) return 1; + if (priorityB) return -1; + return NTIP.GetTier(b) - NTIP.GetTier(a); + }); if (!items.length) return false; - if (getTickCount() - Town.lastShopped.tick < Time.seconds(3) && Town.lastShopped.who === npc.name) return false; + + const checkedItems = items.length; console.time("shopItems"); let bought = 0; - const haveMerc = (!me.classic && Config.UseMerc && !me.mercrevivecost && Misc.poll(() => !!me.getMerc(), 500, 100)); + const haveMerc = !!me.getMercEx(); console.info(true, "ÿc4MiniShopBotÿc0: Scanning " + npc.itemcount + " items."); /** @@ -402,70 +408,58 @@ Developer.debugging.autoEquip && Item.logItem("Shopped " + action, item, result.line !== undefined ? result.line : "null"); }; + /** + * Buy dependancy item if it's needed + * @param {ItemUnit} item + */ + const checkDependancy = (item) => { + let check = Item.hasDependancy(item); + if (check) { + let el = npc.getItem(check); + !!el && el.buy(); + } + }; + for (let i = 0; i < items.length; i++) { const item = items[i]; const myGold = me.gold; const itemCost = item.getItemCost(sdk.items.cost.ToBuy); if (myGold < itemCost) continue; - const result = Pickit.checkItem(item); + const { result, line } = Pickit.checkItem(item); // no tier'ed items - if (!lowLevelShop && result.result === Pickit.Result.SOLOWANTS && NTIP.CheckItem(item, NTIP.SoloCheckListNoTier, true).result !== Pickit.Result.UNWANTED) { + if (!lowLevelShop && result === Pickit.Result.SOLOWANTS && NTIP.CheckItem(item, NTIP.SoloCheckListNoTier, true).result !== Pickit.Result.UNWANTED) { try { if (Storage.Inventory.CanFit(item) && myGold >= itemCost && (myGold - itemCost > goldLimit)) { if (item.isBaseType) { if (Item.betterThanStashed(item) && Item.betterBaseThanWearing(item, Developer.debugging.baseCheck)) { - shopReport(item, "better base", result.line); + shopReport(item, "better base", line); item.buy() && bought++; - - continue; } } else { - shopReport(item, "NoTier", result.line); + shopReport(item, "NoTier", line); item.buy() && bought++; - - continue; } } } catch (e) { console.error(e); } - } - - // tier'ed items - if (result.result === Pickit.Result.SOLOWANTS && AutoEquip.wanted(item)) { - const checkDependancy = (item) => { - let check = Item.hasDependancy(item); - if (check) { - let el = npc.getItem(check); - !!el && el.buy(); - } - }; + } else if (result === Pickit.Result.SOLOWANTS && AutoEquip.wanted(item)) { + // tier'ed items try { if (Storage.Inventory.CanFit(item) && myGold >= itemCost && (myGold - itemCost > goldLimit)) { - if (Item.hasTier(item) && Item.autoEquipCheck(item)) { - try { - shopReport(item, "AutoEquip", result.line, (item.fname + " Tier: " + NTIP.GetTier(item))); - item.buy() && bought++; - Item.autoEquip("InShop"); - } catch (e) { - console.error(e); - } + let [mainTier] = [NTIP.GetTier(item)]; + // we want this to be at least a 5% increase in the tier value + if (Item.hasTier(item) && Item.autoEquipCheck(item) && ((Item.getEquippedItem(item.bodyLocation().first()).tier - mainTier) / (mainTier * 100)) > 5) { + shopReport(item, "AutoEquip", line, (item.prettyPrint + " Tier: " + NTIP.GetTier(item))); + item.buy() && bought++; + Item.autoEquip("InShop"); checkDependancy(item); - - continue; - } - - if (Item.hasSecondaryTier(item) && Item.autoEquipCheckSecondary(item)) { - try { - shopReport(item, "AutoEquip Switch Shopped", result.line, (item.fname + " SecondaryTier: " + NTIP.GetSecondaryTier(item))); - item.buy() && bought++; - Item.autoEquip("InShop"); - } catch (e) { - console.error(e); - } - + } else if (Item.hasSecondaryTier(item) && Item.autoEquipCheckSecondary(item)) { + shopReport(item, "AutoEquip Switch Shopped", line, (item.prettyPrint + " SecondaryTier: " + NTIP.GetSecondaryTier(item))); + item.buy() && bought++; + Item.autoEquip("InShop"); checkDependancy(item); } } @@ -480,7 +474,7 @@ // merc tier'ed items if (haveMerc && !lowLevelShop) { items = npc.getItemsEx() - .filter((item) => !Town.ignoreType(item.itemType) && (itemTypes.length === 0 || itemTypes.includes(item.itemType) && NTIP.GetMercTier(item) > 0)) + .filter((item) => !Town.ignoreType(item.itemType) && NTIP.GetMercTier(item) > 0) .sort((a, b) => NTIP.GetMercTier(b) - NTIP.GetMercTier(a)) .forEach(item => { const myGold = me.gold; @@ -508,7 +502,7 @@ Town.lastShopped.tick = getTickCount(); Town.lastShopped.who = npc.name; - console.info(false, "Bought " + bought + " items", "shopItems"); + console.info(false, "Evaluated " + checkedItems + " items. Bought " + bought + " items. Spent " + (startingGold - me.gold) + " gold", "shopItems"); return true; }; @@ -622,32 +616,24 @@ break; case "buyQuiver": - let bowCheck = Attack.usingBow(); - let switchBowCheck = CharData.skillData.bowData.bowOnSwitch; - !bowCheck && switchBowCheck && (bowCheck = (() => { - switch (CharData.skillData.bowData.bowType) { - case sdk.items.type.Bow: - case sdk.items.type.AmazonBow: - return "bow"; - case sdk.items.type.Crossbow: - return "crossbow"; - default: - return ""; - } - })()); + let bowCheck = me.getItemsEx() + .filter(el => el.isEquipped && [sdk.items.type.Bow, sdk.items.type.Crossbow, sdk.items.type.AmazonBow].includes(el.itemType)) + .first(); if (bowCheck) { - let quiver = bowCheck === "bow" ? "aqv" : "cqv"; - switchBowCheck && me.switchWeapons(sdk.player.slot.Secondary); - let myQuiver = me.getItem(quiver, sdk.items.mode.Equipped); - !!myQuiver && myQuiver.drop(); - + let quiverType = bowCheck.itemType === sdk.items.type.Crossbow ? sdk.items.Bolts : sdk.items.Arrows; + let onSwitch = bowCheck.isOnSwap; + onSwitch && me.switchWeapons(sdk.player.slot.Secondary); + npc = Town.initNPC("Repair", "buyQuiver"); if (!npc) return false; - quiver = npc.getItem(quiver); + let myQuiver = me.getItem(quiver, sdk.items.mode.Equipped); + !!myQuiver && myQuiver.sell(); + + let quiver = npc.getItem(quiverType); !!quiver && quiver.buy(); - switchBowCheck && me.switchWeapons(sdk.player.slot.Main); + onSwitch && me.switchWeapons(sdk.player.slot.Main); } break; diff --git a/libs/SoloPlay/Functions/TownOverrides.js b/libs/SoloPlay/Functions/TownOverrides.js index 21eb0259..f7e4ef1c 100644 --- a/libs/SoloPlay/Functions/TownOverrides.js +++ b/libs/SoloPlay/Functions/TownOverrides.js @@ -230,6 +230,12 @@ Town.identify = function () { let list = (Storage.Inventory.Compare(Config.Inventory) || []); if (!list.length) return false; + let tome = me.getTome(sdk.items.TomeofIdentify); + // if we have a tome might as well use it - this might prevent us from having to run from one npc to another + if (tome && tome.getStat(sdk.stats.Quantity) > 0 && Town.getDistance(Town.tasks[me.act - 1].Shop) > 5) { + me.fieldID(); // not in the field but oh well no need to repeat the code + } + // Avoid unnecessary NPC visits // Only unid items or sellable junk (low level) should trigger a NPC visit if (!list.some(item => { @@ -243,7 +249,6 @@ Town.identify = function () { let npc = Town.initNPC("Shop", "identify"); if (!npc) return false; - let tome = me.getTome(sdk.items.TomeofIdentify); tome && tome.getStat(sdk.stats.Quantity) < list.length && NPCAction.fillTome(sdk.items.TomeofIdentify); MainLoop: @@ -350,6 +355,7 @@ Town.stash = function (stashGold = true) { } }); } + // Stash gold if (stashGold) { @@ -630,6 +636,14 @@ Town.clearJunk = function () { return true; }; +Town.lastChores = 0; + +Town.fillTomes = function () { + NPCAction.fillTome(sdk.items.TomeofTownPortal); + Config.FieldID.Enabled && NPCAction.fillTome(sdk.items.TomeofIdentify); + !!me.getItem(sdk.items.TomeofTownPortal) && this.clearScrolls(); +}; + /** * @override * @param {boolean} repair @@ -659,22 +673,33 @@ Town.doChores = function (repair = false, givenTasks = {}) { const preAct = me.act; + /** + * @todo light chores if last chores was < minute? 2 minutes idk yet + */ + me.switchWeapons(Attack.getPrimarySlot()); extraTasks.fullChores && Quest.unfinishedQuests(); - me.getUnids().length && me.gold < 5000 && NPCAction.cainID(true); + + // Use cainId if we are low on gold or we are closer to him than the shopNPC + if (me.getUnids().length) { + if (me.gold < 5000 + || Town.getDistance("cain") < Town.getDistance(Town.tasks[me.act - 1].Heal)) { + NPCAction.cainID(true); + } + } + + // maybe a check if need healing first, as we might have just used a potion this.heal(); this.identify(); this.clearInventory(); - NPCAction.fillTome(sdk.items.TomeofTownPortal); - Config.FieldID.Enabled && NPCAction.fillTome(sdk.items.TomeofIdentify); - !!me.getItem(sdk.items.TomeofTownPortal) && this.clearScrolls(); + Town.fillTomes(); NPCAction.buyPotions(); this.buyKeys(); extraTasks.thawing && CharData.buffData.thawing.need() && Town.buyPots(12, "Thawing", true); extraTasks.antidote && CharData.buffData.antidote.need() && Town.buyPots(12, "Antidote", true); extraTasks.stamina && Town.buyPots(12, "Stamina", true); NPCAction.shopItems(); - NPCAction.repair(repair) && NPCAction.shopItems(true); + NPCAction.repair(repair); NPCAction.reviveMerc(); NPCAction.gamble(); Cubing.emptyCube(); @@ -687,6 +712,7 @@ Town.doChores = function (repair = false, givenTasks = {}) { Town.haveItemsToSell() && Town.sellItems() && me.cancelUIFlags(); this.clearJunk(); this.stash(); + // check pots again, we might have enough gold now if we didn't before me.needPotions() && NPCAction.buyPotions() && me.cancelUIFlags(); // check repair again, we might have enough gold now if we didn't before @@ -708,6 +734,7 @@ Town.doChores = function (repair = false, givenTasks = {}) { delay(300); console.debug("doChores Ending Gold :: " + me.gold); console.info(false, null, "doChores"); + Town.lastChores = getTickCount(); return true; }; From 0603b2d832b49372dfce4dd3af71086c46249fca Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Wed, 8 Mar 2023 13:13:13 -0500 Subject: [PATCH 059/263] Small pathing changes - Use find and instead of first when looking for nearestNode - Only move to old position during clearPos if it's further than 5 units away - Pathing still needs a lot of work to fix some of the recursion choices --- libs/SoloPlay/Functions/AttackOverrides.js | 27 ++++++++++++++++--- libs/SoloPlay/Functions/PatherOverrides.js | 30 ++++++++++++++++++---- 2 files changed, 48 insertions(+), 9 deletions(-) diff --git a/libs/SoloPlay/Functions/AttackOverrides.js b/libs/SoloPlay/Functions/AttackOverrides.js index b6cd1860..fbeb29c2 100644 --- a/libs/SoloPlay/Functions/AttackOverrides.js +++ b/libs/SoloPlay/Functions/AttackOverrides.js @@ -198,7 +198,9 @@ Attack.openChests = function (range, x, y) { if (unit) { do { - if (unit.name && getDistance(unit, x, y) <= range + if (unit.name + && getDistance(unit, x, y) <= range + && unit.mode === sdk.objects.mode.Inactive && ids.includes(unit.name.toLowerCase()) && unit.getMobCount(10) === 0) { list.push(copyUnit(unit)); @@ -460,7 +462,10 @@ Attack.clearPos = function (x, y, range = 15, pickit = true) { ClassAttack.afterAttack(pickit); pickit && Attack.openChests(range, x, y); attackCount > 0 && pickit && Pickit.pickItems(); - Pather.moveTo(x, y); + if ([x, y].distance > 5) { + Developer.debugging.pathing && console.debug("Returning to position " + x + "/" + y + " distance: " + [x, y].distance); + Pather.moveTo(x, y); + } return true; }; @@ -903,7 +908,11 @@ Attack.getCurrentChargedSkillIds = function (init = false) { return true; }; -Attack.getItemCharges = function (skillId = undefined) { +/** + * @param {number} skillId + * @returns {boolean} + */ +Attack.getItemCharges = function (skillId) { if (!skillId) return false; let chargedItems = [], validCharge = function (itemCharge) { @@ -937,7 +946,12 @@ Attack.getItemCharges = function (skillId = undefined) { return !!(chargedItems.length > 0); }; -Attack.castCharges = function (skillId = undefined, unit = undefined) { +/** + * @param {number} skillId + * @param {Monster} unit + * @returns {boolean} + */ +Attack.castCharges = function (skillId, unit) { if (!skillId || !unit || !Skill.wereFormCheck(skillId) || (me.inTown && !Skill.townSkill(skillId))) { return false; } @@ -948,6 +962,11 @@ Attack.castCharges = function (skillId = undefined, unit = undefined) { return true; }; +/** + * @param {number} skillId + * @param {Monster} unit + * @returns {boolean} + */ Attack.switchCastCharges = function (skillId, unit) { if (!skillId || !unit || !Skill.wereFormCheck(skillId) || (me.inTown && !Skill.townSkill(skillId))) { return false; diff --git a/libs/SoloPlay/Functions/PatherOverrides.js b/libs/SoloPlay/Functions/PatherOverrides.js index d13f898b..8bd03ee5 100644 --- a/libs/SoloPlay/Functions/PatherOverrides.js +++ b/libs/SoloPlay/Functions/PatherOverrides.js @@ -334,6 +334,11 @@ Pather.clearUIFlags = function () { getUIFlag(flag) && me.cancel(); }); }; + +/** + * @memberof Pather + * @type {PathNode[]} + */ Pather.currentWalkingPath = []; /** @@ -351,6 +356,7 @@ Pather.move = function (target, givenSettings = {}) { const settings = Object.assign({}, { clearSettings: { }, + allowNodeActions: true, allowTeleport: true, allowClearing: true, allowTown: true, @@ -478,26 +484,35 @@ Pather.move = function (target, givenSettings = {}) { : useChargedTele && (getDistance(me, node) >= 15 || me.inArea(sdk.areas.ThroneofDestruction)) ? Pather.teleUsingCharges(node.x, node.y) : Pather.walkTo(node.x, node.y, (fail > 0 || me.inTown) ? 2 : 4)) { - if (!me.inTown) { + if (settings.allowNodeActions && !me.inTown) { if (Pather.recursion) { try { Pather.recursion = false; + /** + * @todo We need to pass our path in so we can fix the recursion issues of running forward on our path only to return to the old node and continue + * we should instead perform the actions in a way that moves us forward on our path ensuring we haven't skipped anything in the process as well + * for long paths maybe generate a coordinate list of shrines/chests and have action hooks for them + */ NodeAction.go(settings.clearSettings); // need to determine if its worth going back to our orignal node (items maybe?) // vs our current proximity to our next node // need to export our main path so other functions that cause us to move can see it - if (getDistance(me, node.x, node.y) > 5) { + if (node.distance > 5) { const lastNode = Pather.currentWalkingPath.last(); // lets try and find the nearest node that brings us close to our goal + /** @type {PathNode} */ let nearestNode = Pather.currentWalkingPath.length > 0 && Pather.currentWalkingPath .filter(el => !!el && el.x !== node.x && el.y !== node.y) .sort((a, b) => { if (a.distance < b.distance && getDistance(a, lastNode) < getDistance(b, lastNode)) return -1; if (a.distance > b.distance && getDistance(a, lastNode) > getDistance(b, lastNode)) return 1; return a.distance - b.distance; - }).first(); - if (getDistance(me, node.x, node.y) < 40) { + }) + .find(pNode => pNode.distance > 5); + + if (node.distance < 40) { let goBack = false; + let foundNode = false; // lets see if it's worth walking back to old node Pickit.checkSpotForItems(node, true) && (goBack = true); // @todo check shrines/chests in proximity to old node vs next node @@ -511,12 +526,17 @@ Pather.move = function (target, givenSettings = {}) { console.debug("Found new path index: " + newIndex + " of currentPathLen: " + path.length); path = path.slice(newIndex); node = path.shift(); + foundNode = true; console.debug("New path length: " + path.length); } else { console.debug("Couldn't find new path index"); } } - node.distance > 5 && Pather.move(node, settings); + + if (node.distance > 5) { + !foundNode && console.debug("Path Recursion :: Returning to position " + node.x + "/" + node.y + " distance: " + node.distance); + Pather.move(node, settings); + } } else { Pather.move(node, settings); } From 4ab3373e7fdf07b347581a35d6d8738d3600df4f Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Wed, 8 Mar 2023 13:13:53 -0500 Subject: [PATCH 060/263] Update Me.js - Add `me.cleanUpInvoScrolls` this was supposed to be in the previous town sequence commit --- libs/SoloPlay/Functions/Me.js | 40 +++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/libs/SoloPlay/Functions/Me.js b/libs/SoloPlay/Functions/Me.js index 6ffaecf5..e955f5c1 100644 --- a/libs/SoloPlay/Functions/Me.js +++ b/libs/SoloPlay/Functions/Me.js @@ -238,6 +238,46 @@ me.cleanUpInvoPotions = function (beltSize) { return true; }; +me.cleanUpScrolls = function (tome, scrollId) { + if (!tome || !scrollId) return 0; + + let cleanedUp = 0; + let myScrolls = me.getItemsEx() + .filter(el => el.isInInventory && el.classid === scrollId); + + if (myScrolls.length) { + try { + // If we are at an npc already, open the window otherwise moving potions around fails + if (getUIFlag(sdk.uiflags.NPCMenu) && !getUIFlag(sdk.uiflags.Shop)) { + console.info(null, "Opening npc menu to clean up scrolls"); + Misc.useMenu(sdk.menu.Trade) || me.cancelUIFlags(); + } + + myScrolls.forEach(el => { + if (tome && tome.getStat(sdk.stats.Quantity) < 20) { + let currQuantity = tome.getStat(sdk.stats.Quantity); + if (el.toCursor()) { + new PacketBuilder().byte(sdk.packets.send.ScrollToMe).dword(el.gid).dword(tome.gid).send(); + Misc.poll(() => !me.itemoncursor, 100, 25); + + if (tome.getStat(sdk.stats.Quantity) > currQuantity) { + console.info(null, "Placed scroll in tome"); + cleanedUp++; + } + } else { + console.warn("failed to place scroll in tome"); + } + } + }); + } catch (e) { + console.error(e); + me.cancelUIFlags(); + } + } + + return cleanedUp; +}; + me.needPotions = function () { // we aren't using MinColumn if none of the values are set if (!Config.MinColumn.some(el => el > 0)) return false; From ae90936230c7da17dc74d281c158552ab7b48294 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Wed, 8 Mar 2023 13:17:19 -0500 Subject: [PATCH 061/263] Update SkillOverrides.js - set paladin's aura if that hand isn't being used --- libs/SoloPlay/Functions/SkillOverrides.js | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/libs/SoloPlay/Functions/SkillOverrides.js b/libs/SoloPlay/Functions/SkillOverrides.js index 1a9855b6..a0032dc0 100644 --- a/libs/SoloPlay/Functions/SkillOverrides.js +++ b/libs/SoloPlay/Functions/SkillOverrides.js @@ -192,6 +192,11 @@ Skill.switchCast = function (skillId, givenSettings = {}) { return false; } + if (me.paladin && settings.hand !== sdk.skills.hand.Right) { + // set aura + Skill.setSkill(Config.AttackSkill[2], sdk.skills.hand.Right); + } + if ((this.forcePacket && this.casterSkills.includes(skillId) && (!!me.realm || [sdk.skills.Teeth, sdk.skills.Tornado].indexOf(skillId) === -1)) || Config.PacketCasting > 1 || skillId === sdk.skills.Teleport) { From 61052bd5c7a0d99bac4e4d7765e524209e544131 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Wed, 8 Mar 2023 13:18:33 -0500 Subject: [PATCH 062/263] Update PickitOverrides.js - Add items to an ignore list if we cannot pick them up - Clear the ignore list after a successful townVisit --- libs/SoloPlay/Functions/PickitOverrides.js | 46 ++++++++++++++++------ 1 file changed, 34 insertions(+), 12 deletions(-) diff --git a/libs/SoloPlay/Functions/PickitOverrides.js b/libs/SoloPlay/Functions/PickitOverrides.js index ba829bb9..3798d174 100644 --- a/libs/SoloPlay/Functions/PickitOverrides.js +++ b/libs/SoloPlay/Functions/PickitOverrides.js @@ -30,6 +30,12 @@ Pickit.minItemKeepGoldValue = function () { } }; +/** + * @constant + * This value never changes throughout the game + */ +Pickit.classicMode = me.classic; + /** * @param {ItemUnit} unit */ @@ -43,15 +49,20 @@ Pickit.checkItem = function (unit) { // quick return on essentials - we know they aren't going to be in the other checks if (Pickit.essentials.includes(unit.itemType)) return rval; - if ((unit.classid === sdk.items.runes.Ral || unit.classid === sdk.items.runes.Ort) && Town.repairIngredientCheck(unit)) { - return resultObj(Pickit.Result.UTILITY); - } + if (!Pickit.classicMode) { + if ([sdk.items.runes.Ral, sdk.items.runes.Ort].includes(unit.classid) && Town.repairIngredientCheck(unit)) { + return resultObj(Pickit.Result.UTILITY); + } - if (CharData.skillData.bowData.bowOnSwitch) { - if ([sdk.items.type.Bow, sdk.items.type.AmazonBow].includes(CharData.skillData.bowData.bowType) && unit.itemType === sdk.items.type.BowQuiver && Item.getQuantityOwned(unit, true) < 1) { - return resultObj(Pickit.Result.SOLOWANTS, "Switch-Arrows"); - } else if (CharData.skillData.bowData.bowType === sdk.items.type.Crossbow && unit.itemType === sdk.items.type.CrossbowQuiver && Item.getQuantityOwned(unit, true) < 1) { - return resultObj(Pickit.Result.SOLOWANTS, "Switch-Bolts"); + /** + * Need to redo this + */ + if (CharData.skillData.bowData.bowOnSwitch && [sdk.items.type.BowQuiver, sdk.items.type.CrossbowQuiver].includes(unit.itemType) && rval === Pickit.Result.WANTED) { + if ([sdk.items.type.Bow, sdk.items.type.AmazonBow].includes(CharData.skillData.bowData.bowType) && unit.itemType === sdk.items.type.BowQuiver) { + return resultObj(Pickit.Result.SOLOWANTS, "Switch-Arrows"); + } else if (CharData.skillData.bowData.bowType === sdk.items.type.Crossbow && unit.itemType === sdk.items.type.CrossbowQuiver) { + return resultObj(Pickit.Result.SOLOWANTS, "Switch-Bolts"); + } } } @@ -197,7 +208,7 @@ Pickit.canFit = function (item) { }; /** - * @param {ItemUnit} unit + * @param {ItemUnit} unit * @returns {boolean} */ Pickit.canPick = function (unit) { @@ -337,7 +348,7 @@ Pickit.canPick = function (unit) { } } - return (needPots > 0 || (me.charlvl < 10 && Storage.Inventory.CanFit(unit))); + return (needPots > 0) || (me.charlvl < 10 && Storage.Inventory.CanFit(unit)); case undefined: // Yes, it does happen console.warn("undefined item (!?)"); @@ -594,6 +605,7 @@ Pickit.checkSpotForItems = function (spot, checkVsMyDist = false, range = Config Pickit.pickList = []; Pickit.essentialList = []; +Pickit.ignoreList = []; // Might need to do a global list so this function and pickItems see the same items to prevent an item from being in both Pickit.essessntialsPick = function (clearBeforePick = false, ignoreGold = false, builtList = [], once = false) { @@ -695,6 +707,7 @@ Pickit.pickItems = function (range = Config.PickRange, once = false) { if (item) { do { + if (Pickit.ignoreList.includes(item.gid)) continue; if (Pickit.pickList.some(el => el.gid === item.gid)) continue; if (item.onGroundOrDropping && getDistance(me, item) <= range) { Pickit.pickList.push(copyUnit(item)); @@ -713,6 +726,12 @@ Pickit.pickItems = function (range = Config.PickRange, once = false) { Pickit.pickList.sort(this.sortItems); const currItem = Pickit.pickList[0]; + if (Pickit.ignoreList.includes(currItem.gid)) { + Pickit.pickList.shift(); + + continue; + } + // Check if the item unit is still valid and if it's on ground or being dropped // Don't pick items behind walls/obstacles when walking if (copyUnit(currItem).x !== undefined && currItem.onGroundOrDropping @@ -752,11 +771,12 @@ Pickit.pickItems = function (range = Config.PickRange, once = false) { if (Town.visitTown()) { // Recursive check after going to town. We need to remake item list because gids can change. // Called only if room can be made so it shouldn't error out or block anything. + Pickit.ignoreList = []; // reset the list of ignored gids return this.pickItems(range, once); } // Town visit failed - abort - console.log("ÿc7Not enough room for " + Item.color(currItem) + currItem.name); + console.warn("Failed to visit town. ÿc7Not enough room for " + Item.color(currItem) + currItem.name); return false; } @@ -764,7 +784,9 @@ Pickit.pickItems = function (range = Config.PickRange, once = false) { // Can't make room - trigger automule if (copyUnit(currItem).x !== undefined) { Item.logger("No room for", currItem); - console.log("ÿc7Not enough room for " + Item.color(currItem) + currItem.name); + console.warn("ÿc7Not enough room for " + Item.color(currItem) + currItem.name); + // ignore the item now + Pickit.ignoreList.push(currItem.gid); needMule = true; break; From 9b887a03f77af9a2f0a6afb86d458d615a15fb4e Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Wed, 8 Mar 2023 13:24:30 -0500 Subject: [PATCH 063/263] Update MiscOverrides.js - Handle if we loose reference to the chest while waiting for mode to change after opening it - add shrineMap for cleaner scanShrines initialization - Fix scanShrines for a3/a5 shrines that aren't seen when using `Game.getObject("shrine")` - When looking for a specific shrine don't run all the way to the wrong shrine, only get close enough to let nodeaction decide if we should grab it when its not the one we are actually looking for - add a pickItem call after using a gem shrine --- libs/SoloPlay/Functions/MiscOverrides.js | 128 ++++++++++++++--------- 1 file changed, 81 insertions(+), 47 deletions(-) diff --git a/libs/SoloPlay/Functions/MiscOverrides.js b/libs/SoloPlay/Functions/MiscOverrides.js index 32b7549f..31090db2 100644 --- a/libs/SoloPlay/Functions/MiscOverrides.js +++ b/libs/SoloPlay/Functions/MiscOverrides.js @@ -113,7 +113,7 @@ Misc.openChest = function (unit) { (specialChest || i > 2) ? Misc.click(0, 0, unit) : Packet.entityInteract(unit); } - if (Misc.poll(() => unit.mode, 1000, 50)) { + if (Misc.poll(() => !unit || unit.mode, 1000, 50)) { return true; } Packet.flash(me.gid); @@ -125,6 +125,11 @@ Misc.openChest = function (unit) { return false; }; +/** + * @param {number} range + * @returns {boolean} + * @todo Take path parameter to we can open the chests in an order that brings us closer to our destination + */ Misc.openChests = function (range = 15) { if (!Misc.openChestsEnabled) return false; const containers = [ @@ -170,6 +175,10 @@ Misc.openChests = function (range = 15) { return true; }; +/** + * @param {ObjectUnit} unit + * @returns {boolean} + */ Misc.getWell = function (unit) { if (!unit || unit.mode === sdk.objects.mode.Active) return false; @@ -216,13 +225,39 @@ Misc.useWell = function (range = 15) { return true; }; +const shrineMap = new Map(); +shrineMap.set(sdk.shrines.Refilling, 0); +shrineMap.set(sdk.shrines.Health, 0); +shrineMap.set(sdk.shrines.Mana, 0); +shrineMap.set(sdk.shrines.HealthExchange, 0); +shrineMap.set(sdk.shrines.ManaExchange, 0); +shrineMap.set(sdk.shrines.Armor, sdk.states.ShrineArmor); +shrineMap.set(sdk.shrines.Combat, sdk.states.ShrineCombat); +shrineMap.set(sdk.shrines.ResistFire, sdk.states.ShrineResFire); +shrineMap.set(sdk.shrines.ResistCold, sdk.states.ShrineResCold); +shrineMap.set(sdk.shrines.ResistLightning, sdk.states.ShrineResLighting); +shrineMap.set(sdk.shrines.ResistPoison, sdk.states.ShrineResPoison); +shrineMap.set(sdk.shrines.Skill, sdk.states.ShrineSkill); +shrineMap.set(sdk.shrines.ManaRecharge, sdk.states.ShrineManaRegen); +shrineMap.set(sdk.shrines.Stamina, sdk.states.ShrineStamina); +shrineMap.set(sdk.shrines.Experience, sdk.states.ShrineExperience); +shrineMap.set(sdk.shrines.Enirhs, 0); +shrineMap.set(sdk.shrines.Portal, 0); +shrineMap.set(sdk.shrines.Gem, 0); +shrineMap.set(sdk.shrines.Fire, 0); +shrineMap.set(sdk.shrines.Monster, 0); +shrineMap.set(sdk.shrines.Exploding, 0); +shrineMap.set(sdk.shrines.Poison, 0); + Misc.scanShrines = function (range, ignore = []) { if (!Config.ScanShrines.length) return false; !range && (range = Pather.useTeleport() ? 25 : 15); !Array.isArray(ignore) && (ignore = [ignore]); + /** @type {ObjectUnit[]} */ let shrineList = []; + const rangeCheck = (shrineType) => { switch (true) { case shrineType === sdk.shrines.Refilling && (me.hpPercent < 50 || me.mpPercent < 50 || me.staminaPercent < 50): @@ -245,47 +280,23 @@ Misc.scanShrines = function (range, ignore = []) { // Initiate shrine states if (!this.shrineStates) { this.shrineStates = []; - - for (let i = 0; i < Config.ScanShrines.length; i += 1) { - switch (Config.ScanShrines[i]) { - case sdk.shrines.None: - case sdk.shrines.Refilling: - case sdk.shrines.Health: - case sdk.shrines.Mana: - case sdk.shrines.HealthExchange: // (doesn't exist) - case sdk.shrines.ManaExchange: // (doesn't exist) - case sdk.shrines.Enirhs: // (doesn't exist) - case sdk.shrines.Portal: - case sdk.shrines.Gem: - case sdk.shrines.Fire: - case sdk.shrines.Monster: - case sdk.shrines.Exploding: - case sdk.shrines.Poison: - this.shrineStates[i] = 0; // no state - - break; - case sdk.shrines.Armor: - case sdk.shrines.Combat: - case sdk.shrines.ResistFire: - case sdk.shrines.ResistCold: - case sdk.shrines.ResistLightning: - case sdk.shrines.ResistPoison: - case sdk.shrines.Skill: - case sdk.shrines.ManaRecharge: - case sdk.shrines.Stamina: - case sdk.shrines.Experience: - // Both states and shrines are arranged in same order with armor shrine starting at 128 - this.shrineStates[i] = Config.ScanShrines[i] + 122; - - break; + let i = 0; + for (let shrine of Config.ScanShrines) { + if (shrine > 0) { + this.shrineStates[i] = shrineMap.get(shrine); + i++; } } } - - let shrine = Game.getObject("shrine"); - if (shrine) { + let shrine = Game.getObject(); + + /** + * Fix for a3/a5 shrines + */ + if (shrine && shrineMap.has(shrine.objtype) && shrine.name.toLowerCase().includes("shrine")) { let index = -1; + // Build a list of nearby shrines do { if (shrine.mode === sdk.objects.mode.Inactive && !ignore.includes(shrine.objtype) @@ -304,14 +315,14 @@ Misc.scanShrines = function (range, ignore = []) { } for (let i = 0; i < Config.ScanShrines.length; i += 1) { - for (let j = 0; j < shrineList.length; j += 1) { + for (let shrine of shrineList) { // Get the shrine if we have no active state or to refresh current state or if the shrine has no state // Don't override shrine state with a lesser priority shrine // todo - check to make sure we can actually get the shrine for ones without states // can't grab a health shrine if we are in perfect health, can't grab mana shrine if our mana is maxed if (index === -1 || i <= index || this.shrineStates[i] === 0) { - if (shrineList[j].objtype === Config.ScanShrines[i] && (Pather.useTeleport() || !checkCollision(me, shrineList[j], sdk.collision.WallOrRanged))) { - this.getShrine(shrineList[j]); + if (shrine.objtype === Config.ScanShrines[i] && (Pather.useTeleport() || !checkCollision(me, shrine, sdk.collision.WallOrRanged))) { + this.getShrine(shrine); // Gem shrine - pick gem if (Config.ScanShrines[i] === sdk.shrines.Gem) { @@ -327,15 +338,25 @@ Misc.scanShrines = function (range, ignore = []) { }; Misc.presetShrineIds = [2, 81, 83]; + +/** + * Check all shrines in area and get the first one of specified type + * @param {number} area + * @param {number} type + * @param {boolean} use + * @returns {boolean} Sucesfully found shrine(s) + * @todo If we are trying to find a specific shrine then generate path and perform callback after each node to see if we are within range + * of getUnit and can see the shrine type so we know whether to continue moving to it or not. + */ Misc.getShrinesInArea = function (area, type, use) { let shrineLocs = []; - const shrineIds = [2, 81, 83]; // @todo add more of the shrine id's and document them for sdk - let unit = getPresetUnits(area); + let result = false; + let unit = Game.getPresetObjects(area); if (unit) { for (let i = 0; i < unit.length; i += 1) { - if (shrineIds.includes(unit[i].id)) { - shrineLocs.push([unit[i].roomx * 5 + unit[i].x, unit[i].roomy * 5 + unit[i].y]); + if (Misc.presetShrineIds.includes(unit[i].id)) { + Misc.presetShrineIds.push([unit[i].roomx * 5 + unit[i].x, unit[i].roomy * 5 + unit[i].y]); } } } @@ -347,7 +368,11 @@ Misc.getShrinesInArea = function (area, type, use) { shrineLocs.sort(Sort.points); let coords = shrineLocs.shift(); - Skill.haveTK ? Pather.moveNear(coords[0], coords[1], 20) : Pather.moveTo(coords[0], coords[1], 2); + Pather.moveToEx(coords[0], coords[1], { minDist: Skill.haveTK ? 20 : 5, callback: () => { + let shrine = Game.getObject("shrine"); + // for now until I write a proper isShrineWanted check, get close enough that nodeaction checks it + return !!shrine && shrine.x === coords[0] && shrine.y === coords[1] && shrine.distance <= 15; + } }); let shrine = Game.getObject("shrine"); @@ -357,6 +382,11 @@ Misc.getShrinesInArea = function (area, type, use) { (!Skill.haveTK || !use) && Pather.moveTo(shrine.x - 2, shrine.y - 2); if (!use || this.getShrine(shrine)) { + result = true; + + if (type === sdk.shrines.Gem) { + Pickit.pickItems(); + } return true; } @@ -371,7 +401,7 @@ Misc.getShrinesInArea = function (area, type, use) { NodeAction.shrinesToIgnore.remove(type); } - return false; + return result; }; Misc.getExpShrine = function (shrineLocs = []) { @@ -399,6 +429,10 @@ Misc.getExpShrine = function (shrineLocs = []) { return true; }; +/** + * @param {ItemUnit} item + * @returns {boolean} + */ Misc.unsocketItem = function (item) { if (me.classic || !me.getItem(sdk.items.quest.Cube) || !item) return false; // Item doesn't have anything socketed @@ -409,7 +443,7 @@ Misc.unsocketItem = function (item) { let scroll = Runewords.getScroll(); let bodyLoc; - let {classid, quality} = item; + let { classid, quality } = item; item.isEquipped && (bodyLoc = item.bodylocation); // failed to get scroll or open stash most likely means we're stuck somewhere in town, so it's better to return false From d05a7d9bb59f62616a5b5eed18be8781cfbf3694 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Wed, 8 Mar 2023 23:44:26 -0500 Subject: [PATCH 064/263] Update countess.js - fix typo, only want to wait by the chest if we haven't complete the quest yet --- libs/SoloPlay/Scripts/countess.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libs/SoloPlay/Scripts/countess.js b/libs/SoloPlay/Scripts/countess.js index 789cc029..dec423ed 100644 --- a/libs/SoloPlay/Scripts/countess.js +++ b/libs/SoloPlay/Scripts/countess.js @@ -16,7 +16,7 @@ function countess () { Pather.checkWP(sdk.areas.BlackMarsh, true) ? Pather.useWaypoint(sdk.areas.BlackMarsh) : Pather.getWP(sdk.areas.BlackMarsh); Precast.doPrecast(true); - let forQuest = !!Misc.checkQuest(sdk.quest.id.ForgottenTower, sdk.quest.states.Completed); + let forQuest = !Misc.checkQuest(sdk.quest.id.ForgottenTower, sdk.quest.states.Completed); if (me.charlvl < 12) { // @todo - low level, lets take a scenic route and kill those hawk nests From 452aa249d526ec214f2356e6b16bdeabeb6b096f Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Sun, 12 Mar 2023 13:11:58 -0400 Subject: [PATCH 065/263] Update RunewordsOverrides.js - Update with the new Runeword data structure for core Runewords.js --- libs/SoloPlay/Functions/RunewordsOverrides.js | 58 ++++++++----------- 1 file changed, 25 insertions(+), 33 deletions(-) diff --git a/libs/SoloPlay/Functions/RunewordsOverrides.js b/libs/SoloPlay/Functions/RunewordsOverrides.js index bb85e9e1..8ab42bc8 100644 --- a/libs/SoloPlay/Functions/RunewordsOverrides.js +++ b/libs/SoloPlay/Functions/RunewordsOverrides.js @@ -7,44 +7,36 @@ !includeIfNotIncluded("core/Runewords.js"); -// Don't use ladder-only on NL -Runeword.Brand = (me.ladder || Developer.addLadderRW) ? [sdk.items.runes.Jah, sdk.items.runes.Lo, sdk.items.runes.Mal, sdk.items.runes.Gul] : false; // Jah + Lo + Mal + Gul -Runeword.Death = (me.ladder || Developer.addLadderRW) ? [sdk.items.runes.Hel, sdk.items.runes.El, sdk.items.runes.Vex, sdk.items.runes.Ort, sdk.items.runes.Gul] : false; // Hel + El + Vex + Ort + Gul -Runeword.Destruction = (me.ladder || Developer.addLadderRW) ? [sdk.items.runes.Vex, sdk.items.runes.Lo, sdk.items.runes.Ber, sdk.items.runes.Jah, sdk.items.runes.Ko] : false; // Vex + Lo + Ber + Jah + Ko -Runeword.Dragon = (me.ladder || Developer.addLadderRW) ? [sdk.items.runes.Sur, sdk.items.runes.Lo, sdk.items.runes.Sol] : false; // Sur + Lo + Sol -Runeword.Dream = (me.ladder || Developer.addLadderRW) ? [sdk.items.runes.Io, sdk.items.runes.Jah, sdk.items.runes.Pul] : false; // Io + Jah + Pul -Runeword.Edge = (me.ladder || Developer.addLadderRW) ? [sdk.items.runes.Tir, sdk.items.runes.Tal, sdk.items.runes.Amn] : false; // Tir + Tal + Amn -Runeword.Faith = (me.ladder || Developer.addLadderRW) ? [sdk.items.runes.Ohm, sdk.items.runes.Jah, sdk.items.runes.Lem, sdk.items.runes.Eld] : false; // Ohm + Jah + Lem + Eld -Runeword.Fortitude = (me.ladder || Developer.addLadderRW) ? [sdk.items.runes.El, sdk.items.runes.Sol, sdk.items.runes.Dol, sdk.items.runes.Lo] : false; // El + Sol + Dol + Lo -Runeword.Grief = (me.ladder || Developer.addLadderRW) ? [sdk.items.runes.Eth, sdk.items.runes.Tir, sdk.items.runes.Lo, sdk.items.runes.Mal, sdk.items.runes.Ral] : false; // Eth + Tir + Lo + Mal + Ral -Runeword.Harmony = (me.ladder || Developer.addLadderRW) ? [sdk.items.runes.Tir, sdk.items.runes.Ith, sdk.items.runes.Sol, sdk.items.runes.Ko] : false; // Tir + Ith + Sol + Ko -Runeword.Ice = (me.ladder || Developer.addLadderRW) ? [sdk.items.runes.Amn, sdk.items.runes.Shael, sdk.items.runes.Jah, sdk.items.runes.Lo] : false; // Amn + Shael + Jah + Lo -Runeword.Infinity = (me.ladder || Developer.addLadderRW) ? [sdk.items.runes.Ber, sdk.items.runes.Mal, sdk.items.runes.Ber, sdk.items.runes.Ist] : false; // Ber + Mal + Ber + Ist -Runeword.Insight = (me.ladder || Developer.addLadderRW) ? [sdk.items.runes.Ral, sdk.items.runes.Tir, sdk.items.runes.Tal, sdk.items.runes.Sol] : false; // Ral + Tir + Tal + Sol -Runeword.LastWish = (me.ladder || Developer.addLadderRW) ? [sdk.items.runes.Jah, sdk.items.runes.Mal, sdk.items.runes.Jah, sdk.items.runes.Sur, sdk.items.runes.Jah, sdk.items.runes.Ber] : false; // Jah + Mal + Jah + Sur + Jah + Ber -Runeword.Lawbringer = (me.ladder || Developer.addLadderRW) ? [sdk.items.runes.Amn, sdk.items.runes.Lem, sdk.items.runes.Ko] : false; // Amn + Lem + Ko -Runeword.Oath = (me.ladder || Developer.addLadderRW) ? [sdk.items.runes.Shael, sdk.items.runes.Pul, sdk.items.runes.Mal, sdk.items.runes.Lum] : false; // Shael + Pul + Mal + Lum -Runeword.Obedience = (me.ladder || Developer.addLadderRW) ? [sdk.items.runes.Hel, sdk.items.runes.Ko, sdk.items.runes.Thul, sdk.items.runes.Eth, sdk.items.runes.Fal] : false; // Hel + Ko + Thul + Eth + Fal -Runeword.Phoenix = (me.ladder || Developer.addLadderRW) ? [sdk.items.runes.Vex, sdk.items.runes.Vex, sdk.items.runes.Lo, sdk.items.runes.Jah] : false; // Vex + Vex + Lo + Jah -Runeword.Pride = (me.ladder || Developer.addLadderRW) ? [sdk.items.runes.Cham, sdk.items.runes.Sur, sdk.items.runes.Io, sdk.items.runes.Lo] : false; // Cham + Sur + Io + Lo -Runeword.Rift = (me.ladder || Developer.addLadderRW) ? [sdk.items.runes.Hel, sdk.items.runes.Ko, sdk.items.runes.Lem, sdk.items.runes.Gul] : false; // Hel + Ko + Lem + Gul -Runeword.Spirit = (me.ladder || Developer.addLadderRW) ? [sdk.items.runes.Tal, sdk.items.runes.Thul, sdk.items.runes.Ort, sdk.items.runes.Amn] : false; // Tal + Thul + Ort + Amn -Runeword.VoiceofReason = (me.ladder || Developer.addLadderRW) ? [sdk.items.runes.Lem, sdk.items.runes.Ko, sdk.items.runes.El, sdk.items.runes.Eld] : false; // Lem + Ko + El + Eld -Runeword.Wrath = (me.ladder || Developer.addLadderRW) ? [sdk.items.runes.Pul, sdk.items.runes.Lum, sdk.items.runes.Ber, sdk.items.runes.Mal] : false; // Pul + Lum + Ber + Mal -Runeword.PDiamondShield = [sdk.items.gems.Perfect.Diamond, sdk.items.gems.Perfect.Diamond, sdk.items.gems.Perfect.Diamond]; +Runeword.PDiamondShield = Runeword.addRuneword( + "PDiamondShield", 3, + [sdk.items.gems.Perfect.Diamond, sdk.items.gems.Perfect.Diamond, sdk.items.gems.Perfect.Diamond], + [sdk.items.type.AnyShield] +); +/** + * Get the base item based on classid and runeword recipe + * @param {runeword} runeword + * @param {ItemUnit | number} base - item or classid + * @param {number} [ethFlag] + * @param {boolean} [reroll] - optional reroll argument = gets a runeword that needs rerolling + * @returns {ItemUnit | false} + */ Runewords.getBase = function (runeword, base, ethFlag, reroll) { - let item = typeof base === "object" ? base : me.getItem(base, sdk.items.mode.inStorage); + let item = typeof base === "object" + ? base + : me.getItem(base, sdk.items.mode.inStorage); if (item) { do { - if (item && item.quality < sdk.items.quality.Magic && item.sockets === runeword.length) { - /* check if item has items socketed in it - better check than getFlag(sdk.items.flags.Runeword) because randomly socketed items return false for it - */ - - if ((!reroll && !item.getItem() && Item.betterBaseThanWearing(item, Developer.debugging.baseCheck)) || - (reroll && item.getItem() && !NTIP.CheckItem(item, this.pickitEntries) && !Item.autoEquipCheckMerc(item, true) && !Item.autoEquipCheck(item, true))) { + if (item && item.quality < sdk.items.quality.Magic + && item.sockets === runeword.sockets && runeword.itemTypes.includes(item.itemType)) { + /** + * check if item has items socketed in it + * better check than getFlag(sdk.items.flags.Runeword) because randomly socketed items return false for it + */ + + if ((!reroll && !item.getItem() && Item.betterBaseThanWearing(item, Developer.debugging.baseCheck)) + || (reroll && item.getItem() && !NTIP.CheckItem(item, this.pickitEntries) && !Item.autoEquipCheckMerc(item, true) && !Item.autoEquipCheck(item, true))) { if (!ethFlag || (ethFlag === Roll.Eth && item.ethereal) || (ethFlag === Roll.NonEth && !item.ethereal)) { return copyUnit(item); } From f369906bb6a9358d389af2f1f979912b0024f361 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Sun, 12 Mar 2023 13:46:11 -0400 Subject: [PATCH 066/263] Data modules from Modules\ -> Modules\GameData - cleaned up the folder --- libs/SoloPlay/Modules/{ => GameData}/AreaData.js | 0 libs/SoloPlay/Modules/{ => GameData}/GameData.js | 0 libs/SoloPlay/Modules/{ => GameData}/LocaleStringID.js | 0 libs/SoloPlay/Modules/{ => GameData}/MissileData.js | 0 libs/SoloPlay/Modules/{ => GameData}/MonsterData.js | 0 libs/SoloPlay/Modules/{ => GameData}/PotData.js | 0 6 files changed, 0 insertions(+), 0 deletions(-) rename libs/SoloPlay/Modules/{ => GameData}/AreaData.js (100%) rename libs/SoloPlay/Modules/{ => GameData}/GameData.js (100%) rename libs/SoloPlay/Modules/{ => GameData}/LocaleStringID.js (100%) rename libs/SoloPlay/Modules/{ => GameData}/MissileData.js (100%) rename libs/SoloPlay/Modules/{ => GameData}/MonsterData.js (100%) rename libs/SoloPlay/Modules/{ => GameData}/PotData.js (100%) diff --git a/libs/SoloPlay/Modules/AreaData.js b/libs/SoloPlay/Modules/GameData/AreaData.js similarity index 100% rename from libs/SoloPlay/Modules/AreaData.js rename to libs/SoloPlay/Modules/GameData/AreaData.js diff --git a/libs/SoloPlay/Modules/GameData.js b/libs/SoloPlay/Modules/GameData/GameData.js similarity index 100% rename from libs/SoloPlay/Modules/GameData.js rename to libs/SoloPlay/Modules/GameData/GameData.js diff --git a/libs/SoloPlay/Modules/LocaleStringID.js b/libs/SoloPlay/Modules/GameData/LocaleStringID.js similarity index 100% rename from libs/SoloPlay/Modules/LocaleStringID.js rename to libs/SoloPlay/Modules/GameData/LocaleStringID.js diff --git a/libs/SoloPlay/Modules/MissileData.js b/libs/SoloPlay/Modules/GameData/MissileData.js similarity index 100% rename from libs/SoloPlay/Modules/MissileData.js rename to libs/SoloPlay/Modules/GameData/MissileData.js diff --git a/libs/SoloPlay/Modules/MonsterData.js b/libs/SoloPlay/Modules/GameData/MonsterData.js similarity index 100% rename from libs/SoloPlay/Modules/MonsterData.js rename to libs/SoloPlay/Modules/GameData/MonsterData.js diff --git a/libs/SoloPlay/Modules/PotData.js b/libs/SoloPlay/Modules/GameData/PotData.js similarity index 100% rename from libs/SoloPlay/Modules/PotData.js rename to libs/SoloPlay/Modules/GameData/PotData.js From aa1a30687a256e1b74fac03684537cb0eb0397d3 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Sun, 12 Mar 2023 13:48:26 -0400 Subject: [PATCH 067/263] Formatting mostly --- libs/SoloPlay/Modules/Clear.js | 2 +- libs/SoloPlay/Modules/Events.js | 29 ++++++++++++++++------ libs/SoloPlay/Modules/GameData/AreaData.js | 4 +-- libs/SoloPlay/Modules/GameData/GameData.js | 14 +++++------ libs/SoloPlay/Modules/Guard.js | 2 +- libs/SoloPlay/Modules/TownGuard.js | 2 +- 6 files changed, 34 insertions(+), 19 deletions(-) diff --git a/libs/SoloPlay/Modules/Clear.js b/libs/SoloPlay/Modules/Clear.js index 4369fc1a..421644a4 100644 --- a/libs/SoloPlay/Modules/Clear.js +++ b/libs/SoloPlay/Modules/Clear.js @@ -16,7 +16,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) { const sdk_1 = __importDefault(require("../../modules/sdk")); const Events_1 = require("./Events"); const Coords_1 = require("./Coords"); - const MissileData_1 = __importDefault(require("./MissileData")); + const MissileData_1 = __importDefault(require("./GameData/MissileData")); const defaults = { range: 14, spectype: 0, diff --git a/libs/SoloPlay/Modules/Events.js b/libs/SoloPlay/Modules/Events.js index 915d8c07..3e483790 100644 --- a/libs/SoloPlay/Modules/Events.js +++ b/libs/SoloPlay/Modules/Events.js @@ -30,13 +30,22 @@ } // Generic type S to give to EventHandler to typehint this function gets the same this as where the event is registered Events.prototype.on = function (key, handler, handlerType) { - if (handlerType === void 0) { handlerType = handlers; } + if (handlerType === void 0) { + handlerType = handlers; + } + let map, set; - !handlerType.has(this) ? handlerType.set(this, map = new Map) : map = handlerType.get(this); - !map.has(key) ? map.set(key, set = []) : set = map.get(key); + + !handlerType.has(this) + ? handlerType.set(this, map = new Map) + : map = handlerType.get(this); + !map.has(key) + ? map.set(key, set = []) + : set = map.get(key); // Add this handler, since it has to be unique we dont need to check if it exists set.push(handler); - console.debug(set); + // console.debug(set, map); + // console.trace(); return this; }; Events.prototype.once = function (key, handler) { @@ -46,8 +55,12 @@ let _this = this; [handlers, onceHandlers].forEach(function (handlerType) { let map, set, index; - !handlerType.has(_this) ? handlerType.set(_this, map = new Map) : map = handlerType.get(_this); - !map.has(key) ? map.set(key, set = []) : set = map.get(key); + !handlerType.has(_this) + ? handlerType.set(_this, map = new Map) + : map = handlerType.get(_this); + !map.has(key) + ? map.set(key, set = []) + : set = map.get(key); index = set.indexOf(handler); if (index > -1) { set.splice(index, 1); @@ -66,7 +79,9 @@ let restSet = ((_b = handlers.get(this)) === null || _b === void 0 ? void 0 : _b.get(key)); // store callbacks in a set to avoid duplicate handlers let callbacks = __spreadArray(__spreadArray([], (onceSet && onceSet.splice(0, onceSet.length) || [])), restSet ? restSet : []); - callbacks.forEach(function (el) { return el.apply(_this, args); }); + callbacks.forEach(function (el) { + return el.apply(_this, args); + }); return this; }; return Events; diff --git a/libs/SoloPlay/Modules/GameData/AreaData.js b/libs/SoloPlay/Modules/GameData/AreaData.js index 2b947505..edf5e582 100644 --- a/libs/SoloPlay/Modules/GameData/AreaData.js +++ b/libs/SoloPlay/Modules/GameData/AreaData.js @@ -49,11 +49,11 @@ Level: getBaseStat("levels", index, ["MonLvl1Ex", "MonLvl2Ex", "MonLvl3Ex"][me.diff]), Size: (() => { if (index === 111) { // frigid highlands doesn't specify size, manual measurement - return {x: 210, y: 710}; + return { x: 210, y: 710 }; } if (index === 112) { // arreat plateau doesn't specify size, manual measurement - return {x: 690, y: 230}; + return { x: 690, y: 230 }; } return { diff --git a/libs/SoloPlay/Modules/GameData/GameData.js b/libs/SoloPlay/Modules/GameData/GameData.js index 41c70851..df2f7016 100644 --- a/libs/SoloPlay/Modules/GameData/GameData.js +++ b/libs/SoloPlay/Modules/GameData/GameData.js @@ -11,8 +11,8 @@ const MonsterData = require("./MonsterData"); const AreaData = require("./AreaData"); const MissileData = require("./MissileData"); - const Coords_1 = require("./Coords"); - const sdk = require("../../modules/sdk"); + const Coords_1 = require("../Coords"); + const sdk = require("../../../modules/sdk"); function isAlive(unit) { return Boolean(unit && unit.hp); @@ -92,7 +92,7 @@ return Math.round(levels / total); }, areaImmunities: function (areaID) { - let resists = {Physical: 0, Magic: 0, Fire: 0, Lightning: 0, Cold: 0, Poison: 0}; + let resists = { Physical: 0, Magic: 0, Fire: 0, Lightning: 0, Cold: 0, Poison: 0 }; AreaData[areaID].forEachMonsterAndMinion(mon => { for (let k in resists) { @@ -408,7 +408,7 @@ }, bestForm: function (skillID) { if (this.shiftState() === "human" && this.humanBanned[skillID]) { - let highest = {ID: 0, Level: 0}; + let highest = { ID: 0, Level: 0 }; if (!this.wolfBanned[skillID] && this.skillLevel(223) > highest.Level) { highest.ID = 223; @@ -536,7 +536,7 @@ }, skillDamage: function (skillID, unit) { // TODO: caluclate basic attack damage - if (skillID === 0) return {type: "Physical", pmin: 2, pmax: 8, min: 0, max: 0}; // short sword, no reqs + if (skillID === 0) return { type: "Physical", pmin: 2, pmax: 8, min: 0, max: 0 }; // short sword, no reqs if (this.skillLevel(skillID) < 1) { return { @@ -862,7 +862,7 @@ case sdk.skills.FireBall: case sdk.skills.GlacialSpike: case sdk.skills.ChargedBolt: - let {x, y} = unit; + let { x, y } = unit; if (!Attack.validSpot(x, y, skillID, unit.classid)) { return 0; @@ -1367,7 +1367,7 @@ // Or add it and return the value || ( ( - uniqueSkills.push({skillId: x.skill, used: 0}) + uniqueSkills.push({ skillId: x.skill, used: 0 }) && false ) || uniqueSkills[uniqueSkills.length - 1] diff --git a/libs/SoloPlay/Modules/Guard.js b/libs/SoloPlay/Modules/Guard.js index 875706c4..6183e8ac 100644 --- a/libs/SoloPlay/Modules/Guard.js +++ b/libs/SoloPlay/Modules/Guard.js @@ -71,7 +71,7 @@ Worker.push(function highPrio() { Worker.push(highPrio); if ((getTickCount() - sendStack) < 200 || (sendStack = getTickCount()) && false) return true; - Messaging.send({Guard: {stack: (new Error).stack}}); + Messaging.send({ Guard: { stack: (new Error).stack } }); return true; }); diff --git a/libs/SoloPlay/Modules/TownGuard.js b/libs/SoloPlay/Modules/TownGuard.js index 65ecd1da..42a9b78d 100644 --- a/libs/SoloPlay/Modules/TownGuard.js +++ b/libs/SoloPlay/Modules/TownGuard.js @@ -70,7 +70,7 @@ Worker.push(function highPrio() { Worker.push(highPrio); if ((getTickCount() - sendStack) < 200 || (sendStack = getTickCount()) && false) return true; - Messaging.send({TownGuard: {stack: (new Error).stack}}); + Messaging.send({ TownGuard: { stack: (new Error).stack } }); return true; }); From 4d557696aec4c7fb249e2ee8c3dc5002c8c9450c Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Sun, 12 Mar 2023 14:21:30 -0400 Subject: [PATCH 068/263] Update GameData.js - add some handling for druid fire skills --- libs/SoloPlay/Modules/GameData/GameData.js | 165 ++++++++++++++++----- 1 file changed, 129 insertions(+), 36 deletions(-) diff --git a/libs/SoloPlay/Modules/GameData/GameData.js b/libs/SoloPlay/Modules/GameData/GameData.js index df2f7016..0de3daab 100644 --- a/libs/SoloPlay/Modules/GameData/GameData.js +++ b/libs/SoloPlay/Modules/GameData/GameData.js @@ -534,9 +534,23 @@ return aps; }, + + /** + * @typedef skillDmgObj + * @property {string} type + * @property {number} pmin + * @property {number} pmax + * @property {number} min + * @property {number} max + * @property {boolean} [undeadOnly] + * + * @param {number} skillID + * @param {Monster} unit + * @returns {skillDmgObj} + */ skillDamage: function (skillID, unit) { // TODO: caluclate basic attack damage - if (skillID === 0) return { type: "Physical", pmin: 2, pmax: 8, min: 0, max: 0 }; // short sword, no reqs + if (skillID === sdk.skills.Attack) return { type: "Physical", pmin: 2, pmax: 8, min: 0, max: 0 }; // short sword, no reqs if (this.skillLevel(skillID) < 1) { return { @@ -548,7 +562,8 @@ }; } - let dmg = this.baseSkillDamage(skillID), mastery = 1, psynergy = 1, synergy = 1, shots = 1, sl = 0; + let dmg = this.baseSkillDamage(skillID); + let mastery = 1, psynergy = 1, synergy = 1, shots = 1, sl = 0; if (this.synergyCalc[skillID]) { let sc = this.synergyCalc[skillID]; @@ -577,29 +592,14 @@ switch (dmg.type) { case "Fire": // fire mastery - mastery = 1 + GameData.myReference.getStat(sdk.stats.PassiveFireMastery) / 100; - dmg.min *= mastery; - dmg.max *= mastery; - break; case "Lightning": // lightning mastery - mastery = 1 + GameData.myReference.getStat(sdk.stats.PassiveLightningMastery) / 100; - dmg.min *= mastery; - dmg.max *= mastery; - break; case "Cold": // cold mastery - mastery = 1 + GameData.myReference.getStat(sdk.stats.PassiveColdMastery) / 100; - dmg.min *= mastery; - dmg.max *= mastery; - break; case "Poison": // poison mastery - mastery = 1 + GameData.myReference.getStat(sdk.stats.PassivePoisonMastery) / 100; - dmg.min *= mastery; - dmg.max *= mastery; - break; case "Magic": // magic mastery - mastery = 1 + GameData.myReference.getStat(sdk.stats.PassiveMagMastery) / 100; + mastery = 1 + GameData.myReference.getStat(this.masteryMap[dmg.type]) / 100; dmg.min *= mastery; dmg.max *= mastery; + break; } @@ -690,22 +690,22 @@ } break; - case 221: // raven - a hit per raven + case sdk.skills.Raven: // raven - a hit per raven shots = Math.min(5, this.skillLevel(221)); // 1-5 ravens dmg.pmin *= shots; dmg.pmax *= shots; break; - case 227: // spirit wolf - a hit per wolf + case sdk.skills.SummonSpiritWolf: // spirit wolf - a hit per wolf shots = Math.min(5, this.skillLevel(227)); dmg.pmin *= shots; dmg.pmax *= shots; break; - case 237: // dire wolf - a hit per wolf + case sdk.skills.SummonDireWolf: // dire wolf - a hit per wolf shots = Math.min(3, this.skillLevel(237)); dmg.pmin *= shots; dmg.pmax *= shots; break; - case 240: // twister + case sdk.skills.Twister: // twister dmg.pmin *= 3; dmg.pmax *= 3; break; @@ -717,7 +717,6 @@ dmg.min *= 5; // can have 5 traps out at a time dmg.max *= 5; break; - case sdk.skills.StaticField: if (!(unit instanceof Unit)) { break; @@ -767,17 +766,34 @@ return dmg; }, - // todo - build me metadata - then use it to calulate a range of skills rather than redo the exact same calculations - // example - trying to check the damage of blizard and then frozen orb - // currently it would check our stats, then check amp and conviction - those could all be pre-built as they aren't going to change + + /** + * Calculate actual average damage this skill does taking into account splash/range of skill + * @param {number} skillID + * @param {Monster | number | string} unit + * @returns {number} + * @todo + * - build me metadata - then use it to calulate a range of skills rather than redo the exact same calculations. + * example: trying to check the damage of blizard and then frozen orb, + * currently it would check our stats, then check amp and conviction - those could all be pre-built as they aren't going to change + */ avgSkillDamage: function (skillID, unit) { if (skillID === undefined || unit === undefined || !skillID || !unit || !Skill.canUse(skillID)) return 0; - let skillToCheck, avgDmg; + const ampDmg = Skill.canUse(sdk.skills.AmplifyDamage) ? 100 : (Skill.canUse(sdk.skills.Decrepify) ? 50 : 0); + + /** + * + * @param {skillDmgObj} skillData + * @param {Monster | number | string} unit + * @returns + */ const getTotalDmg = function (skillData, unit) { - let ampDmg = Skill.canUse(66) ? 100 : (Skill.canUse(87) ? 50 : 0); - let avgPDmg = (skillData.pmin + skillData.pmax) / 2, totalDmg = 0, avgDmg = (skillData.min + skillData.max) / 2; - //let hp = GameData.monsterMaxHP(typeof unit === 'number' ? unit : unit.classid, me.area); - let conviction = GameData.getConviction(), isUndead = (typeof unit === "number" ? MonsterData[unit].Undead : MonsterData[unit.classid].Undead); + const isUndead = (typeof unit === "number" ? MonsterData[unit].Undead : MonsterData[unit.classid].Undead); + const conviction = GameData.getConviction(); + let totalDmg = 0; + let avgPDmg = (skillData.pmin + skillData.pmax) / 2; + let avgDmg = (skillData.min + skillData.max) / 2; + if (avgPDmg > 0) { let presist = GameData.monsterResist(unit, "Physical"); presist -= (presist >= 100 ? ampDmg / 5 : ampDmg); @@ -795,6 +811,13 @@ } return totalDmg; }; + + /** + * @param {number} skill + * @param {number} splash + * @param {Monster} target + * @returns {number} + */ const calculateSplashDamage = function (skill, splash, target) { return getUnits(sdk.unittype.Monster) .filter((mon) => mon.attackable && getDistance(target, mon) < splash) @@ -803,6 +826,12 @@ return acc + getTotalDmg(_a, cur); }, 0); }; + + /** + * @param {number} skill + * @param {Monster} target + * @returns {number} + */ const calculateChainDamage = function (skill, target) { skill === undefined && (skill = -1); let rawDmg = 0, totalDmg = 0, range = 0, hits = 0; @@ -833,10 +862,12 @@ return totalDmg; } }; + const calculateRawStaticDamage = function (distanceUnit) { distanceUnit === undefined && (distanceUnit = me); if (!Skill.canUse(sdk.skills.StaticField)) return 0; - let range = Skill.getRange(sdk.skills.StaticField), cap = (me.gametype === sdk.game.gametype.Classic ? 1 : [1, 25, 50][me.diff]); + const range = Skill.getRange(sdk.skills.StaticField); + const cap = (me.gametype === sdk.game.gametype.Classic ? 1 : [1, 25, 50][me.diff]); const pierce = me.getStat(sdk.stats.PierceLtng); return getUnits(sdk.unittype.Monster) .filter(function (mon) { @@ -856,19 +887,82 @@ return acc + (actualDamage); }, 0); }; + + const calculateThroughDamage = function () { + // determine maximum potential distance of this missile + // build points from me -> monster -> max distance + // iterate points checking if any monsters are in the path + // check collision at each point and break if we encounter a missisle blocker + // special considerations for molten boulder: + // - check monster size, based on size the boulder may knock back or go through them + // - if we encounter a collision that causes the boulder to burst, add explosion damage + // + }; + + /** + * + * @param {Monster} unit + */ + const calcVolcanoDamage = function (unit) { + let velocity = unit.currentVelocity; + /** @type {skillDmgObj} */ + let baseDmg = GameData.skillDamage(sdk.skills.Volcano, unit); + // since these are random, lets take them into account but not at their full value + let missleDmg = Object.assign({}, baseDmg); + missleDmg.pmin /= 2; + missleDmg.pmax /= 2; + missleDmg.min /= 2; + missleDmg.max /= 2; + // sorta guess work for now, needs improvment to really figure out on average how many times the actual + // volcano damages the monster cast on based on size/speed + let modifier = (!unit.isMoving || velocity === 1) ? 5 : velocity === 2 ? 3 : 1; + if (modifier > 1) { + baseDmg.pmin *= modifier; + baseDmg.pmax *= modifier; + baseDmg.min *= modifier; + baseDmg.max *= modifier; + } + + // sum the total in the range of the volcano missiles + // what about monsters just directly on the volcano? + return getUnits(sdk.unittype.Monster) + .filter((mon) => mon.attackable && getDistance(unit, mon) < 15) + .reduce(function (acc, cur) { + return acc + getTotalDmg(missleDmg, cur); + }, getTotalDmg(baseDmg, unit)); + }; + + /** + * @todo some skills need special handling + * - Bone spear and Lightning both pierce enemies in a straight path, need to calculate include monsters in that path + * to the total damage done as this can make the difference between wanting to use this skill vs another + * - Fire Wall is similar only seems to be a random angle + * - Inferno/Artic blast same as bonespear/lightning + * - Molten boulder needs to take into account monster size and angle of cast + * - Zeal, can hit same enemy multiple times or multiple enemies, would change total damage applied based on enemy targetted so needs to be handled + * Others? + */ switch (skillID) { case sdk.skills.Blizzard: case sdk.skills.Meteor: case sdk.skills.FireBall: case sdk.skills.GlacialSpike: case sdk.skills.ChargedBolt: + case sdk.skills.Fissure: let { x, y } = unit; + let rad = skillID === sdk.skills.Volcano ? 15 : skillID === sdk.skills.Fissure ? 10 : 5; if (!Attack.validSpot(x, y, skillID, unit.classid)) { return 0; } - return calculateSplashDamage(skillID, 4, unit); + return calculateSplashDamage(skillID, rad, unit); + case sdk.skills.Volcano: + if (!Attack.validSpot(unit.x, unit.y, skillID, unit.classid)) { + return 0; + } + + return calcVolcanoDamage(unit); case sdk.skills.FrostNova: case sdk.skills.Nova: return calculateSplashDamage(skillID, 6, unit); @@ -877,8 +971,7 @@ case sdk.skills.ChainLightning: return calculateChainDamage(skillID, unit); default: - skillToCheck = this.skillDamage(skillID, unit); - return getTotalDmg(skillToCheck, unit); + return getTotalDmg(this.skillDamage(skillID, unit), unit); } }, allSkillDamage: function (unit) { From 186f5abc29b043ae2ab6cd8ec96f6f10b59c58f2 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Sun, 12 Mar 2023 14:23:45 -0400 Subject: [PATCH 069/263] Update ItemOverrides.js - Fix hoarding items we can't use because they are better than what we are currently using. They end up taking up too much space, only keep those that are wanted for final gear or are at least close to being able to be used --- libs/SoloPlay/Functions/ItemOverrides.js | 104 ++++++++++++++++++----- 1 file changed, 84 insertions(+), 20 deletions(-) diff --git a/libs/SoloPlay/Functions/ItemOverrides.js b/libs/SoloPlay/Functions/ItemOverrides.js index 61a1723c..921a6eef 100644 --- a/libs/SoloPlay/Functions/ItemOverrides.js +++ b/libs/SoloPlay/Functions/ItemOverrides.js @@ -182,21 +182,40 @@ Item.autoEquipCheck = function (item, basicCheck = false) { if (tier > equippedItem.tierScore) { return true; } - } else if (tier > equippedItem.tier && (!basicCheck ? this.canEquip(item) || !item.identified : true)) { - if (item.twoHanded && !me.barbarian) { - if (tier < Item.getEquipped(sdk.body.RightArm).tier + Item.getEquipped(sdk.body.LeftArm).tier) return false; - } + } else if (tier > equippedItem.tier && (basicCheck ? true : this.canEquip(item) || !item.identified)) { + if (Item.canEquip(item)) { + if (item.twoHanded && !me.barbarian) { + if (tier < Item.getEquipped(sdk.body.RightArm).tier + Item.getEquipped(sdk.body.LeftArm).tier) return false; + } - if (!me.barbarian && bodyLoc[i] === sdk.body.LeftArm && Item.getEquipped(bodyLoc[i]).tier === -1) { - if (Item.getEquipped(sdk.body.RightArm).twoHanded && tier < Item.getEquipped(sdk.body.RightArm).tier) return false; - } + if (!me.barbarian && bodyLoc[i] === sdk.body.LeftArm && Item.getEquipped(bodyLoc[i]).tier === -1) { + if (Item.getEquipped(sdk.body.RightArm).twoHanded && tier < Item.getEquipped(sdk.body.RightArm).tier) return false; + } + + return true; + } else { + // keep wanted final gear items + if (NTIP.CheckItem(item, NTIP.FinalGear.list) === Pickit.Result.WANTED) { + return true; + } + + let [lvlReq, strReq, dexReq] = [item.getStat(sdk.stats.LevelReq), item.strreq, item.dexreq]; + + // todo - bit hacky, better way would be to track what stats are going to be allocated next + if ((lvlReq - me.charlvl > 5) || (strReq - me.trueStr > 10) || (dexReq - me.trueDex > 10)) { + return false; + } - // lets double check that this is the highest tied'd item of this type in our storage - let betterItem = me.getItemsEx() - .filter(el => el.isInStorage && el.gid !== item.gid && el.identified && Item.getBodyLoc(el).includes(bodyLoc[i])) - .some(el => NTIP.GetTier(el) > tier); + // if we can't equip it, but it's a good item, keep it as long as we have space for it + // lets double check that this is the highest tied'd item of this type in our storage + let betterItem = me.getItemsEx() + .filter(el => el.isInStorage && el.gid !== item.gid && el.identified && Item.getBodyLoc(el).includes(bodyLoc[i])) + .sort((a, b) => NTIP.GetTier(b) - NTIP.GetTier(a)) + .find(el => NTIP.GetTier(el) > tier); + if (!betterItem) return true; - return !betterItem; + return Storage.Stash.CanFit(item) && Storage.Stash.UsedSpacePercent() < 65; + } } } } @@ -227,8 +246,10 @@ Item.autoEquip = function (task = "") { me.switchWeapons(sdk.player.slot.Main); const sortEq = (a, b) => { - if (Item.canEquip(a)) return -1; - if (Item.canEquip(b)) return 1; + let [prioA, prioB] = [Item.canEquip(a), Item.canEquip(b)]; + if (prioA && prioB) return NTIP.GetTier(b) - NTIP.GetTier(a); + if (prioA) return -1; + if (prioB) return 1; return 0; }; @@ -596,7 +617,11 @@ Item.autoEquipSecondary = function (task = "") { */ Item.hasMercTier = (item) => Config.AutoEquip && me.expansion && NTIP.GetMercTier(item) > 0; -// need to re-work using char data so we can shop/keep items if merc is dead *but* we have enough to revive him and buy the item and enough space +/** + * @param {ItemUnit} item + * @param {number} bodyLoc + * @todo re-work using char data so we can shop/keep items if merc is dead *but* we have enough to revive him and buy the item and enough space + */ Item.canEquipMerc = function (item, bodyLoc) { if (item.type !== sdk.unittype.Item || me.classic) return false; let mercenary = me.getMercEx(); @@ -615,6 +640,10 @@ Item.canEquipMerc = function (item, bodyLoc) { return true; }; +/** + * @param {ItemUnit} item + * @param {number} bodyLoc + */ Item.equipMerc = function (item, bodyLoc) { let mercenary = me.getMercEx(); @@ -684,6 +713,9 @@ Item.getMercEquipped = function (bodyLoc = -1) { }; }; +/** + * @param {ItemUnit} item + */ Item.getBodyLocMerc = function (item) { let mercenary = me.getMercEx(); @@ -724,14 +756,24 @@ Item.autoEquipCheckMerc = function (item, basicCheck = false) { if (!Config.AutoEquip) return true; if (Config.AutoEquip && !me.getMercEx()) return false; - let tier = NTIP.GetMercTier(item), bodyLoc = Item.getBodyLocMerc(item); + let tier = NTIP.GetMercTier(item); + let bodyLoc = Item.getBodyLocMerc(item); if (tier > 0 && bodyLoc) { for (let i = 0; i < bodyLoc.length; i += 1) { let oldTier = Item.getMercEquipped(bodyLoc[i]).tier; // Low tier items shouldn't be kept if they can't be equipped - if (tier > oldTier && (!basicCheck ? Item.canEquipMerc(item) || !item.identified : true)) { - return true; + if (tier > oldTier) { + if (Item.canEquipMerc(item) || !item.identified) { + return true; + } else if (basicCheck) { + // keep wanted final gear items + if (NTIP.CheckItem(item, NTIP.FinalGear.list) === Pickit.Result.WANTED) { + return true; + } + + return false; + } } } } @@ -1126,7 +1168,7 @@ Item.removeItemsMerc = function () { /** * @param {ItemUnit} item */ - Item.autoEquipCharmCheck = function (item = undefined) { + Item.autoEquipCharmCheck = function (item) { if (!item || NTIP.GetCharmTier(item) <= 0 || !item.isCharm) return false; // Annhilus, Hellfire Torch, Gheeds - Handled by a different function so return true to keep if (item.isCharm && item.unique) return true; @@ -1139,6 +1181,7 @@ Item.removeItemsMerc = function () { if (!items.length) return true; let quantityCap = NTIP.getInvoQuantity(item); + let have = 0; let charms = Item.autoEquipCharmSort(items); let charmType = Item.getCharmType(item); let cInfo, newList = []; @@ -1200,18 +1243,39 @@ Item.removeItemsMerc = function () { case "magicfind": case "damage": case "elemental": + have = charms[charmType].length; lowestCharm = charms[charmType].last(); if ((charms[charmType].findIndex(c => c.gid === lowestCharm.gid) + 1) > quantityCap) return false; break; default: + have = charms.backup.length; lowestCharm = charms.backup.last(); if ((charms.backup.findIndex(c => c.gid === lowestCharm.gid) + 1) > quantityCap) return false; + // console.debug("Lowest Charm index " + (charms.backup.findIndex(c => c.gid === lowestCharm.gid)) + " out of " + charms.backup.length); break; } - return !!lowestCharm ? !!(NTIP.GetCharmTier(item) >= NTIP.GetCharmTier(lowestCharm) || item.gid === lowestCharm.gid) : true; + if (!lowestCharm) { + // console.debug("Didn't find any other charms of this type " + charmType); + return true; + } + + if (item.gid === lowestCharm.gid) { + // console.debug("Same charm"); + return true; + } + + let [tierParamItem, tierLowestItem] = [NTIP.GetCharmTier(item), NTIP.GetCharmTier(lowestCharm)]; + + if (tierParamItem === tierLowestItem) { + // console.debug("Same tier value"); + // super hacky - arbritrary comparsion of xpos if the tier value is the same + return (have < quantityCap) || (item.isInInventory && lowestCharm.isInInventory && item.x > lowestCharm.y) || (item.isInInventory && !lowestCharm.isInInventory); + } + + return (tierParamItem >= tierLowestItem); }; Item.initCharms = function () { From ffa90682dcd37e61ed6fdb8fe496a323cde557f0 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Sun, 12 Mar 2023 14:30:24 -0400 Subject: [PATCH 070/263] Update the background workers - Mostly cleanup and restructuring the handlers --- libs/SoloPlay/Workers/AutoBuildWorker.js | 279 ------------------ libs/SoloPlay/Workers/EventEmitter.js | 89 ++++++ libs/SoloPlay/Workers/EventHandler.js | 116 ++++++++ libs/SoloPlay/Workers/EventWorker.js | 163 ---------- .../{TownChickenWorker.js => TownChicken.js} | 59 ++-- 5 files changed, 229 insertions(+), 477 deletions(-) delete mode 100644 libs/SoloPlay/Workers/AutoBuildWorker.js create mode 100644 libs/SoloPlay/Workers/EventEmitter.js create mode 100644 libs/SoloPlay/Workers/EventHandler.js delete mode 100644 libs/SoloPlay/Workers/EventWorker.js rename libs/SoloPlay/Workers/{TownChickenWorker.js => TownChicken.js} (93%) diff --git a/libs/SoloPlay/Workers/AutoBuildWorker.js b/libs/SoloPlay/Workers/AutoBuildWorker.js deleted file mode 100644 index a90efa9b..00000000 --- a/libs/SoloPlay/Workers/AutoBuildWorker.js +++ /dev/null @@ -1,279 +0,0 @@ -/** -* @filename AutoBuildWorker.js -* @author theBGuy -* @desc worker thread to handle in game events for Kolbot-SoloPlay -* -*/ - -/** - * @todo make this actually work. Issue is if we interrupt a task and peform an event function, once that is over we've desynced from - * whatever we were orignally doing. In some cases this might be fine to just end whatever it was we were running - * e.g. after receiving finishDen we could kill the den script and perform the event function. We would be able to continue with the next - * script normally afterwards. We can't do that for something like diablo lightning dodge or baal wave skipping though, in those cases it - * might be fine to just continue because in theory we shouldn't be too far from our orginal action so minor desync in physical coordinates - * but not completly different area. We wouldn't be able to do that for diablo clone though. - */ -(function() { - let debug = !!Config.AutoBuild.DebugMode, prevLevel = me.charlvl; - const usingFinalBuild = !["Start", "Stepping", "Leveling"].includes(Config.AutoBuild.Template); - const SPEND_POINTS = true; // For testing, it actually allows skill and stat point spending. - const STAT_ID_TO_NAME = [ - getLocaleString(sdk.locale.text.Strength), - getLocaleString(sdk.locale.text.Energy), - getLocaleString(sdk.locale.text.Dexterity), - getLocaleString(sdk.locale.text.Vitality) - ]; - let currAutoBuild; - - // Will check if value exists in an Array - Array.prototype.contains = (val) => this.indexOf(val) > -1; - - function skillInValidRange (id) { - switch (me.classid) { - case sdk.player.class.Amazon: - return sdk.skills.MagicArrow <= id && id <= sdk.skills.LightningFury; - case sdk.player.class.Sorceress: - return sdk.skills.FireBolt <= id && id <= sdk.skills.ColdMastery; - case sdk.player.class.Necromancer: - return sdk.skills.AmplifyDamage <= id && id <= sdk.skills.Revive; - case sdk.player.class.Paladin: - return sdk.skills.Sacrifice <= id && id <= sdk.skills.Salvation; - case sdk.player.class.Barbarian: - return sdk.skills.Bash <= id && id <= sdk.skills.BattleCommand; - case sdk.player.class.Druid: - return sdk.skills.Raven <= id && id <= sdk.skills.Hurricane; - case sdk.player.class.Assassin: - return sdk.skills.FireBlast <= id && id <= sdk.skills.PhoenixStrike; - default: - return false; - } - } - - const gainedLevels = () => me.charlvl - prevLevel; - - function canSpendPoints () { - let unusedStatPoints = me.getStat(sdk.stats.StatPts); - let haveUnusedStatpoints = unusedStatPoints >= 5; // We spend 5 stat points per level up - let unusedSkillPoints = me.getStat(sdk.stats.NewSkills); - let haveUnusedSkillpoints = unusedSkillPoints >= 1; // We spend 1 skill point per level up - debug && AutoBuild.print("Stat points:", unusedStatPoints, " Skill points:", unusedSkillPoints); - return haveUnusedStatpoints && haveUnusedSkillpoints; - } - - function spendStatPoint (id) { - let unusedStatPoints = me.getStat(sdk.stats.StatPts); - if (SPEND_POINTS) { - useStatPoint(id); - AutoBuild.print("useStatPoint(" + id + "): " + STAT_ID_TO_NAME[id]); - } else { - AutoBuild.print("Fake useStatPoint(" + id + "): " + STAT_ID_TO_NAME[id]); - } - delay(100); // TODO: How long should we wait... if at all? - return (unusedStatPoints - me.getStat(sdk.stats.StatPts) === 1); // Check if we spent one point - } - - // TODO: What do we do if it fails? report/ignore/continue? - function spendStatPoints () { - if (currAutoBuild[me.charlvl] === undefined) return true; - let stats = currAutoBuild[me.charlvl].StatPoints; - let errorMessage = "\nInvalid stat point set in build template " + getTemplateFilename() + " at level " + me.charlvl; - let spentEveryPoint = true; - let unusedStatPoints = me.getStat(sdk.stats.StatPts); - let len = stats.length; - - if (Config.AutoStat.Enabled) { - return spentEveryPoint; - } - - if (len > unusedStatPoints) { - len = unusedStatPoints; - AutoBuild.print("Warning: Number of stats specified in your build template at level " + me.charlvl + " exceeds the available unused stat points" + - "\nOnly the first " + len + " stats " + stats.slice(0, len).join(", ") + " will be added"); - } - - // We silently ignore stats set to -1 - for (let i = 0; i < len; i++) { - let id = stats[i]; - let statIsValid = (typeof id === "number") && (0 <= id && id <= 3); - - if (id === -1) { - continue; - } else if (statIsValid) { - let preStatValue = me.getStat(id); - let pointSpent = spendStatPoint(id); - if (SPEND_POINTS) { - if (!pointSpent) { - spentEveryPoint = false; - AutoBuild.print("Attempt to spend point " + (i + 1) + " in " + STAT_ID_TO_NAME[id] + " may have failed!"); - } else if (debug) { - AutoBuild.print("Stat (" + (i + 1) + "/" + len + ") Increased " + STAT_ID_TO_NAME[id] + " from " + preStatValue + " to " + me.getStat(id)); - } - } - } else { - throw new Error("Stat id must be one of the following:\n0:" + STAT_ID_TO_NAME[0] + - ",\t1:" + STAT_ID_TO_NAME[1] + ",\t2:" + STAT_ID_TO_NAME[2] + ",\t3:" + STAT_ID_TO_NAME[3] + errorMessage); - } - } - - return spentEveryPoint; - } - - function getTemplateFilename () { - let buildType = Config.AutoBuild.Template; - let className = sdk.player.class.nameOf(me.classid); - let templateFilename = "SoloPlay/BuildFiles/" + className + "/" + className + "." + buildType + "Build.js"; - return templateFilename; - } - - function getRequiredSkills (id) { - function searchSkillTree (id) { - let results = []; - let skillTreeRight = getBaseStat("skills", id, sdk.stats.PreviousSkillRight); - let skillTreeMiddle = getBaseStat("skills", id, sdk.stats.PreviousSkillMiddle); - let skillTreeLeft = getBaseStat("skills", id, sdk.stats.PreviousSkillLeft); - - results.push(skillTreeRight); - results.push(skillTreeMiddle); - results.push(skillTreeLeft); - - for (let i = 0; i < results.length; i++) { - let skill = results[i]; - let skillInValidRange = (sdk.skills.Attack < skill && skill <= sdk.skills.PhoenixStrike) && (![sdk.skills.IdentifyScroll, sdk.skills.BookofIdentify, sdk.skills.TownPortalScroll, sdk.skills.BookofTownPortal].contains(skill)); - let hardPointsInSkill = me.getSkill(skill, sdk.skills.subindex.HardPoints); - - if (skillInValidRange && !hardPointsInSkill) { - requirements.push(skill); - searchSkillTree(skill); // search children; - } - } - } - - let requirements = []; - searchSkillTree(id); - const increasing = () => a - b; - return requirements.sort(increasing); - } - - function spendSkillPoint (id) { - let unusedSkillPoints = me.getStat(sdk.stats.NewSkills); - let skillName = getSkillById(id) + " (" + id + ")"; - if (SPEND_POINTS) { - useSkillPoint(id); - AutoBuild.print("useSkillPoint(): " + skillName); - } else { - AutoBuild.print("Fake useSkillPoint(): " + skillName); - } - delay(200); // TODO: How long should we wait... if at all? - return (unusedSkillPoints - me.getStat(sdk.stats.NewSkills) === 1); // Check if we spent one point - } - - function spendSkillPoints () { - if (currAutoBuild[me.charlvl] === undefined) return true; - let skills = currAutoBuild[me.charlvl].SkillPoints; - let errInvalidSkill = "\nInvalid skill point set in build template " + getTemplateFilename() + " for level " + me.charlvl; - let spentEveryPoint = true; - let unusedSkillPoints = me.getStat(sdk.stats.NewSkills); - let len = skills.length; - - if (Config.AutoSkill.Enabled) { - return spentEveryPoint; - } - - if (len > unusedSkillPoints) { - len = unusedSkillPoints; - AutoBuild.print("Warning: Number of skills specified in your build template at level " + me.charlvl + " exceeds the available unused skill points" + - "\nOnly the first " + len + " skills " + skills.slice(0, len).join(", ") + " will be added"); - } - - // We silently ignore skills set to -1 - for (let i = 0; i < len; i++) { - let id = skills[i]; - - if (id === -1) { - continue; - } else if (!skillInValidRange(id)) { - throw new Error("Skill id " + id + " is not a skill for your character class" + errInvalidSkill); - } - - let skillName = getSkillById(id) + " (" + id + ")"; - let requiredSkills = getRequiredSkills(id); - if (requiredSkills.length > 0) { - throw new Error("You need prerequisite skills " + requiredSkills.join(", ") + " before adding " + skillName + errInvalidSkill); - } - - let requiredLevel = getBaseStat("skills", id, sdk.stats.MinimumRequiredLevel); - if (me.charlvl < requiredLevel) { - throw new Error("You need to be at least level " + requiredLevel + " before you get " + skillName + errInvalidSkill); - } - - let pointSpent = spendSkillPoint(id); - - if (SPEND_POINTS) { - if (!pointSpent) { - spentEveryPoint = false; - AutoBuild.print("Attempt to spend skill point " + (i + 1) + " in " + skillName + " may have failed!"); - } else if (debug) { - let actualSkillLevel = me.getSkill(id, sdk.skills.subindex.SoftPoints); - AutoBuild.print("Skill (" + (i + 1) + "/" + len + ") Increased " + skillName + " by one (level: ", actualSkillLevel + ")"); - } - } - - delay(200); // TODO: How long should we wait... if at all? - } - - return spentEveryPoint; - } - - // Only load this in global scope - if (getScript(true).name.toLowerCase() === "libs\\soloplay\\soloplay.js") { - const Worker = require("../../modules/Worker"); - - let waitTick = getTickCount(); - currAutoBuild = usingFinalBuild ? finalBuild.AutoBuildTemplate : build.AutoBuildTemplate; - - // Start - Worker.runInBackground.AutoBuild = function () { - if (getTickCount() - waitTick < 1000) return true; - waitTick = getTickCount(); - - try { - let levels = gainedLevels(); - - if (levels > 0 && (canSpendPoints() || Config.AutoSkill.Enabled || Config.AutoStat.Enabled)) { - scriptBroadcast("toggleQuitlist"); - AutoBuild.print("Level up detected (", prevLevel, "-->", me.charlvl, ")"); - spendSkillPoints(); - spendStatPoints(); - Config.AutoSkill.Enabled && AutoSkill.init(Config.AutoSkill.Build, Config.AutoSkill.Save); - Config.AutoStat.Enabled && AutoStat.init(Config.AutoStat.Build, Config.AutoStat.Save, Config.AutoStat.BlockChance, Config.AutoStat.UseBulk); - scriptBroadcast({event: "level up"}); - AutoBuild.applyConfigUpdates(); // scriptBroadcast() won't trigger listener on this thread. - - debug && AutoBuild.print("Incrementing cached character level to", prevLevel + 1); - - // prevLevel doesn't get set to me.charlvl because - // we may have gained multiple levels at once - prevLevel += 1; - - scriptBroadcast("toggleQuitlist"); - } - } catch (err) { - print("Something broke!"); - print("Error:" + err.toSource()); - print("Stack trace: \n" + err.stack); - - return false; - } - - return true; - }; - - try { - AutoBuild.print("Loaded helper thread"); - console.log("ÿc8Kolbot-SoloPlayÿc0: Start AutoBuildThread"); - console.log(Worker.runInBackground); - } catch (e) { - console.error(e); - } - } -})(); diff --git a/libs/SoloPlay/Workers/EventEmitter.js b/libs/SoloPlay/Workers/EventEmitter.js new file mode 100644 index 00000000..4217db9f --- /dev/null +++ b/libs/SoloPlay/Workers/EventEmitter.js @@ -0,0 +1,89 @@ +/** + * @filename EventEmitter.js + * @author Jaenster + * @desc Transpiled Global modifying UMD module to handle emitting events and add prototypes ("on", "emit", "once", "off") + * to Unit + * + */ + +(function (factory) { + if (typeof module === "object" && typeof module.exports === "object") { + let v = factory(require, exports); + if (v !== undefined) module.exports = v; + } else if (typeof define === "function" && define.amd) { + define(["require", "exports", "../../modules/Worker", "../Modules/Events"], factory); + } +})(function (require, exports) { + "use strict"; + Object.defineProperty(exports, "__esModule", { value: true }); + + const Worker = require("../../modules/Worker"); + const Events = require("../Modules/Events"); + const old = { + level: me.charlvl, + }; + const gainedLevels = () => me.charlvl - old.level; + + let levelTimeout = getTickCount(); + + // Start + Worker.runInBackground.AutoBuild = function () { + if (getTickCount() - levelTimeout < 1000) return true; + levelTimeout = getTickCount(); + + try { + let levels = gainedLevels(); + + if (levels > 0 && (Config.AutoSkill.Enabled || Config.AutoStat.Enabled)) { + scriptBroadcast("toggleQuitlist"); + AutoBuild.print("Level up detected (", old.level, "-->", me.charlvl, ")"); + Config.AutoSkill.Enabled && AutoSkill.init(Config.AutoSkill.Build, Config.AutoSkill.Save); + Config.AutoStat.Enabled && AutoStat.init(Config.AutoStat.Build, Config.AutoStat.Save, Config.AutoStat.BlockChance, Config.AutoStat.UseBulk); + scriptBroadcast({ event: "level up" }); + AutoBuild.applyConfigUpdates(); // scriptBroadcast() won't trigger listener on this thread. + + AutoBuild.print("Incrementing cached character level to", old.level + 1); + Tracker.leveling(); + + // prevLevel doesn't get set to me.charlvl because + // we may have gained multiple levels at once + old.level += 1; + + scriptBroadcast("toggleQuitlist"); + } + } catch (e) { + console.error(e); + console.warn("Something broke! StackWalk :: ", e.stack); + + return false; + } + + return true; + }; + AutoBuild.print("Loaded AutoBuild"); + console.log("ÿc8Kolbot-SoloPlayÿc0: Start AutoBuild"); + + if (Developer.logPerformance && getScript(true).name.toString() === "libs\\soloplay\\soloplay.js") { + Worker.runInBackground.intervalUpdate = function () { + if (getTickCount() - Tracker.tick < Time.minutes(3)) return true; + Tracker.tick = getTickCount(); + + try { + Tracker.update(); + } catch (e) { + console.warn(e.message); + } + + return true; + }; + } + + // @ts-ignore + Unit.prototype.on = Events.Events.prototype.on; + // @ts-ignore + Unit.prototype.off = Events.Events.prototype.off; + // @ts-ignore + Unit.prototype.once = Events.Events.prototype.once; + // @ts-ignore + Unit.prototype.emit = Events.Events.prototype.emit; +}); diff --git a/libs/SoloPlay/Workers/EventHandler.js b/libs/SoloPlay/Workers/EventHandler.js new file mode 100644 index 00000000..b0ab0dc2 --- /dev/null +++ b/libs/SoloPlay/Workers/EventHandler.js @@ -0,0 +1,116 @@ +/** +* @filename EventHandler.js +* @author theBGuy +* @desc worker thread to handle in game events for Kolbot-SoloPlay +* +*/ + +(function (module, require, Worker) { + // Only load this in global scope + if (getScript(true).name.toLowerCase() === "libs\\soloplay\\soloplay.js") { + + let tickDelay = 0; + let [actions, profiles] = [[], []]; + + me.on("soloEvent", function msgEvent (msg) { + switch (msg) { + case "testing": + case "finishDen": + case "dodge": + case "skip": + case "killdclone": + actions.push(msg); + console.debug(actions); + + break; + } + }); + + me.on("processProfileEvent", function copyDataProcessing (id, info) { + console.debug(id, info); + // Torch + if (id === 55) { + let { profile, ladder, torchType } = JSON.parse(info); + console.log("Mesage recived for torch...processing"); + + if (profile !== me.profile && (me.hell || (me.nightmare && me.baal)) && me.ladder === ladder) { + if (torchType === me.classid && !me.findItem(604, 0, null, 7)) { + console.log("Sent Response"); + SoloEvents.sendToProfile(profile, { profile: me.profile, level: me.charlvl, event: 604 }); + } + } + + return; + } + + // Annhilus + if (id === 60) { + let { profile, ladder } = JSON.parse(info); + console.log("Mesage recived for Annhilus...processing"); + + if (profile !== me.profile && (me.hell || (me.nightmare && me.baal)) && me.ladder === ladder) { + if (!me.findItem(603, 0, null, 7)) { + console.log("Sent Response"); + SoloEvents.sendToProfile(profile, { profile: me.profile, level: me.charlvl, event: 603 }); + } + } + + return; + } + + if (id === 65) { + let { profile, level, event } = JSON.parse(info); + + console.log("Sucess: profile that contacted me: " + profile + " level of char: " + level); + SoloEvents.profileResponded = true; + profiles.push({ profile: profile, level: level, event: event }); + tickDelay += 1000; + } + }); + + let waitTick = getTickCount(); + + // Start + Worker.runInBackground.EventWorker = function () { + if (getTickCount() - waitTick < 100 || SoloEvents.townChicken.running) return true; + waitTick = getTickCount(); + + try { + while (actions.length) { + let wasDisabled = SoloEvents.townChicken.disabled; + try { + SoloEvents[actions.shift()](); + } catch (e) { + console.warn(e); + } finally { + // if we disabled townchicken, re-enable it + if (!wasDisabled && SoloEvents.townChicken.disabled) { + SoloEvents.townChicken.disabled = false; + } + } + } + + if (profiles.length > 0) { + let tick = getTickCount(); + + while (getTickCount() - tick < tickDelay) { + delay(500); + } + + let lowestLevelProf = profiles.sort((a, b) => a.level - b.level).first(); + + SoloEvents.sendToProfile(lowestLevelProf.profile, lowestLevelProf.event, 70); + D2Bot.joinMe(lowestLevelProf.profile, me.gamename.toLowerCase(), "", me.gamepassword.toLowerCase(), true); + profiles = []; + } + } catch (e) { + console.error(e); + } + + return true; + }; + + // should there be a heartbeat for the workers? + console.log("ÿc8Kolbot-SoloPlayÿc0: Start EventHandler"); + } +})(module, require, typeof Worker === "object" && Worker || require("../../modules/Worker")); diff --git a/libs/SoloPlay/Workers/EventWorker.js b/libs/SoloPlay/Workers/EventWorker.js deleted file mode 100644 index 32110552..00000000 --- a/libs/SoloPlay/Workers/EventWorker.js +++ /dev/null @@ -1,163 +0,0 @@ -/** -* @filename EventWorker.js -* @author theBGuy -* @desc worker thread to handle in game events for Kolbot-SoloPlay -* -*/ - -/** - * @todo make this actually work. Issue is if we interrupt a task and peform an event function, once that is over we've desynced from - * whatever we were orignally doing. In some cases this might be fine to just end whatever it was we were running - * e.g. after receiving finishDen we could kill the den script and perform the event function. We would be able to continue with the next - * script normally afterwards. We can't do that for something like diablo lightning dodge or baal wave skipping though, in those cases it - * might be fine to just continue because in theory we shouldn't be too far from our orginal action so minor desync in physical coordinates - * but not completly different area. We wouldn't be able to do that for diablo clone though. - */ -(function() { - // Only load this in global scope - if (getScript(true).name.toLowerCase() === "libs\\soloplay\\soloplay.js") { - const Worker = require("../../modules/Worker"); - - let tickDelay = 0; - let [action, profiles] = [[], []]; - - const scriptEventWorker = function (msg) { - if (msg && typeof msg === "string" && msg !== "") { - switch (msg) { - case "testing": - case "finishDen": - case "dodge": - case "skip": - case "killdclone": - action.push(msg); - - break; - case "addDiaEvent": - console.log("Added dia lightning listener"); - addEventListener("gamepacket", SoloEvents.diaEvent); - - break; - case "removeDiaEvent": - console.log("Removed dia lightning listener"); - removeEventListener("gamepacket", SoloEvents.diaEvent); - - break; - case "addBaalEvent": - console.log("Added baal wave listener"); - addEventListener("gamepacket", SoloEvents.baalEvent); - - break; - case "removeBaalEvent": - console.log("Removed baal wave listener"); - removeEventListener("gamepacket", SoloEvents.baalEvent); - - break; - // ignore common scriptBroadcast messages that aren't relevent to this thread - case "mule": - case "muleTorch": - case "muleAnni": - case "torch": - case "crafting": - case "getMuleMode": - case "pingquit": - return; - } - } - }; - - const receiveCopyDataWorker = function (id, info) { - // Torch - if (id === 55) { - let { profile, ladder, torchType } = JSON.parse(info); - console.log("Mesage recived for torch...processing"); - - if (profile !== me.profile && (me.hell || (me.nightmare && me.baal)) && me.ladder === ladder) { - if (torchType === me.classid && !me.findItem(604, 0, null, 7)) { - console.log("Sent Response"); - SoloEvents.sendToProfile(profile, {profile: me.profile, level: me.charlvl, event: 604}); - } - } - - return; - } - - // Annhilus - if (id === 60) { - let { profile, ladder } = JSON.parse(info); - console.log("Mesage recived for Annhilus...processing"); - - if (profile !== me.profile && (me.hell || (me.nightmare && me.baal)) && me.ladder === ladder) { - if (!me.findItem(603, 0, null, 7)) { - console.log("Sent Response"); - SoloEvents.sendToProfile(profile, {profile: me.profile, level: me.charlvl, event: 603}); - } - } - - return; - } - - if (id === 65) { - let { profile, level, event } = JSON.parse(info); - - console.log("Sucess: profile that contacted me: " + profile + " level of char: " + level); - SoloEvents.profileResponded = true; - profiles.push({profile: profile, level: level, event: event}); - tickDelay += 1000; - } - - if (id === 70) { - Messaging.sendToScript("D2BotSoloPlay.dbj", "event"); - delay(100 + me.ping); - scriptBroadcast("quit"); - } - }; - - addEventListener("scriptmsg", scriptEventWorker); - // should this just be added to the starter? would remove needing 3 copydata event listeners (entry, default, and here) - addEventListener("copydata", receiveCopyDataWorker); - - let waitTick = getTickCount(); - - // Start - Worker.runInBackground.EventWorker = function () { - if (getTickCount() - waitTick < 100 || SoloEvents.townChicken.running) return true; - waitTick = getTickCount(); - - try { - while (action.length) { - try { - SoloEvents[action.shift()](); - } catch (e) { - console.log(e); - } - } - - if (profiles.length > 0) { - let tick = getTickCount(); - - while (getTickCount() - tick < tickDelay) { - delay(500); - } - - let lowestLevelProf = profiles.sort((a, b) => a.level - b.level).first(); - - SoloEvents.sendToProfile(lowestLevelProf.profile, lowestLevelProf.event, 70); - D2Bot.joinMe(lowestLevelProf.profile, me.gamename.toLowerCase(), "", me.gamepassword.toLowerCase(), true); - profiles = []; - } - } catch (e) { - D2Bot.printToConsole(JSON.stringify(e)); - console.log(e); - } - - return true; - }; - - try { - console.log("ÿc8Kolbot-SoloPlayÿc0: Start EventThread"); - console.log(Worker.runInBackground); - } catch (e) { - console.error(e); - } - } -})(); diff --git a/libs/SoloPlay/Workers/TownChickenWorker.js b/libs/SoloPlay/Workers/TownChicken.js similarity index 93% rename from libs/SoloPlay/Workers/TownChickenWorker.js rename to libs/SoloPlay/Workers/TownChicken.js index 37cd8994..05d7db4f 100644 --- a/libs/SoloPlay/Workers/TownChickenWorker.js +++ b/libs/SoloPlay/Workers/TownChicken.js @@ -1,5 +1,5 @@ /** -* @filename TownChickenWorker.js +* @filename TownChicken.js * @author theBGuy * @desc TownChicken background worker thread * @@ -10,10 +10,9 @@ * - figure out how to deal with loss of reference to whatever it was we might of been targetting beforehand. * - How many chickens is too many for a script? How to end a script it that amount is reached. */ -(function() { +(function (module, require, Worker) { // Only load this in global scope if (getScript(true).name.toLowerCase() === "libs\\soloplay\\soloplay.js") { - const Worker = require("../../modules/Worker"); const getNearestMonster = () => { let gid = null; @@ -289,38 +288,34 @@ let [townCheck] = [false, false]; - const chickenScriptEvent = function (msg) { + me.on("townChicken", function townChickenEvent (msg) { if (SoloEvents.townChicken.disabled) return; - if (msg && typeof msg === "string" && msg !== "") { - switch (msg) { - case "townCheck": - switch (me.area) { - case sdk.areas.ArreatSummit: - case sdk.areas.UberTristram: - console.warn("Don't tp from " + getAreaName(me.area)); - return; - default: - console.log("townCheck message recieved. First check passed."); - townCheck = true; - - return; - } - case "quit": - //quitFlag = true; - // Maybe stop townChicken thread? Would that keep us from the crash that happens when we try to leave game while townChickening - - break; + if (typeof msg !== "string") return; + switch (msg) { + case "townCheck": + switch (me.area) { + case sdk.areas.ArreatSummit: + case sdk.areas.UberTristram: + console.warn("Don't tp from " + getAreaName(me.area)); + return; default: - break; + console.log("townCheck message recieved. First check passed."); + townCheck = true; + + return; } + case "quit": + //quitFlag = true; + // Maybe stop townChicken thread? Would that keep us from the crash that happens when we try to leave game while townChickening + break; + default: + break; } - }; + }); Misc.townCheck = function () { return false; }; - - addEventListener("scriptmsg", chickenScriptEvent); // const lastChickens = []; const useHowl = Skill.canUse(sdk.skills.Howl); @@ -408,12 +403,6 @@ return true; }; - - try { - console.log("ÿc8Kolbot-SoloPlayÿc0: Start TownChicken thread"); - console.log(Worker.runInBackground); - } catch (e) { - console.error(e); - } + console.log("ÿc8Kolbot-SoloPlayÿc0: Start TownChicken"); } -})(); +})(module, require, typeof Worker === "object" && Worker || require("../../modules/Worker")); From b875af2680f0f11af75cc69ebf9f4b421a77f0ac Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Sun, 12 Mar 2023 14:37:02 -0400 Subject: [PATCH 071/263] Switch to full use of background workers for non vital threads - Townchicken/autobuild/eventhread to be background workers of our main thread --- libs/SoloPlay/Functions/AttackOverrides.js | 5 - .../ClassAttackOverrides/AmazonAttacks-WIP.js | 6 - .../ClassAttackOverrides/AmazonAttacks.js | 6 - .../ClassAttackOverrides/AssassinAttacks.js | 6 - .../NecromancerAttacks.js | 6 - .../ClassAttackOverrides/PaladinAttacks.js | 15 +- .../ClassAttackOverrides/SorceressAttacks.js | 6 - libs/SoloPlay/Functions/MiscOverrides.js | 74 +++++----- libs/SoloPlay/Functions/PatherOverrides.js | 4 +- libs/SoloPlay/Scripts/andariel.js | 2 +- libs/SoloPlay/Scripts/baal.js | 2 - libs/SoloPlay/SoloPlay.js | 138 ++++++++++++------ libs/SoloPlay/Threads/ToolsThread.js | 5 +- libs/SoloPlay/Tools/Tracker.js | 22 +-- 14 files changed, 148 insertions(+), 149 deletions(-) diff --git a/libs/SoloPlay/Functions/AttackOverrides.js b/libs/SoloPlay/Functions/AttackOverrides.js index fbeb29c2..4144c01e 100644 --- a/libs/SoloPlay/Functions/AttackOverrides.js +++ b/libs/SoloPlay/Functions/AttackOverrides.js @@ -267,8 +267,6 @@ Attack.killTarget = function (name) { Misc.openChestsEnabled = false; while (attackCount < Config.MaxAttackCount && target.attackable && !this.skipCheck(target)) { - Misc.townCheck(); - // Check if unit got invalidated, happens if necro raises a skeleton from the boss's corpse. if (!target || !copyUnit(target).x) { target = Game.getMonster(-1, -1, gid); @@ -382,8 +380,6 @@ Attack.clearPos = function (x, y, range = 15, pickit = true) { if (target.x !== undefined && (getDistance(target, x, y) <= range || (this.getScarinessLevel(target) > 7 && getDistance(me, target) <= range)) && target.attackable) { Config.Dodge && me.hpPercent <= Config.DodgeHP && this.deploy(target, Config.DodgeRange, 5, 9); - - Misc.townCheck(true); let checkMobAttackCount = gidAttack.find(g => g.gid === target.gid); let checkAttackSkill = (!!checkMobAttackCount && checkMobAttackCount.attacks > 0 && checkMobAttackCount.attacks % 3 === 0); let result = ClassAttack.doAttack(target, checkAttackSkill); @@ -686,7 +682,6 @@ Attack.clear = function (range = 25, spectype = 0, bossId = false, sortfunc = un if (target.x !== undefined && (getDistance(target, orgx, orgy) <= range || (this.getScarinessLevel(target) > 7 && getDistance(me, target) <= range)) && target.attackable) { Config.Dodge && me.hpPercent <= Config.DodgeHP && this.deploy(target, Config.DodgeRange, 5, 9); - Misc.townCheck(true); tick = getTickCount(); if (!logged && boss && boss.gid === target.gid) { diff --git a/libs/SoloPlay/Functions/ClassAttackOverrides/AmazonAttacks-WIP.js b/libs/SoloPlay/Functions/ClassAttackOverrides/AmazonAttacks-WIP.js index d63d9acb..18c1ef1a 100644 --- a/libs/SoloPlay/Functions/ClassAttackOverrides/AmazonAttacks-WIP.js +++ b/libs/SoloPlay/Functions/ClassAttackOverrides/AmazonAttacks-WIP.js @@ -292,12 +292,6 @@ ClassAttack.doAttack = function (unit) { let merc = me.getMerc(); while (unit.attackable) { - if (Misc.townCheck()) { - if (!unit || !copyUnit(unit).x) { - unit = Misc.poll(() => Game.getMonster(-1, -1, gid), 1000, 80); - } - } - if (!unit) return Attack.Result.SUCCESS; if (me.needMerc()) { diff --git a/libs/SoloPlay/Functions/ClassAttackOverrides/AmazonAttacks.js b/libs/SoloPlay/Functions/ClassAttackOverrides/AmazonAttacks.js index 952151e5..08ccce84 100644 --- a/libs/SoloPlay/Functions/ClassAttackOverrides/AmazonAttacks.js +++ b/libs/SoloPlay/Functions/ClassAttackOverrides/AmazonAttacks.js @@ -289,12 +289,6 @@ ClassAttack.doAttack = function (unit, preattack, once) { let merc = me.getMerc(); while (unit.attackable) { - if (Misc.townCheck()) { - if (!unit || !copyUnit(unit).x) { - unit = Misc.poll(() => Game.getMonster(-1, -1, gid), 1000, 80); - } - } - if (!unit) return Attack.Result.SUCCESS; if (me.needMerc()) { diff --git a/libs/SoloPlay/Functions/ClassAttackOverrides/AssassinAttacks.js b/libs/SoloPlay/Functions/ClassAttackOverrides/AssassinAttacks.js index c8a7db52..109ff63e 100644 --- a/libs/SoloPlay/Functions/ClassAttackOverrides/AssassinAttacks.js +++ b/libs/SoloPlay/Functions/ClassAttackOverrides/AssassinAttacks.js @@ -217,12 +217,6 @@ ClassAttack.doAttack = function (unit, preattack) { let merc = me.getMerc(); while (unit.attackable) { - if (Misc.townCheck()) { - if (!unit || !copyUnit(unit).x) { - unit = Misc.poll(() => Game.getMonster(-1, -1, gid), 1000, 80); - } - } - if (!unit) return Attack.Result.SUCCESS; if (me.needMerc()) { diff --git a/libs/SoloPlay/Functions/ClassAttackOverrides/NecromancerAttacks.js b/libs/SoloPlay/Functions/ClassAttackOverrides/NecromancerAttacks.js index 5509823a..e1213c60 100644 --- a/libs/SoloPlay/Functions/ClassAttackOverrides/NecromancerAttacks.js +++ b/libs/SoloPlay/Functions/ClassAttackOverrides/NecromancerAttacks.js @@ -256,12 +256,6 @@ ClassAttack.doAttack = function (unit, preattack, once) { let merc = me.getMerc(); while (unit.attackable) { - if (Misc.townCheck()) { - if (!unit || !copyUnit(unit).x) { - unit = Misc.poll(() => Game.getMonster(-1, -1, gid), 1000, 80); - } - } - if (!unit) return Attack.Result.SUCCESS; if (me.needMerc()) { diff --git a/libs/SoloPlay/Functions/ClassAttackOverrides/PaladinAttacks.js b/libs/SoloPlay/Functions/ClassAttackOverrides/PaladinAttacks.js index 130dadbc..6a47f529 100644 --- a/libs/SoloPlay/Functions/ClassAttackOverrides/PaladinAttacks.js +++ b/libs/SoloPlay/Functions/ClassAttackOverrides/PaladinAttacks.js @@ -7,10 +7,15 @@ includeIfNotIncluded("core/Attacks/Paladin.js"); +/** + * @todo build selectAura method + */ + const MercWatch = { last: 0, }; + // eslint-disable-next-line no-unused-vars ClassAttack.doAttack = function (unit = undefined, preattack = false, once = false) { if (!unit || !unit.attackable) return Attack.Result.SUCCESS; @@ -113,6 +118,10 @@ ClassAttack.doAttack = function (unit = undefined, preattack = false, once = fal [attackSkill, aura] = Config.LowManaSkill; } + /** + * @param {Monster} unit + * @returns {AttackResult} + */ const switchBowAttack = (unit) => { if (Attack.getIntoPosition(unit, 20, sdk.collision.Ranged)) { try { @@ -154,12 +163,6 @@ ClassAttack.doAttack = function (unit = undefined, preattack = false, once = fal let merc = me.getMerc(); while (unit.attackable) { - if (Misc.townCheck()) { - if (!unit || !copyUnit(unit).x) { - unit = Misc.poll(() => Game.getMonster(-1, -1, gid), 1000, 80); - } - } - if (!unit) return Attack.Result.SUCCESS; if (me.needMerc()) { diff --git a/libs/SoloPlay/Functions/ClassAttackOverrides/SorceressAttacks.js b/libs/SoloPlay/Functions/ClassAttackOverrides/SorceressAttacks.js index 119de2c0..c518d674 100644 --- a/libs/SoloPlay/Functions/ClassAttackOverrides/SorceressAttacks.js +++ b/libs/SoloPlay/Functions/ClassAttackOverrides/SorceressAttacks.js @@ -434,12 +434,6 @@ includeIfNotIncluded("core/Attacks/Sorceress.js"); let mercRevive = 0; while (unit.attackable) { - if (Misc.townCheck()) { - if (!unit || !copyUnit(unit).x) { - unit = Misc.poll(() => Game.getMonster(-1, -1, gid), 1000, 80); - } - } - if (!unit) return Attack.Result.SUCCESS; if (me.needMerc()) { diff --git a/libs/SoloPlay/Functions/MiscOverrides.js b/libs/SoloPlay/Functions/MiscOverrides.js index 31090db2..3cc1c884 100644 --- a/libs/SoloPlay/Functions/MiscOverrides.js +++ b/libs/SoloPlay/Functions/MiscOverrides.js @@ -8,34 +8,6 @@ includeIfNotIncluded("core/Misc.js"); -Misc.townEnabled = true; - -Misc.townCheck = function () { - if (!me.canTpToTown()) return false; - - let check = false; - - if (Config.TownCheck && !me.inTown) { - try { - if (me.needPotions() || (Config.OpenChests.Enabled && Town.needKeys())) { - check = true; - } - } catch (e) { - check = false; - } - } - - if (check) { - if (Messaging.sendToScript("libs/SoloPlay/Threads/TownChicken.js", "fastTown")) { - console.log("BroadCasted townCheck"); - - return true; - } - } - - return false; -}; - Misc.openChestsEnabled = true; Misc.presetChestIds = [ 5, 6, 87, 104, 105, 106, 107, 143, 140, 141, 144, 146, 147, 148, 176, 177, 181, 183, 198, 240, 241, @@ -43,6 +15,13 @@ Misc.presetChestIds = [ 406, 407, 413, 420, 424, 425, 430, 431, 432, 433, 454, 455, 501, 502, 504, 505, 580, 581 ]; +/** + * @override + * @param {number} area + * @param {number[]} chestIds + * @param {Function} [sort] + * @returns {boolean} + */ Misc.openChestsInArea = function (area, chestIds = [], sort = undefined) { !area && (area = me.area); area !== me.area && Pather.journeyTo(area); @@ -289,22 +268,29 @@ Misc.scanShrines = function (range, ignore = []) { } } + /** + * @todo - We should build a list of shrines by their preset values when we scan the area + */ + let shrine = Game.getObject(); /** * Fix for a3/a5 shrines */ - if (shrine && shrineMap.has(shrine.objtype) && shrine.name.toLowerCase().includes("shrine")) { + if (shrine) { let index = -1; // Build a list of nearby shrines do { - if (shrine.mode === sdk.objects.mode.Inactive && !ignore.includes(shrine.objtype) + if (shrine.name.toLowerCase().includes("shrine") && shrineMap.has(shrine.objtype) + && shrine.mode === sdk.objects.mode.Inactive && !ignore.includes(shrine.objtype) && getDistance(me.x, me.y, shrine.x, shrine.y) <= rangeCheck(shrine.objtype)) { shrineList.push(copyUnit(shrine)); } } while (shrine.getNext()); + if (!shrineList.length) return false; + // Check if we have a shrine state, store its index if yes for (let i = 0; i < this.shrineStates.length; i += 1) { if (me.getState(this.shrineStates[i])) { @@ -511,6 +497,11 @@ Misc.checkItemsForImbueing = function () { return false; }; +/** + * @param {ItemUnit} item + * @param {ItemUnit[]} runes + * @returns {boolean} + */ Misc.addSocketablesToItem = function (item, runes = []) { if (!item || item.sockets === 0) return false; let preSockets = item.getItemsEx().length; @@ -565,13 +556,20 @@ Misc.addSocketablesToItem = function (item, runes = []) { return item.getItemsEx().length > original; }; +/** + * @param {ItemUnit} item + * @param {{ classid: number, socketWith: number[], temp: number[], useSocketQuest: boolean, condition: Function }} [itemInfo] + * @returns {boolean} + */ Misc.getSocketables = function (item, itemInfo) { if (!item) return false; + itemInfo === undefined && (itemInfo = {}); + let itemtype, gemType, runeType; let [multiple, temp] = [[], []]; let itemSocketInfo = item.getItemsEx(); let preSockets = itemSocketInfo.length; - let allowTemp = (!!itemInfo && !!itemInfo.temp && itemInfo.temp.length > 0 && (preSockets === 0 || preSockets > 0 && itemSocketInfo.some(el => !itemInfo.socketWith.includes(el.classid)))); + let allowTemp = (itemInfo.hasOwnProperty("temp") && itemInfo.temp.length > 0 && (preSockets === 0 || preSockets > 0 && itemSocketInfo.some(el => !itemInfo.socketWith.includes(el.classid)))); let sockets = item.sockets; let openSockets = sockets - preSockets; let { classid, quality } = item; @@ -595,10 +593,13 @@ Misc.getSocketables = function (item, itemInfo) { return false; } - if (!itemInfo || (!!itemInfo && itemInfo.socketWith.length === 0)) { + if (itemInfo.hasOwnProperty("socketWith") && itemInfo.socketWith.length === 0) { itemtype = item.getItemType(); if (!itemtype) return false; - gemType = ["Helmet", "Armor"].includes(itemtype) ? "Ruby" : itemtype === "Shield" ? "Diamond" : itemtype === "Weapon" && !Check.currentBuild().caster ? "Skull" : ""; + gemType = ["Helmet", "Armor"].includes(itemtype) + ? "Ruby" : itemtype === "Shield" + ? "Diamond" : itemtype === "Weapon" && !Check.currentBuild().caster + ? "Skull" : ""; // Tir rune in normal, Io rune otherwise and Shael's if assassin !gemType && (runeType = me.normal ? "Tir" : me.assassin ? "Shael" : "Io"); @@ -609,7 +610,7 @@ Misc.getSocketables = function (item, itemInfo) { } for (let i = 0; i < socketables.length; i++) { - if (!!itemInfo && itemInfo.socketWith.length > 0) { + if (itemInfo.hasOwnProperty("socketWith") && itemInfo.socketWith.length > 0) { // In case we are trying to use different runes, check if item already has current rune inserted // or if its already in the muliple list. If it is, remove that socketables classid from the list of wanted classids if (itemInfo.socketWith.length > 1 @@ -697,7 +698,8 @@ Misc.getSocketables = function (item, itemInfo) { Misc.checkSocketables = function () { let items = me.getItemsEx() - .filter(item => item.sockets > 0 && AutoEquip.hasTier(item) && item.quality > sdk.items.quality.Superior) + .filter(item => item.sockets > 0 && AutoEquip.hasTier(item) + && (item.quality >= sdk.items.quality.Magic || [sdk.items.quality.Normal, sdk.items.quality.Superior].includes(item.quality) && item.isEquipped)) .sort((a, b) => NTIP.GetTier(b) - NTIP.GetTier(a)); if (!items) return; @@ -706,6 +708,8 @@ Misc.checkSocketables = function () { let sockets = items[i].sockets; switch (items[i].quality) { + case sdk.items.quality.Normal: + case sdk.items.quality.Superior: case sdk.items.quality.Magic: case sdk.items.quality.Rare: case sdk.items.quality.Crafted: diff --git a/libs/SoloPlay/Functions/PatherOverrides.js b/libs/SoloPlay/Functions/PatherOverrides.js index 8bd03ee5..6ea00760 100644 --- a/libs/SoloPlay/Functions/PatherOverrides.js +++ b/libs/SoloPlay/Functions/PatherOverrides.js @@ -519,7 +519,7 @@ Pather.move = function (target, givenSettings = {}) { // let otherObjects = getUnits(sdk.unittype.Object).filter(el => getDistance()); if (goBack) { console.debug("Going back to old node. Distance: " + node.distance); - } else if (nearestNode && nearestNode.distance > 5 && node.distance > 5 && 100 / node.distance * nearestNode.distance < 95) { + } else if (nearestNode && nearestNode.distance > 5 && node.distance > 5 && Math.percentDifference(node.distance, nearestNode.distance) > 5/* && 100 / node.distance * nearestNode.distance < 95 */) { console.debug("Moving to next node. Distance: " + nearestNode.distance); let newIndex = path.findIndex(node => nearestNode.x === node.x && nearestNode.y === node.y); if (newIndex > -1) { @@ -545,8 +545,6 @@ Pather.move = function (target, givenSettings = {}) { Pather.recursion = true; } } - - settings.allowTown && Misc.townCheck(); } } else { if (!me.inTown) { diff --git a/libs/SoloPlay/Scripts/andariel.js b/libs/SoloPlay/Scripts/andariel.js index 968c626b..5926885a 100644 --- a/libs/SoloPlay/Scripts/andariel.js +++ b/libs/SoloPlay/Scripts/andariel.js @@ -72,7 +72,7 @@ function andariel () { Config.TownHP = 0; Config.TownMP = 0; Config.PickRange = -1; - Misc.townEnabled = false; + SoloEvents.townChicken.disabled = true; CharData.updateConfig(); if (Pather.changeAct()) { diff --git a/libs/SoloPlay/Scripts/baal.js b/libs/SoloPlay/Scripts/baal.js index 2b217e0c..4ef8ec8c 100644 --- a/libs/SoloPlay/Scripts/baal.js +++ b/libs/SoloPlay/Scripts/baal.js @@ -77,8 +77,6 @@ function baal () { while (true) { if (!Game.getMonster(sdk.monsters.ThroneBaal)) return true; - Misc.townCheck(); - switch (Common.Baal.checkThrone()) { case 1: Attack.clearClassids(sdk.monsters.WarpedFallen, sdk.monsters.WarpedShaman) && (tick = getTickCount()); diff --git a/libs/SoloPlay/SoloPlay.js b/libs/SoloPlay/SoloPlay.js index 3e762318..4dc9fe3d 100644 --- a/libs/SoloPlay/SoloPlay.js +++ b/libs/SoloPlay/SoloPlay.js @@ -8,7 +8,7 @@ js_strict(true); include("critical.js"); // globals needed for core gameplay -includeCoreLibs({ exclude: ["Storage.js"]}); +includeCoreLibs({ exclude: ["Storage.js"] }); // system libs includeSystemLibs(); @@ -30,6 +30,7 @@ const LocalChat = require("../modules/LocalChat", null, false); * - Add priority to runewords/cubing * - proper script skipping + gold runs when needed * - fix autoequip issues with rings and dual wielding + * - remove all the logic from main so all it does is call functions */ function main () { @@ -70,54 +71,93 @@ function main () { let sojPause = false; let startTime = getTickCount(); - this.scriptEvent = function (msg) { + /** + * Handle script/thread communications + * @param {string} msg + * @returns {void} + */ + const scriptEvent = function (msg) { + if (!msg || typeof msg !== "string") return; let obj; - if (msg && typeof msg === "string" && msg !== "") { - switch (true) { - case msg === "nextScript": - // testing - works so maybe can handle other events as well? - me.emit("nextScript"); - - break; - case msg === "soj": - sojPause = true; - sojCounter = 0; - - break; - case msg.substring(0, 8) === "config--": + if (msg.includes("--")) { + let sub = msg.match(/\w+?--/gm).first(); + + switch (sub) { + case "config--": console.debug("update config"); Config = JSON.parse(msg.split("config--")[1]); - break; - case msg.substring(0, 7) === "skill--": + return; + case "skill--": console.debug("update skillData"); obj = JSON.parse(msg.split("skill--")[1]); Misc.updateRecursively(CharData.skillData, obj); - break; - case msg.substring(0, 6) === "data--": + return; + case "data--": console.debug("update myData"); obj = JSON.parse(msg.split("data--")[1]); Misc.updateRecursively(myData, obj); - break; - case msg.toLowerCase() === "test": - { - console.debug(sdk.colors.Green + "//-----------DataDump Start-----------//", - "\nÿc8ThreadData ::\n", getScript(true), - "\nÿc8MainData ::\n", myData, - "\nÿc8BuffData ::\n", CharData.buffData, - "\nÿc8SkillData ::\n", CharData.skillData, - "\nÿc8GlobalVariabls ::\n", Object.keys(global), - "\n" + sdk.colors.Red + "//-----------DataDump End-----------//"); - } - break; + return; } } + + switch (msg) { + case "testing": + case "finishDen": + case "dodge": + case "skip": + case "killdclone": + me.emit("soloEvent", msg); + + break; + case "addDiaEvent": + console.log("Added dia lightning listener"); + addEventListener("gamepacket", SoloEvents.diaEvent); + + break; + case "removeDiaEvent": + console.log("Removed dia lightning listener"); + removeEventListener("gamepacket", SoloEvents.diaEvent); + + break; + case "addBaalEvent": + console.log("Added baal wave listener"); + addEventListener("gamepacket", SoloEvents.baalEvent); + + break; + case "removeBaalEvent": + console.log("Removed baal wave listener"); + removeEventListener("gamepacket", SoloEvents.baalEvent); + + break; + case "nextScript": + // testing - works so maybe can handle other events as well? + me.emit("nextScript"); + + break; + case "soj": + sojPause = true; + sojCounter = 0; + + break; + case "test": + { + console.debug(sdk.colors.Green + "//-----------DataDump Start-----------//", + "\nÿc8ThreadData ::\n", getScript(true), + "\nÿc8MainData ::\n", myData, + "\nÿc8BuffData ::\n", CharData.buffData, + "\nÿc8SkillData ::\n", CharData.skillData, + "\nÿc8GlobalVariabls ::\n", Object.keys(global), + "\n" + sdk.colors.Red + "//-----------DataDump End-----------//"); + } + break; + } }; - this.copyDataEvent = function (mode, msg) { + const copyDataEvent = function (mode, msg) { // "Mule Profile" option from D2Bot# if (mode === 0 && msg === "mule") { if (AutoMule.getInfo() && AutoMule.getInfo().hasOwnProperty("muleInfo")) { @@ -131,6 +171,13 @@ function main () { } else { D2Bot.printToConsole("Profile not enabled for muling."); } + } else if (mode === 70) { + Messaging.sendToScript("D2BotSoloPlay.dbj", "event"); + delay(100 + me.ping); + scriptBroadcast("quit"); + } else if ([55, 60, 65].includes(mode)) { + // torch/anni sharing event - does this even still work? Haven't tested in awhile + me.emit("processProfileEvent", mode, msg); } }; @@ -145,11 +192,15 @@ function main () { LocalChat.init(); // Load event listeners - addEventListener("scriptmsg", this.scriptEvent); - addEventListener("copydata", this.copyDataEvent); + addEventListener("scriptmsg", scriptEvent); + addEventListener("copydata", copyDataEvent); // AutoMule/TorchSystem/Gambling/Crafting handler - if (AutoMule.inGameCheck() || TorchSystem.inGameCheck() || Gambling.inGameCheck() || CraftingSystem.inGameCheck() || SoloEvents.inGameCheck()) { + if (AutoMule.inGameCheck() + || TorchSystem.inGameCheck() + || Gambling.inGameCheck() + || CraftingSystem.inGameCheck() + || SoloEvents.inGameCheck()) { return true; } @@ -168,18 +219,11 @@ function main () { // Load threads - for now split between old system with threads or opt in for new system with workers load("libs/SoloPlay/Threads/ToolsThread.js"); - if ((Developer.testingMode.enabled && Developer.testingMode.profiles.some(prof => String.isEqual(prof, me.profile)))) { - removeEventListener("scriptmsg", AutoBuild.levelUpHandler); - // require("./Modules/eventEmitter"); // needs work - - includeIfNotIncluded("SoloPlay/Workers/EventWorker.js"); - includeIfNotIncluded("SoloPlay/Workers/TownChickenWorker.js"); - includeIfNotIncluded("SoloPlay/Workers/AutoBuildWorker.js"); - SoloEvents.filePath = "libs/SoloPlay/SoloPlay.js"; // hacky for now, don't want to mess up others running so we just broadcast to ourselves - } else { - load("libs/SoloPlay/Threads/EventThread.js"); - load("libs/SoloPlay/Threads/TownChicken.js"); - } + + require("./Workers/EventEmitter"); + require("./Workers/EventHandler"); + require("./Workers/TownChicken"); + SoloEvents.filePath = "libs/SoloPlay/SoloPlay.js"; // hacky for now, don't want to mess up others running so we just broadcast to ourselves // Load guard if we want to see the stack as it runs if (Developer.debugging.showStack.enabled) { diff --git a/libs/SoloPlay/Threads/ToolsThread.js b/libs/SoloPlay/Threads/ToolsThread.js index 1706ac75..7e966948 100644 --- a/libs/SoloPlay/Threads/ToolsThread.js +++ b/libs/SoloPlay/Threads/ToolsThread.js @@ -524,6 +524,10 @@ function main () { } switch (msg) { + case "deleteAndRemake": + Developer.testingMode.enabled && (quitFlag = true); + + break; case "toggleQuitlist": canQuit = !canQuit; @@ -582,7 +586,6 @@ function main () { addEventListener("keyup", keyEvent); addEventListener("gameevent", gameEvent); addEventListener("scriptmsg", scriptEvent); - addEventListener("scriptmsg", Tracker.logLeveling); Config.QuitListMode > 0 && Common.Toolsthread.initQuitList(); !Array.isArray(Config.QuitList) && (Config.QuitList = [Config.QuitList]); diff --git a/libs/SoloPlay/Tools/Tracker.js b/libs/SoloPlay/Tools/Tracker.js index 2a465a2c..b32475d9 100644 --- a/libs/SoloPlay/Tools/Tracker.js +++ b/libs/SoloPlay/Tools/Tracker.js @@ -166,7 +166,7 @@ const Tracker = { ); FileAction.append(Tracker.SPPath, string); - this.tick = GameTracker.LastSave; + Tracker.tick = GameTracker.LastSave; return true; }, @@ -201,7 +201,7 @@ const Tracker = { ); FileAction.append(Tracker.LPPath, string); - this.tick = GameTracker.LastSave; + Tracker.tick = GameTracker.LastSave; return true; }, @@ -230,24 +230,8 @@ const Tracker = { GameTracker.Total += (Tracker.timer(newTick) + oogTick); GameTracker.LastSave = getTickCount(); Tracker.writeObj(GameTracker, Tracker.GTPath); - this.tick = GameTracker.LastSave; + Tracker.tick = GameTracker.LastSave; return true; } }; - -if (Developer.logPerformance && getScript(true).name.toString() === "libs\\soloplay\\soloplay.js") { - const Worker = require("../../modules/Worker"); - - Worker.runInBackground.intervalUpdate = function () { - if (getTickCount() - Tracker.tick < 3 * 60000) return true; - Tracker.tick = getTickCount(); - try { - Tracker.update(); - } catch (e) { - console.warn(e.message); - } - - return true; - }; -} From 8079c001ac5679bee667539af084de4ef2f7bd44 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Sun, 12 Mar 2023 14:38:15 -0400 Subject: [PATCH 072/263] Update DruidAttacks.js - Complete rebuild to druid attacks, same method as sorc has been using to select skills better --- .../ClassAttackOverrides/DruidAttacks.js | 431 ++++++++++-------- 1 file changed, 244 insertions(+), 187 deletions(-) diff --git a/libs/SoloPlay/Functions/ClassAttackOverrides/DruidAttacks.js b/libs/SoloPlay/Functions/ClassAttackOverrides/DruidAttacks.js index 89aac027..2aea7f20 100644 --- a/libs/SoloPlay/Functions/ClassAttackOverrides/DruidAttacks.js +++ b/libs/SoloPlay/Functions/ClassAttackOverrides/DruidAttacks.js @@ -12,236 +12,293 @@ includeIfNotIncluded("core/Attacks/Druid.js"); -/** - * @param {Monster} unit - * @param {boolean} preattack - * @returns {AttackResult} - */ -ClassAttack.doAttack = function (unit, preattack) { - if (!unit) return Attack.Result.SUCCESS; - let gid = unit.gid; - - if (Config.MercWatch && me.needMerc()) { - console.log("mercwatch"); +(function () { + /** + * @constructor + * @param {number} skillId + * @param {number} reqLvl + * @param {number} range + */ + function ClassData (skillId = -1, range = 0) { + this.have = false; + this.skill = skillId; + this.range = range ? range : Skill.getRange(skillId); + this.mana = Infinity; + this.dmg = 0; + this.timed = Skill.isTimed(skillId); + this.reqLvl = getBaseStat("skills", skillId, "reqlevel"); + } - if (Town.visitTown()) { - // lost reference to the mob we were attacking - if (!unit || !copyUnit(unit).x || !Game.getMonster(-1, -1, gid) || unit.dead) { - return Attack.Result.SUCCESS; + /** + * Initialize data values + * @param {number} [range] + * @returns {void} + */ + ClassData.prototype.assignValues = function (range) { + this.have = Skill.canUse(this.skill); + if (!this.have) return; + this.range = range || Skill.getRange(this.skill); + this.mana = Skill.getManaCost(this.skill); + }; + + /** + * Calculate effective damage for a certain monster unit + * @param {Monster} unit + * @returns {void} + */ + ClassData.prototype.calcDmg = function (unit) { + if (!this.have) return; + this.dmg = GameData.avgSkillDamage(this.skill, unit); + }; + + const AttackData = { + "Attack": new ClassData(sdk.skills.Attack, 4), + "Firestorm": new ClassData(sdk.skills.Firestorm), + "MoltenBoulder": new ClassData(sdk.skills.MoltenBoulder), + "ArcticBlast": new ClassData(sdk.skills.ArcticBlast), + "Fissure": new ClassData(sdk.skills.Fissure), + "Twister": new ClassData(sdk.skills.Twister), + "Tornado": new ClassData(sdk.skills.Tornado), + "Volcano": new ClassData(sdk.skills.Volcano), + // "Hurricane": new ClassData(sdk.skills.Hurricane), + // "Armageddon": new ClassData(sdk.skills.Armageddon), + }; + + /** + * hacky for now - fire skills are wonky with the skill delays + * better solution might be tracking what was last cast and the actual + * delay of each + */ + AttackData.Firestorm.timed = false; + + /** + * The keys never change so this makes it easier to iterate without calling Object.keys each time + * @type {Array} + */ + const AttackDataKeys = Object.keys(AttackData); + + /** + * Helper function to re-init AttackData + * @param {number} currLvl + * @todo decide when AttackData need to be re-initialized becasue doing it every attack is a waste + */ + const initAttackData = (currLvl = me.charlvl) => { + AttackDataKeys.forEach(sk => { + if (currLvl >= AttackData[sk].reqLvl) { + AttackData[sk].assignValues(); + } + }); + }; + + /** + * Helper function to init damage value for unit + * @param {Monster} unit + */ + const setDamageValues = (unit) => { + AttackDataKeys.forEach(sk => { + if (AttackData[sk].have) { + AttackData[sk].calcDmg(unit); + } + }); + }; + + /** + * Check if this skill is the most damaging + * @param {ClassData} skill + * @returns {boolean} + */ + const isHighestDmg = (skill) => { + for (let key of AttackDataKeys) { + if (AttackData[key].dmg > skill.dmg) { + return false; } } - } - - let checkSkill; - let mercRevive = 0; - let timedSkill = -1; - let untimedSkill = -1; - let gold = me.gold; - let index = (unit.isSpecial || unit.isPlayer) ? 1 : 3; - - // Rebuff Hurricane - Skill.canUse(sdk.skills.Hurricane) && !me.getState(sdk.states.Hurricane) && Skill.cast(sdk.skills.Hurricane, sdk.skills.hand.Right); - // Rebuff Cyclone Armor - Skill.canUse(sdk.skills.CycloneArmor) && !me.getState(sdk.states.CycloneArmor) && Skill.cast(sdk.skills.CycloneArmor, sdk.skills.hand.Right); - - if (index === 1 && !unit.dead && unit.curseable) { - const commonCheck = (gold > 500000 || unit.isBoss || [sdk.areas.ChaosSanctuary, sdk.areas.ThroneofDestruction].includes(me.area)); - - if (CharData.skillData.haveChargedSkill(sdk.skills.SlowMissiles) && unit.getEnchant(sdk.enchant.LightningEnchanted) && !unit.getState(sdk.states.SlowMissiles) - && (gold > 500000 && !unit.isBoss) && !checkCollision(me, unit, sdk.collision.Ranged)) { - // Cast slow missiles - Attack.castCharges(sdk.skills.SlowMissiles, unit); + return true; + }; + + /** + * Used to handle times when there isn't a valid skill we can use, to prevent throwing error + */ + const DummyData = new ClassData(-1, -1); + + /** + * @param {Monster} unit + * @param {boolean} recheck + * @returns {AttackResult} + */ + ClassAttack.doAttack = function (unit, recheck) { + if (!unit) return Attack.Result.SUCCESS; + let gid = unit.gid; + + if (Config.MercWatch && me.needMerc()) { + console.log("mercwatch"); + + if (Town.visitTown()) { + // lost reference to the mob we were attacking + if (!unit || !copyUnit(unit).x || !Game.getMonster(-1, -1, gid) || unit.dead) { + return Attack.Result.SUCCESS; + } + } } - if (CharData.skillData.haveChargedSkill(sdk.skills.InnerSight) && !unit.getState(sdk.states.InnerSight) - && gold > 500000 && !checkCollision(me, unit, sdk.collision.Ranged)) { - // Cast slow missiles - Attack.castCharges(sdk.skills.InnerSight, unit); - } + let mercRevive = 0; + let gold = me.gold; + const currLvl = me.charlvl; + const index = (unit.isSpecial || unit.isPlayer) ? 1 : 3; - if (CharData.skillData.haveChargedSkillOnSwitch(sdk.skills.Decrepify) - && !unit.getState(sdk.states.Decrepify) && commonCheck && !checkCollision(me, unit, sdk.collision.Ranged)) { - // Switch cast decrepify - Attack.switchCastCharges(sdk.skills.Decrepify, unit); - } - - if (CharData.skillData.haveChargedSkillOnSwitch(sdk.skills.Weaken) - && !unit.getState(sdk.states.Weaken) && !unit.getState(sdk.states.Decrepify) && commonCheck && !checkCollision(me, unit, sdk.collision.Ranged)) { - // Switch cast weaken - Attack.switchCastCharges(sdk.skills.Weaken, unit); - } - } + // maybe every couple attacks or just the first one? + Precast.doPrecast(); - // specials and dolls for now, should make dolls much less dangerous with the reduction of their damage - if (Precast.haveCTA > -1 && !unit.dead && (index === 1 || unit.isDoll) - && unit.distance < 5 && !unit.getState(sdk.states.BattleCry) && unit.curseable) { - Skill.switchCast(sdk.skills.BattleCry, {oSkill: true}); - } + if (index === 1 && !unit.dead && unit.curseable) { + const commonCheck = (gold > 500000 || unit.isBoss || [sdk.areas.ChaosSanctuary, sdk.areas.ThroneofDestruction].includes(me.area)); - if (preattack && Config.AttackSkill[0] > 0 && Attack.checkResist(unit, Config.AttackSkill[0]) - && (!me.getState(sdk.states.SkillDelay) || !Skill.isTimed(Config.AttackSkill[0]))) { - if (unit.distance > Skill.getRange(Config.AttackSkill[0]) || checkCollision(me, unit, sdk.collision.Ranged)) { - if (!Attack.getIntoPosition(unit, Skill.getRange(Config.AttackSkill[0]), sdk.collision.Ranged)) { - return Attack.Result.FAILED; + if (CharData.skillData.haveChargedSkill(sdk.skills.SlowMissiles) && unit.getEnchant(sdk.enchant.LightningEnchanted) && !unit.getState(sdk.states.SlowMissiles) + && (gold > 500000 && !unit.isBoss) && !checkCollision(me, unit, sdk.collision.Ranged)) { + // Cast slow missiles + Attack.castCharges(sdk.skills.SlowMissiles, unit); } - } - - Skill.cast(Config.AttackSkill[0], Skill.getHand(Config.AttackSkill[0]), unit); - - return Attack.Result.SUCCESS; - } - // Get timed skill - checkSkill = Attack.getCustomAttack(unit) ? Attack.getCustomAttack(unit)[0] : Config.AttackSkill[index]; + if (CharData.skillData.haveChargedSkill(sdk.skills.InnerSight) && !unit.getState(sdk.states.InnerSight) + && gold > 500000 && !checkCollision(me, unit, sdk.collision.Ranged)) { + // Cast slow missiles + Attack.castCharges(sdk.skills.InnerSight, unit); + } - if (Attack.checkResist(unit, checkSkill) && Attack.validSpot(unit.x, unit.y, checkSkill)) { - timedSkill = checkSkill; - } else if (Config.AttackSkill[5] > -1 && Attack.checkResist(unit, Config.AttackSkill[5]) && Attack.validSpot(unit.x, unit.y, Config.AttackSkill[5])) { - timedSkill = Config.AttackSkill[5]; - } + if (CharData.skillData.haveChargedSkillOnSwitch(sdk.skills.Decrepify) + && !unit.getState(sdk.states.Decrepify) && commonCheck && !checkCollision(me, unit, sdk.collision.Ranged)) { + // Switch cast decrepify + Attack.switchCastCharges(sdk.skills.Decrepify, unit); + } + + if (CharData.skillData.haveChargedSkillOnSwitch(sdk.skills.Weaken) + && !unit.getState(sdk.states.Weaken) && !unit.getState(sdk.states.Decrepify) && commonCheck && !checkCollision(me, unit, sdk.collision.Ranged)) { + // Switch cast weaken + Attack.switchCastCharges(sdk.skills.Weaken, unit); + } + } - // Get untimed skill - checkSkill = Attack.getCustomAttack(unit) ? Attack.getCustomAttack(unit)[1] : Config.AttackSkill[index + 1]; + // specials and dolls for now, should make dolls much less dangerous with the reduction of their damage + if (Precast.haveCTA > -1 && !unit.dead && (index === 1 || unit.isDoll) + && unit.distance < 5 && !unit.getState(sdk.states.BattleCry) && unit.curseable) { + Skill.switchCast(sdk.skills.BattleCry, { oSkill: true }); + } - if (Attack.checkResist(unit, checkSkill) && Attack.validSpot(unit.x, unit.y, checkSkill)) { - untimedSkill = checkSkill; - } else if (Config.AttackSkill[6] > -1 && Attack.checkResist(unit, Config.AttackSkill[6]) && Attack.validSpot(unit.x, unit.y, Config.AttackSkill[6])) { - untimedSkill = Config.AttackSkill[6]; - } + initAttackData(currLvl); + setDamageValues(unit); - // Low mana timed skill - if (Config.LowManaSkill[0] > -1 && Skill.getManaCost(timedSkill) > me.mp && Attack.checkResist(unit, Config.LowManaSkill[0])) { - timedSkill = Config.LowManaSkill[0]; - } + let selectedSkillKey = AttackDataKeys + .filter(k => AttackData[k].have && me.mp > AttackData[k].mana && (!AttackData[k].timed || !me.skillDelay)) + .sort((a, b) => AttackData[b].dmg - AttackData[a].dmg) + .first(); + if (!selectedSkillKey) return Attack.Result.FAILED; - // Low mana untimed skill - if (Config.LowManaSkill[1] > -1 && Skill.getManaCost(untimedSkill) > me.mp && Attack.checkResist(unit, Config.LowManaSkill[1])) { - untimedSkill = Config.LowManaSkill[1]; - } + /** @type {ClassData} */ + let selectedSkill = typeof AttackData[selectedSkillKey] === "object" ? AttackData[selectedSkillKey] : DummyData; - if (me.normal && me.charlvl > 12 && gold < 5000 && Skill.getManaCost(timedSkill) > me.mp) { - switch (SetUp.currentBuild) { - case "Start": - if (Skill.canUse(sdk.skills.Firestorm) && Skill.getManaCost(sdk.skills.Firestorm) < me.mp) { - timedSkill = sdk.skills.Firestorm; - } else if (me.getMobCount(6, Coords_1.Collision.BLOCK_MISSILE | Coords_1.BlockBits.BlockWall) >= 1) { - // I have no mana and there are mobs around me, just attack - timedSkill = sdk.skills.Attack; + switch (selectedSkill.skill) { + case sdk.skills.Attack: + if (!me.normal || (me.charlvl > 6 && !me.checkForMobs({ range: 10, coll: (sdk.collision.BlockWall | sdk.collision.ClosedDoor) }))) { + selectedSkill = DummyData; } - - break; - default: - break; } - } - let result = this.doCast(unit, timedSkill, untimedSkill); + // console.debug(AttackData); + // console.debug("Choose skill :: " + getSkillById(selectedSkill.skill) + " Damage: " + selectedSkill.dmg); - if (result === Attack.Result.CANTATTACK && Attack.canTeleStomp(unit)) { - let merc = me.getMerc(); + let result = ClassAttack.doCast(unit, selectedSkill); - while (unit.attackable) { - if (Misc.townCheck()) { - if (!unit || !copyUnit(unit).x) { - unit = Misc.poll(() => Game.getMonster(-1, -1, gid), 1000, 80); - } - } + if (result === Attack.Result.CANTATTACK && Attack.canTeleStomp(unit)) { + let merc = me.getMerc(); - if (!unit) return Attack.Result.SUCCESS; + while (unit.attackable) { + if (!unit) return Attack.Result.SUCCESS; - if (me.needMerc()) { - if (Config.MercWatch && mercRevive++ < 1) { - Town.visitTown(); - } else { - return Attack.Result.CANTATTACK; + if (me.needMerc()) { + if (Config.MercWatch && mercRevive++ < 1) { + Town.visitTown(); + } else { + return Attack.Result.CANTATTACK; + } + + (merc === undefined || !merc) && (merc = me.getMerc()); } - (merc === undefined || !merc) && (merc = me.getMerc()); - } + if (!!merc && getDistance(merc, unit) > 5) { + Pather.moveToUnit(unit); - if (!!merc && getDistance(merc, unit) > 5) { - Pather.moveToUnit(unit); + let spot = Attack.findSafeSpot(unit, 10, 5, 9); + !!spot && Pather.walkTo(spot.x, spot.y); + } - let spot = Attack.findSafeSpot(unit, 10, 5, 9); - !!spot && Pather.walkTo(spot.x, spot.y); + let closeMob = Attack.getNearestMonster({ skipGid: gid }); + !!closeMob && ClassAttack.doCast(closeMob, selectedSkill); } - let closeMob = Attack.getNearestMonster({skipGid: gid}); - !!closeMob && this.doCast(closeMob, timedSkill, untimedSkill); + return Attack.Result.SUCCESS; } - return Attack.Result.SUCCESS; - } - - return result; -}; - -/** - * @param {Monster} unit - * @param {number} timedSkill - * @param {number} untimedSkill - * @returns {AttackResult} - */ -ClassAttack.doCast = function (unit, timedSkill, untimedSkill) { - // No valid skills can be found - if (timedSkill < 0 && untimedSkill < 0) return Attack.Result.CANTATTACK; - - let walk; + return result; + }; + + /** + * @param {Monster} unit + * @param {number} timedSkill + * @param {number} untimedSkill + * @returns {AttackResult} + */ + ClassAttack.doCast = function (unit, choosenSkill) { + let { skill, range, mana, timed } = choosenSkill; + // unit became invalidated + if (!unit || !unit.attackable) return Attack.Result.SUCCESS; + if (!!skill && me.mp < mana) { + return Attack.Result.NEEDMANA; + } + // No valid skills can be found + if (skill < 0) return Attack.Result.CANTATTACK; - // Rebuff Hurricane - Skill.canUse(sdk.skills.Hurricane) && !me.getState(sdk.states.Hurricane) && Skill.cast(sdk.skills.Hurricane, sdk.skills.hand.Right); + /** + * @todo handling targetting for fissure/molten moulder/volcano + */ - if (timedSkill > -1 && (!me.getState(sdk.states.SkillDelay) || !Skill.isTimed(timedSkill))) { - switch (timedSkill) { - case sdk.skills.Tornado: - if (Math.ceil(unit.distance) > (Skill.getRange(timedSkill)) || checkCollision(me, unit, sdk.collision.Ranged)) { - if (!Attack.getIntoPosition(unit, (Skill.getRange(timedSkill)), sdk.collision.Ranged)) { - return Attack.Result.FAILED; + if (range > 8 && me.inDanger()) { + Attack.getIntoPosition(unit, range + 1, Coords_1.Collision.BLOCK_MISSILE, true); + } + + if (!me.skillDelay || !timed) { + switch (skill) { + case sdk.skills.Tornado: + if (Math.ceil(unit.distance) > range || checkCollision(me, unit, sdk.collision.Ranged)) { + if (!Attack.getIntoPosition(unit, range, sdk.collision.Ranged)) { + return Attack.Result.FAILED; + } } - } - - // Randomized x coord changes tornado path and prevents constant missing - if (!unit.dead) { - Skill.cast(timedSkill, Skill.getHand(timedSkill), unit.x + rand(-1, 1), unit.y); - } - - return Attack.Result.SUCCESS; - default: - if (Skill.getRange(timedSkill) < 4 && !Attack.validSpot(unit.x, unit.y)) return Attack.Result.FAILED; - - if (Math.ceil(unit.distance) > (Skill.getRange(timedSkill)) || checkCollision(me, unit, sdk.collision.Ranged)) { - // Allow short-distance walking for melee skills - walk = Skill.getRange(timedSkill) < 4 && unit.distance < 10 && !checkCollision(me, unit, sdk.collision.BlockWall); - if (!Attack.getIntoPosition(unit, (Skill.getRange(timedSkill)), sdk.collision.Ranged, walk)) { - return Attack.Result.FAILED; + // Randomized x coord changes tornado path and prevents constant missing + if (!unit.dead) { + Skill.cast(skill, Skill.getHand(skill), unit.x + rand(-1, 1), unit.y); } - } - !unit.dead && Skill.cast(timedSkill, Skill.getHand(timedSkill), unit); + return Attack.Result.SUCCESS; + default: + if (range < 4 && !Attack.validSpot(unit.x, unit.y)) return Attack.Result.FAILED; - return Attack.Result.SUCCESS; - } - } + if (Math.ceil(unit.distance) > range || checkCollision(me, unit, sdk.collision.Ranged)) { + // Allow short-distance walking for melee skills + let walk = range < 4 && unit.distance < 10 && !checkCollision(me, unit, sdk.collision.BlockWall); - if (untimedSkill > -1) { - if (Skill.getRange(untimedSkill) < 4 && !Attack.validSpot(unit.x, unit.y)) return Attack.Result.FAILED; + if (!Attack.getIntoPosition(unit, range, sdk.collision.Ranged, walk)) { + return Attack.Result.FAILED; + } + } - if (Math.ceil(unit.distance) > (Skill.getRange(untimedSkill)) || checkCollision(me, unit, sdk.collision.Ranged)) { - // Allow short-distance walking for melee skills - walk = Skill.getRange(untimedSkill) < 4 && unit.distance < 10 && !checkCollision(me, unit, sdk.collision.BlockWall); + !unit.dead && Skill.cast(skill, Skill.getHand(skill), unit); - if (!Attack.getIntoPosition(unit, (Skill.getRange(untimedSkill)), sdk.collision.Ranged, walk)) { - return Attack.Result.FAILED; + return Attack.Result.SUCCESS; } } - !unit.dead && Skill.cast(untimedSkill, Skill.getHand(untimedSkill), unit); + Misc.poll(() => !me.skillDelay, 1000, 40); return Attack.Result.SUCCESS; - } - - Misc.poll(() => !me.skillDelay, 1000, 40); - - return Attack.Result.SUCCESS; -}; + }; +})(); From 14752552e681912d4064d096e12ba8f0bba452d7 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Sun, 12 Mar 2023 14:40:13 -0400 Subject: [PATCH 073/263] Update SoloEvents.js - Better handling of events to make sure anything we disable gets reenabled properly - Baal skip also should report success/failure properly now --- libs/SoloPlay/Functions/SoloEvents.js | 101 ++++++++++++++------------ 1 file changed, 55 insertions(+), 46 deletions(-) diff --git a/libs/SoloPlay/Functions/SoloEvents.js b/libs/SoloPlay/Functions/SoloEvents.js index 8c771317..1d30605f 100644 --- a/libs/SoloPlay/Functions/SoloEvents.js +++ b/libs/SoloPlay/Functions/SoloEvents.js @@ -14,7 +14,7 @@ } else { root.SoloEvents = factory(); } -}(this, function() { +}(this, function () { const SoloEvents = { filePath: "libs/SoloPlay/Threads/EventThread.js", check: false, @@ -284,37 +284,41 @@ let tick = getTickCount(); myPrint("Attempting baal wave skip"); - // Disable anything that will cause us to stop - [Precast.enabled, Misc.townEnabled, Pickit.enabled] = [false, false, false]; - me.barbarian && (Config.FindItem = false); - - // Prep, move to throne entrance - while (getTickCount() - tick < 6500) { - this.moveTo(15091, 5073, { allowTeleport: true }); - } + try { + // Disable anything that will cause us to stop + [Precast.enabled, Pickit.enabled] = [false, false]; + SoloEvents.townChicken.disabled = true; + me.barbarian && (Config.FindItem = false); - tick = getTickCount(); + // Prep, move to throne entrance + while (getTickCount() - tick < 6500) { + this.moveTo(15091, 5073, { allowTeleport: true }); + } - // 5 second delay (5000ms), then leave throne - while (getTickCount() - tick < 5000) { - this.moveTo(15098, 5082, { allowTeleport: true }); - } + tick = getTickCount(); - tick = getTickCount(); - this.moveTo(15099, 5078); // Re-enter throne + // 5 second delay (5000ms), then leave throne + while (getTickCount() - tick < 5000) { + this.moveTo(15098, 5082, { allowTeleport: true }); + } - // 2 second delay (2000ms) - while (getTickCount() - tick < 2000) { - this.moveTo(15098, 5082); - } + tick = getTickCount(); + this.moveTo(15099, 5078); // Re-enter throne - this.moveTo(15099, 5078); + // 2 second delay (2000ms) + while (getTickCount() - tick < 2000) { + this.moveTo(15098, 5082); + } - // Re-enable - [Precast.enabled, Misc.townEnabled, Pickit.enabled] = [true, true, true]; + this.moveTo(15099, 5078); + } finally { + // Re-enable + [Precast.enabled, Pickit.enabled] = [true, true]; + SoloEvents.townChicken.disabled = false; + } let skipWorked = getUnits(sdk.unittype.Monster) - .some(el => el.attackable && el.x >= 15070 && el.x <= 15120 && el.y >= 5000 && el.y <= 5075); + .some(mon => mon.attackable && mon.x >= 15072 && mon.x <= 15118 && mon.y >= 5002 && mon.y <= 5079); myPrint("skip " + (skipWorked ? "worked" : "failed")); }, @@ -339,32 +343,37 @@ if (diablo && shouldDodge(me)) { let tick = getTickCount(); let overrides = { allowTeleport: false, allowClearing: false, allowTown: false }; - // Disable anything that will cause us to stop - [Precast.enabled, Misc.townEnabled, Pickit.enabled] = [false, false, false]; - console.log("DODGE"); - // Disable things that will cause us to stop - let dist = me.assassin ? 15 : 3; - while (getTickCount() - tick < 2000) { - // Above D - if (me.y <= diablo.y) { - // Move east - me.x <= diablo.x && this.moveTo(diablo.x + dist, diablo.y, overrides); - // Move south - me.x > diablo.x && this.moveTo(diablo.x, diablo.y + dist, overrides); - } + try { + // Disable anything that will cause us to stop + [Precast.enabled, Pickit.enabled] = [false, false]; + SoloEvents.townChicken.disabled = true; + console.log("DODGE"); + // Disable things that will cause us to stop + let dist = me.assassin ? 15 : 3; + + while (getTickCount() - tick < 2000) { + // Above D + if (me.y <= diablo.y) { + // Move east + me.x <= diablo.x && this.moveTo(diablo.x + dist, diablo.y, overrides); + // Move south + me.x > diablo.x && this.moveTo(diablo.x, diablo.y + dist, overrides); + } - // Below D - if (me.y > diablo.y) { - // Move west - me.x >= diablo.x && this.moveTo(diablo.x - dist, diablo.y, overrides); - // Move north - me.x < diablo.x && this.moveTo(diablo.x, diablo.y - dist, overrides); + // Below D + if (me.y > diablo.y) { + // Move west + me.x >= diablo.x && this.moveTo(diablo.x - dist, diablo.y, overrides); + // Move north + me.x < diablo.x && this.moveTo(diablo.x, diablo.y - dist, overrides); + } } + } finally { + // Re-enable + [Precast.enabled, Pickit.enabled] = [true, true]; + SoloEvents.townChicken.disabled = false; } - - // Re-enable - [Precast.enabled, Misc.townEnabled, Pickit.enabled] = [true, true, true]; } }, From 0f1e6bffc9abcb149a80f0c60b2fb012022a4622 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Sun, 12 Mar 2023 14:41:23 -0400 Subject: [PATCH 074/263] Update Mercenary.js - Merc file cleanup --- libs/SoloPlay/Functions/Mercenary.js | 168 +++++++++++++++++---------- 1 file changed, 108 insertions(+), 60 deletions(-) diff --git a/libs/SoloPlay/Functions/Mercenary.js b/libs/SoloPlay/Functions/Mercenary.js index 6c7e7632..cb5a6a04 100644 --- a/libs/SoloPlay/Functions/Mercenary.js +++ b/libs/SoloPlay/Functions/Mercenary.js @@ -6,11 +6,66 @@ * */ +const MercData = new function MercData () { + /** + * @constructor Merc + * @param {number} classid + * @param {number} skill + * @param {number} act + * @param {number} [difficulty] + */ + function Merc (classid, skill, act, difficulty) { + this.classid = classid; + this.skill = skill; + this.skillName = getSkillById(skill); + this.act = act; + this.difficulty = difficulty || sdk.difficulty.Normal; + } + + this[sdk.skills.FireArrow] = new Merc(sdk.mercs.Rogue, sdk.skills.FireArrow, 1); + this[sdk.skills.ColdArrow] = new Merc(sdk.mercs.Rogue, sdk.skills.ColdArrow, 1); + + this[sdk.skills.Prayer] = new Merc(sdk.mercs.Guard, sdk.skills.Prayer, 2, sdk.difficulty.Normal); + this[sdk.skills.BlessedAim] = new Merc(sdk.mercs.Guard, sdk.skills.BlessedAim, 2, sdk.difficulty.Normal); + this[sdk.skills.Defiance] = new Merc(sdk.mercs.Guard, sdk.skills.Defiance, 2, sdk.difficulty.Normal); + + this[sdk.skills.HolyFreeze] = new Merc(sdk.mercs.Guard, sdk.skills.HolyFreeze, 2, sdk.difficulty.Nightmare); + this[sdk.skills.Might] = new Merc(sdk.mercs.Guard, sdk.skills.Might, 2, sdk.difficulty.Nightmare); + this[sdk.skills.Thorns] = new Merc(sdk.mercs.Guard, sdk.skills.Thorns, 2, sdk.difficulty.Nightmare); + + this[sdk.skills.IceBlast] = new Merc(sdk.mercs.IronWolf, sdk.skills.IceBlast, 3, sdk.difficulty.Normal); + this[sdk.skills.FireBall] = new Merc(sdk.mercs.IronWolf, sdk.skills.FireBall, 3, sdk.difficulty.Normal); + this[sdk.skills.Lightning] = new Merc(sdk.mercs.IronWolf, sdk.skills.Lightning, 3, sdk.difficulty.Normal); + + this[sdk.skills.Bash] = new Merc(sdk.mercs.A5Barb, sdk.skills.Bash, 5, sdk.difficulty.Normal); + + this.actMap = new Map(); + this.actMap.set(sdk.mercs.Rogue, 1); + this.actMap.set(1, [this[sdk.skills.FireArrow], this[sdk.skills.ColdArrow]]); + + this.actMap.set(sdk.mercs.Guard, 2); + this.actMap.set(2, [ + this[sdk.skills.Prayer], this[sdk.skills.BlessedAim], + this[sdk.skills.Defiance], this[sdk.skills.HolyFreeze], + this[sdk.skills.Might], this[sdk.skills.Thorns] + ]); + + this.actMap.set(sdk.mercs.IronWolf, 3); + this.actMap.set(3, [this[sdk.skills.IceBlast], this[sdk.skills.FireBall], this[sdk.skills.Lightning]]); + + this.actMap.set(sdk.mercs.A5Barb, 5); + this.actMap.set(5, [this[sdk.skills.Bash]]); +}; + const Mercenary = { minCost: -1, - // only a2 mercs for now, need to test others to see if ModifierListSkill returns their skill - getMercSkill: function (merc = undefined) { + /** + * only a2 mercs for now, need to test others to see if ModifierListSkill returns their skill + * @param {MercUnit} merc + * @returns {string} + */ + getMercSkill: function (merc) { !merc && (merc = Misc.poll(() => me.getMerc(), 1000, 50)); if (!merc) return false; let mercSkill = (() => { @@ -56,10 +111,15 @@ const Mercenary = { return mercSkill ? getSkillById(mercSkill) : ""; }, - // only a2 mercs for now - getMercDifficulty: function (merc = undefined) { + /** + * @param {MercUnit} merc + * @returns {number} + */ + getMercDifficulty: function (merc) { !merc && (merc = Misc.poll(() => me.getMerc(), 1000, 50)); if (!merc) return false; + if (merc.classid !== sdk.mercs.Guard) return sdk.difficulty.Normal; + let mercSkill = merc.getStat(sdk.stats.ModifierListSkill); switch (mercSkill) { @@ -72,23 +132,19 @@ const Mercenary = { } }, + /** + * @param {MercUnit} merc + * @returns {number} + */ getMercAct: function (merc) { !merc && (merc = Misc.poll(() => me.getMerc(), 1000, 50)); if (!merc) return 0; - switch (merc.classid) { - case sdk.mercs.Rogue: - return 1; - case sdk.mercs.Guard: - return 2; - case sdk.mercs.IronWolf: - return 3; - case sdk.mercs.A5Barb: - return 5; - default: - return 0; - } + return MercData.actMap.get(merc.classid) || 0; }, + /** + * @param {MercUnit} merc + */ getMercInfo: function (merc) { !merc && (merc = Misc.poll(() => me.getMerc(), 1000, 50)); if (!merc) return { classid: 0, act: 0, difficulty: 0, type: "" }; @@ -100,39 +156,21 @@ const Mercenary = { }; }, - checkMercSkill: function (wanted = "", merc = undefined) { + /** + * + * @param {{ classid: number, act: number, skill: number, skillName: string, difficulty: number }} wanted + * @param {MercUnit} merc + * @returns {boolean} + */ + checkMercSkill: function (wanted, merc) { merc = !!merc ? merc : me.getMerc(); if (!merc) return false; let mercSkill = merc.getStat(sdk.stats.ModifierListSkill); - // only a2 mercs for now, need to test others to see if above returns their skill - switch (wanted.toLowerCase()) { - case "defiance": - return mercSkill === sdk.skills.Defiance; - case "prayer": - return mercSkill === sdk.skills.Prayer; - case "blessed aim": - return mercSkill === sdk.skills.BlessedAim; - case "thorns": - return mercSkill === sdk.skills.Thorns; - case "holy freeze": - return mercSkill === sdk.skills.HolyFreeze; - case "might": - return mercSkill === sdk.skills.Might; - case "cold arrow": - return merc.getSkill(sdk.skills.ColdArrow, sdk.skills.subindex.HardPoints); - case "fire arrow": - return merc.getSkill(sdk.skills.FireArrow, sdk.skills.subindex.HardPoints); - case "fire ball": - return merc.getSkill(sdk.skills.FireBall, sdk.skills.subindex.HardPoints); - case "lightning": - return merc.getSkill(sdk.skills.Lightning, sdk.skills.subindex.HardPoints); - case "glacial spike": - return merc.getSkill(sdk.skills.GlacialSpike, sdk.skills.subindex.HardPoints); - case "bash": - return merc.getSkill(sdk.skills.Bash, sdk.skills.subindex.HardPoints); - default: - return false; + if (merc.classid === sdk.mercs.Guard) { + return mercSkill === wanted.skill; + } else { + return merc.getSkill(wanted.skill, sdk.skills.subindex.HardPoints) > 0; } }, @@ -140,8 +178,8 @@ const Mercenary = { hireMerc: function () { if (me.classic) return true; let _a; - let { mercAct, mercAuraWanted, mercDiff } = Check.finalBuild(); - let typeOfMerc = (!Pather.accessToAct(2) && me.normal ? 1 : mercAct); + let { wantedMerc } = Check.finalBuild(); + let typeOfMerc = (!Pather.accessToAct(2) && me.normal ? 1 : wantedMerc.act); let tmpAuraName = "Defiance"; // don't hire if using correct a1 merc, or passed merc hire difficulty @@ -155,33 +193,35 @@ const Mercenary = { // we don't have enough gold to hire our wanted merc switch (true) { case typeOfMerc === 1 && (myData.merc.type === "Cold Arrow" || !Misc.checkQuest(sdk.quest.id.SistersBurialGrounds, sdk.quest.states.Completed)): - case myData.merc.type === mercAuraWanted: - case me.diff > mercDiff: - case me.diff === mercDiff && !Pather.accessToAct(mercAct): - case me.diff !== mercDiff && myData.merc.type === "Defiance": - case (me.charlvl > CharInfo.levelCap + 10 && Mercenary.checkMercSkill(myData.merc.type)): + case myData.merc.type === wantedMerc.skillName: + case me.diff > wantedMerc.difficulty: + case me.diff === wantedMerc.difficulty && !Pather.accessToAct(wantedMerc.act): + case me.diff !== wantedMerc.difficulty && myData.merc.type === "Defiance": + case (me.charlvl > CharInfo.levelCap + 10 && Mercenary.checkMercSkill(MercData[getSkillByName(myData.merc.type)])): case me.gold < Math.round((((me.charlvl - 1) * (me.charlvl - 1)) / 2) * 7.5): case this.minCost > 0 && me.gold < this.minCost: return true; } // lets check what our current actually merc is + /** @type {MercUnit} */ let checkMyMerc = Misc.poll(() => me.getMerc(), 50, 500); + const wantedSkill = (typeOfMerc === 1 - ? "Fire Arrow" === mercAuraWanted - ? mercAuraWanted + ? "Fire Arrow" === wantedMerc.skillName + ? wantedMerc.skillName : "Cold Arrow" : me.normal ? tmpAuraName - : mercAuraWanted + : wantedMerc.skillName ); - if (checkMyMerc && Mercenary.checkMercSkill(wantedSkill, checkMyMerc)) { + if (checkMyMerc && Mercenary.checkMercSkill(wantedMerc, checkMyMerc)) { // we have our wanted merc, data file was probably erased so lets re-update it myData.merc.act = Mercenary.getMercAct(checkMyMerc); myData.merc.classid = checkMyMerc.classid; myData.merc.difficulty = Mercenary.getMercDifficulty(checkMyMerc); - myData.merc.type = wantedSkill; + myData.merc.type = wantedMerc.skillName; CharData.updateData("merc", myData) && updateMyData(); return true; } else if (!!checkMyMerc && checkMyMerc.classid === sdk.mercs.Guard) { @@ -201,8 +241,10 @@ const Mercenary = { me.sortInventory(); Item.removeItemsMerc(); // strip temp merc gear delay(500 + me.ping); + addEventListener("gamepacket", MercLib_1.mercPacket); Town.initNPC("Merc", "getMerc"); + let wantedMerc = MercLib_1.default .filter((merc) => merc.skills.some((skill) => (skill === null || skill === void 0 ? void 0 : skill.name) === wantedSkill)) .sort((a, b) => b.level - a.level) @@ -210,10 +252,12 @@ const Mercenary = { if (wantedMerc) { if (wantedMerc.cost > me.gold) { Mercenary.minCost = wantedMerc.cost; - throw new Error(); + throw new Error("Too expensive " + wantedMerc.cost); } - let oldGid_1 = (_a = me.getMerc()) === null || _a === void 0 ? void 0 : _a.gid; + + let oldGid_1 = (_a = me.getMercEx()) === null || _a === void 0 ? void 0 : _a.gid; console.log("ÿc9Mercenaryÿc0 :: Found a merc to hire " + JSON.stringify(wantedMerc)); + wantedMerc === null || wantedMerc === void 0 ? void 0 : wantedMerc.hire(); let newMerc = Misc.poll(function () { let merc = me.getMerc(); @@ -221,6 +265,7 @@ const Mercenary = { if (oldGid_1 && oldGid_1 === merc.gid) return false; return merc; }); + console.log("Hired a merc?"); if (newMerc) { console.log("Yep"); @@ -228,6 +273,7 @@ const Mercenary = { myData.merc.classid = newMerc.classid; myData.merc.difficulty = me.diff; myData.merc.type = wantedMerc.skills.find(sk => sk.name === wantedSkill).name; + // myData.merc.skills = getSkillByName(myData.merc.type); CharData.updateData("merc", myData) && updateMyData(); console.log("ÿc9Mercenaryÿc0 :: " + myData.merc.type + " merc hired."); } @@ -236,9 +282,11 @@ const Mercenary = { delay(me.ping || 5); me.cancel(); } + } else { + console.warn("Failed to find wanted Merc"); } } catch (e) { - // + console.error(e); } finally { removeEventListener("gamepacket", MercLib_1.mercPacket); } From 3edc88f59888df689b0497583c5c1b843320b67f Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Sun, 12 Mar 2023 14:49:15 -0400 Subject: [PATCH 075/263] Restructure buildfiles/autobuild/configs - Factored out a town of repeat code from the config files - Allow use of white items that are not valid bases, makes for far faster full equip at early level - Restructure every build file into a module - Created me.currentBuild and me.finalBuild prototypes to easily access the data - Cleaned up and improved the user input error handling for final builds, probably still ways people could mess it up but should be much harder to do now - Moved SoloWants out of globals into its own file --- .../amazon/amazon.FaithbowzonBuild.js | 265 ++++----- .../amazon/amazon.FrostmaidenBuild.js | 237 ++++---- .../BuildFiles/amazon/amazon.JavazonBuild.js | 322 +++++------ .../BuildFiles/amazon/amazon.LevelingBuild.js | 145 ++--- .../BuildFiles/amazon/amazon.StartBuild.js | 150 ++--- .../BuildFiles/amazon/amazon.SteppingBuild.js | 104 ++-- .../BuildFiles/amazon/amazon.WfzonBuild.js | 259 ++++----- .../amazon/amazon.WitchyzonBuild.js | 249 +++++---- .../assassin/assassin.LevelingBuild.js | 146 ++--- .../assassin/assassin.StartBuild.js | 117 ++-- .../assassin/assassin.TrapsinBuild.js | 269 ++++----- .../assassin/assassin.WhirlsinBuild.js | 251 +++++---- .../barbarian/barbarian.FrenzyBuild.js | 253 ++++----- .../barbarian/barbarian.ImmortalwhirlBuild.js | 241 ++++---- .../barbarian/barbarian.LevelingBuild.js | 98 ++-- .../barbarian/barbarian.SingerBuild.js | 4 +- .../barbarian/barbarian.StartBuild.js | 151 ++--- .../barbarian/barbarian.SteppingBuild.js | 135 ++--- .../barbarian/barbarian.ThrowBuild.js | 239 ++++---- .../barbarian/barbarian.UberconcBuild.js | 213 ++++---- .../barbarian/barbarian.WhirlwindBuild.js | 237 ++++---- .../BuildFiles/druid/druid.ElementalBuild.js | 273 +++++---- .../BuildFiles/druid/druid.FirewolfBuild.js | 231 ++++---- .../BuildFiles/druid/druid.LevelingBuild.js | 102 ++-- .../BuildFiles/druid/druid.PlaguewolfBuild.js | 213 ++++---- .../BuildFiles/druid/druid.StartBuild.js | 4 +- .../BuildFiles/druid/druid.StormbearBuild.js | 233 ++++---- .../BuildFiles/druid/druid.WindBuild.js | 233 ++++---- .../BuildFiles/druid/druid.WolfBuild.js | 217 ++++---- .../necromancer/necromancer.BoneBuild.js | 296 +++++----- .../necromancer/necromancer.LevelingBuild.js | 106 ++-- .../necromancer/necromancer.PoisonBuild.js | 300 +++++----- .../necromancer/necromancer.StartBuild.js | 139 ++--- .../necromancer/necromancer.SummonBuild.js | 327 +++++------ .../paladin/paladin.AuradinBuild.js | 233 ++++---- .../paladin/paladin.ClassicauradinBuild.js | 282 +++++----- .../paladin/paladin.HammerdinBuild.js | 303 +++++----- .../paladin/paladin.HammershockBuild.js | 281 +++++----- .../paladin/paladin.LevelingBuild.js | 216 ++++---- .../paladin/paladin.SancdreamerBuild.js | 231 ++++---- .../BuildFiles/paladin/paladin.SmiterBuild.js | 213 ++++---- .../BuildFiles/paladin/paladin.StartBuild.js | 168 +++--- .../paladin/paladin.TorchadinBuild.js | 238 ++++---- .../BuildFiles/paladin/paladin.ZealerBuild.js | 230 ++++---- .../sorceress/sorceress.BlizzballerBuild.js | 296 +++++----- .../sorceress/sorceress.BlovaBuild.js | 276 +++++----- .../sorceress/sorceress.ColdBuild.js | 269 ++++----- .../sorceress/sorceress.EsorbBuild.js | 339 ++++++------ .../sorceress/sorceress.LevelingBuild.js | 153 +++--- .../sorceress/sorceress.LightningBuild.js | 272 ++++----- .../sorceress/sorceress.MeteorbBuild.js | 345 ++++++------ .../sorceress/sorceress.StartBuild.js | 165 +++--- .../sorceress/sorceress.SteppingBuild.js | 141 ++--- libs/SoloPlay/Config/Amazon.js | 55 +- libs/SoloPlay/Config/Assassin.js | 55 +- libs/SoloPlay/Config/Barbarian.js | 51 +- libs/SoloPlay/Config/Druid.js | 55 +- libs/SoloPlay/Config/Necromancer.js | 60 +- libs/SoloPlay/Config/Paladin.js | 89 ++- libs/SoloPlay/Config/Sorceress.js | 58 +- .../{AutoBuildOverrides.js => AutoBuild.js} | 56 +- libs/SoloPlay/Functions/ConfigOverrides.js | 6 +- libs/SoloPlay/Functions/DynamicTiers.js | 2 +- libs/SoloPlay/Functions/Globals.js | 517 +++++------------- libs/SoloPlay/Functions/Me.js | 44 +- libs/SoloPlay/Functions/SoloWants.js | 258 +++++++++ libs/SoloPlay/Utils/General.js | 7 + libs/SoloPlay/index.d.ts | 106 +++- 68 files changed, 6546 insertions(+), 6283 deletions(-) rename libs/SoloPlay/Functions/{AutoBuildOverrides.js => AutoBuild.js} (54%) create mode 100644 libs/SoloPlay/Functions/SoloWants.js diff --git a/libs/SoloPlay/BuildFiles/amazon/amazon.FaithbowzonBuild.js b/libs/SoloPlay/BuildFiles/amazon/amazon.FaithbowzonBuild.js index 7d9da63c..1602e584 100644 --- a/libs/SoloPlay/BuildFiles/amazon/amazon.FaithbowzonBuild.js +++ b/libs/SoloPlay/BuildFiles/amazon/amazon.FaithbowzonBuild.js @@ -6,140 +6,149 @@ * */ -const finalBuild = { - caster: false, - skillstab: sdk.skills.tabs.BowandCrossbow, - wantedskills: [sdk.skills.Strafe], - usefulskills: [sdk.skills.Penetrate, sdk.skills.Valkyrie, sdk.skills.Pierce], - precastSkills: [sdk.skills.Valkyrie], - mercDiff: sdk.difficulty.Nightmare, - mercAct: 2, - mercAuraWanted: "Might", - stats: [ - ["strength", 103], ["dexterity", 152], ["vitality", 150], ["dexterity", "all"] - ], - skills: [ - [sdk.skills.Valkyrie, 1], - [sdk.skills.Strafe, 20], - [sdk.skills.Pierce, 19], - [sdk.skills.Penetrate, 3], - [sdk.skills.CriticalStrike, 19], - [sdk.skills.Valkyrie, 20], - [sdk.skills.Dodge, 4], - [sdk.skills.Avoid, 4], - [sdk.skills.Evade, 4], - [sdk.skills.Dodge, 9], - [sdk.skills.Evade, 9], - ], - autoEquipTiers: [ // autoequip final gear - // Final Weapon - Faith - "([type] == amazonbow || [type] == bow) && [flag] == runeword # [fanaticismaura] >= 12 && [itemallskills] >= 1 # [tier] == tierscore(item, 100000)", - // Weapon - WitchWild String up'd - "[name] == diamondbow && [quality] == unique # [fireresist] == 40 # [tier] == tierscore(item, 100000)", - // Helmet - Vampz Gaze - "[name] == grimhelm && [quality] == unique && [flag] != ethereal # [manaleech] >= 6 && [lifeleech] >= 6 && [damageresist] >= 15 # [tier] == tierscore(item, 100000)", - // Boots - War Traveler - "[name] == battleboots && [quality] == unique && [flag] != ethereal # [itemmagicbonus] >= 35 # [tier] == tierscore(item, 100000)", - // Belt - Nosferatu's Coil - "[name] == vampirefangbelt && [quality] == unique && [flag] != ethereal # [lifeleech] >= 5 # [tier] == tierscore(item, 100000)", - // Armor - CoH - "[type] == armor && [flag] == runeword && [flag] != ethereal # [fireresist] == 65 && [hpregen] == 7 # [tier] == 100000", - // Gloves - Lava Gout - "[name] == battlegauntlets && [quality] == unique && [flag] != ethereal # [ias] == 20 && [enhanceddefense] >= 150 # [tier] == tierscore(item, 3000)", - // Final Amulet - Atma's Scarab - "[type] == amulet && [quality] == unique # [poisonresist] == 75 # [tier] == 110000", - // Amulet - Highlords - "[type] == amulet && [quality] == unique # [lightresist] == 35 # [tier] == 100000", - // Final Rings - Perfect Raven Frost & Bul-Kathos' Wedding Band - "[type] == ring && [quality] == unique # [dexterity] == 20 && [tohit] == 250 # [tier] == 110000", - "[type] == ring && [quality] == unique # [maxstamina] == 50 && [lifeleech] == 5 # [tier] == 110000", - // Rings - Raven Frost && Bul-Kathos' Wedding Band - "[type] == ring && [quality] == unique # [dexterity] >= 15 && [tohit] >= 150 # [tier] == 100000", - "[type] == ring && [quality] == unique # [maxstamina] == 50 && [lifeleech] >= 3 # [tier] == 100000", - // Switch Final Weapon - CTA - "[minimumsockets] >= 5 && [flag] == runeword # [plusskillbattleorders] >= 1 # [secondarytier] == 100000", - // Switch Temporary Weapon - Life Tap charged wand - "[type] == wand && [quality] >= normal # [itemchargedskill] == 82 # [secondarytier] == 75000 + chargeditemscore(item, 82)", - // Switch Shield - Any 1+ all skill - "[type] == shield # [itemallskills] >= 1 # [secondarytier] == tierscore(item, 100000)", - // Merc Armor - Fortitude - "[type] == armor && [flag] == runeword # [enhanceddefense] >= 200 && [enhanceddamage] >= 300 # [merctier] == 100000", - // Merc Final Helmet - Eth Andy's - "[name] == demonhead && [quality] == unique && [flag] == ethereal # [strength] >= 25 && [enhanceddefense] >= 100 # [merctier] == 40000 + mercscore(item)", - // Merc Helmet - Andy's - "[name] == demonhead && [quality] == unique && [flag] != ethereal # [strength] >= 25 && [enhanceddefense] >= 100 # [merctier] == 50000 + mercscore(item)", - ], +(function (module) { + module.exports = (function () { + const build = { + caster: false, + skillstab: sdk.skills.tabs.BowandCrossbow, + wantedskills: [sdk.skills.Strafe], + usefulskills: [sdk.skills.Penetrate, sdk.skills.Valkyrie, sdk.skills.Pierce], + precastSkills: [sdk.skills.Valkyrie], + wantedMerc: MercData[sdk.skills.Might], + stats: [ + ["strength", 103], ["dexterity", 152], ["vitality", 150], ["dexterity", "all"] + ], + skills: [ + [sdk.skills.Valkyrie, 1], + [sdk.skills.Strafe, 20], + [sdk.skills.Pierce, 19], + [sdk.skills.Penetrate, 3], + [sdk.skills.CriticalStrike, 19], + [sdk.skills.Valkyrie, 20], + [sdk.skills.Dodge, 4], + [sdk.skills.Avoid, 4], + [sdk.skills.Evade, 4], + [sdk.skills.Dodge, 9], + [sdk.skills.Evade, 9], - charms: { - ResLife: { - max: 3, - have: [], - classid: sdk.items.SmallCharm, - stats: function (check) { - return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MaxHp) === 20); - } - }, + ], - ResMf: { - max: 2, - have: [], - classid: sdk.items.SmallCharm, - stats: function (check) { - return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MagicBonus) === 7); - } - }, + charms: { + ResLife: { + max: 3, + have: [], + classid: sdk.items.SmallCharm, + stats: function (check) { + return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MaxHp) === 20); + } + }, - ResFHR: { - max: 1, - have: [], - classid: sdk.items.SmallCharm, - stats: function (check) { - return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.FHR) === 5); - } - }, + ResMf: { + max: 2, + have: [], + classid: sdk.items.SmallCharm, + stats: function (check) { + return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MagicBonus) === 7); + } + }, - SkillerCrossbow: { - max: 1, - have: [], - classid: sdk.items.GrandCharm, - stats: function (check) { - return (!check.unique && check.classid === this.classid && check.getStat(sdk.stats.AddSkillTab, sdk.skills.tabs.BowandCrossbow) === 1 - && check.getStat(sdk.stats.MaxHp) >= 40); - } - }, + ResFHR: { + max: 1, + have: [], + classid: sdk.items.SmallCharm, + stats: function (check) { + return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.FHR) === 5); + } + }, - SkillerPassive: { - max: 1, - have: [], - classid: sdk.items.GrandCharm, - stats: function (check) { - return (!check.unique && check.classid === this.classid && check.getStat(sdk.stats.AddSkillTab, sdk.skills.tabs.PassiveandMagic) === 1 - && check.getStat(sdk.stats.MaxHp) >= 40); - } - }, - }, + SkillerCrossbow: { + max: 1, + have: [], + classid: sdk.items.GrandCharm, + stats: function (check) { + return (!check.unique && check.classid === this.classid && check.getStat(sdk.stats.AddSkillTab, sdk.skills.tabs.BowandCrossbow) === 1 + && check.getStat(sdk.stats.MaxHp) >= 40); + } + }, - AutoBuildTemplate: { - 1: { - Update: function () { - Config.AttackSkill = [-1, sdk.skills.Strafe, -1, sdk.skills.Strafe, -1, sdk.skills.MagicArrow, -1]; - Config.LowManaSkill = [0, -1]; - Config.HPBuffer = 1; - Config.MPBuffer = 1; - } - }, - }, + SkillerPassive: { + max: 1, + have: [], + classid: sdk.items.GrandCharm, + stats: function (check) { + return (!check.unique && check.classid === this.classid && check.getStat(sdk.stats.AddSkillTab, sdk.skills.tabs.PassiveandMagic) === 1 + && check.getStat(sdk.stats.MaxHp) >= 40); + } + }, + }, - respec: function () { - if (me.classic) { - return false; - } else { - return (me.checkItem({ name: sdk.locale.items.Faith }).have || Check.haveItem("diamondbow", "unique", "Witchwild String")); - } - }, + AutoBuildTemplate: { + 1: { + Update: function () { + Config.AttackSkill = [-1, sdk.skills.Strafe, -1, sdk.skills.Strafe, -1, sdk.skills.MagicArrow, -1]; + Config.LowManaSkill = [0, -1]; + Config.HPBuffer = 1; + Config.MPBuffer = 1; + } + }, + }, - active: function () { - return this.respec() && me.checkSkill(sdk.skills.Strafe, sdk.skills.subindex.HardPoints); - }, -}; + respec: function () { + if (me.classic) { + return false; + } else { + return (me.checkItem({ name: sdk.locale.items.Faith }).have || Check.haveItem("diamondbow", "unique", "Witchwild String")); + } + }, + + active: function () { + return this.respec() && me.checkSkill(sdk.skills.Strafe, sdk.skills.subindex.HardPoints); + }, + }; + + let finalGear = [ // autoequip final gear + // Final Weapon - Faith + "([type] == amazonbow || [type] == bow) && [flag] == runeword # [fanaticismaura] >= 12 && [itemallskills] >= 1 # [tier] == tierscore(item, 100000)", + // Weapon - WitchWild String up'd + "[name] == diamondbow && [quality] == unique # [fireresist] == 40 # [tier] == tierscore(item, 100000)", + // Helmet - Vampz Gaze + "[name] == grimhelm && [quality] == unique && [flag] != ethereal # [manaleech] >= 6 && [lifeleech] >= 6 && [damageresist] >= 15 # [tier] == tierscore(item, 100000)", + // Boots - War Traveler + "[name] == battleboots && [quality] == unique && [flag] != ethereal # [itemmagicbonus] >= 35 # [tier] == tierscore(item, 100000)", + // Belt - Nosferatu's Coil + "[name] == vampirefangbelt && [quality] == unique && [flag] != ethereal # [lifeleech] >= 5 # [tier] == tierscore(item, 100000)", + // Armor - CoH + "[type] == armor && [flag] == runeword && [flag] != ethereal # [fireresist] == 65 && [hpregen] == 7 # [tier] == 100000", + // Gloves - Lava Gout + "[name] == battlegauntlets && [quality] == unique && [flag] != ethereal # [ias] == 20 && [enhanceddefense] >= 150 # [tier] == tierscore(item, 3000)", + // Final Amulet - Atma's Scarab + "[type] == amulet && [quality] == unique # [poisonresist] == 75 # [tier] == 110000", + // Amulet - Highlords + "[type] == amulet && [quality] == unique # [lightresist] == 35 # [tier] == 100000", + // Final Rings - Perfect Raven Frost & Bul-Kathos' Wedding Band + "[type] == ring && [quality] == unique # [dexterity] == 20 && [tohit] == 250 # [tier] == 110000", + "[type] == ring && [quality] == unique # [maxstamina] == 50 && [lifeleech] == 5 # [tier] == 110000", + // Rings - Raven Frost && Bul-Kathos' Wedding Band + "[type] == ring && [quality] == unique # [dexterity] >= 15 && [tohit] >= 150 # [tier] == 100000", + "[type] == ring && [quality] == unique # [maxstamina] == 50 && [lifeleech] >= 3 # [tier] == 100000", + // Switch Final Weapon - CTA + "[minimumsockets] >= 5 && [flag] == runeword # [plusskillbattleorders] >= 1 # [secondarytier] == 100000", + // Switch Temporary Weapon - Life Tap charged wand + "[type] == wand && [quality] >= normal # [itemchargedskill] == 82 # [secondarytier] == 75000 + chargeditemscore(item, 82)", + // Switch Shield - Any 1+ all skill + "[type] == shield # [itemallskills] >= 1 # [secondarytier] == tierscore(item, 100000)", + // Merc Armor - Fortitude + "[type] == armor && [flag] == runeword # [enhanceddefense] >= 200 && [enhanceddamage] >= 300 # [merctier] == 100000", + // Merc Final Helmet - Eth Andy's + "[name] == demonhead && [quality] == unique && [flag] == ethereal # [strength] >= 25 && [enhanceddefense] >= 100 # [merctier] == 40000 + mercscore(item)", + // Merc Helmet - Andy's + "[name] == demonhead && [quality] == unique && [flag] != ethereal # [strength] >= 25 && [enhanceddefense] >= 100 # [merctier] == 50000 + mercscore(item)", + ]; + + NTIP.buildList(finalGear); + NTIP.buildFinalGear(finalGear); + + return build; + })(); +})(module); diff --git a/libs/SoloPlay/BuildFiles/amazon/amazon.FrostmaidenBuild.js b/libs/SoloPlay/BuildFiles/amazon/amazon.FrostmaidenBuild.js index ba47b24f..c44ac72b 100644 --- a/libs/SoloPlay/BuildFiles/amazon/amazon.FrostmaidenBuild.js +++ b/libs/SoloPlay/BuildFiles/amazon/amazon.FrostmaidenBuild.js @@ -5,125 +5,134 @@ * */ -const finalBuild = { - caster: false, - skillstab: sdk.skills.tabs.BowandCrossbow, - wantedskills: [sdk.skills.Strafe, sdk.skills.FreezingArrow, sdk.skills.ExplodingArrow], - usefulskills: [sdk.skills.Penetrate, sdk.skills.Valkyrie, sdk.skills.Pierce, sdk.skills.IceArrow, sdk.skills.ColdArrow], - precastSkills: [sdk.skills.Valkyrie], - mercDiff: sdk.difficulty.Nightmare, - mercAct: 2, - mercAuraWanted: "Might", - stats: [ - ["strength", 156], ["dexterity", 152], ["vitality", 150], ["dexterity", "all"] - ], - skills: [ - [sdk.skills.Valkyrie, 1], - [sdk.skills.Strafe, 1], - [sdk.skills.Pierce, 1], - [sdk.skills.FreezingArrow, 20], - [sdk.skills.IceArrow, 20], - [sdk.skills.ColdArrow, 20], - [sdk.skills.Strafe, 20], - [sdk.skills.Valkyrie, 20], - ], - autoEquipTiers: [ // autoequip final gear - // Weapon - Brand - "[name] == grandmatronbow && [flag] == runeword # [enhanceddamage] >= 260 && [itemknockback] == 1 && [itemdeadlystrike] == 20 # [tier] == tierscore(item, 100000)", - // Helmet - Dream - "[type] == helm && [flag] == runeword # [holyshockaura] >= 15 # [tier] == 110000", - // Boots - War Traveler - "[name] == battleboots && [quality] == unique && [flag] != ethereal # [itemmagicbonus] >= 30 # [tier] == 100000", - // Belt - Nosferatu's Coil - "[name] == sharkskinbelt && [quality] == unique && [flag] != ethereal # [dexterity] == 15 # [tier] == tierscore(item, 100000)", - // Armor - Chains of Honor - "[type] == armor && [flag] == runeword && [flag] != ethereal # [fireresist] == 65 # [tier] == 100000", - // Amulet - Highlords - "[type] == amulet && [quality] == unique # [lightresist] == 35 # [tier] == 110000", - // Rings - Ravenfrost & Carrion Wind - "[type] == ring && [quality] == unique # [dexterity] == 20 && [tohit] == 250 # [tier] == 110000", - "[name] == ring && [quality] == unique # [poisonresist] == 55 && [lifeleech] >= 6 # [tier] == 110000", - // Switch - CTA - "[minimumsockets] >= 5 && [flag] == runeword # [plusskillbattleorders] >= 1 # [secondarytier] == 100000", - // Switch Temporary Weapon - Life Tap charged wand - "[type] == wand && [quality] >= normal # [itemchargedskill] == 82 # [secondarytier] == 75000 + chargeditemscore(item, 82)", - // Switch Shield - Any 1+ all skill - "[type] == shield # [itemallskills] >= 1 # [secondarytier] == tierscore(item, 100000)", - // Merc Armor - Fortitude - "[type] == armor && [flag] == runeword # [enhanceddefense] >= 200 && [enhanceddamage] >= 300 # [merctier] == 100000", - // Merc Final Helmet - Eth Andy's - "[name] == demonhead && [quality] == unique && [flag] == ethereal # [strength] >= 25 && [enhanceddefense] >= 100 # [merctier] == 50000 + mercscore(item)", - // Merc Helmet - Andy's - "[name] == demonhead && [quality] == unique && [flag] != ethereal # [strength] >= 25 && [enhanceddefense] >= 100 # [merctier] == 40000 + mercscore(item)", - ], - charms: { - ResLife: { - max: 3, - have: [], - classid: sdk.items.SmallCharm, - stats: function (check) { - return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MaxHp) === 20); - } - }, +(function (module) { + module.exports = (function () { + const build = { + caster: false, + skillstab: sdk.skills.tabs.BowandCrossbow, + wantedskills: [sdk.skills.Strafe, sdk.skills.FreezingArrow, sdk.skills.ExplodingArrow], + usefulskills: [sdk.skills.Penetrate, sdk.skills.Valkyrie, sdk.skills.Pierce, sdk.skills.IceArrow, sdk.skills.ColdArrow], + precastSkills: [sdk.skills.Valkyrie], + wantedMerc: MercData[sdk.skills.Might], + stats: [ + ["strength", 156], ["dexterity", 152], ["vitality", 150], ["dexterity", "all"] + ], + skills: [ + [sdk.skills.Valkyrie, 1], + [sdk.skills.Strafe, 1], + [sdk.skills.Pierce, 1], + [sdk.skills.FreezingArrow, 20], + [sdk.skills.IceArrow, 20], + [sdk.skills.ColdArrow, 20], + [sdk.skills.Strafe, 20], + [sdk.skills.Valkyrie, 20], + ], - ResMf: { - max: 2, - have: [], - classid: sdk.items.SmallCharm, - stats: function (check) { - return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MagicBonus) === 7); - } - }, + charms: { + ResLife: { + max: 3, + have: [], + classid: sdk.items.SmallCharm, + stats: function (check) { + return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MaxHp) === 20); + } + }, - ResFHR: { - max: 1, - have: [], - classid: sdk.items.SmallCharm, - stats: function (check) { - return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.FHR) === 5); - } - }, + ResMf: { + max: 2, + have: [], + classid: sdk.items.SmallCharm, + stats: function (check) { + return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MagicBonus) === 7); + } + }, - SkillerCrossbow: { - max: 1, - have: [], - classid: sdk.items.GrandCharm, - stats: function (check) { - return (!check.unique && check.classid === this.classid && check.getStat(sdk.stats.AddSkillTab, sdk.skills.tabs.BowandCrossbow) === 1 - && check.getStat(sdk.stats.MaxHp) >= 40); - } - }, + ResFHR: { + max: 1, + have: [], + classid: sdk.items.SmallCharm, + stats: function (check) { + return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.FHR) === 5); + } + }, - SkillerPassive: { - max: 1, - have: [], - classid: sdk.items.GrandCharm, - stats: function (check) { - return (!check.unique && check.classid === this.classid && check.getStat(sdk.stats.AddSkillTab, sdk.skills.tabs.PassiveandMagic) === 1 - && check.getStat(sdk.stats.MaxHp) >= 40); - } - }, - }, + SkillerCrossbow: { + max: 1, + have: [], + classid: sdk.items.GrandCharm, + stats: function (check) { + return (!check.unique && check.classid === this.classid && check.getStat(sdk.stats.AddSkillTab, sdk.skills.tabs.BowandCrossbow) === 1 + && check.getStat(sdk.stats.MaxHp) >= 40); + } + }, - AutoBuildTemplate: { - 1: { - Update: function () { - Config.AttackSkill = [-1, sdk.skills.FreezingArrow, -1, sdk.skills.FreezingArrow, -1, sdk.skills.Strafe, -1]; - Config.LowManaSkill = [0, -1]; - } - }, - }, + SkillerPassive: { + max: 1, + have: [], + classid: sdk.items.GrandCharm, + stats: function (check) { + return (!check.unique && check.classid === this.classid && check.getStat(sdk.stats.AddSkillTab, sdk.skills.tabs.PassiveandMagic) === 1 + && check.getStat(sdk.stats.MaxHp) >= 40); + } + }, + }, - respec: function () { - if (me.classic) { - return false; - } else { - return me.haveAll([{ name: sdk.locale.items.Dream, itemtype: sdk.items.type.Helm }, { name: sdk.locale.items.Brand }, { name: sdk.locale.items.ChainsofHonor }]); - } - }, + AutoBuildTemplate: { + 1: { + Update: function () { + Config.AttackSkill = [-1, sdk.skills.FreezingArrow, -1, sdk.skills.FreezingArrow, -1, sdk.skills.Strafe, -1]; + Config.LowManaSkill = [0, -1]; + } + }, + }, - active: function () { - return this.respec() && me.checkSkill(sdk.skills.FreezingArrow, sdk.skills.subindex.HardPoints); - }, -}; + respec: function () { + if (me.classic) { + return false; + } else { + return me.haveAll([{ name: sdk.locale.items.Dream, itemtype: sdk.items.type.Helm }, { name: sdk.locale.items.Brand }, { name: sdk.locale.items.ChainsofHonor }]); + } + }, + + active: function () { + return this.respec() && me.checkSkill(sdk.skills.FreezingArrow, sdk.skills.subindex.HardPoints); + }, + }; + + let finalGear = [ // autoequip final gear + // Weapon - Brand + "[name] == grandmatronbow && [flag] == runeword # [enhanceddamage] >= 260 && [itemknockback] == 1 && [itemdeadlystrike] == 20 # [tier] == tierscore(item, 100000)", + // Helmet - Dream + "[type] == helm && [flag] == runeword # [holyshockaura] >= 15 # [tier] == 110000", + // Boots - War Traveler + "[name] == battleboots && [quality] == unique && [flag] != ethereal # [itemmagicbonus] >= 30 # [tier] == 100000", + // Belt - Nosferatu's Coil + "[name] == sharkskinbelt && [quality] == unique && [flag] != ethereal # [dexterity] == 15 # [tier] == tierscore(item, 100000)", + // Armor - Chains of Honor + "[type] == armor && [flag] == runeword && [flag] != ethereal # [fireresist] == 65 # [tier] == 100000", + // Amulet - Highlords + "[type] == amulet && [quality] == unique # [lightresist] == 35 # [tier] == 110000", + // Rings - Ravenfrost & Carrion Wind + "[type] == ring && [quality] == unique # [dexterity] == 20 && [tohit] == 250 # [tier] == 110000", + "[name] == ring && [quality] == unique # [poisonresist] == 55 && [lifeleech] >= 6 # [tier] == 110000", + // Switch - CTA + "[minimumsockets] >= 5 && [flag] == runeword # [plusskillbattleorders] >= 1 # [secondarytier] == 100000", + // Switch Temporary Weapon - Life Tap charged wand + "[type] == wand && [quality] >= normal # [itemchargedskill] == 82 # [secondarytier] == 75000 + chargeditemscore(item, 82)", + // Switch Shield - Any 1+ all skill + "[type] == shield # [itemallskills] >= 1 # [secondarytier] == tierscore(item, 100000)", + // Merc Armor - Fortitude + "[type] == armor && [flag] == runeword # [enhanceddefense] >= 200 && [enhanceddamage] >= 300 # [merctier] == 100000", + // Merc Final Helmet - Eth Andy's + "[name] == demonhead && [quality] == unique && [flag] == ethereal # [strength] >= 25 && [enhanceddefense] >= 100 # [merctier] == 50000 + mercscore(item)", + // Merc Helmet - Andy's + "[name] == demonhead && [quality] == unique && [flag] != ethereal # [strength] >= 25 && [enhanceddefense] >= 100 # [merctier] == 40000 + mercscore(item)", + ]; + + NTIP.buildList(finalGear); + NTIP.buildFinalGear(finalGear); + + return build; + })(); +})(module); diff --git a/libs/SoloPlay/BuildFiles/amazon/amazon.JavazonBuild.js b/libs/SoloPlay/BuildFiles/amazon/amazon.JavazonBuild.js index 5ca01bee..ef322b66 100644 --- a/libs/SoloPlay/BuildFiles/amazon/amazon.JavazonBuild.js +++ b/libs/SoloPlay/BuildFiles/amazon/amazon.JavazonBuild.js @@ -5,167 +5,175 @@ * */ -const finalBuild = { - caster: false, - skillstab: sdk.skills.tabs.JavelinandSpear, - wantedskills: [sdk.skills.ChargedStrike, sdk.skills.LightningStrike], - usefulskills: [sdk.skills.CriticalStrike, sdk.skills.Penetrate, sdk.skills.Valkyrie, sdk.skills.Pierce], - precastSkills: [sdk.skills.Valkyrie], - usefulStats: [sdk.stats.PierceLtng, sdk.stats.PassiveLightningMastery, sdk.stats.PassiveLightningPierce], - mercDiff: sdk.difficulty.Nightmare, - mercAct: 2, - mercAuraWanted: "Holy Freeze", - classicStats: [ - ["dexterity", 65], ["strength", 75], ["vitality", "all"] - ], - expansionStats: [ - ["strength", 34], ["vitality", 30], ["dexterity", 47], - ["vitality", 45], ["strength", 47], ["dexterity", 65], - ["vitality", 65], ["strength", 53], ["dexterity", 118], - ["vitality", 100], ["strength", 118], ["dexterity", 151], - ["strength", 156], ["vitality", "all"], - ], - classicSkills: [ - [sdk.skills.Valkyrie, 1], - [sdk.skills.LightningFury, 1], - [sdk.skills.LightningStrike, 1], - [sdk.skills.Pierce, 1], - [sdk.skills.PlagueJavelin, 20], - [sdk.skills.ChargedStrike, 10], - [sdk.skills.LightningStrike, 10], - [sdk.skills.Decoy, 5], - [sdk.skills.LightningStrike, 17], - [sdk.skills.ChargedStrike, 15], - [sdk.skills.LightningStrike, 20, false], - [sdk.skills.ChargedStrike, 20, false], - [sdk.skills.PoisonJavelin, 20, false], - [sdk.skills.Valkyrie, 12, false], - [sdk.skills.LightningFury, 20, false], - ], - expansionSkills: [ - [sdk.skills.Valkyrie, 1], - [sdk.skills.Pierce, 1], - [sdk.skills.LightningStrike, 20], - [sdk.skills.ChargedStrike, 20], - [sdk.skills.LightningFury, 20], - [sdk.skills.Decoy, 5, false], - [sdk.skills.Valkyrie, 17, false], - [sdk.skills.PowerStrike, 20, false], - [sdk.skills.Pierce, 5, false], - ], - classicTiers: [ - // Helm - Tarnhelm - "[name] == skullcap && [quality] == unique # [itemallskills] == 1 && [itemmagicbonus] >= 25 # [tier] == tierscore(item, 100000)", - // Armor - Twitchthroe - "[name] == studdedleather && [quality] == unique # [ias] == 20 && [fhr] == 20 # [tier] == 100000", - // Belt - Death's Guard Sash - "[name] == sash && [quality] == set # [itemcannotbefrozen] == 1 # [tier] == 100000", - // Gloves - Death's Hand Leather Gloves - "[name] == leathergloves && [quality] == set # [poisonresist] >= 50 # [tier] == 100000", - // Rings - SoJ - "[type] == ring && [quality] == unique # [itemmaxmanapercent] == 25 # [tier] == 100000", - ], - expansionTiers: [ - // Weapon - Titan's Revenge - "[name] == ceremonialjavelin && [quality] == unique # [itemchargedskill] >= 0 # [tier] == tierscore(item, 100000)", - // Helmet - Harlequin's Crest - "[name] == shako && [quality] == unique && [flag] != ethereal # [itemallskills] == 2 # [tier] == tierscore(item, 100000)", - // Boots - Sandstorm Treks - "[name] == scarabshellboots && [quality] == unique # [strength]+[vitality] >= 20 # [tier] == tierscore(item, 100000)", - // Belt - Thundergod's Vigor - "[name] == warbelt && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 160 # [tier] == tierscore(item, 110000)", - // Armor - CoH - "[type] == armor && [flag] == runeword && [flag] != ethereal # [fireresist] == 65 && [hpregen] == 7 # [tier] == 110000", - // Shield - Spirit - "[type] == shield # [fcr] >= 25 && [maxmana] >= 89 # [tier] == tierscore(item, 110000)", - // Amulet - Highlords - "[type] == amulet && [quality] == unique # [lightresist] == 35 # [tier] == 110000", - // Final Rings - Perfect Raven Frost & Wisp - "[type] == ring && [quality] == unique # [dexterity] == 20 && [tohit] == 250 # [tier] == 110000", - "[type] == ring && [quality] == unique # [itemabsorblightpercent] == 20 # [tier] == 110000", - // Rings - Raven Frost & Wisp - "[type] == ring && [quality] == unique # [dexterity] >= 15 && [tohit] >= 150 # [tier] == 100000", - "[type] == ring && [quality] == unique # [itemabsorblightpercent] >= 10 # [tier] == 100000", - // Switch - CTA - "[minimumsockets] >= 5 && [flag] == runeword # [plusskillbattleorders] >= 1 # [secondarytier] == 100000", - // Switch - Spirit - "[name] == monarch && [flag] == runeword # [fcr] >= 25 && [maxmana] >= 89 # [secondarytier] == 110000", - // Merc Armor - Fortitude - "[type] == armor && [flag] == runeword # [enhanceddefense] >= 200 && [enhanceddamage] >= 300 # [merctier] == 100000", - // Merc Final Helmet - Eth Andy's - "[name] == demonhead && [quality] == unique && [flag] == ethereal # [strength] >= 25 && [enhanceddefense] >= 100 # [merctier] == 50000 + mercscore(item)", - // Merc Helmet - Andy's - "[name] == demonhead && [quality] == unique && [flag] != ethereal # [strength] >= 25 && [enhanceddefense] >= 100 # [merctier] == 40000 + mercscore(item)", - ], - stats: undefined, - skills: undefined, - autoEquipTiers: undefined, - charms: { - ResLife: { - max: 5, - have: [], - classid: sdk.items.SmallCharm, - stats: function (check) { - return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MaxHp) === 20); - } - }, +(function (module) { + module.exports = (function () { + const build = { + caster: false, + skillstab: sdk.skills.tabs.JavelinandSpear, + wantedskills: [sdk.skills.ChargedStrike, sdk.skills.LightningStrike], + usefulskills: [sdk.skills.CriticalStrike, sdk.skills.Penetrate, sdk.skills.Valkyrie, sdk.skills.Pierce], + precastSkills: [sdk.skills.Valkyrie], + usefulStats: [sdk.stats.PierceLtng, sdk.stats.PassiveLightningMastery, sdk.stats.PassiveLightningPierce], + wantedMerc: MercData[sdk.skills.HolyFreeze], + stats: [], + skills: [], - ResMf: { - max: 2, - have: [], - classid: sdk.items.SmallCharm, - stats: function (check) { - return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MagicBonus) === 7); - } - }, + charms: { + ResLife: { + max: 5, + have: [], + classid: sdk.items.SmallCharm, + stats: function (check) { + return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MaxHp) === 20); + } + }, - ResFHR: { - max: 1, - have: [], - classid: sdk.items.SmallCharm, - stats: function (check) { - return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.FHR) === 5); - } - }, + ResMf: { + max: 2, + have: [], + classid: sdk.items.SmallCharm, + stats: function (check) { + return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MagicBonus) === 7); + } + }, - Skiller: { - max: 2, - have: [], - classid: sdk.items.GrandCharm, - stats: function (check) { - return (!check.unique && check.classid === this.classid && check.getStat(sdk.stats.AddSkillTab, sdk.skills.tabs.JavelinandSpear) === 1 - && check.getStat(sdk.stats.MaxHp) >= 40); - } - }, - }, - - AutoBuildTemplate: { - 1: { - Update: function () { - Config.AttackSkill = [-1, sdk.skills.ChargedStrike, -1, sdk.skills.LightningStrike, -1, -1, -1]; - Config.BeltColumn = ["hp", "hp", "mp", "rv"]; - Config.HPBuffer = me.expansion ? 2 : 4; - Config.MPBuffer = me.expansion ? 4 : 6; - } - }, - }, + ResFHR: { + max: 1, + have: [], + classid: sdk.items.SmallCharm, + stats: function (check) { + return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.FHR) === 5); + } + }, - respec: function () { - if (me.classic) { - return me.charlvl >= 75 && me.diablo; - } else { - return (Attack.checkInfinity() || (myData.merc.gear.includes(sdk.locale.items.Infinity) && !Misc.poll(() => me.getMerc(), 200, 50))); - } - }, + Skiller: { + max: 2, + have: [], + classid: sdk.items.GrandCharm, + stats: function (check) { + return (!check.unique && check.classid === this.classid && check.getStat(sdk.stats.AddSkillTab, sdk.skills.tabs.JavelinandSpear) === 1 + && check.getStat(sdk.stats.MaxHp) >= 40); + } + }, + }, + + AutoBuildTemplate: { + 1: { + Update: function () { + Config.AttackSkill = [-1, sdk.skills.ChargedStrike, -1, sdk.skills.LightningStrike, -1, -1, -1]; + Config.BeltColumn = ["hp", "hp", "mp", "rv"]; + Config.HPBuffer = me.expansion ? 2 : 4; + Config.MPBuffer = me.expansion ? 4 : 6; + } + }, + }, - active: function () { - return this.respec() && (me.expansion ? me.getSkill(sdk.skills.PlagueJavelin, sdk.skills.subindex.HardPoints) > 1 && me.getSkill(sdk.skills.PlagueJavelin, sdk.skills.subindex.HardPoints) < 5 : me.getSkill(sdk.skills.PlagueJavelin, sdk.skills.subindex.HardPoints) === 20); - }, -}; + respec: function () { + if (me.classic) { + return me.charlvl >= 75 && me.diablo; + } else { + return (Attack.checkInfinity() || (myData.merc.gear.includes(sdk.locale.items.Infinity) && !Misc.poll(() => me.getMerc(), 200, 50))); + } + }, -// Has to be set after its loaded -finalBuild.stats = me.classic ? finalBuild.classicStats : finalBuild.expansionStats; -finalBuild.skills = me.classic ? finalBuild.classicSkills : finalBuild.expansionSkills; -finalBuild.autoEquipTiers = me.classic ? finalBuild.classicTiers : finalBuild.expansionTiers; -me.classic && finalBuild.usefulStats.push(sdk.stats.PassivePoisonMastery, sdk.stats.PassivePoisonPierce, sdk.stats.PiercePois); + active: function () { + return this.respec() && (me.expansion ? me.getSkill(sdk.skills.PlagueJavelin, sdk.skills.subindex.HardPoints) > 1 && me.getSkill(sdk.skills.PlagueJavelin, sdk.skills.subindex.HardPoints) < 5 : me.getSkill(sdk.skills.PlagueJavelin, sdk.skills.subindex.HardPoints) === 20); + }, + }; + + build.stats = me.classic + ? [ + ["dexterity", 65], ["strength", 75], ["vitality", "all"] + ] + : [ + ["strength", 34], ["vitality", 30], ["dexterity", 47], + ["vitality", 45], ["strength", 47], ["dexterity", 65], + ["vitality", 65], ["strength", 53], ["dexterity", 118], + ["vitality", 100], ["strength", 118], ["dexterity", 151], + ["strength", 156], ["vitality", "all"], + ]; + + build.skills = me.classic + ? [ + [sdk.skills.Valkyrie, 1], + [sdk.skills.LightningFury, 1], + [sdk.skills.LightningStrike, 1], + [sdk.skills.Pierce, 1], + [sdk.skills.PlagueJavelin, 20], + [sdk.skills.ChargedStrike, 10], + [sdk.skills.LightningStrike, 10], + [sdk.skills.Decoy, 5], + [sdk.skills.LightningStrike, 17], + [sdk.skills.ChargedStrike, 15], + [sdk.skills.LightningStrike, 20, false], + [sdk.skills.ChargedStrike, 20, false], + [sdk.skills.PoisonJavelin, 20, false], + [sdk.skills.Valkyrie, 12, false], + [sdk.skills.LightningFury, 20, false], + ] + : [ + [sdk.skills.Valkyrie, 1], + [sdk.skills.Pierce, 1], + [sdk.skills.LightningStrike, 20], + [sdk.skills.ChargedStrike, 20], + [sdk.skills.LightningFury, 20], + [sdk.skills.Decoy, 5, false], + [sdk.skills.Valkyrie, 17, false], + [sdk.skills.PowerStrike, 20, false], + [sdk.skills.Pierce, 5, false], + ]; + + me.classic && build.usefulStats.push(sdk.stats.PassivePoisonMastery, sdk.stats.PassivePoisonPierce, sdk.stats.PiercePois); + + let finalGear = me.classic + ? [ + // Helm - Tarnhelm + "[name] == skullcap && [quality] == unique # [itemallskills] == 1 && [itemmagicbonus] >= 25 # [tier] == tierscore(item, 100000)", + // Armor - Twitchthroe + "[name] == studdedleather && [quality] == unique # [ias] == 20 && [fhr] == 20 # [tier] == 100000", + // Belt - Death's Guard Sash + "[name] == sash && [quality] == set # [itemcannotbefrozen] == 1 # [tier] == 100000", + // Gloves - Death's Hand Leather Gloves + "[name] == leathergloves && [quality] == set # [poisonresist] >= 50 # [tier] == 100000", + // Rings - SoJ + "[type] == ring && [quality] == unique # [itemmaxmanapercent] == 25 # [tier] == 100000", + ] : [ + // Weapon - Titan's Revenge + "[name] == ceremonialjavelin && [quality] == unique # [itemchargedskill] >= 0 # [tier] == tierscore(item, 100000)", + // Helmet - Harlequin's Crest + "[name] == shako && [quality] == unique && [flag] != ethereal # [itemallskills] == 2 # [tier] == tierscore(item, 100000)", + // Boots - Sandstorm Treks + "[name] == scarabshellboots && [quality] == unique # [strength]+[vitality] >= 20 # [tier] == tierscore(item, 100000)", + // Belt - Thundergod's Vigor + "[name] == warbelt && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 160 # [tier] == tierscore(item, 110000)", + // Armor - CoH + "[type] == armor && [flag] == runeword && [flag] != ethereal # [fireresist] == 65 && [hpregen] == 7 # [tier] == 110000", + // Shield - Spirit + "[type] == shield # [fcr] >= 25 && [maxmana] >= 89 # [tier] == tierscore(item, 110000)", + // Amulet - Highlords + "[type] == amulet && [quality] == unique # [lightresist] == 35 # [tier] == 110000", + // Final Rings - Perfect Raven Frost & Wisp + "[type] == ring && [quality] == unique # [dexterity] == 20 && [tohit] == 250 # [tier] == 110000", + "[type] == ring && [quality] == unique # [itemabsorblightpercent] == 20 # [tier] == 110000", + // Rings - Raven Frost & Wisp + "[type] == ring && [quality] == unique # [dexterity] >= 15 && [tohit] >= 150 # [tier] == 100000", + "[type] == ring && [quality] == unique # [itemabsorblightpercent] >= 10 # [tier] == 100000", + // Switch - CTA + "[minimumsockets] >= 5 && [flag] == runeword # [plusskillbattleorders] >= 1 # [secondarytier] == 100000", + // Switch - Spirit + "[name] == monarch && [flag] == runeword # [fcr] >= 25 && [maxmana] >= 89 # [secondarytier] == 110000", + // Merc Armor - Fortitude + "[type] == armor && [flag] == runeword # [enhanceddefense] >= 200 && [enhanceddamage] >= 300 # [merctier] == 100000", + // Merc Final Helmet - Eth Andy's + "[name] == demonhead && [quality] == unique && [flag] == ethereal # [strength] >= 25 && [enhanceddefense] >= 100 # [merctier] == 50000 + mercscore(item)", + // Merc Helmet - Andy's + "[name] == demonhead && [quality] == unique && [flag] != ethereal # [strength] >= 25 && [enhanceddefense] >= 100 # [merctier] == 40000 + mercscore(item)", + ]; + + NTIP.buildList(finalGear); + NTIP.buildFinalGear(finalGear); + + return build; + })(); +})(module); diff --git a/libs/SoloPlay/BuildFiles/amazon/amazon.LevelingBuild.js b/libs/SoloPlay/BuildFiles/amazon/amazon.LevelingBuild.js index dd473630..5f316198 100644 --- a/libs/SoloPlay/BuildFiles/amazon/amazon.LevelingBuild.js +++ b/libs/SoloPlay/BuildFiles/amazon/amazon.LevelingBuild.js @@ -5,75 +5,82 @@ * */ -let build = { - AutoBuildTemplate: {}, - caster: false, - skillstab: sdk.skills.tabs.JavelinandSpear, - wantedskills: [sdk.skills.ChargedStrike, sdk.skills.LightningStrike], - usefulskills: [sdk.skills.CriticalStrike, sdk.skills.Penetrate, sdk.skills.Valkyrie, sdk.skills.Pierce], - usefulStats: [sdk.stats.PierceLtng, sdk.stats.PassiveLightningMastery, sdk.stats.PassiveLightningPierce], - mercDiff: sdk.difficulty.Nightmare, - mercAct: 2, - mercAuraWanted: "Holy Freeze", - classicStats: [ - ["dexterity", 65], ["strength", 75], ["vitality", "all"] - ], - expansionStats: [ - ["strength", 34], ["vitality", 30], ["dexterity", 47], - ["vitality", 45], ["strength", 47], ["dexterity", 65], - ["vitality", 65], ["strength", 53], ["dexterity", 118], - ["vitality", 100], ["strength", 118], ["dexterity", 151], - ["strength", 156], ["vitality", "all"], - ], - classicSkills: [ - // Points at respec 71 - [sdk.skills.Valkyrie, 1], // points left 64 - [sdk.skills.Pierce, 1], // points left 61 - [sdk.skills.LightningFury, 1], // points left 57 - [sdk.skills.LightningStrike, 17], // points left 37 - [sdk.skills.PlagueJavelin, 20], // points left 18 - [sdk.skills.ChargedStrike, 15], // points left 4 - [sdk.skills.Decoy, 5], // points left 0 - [sdk.skills.LightningStrike, 20, false], - [sdk.skills.ChargedStrike, 20, false], - [sdk.skills.PoisonJavelin, 20, false], // synergy for PlagueJavelin - [sdk.skills.Valkyrie, 12, false], - [sdk.skills.LightningFury, 20, false], - ], - expansionSkills: [ - // Points at respec 71 - [sdk.skills.Valkyrie, 1], // points left 64 - [sdk.skills.Pierce, 1], // points left 61 - [sdk.skills.LightningFury, 1], // points left 57 - [sdk.skills.LightningStrike, 15], // points left 39 - [sdk.skills.ChargedStrike, 17], // points left 23 - [sdk.skills.PlagueJavelin, 20], // points left 4 - [sdk.skills.Decoy, 5], // points left 0 - [sdk.skills.LightningStrike, 20, false], - [sdk.skills.ChargedStrike, 20, false], - [sdk.skills.LightningFury, 20, false], - [sdk.skills.Valkyrie, 17, false], - [sdk.skills.PowerStrike, 20, false], - ], - stats: undefined, - skills: undefined, - active: function () { - return (me.charlvl > CharInfo.respecOne && me.charlvl > CharInfo.respecTwo && me.getSkill(sdk.skills.PlagueJavelin, sdk.skills.subindex.HardPoints) === 20 && !Check.finalBuild().active()); - }, -}; +(function (module) { + module.exports = (function () { + const build = { + caster: false, + skillstab: sdk.skills.tabs.JavelinandSpear, + wantedskills: [sdk.skills.ChargedStrike, sdk.skills.LightningStrike], + usefulskills: [sdk.skills.CriticalStrike, sdk.skills.Penetrate, sdk.skills.Valkyrie, sdk.skills.Pierce], + usefulStats: [sdk.stats.PierceLtng, sdk.stats.PassiveLightningMastery, sdk.stats.PassiveLightningPierce], + wantedMerc: MercData[sdk.skills.HolyFreeze], + stats: [], + skills: [], -// Has to be set after its loaded -build.stats = me.classic ? build.classicStats : build.expansionStats; -build.skills = me.classic ? build.classicSkills : build.expansionSkills; -me.classic && build.usefulStats.push(sdk.stats.PassivePoisonMastery, sdk.stats.PassivePoisonPierce, sdk.stats.PiercePois); + active: function () { + return (me.charlvl > CharInfo.respecOne && me.charlvl > CharInfo.respecTwo && me.getSkill(sdk.skills.PlagueJavelin, sdk.skills.subindex.HardPoints) === 20 && !Check.finalBuild().active()); + }, -build.AutoBuildTemplate[1] = buildAutoBuildTempObj(() => { - let mainSkill = Skill.canUse(sdk.skills.LightningStrike) ? sdk.skills.LightningStrike : sdk.skills.ChargedStrike; - Config.AttackSkill = [-1, sdk.skills.ChargedStrike, 0, mainSkill, 0, -1, -1]; - Config.TownHP = me.hardcore ? 0 : 35; - Config.BeltColumn = ["hp", "hp", "mp", "mp"]; - Config.HPBuffer = me.expansion ? 2 : 4; - Config.MPBuffer = me.expansion && me.charlvl < 80 ? 6 : me.classic ? 5 : 2; - SetUp.belt(); -}); + AutoBuildTemplate: { + 1: { + Update: function () { + let mainSkill = Skill.canUse(sdk.skills.LightningStrike) ? sdk.skills.LightningStrike : sdk.skills.ChargedStrike; + Config.AttackSkill = [-1, sdk.skills.ChargedStrike, 0, mainSkill, 0, -1, -1]; + Config.TownHP = me.hardcore ? 0 : 35; + Config.BeltColumn = ["hp", "hp", "mp", "mp"]; + Config.HPBuffer = me.expansion ? 2 : 4; + Config.MPBuffer = me.expansion && me.charlvl < 80 ? 6 : me.classic ? 5 : 2; + SetUp.belt(); + } + } + }, + }; + + // Has to be set after its loaded + me.classic && build.usefulStats.push(sdk.stats.PassivePoisonMastery, sdk.stats.PassivePoisonPierce, sdk.stats.PiercePois); + build.stats = me.classic + ? [ + ["dexterity", 65], ["strength", 75], ["vitality", "all"] + ] : [ + ["strength", 34], ["vitality", 30], ["dexterity", 47], + ["vitality", 45], ["strength", 47], ["dexterity", 65], + ["vitality", 65], ["strength", 53], ["dexterity", 118], + ["vitality", 100], ["strength", 118], ["dexterity", 151], + ["strength", 156], ["vitality", "all"], + ]; + + build.skills = me.classic + ? [ + // Points at respec 71 + [sdk.skills.Valkyrie, 1], // points left 64 + [sdk.skills.Pierce, 1], // points left 61 + [sdk.skills.LightningFury, 1], // points left 57 + [sdk.skills.LightningStrike, 17], // points left 37 + [sdk.skills.PlagueJavelin, 20], // points left 18 + [sdk.skills.ChargedStrike, 15], // points left 4 + [sdk.skills.Decoy, 5], // points left 0 + [sdk.skills.LightningStrike, 20, false], + [sdk.skills.ChargedStrike, 20, false], + [sdk.skills.PoisonJavelin, 20, false], // synergy for PlagueJavelin + [sdk.skills.Valkyrie, 12, false], + [sdk.skills.LightningFury, 20, false], + ] : [ + // Points at respec 71 + [sdk.skills.Valkyrie, 1], // points left 64 + [sdk.skills.Pierce, 1], // points left 61 + [sdk.skills.LightningFury, 1], // points left 57 + [sdk.skills.LightningStrike, 15], // points left 39 + [sdk.skills.ChargedStrike, 17], // points left 23 + [sdk.skills.PlagueJavelin, 20], // points left 4 + [sdk.skills.Decoy, 5], // points left 0 + [sdk.skills.LightningStrike, 20, false], + [sdk.skills.ChargedStrike, 20, false], + [sdk.skills.LightningFury, 20, false], + [sdk.skills.Valkyrie, 17, false], + [sdk.skills.PowerStrike, 20, false], + ]; + + return build; + })(); +})(module); diff --git a/libs/SoloPlay/BuildFiles/amazon/amazon.StartBuild.js b/libs/SoloPlay/BuildFiles/amazon/amazon.StartBuild.js index ae134ac1..5ed45a2c 100644 --- a/libs/SoloPlay/BuildFiles/amazon/amazon.StartBuild.js +++ b/libs/SoloPlay/BuildFiles/amazon/amazon.StartBuild.js @@ -5,76 +5,84 @@ * */ -let build = { - AutoBuildTemplate: {}, - caster: false, - skillstab: sdk.skills.tabs.JavelinandSpear, - wantedskills: [sdk.skills.ChargedStrike, sdk.skills.LightningStrike], - usefulskills: [sdk.skills.CriticalStrike, sdk.skills.Penetrate, sdk.skills.Valkyrie, sdk.skills.Pierce], - usefulStats: [sdk.stats.PierceLtng, sdk.stats.PassiveLightningMastery, sdk.stats.PassiveLightningPierce], - mercDiff: sdk.difficulty.Nightmare, - mercAct: 2, - mercAuraWanted: "Holy Freeze", - stats: [ - ["strength", 34], ["vitality", 30], ["dexterity", 47], - ["vitality", 45], ["strength", 47], ["dexterity", 65], - ["vitality", "all"], - ], - skills: [ - [sdk.skills.Jab, 1, false], // charlvl 2 - [sdk.skills.InnerSight, 1, false], // charlvl 3 - [sdk.skills.CriticalStrike, 2, false], // charlvl 4 - [sdk.skills.PowerStrike, 1, false], // charlvl 6 - [sdk.skills.Dodge, 1, false], // charlvl 6 - [sdk.skills.PowerStrike, 5, false], // charlvl 10 - [sdk.skills.SlowMissiles, 1, false], // charlvl 12 - [sdk.skills.Avoid, 1, false], // charlvl 12 - [sdk.skills.PowerStrike, 8, false], // charlvl 15 - [sdk.skills.ChargedStrike, 1, false], // charlvl 18 - [sdk.skills.Penetrate, 1, false], // charlvl 18 - [sdk.skills.ChargedStrike, 5, false], // charlvl 22 - [sdk.skills.Evade, 1, false], // charLvl 24 - [sdk.skills.Decoy, 1, false], // charlvl 24 - [sdk.skills.ChargedStrike, 20, false], // respec at 30 - ], - active: function () { - return me.charlvl < CharInfo.respecOne && !me.checkSkill(sdk.skills.LightningStrike, sdk.skills.subindex.HardPoints); - }, -}; -build.AutoBuildTemplate[1] = buildAutoBuildTempObj(() => { - Config.TownHP = me.hardcore ? 0 : 35; - Config.BeltColumn = ["hp", "hp", "hp", "hp"]; - Config.HPBuffer = 8; - Config.MPBuffer = 4; - Config.AttackSkill = [-1, 0, 0, 0, 0, 0, 0]; - Config.LowManaSkill = [0, 0]; - SetUp.belt(); -}); -build.AutoBuildTemplate[2] = buildAutoBuildTempObj(() => { - Config.TownHP = me.hardcore ? 0 : 35; - Config.HPBuffer = 8; - Config.MPBuffer = 4; - Config.AttackSkill = [-1, sdk.skills.Jab, 0, sdk.skills.Jab, 0, 0, 0]; -}); -build.AutoBuildTemplate[6] = buildAutoBuildTempObj(() => { - Config.TownHP = me.hardcore ? 0 : 35; - Config.BeltColumn = ["hp", "hp", "mp", "mp"]; - Config.HPBuffer = me.expansion ? 4 : 6; - Config.MPBuffer = 6; - Config.AttackSkill = [-1, sdk.skills.PowerStrike, 0, sdk.skills.PowerStrike, 0, -1, -1]; - SetUp.belt(); -}); -build.AutoBuildTemplate[7] = buildAutoBuildTempObj(() => { - Config.HPBuffer = me.expansion ? 4 : 6; - Config.MPBuffer = 6; - Config.AttackSkill = [-1, sdk.skills.PowerStrike, 0, sdk.skills.PowerStrike, 0, -1, -1]; -}); -build.AutoBuildTemplate[18] = buildAutoBuildTempObj(() => { - Config.AttackSkill = [-1, sdk.skills.ChargedStrike, 0, sdk.skills.ChargedStrike, 0, -1, -1]; -}); -build.AutoBuildTemplate[50] = buildAutoBuildTempObj(() => { - let mainSkill = Skill.canUse(sdk.skills.LightningStrike) ? sdk.skills.LightningStrike : sdk.skills.ChargedStrike; - Config.AttackSkill = [-1, sdk.skills.ChargedStrike, 0, mainSkill, 0, -1, -1]; -}); +(function (module, require) { + module.exports = (function () { + const build = { + AutoBuildTemplate: {}, + caster: false, + skillstab: sdk.skills.tabs.JavelinandSpear, + wantedskills: [sdk.skills.ChargedStrike, sdk.skills.LightningStrike], + usefulskills: [sdk.skills.CriticalStrike, sdk.skills.Penetrate, sdk.skills.Valkyrie, sdk.skills.Pierce], + usefulStats: [sdk.stats.PierceLtng, sdk.stats.PassiveLightningMastery, sdk.stats.PassiveLightningPierce], + wantedMerc: MercData[sdk.skills.HolyFreeze], + stats: [ + ["strength", 34], ["vitality", 30], ["dexterity", 47], + ["vitality", 45], ["strength", 47], ["dexterity", 65], + ["vitality", "all"], + ], + skills: [ + [sdk.skills.Jab, 1, false], // charlvl 2 + [sdk.skills.InnerSight, 1, false], // charlvl 3 + [sdk.skills.CriticalStrike, 2, false], // charlvl 4 + [sdk.skills.PowerStrike, 1, false], // charlvl 6 + [sdk.skills.Dodge, 1, false], // charlvl 6 + [sdk.skills.PowerStrike, 5, false], // charlvl 10 + [sdk.skills.SlowMissiles, 1, false], // charlvl 12 + [sdk.skills.Avoid, 1, false], // charlvl 12 + [sdk.skills.PowerStrike, 8, false], // charlvl 15 + [sdk.skills.ChargedStrike, 1, false], // charlvl 18 + [sdk.skills.Penetrate, 1, false], // charlvl 18 + [sdk.skills.ChargedStrike, 5, false], // charlvl 22 + [sdk.skills.Evade, 1, false], // charLvl 24 + [sdk.skills.Decoy, 1, false], // charlvl 24 + [sdk.skills.ChargedStrike, 20, false], // respec at 30 + ], + + active: function () { + return me.charlvl < CharInfo.respecOne && !me.checkSkill(sdk.skills.LightningStrike, sdk.skills.subindex.HardPoints); + }, + }; + + const { buildAutoBuildTempObj } = require("../../Utils/General"); + + build.AutoBuildTemplate[1] = buildAutoBuildTempObj(() => { + Config.TownHP = me.hardcore ? 0 : 35; + Config.BeltColumn = ["hp", "hp", "hp", "hp"]; + Config.HPBuffer = 8; + Config.MPBuffer = 4; + Config.AttackSkill = [-1, 0, 0, 0, 0, 0, 0]; + Config.LowManaSkill = [0, 0]; + SetUp.belt(); + }); + build.AutoBuildTemplate[2] = buildAutoBuildTempObj(() => { + Config.TownHP = me.hardcore ? 0 : 35; + Config.HPBuffer = 8; + Config.MPBuffer = 4; + Config.AttackSkill = [-1, sdk.skills.Jab, 0, sdk.skills.Jab, 0, 0, 0]; + }); + build.AutoBuildTemplate[6] = buildAutoBuildTempObj(() => { + Config.TownHP = me.hardcore ? 0 : 35; + Config.BeltColumn = ["hp", "hp", "mp", "mp"]; + Config.HPBuffer = me.expansion ? 4 : 6; + Config.MPBuffer = 6; + Config.AttackSkill = [-1, sdk.skills.PowerStrike, 0, sdk.skills.PowerStrike, 0, -1, -1]; + SetUp.belt(); + }); + build.AutoBuildTemplate[7] = buildAutoBuildTempObj(() => { + Config.HPBuffer = me.expansion ? 4 : 6; + Config.MPBuffer = 6; + Config.AttackSkill = [-1, sdk.skills.PowerStrike, 0, sdk.skills.PowerStrike, 0, -1, -1]; + }); + build.AutoBuildTemplate[18] = buildAutoBuildTempObj(() => { + Config.AttackSkill = [-1, sdk.skills.ChargedStrike, 0, sdk.skills.ChargedStrike, 0, -1, -1]; + }); + build.AutoBuildTemplate[50] = buildAutoBuildTempObj(() => { + let mainSkill = Skill.canUse(sdk.skills.LightningStrike) ? sdk.skills.LightningStrike : sdk.skills.ChargedStrike; + Config.AttackSkill = [-1, sdk.skills.ChargedStrike, 0, mainSkill, 0, -1, -1]; + }); + + return build; + })(); +})(module, require); diff --git a/libs/SoloPlay/BuildFiles/amazon/amazon.SteppingBuild.js b/libs/SoloPlay/BuildFiles/amazon/amazon.SteppingBuild.js index 8156d25b..6a2a6fbb 100644 --- a/libs/SoloPlay/BuildFiles/amazon/amazon.SteppingBuild.js +++ b/libs/SoloPlay/BuildFiles/amazon/amazon.SteppingBuild.js @@ -5,55 +5,61 @@ * */ -let build = { - AutoBuildTemplate: {}, - caster: false, - skillstab: sdk.skills.tabs.JavelinandSpear, - wantedskills: [sdk.skills.ChargedStrike, sdk.skills.LightningStrike], - usefulskills: [sdk.skills.CriticalStrike, sdk.skills.Penetrate, sdk.skills.Valkyrie, sdk.skills.Pierce], - usefulStats: [sdk.stats.PierceLtng, sdk.stats.PassiveLightningMastery, sdk.stats.PassiveLightningPierce], - mercDiff: sdk.difficulty.Nightmare, - mercAct: 2, - mercAuraWanted: "Holy Freeze", - classicStats: [ - ["dexterity", 65], ["strength", 75], ["vitality", "all"] - ], - expansionStats: [ - ["strength", 34], ["vitality", 30], ["dexterity", 47], - ["vitality", 45], ["strength", 47], ["dexterity", 65], - ["vitality", 65], ["strength", 53], ["dexterity", 118], - ["vitality", 100], ["strength", 118], ["dexterity", 151], - ["strength", 156], ["vitality", "all"], - ], - skills: [ - // points at respec 33 - [sdk.skills.LightningStrike, 1], // points left 27 - [sdk.skills.Valkyrie, 1], // points left 20 - [sdk.skills.Penetrate, 1], // points left 18 - [sdk.skills.ChargedStrike, 13], // points left 6 - [sdk.skills.PowerStrike, 5], // points left 2 - [sdk.skills.LightningFury, 1], // points left 0 - [sdk.skills.LightningStrike, 20, false], // charlvl ? - [sdk.skills.ChargedStrike, 20, false], // charlvl 52 - [sdk.skills.LightningFury, 20, false], // respec at 64 - ], - stats: undefined, - active: function () { - return me.charlvl > CharInfo.respecOne && me.charlvl < CharInfo.respecTwo && me.checkSkill(sdk.skills.LightningStrike, sdk.skills.subindex.HardPoints) && me.getSkill(sdk.skills.PlagueJavelin, sdk.skills.subindex.HardPoints) <= 5; - }, -}; +(function (module) { + module.exports = (function () { + const build = { + caster: false, + skillstab: sdk.skills.tabs.JavelinandSpear, + wantedskills: [sdk.skills.ChargedStrike, sdk.skills.LightningStrike], + usefulskills: [sdk.skills.CriticalStrike, sdk.skills.Penetrate, sdk.skills.Valkyrie, sdk.skills.Pierce], + usefulStats: [sdk.stats.PierceLtng, sdk.stats.PassiveLightningMastery, sdk.stats.PassiveLightningPierce], + wantedMerc: MercData[sdk.skills.HolyFreeze], + stats: [], + skills: [ + // points at respec 33 + [sdk.skills.LightningStrike, 1], // points left 27 + [sdk.skills.Valkyrie, 1], // points left 20 + [sdk.skills.Penetrate, 1], // points left 18 + [sdk.skills.ChargedStrike, 13], // points left 6 + [sdk.skills.PowerStrike, 5], // points left 2 + [sdk.skills.LightningFury, 1], // points left 0 + [sdk.skills.LightningStrike, 20, false], // charlvl ? + [sdk.skills.ChargedStrike, 20, false], // charlvl 52 + [sdk.skills.LightningFury, 20, false], // respec at 64 + ], -// Has to be set after its loaded -build.stats = me.classic ? build.classicStats : build.expansionStats; -me.classic && build.usefulStats.push(sdk.stats.PassivePoisonMastery, sdk.stats.PassivePoisonPierce, sdk.stats.PiercePois); - -build.AutoBuildTemplate[1] = buildAutoBuildTempObj(() => { - let mainSkill = Skill.canUse(sdk.skills.LightningStrike) ? sdk.skills.LightningStrike : sdk.skills.ChargedStrike; - Config.AttackSkill = [-1, sdk.skills.ChargedStrike, 0, mainSkill, 0, -1, -1]; - Config.BeltColumn = ["hp", "hp", "mp", "mp"]; - Config.HPBuffer = me.expansion ? 2 : 4; - Config.MPBuffer = 6; - SetUp.belt(); -}); + AutoBuildTemplate: { + 1: { + Update: function () { + let mainSkill = Skill.canUse(sdk.skills.LightningStrike) ? sdk.skills.LightningStrike : sdk.skills.ChargedStrike; + Config.AttackSkill = [-1, sdk.skills.ChargedStrike, 0, mainSkill, 0, -1, -1]; + Config.BeltColumn = ["hp", "hp", "mp", "mp"]; + Config.HPBuffer = me.expansion ? 2 : 4; + Config.MPBuffer = 6; + SetUp.belt(); + } + } + }, + active: function () { + return me.charlvl > CharInfo.respecOne && me.charlvl < CharInfo.respecTwo && me.checkSkill(sdk.skills.LightningStrike, sdk.skills.subindex.HardPoints) && me.getSkill(sdk.skills.PlagueJavelin, sdk.skills.subindex.HardPoints) <= 5; + }, + }; + + // Has to be set after its loaded + me.classic && build.usefulStats.push(sdk.stats.PassivePoisonMastery, sdk.stats.PassivePoisonPierce, sdk.stats.PiercePois); + build.stats = me.classic + ? [ + ["dexterity", 65], ["strength", 75], ["vitality", "all"] + ] : [ + ["strength", 34], ["vitality", 30], ["dexterity", 47], + ["vitality", 45], ["strength", 47], ["dexterity", 65], + ["vitality", 65], ["strength", 53], ["dexterity", 118], + ["vitality", 100], ["strength", 118], ["dexterity", 151], + ["strength", 156], ["vitality", "all"], + ]; + + return build; + })(); +})(module); diff --git a/libs/SoloPlay/BuildFiles/amazon/amazon.WfzonBuild.js b/libs/SoloPlay/BuildFiles/amazon/amazon.WfzonBuild.js index 500fb97e..de17d215 100644 --- a/libs/SoloPlay/BuildFiles/amazon/amazon.WfzonBuild.js +++ b/libs/SoloPlay/BuildFiles/amazon/amazon.WfzonBuild.js @@ -6,136 +6,145 @@ * */ -const finalBuild = { - caster: false, - skillstab: sdk.skills.tabs.BowandCrossbow, - wantedskills: [sdk.skills.Strafe], - usefulskills: [sdk.skills.Penetrate, sdk.skills.Valkyrie, sdk.skills.Pierce], - precastSkills: [sdk.skills.Valkyrie], - mercDiff: sdk.difficulty.Nightmare, - mercAct: 2, - mercAuraWanted: "Might", - stats: [ - ["strength", 134], ["dexterity", 167], ["vitality", 150], ["dexterity", "all"] - ], - skills: [ - [sdk.skills.Valkyrie, 1], - [sdk.skills.Strafe, 20], - [sdk.skills.Pierce, 13], - [sdk.skills.Penetrate, 20], - [sdk.skills.CriticalStrike, 13], - [sdk.skills.Valkyrie, 16], - [sdk.skills.Dodge, 9], - [sdk.skills.Avoid, 4], - [sdk.skills.Evade, 8], - ], - autoEquipTiers: [ // autoequip final gear - // Final Weapon - Windforce - "[name] == hydrabow && [quality] == unique # [manaleech] >= 6 # [tier] == tierscore(item, 100000)", - // Weapon - WitchWild String up'd - "[name] == diamondbow && [quality] == unique # [fireresist] == 40 # [tier] == tierscore(item, 50000)", - // Helmet - Vampz Gaze - "[name] == grimhelm && [quality] == unique && [flag] != ethereal # [manaleech] >= 6 && [lifeleech] >= 6 && [damageresist] >= 15 # [tier] == tierscore(item, 100000)", - // Boots - War Traveler - "[name] == battleboots && [quality] == unique && [flag] != ethereal # [itemmagicbonus] >= 30 # [tier] == 100000", - // Belt - Nosferatu's Coil - "[name] == vampirefangbelt && [quality] == unique && [flag] != ethereal # [lifeleech] >= 5 # [tier] == tierscore(item, 100000)", - // Armor - CoH - "[type] == armor && [flag] == runeword && [flag] != ethereal # [fireresist] == 65 && [hpregen] == 7 # [tier] == 100000", - // Gloves - Lava Gout - "[name] == battlegauntlets && [quality] == unique && [flag] != ethereal # [ias] == 20 && [enhanceddefense] >= 150 # [tier] == tierscore(item, 3000)", - // Final Amulet - Atma's Scarab - "[type] == amulet && [quality] == unique # [poisonresist] == 75 # [tier] == 110000", - // Amulet - Highlords - "[type] == amulet && [quality] == unique # [lightresist] == 35 # [tier] == 100000", - // Final Rings - Perfect Raven Frost & Perfect Bul-Kathos' Wedding Band - "[type] == ring && [quality] == unique # [dexterity] == 20 && [tohit] == 250 # [tier] == 110000", - "[name] == ring && [quality] == unique # [maxstamina] == 50 && [lifeleech] == 5 # [tier] == 110000", - // Rings - Raven Frost && Bul-Kathos' Wedding Band - "[type] == ring && [quality] == unique # [dexterity] >= 15 && [tohit] >= 150 # [tier] == 100000", - "[name] == ring && [quality] == unique # [maxstamina] == 50 && [lifeleech] >= 3 # [tier] == 100000", // Switch Final Weapon - CTA - "[minimumsockets] >= 5 && [flag] == runeword # [plusskillbattleorders] >= 1 # [secondarytier] == 100000", - // Switch Temporary Weapon - Life Tap charged wand - "[type] == wand && [quality] >= normal # [itemchargedskill] == 82 # [secondarytier] == 75000 + chargeditemscore(item, 82)", - // Switch Final Shield - Spirit - "[name] == monarch && [flag] == runeword # [fcr] >= 25 && [maxmana] >= 89 # [secondarytier] == 110000", - // Switch Temporary Shield - Any 1+ all skill - "[type] == shield # [itemallskills] >= 1 # [secondarytier] == tierscore(item, 100000)", - // Merc Armor - Fortitude - "[type] == armor && [flag] == runeword # [enhanceddefense] >= 200 && [enhanceddamage] >= 300 # [merctier] == 100000", - // Merc Helmet - Eth Andy's - "[name] == demonhead && [quality] == unique && [flag] == ethereal # [strength] >= 25 && [enhanceddefense] >= 100 # [merctier] == 40000 + mercscore(item)", - // Merc Helmet - Andy's - "[name] == demonhead && [quality] == unique && [flag] != ethereal # [strength] >= 25 && [enhanceddefense] >= 100 # [merctier] == 50000 + mercscore(item)", - ], - charms: { - ResLife: { - max: 3, - have: [], - classid: sdk.items.SmallCharm, - stats: function (check) { - return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MaxHp) === 20); - } - }, +(function (module) { + module.exports = (function () { + const build = { + caster: false, + skillstab: sdk.skills.tabs.BowandCrossbow, + wantedskills: [sdk.skills.Strafe], + usefulskills: [sdk.skills.Penetrate, sdk.skills.Valkyrie, sdk.skills.Pierce], + precastSkills: [sdk.skills.Valkyrie], + wantedMerc: MercData[sdk.skills.Might], + stats: [ + ["strength", 134], ["dexterity", 167], ["vitality", 150], ["dexterity", "all"] + ], + skills: [ + [sdk.skills.Valkyrie, 1], + [sdk.skills.Strafe, 20], + [sdk.skills.Pierce, 13], + [sdk.skills.Penetrate, 20], + [sdk.skills.CriticalStrike, 13], + [sdk.skills.Valkyrie, 16], + [sdk.skills.Dodge, 9], + [sdk.skills.Avoid, 4], + [sdk.skills.Evade, 8], + ], - ResMf: { - max: 2, - have: [], - classid: sdk.items.SmallCharm, - stats: function (check) { - return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MagicBonus) === 7); - } - }, + charms: { + ResLife: { + max: 3, + have: [], + classid: sdk.items.SmallCharm, + stats: function (check) { + return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MaxHp) === 20); + } + }, - ResFHR: { - max: 1, - have: [], - classid: sdk.items.SmallCharm, - stats: function (check) { - return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.FHR) === 5); - } - }, + ResMf: { + max: 2, + have: [], + classid: sdk.items.SmallCharm, + stats: function (check) { + return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MagicBonus) === 7); + } + }, - SkillerCrossbow: { - max: 1, - have: [], - classid: sdk.items.GrandCharm, - stats: function (check) { - return (!check.unique && check.classid === this.classid && check.getStat(sdk.stats.AddSkillTab, sdk.skills.tabs.BowandCrossbow) === 1 - && check.getStat(sdk.stats.MaxHp) >= 40); - } - }, + ResFHR: { + max: 1, + have: [], + classid: sdk.items.SmallCharm, + stats: function (check) { + return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.FHR) === 5); + } + }, - SkillerPassive: { - max: 1, - have: [], - classid: sdk.items.GrandCharm, - stats: function (check) { - return (!check.unique && check.classid === this.classid && check.getStat(sdk.stats.AddSkillTab, sdk.skills.tabs.PassiveandMagic) === 1 - && check.getStat(sdk.stats.MaxHp) >= 40); - } - }, - }, + SkillerCrossbow: { + max: 1, + have: [], + classid: sdk.items.GrandCharm, + stats: function (check) { + return (!check.unique && check.classid === this.classid && check.getStat(sdk.stats.AddSkillTab, sdk.skills.tabs.BowandCrossbow) === 1 + && check.getStat(sdk.stats.MaxHp) >= 40); + } + }, - AutoBuildTemplate: { - 1: { - Update: function () { - Config.AttackSkill = [-1, sdk.skills.Strafe, -1, sdk.skills.Strafe, -1, sdk.skills.MagicArrow, -1]; - Config.LowManaSkill = [0, -1]; - } - }, - }, + SkillerPassive: { + max: 1, + have: [], + classid: sdk.items.GrandCharm, + stats: function (check) { + return (!check.unique && check.classid === this.classid && check.getStat(sdk.stats.AddSkillTab, sdk.skills.tabs.PassiveandMagic) === 1 + && check.getStat(sdk.stats.MaxHp) >= 40); + } + }, + }, - respec: function () { - if (me.classic) { - return false; - } else { - return (Check.haveItem("hydrabow", "unique", "Windforce") || Check.haveItem("diamondbow", "unique", "Witchwild String")); - } - }, + AutoBuildTemplate: { + 1: { + Update: function () { + Config.AttackSkill = [-1, sdk.skills.Strafe, -1, sdk.skills.Strafe, -1, sdk.skills.MagicArrow, -1]; + Config.LowManaSkill = [0, -1]; + } + }, + }, - active: function () { - return this.respec() && me.checkSkill(sdk.skills.Strafe, sdk.skills.subindex.HardPoints); - }, -}; + respec: function () { + if (me.classic) { + return false; + } else { + return (Check.haveItem("hydrabow", "unique", "Windforce") || Check.haveItem("diamondbow", "unique", "Witchwild String")); + } + }, + + active: function () { + return this.respec() && me.checkSkill(sdk.skills.Strafe, sdk.skills.subindex.HardPoints); + }, + }; + + let finalGear = [ // autoequip final gear + // Final Weapon - Windforce + "[name] == hydrabow && [quality] == unique # [manaleech] >= 6 # [tier] == tierscore(item, 100000)", + // Weapon - WitchWild String up'd + "[name] == diamondbow && [quality] == unique # [fireresist] == 40 # [tier] == tierscore(item, 50000)", + // Helmet - Vampz Gaze + "[name] == grimhelm && [quality] == unique && [flag] != ethereal # [manaleech] >= 6 && [lifeleech] >= 6 && [damageresist] >= 15 # [tier] == tierscore(item, 100000)", + // Boots - War Traveler + "[name] == battleboots && [quality] == unique && [flag] != ethereal # [itemmagicbonus] >= 30 # [tier] == 100000", + // Belt - Nosferatu's Coil + "[name] == vampirefangbelt && [quality] == unique && [flag] != ethereal # [lifeleech] >= 5 # [tier] == tierscore(item, 100000)", + // Armor - CoH + "[type] == armor && [flag] == runeword && [flag] != ethereal # [fireresist] == 65 && [hpregen] == 7 # [tier] == 100000", + // Gloves - Lava Gout + "[name] == battlegauntlets && [quality] == unique && [flag] != ethereal # [ias] == 20 && [enhanceddefense] >= 150 # [tier] == tierscore(item, 3000)", + // Final Amulet - Atma's Scarab + "[type] == amulet && [quality] == unique # [poisonresist] == 75 # [tier] == 110000", + // Amulet - Highlords + "[type] == amulet && [quality] == unique # [lightresist] == 35 # [tier] == 100000", + // Final Rings - Perfect Raven Frost & Perfect Bul-Kathos' Wedding Band + "[type] == ring && [quality] == unique # [dexterity] == 20 && [tohit] == 250 # [tier] == 110000", + "[name] == ring && [quality] == unique # [maxstamina] == 50 && [lifeleech] == 5 # [tier] == 110000", + // Rings - Raven Frost && Bul-Kathos' Wedding Band + "[type] == ring && [quality] == unique # [dexterity] >= 15 && [tohit] >= 150 # [tier] == 100000", + "[name] == ring && [quality] == unique # [maxstamina] == 50 && [lifeleech] >= 3 # [tier] == 100000", // Switch Final Weapon - CTA + "[minimumsockets] >= 5 && [flag] == runeword # [plusskillbattleorders] >= 1 # [secondarytier] == 100000", + // Switch Temporary Weapon - Life Tap charged wand + "[type] == wand && [quality] >= normal # [itemchargedskill] == 82 # [secondarytier] == 75000 + chargeditemscore(item, 82)", + // Switch Final Shield - Spirit + "[name] == monarch && [flag] == runeword # [fcr] >= 25 && [maxmana] >= 89 # [secondarytier] == 110000", + // Switch Temporary Shield - Any 1+ all skill + "[type] == shield # [itemallskills] >= 1 # [secondarytier] == tierscore(item, 100000)", + // Merc Armor - Fortitude + "[type] == armor && [flag] == runeword # [enhanceddefense] >= 200 && [enhanceddamage] >= 300 # [merctier] == 100000", + // Merc Helmet - Eth Andy's + "[name] == demonhead && [quality] == unique && [flag] == ethereal # [strength] >= 25 && [enhanceddefense] >= 100 # [merctier] == 40000 + mercscore(item)", + // Merc Helmet - Andy's + "[name] == demonhead && [quality] == unique && [flag] != ethereal # [strength] >= 25 && [enhanceddefense] >= 100 # [merctier] == 50000 + mercscore(item)", + ]; + + NTIP.buildList(finalGear); + NTIP.buildFinalGear(finalGear); + + return build; + })(); +})(module); diff --git a/libs/SoloPlay/BuildFiles/amazon/amazon.WitchyzonBuild.js b/libs/SoloPlay/BuildFiles/amazon/amazon.WitchyzonBuild.js index 4c4e8505..c039b571 100644 --- a/libs/SoloPlay/BuildFiles/amazon/amazon.WitchyzonBuild.js +++ b/libs/SoloPlay/BuildFiles/amazon/amazon.WitchyzonBuild.js @@ -6,131 +6,140 @@ * */ -const finalBuild = { - caster: false, - skillstab: sdk.skills.tabs.BowandCrossbow, - wantedskills: [sdk.skills.Strafe], - usefulskills: [sdk.skills.Penetrate, sdk.skills.Valkyrie, sdk.skills.Pierce], - precastSkills: [sdk.skills.Valkyrie], - mercDiff: sdk.difficulty.Nightmare, - mercAct: 2, - mercAuraWanted: "Might", - stats: [ - ["strength", 90], ["dexterity", 132], ["vitality", 150], ["dexterity", "all"] - ], - skills: [ - [sdk.skills.Strafe, 1], - [sdk.skills.Valkyrie, 1], - [sdk.skills.Pierce, 1], - [sdk.skills.Strafe, 20], - [sdk.skills.Pierce, 10], - [sdk.skills.Penetrate, 20], - [sdk.skills.Valkyrie, 20], - [sdk.skills.Dodge, 12], - [sdk.skills.Avoid, 7], - [sdk.skills.Evade, 12], - [sdk.skills.Decoy, 2], - ], - autoEquipTiers: [ // autoequip final gear - // Weapon - WitchWild String up'd - "[name] == diamondbow && [quality] == unique # [fireresist] == 40 # [tier] == tierscore(item, 100000)", - // Helmet - Vampz Gaze - "[name] == grimhelm && [quality] == unique && [flag] != ethereal # [manaleech] >= 6 && [lifeleech] >= 6 && [damageresist] >= 15 # [tier] == tierscore(item, 100000)", - // Boots - War Traveler - "[name] == battleboots && [quality] == unique && [flag] != ethereal # [itemmagicbonus] >= 35 # [tier] == tierscore(item, 100000)", - // Belt - Nosferatu's Coil - "[name] == vampirefangbelt && [quality] == unique && [flag] != ethereal # [lifeleech] >= 5 # [tier] == tierscore(item, 100000)", - // Armor - CoH - "[type] == armor && [flag] == runeword && [flag] != ethereal # [fireresist] == 65 && [hpregen] == 7 # [tier] == 100000", - // Amulet - Cat's Eye - "[type] == amulet && [quality] == unique # [dexterity] == 25 # [tier] == 110000", - // Final Rings - Perfect Raven Frost & Bul-Kathos' Wedding Band - "[type] == ring && [quality] == unique # [dexterity] == 20 && [tohit] == 250 # [tier] == 110000", - "[type] == ring && [quality] == unique # [maxstamina] == 50 && [lifeleech] == 5 # [tier] == 110000", - // Rings - Raven Frost && Bul-Kathos' Wedding Band - "[type] == ring && [quality] == unique # [dexterity] >= 15 && [tohit] >= 150 # [tier] == 100000", - "[type] == ring && [quality] == unique # [maxstamina] == 50 && [lifeleech] >= 3 # [tier] == 100000", - // Switch Final Weapon - CTA - "[minimumsockets] >= 5 && [flag] == runeword # [plusskillbattleorders] >= 1 # [secondarytier] == 100000", - // Switch Temporary Weapon - Life Tap charged wand - "[type] == wand && [quality] >= normal # [itemchargedskill] == 82 # [secondarytier] == 75000 + chargeditemscore(item, 82)", - // Switch Shield - Any 1+ all skill - "[type] == shield # [itemallskills] >= 1 # [secondarytier] == tierscore(item, 100000)", - // Merc Armor - Fortitude - "[type] == armor && [flag] == runeword # [enhanceddefense] >= 200 && [enhanceddamage] >= 300 # [merctier] == 100000", - // Merc Final Helmet - Eth Andy's - "[name] == demonhead && [quality] == unique && [flag] == ethereal # [strength] >= 25 && [enhanceddefense] >= 100 # [merctier] == 40000 + mercscore(item)", - // Merc Helmet - Andy's - "[name] == demonhead && [quality] == unique && [flag] != ethereal # [strength] >= 25 && [enhanceddefense] >= 100 # [merctier] == 50000 + mercscore(item)", - ], - charms: { - ResLife: { - max: 3, - have: [], - classid: sdk.items.SmallCharm, - stats: function (check) { - return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MaxHp) === 20); - } - }, +(function (module) { + module.exports = (function () { + const build = { + caster: false, + skillstab: sdk.skills.tabs.BowandCrossbow, + wantedskills: [sdk.skills.Strafe], + usefulskills: [sdk.skills.Penetrate, sdk.skills.Valkyrie, sdk.skills.Pierce], + precastSkills: [sdk.skills.Valkyrie], + wantedMerc: MercData[sdk.skills.Might], + stats: [ + ["strength", 90], ["dexterity", 132], ["vitality", 150], ["dexterity", "all"] + ], + skills: [ + [sdk.skills.Strafe, 1], + [sdk.skills.Valkyrie, 1], + [sdk.skills.Pierce, 1], + [sdk.skills.Strafe, 20], + [sdk.skills.Pierce, 10], + [sdk.skills.Penetrate, 20], + [sdk.skills.Valkyrie, 20], + [sdk.skills.Dodge, 12], + [sdk.skills.Avoid, 7], + [sdk.skills.Evade, 12], + [sdk.skills.Decoy, 2], + ], - ResMf: { - max: 2, - have: [], - classid: sdk.items.SmallCharm, - stats: function (check) { - return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MagicBonus) === 7); - } - }, + charms: { + ResLife: { + max: 3, + have: [], + classid: sdk.items.SmallCharm, + stats: function (check) { + return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MaxHp) === 20); + } + }, - ResFHR: { - max: 1, - have: [], - classid: sdk.items.SmallCharm, - stats: function (check) { - return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.FHR) === 5); - } - }, + ResMf: { + max: 2, + have: [], + classid: sdk.items.SmallCharm, + stats: function (check) { + return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MagicBonus) === 7); + } + }, - SkillerCrossbow: { - max: 1, - have: [], - classid: sdk.items.GrandCharm, - stats: function (check) { - return (!check.unique && check.classid === this.classid && check.getStat(sdk.stats.AddSkillTab, sdk.skills.tabs.BowandCrossbow) === 1 - && check.getStat(sdk.stats.MaxHp) >= 40); - } - }, + ResFHR: { + max: 1, + have: [], + classid: sdk.items.SmallCharm, + stats: function (check) { + return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.FHR) === 5); + } + }, - SkillerPassive: { - max: 1, - have: [], - classid: sdk.items.GrandCharm, - stats: function (check) { - return (!check.unique && check.classid === this.classid && check.getStat(sdk.stats.AddSkillTab, sdk.skills.tabs.PassiveandMagic) === 1 - && check.getStat(sdk.stats.MaxHp) >= 40); - } - }, - }, + SkillerCrossbow: { + max: 1, + have: [], + classid: sdk.items.GrandCharm, + stats: function (check) { + return (!check.unique && check.classid === this.classid && check.getStat(sdk.stats.AddSkillTab, sdk.skills.tabs.BowandCrossbow) === 1 + && check.getStat(sdk.stats.MaxHp) >= 40); + } + }, - AutoBuildTemplate: { - 1: { - Update: function () { - Config.AttackSkill = [-1, sdk.skills.Strafe, -1, sdk.skills.Strafe, -1, sdk.skills.MagicArrow, -1]; - Config.LowManaSkill = [0, -1]; - } - }, - }, + SkillerPassive: { + max: 1, + have: [], + classid: sdk.items.GrandCharm, + stats: function (check) { + return (!check.unique && check.classid === this.classid && check.getStat(sdk.stats.AddSkillTab, sdk.skills.tabs.PassiveandMagic) === 1 + && check.getStat(sdk.stats.MaxHp) >= 40); + } + }, + }, - respec: function () { - if (me.classic) { - return false; - } else { - return Check.haveItem("diamondbow", "unique", "Witchwild String") && Check.haveItem("vampirefangbelt", "unique", "Nosferatu's Coil"); - } - }, + AutoBuildTemplate: { + 1: { + Update: function () { + Config.AttackSkill = [-1, sdk.skills.Strafe, -1, sdk.skills.Strafe, -1, sdk.skills.MagicArrow, -1]; + Config.LowManaSkill = [0, -1]; + } + }, + }, - active: function () { - return this.respec() && me.checkSkill(sdk.skills.Strafe, sdk.skills.subindex.HardPoints); - }, -}; + respec: function () { + if (me.classic) { + return false; + } else { + return Check.haveItem("diamondbow", "unique", "Witchwild String") && Check.haveItem("vampirefangbelt", "unique", "Nosferatu's Coil"); + } + }, + + active: function () { + return this.respec() && me.checkSkill(sdk.skills.Strafe, sdk.skills.subindex.HardPoints); + }, + }; + + let finalGear = [ // autoequip final gear + // Weapon - WitchWild String up'd + "[name] == diamondbow && [quality] == unique # [fireresist] == 40 # [tier] == tierscore(item, 100000)", + // Helmet - Vampz Gaze + "[name] == grimhelm && [quality] == unique && [flag] != ethereal # [manaleech] >= 6 && [lifeleech] >= 6 && [damageresist] >= 15 # [tier] == tierscore(item, 100000)", + // Boots - War Traveler + "[name] == battleboots && [quality] == unique && [flag] != ethereal # [itemmagicbonus] >= 35 # [tier] == tierscore(item, 100000)", + // Belt - Nosferatu's Coil + "[name] == vampirefangbelt && [quality] == unique && [flag] != ethereal # [lifeleech] >= 5 # [tier] == tierscore(item, 100000)", + // Armor - CoH + "[type] == armor && [flag] == runeword && [flag] != ethereal # [fireresist] == 65 && [hpregen] == 7 # [tier] == 100000", + // Amulet - Cat's Eye + "[type] == amulet && [quality] == unique # [dexterity] == 25 # [tier] == 110000", + // Final Rings - Perfect Raven Frost & Bul-Kathos' Wedding Band + "[type] == ring && [quality] == unique # [dexterity] == 20 && [tohit] == 250 # [tier] == 110000", + "[type] == ring && [quality] == unique # [maxstamina] == 50 && [lifeleech] == 5 # [tier] == 110000", + // Rings - Raven Frost && Bul-Kathos' Wedding Band + "[type] == ring && [quality] == unique # [dexterity] >= 15 && [tohit] >= 150 # [tier] == 100000", + "[type] == ring && [quality] == unique # [maxstamina] == 50 && [lifeleech] >= 3 # [tier] == 100000", + // Switch Final Weapon - CTA + "[minimumsockets] >= 5 && [flag] == runeword # [plusskillbattleorders] >= 1 # [secondarytier] == 100000", + // Switch Temporary Weapon - Life Tap charged wand + "[type] == wand && [quality] >= normal # [itemchargedskill] == 82 # [secondarytier] == 75000 + chargeditemscore(item, 82)", + // Switch Shield - Any 1+ all skill + "[type] == shield # [itemallskills] >= 1 # [secondarytier] == tierscore(item, 100000)", + // Merc Armor - Fortitude + "[type] == armor && [flag] == runeword # [enhanceddefense] >= 200 && [enhanceddamage] >= 300 # [merctier] == 100000", + // Merc Final Helmet - Eth Andy's + "[name] == demonhead && [quality] == unique && [flag] == ethereal # [strength] >= 25 && [enhanceddefense] >= 100 # [merctier] == 40000 + mercscore(item)", + // Merc Helmet - Andy's + "[name] == demonhead && [quality] == unique && [flag] != ethereal # [strength] >= 25 && [enhanceddefense] >= 100 # [merctier] == 50000 + mercscore(item)", + ]; + + NTIP.buildList(finalGear); + NTIP.buildFinalGear(finalGear); + + return build; + })(); +})(module); diff --git a/libs/SoloPlay/BuildFiles/assassin/assassin.LevelingBuild.js b/libs/SoloPlay/BuildFiles/assassin/assassin.LevelingBuild.js index 87fa79aa..155675d0 100644 --- a/libs/SoloPlay/BuildFiles/assassin/assassin.LevelingBuild.js +++ b/libs/SoloPlay/BuildFiles/assassin/assassin.LevelingBuild.js @@ -5,74 +5,82 @@ * */ -let build = { - AutoBuildTemplate: {}, - caster: true, - skillstab: sdk.skills.tabs.Traps, - wantedskills: [sdk.skills.FireBlast, sdk.skills.LightningSentry, sdk.skills.DeathSentry, sdk.skills.ShadowMaster], - usefulskills: [sdk.skills.ChargedBoltSentry, sdk.skills.BladeShield, sdk.skills.Fade], - mercDiff: sdk.difficulty.Nightmare, - mercAct: 2, - mercAuraWanted: "Holy Freeze", - stats: [ - ["strength", 47], ["dexterity", 46], ["vitality", 166], - ["strength", 61], ["vitality", 241], ["strength", 79], - ["dexterity", 79], ["strength", 156], ["vitality", "all"] - ], - skills: [ - // Skills points at respec 33 - [sdk.skills.Fade, 1], // points left 30 - [sdk.skills.ShadowMaster, 1], // points left 25 - [sdk.skills.MindBlast, 1], // points left 20 - [sdk.skills.DeathSentry, 1], // points left 19 - [sdk.skills.LightningSentry, 7], // points left 13 - [sdk.skills.FireBlast, 6], // points left 8 - [sdk.skills.ShockWeb, 8], // points left 1 - [sdk.skills.WakeofFire, 1], // points left 0 - [sdk.skills.LightningSentry, 20, false], - [sdk.skills.DeathSentry, 10], - [sdk.skills.ShockWeb, 9], - [sdk.skills.FireBlast, 8], - [sdk.skills.DeathSentry, 12], - [sdk.skills.ShockWeb, 11], - [sdk.skills.FireBlast, 11], - [sdk.skills.DeathSentry, 13], - [sdk.skills.ShockWeb, 13], - [sdk.skills.FireBlast, 12], - [sdk.skills.DeathSentry, 14], - [sdk.skills.ShockWeb, 15], - [sdk.skills.FireBlast, 14], - [sdk.skills.DeathSentry, 15], - [sdk.skills.ShockWeb, 16], - [sdk.skills.FireBlast, 15], - [sdk.skills.DeathSentry, 16], - [sdk.skills.ShockWeb, 18], - [sdk.skills.FireBlast, 16], - [sdk.skills.DeathSentry, 17], - [sdk.skills.ShockWeb, 20], - [sdk.skills.FireBlast, 18], - [sdk.skills.DeathSentry, 20], - [sdk.skills.ShockWeb, 20], - [sdk.skills.FireBlast, 20], - [sdk.skills.ChargedBoltSentry, 20], - ], - active: function () { - return (me.charlvl > CharInfo.respecOne && me.charlvl > CharInfo.respecTwo && me.getSkill(sdk.skills.LightningSentry, sdk.skills.subindex.HardPoints) === 20 && !Check.finalBuild().active()); - }, -}; +(function (module) { + module.exports = (function () { + const build = { + caster: true, + skillstab: sdk.skills.tabs.Traps, + wantedskills: [sdk.skills.FireBlast, sdk.skills.LightningSentry, sdk.skills.DeathSentry, sdk.skills.ShadowMaster], + usefulskills: [sdk.skills.ChargedBoltSentry, sdk.skills.BladeShield, sdk.skills.Fade], + wantedMerc: MercData[sdk.skills.HolyFreeze], + stats: [ + ["strength", 47], ["dexterity", 46], ["vitality", 166], + ["strength", 61], ["vitality", 241], ["strength", 79], + ["dexterity", 79], ["strength", 156], ["vitality", "all"] + ], + skills: [ + // Skills points at respec 33 + [sdk.skills.Fade, 1], // points left 30 + [sdk.skills.ShadowMaster, 1], // points left 25 + [sdk.skills.MindBlast, 1], // points left 20 + [sdk.skills.DeathSentry, 1], // points left 19 + [sdk.skills.LightningSentry, 7], // points left 13 + [sdk.skills.FireBlast, 6], // points left 8 + [sdk.skills.ShockWeb, 8], // points left 1 + [sdk.skills.WakeofFire, 1], // points left 0 + [sdk.skills.LightningSentry, 20, false], + [sdk.skills.DeathSentry, 10], + [sdk.skills.ShockWeb, 9], + [sdk.skills.FireBlast, 8], + [sdk.skills.DeathSentry, 12], + [sdk.skills.ShockWeb, 11], + [sdk.skills.FireBlast, 11], + [sdk.skills.DeathSentry, 13], + [sdk.skills.ShockWeb, 13], + [sdk.skills.FireBlast, 12], + [sdk.skills.DeathSentry, 14], + [sdk.skills.ShockWeb, 15], + [sdk.skills.FireBlast, 14], + [sdk.skills.DeathSentry, 15], + [sdk.skills.ShockWeb, 16], + [sdk.skills.FireBlast, 15], + [sdk.skills.DeathSentry, 16], + [sdk.skills.ShockWeb, 18], + [sdk.skills.FireBlast, 16], + [sdk.skills.DeathSentry, 17], + [sdk.skills.ShockWeb, 20], + [sdk.skills.FireBlast, 18], + [sdk.skills.DeathSentry, 20], + [sdk.skills.ShockWeb, 20], + [sdk.skills.FireBlast, 20], + [sdk.skills.ChargedBoltSentry, 20], + ], -build.AutoBuildTemplate[1] = buildAutoBuildTempObj(() => { - Config.AttackSkill = [-1, sdk.skills.ShockWeb, sdk.skills.FireBlast, sdk.skills.ShockWeb, sdk.skills.FireBlast, -1, -1]; - Config.LowManaSkill = [-1, -1]; - Config.UseTraps = true; - Config.UseFade = true; - Config.Traps = [sdk.skills.LightningSentry, sdk.skills.LightningSentry, sdk.skills.LightningSentry, sdk.skills.DeathSentry, sdk.skills.DeathSentry]; - Config.BossTraps = [sdk.skills.LightningSentry, sdk.skills.LightningSentry, sdk.skills.LightningSentry, sdk.skills.LightningSentry, sdk.skills.LightningSentry]; - Config.TownHP = me.hardcore ? 0 : 35; - Config.BeltColumn = ["hp", "hp", "mp", "mp"]; - Config.HPBuffer = 2; - Config.MPBuffer = me.charlvl < 80 ? 6 : 2; - Config.DodgeHP = 75; - SetUp.belt(); -}); + active: function () { + return (me.charlvl > CharInfo.respecOne && me.charlvl > CharInfo.respecTwo && me.getSkill(sdk.skills.LightningSentry, sdk.skills.subindex.HardPoints) === 20 && !Check.finalBuild().active()); + }, + + AutoBuildTemplate: { + 1: { + Update: function () { + Config.AttackSkill = [-1, sdk.skills.ShockWeb, sdk.skills.FireBlast, sdk.skills.ShockWeb, sdk.skills.FireBlast, -1, -1]; + Config.LowManaSkill = [-1, -1]; + Config.UseTraps = true; + Config.UseFade = true; + Config.Traps = [sdk.skills.LightningSentry, sdk.skills.LightningSentry, sdk.skills.LightningSentry, sdk.skills.DeathSentry, sdk.skills.DeathSentry]; + Config.BossTraps = [sdk.skills.LightningSentry, sdk.skills.LightningSentry, sdk.skills.LightningSentry, sdk.skills.LightningSentry, sdk.skills.LightningSentry]; + Config.TownHP = me.hardcore ? 0 : 35; + Config.BeltColumn = ["hp", "hp", "mp", "mp"]; + Config.HPBuffer = 2; + Config.MPBuffer = me.charlvl < 80 ? 6 : 2; + Config.DodgeHP = 75; + SetUp.belt(); + } + } + }, + }; + + return build; + })(); +})(module); diff --git a/libs/SoloPlay/BuildFiles/assassin/assassin.StartBuild.js b/libs/SoloPlay/BuildFiles/assassin/assassin.StartBuild.js index 43c1a325..b024d254 100644 --- a/libs/SoloPlay/BuildFiles/assassin/assassin.StartBuild.js +++ b/libs/SoloPlay/BuildFiles/assassin/assassin.StartBuild.js @@ -5,60 +5,67 @@ * */ -let build = { - AutoBuildTemplate: {}, - caster: true, - skillstab: sdk.skills.tabs.Traps, - wantedskills: [sdk.skills.FireBlast, sdk.skills.WakeofFire], - usefulskills: [sdk.skills.CloakofShadows, sdk.skills.ShadowWarrior], - mercDiff: sdk.difficulty.Nightmare, - mercAct: 2, - mercAuraWanted: "Holy Freeze", - stats: [ - ["vitality", 35], ["energy", 35], ["strength", 33], ["dexterity", 33], - ["vitality", 50], ["strength", 46], ["dexterity", 46], - ["vitality", 70], ["strength", 50], ["dexterity", 50], - ["energy", 50], ["vitality", "all"] - ], - skills: [ - [sdk.skills.FireBlast, 4, false], // level 4 - [sdk.skills.ClawMastery, 1], // level 5 (den) - [sdk.skills.PsychicHammer, 1], // level 6 - [sdk.skills.BurstofSpeed, 5], // level 11 - [sdk.skills.WakeofFire, 1, false], // level 12 - [sdk.skills.CloakofShadows, 1, true], // level 13 - [sdk.skills.WakeofFire, 10, false], // level 24 - [sdk.skills.FireBlast, 6, false], // level 26 - [sdk.skills.WakeofFire, 20, false], // level 36 - [sdk.skills.FireBlast, 10], // level 42 - ], - active: function () { - return me.charlvl < CharInfo.respecOne && !me.checkSkill(sdk.skills.LightningSentry, sdk.skills.subindex.HardPoints); - }, -}; +(function (module, require) { + module.exports = (function () { + const build = { + AutoBuildTemplate: {}, + caster: true, + skillstab: sdk.skills.tabs.Traps, + wantedskills: [sdk.skills.FireBlast, sdk.skills.WakeofFire], + usefulskills: [sdk.skills.CloakofShadows, sdk.skills.ShadowWarrior], + wantedMerc: MercData[sdk.skills.HolyFreeze], + stats: [ + ["vitality", 35], ["energy", 35], ["strength", 33], ["dexterity", 33], + ["vitality", 50], ["strength", 46], ["dexterity", 46], + ["vitality", 70], ["strength", 50], ["dexterity", 50], + ["energy", 50], ["vitality", "all"] + ], + skills: [ + [sdk.skills.FireBlast, 4, false], // level 4 + [sdk.skills.ClawMastery, 1], // level 5 (den) + [sdk.skills.PsychicHammer, 1], // level 6 + [sdk.skills.BurstofSpeed, 5], // level 11 + [sdk.skills.WakeofFire, 1, false], // level 12 + [sdk.skills.CloakofShadows, 1, true], // level 13 + [sdk.skills.WakeofFire, 10, false], // level 24 + [sdk.skills.FireBlast, 6, false], // level 26 + [sdk.skills.WakeofFire, 20, false], // level 36 + [sdk.skills.FireBlast, 10], // level 42 + ], -build.AutoBuildTemplate[1] = buildAutoBuildTempObj(() => { - Config.TownHP = me.hardcore ? 0 : 35; - Config.BeltColumn = ["hp", "hp", "hp", "hp"]; - Config.HPBuffer = 4; - Config.MPBuffer = 2; - Config.AttackSkill = [-1, 0, 0, 0, 0, 0, 0]; - Config.LowManaSkill = [0, 0]; - SetUp.belt(); -}); -build.AutoBuildTemplate[2] = buildAutoBuildTempObj(() => { - Config.BeltColumn = ["hp", "hp", "mp", "mp"]; - Config.HPBuffer = 2; - Config.MPBuffer = 6; - Config.AttackSkill = [-1, sdk.skills.FireBlast, -1, sdk.skills.FireBlast, -1, (me.checkSkill(sdk.skills.PsychicHammer, sdk.skills.subindex.SoftPoints) ? sdk.skills.PsychicHammer : 0), 0]; - Config.UseBoS = true; - SetUp.belt(); -}); -build.AutoBuildTemplate[12] = buildAutoBuildTempObj(() => { - Config.AttackSkill = [-1, sdk.skills.FireBlast, -1, sdk.skills.FireBlast, -1, (me.checkSkill(sdk.skills.PsychicHammer, sdk.skills.subindex.SoftPoints) ? sdk.skills.PsychicHammer : 0), 0]; - Config.UseTraps = true; - Config.Traps = [sdk.skills.WakeofFire, sdk.skills.WakeofFire, sdk.skills.WakeofFire, -1, -1]; // Skill IDs for traps to be cast on all mosters except act bosses. - Config.BossTraps = [sdk.skills.WakeofFire, sdk.skills.WakeofFire, sdk.skills.WakeofFire, sdk.skills.WakeofFire, sdk.skills.WakeofFire]; // Skill IDs for traps to be cast on act bosses. - SetUp.belt(); -}); + active: function () { + return me.charlvl < CharInfo.respecOne && !me.checkSkill(sdk.skills.LightningSentry, sdk.skills.subindex.HardPoints); + }, + }; + + const { buildAutoBuildTempObj } = require("../../Utils/General"); + + build.AutoBuildTemplate[1] = buildAutoBuildTempObj(() => { + Config.TownHP = me.hardcore ? 0 : 35; + Config.BeltColumn = ["hp", "hp", "hp", "hp"]; + Config.HPBuffer = 4; + Config.MPBuffer = 2; + Config.AttackSkill = [-1, 0, 0, 0, 0, 0, 0]; + Config.LowManaSkill = [0, 0]; + SetUp.belt(); + }); + build.AutoBuildTemplate[2] = buildAutoBuildTempObj(() => { + Config.BeltColumn = ["hp", "hp", "mp", "mp"]; + Config.HPBuffer = 2; + Config.MPBuffer = 6; + Config.AttackSkill = [-1, sdk.skills.FireBlast, -1, sdk.skills.FireBlast, -1, (me.checkSkill(sdk.skills.PsychicHammer, sdk.skills.subindex.SoftPoints) ? sdk.skills.PsychicHammer : 0), 0]; + Config.UseBoS = true; + SetUp.belt(); + }); + build.AutoBuildTemplate[12] = buildAutoBuildTempObj(() => { + Config.AttackSkill = [-1, sdk.skills.FireBlast, -1, sdk.skills.FireBlast, -1, (me.checkSkill(sdk.skills.PsychicHammer, sdk.skills.subindex.SoftPoints) ? sdk.skills.PsychicHammer : 0), 0]; + Config.UseTraps = true; + Config.Traps = [sdk.skills.WakeofFire, sdk.skills.WakeofFire, sdk.skills.WakeofFire, -1, -1]; // Skill IDs for traps to be cast on all mosters except act bosses. + Config.BossTraps = [sdk.skills.WakeofFire, sdk.skills.WakeofFire, sdk.skills.WakeofFire, sdk.skills.WakeofFire, sdk.skills.WakeofFire]; // Skill IDs for traps to be cast on act bosses. + SetUp.belt(); + }); + + return build; + })(); +})(module, require); diff --git a/libs/SoloPlay/BuildFiles/assassin/assassin.TrapsinBuild.js b/libs/SoloPlay/BuildFiles/assassin/assassin.TrapsinBuild.js index 4bd22db0..c7ebc741 100644 --- a/libs/SoloPlay/BuildFiles/assassin/assassin.TrapsinBuild.js +++ b/libs/SoloPlay/BuildFiles/assassin/assassin.TrapsinBuild.js @@ -5,141 +5,150 @@ * */ -const finalBuild = { - caster: true, - skillstab: sdk.skills.tabs.Traps, - wantedskills: [sdk.skills.FireBlast, sdk.skills.LightningSentry, sdk.skills.DeathSentry, sdk.skills.ShadowMaster], - usefulskills: [sdk.skills.ChargedBoltSentry, sdk.skills.BladeShield, sdk.skills.Fade], - precastSkills: [sdk.skills.Fade, sdk.skills.ShadowMaster], - mercDiff: sdk.difficulty.Nightmare, - mercAct: 2, - mercAuraWanted: "Holy Freeze", - stats: [ - ["strength", 156], ["dexterity", 79], ["vitality", "all"] - ], - skills: [ - [sdk.skills.MindBlast, 1], - [sdk.skills.ShadowMaster, 1], - [sdk.skills.Fade, 1], - [sdk.skills.LightningSentry, 20], - [sdk.skills.ShockWeb, 15], - [sdk.skills.FireBlast, 14], - [sdk.skills.DeathSentry, 15], // lvl 74 w/o quest skills pts - [sdk.skills.ShockWeb, 16], - [sdk.skills.FireBlast, 15], - [sdk.skills.DeathSentry, 16], - [sdk.skills.ShockWeb, 18], - [sdk.skills.FireBlast, 16], - [sdk.skills.DeathSentry, 17], - [sdk.skills.ShockWeb, 20], - [sdk.skills.FireBlast, 18], - [sdk.skills.DeathSentry, 20], - [sdk.skills.ShockWeb, 20], - [sdk.skills.FireBlast, 20], - [sdk.skills.ChargedBoltSentry, 20], - ], - autoEquipTiers: [ // autoequip final gear - // Final Weapon - Silence - "[type] == sword && [flag] == runeword # [itemallskills] == 2 && [ias] == 20 && [fireresist] == 75 # [tier] == 200000", - // Temporary Weapon - HotO - "[type] == mace && [flag] == runeword # [itemallskills] == 3 # [tier] == 100000", - // Helmet - Andy's - "[name] == demonhead && [quality] == unique && [flag] != ethereal # [strength] >= 25 && [enhanceddefense] >= 100 # [tier] == tierscore(item, 100000)", - // Belt - Arach's - "[name] == spiderwebsash && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 90 # [tier] == tierscore(item, 100000)", - // Boots - Waterwalks - "[name] == sharkskinboots && [quality] == unique && [flag] != ethereal # [maxhp] >= 65 # [tier] == tierscore(item, 100000)", - // Armor - Enigma - "[type] == armor && [flag] != ethereal && [flag] == runeword # [itemallskills] == 2 # [tier] == 100000", - // Shield - Spirit - "[name] == monarch && [flag] == runeword # [fcr] >= 25 && [maxmana] >= 89 # [tier] == tierscore(item, 110000)", - // Gloves - Lava Gout - "[name] == battlegauntlets && [quality] == unique && [flag] != ethereal # [ias] == 20 # [tier] == tierscore(item, 100000)", - // Amulet - Maras - "[type] == amulet && [quality] == unique # [strength] == 5 && [coldresist] >= 30 # [tier] == tierscore(item, 100000)", - // Final Rings - SoJ & Perfect Raven Frost - "[type] == ring && [quality] == unique # [itemmaxmanapercent] == 25 # [tier] == 100000", - "[type] == ring && [quality] == unique # [dexterity] == 20 # [tier] == 110000", - // Rings - Raven Frost - "[type] == ring && [quality] == unique # [dexterity] >= 15 # [tier] == 100000", - // Switch Final Weapon - CTA - "[minimumsockets] >= 5 && [flag] == runeword # [plusskillbattleorders] >= 1 # [secondarytier] == 100000", - // Switch Final Shield - Spirit - "[name] == monarch && [flag] == runeword # [fcr] >= 25 && [maxmana] >= 89 # [secondarytier] == 110000", - // Switch Temporary Shield - Any 1+ all skill - "[type] == shield # [itemallskills] >= 1 # [secondarytier] == tierscore(item, 50000)", - // Merc Armor - Fortitude - "[type] == armor && [flag] == runeword # [enhanceddefense] >= 200 && [enhanceddamage] >= 300 # [merctier] == 100000", - // Merc Final Helmet - Eth Andy's - "[name] == demonhead && [quality] == unique && [flag] == ethereal # [strength] >= 25 && [enhanceddefense] >= 100 # [merctier] == 50000 + mercscore(item)", - // Merc Helmet - Andy's - "[name] == demonhead && [quality] == unique && [flag] != ethereal # [strength] >= 25 && [enhanceddefense] >= 100 # [merctier] == 40000 + mercscore(item)", - ], - charms: { - ResLife: { - max: 3, - have: [], - classid: sdk.items.SmallCharm, - stats: function (check) { - return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MaxHp) === 20); - } - }, +(function (module) { + module.exports = (function () { + const build = { + caster: true, + skillstab: sdk.skills.tabs.Traps, + wantedskills: [sdk.skills.FireBlast, sdk.skills.LightningSentry, sdk.skills.DeathSentry, sdk.skills.ShadowMaster], + usefulskills: [sdk.skills.ChargedBoltSentry, sdk.skills.BladeShield, sdk.skills.Fade], + precastSkills: [sdk.skills.Fade, sdk.skills.ShadowMaster], + wantedMerc: MercData[sdk.skills.HolyFreeze], + stats: [ + ["strength", 156], ["dexterity", 79], ["vitality", "all"] + ], + skills: [ + [sdk.skills.MindBlast, 1], + [sdk.skills.ShadowMaster, 1], + [sdk.skills.Fade, 1], + [sdk.skills.LightningSentry, 20], + [sdk.skills.ShockWeb, 15], + [sdk.skills.FireBlast, 14], + [sdk.skills.DeathSentry, 15], // lvl 74 w/o quest skills pts + [sdk.skills.ShockWeb, 16], + [sdk.skills.FireBlast, 15], + [sdk.skills.DeathSentry, 16], + [sdk.skills.ShockWeb, 18], + [sdk.skills.FireBlast, 16], + [sdk.skills.DeathSentry, 17], + [sdk.skills.ShockWeb, 20], + [sdk.skills.FireBlast, 18], + [sdk.skills.DeathSentry, 20], + [sdk.skills.ShockWeb, 20], + [sdk.skills.FireBlast, 20], + [sdk.skills.ChargedBoltSentry, 20], + ], - ResMf: { - max: 2, - have: [], - classid: sdk.items.SmallCharm, - stats: function (check) { - return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MagicBonus) === 7); - } - }, + charms: { + ResLife: { + max: 3, + have: [], + classid: sdk.items.SmallCharm, + stats: function (check) { + return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MaxHp) === 20); + } + }, - ResFHR: { - max: 1, - have: [], - classid: sdk.items.SmallCharm, - stats: function (check) { - return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.FHR) === 5); - } - }, + ResMf: { + max: 2, + have: [], + classid: sdk.items.SmallCharm, + stats: function (check) { + return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MagicBonus) === 7); + } + }, - LifeMana: { - max: 2, - have: [], - classid: sdk.items.SmallCharm, - stats: function (check) { - return (!check.unique && check.classid === this.classid && check.getStat(sdk.stats.MaxHp) === 20 && check.getStat(sdk.stats.MaxMana) === 17); - } - }, + ResFHR: { + max: 1, + have: [], + classid: sdk.items.SmallCharm, + stats: function (check) { + return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.FHR) === 5); + } + }, - Skiller: { - max: 2, - have: [], - classid: sdk.items.GrandCharm, - stats: function (check) { - return (!check.unique && check.classid === this.classid && check.getStat(sdk.stats.AddSkillTab, sdk.skills.tabs.Traps) === 1 - && check.getStat(sdk.stats.MaxHp) >= 40); - } - }, - }, + LifeMana: { + max: 2, + have: [], + classid: sdk.items.SmallCharm, + stats: function (check) { + return (!check.unique && check.classid === this.classid && check.getStat(sdk.stats.MaxHp) === 20 && check.getStat(sdk.stats.MaxMana) === 17); + } + }, - AutoBuildTemplate: { - 1: { - Update: function () { - Config.UseTraps = true; - Config.AttackSkill = [-1, sdk.skills.ShockWeb, sdk.skills.FireBlast, sdk.skills.ShockWeb, sdk.skills.FireBlast, -1, -1]; - Config.Traps = [sdk.skills.LightningSentry, sdk.skills.LightningSentry, sdk.skills.LightningSentry, sdk.skills.DeathSentry, sdk.skills.DeathSentry]; - Config.BossTraps = [sdk.skills.LightningSentry, sdk.skills.LightningSentry, sdk.skills.LightningSentry, sdk.skills.LightningSentry, sdk.skills.LightningSentry]; - } - }, - }, + Skiller: { + max: 2, + have: [], + classid: sdk.items.GrandCharm, + stats: function (check) { + return (!check.unique && check.classid === this.classid && check.getStat(sdk.stats.AddSkillTab, sdk.skills.tabs.Traps) === 1 + && check.getStat(sdk.stats.MaxHp) >= 40); + } + }, + }, - respec: function () { - return (Attack.checkInfinity() || (myData.merc.gear.includes(sdk.locale.items.Infinity) && !Misc.poll(() => me.getMerc(), 200, 50))) && Check.haveItem("armor", "runeword", "Enigma"); - }, + AutoBuildTemplate: { + 1: { + Update: function () { + Config.UseTraps = true; + Config.AttackSkill = [-1, sdk.skills.ShockWeb, sdk.skills.FireBlast, sdk.skills.ShockWeb, sdk.skills.FireBlast, -1, -1]; + Config.Traps = [sdk.skills.LightningSentry, sdk.skills.LightningSentry, sdk.skills.LightningSentry, sdk.skills.DeathSentry, sdk.skills.DeathSentry]; + Config.BossTraps = [sdk.skills.LightningSentry, sdk.skills.LightningSentry, sdk.skills.LightningSentry, sdk.skills.LightningSentry, sdk.skills.LightningSentry]; + } + }, + }, - active: function () { - return this.respec() && me.getSkill(sdk.skills.LightningSentry, sdk.skills.subindex.HardPoints) === 20; - }, -}; + respec: function () { + return (Attack.checkInfinity() || (myData.merc.gear.includes(sdk.locale.items.Infinity) && !Misc.poll(() => me.getMerc(), 200, 50))) && Check.haveItem("armor", "runeword", "Enigma"); + }, + + active: function () { + return this.respec() && me.getSkill(sdk.skills.LightningSentry, sdk.skills.subindex.HardPoints) === 20; + }, + }; + + let finalGear = [ // autoequip final gear + // Final Weapon - Silence + "[type] == sword && [flag] == runeword # [itemallskills] == 2 && [ias] == 20 && [fireresist] == 75 # [tier] == 200000", + // Temporary Weapon - HotO + "[type] == mace && [flag] == runeword # [itemallskills] == 3 # [tier] == 100000", + // Helmet - Andy's + "[name] == demonhead && [quality] == unique && [flag] != ethereal # [strength] >= 25 && [enhanceddefense] >= 100 # [tier] == tierscore(item, 100000)", + // Belt - Arach's + "[name] == spiderwebsash && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 90 # [tier] == tierscore(item, 100000)", + // Boots - Waterwalks + "[name] == sharkskinboots && [quality] == unique && [flag] != ethereal # [maxhp] >= 65 # [tier] == tierscore(item, 100000)", + // Armor - Enigma + "[type] == armor && [flag] != ethereal && [flag] == runeword # [itemallskills] == 2 # [tier] == 100000", + // Shield - Spirit + "[name] == monarch && [flag] == runeword # [fcr] >= 25 && [maxmana] >= 89 # [tier] == tierscore(item, 110000)", + // Gloves - Lava Gout + "[name] == battlegauntlets && [quality] == unique && [flag] != ethereal # [ias] == 20 # [tier] == tierscore(item, 100000)", + // Amulet - Maras + "[type] == amulet && [quality] == unique # [strength] == 5 && [coldresist] >= 30 # [tier] == tierscore(item, 100000)", + // Final Rings - SoJ & Perfect Raven Frost + "[type] == ring && [quality] == unique # [itemmaxmanapercent] == 25 # [tier] == 100000", + "[type] == ring && [quality] == unique # [dexterity] == 20 # [tier] == 110000", + // Rings - Raven Frost + "[type] == ring && [quality] == unique # [dexterity] >= 15 # [tier] == 100000", + // Switch Final Weapon - CTA + "[minimumsockets] >= 5 && [flag] == runeword # [plusskillbattleorders] >= 1 # [secondarytier] == 100000", + // Switch Final Shield - Spirit + "[name] == monarch && [flag] == runeword # [fcr] >= 25 && [maxmana] >= 89 # [secondarytier] == 110000", + // Switch Temporary Shield - Any 1+ all skill + "[type] == shield # [itemallskills] >= 1 # [secondarytier] == tierscore(item, 50000)", + // Merc Armor - Fortitude + "[type] == armor && [flag] == runeword # [enhanceddefense] >= 200 && [enhanceddamage] >= 300 # [merctier] == 100000", + // Merc Final Helmet - Eth Andy's + "[name] == demonhead && [quality] == unique && [flag] == ethereal # [strength] >= 25 && [enhanceddefense] >= 100 # [merctier] == 50000 + mercscore(item)", + // Merc Helmet - Andy's + "[name] == demonhead && [quality] == unique && [flag] != ethereal # [strength] >= 25 && [enhanceddefense] >= 100 # [merctier] == 40000 + mercscore(item)", + ]; + + NTIP.buildList(finalGear); + NTIP.buildFinalGear(finalGear); + + return build; + })(); +})(module); diff --git a/libs/SoloPlay/BuildFiles/assassin/assassin.WhirlsinBuild.js b/libs/SoloPlay/BuildFiles/assassin/assassin.WhirlsinBuild.js index 33bfdf85..7d93a148 100644 --- a/libs/SoloPlay/BuildFiles/assassin/assassin.WhirlsinBuild.js +++ b/libs/SoloPlay/BuildFiles/assassin/assassin.WhirlsinBuild.js @@ -5,132 +5,141 @@ * */ -const finalBuild = { - caster: false, - skillstab: sdk.skills.tabs.ShadowDisciplines, - wantedskills: [sdk.skills.FireBlast, sdk.skills.LightningSentry, sdk.skills.DeathSentry, sdk.skills.ShadowMaster], - usefulskills: [sdk.skills.ChargedBoltSentry, sdk.skills.BladeShield, sdk.skills.Fade], - precastSkills: [sdk.skills.Fade, sdk.skills.ShadowMaster], - mercDiff: sdk.difficulty.Nightmare, - mercAct: 2, - mercAuraWanted: "Holy Freeze", - stats: [ - ["strength", 130], ["dexterity", 99], ["vitality", "all"] - ], - skills: [ - [sdk.skills.Fade, 1], - [sdk.skills.Venom, 1], - [sdk.skills.MindBlast, 1], - [sdk.skills.BladeShield, 1], - [sdk.skills.ClawMastery, 20], - [sdk.skills.DeathSentry, 1], - [sdk.skills.ShadowMaster, 20, false], - [sdk.skills.Venom, 20, false], // lvl 77 w/o quest skill pts - [sdk.skills.Fade, 20, false], - [sdk.skills.BladeShield, 20], - ], - autoEquipTiers: [ // autoequip final gear - // Final Weapon - Chaos Claw & Fury - "[type] == assassinclaw && [flag] == runeword # [plusskillwhirlwind] == 1 # [tier] == 100000", - "[type] == assassinclaw && [flag] == runeword # [itemallskills] == 2 && [ias] == 40 && [itemdeadlystrike] == 33 # [tier] == 200000", - // Helmet - GFace - "[name] == wingedhelm && [quality] == set && [flag] != ethereal # [fhr] >= 30 # [tier] == tierscore(item, 100000)", - // Belt - Verdungos - "[name] == mithrilcoil && [quality] == unique && [flag] != ethereal # [damageresist] == 15 # [tier] == tierscore(item, 110000)", - // Boots - Gore Rider - "[name] == warboots && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 160 # [tier] == tierscore(item, 110000)", - // Armor - Fortitude - "[type] == armor && [flag] == runeword # [enhanceddefense] >= 200 && [enhanceddamage] >= 300 # [tier] == 100000", - // Gloves - Trang-Ouls - "[name] == heavybracers && [quality] == set && [flag] != ethereal # [fcr] == 20 # [tier] == 100000", // - // Amulet - Highlords - "[type] == amulet && [quality] == unique # [lightresist] == 35 # [tier] == 100000", - // Final Rings - Perfect Raven Frost & Bul-Kathos' Wedding Band - "[type] == ring && [quality] == unique # [dexterity] == 20 && [tohit] == 250 # [tier] == 110000", - "[type] == ring && [quality] == unique # [maxstamina] == 50 && [lifeleech] == 5 # [tier] == 110000", - // Rings - Raven Frost && Bul-Kathos' Wedding Band - "[type] == ring && [quality] == unique # [dexterity] >= 15 && [tohit] >= 150 # [tier] == 100000", - "[type] == ring && [quality] == unique # [maxstamina] == 50 && [lifeleech] >= 3 # [tier] == 100000", - // Switch Final Weapon - CTA - "[minimumsockets] >= 5 && [flag] == runeword # [plusskillbattleorders] >= 1 # [secondarytier] == 100000", - // Switch Final Shield - Spirit - "[name] == monarch && [flag] == runeword # [fcr] >= 25 && [maxmana] >= 89 # [secondarytier] == 110000", - // Switch Temporary Shield - Any 1+ all skill - "[type] == shield # [itemallskills] >= 1 # [secondarytier] == tierscore(item, 50000)", - // Merc Armor - Fortitude - "[type] == armor && [flag] == runeword # [enhanceddefense] >= 200 && [enhanceddamage] >= 300 # [merctier] == 100000", - // Merc Final Helmet - Eth Andy's - "[name] == demonhead && [quality] == unique && [flag] == ethereal # [strength] >= 25 && [enhanceddefense] >= 100 # [merctier] == 50000 + mercscore(item)", - // Merc Helmet - Andy's - "[name] == demonhead && [quality] == unique && [flag] != ethereal # [strength] >= 25 && [enhanceddefense] >= 100 # [merctier] == 40000 + mercscore(item)", - ], - charms: { - ResLife: { - max: 3, - have: [], - classid: sdk.items.SmallCharm, - stats: function (check) { - return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MaxHp) === 20); - } - }, +(function (module) { + module.exports = (function () { + const build = { + caster: false, + skillstab: sdk.skills.tabs.ShadowDisciplines, + wantedskills: [sdk.skills.FireBlast, sdk.skills.LightningSentry, sdk.skills.DeathSentry, sdk.skills.ShadowMaster], + usefulskills: [sdk.skills.ChargedBoltSentry, sdk.skills.BladeShield, sdk.skills.Fade], + precastSkills: [sdk.skills.Fade, sdk.skills.ShadowMaster], + wantedMerc: MercData[sdk.skills.HolyFreeze], + stats: [ + ["strength", 130], ["dexterity", 99], ["vitality", "all"] + ], + skills: [ + [sdk.skills.Fade, 1], + [sdk.skills.Venom, 1], + [sdk.skills.MindBlast, 1], + [sdk.skills.BladeShield, 1], + [sdk.skills.ClawMastery, 20], + [sdk.skills.DeathSentry, 1], + [sdk.skills.ShadowMaster, 20, false], + [sdk.skills.Venom, 20, false], // lvl 77 w/o quest skill pts + [sdk.skills.Fade, 20, false], + [sdk.skills.BladeShield, 20], + ], - ResMf: { - max: 2, - have: [], - classid: sdk.items.SmallCharm, - stats: function (check) { - return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MagicBonus) === 7); - } - }, + charms: { + ResLife: { + max: 3, + have: [], + classid: sdk.items.SmallCharm, + stats: function (check) { + return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MaxHp) === 20); + } + }, - ResFHR: { - max: 1, - have: [], - classid: sdk.items.SmallCharm, - stats: function (check) { - return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.FHR) === 5); - } - }, + ResMf: { + max: 2, + have: [], + classid: sdk.items.SmallCharm, + stats: function (check) { + return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MagicBonus) === 7); + } + }, - LifeMana: { - max: 2, - have: [], - classid: sdk.items.SmallCharm, - stats: function (check) { - return (!check.unique && check.classid === this.classid && check.getStat(sdk.stats.MaxHp) === 20 && check.getStat(sdk.stats.MaxMana) === 17); - } - }, + ResFHR: { + max: 1, + have: [], + classid: sdk.items.SmallCharm, + stats: function (check) { + return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.FHR) === 5); + } + }, - Skiller: { - max: 1, - have: [], - classid: sdk.items.GrandCharm, - stats: function (check) { - return (!check.unique && check.classid === this.classid && check.getStat(sdk.stats.AddSkillTab, sdk.skills.tabs.ShadowDisciplines) === 1 - && check.getStat(sdk.stats.MaxHp) >= 40); - } - }, - }, + LifeMana: { + max: 2, + have: [], + classid: sdk.items.SmallCharm, + stats: function (check) { + return (!check.unique && check.classid === this.classid && check.getStat(sdk.stats.MaxHp) === 20 && check.getStat(sdk.stats.MaxMana) === 17); + } + }, - AutoBuildTemplate: { - 1: { - Update: function () { - Config.Dodge = false; - Config.UseVenom = true; - Config.UseTraps = true; - Config.AttackSkill = [-1, sdk.skills.Whirlwind, -1, sdk.skills.Whirlwind, -1, -1, -1]; - Config.Traps = [sdk.skills.DeathSentry, sdk.skills.DeathSentry, sdk.skills.DeathSentry, sdk.skills.DeathSentry, sdk.skills.DeathSentry]; - Config.BossTraps = [-1, -1, -1, -1, -1]; - } - }, - }, + Skiller: { + max: 1, + have: [], + classid: sdk.items.GrandCharm, + stats: function (check) { + return (!check.unique && check.classid === this.classid && check.getStat(sdk.stats.AddSkillTab, sdk.skills.tabs.ShadowDisciplines) === 1 + && check.getStat(sdk.stats.MaxHp) >= 40); + } + }, + }, - respec: function () { - return me.haveAll([{ name: sdk.locale.items.Chaos }, { name: sdk.locale.items.Fury }]); - }, + AutoBuildTemplate: { + 1: { + Update: function () { + Config.Dodge = false; + Config.UseVenom = true; + Config.UseTraps = true; + Config.AttackSkill = [-1, sdk.skills.Whirlwind, -1, sdk.skills.Whirlwind, -1, -1, -1]; + Config.Traps = [sdk.skills.DeathSentry, sdk.skills.DeathSentry, sdk.skills.DeathSentry, sdk.skills.DeathSentry, sdk.skills.DeathSentry]; + Config.BossTraps = [-1, -1, -1, -1, -1]; + } + }, + }, - active: function () { - return this.respec() && me.getSkill(sdk.skills.ClawMastery, sdk.skills.subindex.HardPoints) === 20; - }, -}; + respec: function () { + return me.haveAll([{ name: sdk.locale.items.Chaos }, { name: sdk.locale.items.Fury }]); + }, + + active: function () { + return this.respec() && me.getSkill(sdk.skills.ClawMastery, sdk.skills.subindex.HardPoints) === 20; + }, + }; + + let finalGear = [ // autoequip final gear + // Final Weapon - Chaos Claw & Fury + "[type] == assassinclaw && [flag] == runeword # [plusskillwhirlwind] == 1 # [tier] == 100000", + "[type] == assassinclaw && [flag] == runeword # [itemallskills] == 2 && [ias] == 40 && [itemdeadlystrike] == 33 # [tier] == 200000", + // Helmet - GFace + "[name] == wingedhelm && [quality] == set && [flag] != ethereal # [fhr] >= 30 # [tier] == tierscore(item, 100000)", + // Belt - Verdungos + "[name] == mithrilcoil && [quality] == unique && [flag] != ethereal # [damageresist] == 15 # [tier] == tierscore(item, 110000)", + // Boots - Gore Rider + "[name] == warboots && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 160 # [tier] == tierscore(item, 110000)", + // Armor - Fortitude + "[type] == armor && [flag] == runeword # [enhanceddefense] >= 200 && [enhanceddamage] >= 300 # [tier] == 100000", + // Gloves - Trang-Ouls + "[name] == heavybracers && [quality] == set && [flag] != ethereal # [fcr] == 20 # [tier] == 100000", // + // Amulet - Highlords + "[type] == amulet && [quality] == unique # [lightresist] == 35 # [tier] == 100000", + // Final Rings - Perfect Raven Frost & Bul-Kathos' Wedding Band + "[type] == ring && [quality] == unique # [dexterity] == 20 && [tohit] == 250 # [tier] == 110000", + "[type] == ring && [quality] == unique # [maxstamina] == 50 && [lifeleech] == 5 # [tier] == 110000", + // Rings - Raven Frost && Bul-Kathos' Wedding Band + "[type] == ring && [quality] == unique # [dexterity] >= 15 && [tohit] >= 150 # [tier] == 100000", + "[type] == ring && [quality] == unique # [maxstamina] == 50 && [lifeleech] >= 3 # [tier] == 100000", + // Switch Final Weapon - CTA + "[minimumsockets] >= 5 && [flag] == runeword # [plusskillbattleorders] >= 1 # [secondarytier] == 100000", + // Switch Final Shield - Spirit + "[name] == monarch && [flag] == runeword # [fcr] >= 25 && [maxmana] >= 89 # [secondarytier] == 110000", + // Switch Temporary Shield - Any 1+ all skill + "[type] == shield # [itemallskills] >= 1 # [secondarytier] == tierscore(item, 50000)", + // Merc Armor - Fortitude + "[type] == armor && [flag] == runeword # [enhanceddefense] >= 200 && [enhanceddamage] >= 300 # [merctier] == 100000", + // Merc Final Helmet - Eth Andy's + "[name] == demonhead && [quality] == unique && [flag] == ethereal # [strength] >= 25 && [enhanceddefense] >= 100 # [merctier] == 50000 + mercscore(item)", + // Merc Helmet - Andy's + "[name] == demonhead && [quality] == unique && [flag] != ethereal # [strength] >= 25 && [enhanceddefense] >= 100 # [merctier] == 40000 + mercscore(item)", + ]; + + NTIP.buildList(finalGear); + NTIP.buildFinalGear(finalGear); + + return build; + })(); +})(module); diff --git a/libs/SoloPlay/BuildFiles/barbarian/barbarian.FrenzyBuild.js b/libs/SoloPlay/BuildFiles/barbarian/barbarian.FrenzyBuild.js index 4a13668c..ee631d17 100644 --- a/libs/SoloPlay/BuildFiles/barbarian/barbarian.FrenzyBuild.js +++ b/libs/SoloPlay/BuildFiles/barbarian/barbarian.FrenzyBuild.js @@ -5,133 +5,142 @@ * */ -const finalBuild = { - caster: false, - skillstab: sdk.skills.tabs.BarbCombat, - wantedskills: [sdk.skills.BattleOrders, sdk.skills.Frenzy, sdk.skills.DoubleSwing], - usefulskills: [sdk.skills.NaturalResistance, sdk.skills.IronSkin, sdk.skills.IncreasedSpeed], - precastSkills: [sdk.skills.BattleOrders, sdk.skills.WarCry], - mercDiff: sdk.difficulty.Nightmare, - mercAct: 2, - mercAuraWanted: "Might", - stats: [ - ["strength", 103], ["dexterity", 79], ["vitality", 90], - ["dexterity", 136], ["strength", 150], ["vitality", "all"], - ], - skills: [ - [sdk.skills.DoubleSwing, 9, true], - [sdk.skills.SwordMastery, 6, false], - [sdk.skills.BattleCommand, 1, true], - [sdk.skills.WarCry, 1, true], - [sdk.skills.NaturalResistance, 5, true], - [sdk.skills.Berserk, 5, true], - [sdk.skills.Frenzy, 20, false], - [sdk.skills.BattleOrders, 20, false], // lvl 77 w/o quest skill pts - [sdk.skills.SwordMastery, 20, false], - [sdk.skills.DoubleSwing, 20, false], - ], - autoEquipTiers: [ // autoequip final gear - // Final Weapon - Grief & BoTD - "[name] == phaseblade && [flag] == runeword # [ias] >= 30 # [tier] == 100000", - "[name] == colossusblade && [flag] == runeword # [ias] >= 60 && [enhanceddamage] >= 350 # [tier] == 100000", - // Helmet - Arreat's Face - "[name] == slayerguard && [quality] == unique && [flag] != ethereal # [barbarianskills] == 2 # [tier] == tierscore(item, 100000)", - // Belt - Dungo's - "[name] == mithrilcoil && [quality] == unique && [flag] != ethereal # [damageresist] >= 10 && [vitality] >= 30 # [tier] == tierscore(item, 100000)", - // Boots - Gore Rider - "[name] == warboots && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 160 # [tier] == tierscore(item, 100000)", - // Armor - Fortitude - "[type] == armor && [flag] != ethereal && [flag] == runeword # [enhanceddefense] >= 200 && [enhanceddamage] >= 30 # [tier] == 100000", - // Gloves - Laying of Hands - "[name] == bramblemitts && [quality] == set && [flag] != ethereal # [ias] == 20 # [tier] == 100000", - // Amulet - Atma's - "[type] == amulet && [quality] == unique # [poisonresist] == 75 # [tier] == tierscore(item, 100000)", - // Final Rings - Perfect Raven Frost & Bul-Kathos' Wedding Band - "[type] == ring && [quality] == unique # [dexterity] == 20 && [tohit] == 250 # [tier] == 110000", - "[type] == ring && [quality] == unique # [maxstamina] == 50 && [lifeleech] == 5 # [tier] == 110000", - // Rings - Raven Frost && Bul-Kathos' Wedding Band - "[type] == ring && [quality] == unique # [dexterity] >= 15 && [tohit] >= 150 # [tier] == 100000", - "[type] == ring && [quality] == unique # [maxstamina] == 50 && [lifeleech] >= 3 # [tier] == 100000", - // Switch - BO Sticks - "([type] == club || [type] == sword || [type] == knife || [type] == throwingknife || [type] == mace) && ([quality] == magic || [flag] == runeword) && [2handed] == 0 # [itemallskills]+[warcriesskilltab]+[barbarianskills] >= 1 # [secondarytier] == 100000 + secondaryscore(item)", - // Merc Armor - Fortitude - "[type] == armor && [flag] == runeword # [enhanceddefense] >= 200 && [enhanceddamage] >= 300 # [merctier] == 100000", - // Merc Final Helmet - Eth Andy's - "[name] == demonhead && [quality] == unique && [flag] == ethereal # [strength] >= 25 && [enhanceddefense] >= 100 # [merctier] == 50000 + mercscore(item)", - // Merc Helmet - Andy's - "[name] == demonhead && [quality] == unique && [flag] != ethereal # [strength] >= 25 && [enhanceddefense] >= 100 # [merctier] == 40000 + mercscore(item)", - ], - charms: { - ResLife: { - max: 4, - have: [], - classid: sdk.items.SmallCharm, - stats: function (check) { - return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MaxHp) === 20); - } - }, +(function (module) { + module.exports = (function () { + const build = { + caster: false, + skillstab: sdk.skills.tabs.BarbCombat, + wantedskills: [sdk.skills.BattleOrders, sdk.skills.Frenzy, sdk.skills.DoubleSwing], + usefulskills: [sdk.skills.NaturalResistance, sdk.skills.IronSkin, sdk.skills.IncreasedSpeed], + precastSkills: [sdk.skills.BattleOrders, sdk.skills.WarCry], + wantedMerc: MercData[sdk.skills.Might], + stats: [ + ["strength", 103], ["dexterity", 79], ["vitality", 90], + ["dexterity", 136], ["strength", 150], ["vitality", "all"], + ], + skills: [ + [sdk.skills.DoubleSwing, 9, true], + [sdk.skills.SwordMastery, 6, false], + [sdk.skills.BattleCommand, 1, true], + [sdk.skills.WarCry, 1, true], + [sdk.skills.NaturalResistance, 5, true], + [sdk.skills.Berserk, 5, true], + [sdk.skills.Frenzy, 20, false], + [sdk.skills.BattleOrders, 20, false], // lvl 77 w/o quest skill pts + [sdk.skills.SwordMastery, 20, false], + [sdk.skills.DoubleSwing, 20, false], + ], - ResMf: { - max: 2, - have: [], - classid: sdk.items.SmallCharm, - stats: function (check) { - return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MagicBonus) === 7); - } - }, + charms: { + ResLife: { + max: 4, + have: [], + classid: sdk.items.SmallCharm, + stats: function (check) { + return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MaxHp) === 20); + } + }, - ResFHR: { - max: 2, - have: [], - classid: sdk.items.SmallCharm, - stats: function (check) { - return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.FHR) === 5); - } - }, + ResMf: { + max: 2, + have: [], + classid: sdk.items.SmallCharm, + stats: function (check) { + return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MagicBonus) === 7); + } + }, - SkillerCombat: { - max: 1, - have: [], - classid: sdk.items.GrandCharm, - stats: function (check) { - return (!check.unique && check.classid === this.classid && check.getStat(sdk.stats.AddSkillTab, sdk.skills.tabs.BarbCombat) === 1 - && check.getStat(sdk.stats.MaxHp) >= 40); - } - }, + ResFHR: { + max: 2, + have: [], + classid: sdk.items.SmallCharm, + stats: function (check) { + return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.FHR) === 5); + } + }, - SkillerMasteries: { - max: 1, - have: [], - classid: sdk.items.GrandCharm, - stats: function (check) { - return (!check.unique && check.classid === this.classid && check.getStat(sdk.stats.AddSkillTab, sdk.skills.tabs.Masteries) === 1 - && check.getStat(sdk.stats.MaxHp) >= 40); - } - }, - }, + SkillerCombat: { + max: 1, + have: [], + classid: sdk.items.GrandCharm, + stats: function (check) { + return (!check.unique && check.classid === this.classid && check.getStat(sdk.stats.AddSkillTab, sdk.skills.tabs.BarbCombat) === 1 + && check.getStat(sdk.stats.MaxHp) >= 40); + } + }, - AutoBuildTemplate: { - 1: { - Update: function () { - Config.AttackSkill = [sdk.skills.WarCry, sdk.skills.Frenzy, -1, sdk.skills.Frenzy, -1]; - Config.LowManaSkill = me.getSkill(sdk.skills.DoubleSwing, sdk.skills.subindex.SoftPoints) >= 9 ? [sdk.skills.DoubleSwing, 0] : [0, -1]; - Config.BeltColumn = ["hp", "hp", "mp", "rv"]; - Config.MPBuffer = 2; - Config.HPBuffer = 2; - } - }, - }, + SkillerMasteries: { + max: 1, + have: [], + classid: sdk.items.GrandCharm, + stats: function (check) { + return (!check.unique && check.classid === this.classid && check.getStat(sdk.stats.AddSkillTab, sdk.skills.tabs.Masteries) === 1 + && check.getStat(sdk.stats.MaxHp) >= 40); + } + }, + }, - respec: function () { - if (me.classic) { - return me.charlvl >= 75 && me.diablo; - } else { - return me.haveAll([{ name: sdk.locale.items.Grief }, { name: sdk.locale.items.BreathoftheDying }]); - } - }, + AutoBuildTemplate: { + 1: { + Update: function () { + Config.AttackSkill = [sdk.skills.WarCry, sdk.skills.Frenzy, -1, sdk.skills.Frenzy, -1]; + Config.LowManaSkill = me.getSkill(sdk.skills.DoubleSwing, sdk.skills.subindex.SoftPoints) >= 9 ? [sdk.skills.DoubleSwing, 0] : [0, -1]; + Config.BeltColumn = ["hp", "hp", "mp", "rv"]; + Config.MPBuffer = 2; + Config.HPBuffer = 2; + } + }, + }, - active: function () { - return this.respec() && me.getSkill(sdk.skills.Frenzy, sdk.skills.subindex.HardPoints) === 20; - }, -}; + respec: function () { + if (me.classic) { + return me.charlvl >= 75 && me.diablo; + } else { + return me.haveAll([{ name: sdk.locale.items.Grief }, { name: sdk.locale.items.BreathoftheDying }]); + } + }, + + active: function () { + return this.respec() && me.getSkill(sdk.skills.Frenzy, sdk.skills.subindex.HardPoints) === 20; + }, + }; + + let finalGear = [ // autoequip final gear + // Final Weapon - Grief & BoTD + "[name] == phaseblade && [flag] == runeword # [ias] >= 30 # [tier] == 100000", + "[name] == colossusblade && [flag] == runeword # [ias] >= 60 && [enhanceddamage] >= 350 # [tier] == 100000", + // Helmet - Arreat's Face + "[name] == slayerguard && [quality] == unique && [flag] != ethereal # [barbarianskills] == 2 # [tier] == tierscore(item, 100000)", + // Belt - Dungo's + "[name] == mithrilcoil && [quality] == unique && [flag] != ethereal # [damageresist] >= 10 && [vitality] >= 30 # [tier] == tierscore(item, 100000)", + // Boots - Gore Rider + "[name] == warboots && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 160 # [tier] == tierscore(item, 100000)", + // Armor - Fortitude + "[type] == armor && [flag] != ethereal && [flag] == runeword # [enhanceddefense] >= 200 && [enhanceddamage] >= 30 # [tier] == 100000", + // Gloves - Laying of Hands + "[name] == bramblemitts && [quality] == set && [flag] != ethereal # [ias] == 20 # [tier] == 100000", + // Amulet - Atma's + "[type] == amulet && [quality] == unique # [poisonresist] == 75 # [tier] == tierscore(item, 100000)", + // Final Rings - Perfect Raven Frost & Bul-Kathos' Wedding Band + "[type] == ring && [quality] == unique # [dexterity] == 20 && [tohit] == 250 # [tier] == 110000", + "[type] == ring && [quality] == unique # [maxstamina] == 50 && [lifeleech] == 5 # [tier] == 110000", + // Rings - Raven Frost && Bul-Kathos' Wedding Band + "[type] == ring && [quality] == unique # [dexterity] >= 15 && [tohit] >= 150 # [tier] == 100000", + "[type] == ring && [quality] == unique # [maxstamina] == 50 && [lifeleech] >= 3 # [tier] == 100000", + // Switch - BO Sticks + "([type] == club || [type] == sword || [type] == knife || [type] == throwingknife || [type] == mace) && ([quality] == magic || [flag] == runeword) && [2handed] == 0 # [itemallskills]+[warcriesskilltab]+[barbarianskills] >= 1 # [secondarytier] == 100000 + secondaryscore(item)", + // Merc Armor - Fortitude + "[type] == armor && [flag] == runeword # [enhanceddefense] >= 200 && [enhanceddamage] >= 300 # [merctier] == 100000", + // Merc Final Helmet - Eth Andy's + "[name] == demonhead && [quality] == unique && [flag] == ethereal # [strength] >= 25 && [enhanceddefense] >= 100 # [merctier] == 50000 + mercscore(item)", + // Merc Helmet - Andy's + "[name] == demonhead && [quality] == unique && [flag] != ethereal # [strength] >= 25 && [enhanceddefense] >= 100 # [merctier] == 40000 + mercscore(item)", + ]; + + NTIP.buildList(finalGear); + NTIP.buildFinalGear(finalGear); + + return build; + })(); +})(module); diff --git a/libs/SoloPlay/BuildFiles/barbarian/barbarian.ImmortalwhirlBuild.js b/libs/SoloPlay/BuildFiles/barbarian/barbarian.ImmortalwhirlBuild.js index 88945c89..98f7ab1a 100644 --- a/libs/SoloPlay/BuildFiles/barbarian/barbarian.ImmortalwhirlBuild.js +++ b/libs/SoloPlay/BuildFiles/barbarian/barbarian.ImmortalwhirlBuild.js @@ -5,126 +5,135 @@ * */ -const finalBuild = { - caster: false, - skillstab: sdk.skills.tabs.BarbCombat, - wantedskills: [sdk.skills.Bash, sdk.skills.Whirlwind], - usefulskills: [sdk.skills.Howl, sdk.skills.Shout], - precastSkills: [sdk.skills.BattleOrders, sdk.skills.WarCry], // Battle orders, War Cry - mercDiff: sdk.difficulty.Nightmare, - mercAct: 2, - mercAuraWanted: "Might", - stats: [ - ["strength", 232], ["vitality", "all"] - ], - skills: [ - [sdk.skills.MaceMastery, 20], - [sdk.skills.Whirlwind, 20], - [sdk.skills.Shout, 20], - [sdk.skills.BattleCry, 1], - [sdk.skills.BattleCommand, 1], - [sdk.skills.NaturalResistance, 1], - [sdk.skills.IncreasedSpeed, 1], - [sdk.skills.BattleOrders, 20], - ], - autoEquipTiers: [ // autoequip final gear - // Weapon - IK Maul - "[name] == ogremaul && [quality] == set # [enhanceddamage] >= 200 && [ias] >= 40 # [tier] == 110000", - // Helmet - IK Helm - "[name] == avengerguard && [quality] == set && [flag] != ethereal # [warcriesskilltab] == 2 # [tier] == 110000", - // Belt - IK Belt - "[name] == warbelt && [quality] == set && [flag] != ethereal # [strength] >= 25 && [fireresist] >= 28 # [tier] == 110000", - // Boots - IK Boots - "[name] == warboots && [quality] == set && [flag] != ethereal # [frw] >= 40 && [tohit] >= 110 # [tier] == 110000", - // Armor - IK Armor - "[name] == sacredarmor && [quality] == set && [flag] != ethereal # [barbcombatskilltab] == 2 # [tier] == 110000", - // Gloves - IK Gauntlets - "[name] == wargauntlets && [quality] == set && [flag] != ethereal # [strength] >= 20 && [dexterity] >= 20 # [tier] == 110000", - // Amulet - Metalgrid - "[type] == amulet && [quality] == unique # [defense] >= 300 # [tier] == tierscore(item, 110000)", - // Final Rings - Perfect Raven Frost & Bul-Kathos' Wedding Band - "[type] == ring && [quality] == unique # [dexterity] == 20 && [tohit] == 250 # [tier] == 110000", - "[type] == ring && [quality] == unique # [maxstamina] == 50 && [lifeleech] == 5 # [tier] == 110000", - // Rings - Raven Frost && Bul-Kathos' Wedding Band - "[type] == ring && [quality] == unique # [dexterity] >= 15 && [tohit] >= 150 # [tier] == 100000", - "[type] == ring && [quality] == unique # [maxstamina] == 50 && [lifeleech] >= 3 # [tier] == 100000", - // Switch - BO sticks - "([type] == club || [type] == sword || [type] == knife || [type] == throwingknife || [type] == mace) && ([quality] == magic || [flag] == runeword) && [2handed] == 0 # [itemallskills]+[warcriesskilltab]+[barbarianskills] >= 1 # [secondarytier] == 100000 + secondaryscore(item)", - // Merc Armor - Fortitude - "[type] == armor && [flag] == runeword # [enhanceddefense] >= 200 && [enhanceddamage] >= 300 # [merctier] == 100000", - // Merc Final Helmet - Eth Andy's - "[name] == demonhead && [quality] == unique && [flag] == ethereal # [strength] >= 25 && [enhanceddefense] >= 100 # [merctier] == 50000 + mercscore(item)", - // Merc Helmet - Andy's - "[name] == demonhead && [quality] == unique && [flag] != ethereal # [strength] >= 25 && [enhanceddefense] >= 100 # [merctier] == 40000 + mercscore(item)", - ], - charms: { - ResLife: { - max: 3, - have: [], - classid: sdk.items.SmallCharm, - stats: function (check) { - return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MaxHp) === 20); - } - }, +(function (module) { + module.exports = (function () { + const build = { + caster: false, + skillstab: sdk.skills.tabs.BarbCombat, + wantedskills: [sdk.skills.Bash, sdk.skills.Whirlwind], + usefulskills: [sdk.skills.Howl, sdk.skills.Shout], + precastSkills: [sdk.skills.BattleOrders, sdk.skills.WarCry], // Battle orders, War Cry + wantedMerc: MercData[sdk.skills.Might], + stats: [ + ["strength", 232], ["vitality", "all"] + ], + skills: [ + [sdk.skills.MaceMastery, 20], + [sdk.skills.Whirlwind, 20], + [sdk.skills.Shout, 20], + [sdk.skills.BattleCry, 1], + [sdk.skills.BattleCommand, 1], + [sdk.skills.NaturalResistance, 1], + [sdk.skills.IncreasedSpeed, 1], + [sdk.skills.BattleOrders, 20], + ], - ResMf: { - max: 2, - have: [], - classid: sdk.items.SmallCharm, - stats: function (check) { - return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MagicBonus) === 7); - } - }, + charms: { + ResLife: { + max: 3, + have: [], + classid: sdk.items.SmallCharm, + stats: function (check) { + return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MaxHp) === 20); + } + }, - ResFHR: { - max: 1, - have: [], - classid: sdk.items.SmallCharm, - stats: function (check) { - return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.FHR) === 5); - } - }, + ResMf: { + max: 2, + have: [], + classid: sdk.items.SmallCharm, + stats: function (check) { + return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MagicBonus) === 7); + } + }, - Skiller: { - max: 2, - have: [], - classid: sdk.items.GrandCharm, - stats: function (check) { - return (!check.unique && check.classid === this.classid && check.getStat(sdk.stats.AddSkillTab, sdk.skills.tabs.Masteries) === 1 - && check.getStat(sdk.stats.MaxHp) >= 40); - } - }, - }, + ResFHR: { + max: 1, + have: [], + classid: sdk.items.SmallCharm, + stats: function (check) { + return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.FHR) === 5); + } + }, - AutoBuildTemplate: { - 1: { - Update: function () { - Config.AttackSkill = [sdk.skills.BattleCry, sdk.skills.Whirlwind, -1, sdk.skills.Whirlwind, -1]; - Config.LowManaSkill = [0, -1]; - Config.BeltColumn = ["hp", "hp", "mp", "rv"]; - Config.MPBuffer = 2; - Config.HPBuffer = 2; - } - }, - }, + Skiller: { + max: 2, + have: [], + classid: sdk.items.GrandCharm, + stats: function (check) { + return (!check.unique && check.classid === this.classid && check.getStat(sdk.stats.AddSkillTab, sdk.skills.tabs.Masteries) === 1 + && check.getStat(sdk.stats.MaxHp) >= 40); + } + }, + }, - respec: function () { - if (me.classic) { - return false; - } else { - return me.haveAll([ - { name: sdk.locale.items.ImmortalKingsMaul, quality: sdk.items.quality.Set }, - { name: sdk.locale.items.ImmortalKingsBoots, quality: sdk.items.quality.Set }, - { name: sdk.locale.items.ImmortalKingsGloves, quality: sdk.items.quality.Set }, - { name: sdk.locale.items.ImmortalKingsBelt, quality: sdk.items.quality.Set }, - { name: sdk.locale.items.ImmortalKingsArmor, quality: sdk.items.quality.Set }, - { name: sdk.locale.items.ImmortalKingsHelmet, quality: sdk.items.quality.Set }, - ]); - } - }, + AutoBuildTemplate: { + 1: { + Update: function () { + Config.AttackSkill = [sdk.skills.BattleCry, sdk.skills.Whirlwind, -1, sdk.skills.Whirlwind, -1]; + Config.LowManaSkill = [0, -1]; + Config.BeltColumn = ["hp", "hp", "mp", "rv"]; + Config.MPBuffer = 2; + Config.HPBuffer = 2; + } + }, + }, - active: function () { - return this.respec() && me.getSkill(sdk.skills.MaceMastery, sdk.skills.subindex.HardPoints) === 20; - }, -}; + respec: function () { + if (me.classic) { + return false; + } else { + return me.haveAll([ + { name: sdk.locale.items.ImmortalKingsMaul, quality: sdk.items.quality.Set }, + { name: sdk.locale.items.ImmortalKingsBoots, quality: sdk.items.quality.Set }, + { name: sdk.locale.items.ImmortalKingsGloves, quality: sdk.items.quality.Set }, + { name: sdk.locale.items.ImmortalKingsBelt, quality: sdk.items.quality.Set }, + { name: sdk.locale.items.ImmortalKingsArmor, quality: sdk.items.quality.Set }, + { name: sdk.locale.items.ImmortalKingsHelmet, quality: sdk.items.quality.Set }, + ]); + } + }, + + active: function () { + return this.respec() && me.getSkill(sdk.skills.MaceMastery, sdk.skills.subindex.HardPoints) === 20; + }, + }; + + let finalGear = [ // autoequip final gear + // Weapon - IK Maul + "[name] == ogremaul && [quality] == set # [enhanceddamage] >= 200 && [ias] >= 40 # [tier] == 110000", + // Helmet - IK Helm + "[name] == avengerguard && [quality] == set && [flag] != ethereal # [warcriesskilltab] == 2 # [tier] == 110000", + // Belt - IK Belt + "[name] == warbelt && [quality] == set && [flag] != ethereal # [strength] >= 25 && [fireresist] >= 28 # [tier] == 110000", + // Boots - IK Boots + "[name] == warboots && [quality] == set && [flag] != ethereal # [frw] >= 40 && [tohit] >= 110 # [tier] == 110000", + // Armor - IK Armor + "[name] == sacredarmor && [quality] == set && [flag] != ethereal # [barbcombatskilltab] == 2 # [tier] == 110000", + // Gloves - IK Gauntlets + "[name] == wargauntlets && [quality] == set && [flag] != ethereal # [strength] >= 20 && [dexterity] >= 20 # [tier] == 110000", + // Amulet - Metalgrid + "[type] == amulet && [quality] == unique # [defense] >= 300 # [tier] == tierscore(item, 110000)", + // Final Rings - Perfect Raven Frost & Bul-Kathos' Wedding Band + "[type] == ring && [quality] == unique # [dexterity] == 20 && [tohit] == 250 # [tier] == 110000", + "[type] == ring && [quality] == unique # [maxstamina] == 50 && [lifeleech] == 5 # [tier] == 110000", + // Rings - Raven Frost && Bul-Kathos' Wedding Band + "[type] == ring && [quality] == unique # [dexterity] >= 15 && [tohit] >= 150 # [tier] == 100000", + "[type] == ring && [quality] == unique # [maxstamina] == 50 && [lifeleech] >= 3 # [tier] == 100000", + // Switch - BO sticks + "([type] == club || [type] == sword || [type] == knife || [type] == throwingknife || [type] == mace) && ([quality] == magic || [flag] == runeword) && [2handed] == 0 # [itemallskills]+[warcriesskilltab]+[barbarianskills] >= 1 # [secondarytier] == 100000 + secondaryscore(item)", + // Merc Armor - Fortitude + "[type] == armor && [flag] == runeword # [enhanceddefense] >= 200 && [enhanceddamage] >= 300 # [merctier] == 100000", + // Merc Final Helmet - Eth Andy's + "[name] == demonhead && [quality] == unique && [flag] == ethereal # [strength] >= 25 && [enhanceddefense] >= 100 # [merctier] == 50000 + mercscore(item)", + // Merc Helmet - Andy's + "[name] == demonhead && [quality] == unique && [flag] != ethereal # [strength] >= 25 && [enhanceddefense] >= 100 # [merctier] == 40000 + mercscore(item)", + ]; + + NTIP.buildList(finalGear); + NTIP.buildFinalGear(finalGear); + + return build; + })(); +})(module); diff --git a/libs/SoloPlay/BuildFiles/barbarian/barbarian.LevelingBuild.js b/libs/SoloPlay/BuildFiles/barbarian/barbarian.LevelingBuild.js index db266c8a..ad490f4c 100644 --- a/libs/SoloPlay/BuildFiles/barbarian/barbarian.LevelingBuild.js +++ b/libs/SoloPlay/BuildFiles/barbarian/barbarian.LevelingBuild.js @@ -6,50 +6,58 @@ * */ -let build = { - AutoBuildTemplate: {}, - caster: false, - skillstab: sdk.skills.tabs.BarbCombat, - wantedskills: [sdk.skills.BattleOrders, sdk.skills.Frenzy, sdk.skills.DoubleSwing, sdk.skills.SwordMastery], - usefulskills: [sdk.skills.NaturalResistance, sdk.skills.IronSkin, sdk.skills.IncreasedSpeed, sdk.skills.Shout, sdk.skills.FindItem], - mercDiff: sdk.difficulty.Nightmare, - mercAct: 2, - mercAuraWanted: "Might", - stats: [ - ["dexterity", 136], ["strength", 150], ["vitality", 125], - ["strength", 185], ["vitality", "all"], - ], - skills: [ - // Total points at time of respec 79 - [sdk.skills.SwordMastery, 11, true], // total left 68 - [sdk.skills.FindItem, 1, true], // total left 66 - [sdk.skills.DoubleSwing, 9, true], // total left 56 - [sdk.skills.NaturalResistance, 5, true], // total left 51 - [sdk.skills.Frenzy, 9, true], // total left 42 - [sdk.skills.Berserk, 5, true], // total left 35 - [sdk.skills.WarCry, 5, true], // total left 25 - [sdk.skills.BattleCommand, 1, true], // total left 24 - [sdk.skills.BattleOrders, 8, true], // total left 16 - [sdk.skills.Taunt, 16, true], // total left 0 - // End of respec points, Start of Leveling build - total points left to use 31 - [sdk.skills.Taunt, 20, false], // charlvl 75 -> total left 27 - [sdk.skills.BattleOrders, 10, false], // charlvl 77 -> total left 25 - [sdk.skills.SwordMastery, 20, false], // charlvl 84 -> total left 18 - [sdk.skills.Frenzy, 20, false], // total left 7 - [sdk.skills.BattleOrders, 15, false], // total left 0 - ], - active: function () { - return (me.charlvl > CharInfo.respecOne && me.charlvl > CharInfo.respecTwo && me.getSkill(sdk.skills.WarCry, sdk.skills.subindex.HardPoints) >= 5 && !Check.finalBuild().active()); - }, -}; +(function (module) { + module.exports = (function () { + const build = { + caster: false, + skillstab: sdk.skills.tabs.BarbCombat, + wantedskills: [sdk.skills.BattleOrders, sdk.skills.Frenzy, sdk.skills.DoubleSwing, sdk.skills.SwordMastery], + usefulskills: [sdk.skills.NaturalResistance, sdk.skills.IronSkin, sdk.skills.IncreasedSpeed, sdk.skills.Shout, sdk.skills.FindItem], + wantedMerc: MercData[sdk.skills.Might], + stats: [ + ["dexterity", 136], ["strength", 150], ["vitality", 125], + ["strength", 185], ["vitality", "all"], + ], + skills: [ + // Total points at time of respec 79 + [sdk.skills.SwordMastery, 11, true], // total left 68 + [sdk.skills.FindItem, 1, true], // total left 66 + [sdk.skills.DoubleSwing, 9, true], // total left 56 + [sdk.skills.NaturalResistance, 5, true], // total left 51 + [sdk.skills.Frenzy, 9, true], // total left 42 + [sdk.skills.Berserk, 5, true], // total left 35 + [sdk.skills.WarCry, 5, true], // total left 25 + [sdk.skills.BattleCommand, 1, true], // total left 24 + [sdk.skills.BattleOrders, 8, true], // total left 16 + [sdk.skills.Taunt, 16, true], // total left 0 + // End of respec points, Start of Leveling build - total points left to use 31 + [sdk.skills.Taunt, 20, false], // charlvl 75 -> total left 27 + [sdk.skills.BattleOrders, 10, false], // charlvl 77 -> total left 25 + [sdk.skills.SwordMastery, 20, false], // charlvl 84 -> total left 18 + [sdk.skills.Frenzy, 20, false], // total left 7 + [sdk.skills.BattleOrders, 15, false], // total left 0 + ], -build.AutoBuildTemplate[1] = buildAutoBuildTempObj(() => { - Config.AttackSkill = [-1, sdk.skills.Frenzy, sdk.skills.Berserk, sdk.skills.Frenzy, sdk.skills.Berserk]; - Config.LowManaSkill = [sdk.skills.DoubleSwing, 0]; - Config.BeltColumn = ["hp", "hp", "hp", "mp"]; - Config.TownHP = me.hardcore ? 0 : 35; - Config.MPBuffer = me.expansion ? 2 : 4; - Config.HPBuffer = me.expansion && me.charlvl < 80 ? 6 : me.classic ? 5 : 2; - SetUp.belt(); -}); + active: function () { + return (me.charlvl > CharInfo.respecOne && me.charlvl > CharInfo.respecTwo && me.getSkill(sdk.skills.WarCry, sdk.skills.subindex.HardPoints) >= 5 && !Check.finalBuild().active()); + }, + + AutoBuildTemplate: { + 1: { + Update: function () { + Config.AttackSkill = [-1, sdk.skills.Frenzy, sdk.skills.Berserk, sdk.skills.Frenzy, sdk.skills.Berserk]; + Config.LowManaSkill = [sdk.skills.DoubleSwing, 0]; + Config.BeltColumn = ["hp", "hp", "hp", "mp"]; + Config.TownHP = me.hardcore ? 0 : 35; + Config.MPBuffer = me.expansion ? 2 : 4; + Config.HPBuffer = me.expansion && me.charlvl < 80 ? 6 : me.classic ? 5 : 2; + SetUp.belt(); + } + } + }, + }; + + return build; + })(); +})(module); diff --git a/libs/SoloPlay/BuildFiles/barbarian/barbarian.SingerBuild.js b/libs/SoloPlay/BuildFiles/barbarian/barbarian.SingerBuild.js index 8e2460d1..6d5884a5 100644 --- a/libs/SoloPlay/BuildFiles/barbarian/barbarian.SingerBuild.js +++ b/libs/SoloPlay/BuildFiles/barbarian/barbarian.SingerBuild.js @@ -12,9 +12,7 @@ const finalBuild = { wantedskills: [sdk.skills.WarCry, sdk.skills.Shout], usefulskills: [sdk.skills.IncreasedSpeed, sdk.skills.NaturalResistance], precastSkills: [sdk.skills.BattleOrders, sdk.skills.BattleCommand], - mercDiff: sdk.difficulty.Nightmare, - mercAct: 2, - mercAuraWanted: "Might", + wantedMerc: MercData[sdk.skills.Might], stats: [ ["dexterity", 35], ["strength", 103], ["vitality", "all"] ], diff --git a/libs/SoloPlay/BuildFiles/barbarian/barbarian.StartBuild.js b/libs/SoloPlay/BuildFiles/barbarian/barbarian.StartBuild.js index 6da9b6cf..3f711f22 100644 --- a/libs/SoloPlay/BuildFiles/barbarian/barbarian.StartBuild.js +++ b/libs/SoloPlay/BuildFiles/barbarian/barbarian.StartBuild.js @@ -6,79 +6,86 @@ * */ -let build = { - AutoBuildTemplate: {}, - caster: false, - skillstab: sdk.skills.tabs.BarbCombat, - wantedskills: [sdk.skills.BattleOrders, sdk.skills.Frenzy, sdk.skills.DoubleSwing, sdk.skills.SwordMastery], - usefulskills: [sdk.skills.NaturalResistance, sdk.skills.IronSkin, sdk.skills.IncreasedSpeed, sdk.skills.Shout], - mercDiff: sdk.difficulty.Nightmare, - mercAct: 2, - mercAuraWanted: "Might", - stats: [ - ["strength", 35], ["dexterity", 27], ["vitality", 45], - ["strength", 48], ["dexterity", 30], ["vitality", 55], - ["strength", 55], ["dexterity", 39], ["vitality", 65], - ["strength", 60], ["dexterity", 40], ["vitality", 75], - ["strength", 71], ["dexterity", 49], ["vitality", "all"], - ], - skills: [ - [sdk.skills.Bash, 1], // charlevel 2 - [sdk.skills.Howl, 1], // charlevel 3 - [sdk.skills.DoubleSwing, 6, false], // charlevel 9 - [sdk.skills.SwordMastery, 5], // charlevel 13 - [sdk.skills.Taunt, 1], // charlevel 14 - [sdk.skills.SwordMastery, 6], // charlevel 15 - [sdk.skills.IronSkin, 1], // charlevel 18 - [sdk.skills.BattleCry, 1], // charlevel 18 - [sdk.skills.SwordMastery, 9], - [sdk.skills.DoubleThrow, 1], - [sdk.skills.Shout, 1], - [sdk.skills.Taunt, 3, false], - [sdk.skills.Frenzy, 1], - [sdk.skills.BattleOrders, 4, false], - [sdk.skills.Taunt, 20], - ], - active: function () { - return me.charlvl < CharInfo.respecOne && !me.checkSkill(sdk.skills.WarCry, sdk.skills.subindex.HardPoints); - }, -}; +(function (module, require) { + module.exports = (function () { + const build = { + AutoBuildTemplate: {}, + caster: false, + skillstab: sdk.skills.tabs.BarbCombat, + wantedskills: [sdk.skills.BattleOrders, sdk.skills.Frenzy, sdk.skills.DoubleSwing, sdk.skills.SwordMastery], + usefulskills: [sdk.skills.NaturalResistance, sdk.skills.IronSkin, sdk.skills.IncreasedSpeed, sdk.skills.Shout], + wantedMerc: MercData[sdk.skills.Might], + stats: [ + ["strength", 35], ["dexterity", 27], ["vitality", 45], + ["strength", 48], ["dexterity", 30], ["vitality", 55], + ["strength", 55], ["dexterity", 39], ["vitality", 65], + ["strength", 60], ["dexterity", 40], ["vitality", 75], + ["strength", 71], ["dexterity", 49], ["vitality", "all"], + ], + skills: [ + [sdk.skills.Bash, 1], // charlevel 2 + [sdk.skills.Howl, 1], // charlevel 3 + [sdk.skills.DoubleSwing, 6, false], // charlevel 9 + [sdk.skills.SwordMastery, 5], // charlevel 13 + [sdk.skills.Taunt, 1], // charlevel 14 + [sdk.skills.SwordMastery, 6], // charlevel 15 + [sdk.skills.IronSkin, 1], // charlevel 18 + [sdk.skills.BattleCry, 1], // charlevel 18 + [sdk.skills.SwordMastery, 9], + [sdk.skills.DoubleThrow, 1], + [sdk.skills.Shout, 1], + [sdk.skills.Taunt, 3, false], + [sdk.skills.Frenzy, 1], + [sdk.skills.BattleOrders, 4, false], + [sdk.skills.Taunt, 20], + ], -build.AutoBuildTemplate[1] = buildAutoBuildTempObj(() => { - Config.ScanShrines.indexOf(sdk.shrines.Combat) === -1 && Config.ScanShrines.push(sdk.shrines.Combat); - Config.FieldID.Enabled = !Misc.checkQuest(sdk.quest.id.TheSearchForCain, sdk.quest.states.Completed); - Config.FieldID.UsedSpace = 0; + active: function () { + return me.charlvl < CharInfo.respecOne && !me.checkSkill(sdk.skills.WarCry, sdk.skills.subindex.HardPoints); + }, + }; - Config.TownHP = me.hardcore ? 0 : 35; - Config.BeltColumn = ["hp", "hp", "hp", "hp"]; - Config.MPBuffer = 4; - Config.HPBuffer = 6; - Config.AttackSkill = [-1, 0, 0, 0, 0]; - Config.LowManaSkill = [0, 0]; - SetUp.belt(); -}); -build.AutoBuildTemplate[2] = buildAutoBuildTempObj(() => { - if (me.checkSkill(sdk.skills.Bash, sdk.skills.subindex.HardPoints)) { - Config.AttackSkill = [-1, sdk.skills.Bash, -1, sdk.skills.Attack, -1]; - } -}); -build.AutoBuildTemplate[6] = buildAutoBuildTempObj(() => { - Config.AttackSkill = [-1, sdk.skills.Bash, 0, 0, 0]; -}); -build.AutoBuildTemplate[12] = buildAutoBuildTempObj(() => { - Config.BeltColumn = me.charlvl < 13 ? ["hp", "hp", "hp", "mp"] : ["hp", "hp", "mp", "mp"]; - Config.HPBuffer = me.expansion ? 2 : 4; - Config.MPBuffer = 6; - Config.AttackSkill = [-1, sdk.skills.DoubleSwing, -1, sdk.skills.DoubleSwing, -1]; + const { buildAutoBuildTempObj } = require("../../Utils/General"); + + build.AutoBuildTemplate[1] = buildAutoBuildTempObj(() => { + Config.ScanShrines.indexOf(sdk.shrines.Combat) === -1 && Config.ScanShrines.push(sdk.shrines.Combat); + Config.FieldID.Enabled = !Misc.checkQuest(sdk.quest.id.TheSearchForCain, sdk.quest.states.Completed); + Config.FieldID.UsedSpace = 0; - if (me.getSkill(sdk.skills.DoubleSwing, sdk.skills.subindex.SoftPoints) >= 9) { - Config.LowManaSkill = [sdk.skills.DoubleSwing, 0]; - } - SetUp.belt(); -}); -build.AutoBuildTemplate[24] = buildAutoBuildTempObj(() => { - if (me.checkSkill(sdk.skills.Frenzy, sdk.skills.subindex.HardPoints)) { - Config.AttackSkill = [-1, sdk.skills.Frenzy, -1, sdk.skills.Frenzy, -1]; - } -}); + Config.TownHP = me.hardcore ? 0 : 35; + Config.BeltColumn = ["hp", "hp", "hp", "hp"]; + Config.MPBuffer = 4; + Config.HPBuffer = 6; + Config.AttackSkill = [-1, 0, 0, 0, 0]; + Config.LowManaSkill = [0, 0]; + SetUp.belt(); + }); + build.AutoBuildTemplate[2] = buildAutoBuildTempObj(() => { + if (me.checkSkill(sdk.skills.Bash, sdk.skills.subindex.HardPoints)) { + Config.AttackSkill = [-1, sdk.skills.Bash, -1, sdk.skills.Attack, -1]; + } + }); + build.AutoBuildTemplate[6] = buildAutoBuildTempObj(() => { + Config.AttackSkill = [-1, sdk.skills.Bash, 0, 0, 0]; + }); + build.AutoBuildTemplate[12] = buildAutoBuildTempObj(() => { + Config.BeltColumn = me.charlvl < 13 ? ["hp", "hp", "hp", "mp"] : ["hp", "hp", "mp", "mp"]; + Config.HPBuffer = me.expansion ? 2 : 4; + Config.MPBuffer = 6; + Config.AttackSkill = [-1, sdk.skills.DoubleSwing, -1, sdk.skills.DoubleSwing, -1]; + + if (me.getSkill(sdk.skills.DoubleSwing, sdk.skills.subindex.SoftPoints) >= 9) { + Config.LowManaSkill = [sdk.skills.DoubleSwing, 0]; + } + SetUp.belt(); + }); + build.AutoBuildTemplate[24] = buildAutoBuildTempObj(() => { + if (me.checkSkill(sdk.skills.Frenzy, sdk.skills.subindex.HardPoints)) { + Config.AttackSkill = [-1, sdk.skills.Frenzy, -1, sdk.skills.Frenzy, -1]; + } + }); + + return build; + })(); +})(module, require); diff --git a/libs/SoloPlay/BuildFiles/barbarian/barbarian.SteppingBuild.js b/libs/SoloPlay/BuildFiles/barbarian/barbarian.SteppingBuild.js index 84faa37e..2be47518 100644 --- a/libs/SoloPlay/BuildFiles/barbarian/barbarian.SteppingBuild.js +++ b/libs/SoloPlay/BuildFiles/barbarian/barbarian.SteppingBuild.js @@ -6,69 +6,76 @@ * */ -let build = { - AutoBuildTemplate: {}, - caster: false, - skillstab: sdk.skills.tabs.BarbCombat, - wantedskills: [sdk.skills.BattleOrders, sdk.skills.Frenzy, sdk.skills.DoubleSwing, sdk.skills.SwordMastery], - usefulskills: [sdk.skills.NaturalResistance, sdk.skills.IronSkin, sdk.skills.IncreasedSpeed, sdk.skills.Shout, sdk.skills.FindItem], - mercDiff: sdk.difficulty.Nightmare, - mercAct: 2, - mercAuraWanted: "Might", - stats: [ - ["strength", 71], ["dexterity", 50], ["vitality", 100], - ["strength", 85], ["dexterity", 60], ["vitality", 110], - ["strength", 103], ["dexterity", 79], ["vitality", 125], - ["dexterity", 94], ["strength", 125], ["vitality", 130], - ["strength", 140], ["vitality", 135], ["strength", 150], - ["vitality", "all"], - ], - skills: [ - // Total points at time of respec 33 - [sdk.skills.SwordMastery, 9, true], // total left 24 - [sdk.skills.FindItem, 1, true], // total left 22 - [sdk.skills.BattleOrders, 4, true], // total left 16 - [sdk.skills.BattleCommand, 1, true], // total left 15 - [sdk.skills.NaturalResistance, 1, true], // total left 13 - [sdk.skills.Frenzy, 2, true], // total left 8 - [sdk.skills.WarCry, 1, true], // total left 5 - [sdk.skills.DoubleSwing, 5, true], // total left 1 - // End of respec points, Start of Stepping build - [sdk.skills.NaturalResistance, 2, false], // charlvl 31 - [sdk.skills.WarCry, 2, false], // charlvl 32 - [sdk.skills.NaturalResistance, 3, false], // charlvl 33 - [sdk.skills.WarCry, 3, false], // charlvl 34 - [sdk.skills.Taunt, 11, false], // charlvl 45 - [sdk.skills.NaturalResistance, 4, false], // charlvl 46 - [sdk.skills.Frenzy, 6, false], // charlvl 50 - [sdk.skills.WarCry, 5, false], // charlvl 52 - [sdk.skills.Frenzy, 9, false], // charlvl 53 - [sdk.skills.BattleOrders, 6, false], // charlvl 54 - [sdk.skills.NaturalResistance, 5, false], // charlvl 56 - [sdk.skills.WarCry, 6, false], // charlvl 59 - [sdk.skills.SwordMastery, 20, false], // charlvl 67 - [sdk.skills.Taunt, 20, false], // charlvl 76 - ], - active: function () { - return me.charlvl > CharInfo.respecOne && me.charlvl < CharInfo.respecTwo && me.checkSkill(sdk.skills.NaturalResistance, sdk.skills.subindex.HardPoints) && !me.checkSkill(sdk.skills.Berserk, sdk.skills.subindex.HardPoints); - }, -}; +(function (module, require) { + module.exports = (function () { + const build = { + AutoBuildTemplate: {}, + caster: false, + skillstab: sdk.skills.tabs.BarbCombat, + wantedskills: [sdk.skills.BattleOrders, sdk.skills.Frenzy, sdk.skills.DoubleSwing, sdk.skills.SwordMastery], + usefulskills: [sdk.skills.NaturalResistance, sdk.skills.IronSkin, sdk.skills.IncreasedSpeed, sdk.skills.Shout, sdk.skills.FindItem], + wantedMerc: MercData[sdk.skills.Might], + stats: [ + ["strength", 71], ["dexterity", 50], ["vitality", 100], + ["strength", 85], ["dexterity", 60], ["vitality", 110], + ["strength", 103], ["dexterity", 79], ["vitality", 125], + ["dexterity", 94], ["strength", 125], ["vitality", 130], + ["strength", 140], ["vitality", 135], ["strength", 150], + ["vitality", "all"], + ], + skills: [ + // Total points at time of respec 33 + [sdk.skills.SwordMastery, 9, true], // total left 24 + [sdk.skills.FindItem, 1, true], // total left 22 + [sdk.skills.BattleOrders, 4, true], // total left 16 + [sdk.skills.BattleCommand, 1, true], // total left 15 + [sdk.skills.NaturalResistance, 1, true], // total left 13 + [sdk.skills.Frenzy, 2, true], // total left 8 + [sdk.skills.WarCry, 1, true], // total left 5 + [sdk.skills.DoubleSwing, 5, true], // total left 1 + // End of respec points, Start of Stepping build + [sdk.skills.NaturalResistance, 2, false], // charlvl 31 + [sdk.skills.WarCry, 2, false], // charlvl 32 + [sdk.skills.NaturalResistance, 3, false], // charlvl 33 + [sdk.skills.WarCry, 3, false], // charlvl 34 + [sdk.skills.Taunt, 11, false], // charlvl 45 + [sdk.skills.NaturalResistance, 4, false], // charlvl 46 + [sdk.skills.Frenzy, 6, false], // charlvl 50 + [sdk.skills.WarCry, 5, false], // charlvl 52 + [sdk.skills.Frenzy, 9, false], // charlvl 53 + [sdk.skills.BattleOrders, 6, false], // charlvl 54 + [sdk.skills.NaturalResistance, 5, false], // charlvl 56 + [sdk.skills.WarCry, 6, false], // charlvl 59 + [sdk.skills.SwordMastery, 20, false], // charlvl 67 + [sdk.skills.Taunt, 20, false], // charlvl 76 + ], -build.AutoBuildTemplate[1] = buildAutoBuildTempObj(() => { - Config.AttackSkill = [-1, sdk.skills.DoubleSwing, -1, sdk.skills.DoubleSwing, -1]; - if (me.getSkill(sdk.skills.DoubleSwing, sdk.skills.subindex.SoftPoints) >= 9) { - Config.LowManaSkill = [sdk.skills.DoubleSwing, 0]; - } - SetUp.belt(); -}); -build.AutoBuildTemplate[24] = buildAutoBuildTempObj(() => { - if (me.checkSkill(sdk.skills.Frenzy, sdk.skills.subindex.HardPoints)) { - Config.AttackSkill = [-1, sdk.skills.Frenzy, -1, sdk.skills.Frenzy, -1]; - } - Config.BeltColumn = ["hp", "hp", "hp", "mp"]; - Config.TownHP = me.hardcore ? 0 : 35; - Config.MPBuffer = me.expansion ? 2 : 4; - Config.HPBuffer = me.expansion && me.charlvl < 80 ? 6 : me.classic ? 5 : 2; - SetUp.belt(); -}); + active: function () { + return me.charlvl > CharInfo.respecOne && me.charlvl < CharInfo.respecTwo && me.checkSkill(sdk.skills.NaturalResistance, sdk.skills.subindex.HardPoints) && !me.checkSkill(sdk.skills.Berserk, sdk.skills.subindex.HardPoints); + }, + }; + + const { buildAutoBuildTempObj } = require("../../Utils/General"); + + build.AutoBuildTemplate[1] = buildAutoBuildTempObj(() => { + Config.AttackSkill = [-1, sdk.skills.DoubleSwing, -1, sdk.skills.DoubleSwing, -1]; + if (me.getSkill(sdk.skills.DoubleSwing, sdk.skills.subindex.SoftPoints) >= 9) { + Config.LowManaSkill = [sdk.skills.DoubleSwing, 0]; + } + SetUp.belt(); + }); + build.AutoBuildTemplate[24] = buildAutoBuildTempObj(() => { + if (me.checkSkill(sdk.skills.Frenzy, sdk.skills.subindex.HardPoints)) { + Config.AttackSkill = [-1, sdk.skills.Frenzy, -1, sdk.skills.Frenzy, -1]; + } + Config.BeltColumn = ["hp", "hp", "hp", "mp"]; + Config.TownHP = me.hardcore ? 0 : 35; + Config.MPBuffer = me.expansion ? 2 : 4; + Config.HPBuffer = me.expansion && me.charlvl < 80 ? 6 : me.classic ? 5 : 2; + SetUp.belt(); + }); + + return build; + })(); +})(module, require); diff --git a/libs/SoloPlay/BuildFiles/barbarian/barbarian.ThrowBuild.js b/libs/SoloPlay/BuildFiles/barbarian/barbarian.ThrowBuild.js index 2bcf5100..7663170f 100644 --- a/libs/SoloPlay/BuildFiles/barbarian/barbarian.ThrowBuild.js +++ b/libs/SoloPlay/BuildFiles/barbarian/barbarian.ThrowBuild.js @@ -5,125 +5,134 @@ * */ -const finalBuild = { - caster: false, - skillstab: sdk.skills.tabs.BarbCombat, - wantedskills: [sdk.skills.BattleOrders, sdk.skills.DoubleThrow, sdk.skills.DoubleSwing], - usefulskills: [sdk.skills.NaturalResistance, sdk.skills.IronSkin, sdk.skills.IncreasedSpeed], - precastSkills: [sdk.skills.BattleOrders, sdk.skills.BattleCommand], - mercDiff: sdk.difficulty.Nightmare, - mercAct: 2, - mercAuraWanted: "Holy Freeze", - stats: [ - ["strength", 103], ["dexterity", 79], ["vitality", 90], - ["dexterity", 136], ["strength", 150], ["vitality", "all"], - ], - skills: [ - [sdk.skills.DoubleThrow, 20], - [sdk.skills.Howl, 9], - [sdk.skills.BattleOrders, 20], - [sdk.skills.BattleCommand, 1], - [sdk.skills.ThrowMastery, 20], - [sdk.skills.NaturalResistance, 5], - [sdk.skills.DoubleSwing, 20], - [sdk.skills.Frenzy, 1], - [sdk.skills.Berserk, 1], - [sdk.skills.Howl, 20], - ], - autoEquipTiers: [ // autoequip final gear - // Final Weapon - Perfect Eth Lacerator & Warshrike - "[name] == wingedknife && [quality] == unique && [flag] == ethereal # [ias] == 30 && [enhanceddamage] == 250 # [tier] == 110000", - "[name] == wingedaxe && [quality] == unique && [flag] == ethereal # [ias] == 30 && [enhanceddamage] == 210 # [tier] == 110000", - // Weapon - Eth Lacerator & Warshrike - "[name] == wingedknife && [quality] == unique && [flag] == ethereal # [ias] == 30 && [enhanceddamage] >= 200 # [tier] == 100000", - "[name] == wingedaxe && [quality] == unique && [flag] == ethereal # [ias] == 30 && [enhanceddamage] >= 150 # [tier] == 100000", - // Helmet - Arreat's Face - "[name] == slayerguard && [quality] == unique && [flag] != ethereal # [barbarianskills] == 2 # [tier] == 100000", - // Belt- Arach's - "[name] == spiderwebsash && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 90 # [tier] == 100000", - // Boots - IK Boots - "[name] == warboots && [quality] == set && [flag] != ethereal # [frw] >= 40 && [tohit] >= 110 # [tier] == 110000", - // Armor - Enigma - "[type] == armor && [flag] != ethereal && [flag] == runeword # [itemallskills] == 2 # [tier] == 100000", - // Gloves - Laying of Hands - "[name] == bramblemitts && [quality] == set && [flag] != ethereal # [ias] == 20 # [tier] == 100000", - // Amulet - Highlords - "[type] == amulet && [quality] == unique # [lightresist] == 35 # [tier] == 100000", - // Final Rings - Perfect Raven Frost & Bul-Kathos' Wedding Band - "[type] == ring && [quality] == unique # [dexterity] == 20 && [tohit] == 250 # [tier] == 110000", - "[type] == ring && [quality] == unique # [maxstamina] == 50 && [lifeleech] == 5 # [tier] == 110000", - // Rings - Raven Frost && Bul-Kathos' Wedding Band - "[type] == ring && [quality] == unique # [dexterity] >= 15 && [tohit] >= 150 # [tier] == 100000", - "[type] == ring && [quality] == unique # [maxstamina] == 50 && [lifeleech] >= 3 # [tier] == 100000", - // Switch - BO sticks - "([type] == club || [type] == sword || [type] == knife || [type] == throwingknife || [type] == mace) && ([quality] == magic || [flag] == runeword) && [2handed] == 0 # [itemallskills]+[warcriesskilltab]+[barbarianskills] >= 1 # [secondarytier] == 100000 + secondaryscore(item)", - // Merc Armor - Fortitude - "[type] == armor && [flag] == runeword # [enhanceddefense] >= 200 && [enhanceddamage] >= 300 # [merctier] == 100000", - // Merc Final Helmet - Eth Andy's - "[name] == demonhead && [quality] == unique && [flag] == ethereal # [strength] >= 25 && [enhanceddefense] >= 100 # [merctier] == 50000 + mercscore(item)", - // Merc Helmet - Andy's - "[name] == demonhead && [quality] == unique && [flag] != ethereal # [strength] >= 25 && [enhanceddefense] >= 100 # [merctier] == 40000 + mercscore(item)", - ], - charms: { - ResLife: { - max: 4, - have: [], - classid: sdk.items.SmallCharm, - stats: function (check) { - return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MaxHp) === 20); - } - }, +(function (module) { + module.exports = (function () { + const build = { + caster: false, + skillstab: sdk.skills.tabs.BarbCombat, + wantedskills: [sdk.skills.BattleOrders, sdk.skills.DoubleThrow, sdk.skills.DoubleSwing], + usefulskills: [sdk.skills.NaturalResistance, sdk.skills.IronSkin, sdk.skills.IncreasedSpeed], + precastSkills: [sdk.skills.BattleOrders, sdk.skills.BattleCommand], + wantedMerc: MercData[sdk.skills.HolyFreeze], + stats: [ + ["strength", 103], ["dexterity", 79], ["vitality", 90], + ["dexterity", 136], ["strength", 150], ["vitality", "all"], + ], + skills: [ + [sdk.skills.DoubleThrow, 20], + [sdk.skills.Howl, 9], + [sdk.skills.BattleOrders, 20], + [sdk.skills.BattleCommand, 1], + [sdk.skills.ThrowMastery, 20], + [sdk.skills.NaturalResistance, 5], + [sdk.skills.DoubleSwing, 20], + [sdk.skills.Frenzy, 1], + [sdk.skills.Berserk, 1], + [sdk.skills.Howl, 20], + ], - ResFHR: { - max: 4, - have: [], - classid: sdk.items.SmallCharm, - stats: function (check) { - return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.FHR) === 5); - } - }, + charms: { + ResLife: { + max: 4, + have: [], + classid: sdk.items.SmallCharm, + stats: function (check) { + return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MaxHp) === 20); + } + }, - SkillerCombat: { - max: 1, - have: [], - classid: sdk.items.GrandCharm, - stats: function (check) { - return (!check.unique && check.classid === this.classid && check.getStat(sdk.stats.AddSkillTab, sdk.skills.tabs.BarbCombat) === 1 - && check.getStat(sdk.stats.MaxHp) >= 40); - } - }, + ResFHR: { + max: 4, + have: [], + classid: sdk.items.SmallCharm, + stats: function (check) { + return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.FHR) === 5); + } + }, - SkillerMasteries: { - max: 1, - have: [], - classid: sdk.items.GrandCharm, - stats: function (check) { - return (!check.unique && check.classid === this.classid && check.getStat(sdk.stats.AddSkillTab, sdk.skills.tabs.Masteries) === 1 - && check.getStat(sdk.stats.MaxHp) >= 40); - } - }, - }, + SkillerCombat: { + max: 1, + have: [], + classid: sdk.items.GrandCharm, + stats: function (check) { + return (!check.unique && check.classid === this.classid && check.getStat(sdk.stats.AddSkillTab, sdk.skills.tabs.BarbCombat) === 1 + && check.getStat(sdk.stats.MaxHp) >= 40); + } + }, - AutoBuildTemplate: { - 1: { - Update: function () { - Config.AttackSkill = [-1, sdk.skills.DoubleThrow, sdk.skills.Frenzy, sdk.skills.DoubleThrow, sdk.skills.Berserk]; - Config.LowManaSkill = [sdk.skills.DoubleSwing, sdk.skills.DoubleSwing]; - Config.BeltColumn = ["hp", "hp", "mp", "rv"]; - } - }, - }, + SkillerMasteries: { + max: 1, + have: [], + classid: sdk.items.GrandCharm, + stats: function (check) { + return (!check.unique && check.classid === this.classid && check.getStat(sdk.stats.AddSkillTab, sdk.skills.tabs.Masteries) === 1 + && check.getStat(sdk.stats.MaxHp) >= 40); + } + }, + }, - respec: function () { - if (me.classic) { - return false; - } else { - return Check.haveItem("throwingknife", "unique", "Warshrike") && Check.haveItem("throwingaxe", "unique", "Lacerator"); - } - }, + AutoBuildTemplate: { + 1: { + Update: function () { + Config.AttackSkill = [-1, sdk.skills.DoubleThrow, sdk.skills.Frenzy, sdk.skills.DoubleThrow, sdk.skills.Berserk]; + Config.LowManaSkill = [sdk.skills.DoubleSwing, sdk.skills.DoubleSwing]; + Config.BeltColumn = ["hp", "hp", "mp", "rv"]; + } + }, + }, - active: function () { - return this.respec() && me.getSkill(sdk.skills.DoubleThrow, sdk.skills.subindex.HardPoints) === 20; - }, -}; + respec: function () { + if (me.classic) { + return false; + } else { + return Check.haveItem("throwingknife", "unique", "Warshrike") && Check.haveItem("throwingaxe", "unique", "Lacerator"); + } + }, + + active: function () { + return this.respec() && me.getSkill(sdk.skills.DoubleThrow, sdk.skills.subindex.HardPoints) === 20; + }, + }; + + let finalGear = [ // autoequip final gear + // Final Weapon - Perfect Eth Lacerator & Warshrike + "[name] == wingedknife && [quality] == unique && [flag] == ethereal # [ias] == 30 && [enhanceddamage] == 250 # [tier] == 110000", + "[name] == wingedaxe && [quality] == unique && [flag] == ethereal # [ias] == 30 && [enhanceddamage] == 210 # [tier] == 110000", + // Weapon - Eth Lacerator & Warshrike + "[name] == wingedknife && [quality] == unique && [flag] == ethereal # [ias] == 30 && [enhanceddamage] >= 200 # [tier] == 100000", + "[name] == wingedaxe && [quality] == unique && [flag] == ethereal # [ias] == 30 && [enhanceddamage] >= 150 # [tier] == 100000", + // Helmet - Arreat's Face + "[name] == slayerguard && [quality] == unique && [flag] != ethereal # [barbarianskills] == 2 # [tier] == 100000", + // Belt- Arach's + "[name] == spiderwebsash && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 90 # [tier] == 100000", + // Boots - IK Boots + "[name] == warboots && [quality] == set && [flag] != ethereal # [frw] >= 40 && [tohit] >= 110 # [tier] == 110000", + // Armor - Enigma + "[type] == armor && [flag] != ethereal && [flag] == runeword # [itemallskills] == 2 # [tier] == 100000", + // Gloves - Laying of Hands + "[name] == bramblemitts && [quality] == set && [flag] != ethereal # [ias] == 20 # [tier] == 100000", + // Amulet - Highlords + "[type] == amulet && [quality] == unique # [lightresist] == 35 # [tier] == 100000", + // Final Rings - Perfect Raven Frost & Bul-Kathos' Wedding Band + "[type] == ring && [quality] == unique # [dexterity] == 20 && [tohit] == 250 # [tier] == 110000", + "[type] == ring && [quality] == unique # [maxstamina] == 50 && [lifeleech] == 5 # [tier] == 110000", + // Rings - Raven Frost && Bul-Kathos' Wedding Band + "[type] == ring && [quality] == unique # [dexterity] >= 15 && [tohit] >= 150 # [tier] == 100000", + "[type] == ring && [quality] == unique # [maxstamina] == 50 && [lifeleech] >= 3 # [tier] == 100000", + // Switch - BO sticks + "([type] == club || [type] == sword || [type] == knife || [type] == throwingknife || [type] == mace) && ([quality] == magic || [flag] == runeword) && [2handed] == 0 # [itemallskills]+[warcriesskilltab]+[barbarianskills] >= 1 # [secondarytier] == 100000 + secondaryscore(item)", + // Merc Armor - Fortitude + "[type] == armor && [flag] == runeword # [enhanceddefense] >= 200 && [enhanceddamage] >= 300 # [merctier] == 100000", + // Merc Final Helmet - Eth Andy's + "[name] == demonhead && [quality] == unique && [flag] == ethereal # [strength] >= 25 && [enhanceddefense] >= 100 # [merctier] == 50000 + mercscore(item)", + // Merc Helmet - Andy's + "[name] == demonhead && [quality] == unique && [flag] != ethereal # [strength] >= 25 && [enhanceddefense] >= 100 # [merctier] == 40000 + mercscore(item)", + ]; + + NTIP.buildList(finalGear); + NTIP.buildFinalGear(finalGear); + + return build; + })(); +})(module); diff --git a/libs/SoloPlay/BuildFiles/barbarian/barbarian.UberconcBuild.js b/libs/SoloPlay/BuildFiles/barbarian/barbarian.UberconcBuild.js index 8ce5c680..5f430c6c 100644 --- a/libs/SoloPlay/BuildFiles/barbarian/barbarian.UberconcBuild.js +++ b/libs/SoloPlay/BuildFiles/barbarian/barbarian.UberconcBuild.js @@ -5,111 +5,120 @@ * */ -const finalBuild = { - caster: false, - skillstab: sdk.skills.tabs.BarbCombat, - wantedskills: [sdk.skills.BattleOrders, sdk.skills.Concentrate], - usefulskills: [sdk.skills.SwordMastery, sdk.skills.Bash], - precastSkills: [sdk.skills.BattleOrders, sdk.skills.BattleCommand], - mercDiff: sdk.difficulty.Nightmare, - mercAct: 2, - mercAuraWanted: "Might", - stats: [ - ["strength", 196], ["dexterity", "block"], ["vitality", "all"], - ], - skills: [ - [sdk.skills.BattleCommand, 1], - [sdk.skills.NaturalResistance, 1], - [sdk.skills.WarCry, 1], - [sdk.skills.Berserk, 1], - [sdk.skills.BattleOrders, 20, false], - [sdk.skills.SwordMastery, 10, false], - [sdk.skills.Concentrate, 20, false], - [sdk.skills.Shout, 20, false], - [sdk.skills.LeapAttack, 1, false], - [sdk.skills.Bash, 20, false], - [sdk.skills.SwordMastery, 20], - ], - autoEquipTiers: [ // autoequip final gear - // Weapon - Grief - "[type] == sword && [flag] == runeword # [ias] >= 30 && [itemdeadlystrike] == 20 && [passivepoispierce] >= 20 # [tier] == 100000", - // Helmet - Arreat's Face - "[name] == slayerguard && [quality] == unique && [flag] != ethereal # [barbarianskills] == 2 # [tier] == tierscore(item, 100000)", - // Belt - Dungo's - "[name] == mithrilcoil && [quality] == unique && [flag] != ethereal # [damageresist] >= 10 && [vitality] >= 30 # [tier] == tierscore(item, 100000)", - // Boots - Gore Rider's - "[name] == warboots && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 160 # [tier] == tierscore(item, 100000)", - // Armor - CoH - "[type] == armor && [flag] == runeword && [flag] != ethereal # [fireresist] == 65 && [hpregen] == 7 # [tier] == 100000", - // Gloves - Drac's - "[name] == vampirebonegloves && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 100 && [strength] >= 12 && [lifeleech] >= 9 # [tier] == tierscore(item, 100000)", - // Amulet - Highlords - "[type] == amulet && [quality] == unique # [lightresist] == 35 # [tier] == 100000", - // Final Rings - Perfect Raven Frost & Bul-Kathos' Wedding Band - "[type] == ring && [quality] == unique # [dexterity] == 20 && [tohit] == 250 # [tier] == 110000", - "[name] == ring && [quality] == unique # [maxstamina] == 50 && [lifeleech] == 5 # [tier] == 110000", - // Rings - Raven Frost && Bul-Kathos' Wedding Band - "[type] == ring && [quality] == unique # [dexterity] >= 15 && [tohit] >= 150 # [tier] == 100000", - "[name] == ring && [quality] == unique # [maxstamina] == 50 && [lifeleech] >= 3 # [tier] == 100000", - // Switch - BO sticks - "([type] == club || [type] == sword || [type] == knife || [type] == throwingknife || [type] == mace) && ([quality] == magic || [flag] == runeword) && [2handed] == 0 # [itemallskills]+[warcriesskilltab]+[barbarianskills] >= 1 # [secondarytier] == 100000 + secondaryscore(item)", - // Merc Armor - Fortitude - "[type] == armor && [flag] == runeword # [enhanceddefense] >= 200 && [enhanceddamage] >= 300 # [merctier] == 100000", - // Merc Final Helmet - Eth Andy's - "[name] == demonhead && [quality] == unique && [flag] == ethereal # [strength] >= 25 && [enhanceddefense] >= 100 # [merctier] == 50000 + mercscore(item)", - // Merc Helmet - Andy's - "[name] == demonhead && [quality] == unique && [flag] != ethereal # [strength] >= 25 && [enhanceddefense] >= 100 # [merctier] == 40000 + mercscore(item)", - ], - charms: { - ResLife: { - max: 4, - have: [], - classid: sdk.items.SmallCharm, - stats: function (check) { - return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MaxHp) === 20); - } - }, +(function (module, require) { + module.exports = (function () { + const build = { + caster: false, + skillstab: sdk.skills.tabs.BarbCombat, + wantedskills: [sdk.skills.BattleOrders, sdk.skills.Concentrate], + usefulskills: [sdk.skills.SwordMastery, sdk.skills.Bash], + precastSkills: [sdk.skills.BattleOrders, sdk.skills.BattleCommand], + wantedMerc: MercData[sdk.skills.Might], + stats: [ + ["strength", 196], ["dexterity", "block"], ["vitality", "all"], + ], + skills: [ + [sdk.skills.BattleCommand, 1], + [sdk.skills.NaturalResistance, 1], + [sdk.skills.WarCry, 1], + [sdk.skills.Berserk, 1], + [sdk.skills.BattleOrders, 20, false], + [sdk.skills.SwordMastery, 10, false], + [sdk.skills.Concentrate, 20, false], + [sdk.skills.Shout, 20, false], + [sdk.skills.LeapAttack, 1, false], + [sdk.skills.Bash, 20, false], + [sdk.skills.SwordMastery, 20], + ], - ResFHR: { - max: 4, - have: [], - classid: sdk.items.SmallCharm, - stats: function (check) { - return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.FHR) === 5); - } - }, + charms: { + ResLife: { + max: 4, + have: [], + classid: sdk.items.SmallCharm, + stats: function (check) { + return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MaxHp) === 20); + } + }, - Skiller: { - max: 2, - have: [], - classid: sdk.items.GrandCharm, - stats: function (check) { - return (!check.unique && check.classid === this.classid && check.getStat(sdk.stats.AddSkillTab, sdk.skills.tabs.BarbCombat) === 1 - && check.getStat(sdk.stats.MaxHp) >= 40); - } - }, - }, + ResFHR: { + max: 4, + have: [], + classid: sdk.items.SmallCharm, + stats: function (check) { + return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.FHR) === 5); + } + }, - AutoBuildTemplate: { - 1: { - Update: function () { - Config.AttackSkill = [-1, sdk.skills.Concentrate, sdk.skills.Berserk, sdk.skills.Concentrate, sdk.skills.Berserk]; - Config.LowManaSkill = [0, 0]; - Config.BeltColumn = ["hp", "hp", "mp", "rv"]; - } - }, - }, + Skiller: { + max: 2, + have: [], + classid: sdk.items.GrandCharm, + stats: function (check) { + return (!check.unique && check.classid === this.classid && check.getStat(sdk.stats.AddSkillTab, sdk.skills.tabs.BarbCombat) === 1 + && check.getStat(sdk.stats.MaxHp) >= 40); + } + }, + }, - respec: function () { - if (me.classic) { - return me.charlvl >= 75 && me.diablo; - } else { - return me.checkItem({ name: sdk.locale.items.Grief, itemtype: sdk.items.type.Sword }).have && Check.haveItem("monarch", "unique", "Stormshield"); - } - }, + AutoBuildTemplate: { + 1: { + Update: function () { + Config.AttackSkill = [-1, sdk.skills.Concentrate, sdk.skills.Berserk, sdk.skills.Concentrate, sdk.skills.Berserk]; + Config.LowManaSkill = [0, 0]; + Config.BeltColumn = ["hp", "hp", "mp", "rv"]; + } + }, + }, - active: function () { - return this.respec() && me.getSkill(sdk.skills.Concentrate, sdk.skills.subindex.HardPoints) >= 5; - }, -}; + respec: function () { + if (me.classic) { + return me.charlvl >= 75 && me.diablo; + } else { + return me.checkItem({ name: sdk.locale.items.Grief, itemtype: sdk.items.type.Sword }).have && Check.haveItem("monarch", "unique", "Stormshield"); + } + }, + + active: function () { + return this.respec() && me.getSkill(sdk.skills.Concentrate, sdk.skills.subindex.HardPoints) >= 5; + }, + }; + + let finalGear = [ // autoequip final gear + // Weapon - Grief + "[type] == sword && [flag] == runeword # [ias] >= 30 && [itemdeadlystrike] == 20 && [passivepoispierce] >= 20 # [tier] == 100000", + // Helmet - Arreat's Face + "[name] == slayerguard && [quality] == unique && [flag] != ethereal # [barbarianskills] == 2 # [tier] == tierscore(item, 100000)", + // Belt - Dungo's + "[name] == mithrilcoil && [quality] == unique && [flag] != ethereal # [damageresist] >= 10 && [vitality] >= 30 # [tier] == tierscore(item, 100000)", + // Boots - Gore Rider's + "[name] == warboots && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 160 # [tier] == tierscore(item, 100000)", + // Armor - CoH + "[type] == armor && [flag] == runeword && [flag] != ethereal # [fireresist] == 65 && [hpregen] == 7 # [tier] == 100000", + // Gloves - Drac's + "[name] == vampirebonegloves && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 100 && [strength] >= 12 && [lifeleech] >= 9 # [tier] == tierscore(item, 100000)", + // Amulet - Highlords + "[type] == amulet && [quality] == unique # [lightresist] == 35 # [tier] == 100000", + // Final Rings - Perfect Raven Frost & Bul-Kathos' Wedding Band + "[type] == ring && [quality] == unique # [dexterity] == 20 && [tohit] == 250 # [tier] == 110000", + "[name] == ring && [quality] == unique # [maxstamina] == 50 && [lifeleech] == 5 # [tier] == 110000", + // Rings - Raven Frost && Bul-Kathos' Wedding Band + "[type] == ring && [quality] == unique # [dexterity] >= 15 && [tohit] >= 150 # [tier] == 100000", + "[name] == ring && [quality] == unique # [maxstamina] == 50 && [lifeleech] >= 3 # [tier] == 100000", + // Switch - BO sticks + "([type] == club || [type] == sword || [type] == knife || [type] == throwingknife || [type] == mace) && ([quality] == magic || [flag] == runeword) && [2handed] == 0 # [itemallskills]+[warcriesskilltab]+[barbarianskills] >= 1 # [secondarytier] == 100000 + secondaryscore(item)", + // Merc Armor - Fortitude + "[type] == armor && [flag] == runeword # [enhanceddefense] >= 200 && [enhanceddamage] >= 300 # [merctier] == 100000", + // Merc Final Helmet - Eth Andy's + "[name] == demonhead && [quality] == unique && [flag] == ethereal # [strength] >= 25 && [enhanceddefense] >= 100 # [merctier] == 50000 + mercscore(item)", + // Merc Helmet - Andy's + "[name] == demonhead && [quality] == unique && [flag] != ethereal # [strength] >= 25 && [enhanceddefense] >= 100 # [merctier] == 40000 + mercscore(item)", + ]; + + NTIP.buildList(finalGear); + NTIP.buildFinalGear(finalGear); + + return build; + })(); +})(module, require); diff --git a/libs/SoloPlay/BuildFiles/barbarian/barbarian.WhirlwindBuild.js b/libs/SoloPlay/BuildFiles/barbarian/barbarian.WhirlwindBuild.js index 76c505dc..46d52db6 100644 --- a/libs/SoloPlay/BuildFiles/barbarian/barbarian.WhirlwindBuild.js +++ b/libs/SoloPlay/BuildFiles/barbarian/barbarian.WhirlwindBuild.js @@ -5,124 +5,133 @@ * */ -const finalBuild = { - caster: false, - skillstab: sdk.skills.tabs.BarbCombat, - wantedskills: [sdk.skills.Bash, sdk.skills.Whirlwind], - usefulskills: [sdk.skills.Howl, sdk.skills.Shout], - precastSkills: [sdk.skills.BattleOrders, sdk.skills.BattleCommand], - mercDiff: sdk.difficulty.Nightmare, - mercAct: 2, - mercAuraWanted: "Might", - stats: [ - ["strength", 118], ["dexterity", 136], ["vitality", "all"] - ], - skills: [ - [sdk.skills.Whirlwind, 20, true], - [sdk.skills.SwordMastery, 20, true], - [sdk.skills.NaturalResistance, 5, true], - [sdk.skills.BattleCommand, 1, true], - [sdk.skills.Berserk, 5, true], - [sdk.skills.IncreasedSpeed, 1, true], - [sdk.skills.WarCry, 5, true], - [sdk.skills.BattleOrders, 20, true], - [sdk.skills.Shout, 20, true], - [sdk.skills.IronSkin, 3, true], - ], - autoEquipTiers: [ // autoequip final gear - // Weapon - Grief x2 dual wield - "[type] == sword && [flag] == runeword # [ias] >= 30 && [itemdeadlystrike] == 20 && [passivepoispierce] >= 20 # [tier] == 100000", - // Final Helmet - Upp'ed Arreat's Face - "[name] == guardiancrown && [quality] == unique && [flag] != ethereal # [barbarianskills] == 2 && [fhr] >= 30 # [tier] == tierscore(item, 150000)", - // Helmet - Arreat's Face - "[name] == slayerguard && [quality] == unique && [flag] != ethereal # [barbarianskills] == 2 && [fhr] >= 30 # [tier] == tierscore(item, 100000)", - // Belt - Dungo's - "[name] == mithrilcoil && [quality] == unique && [flag] != ethereal # [damageresist] >= 10 && [vitality] >= 30 # [tier] == tierscore(item, 100000)", - // Boots - Gore Rider - "[name] == warboots && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 160 # [tier] == tierscore(item, 100000)", - // Armor - Fortitude - "[type] == armor && [flag] != ethereal && [flag] == runeword # [enhanceddefense] >= 200 && [enhanceddamage] >= 30 # [tier] == 100000", - // Gloves - Laying of Hands - "[name] == bramblemitts && [quality] == set && [flag] != ethereal # [ias] == 20 # [tier] == 100000", - // Amulet - Atma's - "[type] == amulet && [quality] == unique # [poisonresist] == 75 # [tier] == tierscore(item, 100000)", - // Final Rings - Perfect Raven Frost & Bul-Kathos' Wedding Band - "[type] == ring && [quality] == unique # [dexterity] == 20 && [tohit] == 250 # [tier] == 110000", - "[type] == ring && [quality] == unique # [maxstamina] == 50 && [lifeleech] == 5 # [tier] == 110000", - // Rings - Raven Frost && Bul-Kathos' Wedding Band - "[type] == ring && [quality] == unique # [dexterity] >= 15 && [tohit] >= 150 # [tier] == 100000", - "[type] == ring && [quality] == unique # [maxstamina] == 50 && [lifeleech] >= 3 # [tier] == 100000", - // Switch - BO sticks - "([type] == club || [type] == sword || [type] == knife || [type] == throwingknife || [type] == mace) && ([quality] == magic || [flag] == runeword) && [2handed] == 0 # [itemallskills]+[warcriesskilltab]+[barbarianskills] >= 1 # [secondarytier] == 100000 + secondaryscore(item)", - // Merc Armor - Fortitude - "[type] == armor && [flag] == runeword # [enhanceddefense] >= 200 && [enhanceddamage] >= 300 # [merctier] == 100000", - // Merc Final Helmet - Eth Andy's - "[name] == demonhead && [quality] == unique && [flag] == ethereal # [strength] >= 25 && [enhanceddefense] >= 100 # [merctier] == 50000 + mercscore(item)", - // Merc Helmet - Andy's - "[name] == demonhead && [quality] == unique && [flag] != ethereal # [strength] >= 25 && [enhanceddefense] >= 100 # [merctier] == 40000 + mercscore(item)", - ], - charms: { - ResLife: { - max: 4, - have: [], - classid: sdk.items.SmallCharm, - stats: function (check) { - return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MaxHp) === 20); - } - }, +(function (module) { + module.exports = (function () { + const build = { + caster: false, + skillstab: sdk.skills.tabs.BarbCombat, + wantedskills: [sdk.skills.Bash, sdk.skills.Whirlwind], + usefulskills: [sdk.skills.Howl, sdk.skills.Shout], + precastSkills: [sdk.skills.BattleOrders, sdk.skills.BattleCommand], + wantedMerc: MercData[sdk.skills.Might], + stats: [ + ["strength", 118], ["dexterity", 136], ["vitality", "all"] + ], + skills: [ + [sdk.skills.Whirlwind, 20, true], + [sdk.skills.SwordMastery, 20, true], + [sdk.skills.NaturalResistance, 5, true], + [sdk.skills.BattleCommand, 1, true], + [sdk.skills.Berserk, 5, true], + [sdk.skills.IncreasedSpeed, 1, true], + [sdk.skills.WarCry, 5, true], + [sdk.skills.BattleOrders, 20, true], + [sdk.skills.Shout, 20, true], + [sdk.skills.IronSkin, 3, true], + ], - ResMf: { - max: 2, - have: [], - classid: sdk.items.SmallCharm, - stats: function (check) { - return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MagicBonus) === 7); - } - }, + charms: { + ResLife: { + max: 4, + have: [], + classid: sdk.items.SmallCharm, + stats: function (check) { + return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MaxHp) === 20); + } + }, - ResFHR: { - max: 2, - have: [], - classid: sdk.items.SmallCharm, - stats: function (check) { - return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.FHR) === 5); - } - }, + ResMf: { + max: 2, + have: [], + classid: sdk.items.SmallCharm, + stats: function (check) { + return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MagicBonus) === 7); + } + }, - Skiller: { - max: 2, - have: [], - classid: sdk.items.GrandCharm, - stats: function (check) { - return (!check.unique && check.classid === this.classid && check.getStat(sdk.stats.AddSkillTab, sdk.skills.tabs.Masteries) === 1 - && check.getStat(sdk.stats.MaxHp) >= 40); - } - }, - }, + ResFHR: { + max: 2, + have: [], + classid: sdk.items.SmallCharm, + stats: function (check) { + return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.FHR) === 5); + } + }, - AutoBuildTemplate: { - 1: { - Update: function () { - Config.AttackSkill = [sdk.skills.BattleCry, sdk.skills.Whirlwind, -1, sdk.skills.Whirlwind, -1]; - Config.LowManaSkill = [0, -1]; - Config.BeltColumn = ["hp", "hp", "mp", "rv"]; - Config.MPBuffer = 2; - Config.HPBuffer = 2; - } - }, - }, + Skiller: { + max: 2, + have: [], + classid: sdk.items.GrandCharm, + stats: function (check) { + return (!check.unique && check.classid === this.classid && check.getStat(sdk.stats.AddSkillTab, sdk.skills.tabs.Masteries) === 1 + && check.getStat(sdk.stats.MaxHp) >= 40); + } + }, + }, - respec: function () { - if (me.classic) { - return me.charlvl >= 75 && me.diablo; - } else { - // TODO: figure out how to make sure we have two, or determine if that even matters - return me.checkItem({ name: sdk.locale.items.Grief, itemtype: sdk.items.type.Sword }).have; - } - }, + AutoBuildTemplate: { + 1: { + Update: function () { + Config.AttackSkill = [sdk.skills.BattleCry, sdk.skills.Whirlwind, -1, sdk.skills.Whirlwind, -1]; + Config.LowManaSkill = [0, -1]; + Config.BeltColumn = ["hp", "hp", "mp", "rv"]; + Config.MPBuffer = 2; + Config.HPBuffer = 2; + } + }, + }, - active: function () { - return this.respec() && me.getSkill(sdk.skills.Whirlwind, sdk.skills.subindex.HardPoints) === 20; - }, -}; + respec: function () { + if (me.classic) { + return me.charlvl >= 75 && me.diablo; + } else { + // TODO: figure out how to make sure we have two, or determine if that even matters + return me.checkItem({ name: sdk.locale.items.Grief, itemtype: sdk.items.type.Sword }).have; + } + }, + + active: function () { + return this.respec() && me.getSkill(sdk.skills.Whirlwind, sdk.skills.subindex.HardPoints) === 20; + }, + }; + + let finalGear = [ // autoequip final gear + // Weapon - Grief x2 dual wield + "[type] == sword && [flag] == runeword # [ias] >= 30 && [itemdeadlystrike] == 20 && [passivepoispierce] >= 20 # [tier] == 100000", + // Final Helmet - Upp'ed Arreat's Face + "[name] == guardiancrown && [quality] == unique && [flag] != ethereal # [barbarianskills] == 2 && [fhr] >= 30 # [tier] == tierscore(item, 150000)", + // Helmet - Arreat's Face + "[name] == slayerguard && [quality] == unique && [flag] != ethereal # [barbarianskills] == 2 && [fhr] >= 30 # [tier] == tierscore(item, 100000)", + // Belt - Dungo's + "[name] == mithrilcoil && [quality] == unique && [flag] != ethereal # [damageresist] >= 10 && [vitality] >= 30 # [tier] == tierscore(item, 100000)", + // Boots - Gore Rider + "[name] == warboots && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 160 # [tier] == tierscore(item, 100000)", + // Armor - Fortitude + "[type] == armor && [flag] != ethereal && [flag] == runeword # [enhanceddefense] >= 200 && [enhanceddamage] >= 30 # [tier] == 100000", + // Gloves - Laying of Hands + "[name] == bramblemitts && [quality] == set && [flag] != ethereal # [ias] == 20 # [tier] == 100000", + // Amulet - Atma's + "[type] == amulet && [quality] == unique # [poisonresist] == 75 # [tier] == tierscore(item, 100000)", + // Final Rings - Perfect Raven Frost & Bul-Kathos' Wedding Band + "[type] == ring && [quality] == unique # [dexterity] == 20 && [tohit] == 250 # [tier] == 110000", + "[type] == ring && [quality] == unique # [maxstamina] == 50 && [lifeleech] == 5 # [tier] == 110000", + // Rings - Raven Frost && Bul-Kathos' Wedding Band + "[type] == ring && [quality] == unique # [dexterity] >= 15 && [tohit] >= 150 # [tier] == 100000", + "[type] == ring && [quality] == unique # [maxstamina] == 50 && [lifeleech] >= 3 # [tier] == 100000", + // Switch - BO sticks + "([type] == club || [type] == sword || [type] == knife || [type] == throwingknife || [type] == mace) && ([quality] == magic || [flag] == runeword) && [2handed] == 0 # [itemallskills]+[warcriesskilltab]+[barbarianskills] >= 1 # [secondarytier] == 100000 + secondaryscore(item)", + // Merc Armor - Fortitude + "[type] == armor && [flag] == runeword # [enhanceddefense] >= 200 && [enhanceddamage] >= 300 # [merctier] == 100000", + // Merc Final Helmet - Eth Andy's + "[name] == demonhead && [quality] == unique && [flag] == ethereal # [strength] >= 25 && [enhanceddefense] >= 100 # [merctier] == 50000 + mercscore(item)", + // Merc Helmet - Andy's + "[name] == demonhead && [quality] == unique && [flag] != ethereal # [strength] >= 25 && [enhanceddefense] >= 100 # [merctier] == 40000 + mercscore(item)", + ]; + + NTIP.buildList(finalGear); + NTIP.buildFinalGear(finalGear); + + return build; + })(); +})(module); diff --git a/libs/SoloPlay/BuildFiles/druid/druid.ElementalBuild.js b/libs/SoloPlay/BuildFiles/druid/druid.ElementalBuild.js index 9ba135d0..2f6bdb58 100644 --- a/libs/SoloPlay/BuildFiles/druid/druid.ElementalBuild.js +++ b/libs/SoloPlay/BuildFiles/druid/druid.ElementalBuild.js @@ -5,124 +5,169 @@ * */ -const finalBuild = { - caster: true, - skillstab: sdk.skills.tabs.Elemental, - wantedskills: [sdk.skills.Firestorm, sdk.skills.Fissure], - usefulskills: [sdk.skills.CycloneArmor], - precastSkills: [sdk.skills.CycloneArmor], - usefulStats: [sdk.stats.PassiveFireMastery, sdk.stats.PassiveFirePierce], - mercDiff: sdk.difficulty.Nightmare, - mercAct: 2, - mercAuraWanted: "Holy Freeze", - stats: [ - ["dexterity", 35], ["strength", 48], ["vitality", 165], - ["strength", 61], ["vitality", 252], ["strength", 156], ["vitality", "all"] - ], - skills: [ - [sdk.skills.OakSage, 6, false], - [sdk.skills.Fissure, 11, false], - [sdk.skills.Grizzly, 1, false], - [sdk.skills.Volcano, 1, false], - [sdk.skills.Fissure, 20, false], - [sdk.skills.CycloneArmor, 1, false], - [sdk.skills.Firestorm, 20, false], - [sdk.skills.Volcano, 20, false], - [sdk.skills.OakSage, 20, false], - [sdk.skills.CycloneArmor, 20, false], - [sdk.skills.Grizzly, 5, false], - ], - autoEquipTiers: [ // autoequip final gear - // Weapon - HotO - "[type] == mace && [flag] == runeword # [itemallskills] == 3 # [tier] == 100000", - // Helmet - Ravenlore - "[name] == skyspirit && [quality] == unique # [passivefirepierce] >= 10 # [tier] == tierscore(item, 100000)", - // Belt - Arach's - "[name] == spiderwebsash && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 90 # [tier] == tierscore(item, 100000)", - // Final Boots - Sandstorm Treks - "[name] == scarabshellboots && [quality] == unique # [strength]+[vitality] >= 20 # [tier] == tierscore(item, 100000)", - // Boots - War Traveler - "[name] == battleboots && [quality] == unique && [flag] != ethereal # [itemmagicbonus] >= 50 # [tier] == tierscore(item, 5000)", - // Armor - Enigma - "[type] == armor && [flag] != ethereal && [flag] == runeword # [itemallskills] == 2 # [tier] == 100000", - // Shield - Phoenix - "[name] == monarch && [flag] != ethereal && [flag] == runeword # [passivefirepierce] >= 28 # [tier] == tierscore(item, 100000)", - // Final Gloves - Perfect 2x Upp'ed Magefist - "[name] == crusadergauntlets && [quality] == unique && [flag] != ethereal # [enhanceddefense] == 30 && [addfireskills] == 1 # [tier] == 110000", - // Gloves - 2x Upp'ed Magefist - "[name] == crusadergauntlets && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 20 && [addfireskills] == 1 # [tier] == tierscore(item, 100000)", - // Gloves - Upp'ed Magefist - "[name] == battlegauntlets && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 20 && [addfireskills] == 1 # [tier] == tierscore(item, 100000)", - // Gloves - Magefist - "[name] == lightgauntlets && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 20 && [addfireskills] == 1 # [tier] == tierscore(item, 100000)", - // Amulet - Maras - "[type] == amulet && [quality] == unique # [strength] == 5 && [coldresist] >= 30 # [tier] == tierscore(item, 100000)", - // Rings - SoJ - "[type] == ring && [quality] == unique # [itemmaxmanapercent] == 25 # [tier] == 100000", - // Switch - CTA - "[minimumsockets] >= 5 && [flag] == runeword # [plusskillbattleorders] >= 1 # [secondarytier] == 100000", - // Merc Armor - Fortitude - "[type] == armor && [flag] == runeword # [enhanceddefense] >= 200 && [enhanceddamage] >= 300 # [merctier] == 100000", - // Merc Final Helmet - Eth Andy's - "[name] == demonhead && [quality] == unique && [flag] == ethereal # [strength] >= 25 && [enhanceddefense] >= 100 # [merctier] == 50000 + mercscore(item)", - // Merc Helmet - Andy's - "[name] == demonhead && [quality] == unique && [flag] != ethereal # [strength] >= 25 && [enhanceddefense] >= 100 # [merctier] == 40000 + mercscore(item)", - ], - charms: { - ResLife: { - max: 3, - have: [], - classid: sdk.items.SmallCharm, - stats: function (check) { - return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MaxHp) === 20); - } - }, +(function (module) { + module.exports = (function () { + const build = { + caster: true, + skillstab: sdk.skills.tabs.Elemental, + wantedskills: [sdk.skills.Firestorm, sdk.skills.Fissure], + usefulskills: [sdk.skills.CycloneArmor], + precastSkills: [sdk.skills.CycloneArmor], + usefulStats: [sdk.stats.PassiveFireMastery, sdk.stats.PassiveFirePierce], + wantedMerc: MercData[sdk.skills.HolyFreeze], + stats: [ + ["dexterity", 35], ["strength", 48], ["vitality", 165], + ["strength", 61], ["vitality", 252], ["strength", 156], ["vitality", "all"] + ], + skills: [ + [sdk.skills.OakSage, 6, false], + [sdk.skills.Fissure, 11, false], + [sdk.skills.Grizzly, 1, false], + [sdk.skills.Volcano, 1, false], + [sdk.skills.Fissure, 20, false], + [sdk.skills.CycloneArmor, 1, false], + [sdk.skills.Firestorm, 20, false], + [sdk.skills.Volcano, 20, false], + [sdk.skills.OakSage, 20, false], + [sdk.skills.CycloneArmor, 20, false], + [sdk.skills.Grizzly, 5, false], + ], + autoEquipTiers: [ // autoequip final gear + // Weapon - HotO + "[type] == mace && [flag] == runeword # [itemallskills] == 3 # [tier] == 100000", + // Helmet - Ravenlore + "[name] == skyspirit && [quality] == unique # [passivefirepierce] >= 10 # [tier] == tierscore(item, 100000)", + // Belt - Arach's + "[name] == spiderwebsash && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 90 # [tier] == tierscore(item, 100000)", + // Final Boots - Sandstorm Treks + "[name] == scarabshellboots && [quality] == unique # [strength]+[vitality] >= 20 # [tier] == tierscore(item, 100000)", + // Boots - War Traveler + "[name] == battleboots && [quality] == unique && [flag] != ethereal # [itemmagicbonus] >= 50 # [tier] == tierscore(item, 5000)", + // Armor - Enigma + "[type] == armor && [flag] != ethereal && [flag] == runeword # [itemallskills] == 2 # [tier] == 100000", + // Shield - Phoenix + "[name] == monarch && [flag] != ethereal && [flag] == runeword # [passivefirepierce] >= 28 # [tier] == tierscore(item, 100000)", + // Final Gloves - Perfect 2x Upp'ed Magefist + "[name] == crusadergauntlets && [quality] == unique && [flag] != ethereal # [enhanceddefense] == 30 && [addfireskills] == 1 # [tier] == 110000", + // Gloves - 2x Upp'ed Magefist + "[name] == crusadergauntlets && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 20 && [addfireskills] == 1 # [tier] == tierscore(item, 100000)", + // Gloves - Upp'ed Magefist + "[name] == battlegauntlets && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 20 && [addfireskills] == 1 # [tier] == tierscore(item, 100000)", + // Gloves - Magefist + "[name] == lightgauntlets && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 20 && [addfireskills] == 1 # [tier] == tierscore(item, 100000)", + // Amulet - Maras + "[type] == amulet && [quality] == unique # [strength] == 5 && [coldresist] >= 30 # [tier] == tierscore(item, 100000)", + // Rings - SoJ + "[type] == ring && [quality] == unique # [itemmaxmanapercent] == 25 # [tier] == 100000", + // Switch - CTA + "[minimumsockets] >= 5 && [flag] == runeword # [plusskillbattleorders] >= 1 # [secondarytier] == 100000", + // Merc Armor - Fortitude + "[type] == armor && [flag] == runeword # [enhanceddefense] >= 200 && [enhanceddamage] >= 300 # [merctier] == 100000", + // Merc Final Helmet - Eth Andy's + "[name] == demonhead && [quality] == unique && [flag] == ethereal # [strength] >= 25 && [enhanceddefense] >= 100 # [merctier] == 50000 + mercscore(item)", + // Merc Helmet - Andy's + "[name] == demonhead && [quality] == unique && [flag] != ethereal # [strength] >= 25 && [enhanceddefense] >= 100 # [merctier] == 40000 + mercscore(item)", + ], - ResMf: { - max: 2, - have: [], - classid: sdk.items.SmallCharm, - stats: function (check) { - return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MagicBonus) === 7); - } - }, + charms: { + ResLife: { + max: 3, + have: [], + classid: sdk.items.SmallCharm, + stats: function (check) { + return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MaxHp) === 20); + } + }, - ResFHR: { - max: 1, - have: [], - classid: sdk.items.SmallCharm, - stats: function (check) { - return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.FHR) === 5); - } - }, + ResMf: { + max: 2, + have: [], + classid: sdk.items.SmallCharm, + stats: function (check) { + return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MagicBonus) === 7); + } + }, - Skiller: { - max: 2, - have: [], - classid: sdk.items.GrandCharm, - stats: function (check) { - return (!check.unique && check.classid === this.classid && check.getStat(sdk.stats.AddSkillTab, sdk.skills.tabs.Elemental) === 1 - && check.getStat(sdk.stats.MaxHp) >= 40); - } - }, - }, + ResFHR: { + max: 1, + have: [], + classid: sdk.items.SmallCharm, + stats: function (check) { + return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.FHR) === 5); + } + }, - AutoBuildTemplate: { - 1: { - Update: function () { - Config.AttackSkill = [-1, sdk.skills.Fissure, sdk.skills.Firestorm, sdk.skills.Fissure, sdk.skills.Firestorm, sdk.skills.ArticBlast, -1]; - Config.SummonAnimal = "Grizzly"; - Config.SummonSpirit = "Oak Sage"; - } - }, - }, + Skiller: { + max: 2, + have: [], + classid: sdk.items.GrandCharm, + stats: function (check) { + return (!check.unique && check.classid === this.classid && check.getStat(sdk.stats.AddSkillTab, sdk.skills.tabs.Elemental) === 1 + && check.getStat(sdk.stats.MaxHp) >= 40); + } + }, + }, - respec: function () { - return Check.haveItem("armor", "runeword", "Enigma"); - }, + AutoBuildTemplate: { + 1: { + Update: function () { + Config.AttackSkill = [-1, sdk.skills.Fissure, sdk.skills.Firestorm, sdk.skills.Fissure, sdk.skills.Firestorm, sdk.skills.ArticBlast, -1]; + Config.SummonAnimal = "Grizzly"; + Config.SummonSpirit = "Oak Sage"; + } + }, + }, - active: function () { - return this.respec() && me.checkSkill(sdk.skills.Volcano, sdk.skills.subindex.HardPoints); - }, -}; + respec: function () { + return Check.haveItem("armor", "runeword", "Enigma"); + }, + + active: function () { + return this.respec() && me.checkSkill(sdk.skills.Volcano, sdk.skills.subindex.HardPoints); + }, + }; + + let finalGear = [ // autoequip final gear + // Weapon - HotO + "[type] == mace && [flag] == runeword # [itemallskills] == 3 # [tier] == 100000", + // Helmet - Ravenlore + "[name] == skyspirit && [quality] == unique # [passivefirepierce] >= 10 # [tier] == tierscore(item, 100000)", + // Belt - Arach's + "[name] == spiderwebsash && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 90 # [tier] == tierscore(item, 100000)", + // Final Boots - Sandstorm Treks + "[name] == scarabshellboots && [quality] == unique # [strength]+[vitality] >= 20 # [tier] == tierscore(item, 100000)", + // Boots - War Traveler + "[name] == battleboots && [quality] == unique && [flag] != ethereal # [itemmagicbonus] >= 50 # [tier] == tierscore(item, 5000)", + // Armor - Enigma + "[type] == armor && [flag] != ethereal && [flag] == runeword # [itemallskills] == 2 # [tier] == 100000", + // Shield - Phoenix + "[name] == monarch && [flag] != ethereal && [flag] == runeword # [passivefirepierce] >= 28 # [tier] == tierscore(item, 100000)", + // Final Gloves - Perfect 2x Upp'ed Magefist + "[name] == crusadergauntlets && [quality] == unique && [flag] != ethereal # [enhanceddefense] == 30 && [addfireskills] == 1 # [tier] == 110000", + // Gloves - 2x Upp'ed Magefist + "[name] == crusadergauntlets && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 20 && [addfireskills] == 1 # [tier] == tierscore(item, 100000)", + // Gloves - Upp'ed Magefist + "[name] == battlegauntlets && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 20 && [addfireskills] == 1 # [tier] == tierscore(item, 100000)", + // Gloves - Magefist + "[name] == lightgauntlets && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 20 && [addfireskills] == 1 # [tier] == tierscore(item, 100000)", + // Amulet - Maras + "[type] == amulet && [quality] == unique # [strength] == 5 && [coldresist] >= 30 # [tier] == tierscore(item, 100000)", + // Rings - SoJ + "[type] == ring && [quality] == unique # [itemmaxmanapercent] == 25 # [tier] == 100000", + // Switch - CTA + "[minimumsockets] >= 5 && [flag] == runeword # [plusskillbattleorders] >= 1 # [secondarytier] == 100000", + // Merc Armor - Fortitude + "[type] == armor && [flag] == runeword # [enhanceddefense] >= 200 && [enhanceddamage] >= 300 # [merctier] == 100000", + // Merc Final Helmet - Eth Andy's + "[name] == demonhead && [quality] == unique && [flag] == ethereal # [strength] >= 25 && [enhanceddefense] >= 100 # [merctier] == 50000 + mercscore(item)", + // Merc Helmet - Andy's + "[name] == demonhead && [quality] == unique && [flag] != ethereal # [strength] >= 25 && [enhanceddefense] >= 100 # [merctier] == 40000 + mercscore(item)", + ]; + + NTIP.buildList(finalGear); + NTIP.buildFinalGear(finalGear); + + return build; + })(); +})(module); diff --git a/libs/SoloPlay/BuildFiles/druid/druid.FirewolfBuild.js b/libs/SoloPlay/BuildFiles/druid/druid.FirewolfBuild.js index e8eef02c..6df25083 100644 --- a/libs/SoloPlay/BuildFiles/druid/druid.FirewolfBuild.js +++ b/libs/SoloPlay/BuildFiles/druid/druid.FirewolfBuild.js @@ -5,122 +5,129 @@ * */ -const finalBuild = { - caster: false, - skillstab: sdk.skills.tabs.ShapeShifting, - wantedskills: [sdk.skills.Werewolf, sdk.skills.Lycanthropy, sdk.skills.FireClaws], - usefulskills: [sdk.skills.HeartofWolverine, sdk.skills.Grizzly, sdk.skills.Rabies, sdk.skills.Volcano, sdk.skills.Fissure, sdk.skills.Firestorm], - precastSkills: [sdk.skills.Werewolf, sdk.skills.HeartofWolverine, sdk.skills.Grizzly], - usefulStats: [sdk.stats.PassiveFireMastery, sdk.stats.PassiveFirePierce, sdk.stats.PierceFire], - mercDiff: sdk.difficulty.Nightmare, - mercAct: 2, - mercAuraWanted: "Might", - stats: [ - ["strength", 156], ["dexterity", 136], ["vitality", "all"] - ], - skills: [ - [sdk.skills.Werewolf, 1, false], - [sdk.skills.Lycanthropy, 20, false], - [sdk.skills.PoisonCreeper, 1, false], - [sdk.skills.Grizzly, 1, false], - [sdk.skills.HeartofWolverine, 1, false], - [sdk.skills.Volcano, 1, false], - [sdk.skills.Fury, 1, false], - [sdk.skills.FireClaws, 20, false], - [sdk.skills.Rabies, 20, false], - [sdk.skills.Volcano, 20, false], - [sdk.skills.HeartofWolverine, 20, false], - ], - // AutoEquip Final Gear - autoEquipTiers: [ - // Weapon - Ice - "[name] == demoncrossbow && [flag] == runeword # [holyfreezeaura] == 18 # [tier] == 110000", - // Helmet - Jalal's Mane - "[name] == totemicmask && [quality] == unique # [druidskills] == 2 && [shapeshiftingskilltab] == 2 # [tier] == tierscore(item, 110000)", - // Belt - Verdungo's - "[name] == mithrilcoil && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 90 # [tier] == tierscore(item, 110000)", - // Armor - Chains of Honor - "[type] == armor && [flag] == runeword && [flag] != ethereal # [fireresist] == 65 && [hpregen] == 7 # [tier] == 110000", - // Gloves - Dracul's Grasp - "[name] == vampirebonegloves && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 90 # [tier] == tierscore(item, 110000)", - // Boots - Goblin Toes - "[name] == lightplatedboots && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 50 # [tier] == tierscore(item, 100000)", - // Amulet - Metalgrid - "[type] == amulet && [quality] == unique # [tohit] >= 400 && [coldresist] >= 25 # [tier] == tierscore(item, 110000)", - // Ring 1 - Ravenfrost - "[type] == ring && [quality] == unique # [tohit] >= 180 && [dexterity] >= 15 # [tier] == 100000", - // Ring 2 - Carrion Wind - "[name] == ring && [quality] == unique # [poisonresist] == 55 && [lifeleech] >= 6 # [tier] == tierscore(item, 110000)", - // Merc - // Final Armor - Fortitude - "[type] == armor && [flag] == runeword # [enhanceddefense] >= 200 && [enhanceddamage] >= 300 # [merctier] == 100000", - // Temporary Armor - Treachery - "[type] == armor && [flag] == runeword # [ias] == 45 && [coldresist] == 30 # [merctier] == 50000 + mercscore(item)", - // Final Helm - Eth Andy's - "[name] == demonhead && [quality] == unique && [flag] == ethereal # [strength] >= 25 && [enhanceddefense] >= 100 # [merctier] == 50000 + mercscore(item)", - // Temporary Helm - Andy's - "[name] == demonhead && [quality] == unique && [flag] != ethereal # [strength] >= 25 && [enhanceddefense] >= 100 # [merctier] == 40000 + mercscore(item)", - // Final Weapon - Infinity - "[type] == polearm && [flag] == runeword # [convictionaura] >= 13 # [merctier] == 100000 + mercscore(item)", - // Temporary Weapon - Reaper's Toll - "[name] == thresher && [quality] == unique # [enhanceddamage] >= 190 && [lifeleech] >= 11 # [merctier] == 50000 + mercscore(item)", - ], +(function (module) { + module.exports = (function () { + const build = { + caster: false, + skillstab: sdk.skills.tabs.ShapeShifting, + wantedskills: [sdk.skills.Werewolf, sdk.skills.Lycanthropy, sdk.skills.FireClaws], + usefulskills: [sdk.skills.HeartofWolverine, sdk.skills.Grizzly, sdk.skills.Rabies, sdk.skills.Volcano, sdk.skills.Fissure, sdk.skills.Firestorm], + precastSkills: [sdk.skills.Werewolf, sdk.skills.HeartofWolverine, sdk.skills.Grizzly], + usefulStats: [sdk.stats.PassiveFireMastery, sdk.stats.PassiveFirePierce, sdk.stats.PierceFire], + wantedMerc: MercData[sdk.skills.Might], + stats: [ + ["strength", 156], ["dexterity", 136], ["vitality", "all"] + ], + skills: [ + [sdk.skills.Werewolf, 1, false], + [sdk.skills.Lycanthropy, 20, false], + [sdk.skills.PoisonCreeper, 1, false], + [sdk.skills.Grizzly, 1, false], + [sdk.skills.HeartofWolverine, 1, false], + [sdk.skills.Volcano, 1, false], + [sdk.skills.Fury, 1, false], + [sdk.skills.FireClaws, 20, false], + [sdk.skills.Rabies, 20, false], + [sdk.skills.Volcano, 20, false], + [sdk.skills.HeartofWolverine, 20, false], + ], - charms: { - ResMf: { - max: 2, - have: [], - classid: sdk.items.SmallCharm, - stats: function (check) { - return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MagicBonus) === 7); - } - }, + charms: { + ResMf: { + max: 2, + have: [], + classid: sdk.items.SmallCharm, + stats: function (check) { + return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MagicBonus) === 7); + } + }, - Poison: { - max: 6, - have: [], - classid: sdk.items.SmallCharm, - stats: function (check) { - return (!check.unique && check.classid === this.classid - && ((check.getStat(sdk.stats.PoisonLength) * check.getStat(sdk.stats.PoisonMaxDamage)) / 256) >= 141); - } - }, + Poison: { + max: 6, + have: [], + classid: sdk.items.SmallCharm, + stats: function (check) { + return (!check.unique && check.classid === this.classid + && ((check.getStat(sdk.stats.PoisonLength) * check.getStat(sdk.stats.PoisonMaxDamage)) / 256) >= 141); + } + }, - Skiller: { - max: 2, - have: [], - classid: sdk.items.GrandCharm, - stats: function (check) { - return (!check.unique && check.classid === this.classid && check.getStat(sdk.stats.AddSkillTab, sdk.skills.tabs.ShapeShifting) === 1 - && check.getStat(sdk.stats.MaxHp) >= 40); - } - }, - }, + Skiller: { + max: 2, + have: [], + classid: sdk.items.GrandCharm, + stats: function (check) { + return (!check.unique && check.classid === this.classid && check.getStat(sdk.stats.AddSkillTab, sdk.skills.tabs.ShapeShifting) === 1 + && check.getStat(sdk.stats.MaxHp) >= 40); + } + }, + }, - AutoBuildTemplate: { - 1: { - Update: function () { - Config.BeltColumn = ["hp", "hp", "mp", "rv"]; - Config.AttackSkill = [ - sdk.skills.FeralRage, - sdk.skills.FireClaws, sdk.skills.Rabies, - sdk.skills.FireClaws, sdk.skills.Rabies, - sdk.skills.Fury, sdk.skills.Rabies - ]; - Config.LowManaSkill = [0, 0]; - Config.Wereform = "Werewolf"; - Config.SummonAnimal = "Grizzly"; - Config.SummonSpirit = "Heart of Wolverine"; - } - }, - }, + AutoBuildTemplate: { + 1: { + Update: function () { + Config.BeltColumn = ["hp", "hp", "mp", "rv"]; + Config.AttackSkill = [ + sdk.skills.FeralRage, + sdk.skills.FireClaws, sdk.skills.Rabies, + sdk.skills.FireClaws, sdk.skills.Rabies, + sdk.skills.Fury, sdk.skills.Rabies + ]; + Config.LowManaSkill = [0, 0]; + Config.Wereform = "Werewolf"; + Config.SummonAnimal = "Grizzly"; + Config.SummonSpirit = "Heart of Wolverine"; + } + }, + }, - respec: function () { - return me.haveAll([{ name: sdk.locale.items.Ice }, { name: sdk.locale.items.ChainsofHonor }]); - }, + respec: function () { + return me.haveAll([{ name: sdk.locale.items.Ice }, { name: sdk.locale.items.ChainsofHonor }]); + }, - active: function () { - return this.respec() && me.getSkill(sdk.skills.FireClaws, sdk.skills.subindex.HardPoints) === 20; - }, -}; + active: function () { + return this.respec() && me.getSkill(sdk.skills.FireClaws, sdk.skills.subindex.HardPoints) === 20; + }, + }; + + let finalGear = [ + // Weapon - Ice + "[name] == demoncrossbow && [flag] == runeword # [holyfreezeaura] == 18 # [tier] == 110000", + // Helmet - Jalal's Mane + "[name] == totemicmask && [quality] == unique # [druidskills] == 2 && [shapeshiftingskilltab] == 2 # [tier] == tierscore(item, 110000)", + // Belt - Verdungo's + "[name] == mithrilcoil && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 90 # [tier] == tierscore(item, 110000)", + // Armor - Chains of Honor + "[type] == armor && [flag] == runeword && [flag] != ethereal # [fireresist] == 65 && [hpregen] == 7 # [tier] == 110000", + // Gloves - Dracul's Grasp + "[name] == vampirebonegloves && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 90 # [tier] == tierscore(item, 110000)", + // Boots - Goblin Toes + "[name] == lightplatedboots && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 50 # [tier] == tierscore(item, 100000)", + // Amulet - Metalgrid + "[type] == amulet && [quality] == unique # [tohit] >= 400 && [coldresist] >= 25 # [tier] == tierscore(item, 110000)", + // Ring 1 - Ravenfrost + "[type] == ring && [quality] == unique # [tohit] >= 180 && [dexterity] >= 15 # [tier] == 100000", + // Ring 2 - Carrion Wind + "[name] == ring && [quality] == unique # [poisonresist] == 55 && [lifeleech] >= 6 # [tier] == tierscore(item, 110000)", + // Merc + // Final Armor - Fortitude + "[type] == armor && [flag] == runeword # [enhanceddefense] >= 200 && [enhanceddamage] >= 300 # [merctier] == 100000", + // Temporary Armor - Treachery + "[type] == armor && [flag] == runeword # [ias] == 45 && [coldresist] == 30 # [merctier] == 50000 + mercscore(item)", + // Final Helm - Eth Andy's + "[name] == demonhead && [quality] == unique && [flag] == ethereal # [strength] >= 25 && [enhanceddefense] >= 100 # [merctier] == 50000 + mercscore(item)", + // Temporary Helm - Andy's + "[name] == demonhead && [quality] == unique && [flag] != ethereal # [strength] >= 25 && [enhanceddefense] >= 100 # [merctier] == 40000 + mercscore(item)", + // Final Weapon - Infinity + "[type] == polearm && [flag] == runeword # [convictionaura] >= 13 # [merctier] == 100000 + mercscore(item)", + // Temporary Weapon - Reaper's Toll + "[name] == thresher && [quality] == unique # [enhanceddamage] >= 190 && [lifeleech] >= 11 # [merctier] == 50000 + mercscore(item)", + ]; + + NTIP.buildList(finalGear); + NTIP.buildFinalGear(finalGear); + + return build; + })(); +})(module); diff --git a/libs/SoloPlay/BuildFiles/druid/druid.LevelingBuild.js b/libs/SoloPlay/BuildFiles/druid/druid.LevelingBuild.js index 2ac04817..0567ce48 100644 --- a/libs/SoloPlay/BuildFiles/druid/druid.LevelingBuild.js +++ b/libs/SoloPlay/BuildFiles/druid/druid.LevelingBuild.js @@ -5,52 +5,60 @@ * */ -let build = { - AutoBuildTemplate: {}, - caster: true, - skillstab: 42, // elemental - wantedskills: [sdk.skills.Tornado, sdk.skills.Hurricane, sdk.skills.Twister], - usefulskills: [sdk.skills.CycloneArmor], - mercAuraName: "Blessed Aim", - mercAuraWanted: sdk.skills.BlessedAim, - mercDiff: sdk.difficulty.Normal, - stats: [ - ["strength", 48], ["dexterity", 35], ["vitality", 165], - ["strength", 61], ["vitality", 252], ["strength", 156], ["vitality", "all"] - ], - skills: [ - // Total skills at respec = 25 (assume hasn't killed izual yet) - [sdk.skills.Tornado, 1], // points left 21 - [sdk.skills.OakSage, 6], // points left 15 - [sdk.skills.SummonDireWolf, 1], // points left 12 - [sdk.skills.CycloneArmor, 13], // points left 0 - // Start - [sdk.skills.Tornado, 13, false], - [sdk.skills.Hurricane, 6, false], - [sdk.skills.Tornado, 14, false], - [sdk.skills.Hurricane, 7, false], - [sdk.skills.Grizzly, 1, false], - [sdk.skills.Tornado, 15, false], - [sdk.skills.Hurricane, 8, false], - [sdk.skills.Tornado, 20, false], - [sdk.skills.Hurricane, 20, false], - [sdk.skills.CycloneArmor, 20, false], - [sdk.skills.OakSage, 20, false], - [sdk.skills.Twister, 20], - ], - active: function () { - return (me.charlvl > CharInfo.respecOne && me.charlvl > CharInfo.respecTwo && me.checkSkill(sdk.skills.Tornado, sdk.skills.subindex.HardPoints) && !Check.finalBuild().active()); - }, -}; +(function (module) { + module.exports = (function () { + const build = { + caster: true, + skillstab: 42, // elemental + wantedskills: [sdk.skills.Tornado, sdk.skills.Hurricane, sdk.skills.Twister], + usefulskills: [sdk.skills.CycloneArmor], + wantedMerc: MercData[sdk.skills.BlessedAim], + stats: [ + ["strength", 48], ["dexterity", 35], ["vitality", 165], + ["strength", 61], ["vitality", 252], ["strength", 156], ["vitality", "all"] + ], + skills: [ + // Total skills at respec = 25 (assume hasn't killed izual yet) + [sdk.skills.Tornado, 1], // points left 21 + [sdk.skills.OakSage, 6], // points left 15 + [sdk.skills.SummonDireWolf, 1], // points left 12 + [sdk.skills.CycloneArmor, 13], // points left 0 + // Start + [sdk.skills.Tornado, 13, false], + [sdk.skills.Hurricane, 6, false], + [sdk.skills.Tornado, 14, false], + [sdk.skills.Hurricane, 7, false], + [sdk.skills.Grizzly, 1, false], + [sdk.skills.Tornado, 15, false], + [sdk.skills.Hurricane, 8, false], + [sdk.skills.Tornado, 20, false], + [sdk.skills.Hurricane, 20, false], + [sdk.skills.CycloneArmor, 20, false], + [sdk.skills.OakSage, 20, false], + [sdk.skills.Twister, 20], + ], -build.AutoBuildTemplate[1] = buildAutoBuildTempObj(() => { - Config.AttackSkill = [-1, sdk.skills.Tornado, -1, sdk.skills.Tornado, -1, sdk.skills.ArticBlast, -1]; - Config.LowManaSkill = [-1, -1]; - Config.SummonAnimal = "Grizzly"; - Config.SummonSpirit = "Oak Sage"; - Config.BeltColumn = ["hp", "hp", "mp", "mp"]; - Config.HPBuffer = 2; - Config.MPBuffer = me.charlvl < 80 ? 6 : 2; - SetUp.belt(); -}); + active: function () { + return (me.charlvl > CharInfo.respecOne && me.charlvl > CharInfo.respecTwo && me.checkSkill(sdk.skills.Tornado, sdk.skills.subindex.HardPoints) && !Check.finalBuild().active()); + }, + + AutoBuildTemplate: { + 1: { + Update: function () { + Config.AttackSkill = [-1, sdk.skills.Tornado, -1, sdk.skills.Tornado, -1, sdk.skills.ArticBlast, -1]; + Config.LowManaSkill = [-1, -1]; + Config.SummonAnimal = "Grizzly"; + Config.SummonSpirit = "Oak Sage"; + Config.BeltColumn = ["hp", "hp", "mp", "mp"]; + Config.HPBuffer = 2; + Config.MPBuffer = me.charlvl < 80 ? 6 : 2; + SetUp.belt(); + } + } + }, + }; + + return build; + })(); +})(module); diff --git a/libs/SoloPlay/BuildFiles/druid/druid.PlaguewolfBuild.js b/libs/SoloPlay/BuildFiles/druid/druid.PlaguewolfBuild.js index 4a81dc5c..057cbcf1 100644 --- a/libs/SoloPlay/BuildFiles/druid/druid.PlaguewolfBuild.js +++ b/libs/SoloPlay/BuildFiles/druid/druid.PlaguewolfBuild.js @@ -5,111 +5,120 @@ * */ -const finalBuild = { - caster: false, - skillstab: sdk.skills.tabs.ShapeShifting, - wantedskills: [sdk.skills.Werewolf, sdk.skills.Lycanthropy, sdk.skills.Fury], - usefulskills: [sdk.skills.HeartofWolverine, sdk.skills.Grizzly], - precastSkills: [sdk.skills.Werewolf, sdk.skills.HeartofWolverine, sdk.skills.Grizzly], - usefulStats: [sdk.stats.PassivePoisonMastery, sdk.stats.PassivePoisonPierce, sdk.stats.PiercePois], - mercDiff: sdk.difficulty.Nightmare, - mercAct: 2, - mercAuraWanted: "Might", - stats: [ - ["strength", 156], ["dexterity", 136], ["vitality", "all"] - ], - skills: [ - [sdk.skills.Werewolf, 20, false], - [sdk.skills.Lycanthropy, 20, false], - [sdk.skills.PoisonCreeper, 1, false], - [sdk.skills.Grizzly, 1, false], - [sdk.skills.Rabies, 20, false], - [sdk.skills.Fury, 20, false], - [sdk.skills.HeartofWolverine, 20, false], - [sdk.skills.PoisonCreeper, 20, false], - ], - autoEquipTiers: [ // autoequip final gear - // Weapon - Grief - "[type] == sword && [flag] == runeword # [ias] >= 30 && [itemdeadlystrike] == 20 && [passivepoispierce] >= 20 # [tier] == 110000", - // Shield - Stormshield - "[name] == monarch && [quality] == unique # [damageresist] >= 35 # [tier] == 110000", - // Helmet - Jalal's Mane - "[name] == totemicmask && [quality] == unique # [druidskills] == 2 && [shapeshiftingskilltab] == 2 # [tier] == tierscore(item, 110000)", - // Belt - Verdungo's - "[name] == mithrilcoil && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 90 # [tier] == tierscore(item, 110000)", - // Armor - CoH - "[type] == armor && [flag] == runeword && [flag] != ethereal # [fireresist] == 65 && [hpregen] == 7 # [tier] == 110000", - // Gloves - Dracul's - "[name] == vampirebonegloves && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 90 # [tier] == tierscore(item, 110000)", - // Amulet - Maras - "[type] == amulet && [quality] == unique # [strength] == 5 && [coldresist] >= 30 # [tier] == tierscore(item, 110000)", - // Final Rings - Perfect Wisp & Bul-Kathos' Wedding Band - "[type] == ring && [quality] == unique # [itemabsorblightpercent] == 20 # [tier] == 110000", - "[type] == ring && [quality] == unique # [maxstamina] == 50 && [lifeleech] == 5 # [tier] == 110000", - // Rings - Wisp & Bul-Kathos' Wedding Band - "[type] == ring && [quality] == unique # [itemabsorblightpercent] >= 10 # [tier] == 100000", - "[type] == ring && [quality] == unique # [maxstamina] == 50 && [lifeleech] >= 3 # [tier] == 100000", - // Merc Final Armor - Fortitude - "[type] == armor && [flag] == runeword # [enhanceddefense] >= 200 && [enhanceddamage] >= 300 # [merctier] == 100000", - // Merc Armor - Treachery - "[type] == armor && [flag] == runeword # [ias] == 45 && [coldresist] == 30 # [merctier] == 50000 + mercscore(item)", - // Merc Final Helmet - Eth Andy's - "[name] == demonhead && [quality] == unique && [flag] == ethereal # [strength] >= 25 && [enhanceddefense] >= 100 # [merctier] == 50000 + mercscore(item)", - // Merc Helmet - Andy's - "[name] == demonhead && [quality] == unique && [flag] != ethereal # [strength] >= 25 && [enhanceddefense] >= 100 # [merctier] == 40000 + mercscore(item)", - // Merc Weapon - Reaper's Toll - "[name] == thresher && [quality] == unique # [enhanceddamage] >= 190 && [lifeleech] >= 11 # [merctier] == 100000 + mercscore(item)", - ], - charms: { - ResMf: { - max: 2, - have: [], - classid: sdk.items.SmallCharm, - stats: function (check) { - return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MagicBonus) === 7); - } - }, +(function (module) { + module.exports = (function () { + const build = { + caster: false, + skillstab: sdk.skills.tabs.ShapeShifting, + wantedskills: [sdk.skills.Werewolf, sdk.skills.Lycanthropy, sdk.skills.Fury], + usefulskills: [sdk.skills.HeartofWolverine, sdk.skills.Grizzly], + precastSkills: [sdk.skills.Werewolf, sdk.skills.HeartofWolverine, sdk.skills.Grizzly], + usefulStats: [sdk.stats.PassivePoisonMastery, sdk.stats.PassivePoisonPierce, sdk.stats.PiercePois], + wantedMerc: MercData[sdk.skills.Might], + stats: [ + ["strength", 156], ["dexterity", 136], ["vitality", "all"] + ], + skills: [ + [sdk.skills.Werewolf, 20, false], + [sdk.skills.Lycanthropy, 20, false], + [sdk.skills.PoisonCreeper, 1, false], + [sdk.skills.Grizzly, 1, false], + [sdk.skills.Rabies, 20, false], + [sdk.skills.Fury, 20, false], + [sdk.skills.HeartofWolverine, 20, false], + [sdk.skills.PoisonCreeper, 20, false], + ], - Poison: { - max: 6, - have: [], - classid: sdk.items.SmallCharm, - stats: function (check) { - return (!check.unique && check.classid === this.classid - && ((check.getStat(sdk.stats.PoisonLength) * check.getStat(sdk.stats.PoisonMaxDamage)) / 256) >= 141); - } - }, + charms: { + ResMf: { + max: 2, + have: [], + classid: sdk.items.SmallCharm, + stats: function (check) { + return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MagicBonus) === 7); + } + }, - Skiller: { - max: 2, - have: [], - classid: sdk.items.GrandCharm, - stats: function (check) { - return (!check.unique && check.classid === this.classid && check.getStat(sdk.stats.AddSkillTab, sdk.skills.tabs.ShapeShifting) === 1 - && check.getStat(sdk.stats.MaxHp) >= 40); - } - }, - }, + Poison: { + max: 6, + have: [], + classid: sdk.items.SmallCharm, + stats: function (check) { + return (!check.unique && check.classid === this.classid + && ((check.getStat(sdk.stats.PoisonLength) * check.getStat(sdk.stats.PoisonMaxDamage)) / 256) >= 141); + } + }, - AutoBuildTemplate: { - 1: { - Update: function () { - Config.BeltColumn = ["hp", "hp", "mp", "rv"]; - Config.AttackSkill = [sdk.skills.FeralRage, sdk.skills.Fury, sdk.skills.Rabies, sdk.skills.Fury, sdk.skills.Rabies, sdk.skills.Rabies, -1]; - Config.LowManaSkill = [0, 0]; - Config.Wereform = "Werewolf"; - Config.SummonAnimal = "Grizzly"; - Config.SummonSpirit = "Heart of Wolverine"; - } - }, - }, + Skiller: { + max: 2, + have: [], + classid: sdk.items.GrandCharm, + stats: function (check) { + return (!check.unique && check.classid === this.classid && check.getStat(sdk.stats.AddSkillTab, sdk.skills.tabs.ShapeShifting) === 1 + && check.getStat(sdk.stats.MaxHp) >= 40); + } + }, + }, - respec: function () { - return me.haveAll([{ name: sdk.locale.items.Grief }, { name: sdk.locale.items.ChainsofHonor }]); - }, + AutoBuildTemplate: { + 1: { + Update: function () { + Config.BeltColumn = ["hp", "hp", "mp", "rv"]; + Config.AttackSkill = [sdk.skills.FeralRage, sdk.skills.Fury, sdk.skills.Rabies, sdk.skills.Fury, sdk.skills.Rabies, sdk.skills.Rabies, -1]; + Config.LowManaSkill = [0, 0]; + Config.Wereform = "Werewolf"; + Config.SummonAnimal = "Grizzly"; + Config.SummonSpirit = "Heart of Wolverine"; + } + }, + }, - active: function () { - return this.respec() && me.getSkill(sdk.skills.Rabies, sdk.skills.subindex.HardPoints) === 20; - }, -}; + respec: function () { + return me.haveAll([{ name: sdk.locale.items.Grief }, { name: sdk.locale.items.ChainsofHonor }]); + }, + + active: function () { + return this.respec() && me.getSkill(sdk.skills.Rabies, sdk.skills.subindex.HardPoints) === 20; + }, + }; + + let finalGear = [ // autoequip final gear + // Weapon - Grief + "[type] == sword && [flag] == runeword # [ias] >= 30 && [itemdeadlystrike] == 20 && [passivepoispierce] >= 20 # [tier] == 110000", + // Shield - Stormshield + "[name] == monarch && [quality] == unique # [damageresist] >= 35 # [tier] == 110000", + // Helmet - Jalal's Mane + "[name] == totemicmask && [quality] == unique # [druidskills] == 2 && [shapeshiftingskilltab] == 2 # [tier] == tierscore(item, 110000)", + // Belt - Verdungo's + "[name] == mithrilcoil && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 90 # [tier] == tierscore(item, 110000)", + // Armor - CoH + "[type] == armor && [flag] == runeword && [flag] != ethereal # [fireresist] == 65 && [hpregen] == 7 # [tier] == 110000", + // Gloves - Dracul's + "[name] == vampirebonegloves && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 90 # [tier] == tierscore(item, 110000)", + // Amulet - Maras + "[type] == amulet && [quality] == unique # [strength] == 5 && [coldresist] >= 30 # [tier] == tierscore(item, 110000)", + // Final Rings - Perfect Wisp & Bul-Kathos' Wedding Band + "[type] == ring && [quality] == unique # [itemabsorblightpercent] == 20 # [tier] == 110000", + "[type] == ring && [quality] == unique # [maxstamina] == 50 && [lifeleech] == 5 # [tier] == 110000", + // Rings - Wisp & Bul-Kathos' Wedding Band + "[type] == ring && [quality] == unique # [itemabsorblightpercent] >= 10 # [tier] == 100000", + "[type] == ring && [quality] == unique # [maxstamina] == 50 && [lifeleech] >= 3 # [tier] == 100000", + // Merc Final Armor - Fortitude + "[type] == armor && [flag] == runeword # [enhanceddefense] >= 200 && [enhanceddamage] >= 300 # [merctier] == 100000", + // Merc Armor - Treachery + "[type] == armor && [flag] == runeword # [ias] == 45 && [coldresist] == 30 # [merctier] == 50000 + mercscore(item)", + // Merc Final Helmet - Eth Andy's + "[name] == demonhead && [quality] == unique && [flag] == ethereal # [strength] >= 25 && [enhanceddefense] >= 100 # [merctier] == 50000 + mercscore(item)", + // Merc Helmet - Andy's + "[name] == demonhead && [quality] == unique && [flag] != ethereal # [strength] >= 25 && [enhanceddefense] >= 100 # [merctier] == 40000 + mercscore(item)", + // Merc Weapon - Reaper's Toll + "[name] == thresher && [quality] == unique # [enhanceddamage] >= 190 && [lifeleech] >= 11 # [merctier] == 100000 + mercscore(item)", + ]; + + NTIP.buildList(finalGear); + NTIP.buildFinalGear(finalGear); + + return build; + })(); +})(module); diff --git a/libs/SoloPlay/BuildFiles/druid/druid.StartBuild.js b/libs/SoloPlay/BuildFiles/druid/druid.StartBuild.js index 4950d432..e0f61dd5 100644 --- a/libs/SoloPlay/BuildFiles/druid/druid.StartBuild.js +++ b/libs/SoloPlay/BuildFiles/druid/druid.StartBuild.js @@ -11,9 +11,7 @@ let build = { skillstab: 42, // elemental wantedskills: [sdk.skills.Firestorm, sdk.skills.Fissure], usefulskills: [sdk.skills.MoltenBoulder], - mercAuraName: "Blessed Aim", - mercAuraWanted: 108, - mercDiff: 0, + wantedMerc: MercData[sdk.skills.BlessedAim], stats: [ ["vitality", 70], ["strength", 35], diff --git a/libs/SoloPlay/BuildFiles/druid/druid.StormbearBuild.js b/libs/SoloPlay/BuildFiles/druid/druid.StormbearBuild.js index 49813574..cc71910b 100644 --- a/libs/SoloPlay/BuildFiles/druid/druid.StormbearBuild.js +++ b/libs/SoloPlay/BuildFiles/druid/druid.StormbearBuild.js @@ -1,126 +1,133 @@ /** * @filename druid.StormbearBuild.js * @author theBGuy -* @desc CTC based bear final build - uses Destruction and Dragon RWs for chance to cast spells +* @desc CTC based bear final build - uses Destruction and Dragon RWs for chance to cast spells - not implemented - don't use! * */ -const finalBuild = { - caster: false, - skillstab: sdk.skills.tabs.ShapeShifting, - wantedskills: [sdk.skills.Werebear, sdk.skills.Lycanthropy, sdk.skills.Maul], - usefulskills: [sdk.skills.HeartofWolverine, sdk.skills.Grizzly, sdk.skills.Shockwave, sdk.skills.PoisonCreeper], - precastSkills: [sdk.skills.Werebear, sdk.skills.HeartofWolverine, sdk.skills.Grizzly], - usefulStats: [], - mercDiff: sdk.difficulty.Nightmare, - mercAct: 2, - mercAuraWanted: "Might", - stats: [ - ["strength", 127], ["dexterity", 136], ["vitality", "all"] - ], - skills: [ - [sdk.skills.Werebear, 20, false], - [sdk.skills.Lycanthropy, 20, false], - [sdk.skills.ShockWave, 1, false], - [sdk.skills.Maul, 20, false], - [sdk.skills.Grizzly, 20, false], - [sdk.skills.HeartofWolverine, 20, false], - [sdk.skills.PoisonCreeper, 20, false], - [sdk.skills.Shockwave, 20, false], - ], - // AutoEquip Final Gear - autoEquipTiers: [ - // Weapon - Destruction - "[name] == phaseblade && [flag] == runeword # [enhanceddamage] >= 350 && [itemdeadlystrike] == 20 && [itemcrushingblow] == 20 # [tier] == 110000", - // Shield - Sanctuary - "[name] == hyperion # [fhr] >= 20 && [enhanceddefense] >= 130 && [fireresist] >= 70 # [tier] == 110000", - "[name] == hyperion # [fhr] >= 20 && [enhanceddefense] >= 130 && [fireresist] >= 50 # [tier] == tierscore(item, 100000)", - // Helmet - Jalal's Mane - "[name] == totemicmask && [quality] == unique # [druidskills] == 2 && [shapeshiftingskilltab] == 2 # [tier] == tierscore(item, 110000)", - // Belt - Verdungo's - "[name] == mithrilcoil && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 90 # [tier] == tierscore(item, 110000)", - // Armor - Chains of Honor - "[type] == armor && [flag] == runeword && [flag] != ethereal # [fireresist] == 65 && [hpregen] == 7 # [tier] == 110000", - // Gloves - Dracul's Grasp - "[name] == vampirebonegloves && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 90 # [tier] == tierscore(item, 110000)", - // Boots - Gore Rider - "[name] == warboots && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 160 # [tier] == tierscore(item, 110000)", - // Amulet - Metalgrid - "[type] == amulet && [quality] == unique # [tohit] >= 400 && [coldresist] >= 25 # [tier] == tierscore(item, 110000)", - // Ring 1 - Ravenfrost - "[type] == ring && [quality] == unique # [tohit] >= 180 && [dexterity] >= 15 # [tier] == 100000", - // Ring 2 - Carrion Wind - "[name] == ring && [quality] == unique # [poisonresist] == 55 && [lifeleech] >= 6 # [tier] == tierscore(item, 110000)", - // Merc - // Final Armor - Fortitude - "[type] == armor && [flag] == runeword # [enhanceddefense] >= 200 && [enhanceddamage] >= 300 # [merctier] == 100000", - // Temporary Armor - Treachery - "[type] == armor && [flag] == runeword # [ias] == 45 && [coldresist] == 30 # [merctier] == 50000 + mercscore(item)", - // Final Helm - Eth Andy's - "[name] == demonhead && [quality] == unique && [flag] == ethereal # [strength] >= 25 && [enhanceddefense] >= 100 # [merctier] == 50000 + mercscore(item)", - // Temporary Helm - Andy's - "[name] == demonhead && [quality] == unique && [flag] != ethereal # [strength] >= 25 && [enhanceddefense] >= 100 # [merctier] == 40000 + mercscore(item)", - // Final Weapon - Infinity - "[type] == polearm && [flag] == runeword # [convictionaura] >= 13 # [merctier] == 100000 + mercscore(item)", - // Temporary Weapon - Reaper's Toll - "[name] == thresher && [quality] == unique # [enhanceddamage] >= 190 && [lifeleech] >= 11 # [merctier] == 50000 + mercscore(item)", - ], +(function (module) { + module.exports = (function () { + const build = { + caster: false, + skillstab: sdk.skills.tabs.ShapeShifting, + wantedskills: [sdk.skills.Werebear, sdk.skills.Lycanthropy, sdk.skills.Maul], + usefulskills: [sdk.skills.HeartofWolverine, sdk.skills.Grizzly, sdk.skills.Shockwave, sdk.skills.PoisonCreeper], + precastSkills: [sdk.skills.Werebear, sdk.skills.HeartofWolverine, sdk.skills.Grizzly], + usefulStats: [], + wantedMerc: MercData[sdk.skills.Might], + stats: [ + ["strength", 127], ["dexterity", 136], ["vitality", "all"] + ], + skills: [ + [sdk.skills.Werebear, 20, false], + [sdk.skills.Lycanthropy, 20, false], + [sdk.skills.ShockWave, 1, false], + [sdk.skills.Maul, 20, false], + [sdk.skills.Grizzly, 20, false], + [sdk.skills.HeartofWolverine, 20, false], + [sdk.skills.PoisonCreeper, 20, false], + [sdk.skills.Shockwave, 20, false], + ], - charms: { - ResMf: { - max: 2, - have: [], - classid: sdk.items.SmallCharm, - stats: function (check) { - return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MagicBonus) === 7); - } - }, + charms: { + ResMf: { + max: 2, + have: [], + classid: sdk.items.SmallCharm, + stats: function (check) { + return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MagicBonus) === 7); + } + }, - Poison: { - max: 6, - have: [], - classid: sdk.items.SmallCharm, - stats: function (check) { - return (!check.unique && check.classid === this.classid - && ((check.getStat(sdk.stats.PoisonLength) * check.getStat(sdk.stats.PoisonMaxDamage)) / 256) >= 141); - } - }, + Poison: { + max: 6, + have: [], + classid: sdk.items.SmallCharm, + stats: function (check) { + return (!check.unique && check.classid === this.classid + && ((check.getStat(sdk.stats.PoisonLength) * check.getStat(sdk.stats.PoisonMaxDamage)) / 256) >= 141); + } + }, - Skiller: { - max: 2, - have: [], - classid: sdk.items.GrandCharm, - stats: function (check) { - return (!check.unique && check.classid === this.classid && check.getStat(sdk.stats.AddSkillTab, sdk.skills.tabs.ShapeShifting) === 1 - && check.getStat(sdk.stats.MaxHp) >= 40); - } - }, - }, + Skiller: { + max: 2, + have: [], + classid: sdk.items.GrandCharm, + stats: function (check) { + return (!check.unique && check.classid === this.classid && check.getStat(sdk.stats.AddSkillTab, sdk.skills.tabs.ShapeShifting) === 1 + && check.getStat(sdk.stats.MaxHp) >= 40); + } + }, + }, - AutoBuildTemplate: { - 1: { - Update: function () { - Config.BeltColumn = ["hp", "hp", "mp", "rv"]; - Config.AttackSkill = [ - sdk.skills.Maul, - sdk.skills.Maul, sdk.skills.Shockwave, - sdk.skills.Maul, sdk.skills.Shockwave, - -1, -1 - ]; - Config.LowManaSkill = [0, 0]; - Config.Wereform = "Werebear"; - Config.SummonAnimal = "Grizzly"; - Config.SummonSpirit = "Heart of Wolverine"; - } - }, - }, + AutoBuildTemplate: { + 1: { + Update: function () { + Config.BeltColumn = ["hp", "hp", "mp", "rv"]; + Config.AttackSkill = [ + sdk.skills.Maul, + sdk.skills.Maul, sdk.skills.Shockwave, + sdk.skills.Maul, sdk.skills.Shockwave, + -1, -1 + ]; + Config.LowManaSkill = [0, 0]; + Config.Wereform = "Werebear"; + Config.SummonAnimal = "Grizzly"; + Config.SummonSpirit = "Heart of Wolverine"; + } + }, + }, - respec: function () { - return me.haveAll([{ name: sdk.locale.items.Destruction }, { name: sdk.locale.items.Dragon, itemtype: sdk.items.type.Armor }]); - }, + respec: function () { + return me.haveAll([{ name: sdk.locale.items.Destruction }, { name: sdk.locale.items.Dragon, itemtype: sdk.items.type.Armor }]); + }, - active: function () { - return this.respec() && me.getSkill(sdk.skills.Maul, sdk.skills.subindex.HardPoints) === 20; - }, -}; + active: function () { + return this.respec() && me.getSkill(sdk.skills.Maul, sdk.skills.subindex.HardPoints) === 20; + }, + }; + + let finalGear = [ + // Weapon - Destruction + "[name] == phaseblade && [flag] == runeword # [enhanceddamage] >= 350 && [itemdeadlystrike] == 20 && [itemcrushingblow] == 20 # [tier] == 110000", + // Shield - Sanctuary + "[name] == hyperion # [fhr] >= 20 && [enhanceddefense] >= 130 && [fireresist] >= 70 # [tier] == 110000", + "[name] == hyperion # [fhr] >= 20 && [enhanceddefense] >= 130 && [fireresist] >= 50 # [tier] == tierscore(item, 100000)", + // Helmet - Jalal's Mane + "[name] == totemicmask && [quality] == unique # [druidskills] == 2 && [shapeshiftingskilltab] == 2 # [tier] == tierscore(item, 110000)", + // Belt - Verdungo's + "[name] == mithrilcoil && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 90 # [tier] == tierscore(item, 110000)", + // Armor - Chains of Honor + "[type] == armor && [flag] == runeword && [flag] != ethereal # [fireresist] == 65 && [hpregen] == 7 # [tier] == 110000", + // Gloves - Dracul's Grasp + "[name] == vampirebonegloves && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 90 # [tier] == tierscore(item, 110000)", + // Boots - Gore Rider + "[name] == warboots && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 160 # [tier] == tierscore(item, 110000)", + // Amulet - Metalgrid + "[type] == amulet && [quality] == unique # [tohit] >= 400 && [coldresist] >= 25 # [tier] == tierscore(item, 110000)", + // Ring 1 - Ravenfrost + "[type] == ring && [quality] == unique # [tohit] >= 180 && [dexterity] >= 15 # [tier] == 100000", + // Ring 2 - Carrion Wind + "[name] == ring && [quality] == unique # [poisonresist] == 55 && [lifeleech] >= 6 # [tier] == tierscore(item, 110000)", + // Merc + // Final Armor - Fortitude + "[type] == armor && [flag] == runeword # [enhanceddefense] >= 200 && [enhanceddamage] >= 300 # [merctier] == 100000", + // Temporary Armor - Treachery + "[type] == armor && [flag] == runeword # [ias] == 45 && [coldresist] == 30 # [merctier] == 50000 + mercscore(item)", + // Final Helm - Eth Andy's + "[name] == demonhead && [quality] == unique && [flag] == ethereal # [strength] >= 25 && [enhanceddefense] >= 100 # [merctier] == 50000 + mercscore(item)", + // Temporary Helm - Andy's + "[name] == demonhead && [quality] == unique && [flag] != ethereal # [strength] >= 25 && [enhanceddefense] >= 100 # [merctier] == 40000 + mercscore(item)", + // Final Weapon - Infinity + "[type] == polearm && [flag] == runeword # [convictionaura] >= 13 # [merctier] == 100000 + mercscore(item)", + // Temporary Weapon - Reaper's Toll + "[name] == thresher && [quality] == unique # [enhanceddamage] >= 190 && [lifeleech] >= 11 # [merctier] == 50000 + mercscore(item)", + ]; + + NTIP.buildList(finalGear); + NTIP.buildFinalGear(finalGear); + + return build; + })(); +})(module); diff --git a/libs/SoloPlay/BuildFiles/druid/druid.WindBuild.js b/libs/SoloPlay/BuildFiles/druid/druid.WindBuild.js index 018c0f77..ec0c6036 100644 --- a/libs/SoloPlay/BuildFiles/druid/druid.WindBuild.js +++ b/libs/SoloPlay/BuildFiles/druid/druid.WindBuild.js @@ -5,122 +5,131 @@ * */ -const finalBuild = { - caster: true, - skillstab: sdk.skills.tabs.Elemental, - wantedskills: [sdk.skills.Tornado, sdk.skills.Hurricane, sdk.skills.Twister], - usefulskills: [sdk.skills.CycloneArmor], - precastSkills: [sdk.skills.CycloneArmor], - usefulStats: [sdk.stats.PassiveColdPierce, sdk.stats.PassiveColdMastery], - mercDiff: sdk.difficulty.Nightmare, - mercAct: 2, - mercAuraWanted: "Might", - stats: [ - ["dexterity", 35], ["strength", 156], ["vitality", "all"] - ], - skills: [ - [sdk.skills.Grizzly, 1], - [sdk.skills.Tornado, 20, false], - [sdk.skills.Hurricane, 20, false], - [sdk.skills.CycloneArmor, 20, false], - [sdk.skills.OakSage, 20, false], - [sdk.skills.Twister, 20], - [sdk.skills.Grizzly, 5], - ], - autoEquipTiers: [ // autoequip final gear - // Weapon - HotO - "[type] == mace && [flag] == runeword # [itemallskills] == 3 # [tier] == 100000", - // Helmet - Jalal's mane - "[name] == totemicmask && [quality] == unique # [druidskills] == 2 && [shapeshiftingskilltab] == 2 # [tier] == tierscore(item, 110000)", - // Belt - Arach's - "[name] == spiderwebsash && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 90 # [tier] == tierscore(item, 100000)", - // Final Boots - Sandstorm Treks - "[name] == scarabshellboots && [quality] == unique # [strength]+[vitality] >= 20 # [tier] == tierscore(item, 100000)", - // Boots - War Traveler - "[name] == battleboots && [quality] == unique && [flag] != ethereal # [itemmagicbonus] >= 50 # [tier] == tierscore(item, 5000)", - // Armor - Enigma - "[type] == armor && [flag] != ethereal && [flag] == runeword # [itemallskills] == 2 # [tier] == 100000", - // Shield - Spirit - "[name] == monarch && [flag] != ethereal && [flag] == runeword # [fcr] >= 35 # [tier] == 100000", - // Final Gloves - Perfect 2x Upp'ed Magefist - "[name] == crusadergauntlets && [quality] == unique && [flag] != ethereal # [enhanceddefense] == 30 && [addfireskills] == 1 # [tier] == 110000", - // Gloves - 2x Upp'ed Magefist - "[name] == crusadergauntlets && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 20 && [addfireskills] == 1 # [tier] == tierscore(item, 100000)", - // Gloves - Upp'ed Magefist - "[name] == battlegauntlets && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 20 && [addfireskills] == 1 # [tier] == tierscore(item, 100000)", - // Gloves - Magefist - "[name] == lightgauntlets && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 20 && [addfireskills] == 1 # [tier] == tierscore(item, 100000)", - // Amulet - Maras - "[type] == amulet && [quality] == unique # [strength] == 5 && [coldresist] >= 30 # [tier] == tierscore(item, 100000)", - // Rings - SoJ - "[type] == ring && [quality] == unique # [itemmaxmanapercent] == 25 # [tier] == 100000", - // Switch - CTA - "[minimumsockets] >= 5 && [flag] == runeword # [plusskillbattleorders] >= 1 # [secondarytier] == 100000", - // Merc Armor - Fortitude - "[type] == armor && [flag] == runeword # [enhanceddefense] >= 200 && [enhanceddamage] >= 300 # [merctier] == 100000", - // Merc Final Helmet - Eth Andy's - "[name] == demonhead && [quality] == unique && [flag] == ethereal # [strength] >= 25 && [enhanceddefense] >= 100 # [merctier] == 50000 + mercscore(item)", - // Merc Helmet - Andy's - "[name] == demonhead && [quality] == unique && [flag] != ethereal # [strength] >= 25 && [enhanceddefense] >= 100 # [merctier] == 40000 + mercscore(item)", - // Merc Weapon - Reaper's Toll - "[name] == thresher && [quality] == unique # [enhanceddamage] >= 190 && [lifeleech] >= 11 # [merctier] == 100000 + mercscore(item)", - ], - charms: { - ResLife: { - max: 3, - have: [], - classid: sdk.items.SmallCharm, - stats: function (check) { - return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MaxHp) === 20); - } - }, +(function (module) { + module.exports = (function () { + const build = { + caster: true, + skillstab: sdk.skills.tabs.Elemental, + wantedskills: [sdk.skills.Tornado, sdk.skills.Hurricane, sdk.skills.Twister], + usefulskills: [sdk.skills.CycloneArmor], + precastSkills: [sdk.skills.CycloneArmor], + usefulStats: [sdk.stats.PassiveColdPierce, sdk.stats.PassiveColdMastery], + wantedMerc: MercData[sdk.skills.Might], + stats: [ + ["dexterity", 35], ["strength", 156], ["vitality", "all"] + ], + skills: [ + [sdk.skills.Grizzly, 1], + [sdk.skills.Tornado, 20, false], + [sdk.skills.Hurricane, 20, false], + [sdk.skills.CycloneArmor, 20, false], + [sdk.skills.OakSage, 20, false], + [sdk.skills.Twister, 20], + [sdk.skills.Grizzly, 5], + ], - ResMf: { - max: 2, - have: [], - classid: sdk.items.SmallCharm, - stats: function (check) { - return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MagicBonus) === 7); - } - }, + charms: { + ResLife: { + max: 3, + have: [], + classid: sdk.items.SmallCharm, + stats: function (check) { + return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MaxHp) === 20); + } + }, - ResFHR: { - max: 1, - have: [], - classid: sdk.items.SmallCharm, - stats: function (check) { - return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.FHR) === 5); - } - }, + ResMf: { + max: 2, + have: [], + classid: sdk.items.SmallCharm, + stats: function (check) { + return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MagicBonus) === 7); + } + }, - Skiller: { - max: 2, - have: [], - classid: sdk.items.GrandCharm, - stats: function (check) { - return (!check.unique && check.classid === this.classid && check.getStat(sdk.stats.AddSkillTab, sdk.skills.tabs.Elemental) === 1 - && check.getStat(sdk.stats.MaxHp) >= 40); - } - }, - }, + ResFHR: { + max: 1, + have: [], + classid: sdk.items.SmallCharm, + stats: function (check) { + return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.FHR) === 5); + } + }, - AutoBuildTemplate: { - 1: { - Update: function () { - Config.AttackSkill = [-1, sdk.skills.Tornado, -1, sdk.skills.Tornado, -1, sdk.skills.ArticBlast, -1]; - Config.LowManaSkill = [-1, -1]; - Config.SummonAnimal = "Grizzly"; - Config.SummonSpirit = "Oak Sage"; - } - }, - }, + Skiller: { + max: 2, + have: [], + classid: sdk.items.GrandCharm, + stats: function (check) { + return (!check.unique && check.classid === this.classid && check.getStat(sdk.stats.AddSkillTab, sdk.skills.tabs.Elemental) === 1 + && check.getStat(sdk.stats.MaxHp) >= 40); + } + }, + }, - respec: function () { - return Check.haveItem("armor", "runeword", "Enigma"); - }, + AutoBuildTemplate: { + 1: { + Update: function () { + Config.AttackSkill = [-1, sdk.skills.Tornado, -1, sdk.skills.Tornado, -1, sdk.skills.ArticBlast, -1]; + Config.LowManaSkill = [-1, -1]; + Config.SummonAnimal = "Grizzly"; + Config.SummonSpirit = "Oak Sage"; + } + }, + }, - active: function () { - return this.respec() && me.getSkill(sdk.skills.Tornado, sdk.skills.subindex.HardPoints) === 20; - }, -}; + respec: function () { + return Check.haveItem("armor", "runeword", "Enigma"); + }, + + active: function () { + return this.respec() && me.getSkill(sdk.skills.Tornado, sdk.skills.subindex.HardPoints) === 20; + }, + }; + + let finalGear = [ // autoequip final gear + // Weapon - HotO + "[type] == mace && [flag] == runeword # [itemallskills] == 3 # [tier] == 100000", + // Helmet - Jalal's mane + "[name] == totemicmask && [quality] == unique # [druidskills] == 2 && [shapeshiftingskilltab] == 2 # [tier] == tierscore(item, 110000)", + // Belt - Arach's + "[name] == spiderwebsash && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 90 # [tier] == tierscore(item, 100000)", + // Final Boots - Sandstorm Treks + "[name] == scarabshellboots && [quality] == unique # [strength]+[vitality] >= 20 # [tier] == tierscore(item, 100000)", + // Boots - War Traveler + "[name] == battleboots && [quality] == unique && [flag] != ethereal # [itemmagicbonus] >= 50 # [tier] == tierscore(item, 5000)", + // Armor - Enigma + "[type] == armor && [flag] != ethereal && [flag] == runeword # [itemallskills] == 2 # [tier] == 100000", + // Shield - Spirit + "[name] == monarch && [flag] != ethereal && [flag] == runeword # [fcr] >= 35 # [tier] == 100000", + // Final Gloves - Perfect 2x Upp'ed Magefist + "[name] == crusadergauntlets && [quality] == unique && [flag] != ethereal # [enhanceddefense] == 30 && [addfireskills] == 1 # [tier] == 110000", + // Gloves - 2x Upp'ed Magefist + "[name] == crusadergauntlets && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 20 && [addfireskills] == 1 # [tier] == tierscore(item, 100000)", + // Gloves - Upp'ed Magefist + "[name] == battlegauntlets && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 20 && [addfireskills] == 1 # [tier] == tierscore(item, 100000)", + // Gloves - Magefist + "[name] == lightgauntlets && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 20 && [addfireskills] == 1 # [tier] == tierscore(item, 100000)", + // Amulet - Maras + "[type] == amulet && [quality] == unique # [strength] == 5 && [coldresist] >= 30 # [tier] == tierscore(item, 100000)", + // Rings - SoJ + "[type] == ring && [quality] == unique # [itemmaxmanapercent] == 25 # [tier] == 100000", + // Switch - CTA + "[minimumsockets] >= 5 && [flag] == runeword # [plusskillbattleorders] >= 1 # [secondarytier] == 100000", + // Merc Armor - Fortitude + "[type] == armor && [flag] == runeword # [enhanceddefense] >= 200 && [enhanceddamage] >= 300 # [merctier] == 100000", + // Merc Final Helmet - Eth Andy's + "[name] == demonhead && [quality] == unique && [flag] == ethereal # [strength] >= 25 && [enhanceddefense] >= 100 # [merctier] == 50000 + mercscore(item)", + // Merc Helmet - Andy's + "[name] == demonhead && [quality] == unique && [flag] != ethereal # [strength] >= 25 && [enhanceddefense] >= 100 # [merctier] == 40000 + mercscore(item)", + // Merc Weapon - Reaper's Toll + "[name] == thresher && [quality] == unique # [enhanceddamage] >= 190 && [lifeleech] >= 11 # [merctier] == 100000 + mercscore(item)", + ]; + + NTIP.buildList(finalGear); + NTIP.buildFinalGear(finalGear); + + return build; + })(); +})(module); diff --git a/libs/SoloPlay/BuildFiles/druid/druid.WolfBuild.js b/libs/SoloPlay/BuildFiles/druid/druid.WolfBuild.js index 77f17f73..15035f67 100644 --- a/libs/SoloPlay/BuildFiles/druid/druid.WolfBuild.js +++ b/libs/SoloPlay/BuildFiles/druid/druid.WolfBuild.js @@ -5,114 +5,123 @@ * */ -const finalBuild = { - caster: false, - skillstab: sdk.skills.tabs.ShapeShifting, - wantedskills: [sdk.skills.Werewolf, sdk.skills.Lycanthropy, sdk.skills.Fury], - usefulskills: [sdk.skills.HeartofWolverine, sdk.skills.Grizzly], - precastSkills: [sdk.skills.Werewolf, sdk.skills.HeartofWolverine, sdk.skills.Grizzly], - mercDiff: sdk.difficulty.Nightmare, - mercAct: 2, - mercAuraWanted: "Might", - stats: [ - ["strength", 103], ["dexterity", 35], ["vitality", "all"] - ], - skills: [ - [sdk.skills.Werewolf, 20, true], - [sdk.skills.Lycanthropy, 20, true], - [sdk.skills.Fury, 20, true], - [sdk.skills.Grizzly, 1, false], - [sdk.skills.HeartofWolverine, 20, true], - [sdk.skills.FeralRage, 10, false], - [sdk.skills.Grizzly, 15], - ], - autoEquipTiers: [ // autoequip final gear - // Weapon - Upp'ed Ribcracker - "[name] == stalagmite && [quality] == unique # [enhanceddamage] >= 300 && [ias] >= 50 # [tier] == 110000", - // Helmet - Jalal's Mane - "[name] == totemicmask && [quality] == unique # [druidskills] == 2 && [shapeshiftingskilltab] == 2 # [tier] == tierscore(item, 110000)", - // Belt - Verdungo's - "[name] == mithrilcoil && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 90 # [tier] == tierscore(item, 110000)", - // Armor - CoH - "[type] == armor && [flag] == runeword && [flag] != ethereal # [fireresist] == 65 && [hpregen] == 7 # [tier] == 110000", - // Gloves - Dracul's - "[name] == vampirebonegloves && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 90 # [tier] == tierscore(item, 110000)", - // Amulet - Maras - "[type] == amulet && [quality] == unique # [strength] == 5 && [coldresist] >= 30 # [tier] == tierscore(item, 110000)", - // Final Rings - Perfect Wisp & Bul-Kathos' Wedding Band - "[type] == ring && [quality] == unique # [itemabsorblightpercent] == 20 # [tier] == tierscore(item, 110000)", - "[type] == ring && [quality] == unique # [maxstamina] == 50 && [lifeleech] == 5 # [tier] == tierscore(item, 110000)", - // Rings - Wisp & Bul-Kathos' Wedding Band - "[type] == ring && [quality] == unique # [itemabsorblightpercent] >= 10 # [tier] == tierscore(item, 110000)", - "[type] == ring && [quality] == unique # [maxstamina] == 50 && [lifeleech] >= 3 # [tier] == tierscore(item, 110000)", - // Merc Final Armor - Fortitude - "[type] == armor && [flag] == runeword # [enhanceddefense] >= 200 && [enhanceddamage] >= 300 # [merctier] == 100000", - // Merc Armor - Treachery - "[type] == armor && [flag] == runeword # [ias] == 45 && [coldresist] == 30 # [merctier] == 50000 + mercscore(item)", - // Merc Final Helmet - Eth Andy's - "[name] == demonhead && [quality] == unique && [flag] == ethereal # [strength] >= 25 && [enhanceddefense] >= 100 # [merctier] == 50000 + mercscore(item)", - // Merc Helmet - Andy's - "[name] == demonhead && [quality] == unique && [flag] != ethereal # [strength] >= 25 && [enhanceddefense] >= 100 # [merctier] == 40000 + mercscore(item)", - // Merc Weapon - Reaper's Toll - "[name] == thresher && [quality] == unique # [enhanceddamage] >= 190 && [lifeleech] >= 11 # [merctier] == 100000 + mercscore(item)", - ], - charms: { - ResLife: { - max: 3, - have: [], - classid: sdk.items.SmallCharm, - stats: function (check) { - return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MaxHp) === 20); - } - }, +(function (module) { + module.exports = (function () { + const build = { + caster: false, + skillstab: sdk.skills.tabs.ShapeShifting, + wantedskills: [sdk.skills.Werewolf, sdk.skills.Lycanthropy, sdk.skills.Fury], + usefulskills: [sdk.skills.HeartofWolverine, sdk.skills.Grizzly], + precastSkills: [sdk.skills.Werewolf, sdk.skills.HeartofWolverine, sdk.skills.Grizzly], + wantedMerc: MercData[sdk.skills.Might], + stats: [ + ["strength", 103], ["dexterity", 35], ["vitality", "all"] + ], + skills: [ + [sdk.skills.Werewolf, 20, true], + [sdk.skills.Lycanthropy, 20, true], + [sdk.skills.Fury, 20, true], + [sdk.skills.Grizzly, 1, false], + [sdk.skills.HeartofWolverine, 20, true], + [sdk.skills.FeralRage, 10, false], + [sdk.skills.Grizzly, 15], + ], - ResMf: { - max: 2, - have: [], - classid: sdk.items.SmallCharm, - stats: function (check) { - return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MagicBonus) === 7); - } - }, + charms: { + ResLife: { + max: 3, + have: [], + classid: sdk.items.SmallCharm, + stats: function (check) { + return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MaxHp) === 20); + } + }, - ResFHR: { - max: 1, - have: [], - classid: sdk.items.SmallCharm, - stats: function (check) { - return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.FHR) === 5); - } - }, + ResMf: { + max: 2, + have: [], + classid: sdk.items.SmallCharm, + stats: function (check) { + return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MagicBonus) === 7); + } + }, - Skiller: { - max: 2, - have: [], - classid: sdk.items.GrandCharm, - stats: function (check) { - return (!check.unique && check.classid === this.classid && check.getStat(sdk.stats.AddSkillTab, sdk.skills.tabs.ShapeShifting) === 1 - && check.getStat(sdk.stats.MaxHp) >= 40); - } - }, - }, + ResFHR: { + max: 1, + have: [], + classid: sdk.items.SmallCharm, + stats: function (check) { + return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.FHR) === 5); + } + }, - AutoBuildTemplate: { - 1: { - Update: function () { - Config.AttackSkill = [-1, sdk.skills.Fury, sdk.skills.FeralRage, sdk.skills.Fury, sdk.skills.FeralRage, sdk.skills.Rabies, -1]; - Config.LowManaSkill = [0, 0]; - Config.Wereform = "Werewolf"; - Config.SummonAnimal = "Grizzly"; - Config.SummonSpirit = "Heart of Wolverine"; - } - }, - }, + Skiller: { + max: 2, + have: [], + classid: sdk.items.GrandCharm, + stats: function (check) { + return (!check.unique && check.classid === this.classid && check.getStat(sdk.stats.AddSkillTab, sdk.skills.tabs.ShapeShifting) === 1 + && check.getStat(sdk.stats.MaxHp) >= 40); + } + }, + }, - respec: function () { - return Check.haveItem("stalagmite", "unique", "Ribcracker") && Check.haveItem("armor", "runeword", "Chains of Honor"); - }, + AutoBuildTemplate: { + 1: { + Update: function () { + Config.AttackSkill = [-1, sdk.skills.Fury, sdk.skills.FeralRage, sdk.skills.Fury, sdk.skills.FeralRage, sdk.skills.Rabies, -1]; + Config.LowManaSkill = [0, 0]; + Config.Wereform = "Werewolf"; + Config.SummonAnimal = "Grizzly"; + Config.SummonSpirit = "Heart of Wolverine"; + } + }, + }, - active: function () { - return this.respec() && me.getSkill(sdk.skills.Werewolf, sdk.skills.subindex.HardPoints) === 20; - }, -}; + respec: function () { + return Check.haveItem("stalagmite", "unique", "Ribcracker") && Check.haveItem("armor", "runeword", "Chains of Honor"); + }, + + active: function () { + return this.respec() && me.getSkill(sdk.skills.Werewolf, sdk.skills.subindex.HardPoints) === 20; + }, + }; + + let finalGear = [ // autoequip final gear + // Weapon - Upp'ed Ribcracker + "[name] == stalagmite && [quality] == unique # [enhanceddamage] >= 300 && [ias] >= 50 # [tier] == 110000", + // Helmet - Jalal's Mane + "[name] == totemicmask && [quality] == unique # [druidskills] == 2 && [shapeshiftingskilltab] == 2 # [tier] == tierscore(item, 110000)", + // Belt - Verdungo's + "[name] == mithrilcoil && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 90 # [tier] == tierscore(item, 110000)", + // Armor - CoH + "[type] == armor && [flag] == runeword && [flag] != ethereal # [fireresist] == 65 && [hpregen] == 7 # [tier] == 110000", + // Gloves - Dracul's + "[name] == vampirebonegloves && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 90 # [tier] == tierscore(item, 110000)", + // Amulet - Maras + "[type] == amulet && [quality] == unique # [strength] == 5 && [coldresist] >= 30 # [tier] == tierscore(item, 110000)", + // Final Rings - Perfect Wisp & Bul-Kathos' Wedding Band + "[type] == ring && [quality] == unique # [itemabsorblightpercent] == 20 # [tier] == tierscore(item, 110000)", + "[type] == ring && [quality] == unique # [maxstamina] == 50 && [lifeleech] == 5 # [tier] == tierscore(item, 110000)", + // Rings - Wisp & Bul-Kathos' Wedding Band + "[type] == ring && [quality] == unique # [itemabsorblightpercent] >= 10 # [tier] == tierscore(item, 110000)", + "[type] == ring && [quality] == unique # [maxstamina] == 50 && [lifeleech] >= 3 # [tier] == tierscore(item, 110000)", + // Merc Final Armor - Fortitude + "[type] == armor && [flag] == runeword # [enhanceddefense] >= 200 && [enhanceddamage] >= 300 # [merctier] == 100000", + // Merc Armor - Treachery + "[type] == armor && [flag] == runeword # [ias] == 45 && [coldresist] == 30 # [merctier] == 50000 + mercscore(item)", + // Merc Final Helmet - Eth Andy's + "[name] == demonhead && [quality] == unique && [flag] == ethereal # [strength] >= 25 && [enhanceddefense] >= 100 # [merctier] == 50000 + mercscore(item)", + // Merc Helmet - Andy's + "[name] == demonhead && [quality] == unique && [flag] != ethereal # [strength] >= 25 && [enhanceddefense] >= 100 # [merctier] == 40000 + mercscore(item)", + // Merc Weapon - Reaper's Toll + "[name] == thresher && [quality] == unique # [enhanceddamage] >= 190 && [lifeleech] >= 11 # [merctier] == 100000 + mercscore(item)", + ]; + + NTIP.buildList(finalGear); + NTIP.buildFinalGear(finalGear); + + return build; + })(); +})(module); diff --git a/libs/SoloPlay/BuildFiles/necromancer/necromancer.BoneBuild.js b/libs/SoloPlay/BuildFiles/necromancer/necromancer.BoneBuild.js index 11144bc1..5a1c484a 100644 --- a/libs/SoloPlay/BuildFiles/necromancer/necromancer.BoneBuild.js +++ b/libs/SoloPlay/BuildFiles/necromancer/necromancer.BoneBuild.js @@ -5,156 +5,162 @@ * */ -const finalBuild = { - caster: true, - skillstab: sdk.skills.tabs.PoisonandBone, - wantedskills: [sdk.skills.BoneSpirit, sdk.skills.BoneSpear, sdk.skills.Teeth], - usefulskills: [sdk.skills.AmplifyDamage, sdk.skills.BoneArmor, sdk.skills.Decrepify, sdk.skills.BoneWall, sdk.skills.BonePrison], - precastSkills: [sdk.skills.BoneArmor], - mercDiff: sdk.difficulty.Nightmare, - mercAct: 2, - mercAuraWanted: "Might", - classicStats: [ - ["dexterity", 51], ["strength", 80], ["energy", 100], ["vitality", "all"] - ], - expansionStats: [ - ["strength", 48], ["dexterity", 35], ["vitality", 165], - ["strength", 61], ["vitality", 252], ["strength", 156], ["vitality", "all"] - ], - skills: [ - [sdk.skills.BoneSpirit, 1], - [sdk.skills.BonePrison, 1], - [sdk.skills.SummonResist, 1], - [sdk.skills.Decrepify, 1], - [sdk.skills.Attract, 1], - [sdk.skills.BoneSpear, 20, false], - [sdk.skills.BonePrison, 20, false], - [sdk.skills.BoneWall, 20, false], - [sdk.skills.BoneSpirit, 20, false], - [sdk.skills.Teeth, 20, false], - ], - classicTiers: [ - // Weapon - Spectral Shard - "[name] == blade && [quality] == unique # [fcr] == 20 && [allres] == 10 # [tier] == 100000", - // Helm - Tarnhelm - "[name] == skullcap && [quality] == unique # [itemallskills] == 1 && [itemmagicbonus] >= 25 # [tier] == tierscore(item, 100000)", - // Shield - "[type] == shield && [quality] >= magic # [necromancerskills] == 2 && [allres] >= 16 # [tier] == tierscore(item, 100000)", - // Rings - SoJ - "[type] == ring && [quality] == unique # [itemmaxmanapercent] == 25 # [tier] == 100000", - // Amulet - "[type] == amulet && [quality] >= magic # [necromancerskills] == 2 && [fcr] == 10 # [tier] == tierscore(item, 100000)", - // Boots - "[type] == boots && [quality] >= magic # [frw] >= 20 && [fhr] == 10 && [coldresist]+[lightresist] >= 10 # [tier] == tierscore(item, 100000)", - // Belt - "[type] == belt && [quality] >= magic # [fhr] >= 20 && [maxhp] >= 40 && [fireresist]+[lightresist] >= 20 # [tier] == tierscore(item, 100000)", - // Gloves - Magefist - "[name] == lightgauntlets && [quality] == unique # [fcr] >= 20 && [addfireskills] == 1 # [tier] == tierscore(item, 100000)", - ], - expansionTiers: [ - // Weapon - "([type] == wand || [type] == sword && ([quality] >= magic || [flag] == runeword) || [type] == knife && [quality] >= magic) && [flag] != ethereal # [secondarymindamage] == 0 && [itemchargedskill] >= 0 # [tier] == tierscore(item)", - // Helmet - Harlequin's Crest - "[name] == shako && [quality] == unique && [flag] != ethereal # [damageresist] == 10 # [tier] == tierscore(item, 100000)", - // Belt - Arach's - "[name] == spiderwebsash && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 90 # [tier] == tierscore(item, 100000)", - // Final Boots - Sandstorm Treks - "[name] == scarabshellboots && [quality] == unique # [strength]+[vitality] >= 20 # [tier] == tierscore(item, 100000)", - // Boots - War Traveler - "[name] == battleboots && [quality] == unique && [flag] != ethereal # [itemmagicbonus] >= 50 # [tier] == tierscore(item, 5000)", - // Armor - Enigma - "[type] == armor && [flag] != ethereal && [flag] == runeword # [itemallskills] == 2 # [tier] == 100000", - // Shield - "([type] == shield && ([quality] >= magic || [flag] == runeword) || [type] == voodooheads) && [flag] != ethereal # [itemchargedskill] >= 0 # [tier] == tierscore(item)", - // Final Gloves - Perfect 2x Upp'ed Magefist - "[name] == crusadergauntlets && [quality] == unique && [flag] != ethereal # [enhanceddefense] == 30 && [addfireskills] == 1 # [tier] == 110000", - // Gloves - 2x Upp'ed Magefist - "[name] == crusadergauntlets && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 20 && [addfireskills] == 1 # [tier] == tierscore(item, 100000)", - // Gloves - Upp'ed Magefist - "[name] == battlegauntlets && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 20 && [addfireskills] == 1 # [tier] == tierscore(item, 100000)", - // Gloves - Magefist - "[name] == lightgauntlets && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 20 && [addfireskills] == 1 # [tier] == tierscore(item, 100000)", - // Amulet - Maras - "[type] == amulet && [quality] == unique # [strength] == 5 && [coldresist] >= 30 # [tier] == tierscore(item, 100000)", - // Rings - Dwarf Star & SoJ - "[type] == ring && [quality] == unique # [maxhp] >= 40 && [magicdamagereduction] >= 12 # [tier] == 99000", - "[type] == ring && [quality] == unique # [itemmaxmanapercent] == 25 # [tier] == 100000", - // Switch - CTA - "[minimumsockets] >= 5 && [flag] == runeword # [plusskillbattleorders] >= 1 # [secondarytier] == 100000", - // Switch - Spirit - "[name] == monarch && [flag] == runeword # [fcr] >= 25 && [maxmana] >= 89 # [secondarytier] == 110000", - // Merc Armor - Fortitude - "[type] == armor && [flag] == runeword # [enhanceddefense] >= 200 && [enhanceddamage] >= 300 # [merctier] == 100000", - // Merc Final Helmet - Eth Andy's - "[name] == demonhead && [quality] == unique && [flag] == ethereal # [strength] >= 25 && [enhanceddefense] >= 100 # [merctier] == 50000 + mercscore(item)", - // Merc Helmet - Andy's - "[name] == demonhead && [quality] == unique && [flag] != ethereal # [strength] >= 25 && [enhanceddefense] >= 100 # [merctier] == 40000 + mercscore(item)", - ], - stats: undefined, - autoEquipTiers: undefined, - charms: { - ResLife: { - max: 4, - have: [], - classid: sdk.items.SmallCharm, - stats: function (check) { - return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MaxHp) === 20); - } - }, +(function (module) { + module.exports = (function () { + const build = { + caster: true, + skillstab: sdk.skills.tabs.PoisonandBone, + wantedskills: [sdk.skills.BoneSpirit, sdk.skills.BoneSpear, sdk.skills.Teeth], + usefulskills: [sdk.skills.AmplifyDamage, sdk.skills.BoneArmor, sdk.skills.Decrepify, sdk.skills.BoneWall, sdk.skills.BonePrison], + precastSkills: [sdk.skills.BoneArmor], + wantedMerc: MercData[sdk.skills.Might], + skills: [ + [sdk.skills.BoneSpirit, 1], + [sdk.skills.BonePrison, 1], + [sdk.skills.SummonResist, 1], + [sdk.skills.Decrepify, 1], + [sdk.skills.Attract, 1], + [sdk.skills.BoneSpear, 20, false], + [sdk.skills.BonePrison, 20, false], + [sdk.skills.BoneWall, 20, false], + [sdk.skills.BoneSpirit, 20, false], + [sdk.skills.Teeth, 20, false], + ], + stats: [], - ResMf: { - max: 3, - have: [], - classid: sdk.items.SmallCharm, - stats: function (check) { - return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MagicBonus) === 7); - } - }, + charms: { + ResLife: { + max: 4, + have: [], + classid: sdk.items.SmallCharm, + stats: function (check) { + return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MaxHp) === 20); + } + }, - ResFHR: { - max: 1, - have: [], - classid: sdk.items.SmallCharm, - stats: function (check) { - return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.FHR) === 5); - } - }, + ResMf: { + max: 3, + have: [], + classid: sdk.items.SmallCharm, + stats: function (check) { + return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MagicBonus) === 7); + } + }, - Skiller: { - max: 2, - have: [], - classid: sdk.items.GrandCharm, - stats: function (check) { - return (!check.unique && check.classid === this.classid && check.getStat(sdk.stats.AddSkillTab, sdk.skills.tabs.PoisonandBone) === 1 - && check.getStat(sdk.stats.MaxHp) >= 40); - } - }, - }, + ResFHR: { + max: 1, + have: [], + classid: sdk.items.SmallCharm, + stats: function (check) { + return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.FHR) === 5); + } + }, - AutoBuildTemplate: { - 1: { - Update: function () { - Config.AttackSkill = [-1, sdk.skills.BoneSpear, -1, sdk.skills.BoneSpear, -1, -1, -1]; - Config.ExplodeCorpses = sdk.skills.CorpseExplosion; - Config.Golem = "Clay"; - } - }, - }, + Skiller: { + max: 2, + have: [], + classid: sdk.items.GrandCharm, + stats: function (check) { + return (!check.unique && check.classid === this.classid && check.getStat(sdk.stats.AddSkillTab, sdk.skills.tabs.PoisonandBone) === 1 + && check.getStat(sdk.stats.MaxHp) >= 40); + } + }, + }, - respec: function () { - if (me.classic) { - return me.charlvl >= 75 && me.diablo; - } else { - return Check.haveItem("armor", "runeword", "Enigma"); - } - }, + AutoBuildTemplate: { + 1: { + Update: function () { + Config.AttackSkill = [-1, sdk.skills.BoneSpear, -1, sdk.skills.BoneSpear, -1, -1, -1]; + Config.ExplodeCorpses = sdk.skills.CorpseExplosion; + Config.Golem = "Clay"; + } + }, + }, - active: function () { - return this.respec() && me.getSkill(sdk.skills.BoneSpear, sdk.skills.subindex.HardPoints) === 20; - }, -}; + respec: function () { + if (me.classic) { + return me.charlvl >= 75 && me.diablo; + } else { + return Check.haveItem("armor", "runeword", "Enigma"); + } + }, -// Has to be set after its loaded -finalBuild.stats = me.classic ? finalBuild.classicStats : finalBuild.expansionStats; -finalBuild.autoEquipTiers = me.classic ? finalBuild.classicTiers : finalBuild.expansionTiers; + active: function () { + return this.respec() && me.getSkill(sdk.skills.BoneSpear, sdk.skills.subindex.HardPoints) === 20; + }, + }; + + build.stats = me.classic + ? [ + ["dexterity", 51], ["strength", 80], ["energy", 100], ["vitality", "all"] + ] + : [ + ["strength", 48], ["dexterity", 35], ["vitality", 165], + ["strength", 61], ["vitality", 252], ["strength", 156], ["vitality", "all"] + ]; + + let finalGear = me.classic + ? [ + // Weapon - Spectral Shard + "[name] == blade && [quality] == unique # [fcr] == 20 && [allres] == 10 # [tier] == 100000", + // Helm - Tarnhelm + "[name] == skullcap && [quality] == unique # [itemallskills] == 1 && [itemmagicbonus] >= 25 # [tier] == tierscore(item, 100000)", + // Shield + "[type] == shield && [quality] >= magic # [necromancerskills] == 2 && [allres] >= 16 # [tier] == tierscore(item, 100000)", + // Rings - SoJ + "[type] == ring && [quality] == unique # [itemmaxmanapercent] == 25 # [tier] == 100000", + // Amulet + "[type] == amulet && [quality] >= magic # [necromancerskills] == 2 && [fcr] == 10 # [tier] == tierscore(item, 100000)", + // Boots + "[type] == boots && [quality] >= magic # [frw] >= 20 && [fhr] == 10 && [coldresist]+[lightresist] >= 10 # [tier] == tierscore(item, 100000)", + // Belt + "[type] == belt && [quality] >= magic # [fhr] >= 20 && [maxhp] >= 40 && [fireresist]+[lightresist] >= 20 # [tier] == tierscore(item, 100000)", + // Gloves - Magefist + "[name] == lightgauntlets && [quality] == unique # [fcr] >= 20 && [addfireskills] == 1 # [tier] == tierscore(item, 100000)", + ] : [ + // Weapon + "([type] == wand || [type] == sword && ([quality] >= magic || [flag] == runeword) || [type] == knife && [quality] >= magic) && [flag] != ethereal # [secondarymindamage] == 0 && [itemchargedskill] >= 0 # [tier] == tierscore(item)", + // Helmet - Harlequin's Crest + "[name] == shako && [quality] == unique && [flag] != ethereal # [damageresist] == 10 # [tier] == tierscore(item, 100000)", + // Belt - Arach's + "[name] == spiderwebsash && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 90 # [tier] == tierscore(item, 100000)", + // Final Boots - Sandstorm Treks + "[name] == scarabshellboots && [quality] == unique # [strength]+[vitality] >= 20 # [tier] == tierscore(item, 100000)", + // Boots - War Traveler + "[name] == battleboots && [quality] == unique && [flag] != ethereal # [itemmagicbonus] >= 50 # [tier] == tierscore(item, 5000)", + // Armor - Enigma + "[type] == armor && [flag] != ethereal && [flag] == runeword # [itemallskills] == 2 # [tier] == 100000", + // Shield + "([type] == shield && ([quality] >= magic || [flag] == runeword) || [type] == voodooheads) && [flag] != ethereal # [itemchargedskill] >= 0 # [tier] == tierscore(item)", + // Final Gloves - Perfect 2x Upp'ed Magefist + "[name] == crusadergauntlets && [quality] == unique && [flag] != ethereal # [enhanceddefense] == 30 && [addfireskills] == 1 # [tier] == 110000", + // Gloves - 2x Upp'ed Magefist + "[name] == crusadergauntlets && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 20 && [addfireskills] == 1 # [tier] == tierscore(item, 100000)", + // Gloves - Upp'ed Magefist + "[name] == battlegauntlets && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 20 && [addfireskills] == 1 # [tier] == tierscore(item, 100000)", + // Gloves - Magefist + "[name] == lightgauntlets && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 20 && [addfireskills] == 1 # [tier] == tierscore(item, 100000)", + // Amulet - Maras + "[type] == amulet && [quality] == unique # [strength] == 5 && [coldresist] >= 30 # [tier] == tierscore(item, 100000)", + // Rings - Dwarf Star & SoJ + "[type] == ring && [quality] == unique # [maxhp] >= 40 && [magicdamagereduction] >= 12 # [tier] == 99000", + "[type] == ring && [quality] == unique # [itemmaxmanapercent] == 25 # [tier] == 100000", + // Switch - CTA + "[minimumsockets] >= 5 && [flag] == runeword # [plusskillbattleorders] >= 1 # [secondarytier] == 100000", + // Switch - Spirit + "[name] == monarch && [flag] == runeword # [fcr] >= 25 && [maxmana] >= 89 # [secondarytier] == 110000", + // Merc Armor - Fortitude + "[type] == armor && [flag] == runeword # [enhanceddefense] >= 200 && [enhanceddamage] >= 300 # [merctier] == 100000", + // Merc Final Helmet - Eth Andy's + "[name] == demonhead && [quality] == unique && [flag] == ethereal # [strength] >= 25 && [enhanceddefense] >= 100 # [merctier] == 50000 + mercscore(item)", + // Merc Helmet - Andy's + "[name] == demonhead && [quality] == unique && [flag] != ethereal # [strength] >= 25 && [enhanceddefense] >= 100 # [merctier] == 40000 + mercscore(item)", + ]; + + NTIP.buildList(finalGear); + NTIP.buildFinalGear(finalGear); + + return build; + })(); +})(module); diff --git a/libs/SoloPlay/BuildFiles/necromancer/necromancer.LevelingBuild.js b/libs/SoloPlay/BuildFiles/necromancer/necromancer.LevelingBuild.js index 7004fa87..693b4efc 100644 --- a/libs/SoloPlay/BuildFiles/necromancer/necromancer.LevelingBuild.js +++ b/libs/SoloPlay/BuildFiles/necromancer/necromancer.LevelingBuild.js @@ -7,55 +7,61 @@ // TODO: test summonnovamancer for classic (wouldn't be able to farcast diablo though :( ) -let build = { - AutoBuildTemplate: {}, - caster: true, - skillstab: sdk.skills.tabs.PoisonandBone, - wantedskills: [sdk.skills.CorpseExplosion, sdk.skills.BoneSpear], - usefulskills: [sdk.skills.AmplifyDamage, sdk.skills.BoneArmor, sdk.skills.Decrepify, sdk.skills.BoneWall, sdk.skills.BonePrison, sdk.skills.BoneSpirit, sdk.skills.Teeth], - mercDiff: sdk.difficulty.Nightmare, - mercAct: 2, - mercAuraWanted: "Might", - classicStats: [ - ["dexterity", 51], ["strength", 80], ["energy", 100], ["vitality", "all"] - ], - expansionStats: [ - ["strength", 48], ["vitality", 165], ["strength", 61], - ["vitality", 252], ["strength", 156], ["vitality", "all"] - ], - skills: [ - // Total skills at respec = 29 - [sdk.skills.Decrepify, 1], // points left 25 - [sdk.skills.SummonResist, 1], // points left 22 - [sdk.skills.BonePrison, 1], // points left 16 - [sdk.skills.Attract, 1], // points left 13 - [sdk.skills.BoneSpear, 9], // points left 5 - [sdk.skills.BonePrison, 3], // points left 3 - [sdk.skills.BoneSpear, 20, false], - [sdk.skills.BoneSpirit, 1, false], - [sdk.skills.BonePrison, 20, false], - [sdk.skills.CorpseExplosion, 20, false], - [sdk.skills.BoneWall, 20, false], - [sdk.skills.Teeth, 20, false], - ], - stats: undefined, +(function (module) { + module.exports = (function () { + const build = { + caster: true, + skillstab: sdk.skills.tabs.PoisonandBone, + wantedskills: [sdk.skills.CorpseExplosion, sdk.skills.BoneSpear], + usefulskills: [sdk.skills.AmplifyDamage, sdk.skills.BoneArmor, sdk.skills.Decrepify, sdk.skills.BoneWall, sdk.skills.BonePrison, sdk.skills.BoneSpirit, sdk.skills.Teeth], + wantedMerc: MercData[sdk.skills.Might], + skills: [ + // Total skills at respec = 29 + [sdk.skills.Decrepify, 1], // points left 25 + [sdk.skills.SummonResist, 1], // points left 22 + [sdk.skills.BonePrison, 1], // points left 16 + [sdk.skills.Attract, 1], // points left 13 + [sdk.skills.BoneSpear, 9], // points left 5 + [sdk.skills.BonePrison, 3], // points left 3 + [sdk.skills.BoneSpear, 20, false], + [sdk.skills.BoneSpirit, 1, false], + [sdk.skills.BonePrison, 20, false], + [sdk.skills.CorpseExplosion, 20, false], + [sdk.skills.BoneWall, 20, false], + [sdk.skills.Teeth, 20, false], + ], + stats: [], - active: function () { - return (me.charlvl > CharInfo.respecOne && me.charlvl > CharInfo.respecTwo && me.checkSkill(sdk.skills.BonePrison, sdk.skills.subindex.HardPoints) && !Check.finalBuild().active()); - }, -}; + active: function () { + return (me.charlvl > CharInfo.respecOne && me.charlvl > CharInfo.respecTwo && me.checkSkill(sdk.skills.BonePrison, sdk.skills.subindex.HardPoints) && !Check.finalBuild().active()); + }, -// Has to be set after its loaded -build.stats = me.classic ? build.classicStats : build.expansionStats; - -build.AutoBuildTemplate[1] = buildAutoBuildTempObj(() => { - Config.TownHP = me.hardcore ? 0 : 35; - Config.AttackSkill = [-1, sdk.skills.BoneSpear, -1, sdk.skills.BoneSpear, -1, -1, -1]; - Config.LowManaSkill = [sdk.skills.Teeth, -1]; - Config.ExplodeCorpses = sdk.skills.CorpseExplosion; - Config.Golem = "Clay"; - Config.BeltColumn = ["hp", "hp", "mp", "mp"]; - Config.HPBuffer = me.expansion ? 2 : 4; - Config.MPBuffer = me.expansion && me.charlvl < 80 ? 6 : me.classic ? 5 : 2; - SetUp.belt(); -}); + AutoBuildTemplate: { + 1: { + Update: function () { + Config.TownHP = me.hardcore ? 0 : 35; + Config.AttackSkill = [-1, sdk.skills.BoneSpear, -1, sdk.skills.BoneSpear, -1, -1, -1]; + Config.LowManaSkill = [sdk.skills.Teeth, -1]; + Config.ExplodeCorpses = sdk.skills.CorpseExplosion; + Config.Golem = "Clay"; + Config.BeltColumn = ["hp", "hp", "mp", "mp"]; + Config.HPBuffer = me.expansion ? 2 : 4; + Config.MPBuffer = me.expansion && me.charlvl < 80 ? 6 : me.classic ? 5 : 2; + SetUp.belt(); + } + } + }, + }; + + // Has to be set after its loaded + build.stats = me.classic + ? [ + ["dexterity", 51], ["strength", 80], ["energy", 100], ["vitality", "all"] + ] : [ + ["strength", 48], ["vitality", 165], ["strength", 61], + ["vitality", 252], ["strength", 156], ["vitality", "all"] + ]; + + return build; + })(); +})(module); diff --git a/libs/SoloPlay/BuildFiles/necromancer/necromancer.PoisonBuild.js b/libs/SoloPlay/BuildFiles/necromancer/necromancer.PoisonBuild.js index 93ceae86..902a7990 100644 --- a/libs/SoloPlay/BuildFiles/necromancer/necromancer.PoisonBuild.js +++ b/libs/SoloPlay/BuildFiles/necromancer/necromancer.PoisonBuild.js @@ -5,157 +5,163 @@ * */ -const finalBuild = { - caster: true, - skillstab: sdk.skills.tabs.PoisonandBone, - wantedskills: [sdk.skills.PoisonNova, sdk.skills.CorpseExplosion], - usefulskills: [sdk.skills.AmplifyDamage, sdk.skills.BoneArmor, sdk.skills.LowerResist], - precastSkills: [sdk.skills.BoneArmor], - usefulStats: [sdk.stats.PassivePoisonMastery, sdk.stats.PassivePoisonPierce], - mercDiff: sdk.difficulty.Nightmare, - mercAct: 2, - mercAuraWanted: "Holy Freeze", - classicStats: [ - ["dexterity", 51], ["strength", 80], ["energy", 100], ["vitality", "all"] - ], - expansionStats: [ - ["strength", 48], ["vitality", 165], ["strength", 61], - ["vitality", 252], ["strength", 156], ["vitality", "all"] - ], - skills: [ - [sdk.skills.LowerResist, 5], - [sdk.skills.SummonResist, 1], - [sdk.skills.BonePrison, 1], - [sdk.skills.PoisonNova, 20], - [sdk.skills.PoisonExplosion, 20], - [sdk.skills.PoisonDagger, 20], - [sdk.skills.BonePrison, 10], - [sdk.skills.BoneSpear, 10], - [sdk.skills.BonePrison, 20], - [sdk.skills.BoneSpear, 20], - ], - classicTiers: [ - // Weapon - Blackbog's Sharp - "[name] == cinquedeas && [quality] == unique # [ias] == 30 && [skillpoisonnova] == 4 # [tier] == 100000", - // Helm - Tarnhelm - "[name] == skullcap && [quality] == unique # [itemallskills] == 1 && [itemmagicbonus] >= 25 # [tier] == tierscore(item, 100000)", - // Shield - "[type] == shield && [quality] >= magic # [necromancerskills] == 2 && [allres] >= 16 # [tier] == tierscore(item, 100000)", - // Rings - SoJ - "[type] == ring && [quality] == unique # [itemmaxmanapercent] == 25 # [tier] == 100000", - // Amulet - "[type] == amulet && [quality] >= magic # [necromancerskills] == 2 && [fcr] == 10 # [tier] == tierscore(item, 100000)", - // Boots - "[type] == boots && [quality] >= magic # [frw] >= 20 && [fhr] == 10 && [coldresist]+[lightresist] >= 10 # [tier] == tierscore(item, 100000)", - // Belt - "[type] == belt && [quality] >= magic # [fhr] >= 20 && [maxhp] >= 40 && [fireresist]+[lightresist] >= 20 # [tier] == tierscore(item, 100000)", - // Gloves - Magefist - "[name] == lightgauntlets && [quality] == unique # [fcr] >= 20 && [addfireskills] == 1 # [tier] == tierscore(item, 100000)", - ], - expansionTiers: [ - // Weapon - "([type] == wand || [type] == sword && ([quality] >= magic || [flag] == runeword) || [type] == knife && [quality] >= magic) && [flag] != ethereal # [secondarymindamage] == 0 && [itemchargedskill] >= 0 # [tier] == tierscore(item)", - // Helmet - Harlequin's Crest - "[name] == shako && [quality] == unique && [flag] != ethereal # [damageresist] == 10 # [tier] == tierscore(item, 100000)", - // Belt - Arach's - "[name] == spiderwebsash && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 90 # [tier] == tierscore(item, 100000)", - // Final Boots - Sandstorm Treks - "[name] == scarabshellboots && [quality] == unique # [strength]+[vitality] >= 20 # [tier] == tierscore(item, 100000)", - // Boots - War Traveler - "[name] == battleboots && [quality] == unique && [flag] != ethereal # [itemmagicbonus] >= 50 # [tier] == tierscore(item, 5000)", - // Armor - Enigma - "[type] == armor && [flag] != ethereal && [flag] == runeword # [itemallskills] == 2 # [tier] == 100000", - // Shield - "([type] == shield && ([quality] >= magic || [flag] == runeword) || [type] == voodooheads) && [flag] != ethereal # [itemchargedskill] >= 0 # [tier] == tierscore(item)", - // Final Gloves - Perfect 2x Upp'ed Magefist - "[name] == crusadergauntlets && [quality] == unique && [flag] != ethereal # [enhanceddefense] == 30 && [addfireskills] == 1 # [tier] == 110000", - // Gloves - 2x Upp'ed Magefist - "[name] == crusadergauntlets && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 20 && [addfireskills] == 1 # [tier] == tierscore(item, 100000)", - // Gloves - Upp'ed Magefist - "[name] == battlegauntlets && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 20 && [addfireskills] == 1 # [tier] == tierscore(item, 100000)", - // Gloves - Magefist - "[name] == lightgauntlets && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 20 && [addfireskills] == 1 # [tier] == tierscore(item, 100000)", - // Amulet - "[type] == amulet && [quality] == unique # [strength] == 5 && [coldresist] >= 30 # [tier] == tierscore(item, 100000)", // Maras - // Rings - Dwarf Star & SoJ - "[name] == ring && [quality] == unique # [maxhp] >= 40 && [magicdamagereduction] >= 12 # [tier] == 99000", - "[type] == ring && [quality] == unique # [itemmaxmanapercent] == 25 # [tier] == 100000", - // Switch - CTA - "[minimumsockets] >= 5 && [flag] == runeword # [plusskillbattleorders] >= 1 # [secondarytier] == 100000", - // Switch - Spirit - "[name] == monarch && [flag] == runeword # [fcr] >= 25 && [maxmana] >= 89 # [secondarytier] == 110000", - // Merc Armor - Fortitude - "[type] == armor && [flag] == runeword # [enhanceddefense] >= 200 && [enhanceddamage] >= 300 # [merctier] == 100000", - // Merc Final Helmet - Eth Andy's - "[name] == demonhead && [quality] == unique && [flag] == ethereal # [strength] >= 25 && [enhanceddefense] >= 100 # [merctier] == 50000 + mercscore(item)", - // Merc Helmet - Andy's - "[name] == demonhead && [quality] == unique && [flag] != ethereal # [strength] >= 25 && [enhanceddefense] >= 100 # [merctier] == 40000 + mercscore(item)", - ], - stats: undefined, - autoEquipTiers: undefined, - charms: { - ResLife: { - max: 4, - have: [], - classid: sdk.items.SmallCharm, - stats: function (check) { - return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MaxHp) === 20); - } - }, +(function (module) { + module.exports = (function () { + const build = { + caster: true, + skillstab: sdk.skills.tabs.PoisonandBone, + wantedskills: [sdk.skills.PoisonNova, sdk.skills.CorpseExplosion], + usefulskills: [sdk.skills.AmplifyDamage, sdk.skills.BoneArmor, sdk.skills.LowerResist], + precastSkills: [sdk.skills.BoneArmor], + usefulStats: [sdk.stats.PassivePoisonMastery, sdk.stats.PassivePoisonPierce], + wantedMerc: MercData[sdk.skills.HolyFreeze], + skills: [ + [sdk.skills.LowerResist, 5], + [sdk.skills.SummonResist, 1], + [sdk.skills.BonePrison, 1], + [sdk.skills.PoisonNova, 20], + [sdk.skills.PoisonExplosion, 20], + [sdk.skills.PoisonDagger, 20], + [sdk.skills.BonePrison, 10], + [sdk.skills.BoneSpear, 10], + [sdk.skills.BonePrison, 20], + [sdk.skills.BoneSpear, 20], + ], + stats: [], - ResMf: { - max: 2, - have: [], - classid: sdk.items.SmallCharm, - stats: function (check) { - return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MagicBonus) === 7); - } - }, + charms: { + ResLife: { + max: 4, + have: [], + classid: sdk.items.SmallCharm, + stats: function (check) { + return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MaxHp) === 20); + } + }, - ResFHR: { - max: 2, - have: [], - classid: sdk.items.SmallCharm, - stats: function (check) { - return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.FHR) === 5); - } - }, + ResMf: { + max: 2, + have: [], + classid: sdk.items.SmallCharm, + stats: function (check) { + return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MagicBonus) === 7); + } + }, - Skiller: { - max: 2, - have: [], - classid: sdk.items.GrandCharm, - stats: function (check) { - return (!check.unique && check.classid === this.classid && check.getStat(sdk.stats.AddSkillTab, sdk.skills.tabs.PoisonandBone) === 1 - && check.getStat(sdk.stats.MaxHp) >= 40); - } - }, - }, - - AutoBuildTemplate: { - 1: { - Update: function () { - Config.AttackSkill = [-1, sdk.skills.PoisonNova, -1, sdk.skills.PoisonNova, -1, sdk.skills.BoneSpear, -1]; - Config.ExplodeCorpses = sdk.skills.CorpseExplosion; - Config.Golem = "Clay"; - } - }, - }, + ResFHR: { + max: 2, + have: [], + classid: sdk.items.SmallCharm, + stats: function (check) { + return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.FHR) === 5); + } + }, - respec: function () { - if (me.classic) { - return me.charlvl >= 75 && me.diablo; - } else { - return Check.haveItem("armor", "runeword", "Enigma"); - } - }, + Skiller: { + max: 2, + have: [], + classid: sdk.items.GrandCharm, + stats: function (check) { + return (!check.unique && check.classid === this.classid && check.getStat(sdk.stats.AddSkillTab, sdk.skills.tabs.PoisonandBone) === 1 + && check.getStat(sdk.stats.MaxHp) >= 40); + } + }, + }, + + AutoBuildTemplate: { + 1: { + Update: function () { + Config.AttackSkill = [-1, sdk.skills.PoisonNova, -1, sdk.skills.PoisonNova, -1, sdk.skills.BoneSpear, -1]; + Config.ExplodeCorpses = sdk.skills.CorpseExplosion; + Config.Golem = "Clay"; + } + }, + }, - active: function () { - return this.respec() && me.getSkill(sdk.skills.PoisonNova, sdk.skills.subindex.HardPoints) === 20; - }, -}; + respec: function () { + if (me.classic) { + return me.charlvl >= 75 && me.diablo; + } else { + return Check.haveItem("armor", "runeword", "Enigma"); + } + }, -// Has to be set after its loaded -finalBuild.stats = me.classic ? finalBuild.classicStats : finalBuild.expansionStats; -finalBuild.autoEquipTiers = me.classic ? finalBuild.classicTiers : finalBuild.expansionTiers; + active: function () { + return this.respec() && me.getSkill(sdk.skills.PoisonNova, sdk.skills.subindex.HardPoints) === 20; + }, + }; + + build.stats = me.classic + ? [ + ["dexterity", 51], ["strength", 80], ["energy", 100], ["vitality", "all"] + ] + : [ + ["strength", 48], ["vitality", 165], ["strength", 61], + ["vitality", 252], ["strength", 156], ["vitality", "all"] + ]; + + let finalGear = me.classic + ? [ + // Weapon - Blackbog's Sharp + "[name] == cinquedeas && [quality] == unique # [ias] == 30 && [skillpoisonnova] == 4 # [tier] == 100000", + // Helm - Tarnhelm + "[name] == skullcap && [quality] == unique # [itemallskills] == 1 && [itemmagicbonus] >= 25 # [tier] == tierscore(item, 100000)", + // Shield + "[type] == shield && [quality] >= magic # [necromancerskills] == 2 && [allres] >= 16 # [tier] == tierscore(item, 100000)", + // Rings - SoJ + "[type] == ring && [quality] == unique # [itemmaxmanapercent] == 25 # [tier] == 100000", + // Amulet + "[type] == amulet && [quality] >= magic # [necromancerskills] == 2 && [fcr] == 10 # [tier] == tierscore(item, 100000)", + // Boots + "[type] == boots && [quality] >= magic # [frw] >= 20 && [fhr] == 10 && [coldresist]+[lightresist] >= 10 # [tier] == tierscore(item, 100000)", + // Belt + "[type] == belt && [quality] >= magic # [fhr] >= 20 && [maxhp] >= 40 && [fireresist]+[lightresist] >= 20 # [tier] == tierscore(item, 100000)", + // Gloves - Magefist + "[name] == lightgauntlets && [quality] == unique # [fcr] >= 20 && [addfireskills] == 1 # [tier] == tierscore(item, 100000)", + ] : [ + // Weapon + "([type] == wand || [type] == sword && ([quality] >= magic || [flag] == runeword) || [type] == knife && [quality] >= magic) && [flag] != ethereal # [secondarymindamage] == 0 && [itemchargedskill] >= 0 # [tier] == tierscore(item)", + // Helmet - Harlequin's Crest + "[name] == shako && [quality] == unique && [flag] != ethereal # [damageresist] == 10 # [tier] == tierscore(item, 100000)", + // Belt - Arach's + "[name] == spiderwebsash && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 90 # [tier] == tierscore(item, 100000)", + // Final Boots - Sandstorm Treks + "[name] == scarabshellboots && [quality] == unique # [strength]+[vitality] >= 20 # [tier] == tierscore(item, 100000)", + // Boots - War Traveler + "[name] == battleboots && [quality] == unique && [flag] != ethereal # [itemmagicbonus] >= 50 # [tier] == tierscore(item, 5000)", + // Armor - Enigma + "[type] == armor && [flag] != ethereal && [flag] == runeword # [itemallskills] == 2 # [tier] == 100000", + // Shield + "([type] == shield && ([quality] >= magic || [flag] == runeword) || [type] == voodooheads) && [flag] != ethereal # [itemchargedskill] >= 0 # [tier] == tierscore(item)", + // Final Gloves - Perfect 2x Upp'ed Magefist + "[name] == crusadergauntlets && [quality] == unique && [flag] != ethereal # [enhanceddefense] == 30 && [addfireskills] == 1 # [tier] == 110000", + // Gloves - 2x Upp'ed Magefist + "[name] == crusadergauntlets && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 20 && [addfireskills] == 1 # [tier] == tierscore(item, 100000)", + // Gloves - Upp'ed Magefist + "[name] == battlegauntlets && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 20 && [addfireskills] == 1 # [tier] == tierscore(item, 100000)", + // Gloves - Magefist + "[name] == lightgauntlets && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 20 && [addfireskills] == 1 # [tier] == tierscore(item, 100000)", + // Amulet + "[type] == amulet && [quality] == unique # [strength] == 5 && [coldresist] >= 30 # [tier] == tierscore(item, 100000)", // Maras + // Rings - Dwarf Star & SoJ + "[name] == ring && [quality] == unique # [maxhp] >= 40 && [magicdamagereduction] >= 12 # [tier] == 99000", + "[type] == ring && [quality] == unique # [itemmaxmanapercent] == 25 # [tier] == 100000", + // Switch - CTA + "[minimumsockets] >= 5 && [flag] == runeword # [plusskillbattleorders] >= 1 # [secondarytier] == 100000", + // Switch - Spirit + "[name] == monarch && [flag] == runeword # [fcr] >= 25 && [maxmana] >= 89 # [secondarytier] == 110000", + // Merc Armor - Fortitude + "[type] == armor && [flag] == runeword # [enhanceddefense] >= 200 && [enhanceddamage] >= 300 # [merctier] == 100000", + // Merc Final Helmet - Eth Andy's + "[name] == demonhead && [quality] == unique && [flag] == ethereal # [strength] >= 25 && [enhanceddefense] >= 100 # [merctier] == 50000 + mercscore(item)", + // Merc Helmet - Andy's + "[name] == demonhead && [quality] == unique && [flag] != ethereal # [strength] >= 25 && [enhanceddefense] >= 100 # [merctier] == 40000 + mercscore(item)", + ]; + + NTIP.buildList(finalGear); + NTIP.buildFinalGear(finalGear); + + return build; + })(); +})(module); diff --git a/libs/SoloPlay/BuildFiles/necromancer/necromancer.StartBuild.js b/libs/SoloPlay/BuildFiles/necromancer/necromancer.StartBuild.js index 6f1cc864..7455afb2 100644 --- a/libs/SoloPlay/BuildFiles/necromancer/necromancer.StartBuild.js +++ b/libs/SoloPlay/BuildFiles/necromancer/necromancer.StartBuild.js @@ -5,72 +5,79 @@ * */ -let build = { - AutoBuildTemplate: {}, - caster: true, - skillstab: sdk.skills.tabs.PoisonandBone, - wantedskills: [sdk.skills.Teeth, sdk.skills.BoneSpear], - usefulskills: [sdk.skills.AmplifyDamage, sdk.skills.BoneArmor, sdk.skills.Decrepify, sdk.skills.BoneWall], - mercDiff: sdk.difficulty.Nightmare, - mercAct: 2, - mercAuraWanted: "Might", - stats: [ - ["strength", 20], ["vitality", 70], ["strength", 35], - ["energy", 85], ["vitality", "all"] - ], - skills: [ - [sdk.skills.Teeth, 4], // charlvl 4 - [sdk.skills.AmplifyDamage, 1], // charlvl 5 - [sdk.skills.ClayGolem, 1], // charlvl 6 - [sdk.skills.BoneArmor, 1], // charlvl 7 - [sdk.skills.Weaken, 1], // charlvl 8 - [sdk.skills.DimVision, 1], // charlvl 9 - [sdk.skills.Teeth, 7], // charlvl 11 - [sdk.skills.GolemMastery, 1], // charlvl 12 - [sdk.skills.IronMaiden, 1], // charlvl 13 - [sdk.skills.CorpseExplosion, 1], // charlvl 14 - [sdk.skills.BoneWall, 3], // charlvl 17 - [sdk.skills.BoneSpear, 6], // charlvl 23 - [sdk.skills.Decrepify, 1], // charlvl 24 - [sdk.skills.BoneSpear, 20], // charlvl -> Until respec at 26 - ], - active: function () { - return me.charlvl < CharInfo.respecOne && !me.checkSkill(sdk.skills.BonePrison, sdk.skills.subindex.HardPoints); - }, -}; +(function (module, require) { + module.exports = (function () { + const build = { + AutoBuildTemplate: {}, + caster: true, + skillstab: sdk.skills.tabs.PoisonandBone, + wantedskills: [sdk.skills.Teeth, sdk.skills.BoneSpear], + usefulskills: [sdk.skills.AmplifyDamage, sdk.skills.BoneArmor, sdk.skills.Decrepify, sdk.skills.BoneWall], + wantedMerc: MercData[sdk.skills.Might], + stats: [ + ["strength", 20], ["vitality", 70], ["strength", 35], + ["energy", 85], ["vitality", "all"] + ], + skills: [ + [sdk.skills.Teeth, 4], // charlvl 4 + [sdk.skills.AmplifyDamage, 1], // charlvl 5 + [sdk.skills.ClayGolem, 1], // charlvl 6 + [sdk.skills.BoneArmor, 1], // charlvl 7 + [sdk.skills.Weaken, 1], // charlvl 8 + [sdk.skills.DimVision, 1], // charlvl 9 + [sdk.skills.Teeth, 7], // charlvl 11 + [sdk.skills.GolemMastery, 1], // charlvl 12 + [sdk.skills.IronMaiden, 1], // charlvl 13 + [sdk.skills.CorpseExplosion, 1], // charlvl 14 + [sdk.skills.BoneWall, 3], // charlvl 17 + [sdk.skills.BoneSpear, 6], // charlvl 23 + [sdk.skills.Decrepify, 1], // charlvl 24 + [sdk.skills.BoneSpear, 20], // charlvl -> Until respec at 26 + ], -build.AutoBuildTemplate[1] = buildAutoBuildTempObj(() => { - Config.ScanShrines.indexOf(sdk.shrines.Combat) === -1 && Config.ScanShrines.push(sdk.shrines.Combat); - Config.FieldID.Enabled = !Misc.checkQuest(sdk.quest.id.TheSearchForCain, sdk.quest.states.Completed); - Config.FieldID.UsedSpace = 0; - - Config.TownHP = me.hardcore ? 0 : 35; - Config.BeltColumn = ["hp", "hp", "hp", "hp"]; - Config.HPBuffer = 6; - Config.MPBuffer = 6; - Config.AttackSkill = [-1, 0, 0, 0, 0, -1, -1]; - Config.LowManaSkill = [0, 0]; - Config.Golem = "Clay"; - SetUp.belt(); -}); -build.AutoBuildTemplate[2] = buildAutoBuildTempObj(() => { - Config.ScanShrines.indexOf(sdk.shrines.Combat) === -1 && Config.ScanShrines.push(sdk.shrines.Combat); - Config.FieldID.Enabled = !Misc.checkQuest(sdk.quest.id.TheSearchForCain, sdk.quest.states.Completed); - Config.FieldID.UsedSpace = 0; - - Config.AttackSkill = [-1, sdk.skills.Teeth, -1, sdk.skills.Teeth, -1, -1, -1]; - Config.BeltColumn = ["hp", "hp", "mp", "mp"]; - Config.HPBuffer = 2; - Config.MPBuffer = 6; - SetUp.belt(); -}); -build.AutoBuildTemplate[18] = buildAutoBuildTempObj(() => { - Config.AttackSkill = [-1, sdk.skills.Teeth, -1, sdk.skills.Teeth, -1, -1, -1]; - Config.ExplodeCorpses = sdk.skills.CorpseExplosion; + active: function () { + return me.charlvl < CharInfo.respecOne && !me.checkSkill(sdk.skills.BonePrison, sdk.skills.subindex.HardPoints); + }, + }; - if (me.checkSkill(sdk.skills.BoneSpear, sdk.skills.subindex.HardPoints)) { - Config.AttackSkill = [-1, sdk.skills.BoneSpear, -1, sdk.skills.BoneSpear, -1, -1, -1]; - Config.LowManaSkill = [sdk.skills.Teeth, -1]; - } -}); + const { buildAutoBuildTempObj } = require("../../Utils/General"); + + build.AutoBuildTemplate[1] = buildAutoBuildTempObj(() => { + Config.ScanShrines.indexOf(sdk.shrines.Combat) === -1 && Config.ScanShrines.push(sdk.shrines.Combat); + Config.FieldID.Enabled = !Misc.checkQuest(sdk.quest.id.TheSearchForCain, sdk.quest.states.Completed); + Config.FieldID.UsedSpace = 0; + + Config.TownHP = me.hardcore ? 0 : 35; + Config.BeltColumn = ["hp", "hp", "hp", "hp"]; + Config.HPBuffer = 6; + Config.MPBuffer = 6; + Config.AttackSkill = [-1, 0, 0, 0, 0, -1, -1]; + Config.LowManaSkill = [0, 0]; + Config.Golem = "Clay"; + SetUp.belt(); + }); + build.AutoBuildTemplate[2] = buildAutoBuildTempObj(() => { + Config.ScanShrines.indexOf(sdk.shrines.Combat) === -1 && Config.ScanShrines.push(sdk.shrines.Combat); + Config.FieldID.Enabled = !Misc.checkQuest(sdk.quest.id.TheSearchForCain, sdk.quest.states.Completed); + Config.FieldID.UsedSpace = 0; + + Config.AttackSkill = [-1, sdk.skills.Teeth, -1, sdk.skills.Teeth, -1, -1, -1]; + Config.BeltColumn = ["hp", "hp", "mp", "mp"]; + Config.HPBuffer = 2; + Config.MPBuffer = 6; + SetUp.belt(); + }); + build.AutoBuildTemplate[18] = buildAutoBuildTempObj(() => { + Config.AttackSkill = [-1, sdk.skills.Teeth, -1, sdk.skills.Teeth, -1, -1, -1]; + Config.ExplodeCorpses = sdk.skills.CorpseExplosion; + + if (me.checkSkill(sdk.skills.BoneSpear, sdk.skills.subindex.HardPoints)) { + Config.AttackSkill = [-1, sdk.skills.BoneSpear, -1, sdk.skills.BoneSpear, -1, -1, -1]; + Config.LowManaSkill = [sdk.skills.Teeth, -1]; + } + }); + + return build; + })(); +})(module, require); diff --git a/libs/SoloPlay/BuildFiles/necromancer/necromancer.SummonBuild.js b/libs/SoloPlay/BuildFiles/necromancer/necromancer.SummonBuild.js index 1037d373..a0f22cf9 100644 --- a/libs/SoloPlay/BuildFiles/necromancer/necromancer.SummonBuild.js +++ b/libs/SoloPlay/BuildFiles/necromancer/necromancer.SummonBuild.js @@ -5,170 +5,177 @@ * */ -const finalBuild = { - caster: true, - skillstab: sdk.skills.tabs.NecroSummoning, - wantedskills: [sdk.skills.RaiseSkeleton, sdk.skills.CorpseExplosion], - usefulskills: [sdk.skills.AmplifyDamage, sdk.skills.SkeletonMastery, sdk.skills.BoneArmor, sdk.skills.Decrepify], - precastSkills: [sdk.skills.BoneArmor], - mercDiff: sdk.difficulty.Nightmare, - mercAct: 2, - mercAuraWanted: "Might", - classicStats: [ - ["dexterity", 51], ["strength", 80], ["energy", 100], ["vitality", "all"] - ], - expansionStats: [ - ["strength", 48], ["vitality", 165], ["strength", 61], - ["vitality", 252], ["strength", 156], ["vitality", "all"] - ], - classicSkills: [ - [sdk.skills.SummonResist, 1], - [sdk.skills.BonePrison, 1], - [sdk.skills.Decrepify, 1], - [sdk.skills.LowerResist, 1], - [sdk.skills.RaiseSkeleton, 20, false], - [sdk.skills.SkeletonMastery, 20, false], - [sdk.skills.PoisonNova, 20, false], - [sdk.skills.LowerResist, 5, false], - [sdk.skills.SummonResist, 5, false], - [sdk.skills.RaiseSkeletalMage, 20, false], - [sdk.skills.BonePrison, 20, false], - ], - expansionSkills: [ - [sdk.skills.SummonResist, 1], - [sdk.skills.BonePrison, 1], - [sdk.skills.Decrepify, 1], - [sdk.skills.RaiseSkeleton, 20, false], - [sdk.skills.SkeletonMastery, 20, false], - [sdk.skills.CorpseExplosion, 20, false], - [sdk.skills.AmplifyDamage, 20, false], - [sdk.skills.Revive, 20, false], - ], - classicTiers: [ - // Weapon - Blackbog's Sharp - "[name] == cinquedeas && [quality] == unique # [ias] == 30 && [skillpoisonnova] == 4 # [tier] == 100000", - // Helm - Tarnhelm - "[name] == skullcap && [quality] == unique # [itemallskills] == 1 && [itemmagicbonus] >= 25 # [tier] == tierscore(item, 100000)", - // Shield - "[type] == shield && [quality] >= magic # [necromancerskills] == 2 && [allres] >= 16 # [tier] == tierscore(item, 100000)", - // Rings - SoJ - "[type] == ring && [quality] == unique # [itemmaxmanapercent] == 25 # [tier] == 100000", - // Amulet - "[type] == amulet && [quality] >= magic # [necromancerskills] == 2 && [fcr] == 10 # [tier] == tierscore(item, 100000)", - // Boots - "[type] == boots && [quality] >= magic # [frw] >= 20 && [fhr] == 10 && [coldresist]+[lightresist] >= 10 # [tier] == tierscore(item, 100000)", - // Belt - "[type] == belt && [quality] >= magic # [fhr] >= 20 && [maxhp] >= 40 && [fireresist]+[lightresist] >= 20 # [tier] == tierscore(item, 100000)", - // Gloves - Magefist - "[name] == lightgauntlets && [quality] == unique # [fcr] >= 20 && [addfireskills] == 1 # [tier] == tierscore(item, 100000)", - ], - expansionTiers: [ - // Weapon - "([type] == wand || [type] == sword && ([quality] >= magic || [flag] == runeword) || [type] == knife && [quality] >= magic) && [flag] != ethereal # [secondarymindamage] == 0 && [itemchargedskill] >= 0 # [tier] == tierscore(item)", - // Helmet - Harlequin's Crest - "[name] == shako && [quality] == unique && [flag] != ethereal # [damageresist] == 10 # [tier] == tierscore(item, 100000)", - // Belt - Arach's - "[name] == spiderwebsash && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 90 # [tier] == tierscore(item, 100000)", - // Boots - Marrowalk - "[name] == boneweaveboots && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 170 # [tier] == tierscore(item, 100000)", - // Boots - War Traveler - "[name] == battleboots && [quality] == unique && [flag] != ethereal # [itemmagicbonus] >= 50 # [tier] == tierscore(item, 5000)", - // Armor - Enigma - "[type] == armor && [flag] != ethereal && [flag] == runeword # [itemallskills] == 2 # [tier] == 100000", - // Shield - "([type] == shield && ([quality] >= magic || [flag] == runeword) || [type] == voodooheads) && [flag] != ethereal # [itemchargedskill] >= 0 # [tier] == tierscore(item)", - // Final Gloves - Perfect 2x Upp'ed Magefist - "[name] == crusadergauntlets && [quality] == unique && [flag] != ethereal # [enhanceddefense] == 30 && [addfireskills] == 1 # [tier] == 110000", - // Gloves - 2x Upp'ed Magefist - "[name] == crusadergauntlets && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 20 && [addfireskills] == 1 # [tier] == tierscore(item, 100000)", - // Gloves - Upp'ed Magefist - "[name] == battlegauntlets && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 20 && [addfireskills] == 1 # [tier] == tierscore(item, 100000)", - // Gloves - Magefist - "[name] == lightgauntlets && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 20 && [addfireskills] == 1 # [tier] == tierscore(item, 100000)", - // Amulet - Maras - "[type] == amulet && [quality] == unique # [strength] == 5 && [coldresist] >= 30 # [tier] == tierscore(item, 100000)", - // Final Rings - SoJ & Perfect Raven Frost - "[type] == ring && [quality] == unique # [itemmaxmanapercent] == 25 # [tier] == 100000", - "[type] == ring && [quality] == unique # [dexterity] >= 20 # [tier] == 100000", - // Rings - Dwarf Star & Raven Frost - "[name] == ring && [quality] == unique # [maxhp] >= 40 && [magicdamagereduction] >= 12 # [tier] == 99000", - "[type] == ring && [quality] == unique # [dexterity] >= 20 # [tier] == 99000", - // Switch - CTA - "[minimumsockets] >= 5 && [flag] == runeword # [plusskillbattleorders] >= 1 # [secondarytier] == 100000", - // Switch - Spirit - "[name] == monarch && [flag] == runeword # [fcr] >= 25 && [maxmana] >= 89 # [secondarytier] == 110000", - // Merc Armor - Fortitude - "[type] == armor && [flag] == runeword # [enhanceddefense] >= 200 && [enhanceddamage] >= 300 # [merctier] == 100000", - // Merc Final Helmet - Eth Andy's - "[name] == demonhead && [quality] == unique && [flag] == ethereal # [strength] >= 25 && [enhanceddefense] >= 100 # [merctier] == 50000 + mercscore(item)", - // Merc Helmet - Andy's - "[name] == demonhead && [quality] == unique && [flag] != ethereal # [strength] >= 25 && [enhanceddefense] >= 100 # [merctier] == 40000 + mercscore(item)", - ], - stats: undefined, - skills: undefined, - autoEquipTiers: undefined, - charms: { - ResLife: { - max: 4, - have: [], - classid: sdk.items.SmallCharm, - stats: function (check) { - return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MaxHp) === 20); - } - }, +(function (module) { + module.exports = (function () { + const build = { + caster: true, + skillstab: sdk.skills.tabs.NecroSummoning, + wantedskills: [sdk.skills.RaiseSkeleton, sdk.skills.CorpseExplosion], + usefulskills: [sdk.skills.AmplifyDamage, sdk.skills.SkeletonMastery, sdk.skills.BoneArmor, sdk.skills.Decrepify], + precastSkills: [sdk.skills.BoneArmor], + wantedMerc: MercData[sdk.skills.Might], + stats: [], + skills: [], - ResMf: { - max: 4, - have: [], - classid: sdk.items.SmallCharm, - stats: function (check) { - return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MagicBonus) === 7); - } - }, + charms: { + ResLife: { + max: 4, + have: [], + classid: sdk.items.SmallCharm, + stats: function (check) { + return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MaxHp) === 20); + } + }, - Skiller: { - max: 2, - have: [], - classid: sdk.items.GrandCharm, - stats: function (check) { - return (!check.unique && check.classid === this.classid && check.getStat(sdk.stats.AddSkillTab, sdk.skills.tabs.NecroSummoning) === 1 - && check.getStat(sdk.stats.MaxHp) >= 40); - } - }, - }, + ResMf: { + max: 4, + have: [], + classid: sdk.items.SmallCharm, + stats: function (check) { + return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MagicBonus) === 7); + } + }, - AutoBuildTemplate: { - 1: { - Update: function () { - Config.AttackSkill = me.classic - ? [-1, sdk.skills.PoisonNova, -1, sdk.skills.PoisonNova, -1, sdk.skills.BoneSpear, -1] - : [-1, sdk.skills.BoneSpear, -1, sdk.skills.BoneSpear, -1, -1, -1]; - Config.LowManaSkill = [0, 0]; - Config.ActiveSummon = true; - Config.Skeletons = "max"; - Config.SkeletonMages = "max"; - Config.Revives = "max"; - Config.Golem = "Clay"; - Config.MPBuffer = me.expansion ? 4 : 6; - } - }, - }, + Skiller: { + max: 2, + have: [], + classid: sdk.items.GrandCharm, + stats: function (check) { + return (!check.unique && check.classid === this.classid && check.getStat(sdk.stats.AddSkillTab, sdk.skills.tabs.NecroSummoning) === 1 + && check.getStat(sdk.stats.MaxHp) >= 40); + } + }, + }, - respec: function () { - if (me.classic) { - return me.charlvl >= 75 && me.diablo; - } else { - return Check.haveItem("armor", "runeword", "Enigma"); - } - }, + AutoBuildTemplate: { + 1: { + Update: function () { + Config.AttackSkill = me.classic + ? [-1, sdk.skills.PoisonNova, -1, sdk.skills.PoisonNova, -1, sdk.skills.BoneSpear, -1] + : [-1, sdk.skills.BoneSpear, -1, sdk.skills.BoneSpear, -1, -1, -1]; + Config.LowManaSkill = [0, 0]; + Config.ActiveSummon = true; + Config.Skeletons = "max"; + Config.SkeletonMages = "max"; + Config.Revives = "max"; + Config.Golem = "Clay"; + Config.MPBuffer = me.expansion ? 4 : 6; + } + }, + }, - active: function () { - return this.respec() && me.getSkill(sdk.skills.RaiseSkeleton, sdk.skills.subindex.HardPoints) === 20; - }, -}; + respec: function () { + if (me.classic) { + return me.charlvl >= 75 && me.diablo; + } else { + return Check.haveItem("armor", "runeword", "Enigma"); + } + }, -// Has to be set after its loaded -finalBuild.stats = me.classic ? finalBuild.classicStats : finalBuild.expansionStats; -finalBuild.skills = me.classic ? finalBuild.classicSkills : finalBuild.expansionSkills; -finalBuild.autoEquipTiers = me.classic ? finalBuild.classicTiers : finalBuild.expansionTiers; + active: function () { + return this.respec() && me.getSkill(sdk.skills.RaiseSkeleton, sdk.skills.subindex.HardPoints) === 20; + }, + }; + + build.stats = me.classic + ? [ + ["dexterity", 51], ["strength", 80], ["energy", 100], ["vitality", "all"] + ] + : [ + ["strength", 48], ["vitality", 165], ["strength", 61], + ["vitality", 252], ["strength", 156], ["vitality", "all"] + ]; + + build.skills = me.classic + ? [ + [sdk.skills.SummonResist, 1], + [sdk.skills.BonePrison, 1], + [sdk.skills.Decrepify, 1], + [sdk.skills.LowerResist, 1], + [sdk.skills.RaiseSkeleton, 20, false], + [sdk.skills.SkeletonMastery, 20, false], + [sdk.skills.PoisonNova, 20, false], + [sdk.skills.LowerResist, 5, false], + [sdk.skills.SummonResist, 5, false], + [sdk.skills.RaiseSkeletalMage, 20, false], + [sdk.skills.BonePrison, 20, false], + ] + : [ + [sdk.skills.SummonResist, 1], + [sdk.skills.BonePrison, 1], + [sdk.skills.Decrepify, 1], + [sdk.skills.RaiseSkeleton, 20, false], + [sdk.skills.SkeletonMastery, 20, false], + [sdk.skills.CorpseExplosion, 20, false], + [sdk.skills.AmplifyDamage, 20, false], + [sdk.skills.Revive, 20, false], + ]; + + let finalGear = me.classic + ? [ + // Weapon - Blackbog's Sharp + "[name] == cinquedeas && [quality] == unique # [ias] == 30 && [skillpoisonnova] == 4 # [tier] == 100000", + // Helm - Tarnhelm + "[name] == skullcap && [quality] == unique # [itemallskills] == 1 && [itemmagicbonus] >= 25 # [tier] == tierscore(item, 100000)", + // Shield + "[type] == shield && [quality] >= magic # [necromancerskills] == 2 && [allres] >= 16 # [tier] == tierscore(item, 100000)", + // Rings - SoJ + "[type] == ring && [quality] == unique # [itemmaxmanapercent] == 25 # [tier] == 100000", + // Amulet + "[type] == amulet && [quality] >= magic # [necromancerskills] == 2 && [fcr] == 10 # [tier] == tierscore(item, 100000)", + // Boots + "[type] == boots && [quality] >= magic # [frw] >= 20 && [fhr] == 10 && [coldresist]+[lightresist] >= 10 # [tier] == tierscore(item, 100000)", + // Belt + "[type] == belt && [quality] >= magic # [fhr] >= 20 && [maxhp] >= 40 && [fireresist]+[lightresist] >= 20 # [tier] == tierscore(item, 100000)", + // Gloves - Magefist + "[name] == lightgauntlets && [quality] == unique # [fcr] >= 20 && [addfireskills] == 1 # [tier] == tierscore(item, 100000)", + ] : [ + // Weapon + "([type] == wand || [type] == sword && ([quality] >= magic || [flag] == runeword) || [type] == knife && [quality] >= magic) && [flag] != ethereal # [secondarymindamage] == 0 && [itemchargedskill] >= 0 # [tier] == tierscore(item)", + // Helmet - Harlequin's Crest + "[name] == shako && [quality] == unique && [flag] != ethereal # [damageresist] == 10 # [tier] == tierscore(item, 100000)", + // Belt - Arach's + "[name] == spiderwebsash && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 90 # [tier] == tierscore(item, 100000)", + // Boots - Marrowalk + "[name] == boneweaveboots && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 170 # [tier] == tierscore(item, 100000)", + // Boots - War Traveler + "[name] == battleboots && [quality] == unique && [flag] != ethereal # [itemmagicbonus] >= 50 # [tier] == tierscore(item, 5000)", + // Armor - Enigma + "[type] == armor && [flag] != ethereal && [flag] == runeword # [itemallskills] == 2 # [tier] == 100000", + // Shield + "([type] == shield && ([quality] >= magic || [flag] == runeword) || [type] == voodooheads) && [flag] != ethereal # [itemchargedskill] >= 0 # [tier] == tierscore(item)", + // Final Gloves - Perfect 2x Upp'ed Magefist + "[name] == crusadergauntlets && [quality] == unique && [flag] != ethereal # [enhanceddefense] == 30 && [addfireskills] == 1 # [tier] == 110000", + // Gloves - 2x Upp'ed Magefist + "[name] == crusadergauntlets && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 20 && [addfireskills] == 1 # [tier] == tierscore(item, 100000)", + // Gloves - Upp'ed Magefist + "[name] == battlegauntlets && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 20 && [addfireskills] == 1 # [tier] == tierscore(item, 100000)", + // Gloves - Magefist + "[name] == lightgauntlets && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 20 && [addfireskills] == 1 # [tier] == tierscore(item, 100000)", + // Amulet - Maras + "[type] == amulet && [quality] == unique # [strength] == 5 && [coldresist] >= 30 # [tier] == tierscore(item, 100000)", + // Final Rings - SoJ & Perfect Raven Frost + "[type] == ring && [quality] == unique # [itemmaxmanapercent] == 25 # [tier] == 100000", + "[type] == ring && [quality] == unique # [dexterity] >= 20 # [tier] == 100000", + // Rings - Dwarf Star & Raven Frost + "[name] == ring && [quality] == unique # [maxhp] >= 40 && [magicdamagereduction] >= 12 # [tier] == 99000", + "[type] == ring && [quality] == unique # [dexterity] >= 20 # [tier] == 99000", + // Switch - CTA + "[minimumsockets] >= 5 && [flag] == runeword # [plusskillbattleorders] >= 1 # [secondarytier] == 100000", + // Switch - Spirit + "[name] == monarch && [flag] == runeword # [fcr] >= 25 && [maxmana] >= 89 # [secondarytier] == 110000", + // Merc Armor - Fortitude + "[type] == armor && [flag] == runeword # [enhanceddefense] >= 200 && [enhanceddamage] >= 300 # [merctier] == 100000", + // Merc Final Helmet - Eth Andy's + "[name] == demonhead && [quality] == unique && [flag] == ethereal # [strength] >= 25 && [enhanceddefense] >= 100 # [merctier] == 50000 + mercscore(item)", + // Merc Helmet - Andy's + "[name] == demonhead && [quality] == unique && [flag] != ethereal # [strength] >= 25 && [enhanceddefense] >= 100 # [merctier] == 40000 + mercscore(item)", + ]; + + NTIP.buildList(finalGear); + NTIP.buildFinalGear(finalGear); + + return build; + })(); +})(module); diff --git a/libs/SoloPlay/BuildFiles/paladin/paladin.AuradinBuild.js b/libs/SoloPlay/BuildFiles/paladin/paladin.AuradinBuild.js index 5be86b18..58a42e76 100644 --- a/libs/SoloPlay/BuildFiles/paladin/paladin.AuradinBuild.js +++ b/libs/SoloPlay/BuildFiles/paladin/paladin.AuradinBuild.js @@ -5,125 +5,134 @@ * */ -const finalBuild = { - caster: false, - skillstab: sdk.skills.tabs.PalaCombat, - wantedskills: [sdk.skills.Zeal, sdk.skills.Conviction], - usefulskills: [sdk.skills.HolyShield, sdk.skills.ResistFire, sdk.skills.ResistLightning], - precastSkills: [sdk.skills.HolyShield], - usefulStats: [sdk.stats.PassiveLightningMastery, sdk.stats.PassiveLightningPierce, sdk.stats.PierceLtng, sdk.stats.PassiveFireMastery, sdk.stats.PassiveFirePierce, sdk.stats.PierceFire], - mercDiff: sdk.difficulty.Nightmare, - mercAct: 2, - mercAuraWanted: "Holy Freeze", - stats: [ - ["strength", 103], ["dexterity", 136], ["vitality", 300], ["dexterity", "block"], ["vitality", "all"] - ], - skills: [ - [sdk.skills.Conviction, 20], - [sdk.skills.Zeal, 4], - [sdk.skills.ResistLightning, 20], - [sdk.skills.Salvation, 20], - [sdk.skills.ResistFire, 20], - [sdk.skills.Redemption, 1], - [sdk.skills.HolyShield, 15], - [sdk.skills.Zeal, 10], - ], - autoEquipTiers: [ // autoequip final gear - // Final Weapon - HoJ - "[type] == sword && [flag] == runeword # [holyfireaura] >= 16 # [tier] == 120000", - // Weapon - Crescent Moon & Voice of Reason - "[type] == sword && [flag] == runeword # [ias] >= 20 && [passiveltngpierce] >= 35 # [tier] == 110000", - "[type] == sword && [flag] == runeword # [passivecoldpierce] >= 24 # [tier] == 102500", - // Helm - Dream - "[type] == helm && [flag] == runeword # [holyshockaura] >= 15 # [tier] == 110000", - // Belt - TGods - "[name] == warbelt && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 160 # [tier] == tierscore(item, 110000)", - // Boots - Gore Rider - "[name] == warboots && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 160 # [tier] == tierscore(item, 110000)", - // Armor - Dragon - "[type] == armor && [flag] != ethereal && [flag] == runeword # [holyfireaura] >= 14 # [tier] == 110000", - // Shield - Dream - "[type] == auricshields && [flag] != ethereal && [flag] == runeword # [holyshockaura] >= 15 # [tier] == 110000", - // Gloves - Laying of Hand's - "[name] == bramblemitts && [quality] == set && [flag] != ethereal # [ias] >= 20 # [tier] == 110000", - // Amulet - Highlords - "[type] == amulet && [quality] == unique # [lightresist] == 35 # [tier] == 100000", - // Final Rings - Perfect Raven Frost & Bul-Kathos' Wedding Band - "[type] == ring && [quality] == unique # [dexterity] == 20 && [tohit] == 250 # [tier] == 110000", - "[type] == ring && [quality] == unique # [maxstamina] == 50 && [lifeleech] == 5 # [tier] == 110000", - // Rings - Raven Frost && Bul-Kathos' Wedding Band - "[type] == ring && [quality] == unique # [dexterity] >= 15 && [tohit] >= 150 # [tier] == 100000", - "[type] == ring && [quality] == unique # [maxstamina] == 50 && [lifeleech] >= 3 # [tier] == 100000", - // Switch - CTA - "[minimumsockets] >= 5 && [flag] == runeword # [plusskillbattleorders] >= 1 # [secondarytier] == 100000", - // Merc Final Armor - Fortitude - "[type] == armor && [flag] == runeword # [enhanceddefense] >= 200 && [enhanceddamage] >= 300 # [merctier] == 100000", - // Merc Armor - Treachery - "[type] == armor && [flag] == runeword # [ias] == 45 && [coldresist] == 30 # [merctier] == 50000 + mercscore(item)", - // Merc Final Helmet - Eth Andy's - "[name] == demonhead && [quality] == unique && [flag] == ethereal # [strength] >= 25 && [enhanceddefense] >= 100 # [merctier] == 50000 + mercscore(item)", - // Merc Helmet - Andy's - "[name] == demonhead && [quality] == unique && [flag] != ethereal # [strength] >= 25 && [enhanceddefense] >= 100 # [merctier] == 40000 + mercscore(item)", - ], - charms: { - ResLife: { - max: 6, - have: [], - classid: sdk.items.SmallCharm, - stats: function (check) { - return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MaxHp) === 20); - } - }, +(function (module) { + module.exports = (function () { + const build = { + caster: false, + skillstab: sdk.skills.tabs.PalaCombat, + wantedskills: [sdk.skills.Zeal, sdk.skills.Conviction], + usefulskills: [sdk.skills.HolyShield, sdk.skills.ResistFire, sdk.skills.ResistLightning], + precastSkills: [sdk.skills.HolyShield], + usefulStats: [sdk.stats.PassiveLightningMastery, sdk.stats.PassiveLightningPierce, sdk.stats.PierceLtng, sdk.stats.PassiveFireMastery, sdk.stats.PassiveFirePierce, sdk.stats.PierceFire], + wantedMerc: MercData[sdk.skills.HolyFreeze], + stats: [ + ["strength", 103], ["dexterity", 136], ["vitality", 300], ["dexterity", "block"], ["vitality", "all"] + ], + skills: [ + [sdk.skills.Conviction, 20], + [sdk.skills.Zeal, 4], + [sdk.skills.ResistLightning, 20], + [sdk.skills.Salvation, 20], + [sdk.skills.ResistFire, 20], + [sdk.skills.Redemption, 1], + [sdk.skills.HolyShield, 15], + [sdk.skills.Zeal, 10], + ], - ResMf: { - max: 2, - have: [], - classid: sdk.items.SmallCharm, - stats: function (check) { - return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MagicBonus) === 7); - } - }, + charms: { + ResLife: { + max: 6, + have: [], + classid: sdk.items.SmallCharm, + stats: function (check) { + return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MaxHp) === 20); + } + }, - Skiller: { - max: 2, - have: [], - classid: sdk.items.GrandCharm, - stats: function (check) { - return (!check.unique && check.classid === this.classid && check.getStat(sdk.stats.AddSkillTab, sdk.skills.tabs.Offensive) === 1 - && check.getStat(sdk.stats.MaxHp) >= 40); - } - }, - }, + ResMf: { + max: 2, + have: [], + classid: sdk.items.SmallCharm, + stats: function (check) { + return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MagicBonus) === 7); + } + }, - AutoBuildTemplate: { - 1: { - Update: function () { - Config.Vigor = false; - Config.AttackSkill = [-1, sdk.skills.Zeal, sdk.skills.Conviction, sdk.skills.Zeal, sdk.skills.Conviction, -1, -1]; - Config.LowManaSkill = [-1, -1]; + Skiller: { + max: 2, + have: [], + classid: sdk.items.GrandCharm, + stats: function (check) { + return (!check.unique && check.classid === this.classid && check.getStat(sdk.stats.AddSkillTab, sdk.skills.tabs.Offensive) === 1 + && check.getStat(sdk.stats.MaxHp) >= 40); + } + }, + }, - if (!me.haveSome([{ name: sdk.locale.items.Dragon, itemtype: sdk.items.type.Armor }, { name: sdk.locale.items.HandofJustice }])) { - Config.SkipImmune = ["lightning and physical"]; + AutoBuildTemplate: { + 1: { + Update: function () { + Config.Vigor = false; + Config.AttackSkill = [-1, sdk.skills.Zeal, sdk.skills.Conviction, sdk.skills.Zeal, sdk.skills.Conviction, -1, -1]; + Config.LowManaSkill = [-1, -1]; + + if (!me.haveSome([{ name: sdk.locale.items.Dragon, itemtype: sdk.items.type.Armor }, { name: sdk.locale.items.HandofJustice }])) { + Config.SkipImmune = ["lightning and physical"]; + } else { + Config.SkipImmune = ["lightning and fire and physical"]; // Don't think this ever happens but should skip if it does + } + + Config.BeltColumn = ["hp", "hp", "mp", "rv"]; + SetUp.belt(); + } + }, + }, + + respec: function () { + if (me.classic) { + return false; } else { - Config.SkipImmune = ["lightning and fire and physical"]; // Don't think this ever happens but should skip if it does + return me.haveAll([{ name: sdk.locale.items.Dream, itemtype: sdk.items.type.AuricShields }, { name: sdk.locale.items.Dream, itemtype: sdk.items.type.Helm }]); } + }, - Config.BeltColumn = ["hp", "hp", "mp", "rv"]; - SetUp.belt(); - } - }, - }, + active: function () { + return this.respec() && me.getSkill(sdk.skills.Conviction, sdk.skills.subindex.HardPoints) === 20; + }, + }; + + let finalGear = [ // autoequip final gear + // Final Weapon - HoJ + "[type] == sword && [flag] == runeword # [holyfireaura] >= 16 # [tier] == 120000", + // Weapon - Crescent Moon & Voice of Reason + "[type] == sword && [flag] == runeword # [ias] >= 20 && [passiveltngpierce] >= 35 # [tier] == 110000", + "[type] == sword && [flag] == runeword # [passivecoldpierce] >= 24 # [tier] == 102500", + // Helm - Dream + "[type] == helm && [flag] == runeword # [holyshockaura] >= 15 # [tier] == 110000", + // Belt - TGods + "[name] == warbelt && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 160 # [tier] == tierscore(item, 110000)", + // Boots - Gore Rider + "[name] == warboots && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 160 # [tier] == tierscore(item, 110000)", + // Armor - Dragon + "[type] == armor && [flag] != ethereal && [flag] == runeword # [holyfireaura] >= 14 # [tier] == 110000", + // Shield - Dream + "[type] == auricshields && [flag] != ethereal && [flag] == runeword # [holyshockaura] >= 15 # [tier] == 110000", + // Gloves - Laying of Hand's + "[name] == bramblemitts && [quality] == set && [flag] != ethereal # [ias] >= 20 # [tier] == 110000", + // Amulet - Highlords + "[type] == amulet && [quality] == unique # [lightresist] == 35 # [tier] == 100000", + // Final Rings - Perfect Raven Frost & Bul-Kathos' Wedding Band + "[type] == ring && [quality] == unique # [dexterity] == 20 && [tohit] == 250 # [tier] == 110000", + "[type] == ring && [quality] == unique # [maxstamina] == 50 && [lifeleech] == 5 # [tier] == 110000", + // Rings - Raven Frost && Bul-Kathos' Wedding Band + "[type] == ring && [quality] == unique # [dexterity] >= 15 && [tohit] >= 150 # [tier] == 100000", + "[type] == ring && [quality] == unique # [maxstamina] == 50 && [lifeleech] >= 3 # [tier] == 100000", + // Switch - CTA + "[minimumsockets] >= 5 && [flag] == runeword # [plusskillbattleorders] >= 1 # [secondarytier] == 100000", + // Merc Final Armor - Fortitude + "[type] == armor && [flag] == runeword # [enhanceddefense] >= 200 && [enhanceddamage] >= 300 # [merctier] == 100000", + // Merc Armor - Treachery + "[type] == armor && [flag] == runeword # [ias] == 45 && [coldresist] == 30 # [merctier] == 50000 + mercscore(item)", + // Merc Final Helmet - Eth Andy's + "[name] == demonhead && [quality] == unique && [flag] == ethereal # [strength] >= 25 && [enhanceddefense] >= 100 # [merctier] == 50000 + mercscore(item)", + // Merc Helmet - Andy's + "[name] == demonhead && [quality] == unique && [flag] != ethereal # [strength] >= 25 && [enhanceddefense] >= 100 # [merctier] == 40000 + mercscore(item)", + ]; - respec: function () { - if (me.classic) { - return false; - } else { - return me.haveAll([{ name: sdk.locale.items.Dream, itemtype: sdk.items.type.AuricShields }, { name: sdk.locale.items.Dream, itemtype: sdk.items.type.Helm }]); - } - }, + NTIP.buildList(finalGear); + NTIP.buildFinalGear(finalGear); - active: function () { - return this.respec() && me.getSkill(sdk.skills.Conviction, sdk.skills.subindex.HardPoints) === 20; - }, -}; + return build; + })(); +})(module); diff --git a/libs/SoloPlay/BuildFiles/paladin/paladin.ClassicauradinBuild.js b/libs/SoloPlay/BuildFiles/paladin/paladin.ClassicauradinBuild.js index a6e17c32..df7d9d75 100644 --- a/libs/SoloPlay/BuildFiles/paladin/paladin.ClassicauradinBuild.js +++ b/libs/SoloPlay/BuildFiles/paladin/paladin.ClassicauradinBuild.js @@ -5,148 +5,154 @@ * */ -const finalBuild = { - caster: false, - skillstab: sdk.skills.tabs.PalaCombat, - wantedskills: [sdk.skills.Zeal, sdk.skills.HolyShock], - usefulskills: [sdk.skills.HolyShield, sdk.skills.HolyFreeze, sdk.skills.ResistCold, sdk.skills.ResistLightning], - precastSkills: [sdk.skills.HolyShield], - mercDiff: sdk.difficulty.Nightmare, - mercAct: 2, - mercAuraWanted: "Holy Freeze", - classicStats: [ - ["strength", 80], ["vitality", "all"] - ], - expansionStats: [ - ["strength", 103], ["dexterity", 136], ["vitality", 300], - ["dexterity", "block"], ["vitality", "all"] - ], - skills: [ - [sdk.skills.Zeal, 4], - [sdk.skills.Redemption, 1], - [sdk.skills.HolyShield, 1], - [sdk.skills.HolyShock, 20], - [sdk.skills.HolyFreeze, 20], - [sdk.skills.Salvation, 20, false], - [sdk.skills.ResistLightning, 20, false], - [sdk.skills.Zeal, 20, false], - ], - classicTiers: [ - // Weapon - "[name] == warscepter && [quality] >= magic # [paladinskills] == 2 && [ias] == 40 && [skillholyshock] >= 1 # [tier] == tierscore(item, 100000)", - // Helm - Tarnhelm - "[name] == skullcap && [quality] == unique # [itemallskills] == 1 && [itemmagicbonus] >= 25 # [tier] == tierscore(item, 100000)", - // Shield - "[type] == shield && [quality] >= magic # [paladinskills] == 2 && [allres] >= 16 # [tier] == tierscore(item, 100000)", - // Rings - SoJ - "[type] == ring && [quality] == unique # [itemmaxmanapercent] == 25 # [tier] == 100000", - // Amulet - "[type] == amulet && [quality] >= magic # [paladinskills] == 2 && [allres] >= 16 # [tier] == tierscore(item, 100000)", - // Boots - Hsaru's Iron Heel - "[name] == chainboots && [quality] == set # [frw] == 20 # [tier] == 100000", - // Belt - Hsaru's Iron Stay - "[name] == belt && [quality] == set # [coldresist] == 20 && [maxhp] == 20 # [tier] == 100000", - ], - // idea: since this is based on the classic build, could do tri-element instead of just normal auradin - // max Holy Shock/Freeze then use HoJ + Dragon Armor/Shield for level 44 Holy Fire - // could even then do infinity for the merc to still have some conviction - expansionTiers: [ - // Final Weapon - HoJ - "[type] == sword && [flag] == runeword # [holyfireaura] >= 16 # [tier] == 120000", - // Weapon - Crescent Moon - "[type] == sword && [flag] == runeword # [ias] >= 20 && [passiveltngpierce] >= 35 # [tier] == 110000", - // Weapon - Voice of Reason - "[type] == sword && [flag] == runeword # [passivecoldpierce] >= 24 # [tier] == 102500", - // Helm - Dream - "[type] == helm && [flag] == runeword # [holyshockaura] >= 15 # [tier] == 110000", - // Belt - TGods - "[name] == warbelt && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 160 # [tier] == tierscore(item, 110000)", - // Boots - Gore Rider - "[name] == warboots && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 160 # [tier] == tierscore(item, 110000)", - // Armor - Dragon - "[type] == armor && [flag] != ethereal && [flag] == runeword # [holyfireaura] >= 14 # [tier] == 110000", - // Shield - Dream - "[type] == auricshields && [flag] != ethereal && [flag] == runeword # [holyshockaura] >= 15 # [tier] == 110000", - // Gloves - Laying of Hand's - "[name] == bramblemitts && [quality] == set && [flag] != ethereal # [ias] >= 20 # [tier] == 110000", - // Amulet - Highlords - "[type] == amulet && [quality] == unique # [lightresist] == 35 # [tier] == 100000", - // Final Rings - Perfect Raven Frost & Bul-Kathos' Wedding Band - "[type] == ring && [quality] == unique # [dexterity] == 20 && [tohit] == 250 # [tier] == 110000", - "[type] == ring && [quality] == unique # [maxstamina] == 50 && [lifeleech] == 5 # [tier] == 110000", - // Rings - Raven Frost && Bul-Kathos' Wedding Band - "[type] == ring && [quality] == unique # [dexterity] >= 15 && [tohit] >= 150 # [tier] == 100000", - "[type] == ring && [quality] == unique # [maxstamina] == 50 && [lifeleech] >= 3 # [tier] == 100000", - // Switch - CTA - "[minimumsockets] >= 5 && [flag] == runeword # [plusskillbattleorders] >= 1 # [secondarytier] == 100000", - // Merc Final Armor - Fortitude - "[type] == armor && [flag] == runeword # [enhanceddefense] >= 200 && [enhanceddamage] >= 300 # [merctier] == 100000", - // Merc Armor - Treachery - "[type] == armor && [flag] == runeword # [ias] == 45 && [coldresist] == 30 # [merctier] == 50000 + mercscore(item)", - // Merc Final Helmet - Eth Andy's - "[name] == demonhead && [quality] == unique && [flag] == ethereal # [strength] >= 25 && [enhanceddefense] >= 100 # [merctier] == 50000 + mercscore(item)", - // Merc Helmet - Andy's - "[name] == demonhead && [quality] == unique && [flag] != ethereal # [strength] >= 25 && [enhanceddefense] >= 100 # [merctier] == 40000 + mercscore(item)", - ], - stats: undefined, - autoEquipTiers: undefined, - charms: { - ResLife: { - max: 6, - have: [], - classid: sdk.items.SmallCharm, - stats: function (check) { - return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MaxHp) === 20); - } - }, +(function (module) { + module.exports = (function () { + // idea: since this is based on the classic build, could do tri-element instead of just normal auradin + // max Holy Shock/Freeze then use HoJ + Dragon Armor/Shield for level 44 Holy Fire + // could even then do infinity for the merc to still have some conviction + const build = { + caster: false, + skillstab: sdk.skills.tabs.PalaCombat, + wantedskills: [sdk.skills.Zeal, sdk.skills.HolyShock], + usefulskills: [sdk.skills.HolyShield, sdk.skills.HolyFreeze, sdk.skills.ResistCold, sdk.skills.ResistLightning], + precastSkills: [sdk.skills.HolyShield], + wantedMerc: MercData[sdk.skills.HolyFreeze], + skills: [ + [sdk.skills.Zeal, 4], + [sdk.skills.Redemption, 1], + [sdk.skills.HolyShield, 1], + [sdk.skills.HolyShock, 20], + [sdk.skills.HolyFreeze, 20], + [sdk.skills.Salvation, 20, false], + [sdk.skills.ResistLightning, 20, false], + [sdk.skills.Zeal, 20, false], + ], + stats: [], - ResMf: { - max: 2, - have: [], - classid: sdk.items.SmallCharm, - stats: function (check) { - return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MagicBonus) === 7); - } - }, + charms: { + ResLife: { + max: 6, + have: [], + classid: sdk.items.SmallCharm, + stats: function (check) { + return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MaxHp) === 20); + } + }, - Skiller: { - max: 2, - have: [], - classid: sdk.items.GrandCharm, - stats: function (check) { - return (!check.unique && check.classid === this.classid && check.getStat(sdk.stats.AddSkillTab, sdk.skills.tabs.Offensive) === 1 - && check.getStat(sdk.stats.MaxHp) >= 40); - } - }, - }, - - AutoBuildTemplate: { - 1: { - Update: function () { - Config.Vigor = false; - Config.AttackSkill = [-1, sdk.skills.Zeal, sdk.skills.HolyShock, sdk.skills.Zeal, sdk.skills.HolyShock, sdk.skills.Zeal, sdk.skills.HolyFreeze]; - Config.LowManaSkill = [-1, -1]; - Config.SkipImmune = ["lightning and cold and physical"]; // Don't think this ever happens but should skip if it does + ResMf: { + max: 2, + have: [], + classid: sdk.items.SmallCharm, + stats: function (check) { + return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MagicBonus) === 7); + } + }, - Config.BeltColumn = ["hp", "hp", "mp", "rv"]; - } - }, - }, + Skiller: { + max: 2, + have: [], + classid: sdk.items.GrandCharm, + stats: function (check) { + return (!check.unique && check.classid === this.classid && check.getStat(sdk.stats.AddSkillTab, sdk.skills.tabs.Offensive) === 1 + && check.getStat(sdk.stats.MaxHp) >= 40); + } + }, + }, + + AutoBuildTemplate: { + 1: { + Update: function () { + Config.Vigor = false; + Config.AttackSkill = [-1, sdk.skills.Zeal, sdk.skills.HolyShock, sdk.skills.Zeal, sdk.skills.HolyShock, sdk.skills.Zeal, sdk.skills.HolyFreeze]; + Config.LowManaSkill = [-1, -1]; + Config.SkipImmune = ["lightning and cold and physical"]; // Don't think this ever happens but should skip if it does - respec: function () { - if (me.classic) { - return me.charlvl >= 75 && me.diablo; - } else { - return me.haveAll([{ name: sdk.locale.items.Dream, itemtype: sdk.items.type.AuricShields }, { name: sdk.locale.items.Dream, itemtype: sdk.items.type.Helm }]); - } - }, + Config.BeltColumn = ["hp", "hp", "mp", "rv"]; + } + }, + }, - active: function () { - return this.respec() && me.getSkill(sdk.skills.HolyShock, sdk.skills.subindex.HardPoints) === 20; - }, -}; + respec: function () { + if (me.classic) { + return me.charlvl >= 75 && me.diablo; + } else { + return me.haveAll([{ name: sdk.locale.items.Dream, itemtype: sdk.items.type.AuricShields }, { name: sdk.locale.items.Dream, itemtype: sdk.items.type.Helm }]); + } + }, -// Has to be set after its loaded -finalBuild.stats = me.classic ? finalBuild.classicStats : finalBuild.expansionStats; -finalBuild.autoEquipTiers = me.classic ? finalBuild.classicTiers : finalBuild.expansionTiers; + active: function () { + return this.respec() && me.getSkill(sdk.skills.HolyShock, sdk.skills.subindex.HardPoints) === 20; + }, + }; + + build.stats = me.classic + ? [ + ["strength", 80], ["vitality", "all"] + ] + : [ + ["strength", 103], ["dexterity", 136], ["vitality", 300], + ["dexterity", "block"], ["vitality", "all"] + ]; + + let finalGear = me.classic + ? [ + // Weapon + "[name] == warscepter && [quality] >= magic # [paladinskills] == 2 && [ias] == 40 && [skillholyshock] >= 1 # [tier] == tierscore(item, 100000)", + // Helm - Tarnhelm + "[name] == skullcap && [quality] == unique # [itemallskills] == 1 && [itemmagicbonus] >= 25 # [tier] == tierscore(item, 100000)", + // Shield + "[type] == shield && [quality] >= magic # [paladinskills] == 2 && [allres] >= 16 # [tier] == tierscore(item, 100000)", + // Rings - SoJ + "[type] == ring && [quality] == unique # [itemmaxmanapercent] == 25 # [tier] == 100000", + // Amulet + "[type] == amulet && [quality] >= magic # [paladinskills] == 2 && [allres] >= 16 # [tier] == tierscore(item, 100000)", + // Boots - Hsaru's Iron Heel + "[name] == chainboots && [quality] == set # [frw] == 20 # [tier] == 100000", + // Belt - Hsaru's Iron Stay + "[name] == belt && [quality] == set # [coldresist] == 20 && [maxhp] == 20 # [tier] == 100000", + ] : [ + // Final Weapon - HoJ + "[type] == sword && [flag] == runeword # [holyfireaura] >= 16 # [tier] == 120000", + // Weapon - Crescent Moon + "[type] == sword && [flag] == runeword # [ias] >= 20 && [passiveltngpierce] >= 35 # [tier] == 110000", + // Weapon - Voice of Reason + "[type] == sword && [flag] == runeword # [passivecoldpierce] >= 24 # [tier] == 102500", + // Helm - Dream + "[type] == helm && [flag] == runeword # [holyshockaura] >= 15 # [tier] == 110000", + // Belt - TGods + "[name] == warbelt && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 160 # [tier] == tierscore(item, 110000)", + // Boots - Gore Rider + "[name] == warboots && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 160 # [tier] == tierscore(item, 110000)", + // Armor - Dragon + "[type] == armor && [flag] != ethereal && [flag] == runeword # [holyfireaura] >= 14 # [tier] == 110000", + // Shield - Dream + "[type] == auricshields && [flag] != ethereal && [flag] == runeword # [holyshockaura] >= 15 # [tier] == 110000", + // Gloves - Laying of Hand's + "[name] == bramblemitts && [quality] == set && [flag] != ethereal # [ias] >= 20 # [tier] == 110000", + // Amulet - Highlords + "[type] == amulet && [quality] == unique # [lightresist] == 35 # [tier] == 100000", + // Final Rings - Perfect Raven Frost & Bul-Kathos' Wedding Band + "[type] == ring && [quality] == unique # [dexterity] == 20 && [tohit] == 250 # [tier] == 110000", + "[type] == ring && [quality] == unique # [maxstamina] == 50 && [lifeleech] == 5 # [tier] == 110000", + // Rings - Raven Frost && Bul-Kathos' Wedding Band + "[type] == ring && [quality] == unique # [dexterity] >= 15 && [tohit] >= 150 # [tier] == 100000", + "[type] == ring && [quality] == unique # [maxstamina] == 50 && [lifeleech] >= 3 # [tier] == 100000", + // Switch - CTA + "[minimumsockets] >= 5 && [flag] == runeword # [plusskillbattleorders] >= 1 # [secondarytier] == 100000", + // Merc Final Armor - Fortitude + "[type] == armor && [flag] == runeword # [enhanceddefense] >= 200 && [enhanceddamage] >= 300 # [merctier] == 100000", + // Merc Armor - Treachery + "[type] == armor && [flag] == runeword # [ias] == 45 && [coldresist] == 30 # [merctier] == 50000 + mercscore(item)", + // Merc Final Helmet - Eth Andy's + "[name] == demonhead && [quality] == unique && [flag] == ethereal # [strength] >= 25 && [enhanceddefense] >= 100 # [merctier] == 50000 + mercscore(item)", + // Merc Helmet - Andy's + "[name] == demonhead && [quality] == unique && [flag] != ethereal # [strength] >= 25 && [enhanceddefense] >= 100 # [merctier] == 40000 + mercscore(item)", + ]; + + NTIP.buildList(finalGear); + NTIP.buildFinalGear(finalGear); + + return build; + })(); +})(module); diff --git a/libs/SoloPlay/BuildFiles/paladin/paladin.HammerdinBuild.js b/libs/SoloPlay/BuildFiles/paladin/paladin.HammerdinBuild.js index a561b450..188e5584 100644 --- a/libs/SoloPlay/BuildFiles/paladin/paladin.HammerdinBuild.js +++ b/libs/SoloPlay/BuildFiles/paladin/paladin.HammerdinBuild.js @@ -6,160 +6,167 @@ * */ -const finalBuild = { - caster: true, - skillstab: sdk.skills.tabs.PalaCombat, - wantedskills: [sdk.skills.BlessedHammer, sdk.skills.Concentration], - usefulskills: [sdk.skills.HolyShield, sdk.skills.BlessedAim], - precastSkills: [sdk.skills.HolyShield], - mercDiff: sdk.difficulty.Nightmare, - mercAct: 2, - mercAuraWanted: "Holy Freeze", - classicStats: [ - ["dexterity", 51], ["strength", 80], ["vitality", "all"] - ], - expansionStats: [ - ["vitality", 60], ["dexterity", 30], ["strength", 27], - ["vitality", 91], ["dexterity", 44], ["strength", 30], - ["vitality", 96], ["dexterity", 59], ["strength", 60], - ["vitality", 109], ["dexterity", 77], ["strength", 89], - ["vitality", 137], ["dexterity", 89], ["strength", 103], - ["vitality", 173], ["dexterity", 103], - ["vitality", 208], ["dexterity", 118], - ["vitality", 243], ["dexterity", 133], - ["vitality", 279], ["dexterity", 147], - ["vitality", "all"] - ], - skills: [ - [sdk.skills.HolyShield, 1], - [sdk.skills.Meditation, 1], - [sdk.skills.Redemption, 1], - [sdk.skills.BlessedHammer, 20], - [sdk.skills.Concentration, 20], - [sdk.skills.Vigor, 20], - [sdk.skills.BlessedAim, 20], - [sdk.skills.HolyShield, 20] - ], - classicTiers: [ - // Weapon - Spectral Shard - "[name] == blade && [quality] == unique # [fcr] == 20 && [allres] == 10 # [tier] == 100000", - // Rings - SoJ - "[type] == ring && [quality] == unique # [itemmaxmanapercent] == 25 # [tier] == 100000", - // Gloves - Magefist - "[name] == lightgauntlets && [quality] == unique # [fcr] >= 20 && [addfireskills] == 1 # [tier] == tierscore(item, 100000)", - ], - expansionTiers: [ - // Weapon - HotO - "[type] == mace && [flag] == runeword # [fcr] == 40 # [tier] == 100000", - // Helm - Harlequin's Crest - "[name] == shako && [quality] == unique && [flag] != ethereal # [damageresist] == 10 # [tier] == tierscore(item, 100000)", - // Belt - Arach's - "[name] == spiderwebsash && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 90 # [tier] == tierscore(item, 100000)", - // Final Boots - Sandstorm Treks - "[name] == scarabshellboots && [quality] == unique # [strength]+[vitality] >= 20 # [tier] == tierscore(item, 100000)", - // Boots - War Traveler - "[name] == battleboots && [quality] == unique && [flag] != ethereal # [itemmagicbonus] >= 50 # [tier] == tierscore(item, 5000)", - // Armor - Enigma - "[type] == armor && [flag] != ethereal && [flag] == runeword # [itemallskills] == 2 # [tier] == 100000", - // Final Shield - Spirit - "[type] == auricshields && [flag] == runeword # [fcr] == 35 && [maxmana] == 112 && [coldresist] == 80 # [tier] == 110000", - // Shield - Spirit - "[type] == auricshields && [flag] == runeword # [fcr] >= 25 && [maxmana] >= 89 && [coldresist] == 80 # [tier] == tierscore(item, 100000)", - // Shield - HoZ - "[name] == gildedshield && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 150 # [tier] == tierscore(item, 50000)", - // Final Gloves - Perfect Upp'ed Magefist - "[name] == battlegauntlets && [quality] == unique && [flag] != ethereal # [enhanceddefense] == 30 && [addfireskills] == 1 && [defense] == 71 # [tier] == 110000", - // Gloves - Upp'ed Magefist - "[name] == battlegauntlets && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 20 && [addfireskills] == 1 # [tier] == tierscore(item, 100000)", - // Gloves - Magefist - "[name] == lightgauntlets && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 20 && [addfireskills] == 1 # [tier] == tierscore(item, 100000)", - // Amulet - Maras - "[type] == amulet && [quality] == unique # [strength] == 5 && [coldresist] >= 30 # [tier] == tierscore(item, 100000)", - // Final Rings - SoJ & Perfect Raven Frost - "[type] == ring && [quality] == unique # [itemmaxmanapercent] == 25 # [tier] == 100000", - "[type] == ring && [quality] == unique # [dexterity] >= 20 # [tier] == 100000", - // Rings - Dwarf Star & Raven Frost - "[name] == ring && [quality] == unique # [maxhp] >= 40 && [magicdamagereduction] >= 12 # [tier] == 99000", - "[type] == ring && [quality] == unique # [dexterity] >= 20 # [tier] == 99000", - // Switch - CTA - "[minimumsockets] >= 5 && [flag] == runeword # [plusskillbattleorders] >= 1 # [secondarytier] == 100000", - // Merc Armor - Fortitude - "[type] == armor && [flag] == runeword # [enhanceddefense] >= 200 && [enhanceddamage] >= 300 # [merctier] == 100000", - // Merc Final Helmet - Eth Andy's - "[name] == demonhead && [quality] == unique && [flag] == ethereal # [strength] >= 25 && [enhanceddefense] >= 100 # [merctier] == 50000 + mercscore(item)", - // Merc Helmet - Andy's - "[name] == demonhead && [quality] == unique && [flag] != ethereal # [strength] >= 25 && [enhanceddefense] >= 100 # [merctier] == 40000 + mercscore(item)", - ], - stats: undefined, - autoEquipTiers: undefined, - charms: { - ResLife: { - max: 3, - have: [], - classid: sdk.items.SmallCharm, - stats: function (check) { - return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MaxHp) === 20); - } - }, +(function (module) { + module.exports = (function () { + const build = { + caster: true, + skillstab: sdk.skills.tabs.PalaCombat, + wantedskills: [sdk.skills.BlessedHammer, sdk.skills.Concentration], + usefulskills: [sdk.skills.HolyShield, sdk.skills.BlessedAim], + precastSkills: [sdk.skills.HolyShield], + wantedMerc: MercData[sdk.skills.HolyFreeze], + skills: [ + [sdk.skills.HolyShield, 1], + [sdk.skills.Meditation, 1], + [sdk.skills.Redemption, 1], + [sdk.skills.BlessedHammer, 20], + [sdk.skills.Concentration, 20], + [sdk.skills.Vigor, 20], + [sdk.skills.BlessedAim, 20], + [sdk.skills.HolyShield, 20] + ], + stats: [], - ResMf: { - max: 2, - have: [], - classid: sdk.items.SmallCharm, - stats: function (check) { - return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MagicBonus) === 7); - } - }, + charms: { + ResLife: { + max: 3, + have: [], + classid: sdk.items.SmallCharm, + stats: function (check) { + return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MaxHp) === 20); + } + }, - ResFHR: { - max: 3, - have: [], - classid: sdk.items.SmallCharm, - stats: function (check) { - return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.FHR) === 5); - } - }, + ResMf: { + max: 2, + have: [], + classid: sdk.items.SmallCharm, + stats: function (check) { + return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MagicBonus) === 7); + } + }, - Skiller: { - max: 2, - have: [], - classid: sdk.items.GrandCharm, - stats: function (check) { - return (!check.unique && check.classid === this.classid && check.getStat(sdk.stats.AddSkillTab, sdk.skills.tabs.PalaCombat) === 1 - && check.getStat(sdk.stats.MaxHp) >= 40); - } - }, - }, - - AutoBuildTemplate: { - 1: { - Update: function () { - Config.AttackSkill = [-1, sdk.skills.BlessedHammer, sdk.skills.Concentration, sdk.skills.BlessedHammer, sdk.skills.Concentration, sdk.skills.HolyBolt, sdk.skills.Concentration]; - Config.LowManaSkill = [0, sdk.skills.Concentration]; + ResFHR: { + max: 3, + have: [], + classid: sdk.items.SmallCharm, + stats: function (check) { + return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.FHR) === 5); + } + }, - if (me.hell && !Pather.accessToAct(5)) { - Config.SkipImmune = ["magic"]; + Skiller: { + max: 2, + have: [], + classid: sdk.items.GrandCharm, + stats: function (check) { + return (!check.unique && check.classid === this.classid && check.getStat(sdk.stats.AddSkillTab, sdk.skills.tabs.PalaCombat) === 1 + && check.getStat(sdk.stats.MaxHp) >= 40); + } + }, + }, + + AutoBuildTemplate: { + 1: { + Update: function () { + Config.AttackSkill = [-1, sdk.skills.BlessedHammer, sdk.skills.Concentration, sdk.skills.BlessedHammer, sdk.skills.Concentration, sdk.skills.HolyBolt, sdk.skills.Concentration]; + Config.LowManaSkill = [0, sdk.skills.Concentration]; + + if (me.hell && !Pather.accessToAct(5)) { + Config.SkipImmune = ["magic"]; + } + Config.BeltColumn = ["hp", "hp", "mp", "rv"]; + SetUp.belt(); + } + }, + }, + + respec: function () { + if (me.classic) { + return me.charlvl >= 75 && me.diablo; + } else { + return Check.haveItem("armor", "runeword", "Enigma"); } - Config.BeltColumn = ["hp", "hp", "mp", "rv"]; - SetUp.belt(); - } - }, - }, + }, + + active: function () { + return this.respec() && me.getSkill(sdk.skills.BlessedHammer, sdk.skills.subindex.HardPoints) === 20; + }, + }; - respec: function () { - if (me.classic) { - return me.charlvl >= 75 && me.diablo; - } else { - return Check.haveItem("armor", "runeword", "Enigma"); - } - }, + build.stats = me.classic + ? [ + ["dexterity", 51], ["strength", 80], ["vitality", "all"] + ] + : [ + ["vitality", 60], ["dexterity", 30], ["strength", 27], + ["vitality", 91], ["dexterity", 44], ["strength", 30], + ["vitality", 96], ["dexterity", 59], ["strength", 60], + ["vitality", 109], ["dexterity", 77], ["strength", 89], + ["vitality", 137], ["dexterity", 89], ["strength", 103], + ["vitality", 173], ["dexterity", 103], + ["vitality", 208], ["dexterity", 118], + ["vitality", 243], ["dexterity", 133], + ["vitality", 279], ["dexterity", 147], + ["vitality", "all"] + ]; + + let finalGear = me.classic + ? [ + // Weapon - Spectral Shard + "[name] == blade && [quality] == unique # [fcr] == 20 && [allres] == 10 # [tier] == 100000", + // Rings - SoJ + "[type] == ring && [quality] == unique # [itemmaxmanapercent] == 25 # [tier] == 100000", + // Gloves - Magefist + "[name] == lightgauntlets && [quality] == unique # [fcr] >= 20 && [addfireskills] == 1 # [tier] == tierscore(item, 100000)", + ] + : [ + // Weapon - HotO + "[type] == mace && [flag] == runeword # [fcr] == 40 # [tier] == 100000", + // Helm - Harlequin's Crest + "[name] == shako && [quality] == unique && [flag] != ethereal # [damageresist] == 10 # [tier] == tierscore(item, 100000)", + // Belt - Arach's + "[name] == spiderwebsash && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 90 # [tier] == tierscore(item, 100000)", + // Final Boots - Sandstorm Treks + "[name] == scarabshellboots && [quality] == unique # [strength]+[vitality] >= 20 # [tier] == tierscore(item, 100000)", + // Boots - War Traveler + "[name] == battleboots && [quality] == unique && [flag] != ethereal # [itemmagicbonus] >= 50 # [tier] == tierscore(item, 5000)", + // Armor - Enigma + "[type] == armor && [flag] != ethereal && [flag] == runeword # [itemallskills] == 2 # [tier] == 100000", + // Final Shield - Spirit + "[type] == auricshields && [flag] == runeword # [fcr] == 35 && [maxmana] == 112 && [coldresist] == 80 # [tier] == 110000", + // Shield - Spirit + "[type] == auricshields && [flag] == runeword # [fcr] >= 25 && [maxmana] >= 89 && [coldresist] == 80 # [tier] == tierscore(item, 100000)", + // Shield - HoZ + "[name] == gildedshield && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 150 # [tier] == tierscore(item, 50000)", + // Final Gloves - Perfect Upp'ed Magefist + "[name] == battlegauntlets && [quality] == unique && [flag] != ethereal # [enhanceddefense] == 30 && [addfireskills] == 1 && [defense] == 71 # [tier] == 110000", + // Gloves - Upp'ed Magefist + "[name] == battlegauntlets && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 20 && [addfireskills] == 1 # [tier] == tierscore(item, 100000)", + // Gloves - Magefist + "[name] == lightgauntlets && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 20 && [addfireskills] == 1 # [tier] == tierscore(item, 100000)", + // Amulet - Maras + "[type] == amulet && [quality] == unique # [strength] == 5 && [coldresist] >= 30 # [tier] == tierscore(item, 100000)", + // Final Rings - SoJ & Perfect Raven Frost + "[type] == ring && [quality] == unique # [itemmaxmanapercent] == 25 # [tier] == 100000", + "[type] == ring && [quality] == unique # [dexterity] >= 20 # [tier] == 100000", + // Rings - Dwarf Star & Raven Frost + "[name] == ring && [quality] == unique # [maxhp] >= 40 && [magicdamagereduction] >= 12 # [tier] == 99000", + "[type] == ring && [quality] == unique # [dexterity] >= 20 # [tier] == 99000", + // Switch - CTA + "[minimumsockets] >= 5 && [flag] == runeword # [plusskillbattleorders] >= 1 # [secondarytier] == 100000", + // Merc Armor - Fortitude + "[type] == armor && [flag] == runeword # [enhanceddefense] >= 200 && [enhanceddamage] >= 300 # [merctier] == 100000", + // Merc Final Helmet - Eth Andy's + "[name] == demonhead && [quality] == unique && [flag] == ethereal # [strength] >= 25 && [enhanceddefense] >= 100 # [merctier] == 50000 + mercscore(item)", + // Merc Helmet - Andy's + "[name] == demonhead && [quality] == unique && [flag] != ethereal # [strength] >= 25 && [enhanceddefense] >= 100 # [merctier] == 40000 + mercscore(item)", + ]; - active: function () { - return this.respec() && me.getSkill(sdk.skills.BlessedHammer, sdk.skills.subindex.HardPoints) === 20; - }, -}; + NTIP.buildList(finalGear); + NTIP.buildFinalGear(finalGear); -// Has to be set after its loaded -finalBuild.stats = me.classic ? finalBuild.classicStats : finalBuild.expansionStats; -finalBuild.autoEquipTiers = me.classic ? finalBuild.classicTiers : finalBuild.expansionTiers; + return build; + })(); +})(module); diff --git a/libs/SoloPlay/BuildFiles/paladin/paladin.HammershockBuild.js b/libs/SoloPlay/BuildFiles/paladin/paladin.HammershockBuild.js index f5f52902..f5179706 100644 --- a/libs/SoloPlay/BuildFiles/paladin/paladin.HammershockBuild.js +++ b/libs/SoloPlay/BuildFiles/paladin/paladin.HammershockBuild.js @@ -5,147 +5,154 @@ * */ -const finalBuild = { - caster: false, - skillstab: sdk.skills.tabs.PalaCombat, - wantedskills: [sdk.skills.BlessedHammer, sdk.skills.HolyShock], - usefulskills: [sdk.skills.HolyShield, sdk.skills.ResistLightning, sdk.skills.Zeal, sdk.skills.Concentration, sdk.skills.Vigor, sdk.skills.BlessedAim], - precastSkills: [sdk.skills.HolyShield], - usefulStats: [sdk.stats.PierceLtng, sdk.stats.PassiveLightningMastery, sdk.stats.PassiveLightningPierce], - mercDiff: sdk.difficulty.Nightmare, - mercAct: 2, - mercAuraWanted: "Holy Freeze", - classicStats: [ - ["strength", 80], ["vitality", "all"] - ], - expansionStats: [ - ["strength", 103], ["dexterity", 136], ["vitality", 300], - ["dexterity", "block"], ["vitality", "all"] - ], - skills: [ - [sdk.skills.Zeal, 4], - [sdk.skills.Vengeance, 1], - [sdk.skills.Redemption, 1], - [sdk.skills.Salvation, 1], - [sdk.skills.HolyShield, 1], - [sdk.skills.Concentration, 1], - [sdk.skills.BlessedHammer, 20], - [sdk.skills.HolyShock, 20], - [sdk.skills.BlessedAim, 20, false], - [sdk.skills.ResistLightning, 20, false], - [sdk.skills.Zeal, 20, false], - ], - classicTiers: [ - // Weapon - "[name] == warscepter && [quality] >= magic # [paladinskills] == 2 && [ias] == 40 && [skillholyshock] >= 1 # [tier] == tierscore(item, 100000)", - // Helm - Tarnhelm - "[name] == skullcap && [quality] == unique # [itemallskills] == 1 && [itemmagicbonus] >= 25 # [tier] == tierscore(item, 100000)", - // Shield - "[type] == shield && [quality] >= magic # [paladinskills] == 2 && [allres] >= 16 # [tier] == tierscore(item, 100000)", - // Rings - SoJ - "[type] == ring && [quality] == unique # [itemmaxmanapercent] == 25 # [tier] == 100000", - // Amulet - "[type] == amulet && [quality] >= magic # [paladinskills] == 2 && [allres] >= 16 # [tier] == tierscore(item, 100000)", - // Boots - Hsaru's Iron Heel - "[name] == chainboots && [quality] == set # [frw] == 20 # [tier] == 100000", - // Belt - Hsaru's Iron Stay - "[name] == belt && [quality] == set # [coldresist] == 20 && [maxhp] == 20 # [tier] == 100000", - ], - expansionTiers: [ - // Weapon - Heaven's Light - "[type] == scepter && [quality] == unique && [flag] != ethereal # [paladinskills] >= 2 && [enhanceddamage] >= 250 # [tier] == tierscore(item, 100000)", - // Helm - Harlequin's Crest - "[name] == shako && [quality] == unique && [flag] != ethereal # [damageresist] == 10 # [tier] == tierscore(item, 100000)", - // Belt - TGods - "[name] == warbelt && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 160 # [tier] == tierscore(item, 110000)", - // Boots - Gore Rider - "[name] == warboots && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 160 # [tier] == tierscore(item, 110000)", - // Armor - Enigma - "[type] == armor && [flag] != ethereal && [flag] == runeword # [itemallskills] == 2 # [tier] == 100000", - // Final Shield - Exile - "[type] == auricshields && [flag] == runeword # [defianceaura] >= 13 # [tier] == 110000", - // Shield - HoZ - "[name] == gildedshield && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 150 # [tier] == tierscore(item, 50000)", - // Gloves - Laying of Hand's - "[name] == bramblemitts && [quality] == set && [flag] != ethereal # [ias] >= 20 # [tier] == 110000", - // Amulet - Highlords - "[type] == amulet && [quality] == unique # [lightresist] == 35 # [tier] == 100000", - // Final Rings - Perfect Raven Frost & Bul-Kathos' Wedding Band - "[type] == ring && [quality] == unique # [dexterity] == 20 && [tohit] == 250 # [tier] == 110000", - "[type] == ring && [quality] == unique # [maxstamina] == 50 && [lifeleech] == 5 # [tier] == 110000", - // Rings - Raven Frost && Bul-Kathos' Wedding Band - "[type] == ring && [quality] == unique # [dexterity] >= 15 && [tohit] >= 150 # [tier] == 100000", - "[type] == ring && [quality] == unique # [maxstamina] == 50 && [lifeleech] >= 3 # [tier] == 100000", - // Switch - CTA - "[minimumsockets] >= 5 && [flag] == runeword # [plusskillbattleorders] >= 1 # [secondarytier] == 100000", - // Merc Final Armor - Fortitude - "[type] == armor && [flag] == runeword # [enhanceddefense] >= 200 && [enhanceddamage] >= 300 # [merctier] == 100000", - // Merc Armor - Treachery - "[type] == armor && [flag] == runeword # [ias] == 45 && [coldresist] == 30 # [merctier] == 50000 + mercscore(item)", - // Merc Final Helmet - Eth Andy's - "[name] == demonhead && [quality] == unique && [flag] == ethereal # [strength] >= 25 && [enhanceddefense] >= 100 # [merctier] == 50000 + mercscore(item)", - // Merc Helmet - Andy's - "[name] == demonhead && [quality] == unique && [flag] != ethereal # [strength] >= 25 && [enhanceddefense] >= 100 # [merctier] == 40000 + mercscore(item)", - ], - stats: undefined, - autoEquipTiers: undefined, - charms: { - ResLife: { - max: 6, - have: [], - classid: sdk.items.SmallCharm, - stats: function (check) { - return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MaxHp) === 20); - } - }, +(function (module) { + module.exports = (function () { + const build = { + caster: false, + skillstab: sdk.skills.tabs.PalaCombat, + wantedskills: [sdk.skills.BlessedHammer, sdk.skills.HolyShock], + usefulskills: [sdk.skills.HolyShield, sdk.skills.ResistLightning, sdk.skills.Zeal, sdk.skills.Concentration, sdk.skills.Vigor, sdk.skills.BlessedAim], + precastSkills: [sdk.skills.HolyShield], + usefulStats: [sdk.stats.PierceLtng, sdk.stats.PassiveLightningMastery, sdk.stats.PassiveLightningPierce], + wantedMerc: MercData[sdk.skills.HolyFreeze], + skills: [ + [sdk.skills.Zeal, 4], + [sdk.skills.Vengeance, 1], + [sdk.skills.Redemption, 1], + [sdk.skills.Salvation, 1], + [sdk.skills.HolyShield, 1], + [sdk.skills.Concentration, 1], + [sdk.skills.BlessedHammer, 20], + [sdk.skills.HolyShock, 20], + [sdk.skills.BlessedAim, 20, false], + [sdk.skills.ResistLightning, 20, false], + [sdk.skills.Zeal, 20, false], + ], + stats: [], - ResMf: { - max: 2, - have: [], - classid: sdk.items.SmallCharm, - stats: function (check) { - return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MagicBonus) === 7); - } - }, + charms: { + ResLife: { + max: 6, + have: [], + classid: sdk.items.SmallCharm, + stats: function (check) { + return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MaxHp) === 20); + } + }, - Skiller: { - max: 2, - have: [], - classid: sdk.items.GrandCharm, - stats: function (check) { - return (!check.unique && check.classid === this.classid && check.getStat(sdk.stats.AddSkillTab, sdk.skills.tabs.Offensive) === 1 - && check.getStat(sdk.stats.MaxHp) >= 40); - } - }, - }, + ResMf: { + max: 2, + have: [], + classid: sdk.items.SmallCharm, + stats: function (check) { + return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MagicBonus) === 7); + } + }, - AutoBuildTemplate: { - 1: { - Update: function () { - Config.Vigor = false; - Config.AttackSkill = [-1, sdk.skills.BlessedHammer, sdk.skills.Concentration, sdk.skills.BlessedHammer, sdk.skills.Concentration, sdk.skills.Zeal, sdk.skills.HolyShock]; - Config.LowManaSkill = [-1, -1]; - Config.SkipImmune = ["magic and lightning and physical"]; // Don't think this ever happens but should skip if it does - Config.BeltColumn = ["hp", "hp", "mp", "rv"]; - SetUp.belt(); - } - }, - }, + Skiller: { + max: 2, + have: [], + classid: sdk.items.GrandCharm, + stats: function (check) { + return (!check.unique && check.classid === this.classid && check.getStat(sdk.stats.AddSkillTab, sdk.skills.tabs.Offensive) === 1 + && check.getStat(sdk.stats.MaxHp) >= 40); + } + }, + }, - respec: function () { - if (me.classic) { - return me.charlvl >= 75 && me.diablo; - } else { - return Check.haveItem("scepter", "unique", "Heaven's Light") && Check.haveItem("armor", "runeword", "Enigma"); - } - }, + AutoBuildTemplate: { + 1: { + Update: function () { + Config.Vigor = false; + Config.AttackSkill = [-1, sdk.skills.BlessedHammer, sdk.skills.Concentration, sdk.skills.BlessedHammer, sdk.skills.Concentration, sdk.skills.Zeal, sdk.skills.HolyShock]; + Config.LowManaSkill = [-1, -1]; + Config.SkipImmune = ["magic and lightning and physical"]; // Don't think this ever happens but should skip if it does + Config.BeltColumn = ["hp", "hp", "mp", "rv"]; + SetUp.belt(); + } + }, + }, - active: function () { - return this.respec() && (me.getSkill(sdk.skills.HolyShock, sdk.skills.subindex.HardPoints) === 20 && me.getSkill(sdk.skills.BlessedHammer, sdk.skills.subindex.HardPoints) === 20); - }, -}; + respec: function () { + if (me.classic) { + return me.charlvl >= 75 && me.diablo; + } else { + return Check.haveItem("scepter", "unique", "Heaven's Light") && Check.haveItem("armor", "runeword", "Enigma"); + } + }, -// Has to be set after its loaded -finalBuild.stats = me.classic ? finalBuild.classicStats : finalBuild.expansionStats; -finalBuild.autoEquipTiers = me.classic ? finalBuild.classicTiers : finalBuild.expansionTiers; + active: function () { + return this.respec() && (me.getSkill(sdk.skills.HolyShock, sdk.skills.subindex.HardPoints) === 20 && me.getSkill(sdk.skills.BlessedHammer, sdk.skills.subindex.HardPoints) === 20); + }, + }; + + build.stats = me.classic + ? [ + ["strength", 80], ["vitality", "all"] + ] + : [ + ["strength", 103], ["dexterity", 136], ["vitality", 300], + ["dexterity", "block"], ["vitality", "all"] + ]; + + let finalGear = me.classic + ? [ + // Weapon + "[name] == warscepter && [quality] >= magic # [paladinskills] == 2 && [ias] == 40 && [skillholyshock] >= 1 # [tier] == tierscore(item, 100000)", + // Helm - Tarnhelm + "[name] == skullcap && [quality] == unique # [itemallskills] == 1 && [itemmagicbonus] >= 25 # [tier] == tierscore(item, 100000)", + // Shield + "[type] == shield && [quality] >= magic # [paladinskills] == 2 && [allres] >= 16 # [tier] == tierscore(item, 100000)", + // Rings - SoJ + "[type] == ring && [quality] == unique # [itemmaxmanapercent] == 25 # [tier] == 100000", + // Amulet + "[type] == amulet && [quality] >= magic # [paladinskills] == 2 && [allres] >= 16 # [tier] == tierscore(item, 100000)", + // Boots - Hsaru's Iron Heel + "[name] == chainboots && [quality] == set # [frw] == 20 # [tier] == 100000", + // Belt - Hsaru's Iron Stay + "[name] == belt && [quality] == set # [coldresist] == 20 && [maxhp] == 20 # [tier] == 100000", + ] + : [ + // Weapon - Heaven's Light + "[type] == scepter && [quality] == unique && [flag] != ethereal # [paladinskills] >= 2 && [enhanceddamage] >= 250 # [tier] == tierscore(item, 100000)", + // Helm - Harlequin's Crest + "[name] == shako && [quality] == unique && [flag] != ethereal # [damageresist] == 10 # [tier] == tierscore(item, 100000)", + // Belt - TGods + "[name] == warbelt && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 160 # [tier] == tierscore(item, 110000)", + // Boots - Gore Rider + "[name] == warboots && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 160 # [tier] == tierscore(item, 110000)", + // Armor - Enigma + "[type] == armor && [flag] != ethereal && [flag] == runeword # [itemallskills] == 2 # [tier] == 100000", + // Final Shield - Exile + "[type] == auricshields && [flag] == runeword # [defianceaura] >= 13 # [tier] == 110000", + // Shield - HoZ + "[name] == gildedshield && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 150 # [tier] == tierscore(item, 50000)", + // Gloves - Laying of Hand's + "[name] == bramblemitts && [quality] == set && [flag] != ethereal # [ias] >= 20 # [tier] == 110000", + // Amulet - Highlords + "[type] == amulet && [quality] == unique # [lightresist] == 35 # [tier] == 100000", + // Final Rings - Perfect Raven Frost & Bul-Kathos' Wedding Band + "[type] == ring && [quality] == unique # [dexterity] == 20 && [tohit] == 250 # [tier] == 110000", + "[type] == ring && [quality] == unique # [maxstamina] == 50 && [lifeleech] == 5 # [tier] == 110000", + // Rings - Raven Frost && Bul-Kathos' Wedding Band + "[type] == ring && [quality] == unique # [dexterity] >= 15 && [tohit] >= 150 # [tier] == 100000", + "[type] == ring && [quality] == unique # [maxstamina] == 50 && [lifeleech] >= 3 # [tier] == 100000", + // Switch - CTA + "[minimumsockets] >= 5 && [flag] == runeword # [plusskillbattleorders] >= 1 # [secondarytier] == 100000", + // Merc Final Armor - Fortitude + "[type] == armor && [flag] == runeword # [enhanceddefense] >= 200 && [enhanceddamage] >= 300 # [merctier] == 100000", + // Merc Armor - Treachery + "[type] == armor && [flag] == runeword # [ias] == 45 && [coldresist] == 30 # [merctier] == 50000 + mercscore(item)", + // Merc Final Helmet - Eth Andy's + "[name] == demonhead && [quality] == unique && [flag] == ethereal # [strength] >= 25 && [enhanceddefense] >= 100 # [merctier] == 50000 + mercscore(item)", + // Merc Helmet - Andy's + "[name] == demonhead && [quality] == unique && [flag] != ethereal # [strength] >= 25 && [enhanceddefense] >= 100 # [merctier] == 40000 + mercscore(item)", + ]; + + NTIP.buildList(finalGear); + NTIP.buildFinalGear(finalGear); + + return build; + })(); +})(module); diff --git a/libs/SoloPlay/BuildFiles/paladin/paladin.LevelingBuild.js b/libs/SoloPlay/BuildFiles/paladin/paladin.LevelingBuild.js index 43396fdb..bd72395b 100644 --- a/libs/SoloPlay/BuildFiles/paladin/paladin.LevelingBuild.js +++ b/libs/SoloPlay/BuildFiles/paladin/paladin.LevelingBuild.js @@ -6,109 +6,119 @@ * */ -let build = { - AutoBuildTemplate: {}, - caster: true, - skillstab: sdk.skills.tabs.PalaCombat, - wantedskills: [sdk.skills.BlessedHammer, sdk.skills.Concentration], - usefulskills: [sdk.skills.HolyShield, sdk.skills.BlessedAim], - mercDiff: sdk.difficulty.Nightmare, - mercAct: 2, - mercAuraWanted: "Holy Freeze", - classicStats: [ - ["dexterity", 51], ["strength", 80], ["vitality", "all"] - ], - expansionStats: [ - ["vitality", 60], ["dexterity", 30], ["strength", 27], - ["vitality", 91], ["dexterity", 44], ["strength", 30], - ["vitality", 96], ["dexterity", 59], ["strength", 60], - ["vitality", 109], ["dexterity", 77], ["strength", 89], - ["vitality", 137], ["dexterity", 89], ["strength", 103], - ["vitality", 173], ["dexterity", 103], - ["vitality", 208], ["dexterity", 118], - ["vitality", 243], ["dexterity", 133], - ["vitality", 279], ["dexterity", 147], - ["vitality", "all"] - ], - skills: [ - [sdk.skills.Might, 1], - [sdk.skills.Smite, 1], - [sdk.skills.Prayer, 1], - [sdk.skills.HolyBolt, 1], - [sdk.skills.Defiance, 1], - [sdk.skills.Charge, 1], - [sdk.skills.BlessedAim, 1], - [sdk.skills.Cleansing, 1], - [sdk.skills.BlessedAim, 6], - [sdk.skills.BlessedHammer, 1], - [sdk.skills.Concentration, 1], - [sdk.skills.Vigor, 1], - [sdk.skills.BlessedAim, 7], - [sdk.skills.BlessedHammer, 2], - [sdk.skills.Concentration, 2], - [sdk.skills.Vigor, 2], - [sdk.skills.BlessedHammer, 7], - [sdk.skills.HolyShield, 1], - [sdk.skills.Meditation, 1], - [sdk.skills.BlessedHammer, 12], - [sdk.skills.Redemption, 1], - [sdk.skills.BlessedHammer, 20], - [sdk.skills.Concentration, 3], - [sdk.skills.Vigor, 3], - [sdk.skills.Concentration, 4], - [sdk.skills.Vigor, 4], - [sdk.skills.Concentration, 5], - [sdk.skills.Vigor, 5], - [sdk.skills.Concentration, 6], - [sdk.skills.Vigor, 6], - [sdk.skills.Concentration, 7], - [sdk.skills.Vigor, 7], - [sdk.skills.Concentration, 8], - [sdk.skills.Vigor, 8], - [sdk.skills.Concentration, 9], - [sdk.skills.Vigor, 9], - [sdk.skills.Concentration, 10], - [sdk.skills.Vigor, 10], - [sdk.skills.Concentration, 11], - [sdk.skills.Vigor, 11], - [sdk.skills.Concentration, 12], - [sdk.skills.Vigor, 12], - [sdk.skills.Concentration, 13], - [sdk.skills.Vigor, 13], - [sdk.skills.Concentration, 14], - [sdk.skills.Vigor, 14], - [sdk.skills.Concentration, 15], - [sdk.skills.Vigor, 15], - [sdk.skills.Concentration, 16], - [sdk.skills.Vigor, 16], - [sdk.skills.Concentration, 17], - [sdk.skills.Vigor, 17], - [sdk.skills.Concentration, 18], - [sdk.skills.Vigor, 18], - [sdk.skills.Concentration, 19], - [sdk.skills.Vigor, 19], - [sdk.skills.Concentration, 20], - [sdk.skills.Vigor, 20], - [sdk.skills.BlessedAim, 20], - [sdk.skills.HolyShield, 20] - ], - stats: undefined, - active: function () { - return (me.charlvl > CharInfo.respecOne && me.charlvl > CharInfo.respecTwo && me.checkSkill(sdk.skills.Concentration, sdk.skills.subindex.HardPoints) && !Check.finalBuild().active()); - }, -}; +(function (module) { + module.exports = (function () { + const build = { + caster: true, + skillstab: sdk.skills.tabs.PalaCombat, + wantedskills: [sdk.skills.BlessedHammer, sdk.skills.Concentration], + usefulskills: [sdk.skills.HolyShield, sdk.skills.BlessedAim], + wantedMerc: MercData[sdk.skills.HolyFreeze], + stats: [], + skills: [ + [sdk.skills.Might, 1], + [sdk.skills.Smite, 1], + [sdk.skills.Prayer, 1], + [sdk.skills.HolyBolt, 1], + [sdk.skills.Defiance, 1], + [sdk.skills.Charge, 1], + [sdk.skills.BlessedAim, 1], + [sdk.skills.Cleansing, 1], + [sdk.skills.BlessedAim, 6], + [sdk.skills.BlessedHammer, 1], + [sdk.skills.Concentration, 1], + [sdk.skills.Vigor, 1], + [sdk.skills.BlessedAim, 7], + [sdk.skills.BlessedHammer, 2], + [sdk.skills.Concentration, 2], + [sdk.skills.Vigor, 2], + [sdk.skills.BlessedHammer, 7], + [sdk.skills.HolyShield, 1], + [sdk.skills.Meditation, 1], + [sdk.skills.BlessedHammer, 12], + [sdk.skills.Redemption, 1], + [sdk.skills.BlessedHammer, 20], + [sdk.skills.Concentration, 3], + [sdk.skills.Vigor, 3], + [sdk.skills.Concentration, 4], + [sdk.skills.Vigor, 4], + [sdk.skills.Concentration, 5], + [sdk.skills.Vigor, 5], + [sdk.skills.Concentration, 6], + [sdk.skills.Vigor, 6], + [sdk.skills.Concentration, 7], + [sdk.skills.Vigor, 7], + [sdk.skills.Concentration, 8], + [sdk.skills.Vigor, 8], + [sdk.skills.Concentration, 9], + [sdk.skills.Vigor, 9], + [sdk.skills.Concentration, 10], + [sdk.skills.Vigor, 10], + [sdk.skills.Concentration, 11], + [sdk.skills.Vigor, 11], + [sdk.skills.Concentration, 12], + [sdk.skills.Vigor, 12], + [sdk.skills.Concentration, 13], + [sdk.skills.Vigor, 13], + [sdk.skills.Concentration, 14], + [sdk.skills.Vigor, 14], + [sdk.skills.Concentration, 15], + [sdk.skills.Vigor, 15], + [sdk.skills.Concentration, 16], + [sdk.skills.Vigor, 16], + [sdk.skills.Concentration, 17], + [sdk.skills.Vigor, 17], + [sdk.skills.Concentration, 18], + [sdk.skills.Vigor, 18], + [sdk.skills.Concentration, 19], + [sdk.skills.Vigor, 19], + [sdk.skills.Concentration, 20], + [sdk.skills.Vigor, 20], + [sdk.skills.BlessedAim, 20], + [sdk.skills.HolyShield, 20] + ], -// Has to be set after its loaded -build.stats = me.classic ? build.classicStats : build.expansionStats; + AutoBuildTemplate: { + 1: { + Update: function () { + Config.TownHP = me.hardcore ? 0 : 35; + Config.AttackSkill = [ + -1, sdk.skills.BlessedHammer, sdk.skills.Concentration, + sdk.skills.BlessedHammer, sdk.skills.Concentration, + sdk.skills.HolyBolt, sdk.skills.Concentration + ]; + Config.LowManaSkill = [0, sdk.skills.Concentration]; + Config.BeltColumn = ["hp", "hp", "mp", "mp"]; + Config.HPBuffer = me.expansion ? 2 : 4; + Config.MPBuffer = me.expansion && me.charlvl < 80 ? 6 : me.classic ? 5 : 2; + (me.hell && !Pather.accessToAct(5)) && (Config.SkipImmune = ["magic"]); + SetUp.belt(); + } + } + }, -build.AutoBuildTemplate[1] = buildAutoBuildTempObj(() => { - Config.TownHP = me.hardcore ? 0 : 35; - Config.AttackSkill = [-1, sdk.skills.BlessedHammer, sdk.skills.Concentration, sdk.skills.BlessedHammer, sdk.skills.Concentration, sdk.skills.HolyBolt, sdk.skills.Concentration]; - Config.LowManaSkill = [0, sdk.skills.Concentration]; - Config.BeltColumn = ["hp", "hp", "mp", "mp"]; - Config.HPBuffer = me.expansion ? 2 : 4; - Config.MPBuffer = me.expansion && me.charlvl < 80 ? 6 : me.classic ? 5 : 2; - (me.hell && !Pather.accessToAct(5)) && (Config.SkipImmune = ["magic"]); - SetUp.belt(); -}); + active: function () { + return (me.charlvl > CharInfo.respecOne && me.charlvl > CharInfo.respecTwo && me.checkSkill(sdk.skills.Concentration, sdk.skills.subindex.HardPoints) && !Check.finalBuild().active()); + }, + }; + + // Has to be set after its loaded + build.stats = me.classic + ? [["dexterity", 51], ["strength", 80], ["vitality", "all"]] + : [ + ["vitality", 60], ["dexterity", 30], ["strength", 27], + ["vitality", 91], ["dexterity", 44], ["strength", 30], + ["vitality", 96], ["dexterity", 59], ["strength", 60], + ["vitality", 109], ["dexterity", 77], ["strength", 89], + ["vitality", 137], ["dexterity", 89], ["strength", 103], + ["vitality", 173], ["dexterity", 103], + ["vitality", 208], ["dexterity", 118], + ["vitality", 243], ["dexterity", 133], + ["vitality", 279], ["dexterity", 147], + ["vitality", "all"] + ]; + + return build; + })(); +})(module); diff --git a/libs/SoloPlay/BuildFiles/paladin/paladin.SancdreamerBuild.js b/libs/SoloPlay/BuildFiles/paladin/paladin.SancdreamerBuild.js index 64aa3e36..3749d8bf 100644 --- a/libs/SoloPlay/BuildFiles/paladin/paladin.SancdreamerBuild.js +++ b/libs/SoloPlay/BuildFiles/paladin/paladin.SancdreamerBuild.js @@ -8,121 +8,130 @@ * */ -const finalBuild = { - caster: false, - skillstab: sdk.skills.tabs.PalaCombat, - wantedskills: [sdk.skills.Zeal, sdk.skills.Sanctuary], - usefulskills: [sdk.skills.HolyShield, sdk.skills.Sacrifice, sdk.skills.ResistLightning], - precastSkills: [sdk.skills.HolyShield], - usefulStats: [sdk.stats.PassiveLightningMastery, sdk.stats.PassiveLightningPierce, sdk.stats.PierceLtng, sdk.stats.PassiveMagMastery, sdk.stats.PassiveMagPierce], - mercDiff: sdk.difficulty.Nightmare, - mercAct: 2, - mercAuraWanted: "Holy Freeze", - stats: [ - ["strength", 103], ["dexterity", 136], ["vitality", 300], ["dexterity", "block"], ["vitality", "all"] - ], - skills: [ - [sdk.skills.Sanctuary, 20], - [sdk.skills.Zeal, 4], - [sdk.skills.ResistLightning, 20], - [sdk.skills.Cleansing, 20], - [sdk.skills.Redemption, 1], - [sdk.skills.HolyShield, 15], - [sdk.skills.Sacrifice, 19], - ], - autoEquipTiers: [ // autoequip final gear - // Final Weapon - Last Wish - "[type] == sword && [flag] == runeword # [mightaura] >= 17 # [tier] == 120000", - // Temporary Weapon - Crescent Moon - "[type] == sword && [flag] == runeword # [ias] >= 20 && [passiveltngpierce] >= 35 # [tier] == 110000", - // Temporary Weapon - Voice of Reason - "[type] == sword && [flag] == runeword # [passivecoldpierce] >= 24 # [tier] == 102500", - // Helm - Dream - "[type] == helm && [flag] == runeword # [holyshockaura] >= 15 # [tier] == 110000", - // Belt - Verdungos - "[name] == mithrilcoil && [quality] == unique && [flag] != ethereal # [damageresist] == 15 # [tier] == tierscore(item, 110000)", - // Boots - Gore Rider - "[name] == warboots && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 160 # [tier] == tierscore(item, 110000)", - // Armor - CoH - "[type] == armor && [flag] == runeword && [flag] != ethereal # [fireresist] == 65 && [hpregen] == 7 # [tier] == 100000", - // Shield - Dream - "[type] == auricshields && [flag] != ethereal && [flag] == runeword # [holyshockaura] >= 15 # [tier] == 110000", - // Gloves - Laying of Hands - "[name] == bramblemitts && [quality] == set && [flag] != ethereal # [ias] >= 20 # [tier] == 110000", - // Amulet - Highlords - "[type] == amulet && [quality] == unique # [lightresist] == 35 # [tier] == 100000", - // Final Rings - Perfect Raven Frost & Perfect Wisp - "[type] == ring && [quality] == unique # [dexterity] == 20 && [tohit] == 250 # [tier] == 110000", - "[type] == ring && [quality] == unique # [itemabsorblightpercent] == 20 # [tier] == 110000", - // Rings - Raven Frost & Wisp - "[type] == ring && [quality] == unique # [dexterity] >= 15 && [tohit] >= 150 # [tier] == 100000", - "[type] == ring && [quality] == unique # [itemabsorblightpercent] >= 10 # [tier] == 100000", - // Switch - CTA - "[minimumsockets] >= 5 && [flag] == runeword # [plusskillbattleorders] >= 1 # [secondarytier] == 100000", - // Merc Final Armor - Fortitude - "[type] == armor && [flag] == runeword # [enhanceddefense] >= 200 && [enhanceddamage] >= 300 # [merctier] == 100000", - // Merc Armor - Treachery - "[type] == armor && [flag] == runeword # [ias] == 45 && [coldresist] == 30 # [merctier] == 50000 + mercscore(item)", - // Merc Final Helmet - Eth Andy's - "[name] == demonhead && [quality] == unique && [flag] == ethereal # [strength] >= 25 && [enhanceddefense] >= 100 # [merctier] == 50000 + mercscore(item)", - // Merc Helmet - Andy's - "[name] == demonhead && [quality] == unique && [flag] != ethereal # [strength] >= 25 && [enhanceddefense] >= 100 # [merctier] == 40000 + mercscore(item)", - ], - charms: { - ResLife: { - max: 6, - have: [], - classid: sdk.items.SmallCharm, - stats: function (check) { - return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MaxHp) === 20); - } - }, +(function (module) { + module.exports = (function () { + const build = { + caster: false, + skillstab: sdk.skills.tabs.PalaCombat, + wantedskills: [sdk.skills.Zeal, sdk.skills.Sanctuary], + usefulskills: [sdk.skills.HolyShield, sdk.skills.Sacrifice, sdk.skills.ResistLightning], + precastSkills: [sdk.skills.HolyShield], + usefulStats: [sdk.stats.PassiveLightningMastery, sdk.stats.PassiveLightningPierce, sdk.stats.PierceLtng, sdk.stats.PassiveMagMastery, sdk.stats.PassiveMagPierce], + wantedMerc: MercData[sdk.skills.HolyFreeze], + stats: [ + ["strength", 103], ["dexterity", 136], ["vitality", 300], ["dexterity", "block"], ["vitality", "all"] + ], + skills: [ + [sdk.skills.Sanctuary, 20], + [sdk.skills.Zeal, 4], + [sdk.skills.ResistLightning, 20], + [sdk.skills.Cleansing, 20], + [sdk.skills.Redemption, 1], + [sdk.skills.HolyShield, 15], + [sdk.skills.Sacrifice, 19], + ], - ResMf: { - max: 2, - have: [], - classid: sdk.items.SmallCharm, - stats: function (check) { - return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MagicBonus) === 7); - } - }, + charms: { + ResLife: { + max: 6, + have: [], + classid: sdk.items.SmallCharm, + stats: function (check) { + return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MaxHp) === 20); + } + }, - Skiller: { - max: 2, - have: [], - classid: sdk.items.GrandCharm, - stats: function (check) { - return (!check.unique && check.classid === this.classid && check.getStat(sdk.stats.AddSkillTab, sdk.skills.tabs.Offensive) === 1 - && check.getStat(sdk.stats.MaxHp) >= 40); - } - }, - }, + ResMf: { + max: 2, + have: [], + classid: sdk.items.SmallCharm, + stats: function (check) { + return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MagicBonus) === 7); + } + }, - AutoBuildTemplate: { - 1: { - Update: function () { - Config.Vigor = false; - Config.AttackSkill = [-1, sdk.skills.Zeal, sdk.skills.Sanctuary, sdk.skills.Zeal, sdk.skills.Sanctuary, -1, -1]; - Config.LowManaSkill = [-1, -1]; + Skiller: { + max: 2, + have: [], + classid: sdk.items.GrandCharm, + stats: function (check) { + return (!check.unique && check.classid === this.classid && check.getStat(sdk.stats.AddSkillTab, sdk.skills.tabs.Offensive) === 1 + && check.getStat(sdk.stats.MaxHp) >= 40); + } + }, + }, - Config.SkipImmune = ["lightning and magic and physical"]; // Don't think this ever happens but should skip if it does - Config.BeltColumn = ["hp", "hp", "mp", "rv"]; - SetUp.belt(); - } - }, - }, + AutoBuildTemplate: { + 1: { + Update: function () { + Config.Vigor = false; + Config.AttackSkill = [-1, sdk.skills.Zeal, sdk.skills.Sanctuary, sdk.skills.Zeal, sdk.skills.Sanctuary, -1, -1]; + Config.LowManaSkill = [-1, -1]; - respec: function () { - if (me.classic) { - return false; - } else { - return me.haveAll([{ name: sdk.locale.items.Dream, itemtype: sdk.items.type.AuricShields }, { name: sdk.locale.items.Dream, itemtype: sdk.items.type.Helm }, - { name: sdk.locale.items.LastWish }]); - } - }, + Config.SkipImmune = ["lightning and magic and physical"]; // Don't think this ever happens but should skip if it does + Config.BeltColumn = ["hp", "hp", "mp", "rv"]; + SetUp.belt(); + } + }, + }, - active: function () { - return this.respec() && me.getSkill(sdk.skills.Sanctuary, sdk.skills.subindex.HardPoints) === 20; - }, -}; + respec: function () { + if (me.classic) { + return false; + } else { + return me.haveAll([{ name: sdk.locale.items.Dream, itemtype: sdk.items.type.AuricShields }, { name: sdk.locale.items.Dream, itemtype: sdk.items.type.Helm }, + { name: sdk.locale.items.LastWish }]); + } + }, + + active: function () { + return this.respec() && me.getSkill(sdk.skills.Sanctuary, sdk.skills.subindex.HardPoints) === 20; + }, + }; + + let finalGear = [ // autoequip final gear + // Final Weapon - Last Wish + "[type] == sword && [flag] == runeword # [mightaura] >= 17 # [tier] == 120000", + // Temporary Weapon - Crescent Moon + "[type] == sword && [flag] == runeword # [ias] >= 20 && [passiveltngpierce] >= 35 # [tier] == 110000", + // Temporary Weapon - Voice of Reason + "[type] == sword && [flag] == runeword # [passivecoldpierce] >= 24 # [tier] == 102500", + // Helm - Dream + "[type] == helm && [flag] == runeword # [holyshockaura] >= 15 # [tier] == 110000", + // Belt - Verdungos + "[name] == mithrilcoil && [quality] == unique && [flag] != ethereal # [damageresist] == 15 # [tier] == tierscore(item, 110000)", + // Boots - Gore Rider + "[name] == warboots && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 160 # [tier] == tierscore(item, 110000)", + // Armor - CoH + "[type] == armor && [flag] == runeword && [flag] != ethereal # [fireresist] == 65 && [hpregen] == 7 # [tier] == 100000", + // Shield - Dream + "[type] == auricshields && [flag] != ethereal && [flag] == runeword # [holyshockaura] >= 15 # [tier] == 110000", + // Gloves - Laying of Hands + "[name] == bramblemitts && [quality] == set && [flag] != ethereal # [ias] >= 20 # [tier] == 110000", + // Amulet - Highlords + "[type] == amulet && [quality] == unique # [lightresist] == 35 # [tier] == 100000", + // Final Rings - Perfect Raven Frost & Perfect Wisp + "[type] == ring && [quality] == unique # [dexterity] == 20 && [tohit] == 250 # [tier] == 110000", + "[type] == ring && [quality] == unique # [itemabsorblightpercent] == 20 # [tier] == 110000", + // Rings - Raven Frost & Wisp + "[type] == ring && [quality] == unique # [dexterity] >= 15 && [tohit] >= 150 # [tier] == 100000", + "[type] == ring && [quality] == unique # [itemabsorblightpercent] >= 10 # [tier] == 100000", + // Switch - CTA + "[minimumsockets] >= 5 && [flag] == runeword # [plusskillbattleorders] >= 1 # [secondarytier] == 100000", + // Merc Final Armor - Fortitude + "[type] == armor && [flag] == runeword # [enhanceddefense] >= 200 && [enhanceddamage] >= 300 # [merctier] == 100000", + // Merc Armor - Treachery + "[type] == armor && [flag] == runeword # [ias] == 45 && [coldresist] == 30 # [merctier] == 50000 + mercscore(item)", + // Merc Final Helmet - Eth Andy's + "[name] == demonhead && [quality] == unique && [flag] == ethereal # [strength] >= 25 && [enhanceddefense] >= 100 # [merctier] == 50000 + mercscore(item)", + // Merc Helmet - Andy's + "[name] == demonhead && [quality] == unique && [flag] != ethereal # [strength] >= 25 && [enhanceddefense] >= 100 # [merctier] == 40000 + mercscore(item)", + ]; + + NTIP.buildList(finalGear); + NTIP.buildFinalGear(finalGear); + + return build; + })(); +})(module); diff --git a/libs/SoloPlay/BuildFiles/paladin/paladin.SmiterBuild.js b/libs/SoloPlay/BuildFiles/paladin/paladin.SmiterBuild.js index dd022329..ba382ad0 100644 --- a/libs/SoloPlay/BuildFiles/paladin/paladin.SmiterBuild.js +++ b/libs/SoloPlay/BuildFiles/paladin/paladin.SmiterBuild.js @@ -5,111 +5,120 @@ * */ -const finalBuild = { - caster: false, - skillstab: sdk.skills.tabs.PalaCombat, - wantedskills: [sdk.skills.Smite, sdk.skills.Fanaticism], - usefulskills: [sdk.skills.HolyShield, sdk.skills.Salvation], - precastSkills: [sdk.skills.HolyShield], - mercDiff: sdk.difficulty.Nightmare, - mercAct: 2, - mercAuraWanted: "Holy Freeze", - stats: [ - ["strength", 115], ["dexterity", 136], ["vitality", 300], - ["dexterity", "block"], ["vitality", "all"] - ], - skills: [ - [sdk.skills.Smite, 20], - [sdk.skills.HolyShield, 20], - [sdk.skills.Fanaticism, 20], - [sdk.skills.Salvation, 5], - [sdk.skills.ResistLightning, 15], - [sdk.skills.ResistFire, 14], - [sdk.skills.ResistCold, 10] - ], - autoEquipTiers: [ // autoequip final gear - // Weapon - Grief - "[type] == sword && [flag] == runeword # [ias] >= 30 && [itemdeadlystrike] == 20 && [passivepoispierce] >= 20 # [tier] == 100000", - // Helm - GFace - "[name] == wingedhelm && [quality] == set && [flag] != ethereal # [fhr] >= 30 # [tier] == tierscore(item, 100000)", - // Belt - Tgods - "[name] == warbelt && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 160 # [tier] == tierscore(item, 100000)", - // Boots - Goblin Toes - "[name] == lightplatedboots && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 50 # [tier] == tierscore(item, 100000)", - // Armor - Enigma - "[type] == armor && [flag] != ethereal && [flag] == runeword # [itemallskills] == 2 # [tier] == 100000", - // Shield - HoZ - "[name] == gildedshield && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 150 # [tier] == tierscore(item, 100000)", - // Gloves - Drac's - "[name] == vampirebonegloves && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 100 && [strength] >= 12 && [lifeleech] >= 9 # [tier] == tierscore(item, 100000)", - // Amulet - Highlords - "[type] == amulet && [quality] == unique # [lightresist] == 35 # [tier] == 100000", - // Final Rings - Perfect Raven Frost & Bul-Kathos' Wedding Band - "[type] == ring && [quality] == unique # [dexterity] == 20 && [tohit] == 250 # [tier] == 110000", - "[type] == ring && [quality] == unique # [maxstamina] == 50 && [lifeleech] == 5 # [tier] == 110000", - // Rings - Raven Frost && Bul-Kathos' Wedding Band - "[type] == ring && [quality] == unique # [dexterity] >= 15 && [tohit] >= 150 # [tier] == 100000", - "[type] == ring && [quality] == unique # [maxstamina] == 50 && [lifeleech] >= 3 # [tier] == 100000", - // Switch - CTA - "[minimumsockets] >= 5 && [flag] == runeword # [plusskillbattleorders] >= 1 # [secondarytier] == 100000", - // Merc Final Armor - Fortitude - "[type] == armor && [flag] == runeword # [enhanceddefense] >= 200 && [enhanceddamage] >= 300 # [merctier] == 100000", - // Merc Final Helmet - Eth Andy's - "[name] == demonhead && [quality] == unique && [flag] == ethereal # [strength] >= 25 && [enhanceddefense] >= 100 # [merctier] == 50000 + mercscore(item)", - // Merc Helmet - Andy's - "[name] == demonhead && [quality] == unique && [flag] != ethereal # [strength] >= 25 && [enhanceddefense] >= 100 # [merctier] == 40000 + mercscore(item)", - ], - charms: { - ResLife: { - max: 6, - have: [], - classid: sdk.items.SmallCharm, - stats: function (check) { - return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MaxHp) === 20); - } - }, +(function (module) { + module.exports = (function () { + const build = { + caster: false, + skillstab: sdk.skills.tabs.PalaCombat, + wantedskills: [sdk.skills.Smite, sdk.skills.Fanaticism], + usefulskills: [sdk.skills.HolyShield, sdk.skills.Salvation], + precastSkills: [sdk.skills.HolyShield], + wantedMerc: MercData[sdk.skills.HolyFreeze], + stats: [ + ["strength", 115], ["dexterity", 136], ["vitality", 300], + ["dexterity", "block"], ["vitality", "all"] + ], + skills: [ + [sdk.skills.Smite, 20], + [sdk.skills.HolyShield, 20], + [sdk.skills.Fanaticism, 20], + [sdk.skills.Salvation, 5], + [sdk.skills.ResistLightning, 15], + [sdk.skills.ResistFire, 14], + [sdk.skills.ResistCold, 10] + ], - ResMf: { - max: 2, - have: [], - classid: sdk.items.SmallCharm, - stats: function (check) { - return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MagicBonus) === 7); - } - }, + charms: { + ResLife: { + max: 6, + have: [], + classid: sdk.items.SmallCharm, + stats: function (check) { + return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MaxHp) === 20); + } + }, - Skiller: { - max: 2, - have: [], - classid: sdk.items.GrandCharm, - stats: function (check) { - return (!check.unique && check.classid === this.classid && check.getStat(sdk.stats.AddSkillTab, sdk.skills.tabs.PalaCombat) === 1 - && check.getStat(sdk.stats.MaxHp) >= 40); - } - }, - }, + ResMf: { + max: 2, + have: [], + classid: sdk.items.SmallCharm, + stats: function (check) { + return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MagicBonus) === 7); + } + }, - AutoBuildTemplate: { - 1: { - Update: function () { - Config.AttackSkill = [-1, sdk.skills.Smite, sdk.skills.Fanaticism, sdk.skills.Smite, sdk.skills.Fanaticism, sdk.skills.BlessedHammer, sdk.skills.Concentration]; - Config.LowManaSkill = [0, sdk.skills.Fanaticism]; - Config.BeltColumn = ["hp", "hp", "mp", "rv"]; - SetUp.belt(); - } - }, - }, + Skiller: { + max: 2, + have: [], + classid: sdk.items.GrandCharm, + stats: function (check) { + return (!check.unique && check.classid === this.classid && check.getStat(sdk.stats.AddSkillTab, sdk.skills.tabs.PalaCombat) === 1 + && check.getStat(sdk.stats.MaxHp) >= 40); + } + }, + }, - respec: function () { - if (me.classic) { - return me.charlvl >= 75 && me.diablo; - } else { - return me.checkItem({ name: sdk.locale.items.Grief, itemtype: sdk.items.type.Sword }).have; - } - }, + AutoBuildTemplate: { + 1: { + Update: function () { + Config.AttackSkill = [-1, sdk.skills.Smite, sdk.skills.Fanaticism, sdk.skills.Smite, sdk.skills.Fanaticism, sdk.skills.BlessedHammer, sdk.skills.Concentration]; + Config.LowManaSkill = [0, sdk.skills.Fanaticism]; + Config.BeltColumn = ["hp", "hp", "mp", "rv"]; + SetUp.belt(); + } + }, + }, - active: function () { - return this.respec() && me.getSkill(sdk.skills.Smite, sdk.skills.subindex.HardPoints) === 20; - }, -}; + respec: function () { + if (me.classic) { + return me.charlvl >= 75 && me.diablo; + } else { + return me.checkItem({ name: sdk.locale.items.Grief, itemtype: sdk.items.type.Sword }).have; + } + }, + + active: function () { + return this.respec() && me.getSkill(sdk.skills.Smite, sdk.skills.subindex.HardPoints) === 20; + }, + }; + + let finalGear = [ // autoequip final gear + // Weapon - Grief + "[type] == sword && [flag] == runeword # [ias] >= 30 && [itemdeadlystrike] == 20 && [passivepoispierce] >= 20 # [tier] == 100000", + // Helm - GFace + "[name] == wingedhelm && [quality] == set && [flag] != ethereal # [fhr] >= 30 # [tier] == tierscore(item, 100000)", + // Belt - Tgods + "[name] == warbelt && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 160 # [tier] == tierscore(item, 100000)", + // Boots - Goblin Toes + "[name] == lightplatedboots && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 50 # [tier] == tierscore(item, 100000)", + // Armor - Enigma + "[type] == armor && [flag] != ethereal && [flag] == runeword # [itemallskills] == 2 # [tier] == 100000", + // Shield - HoZ + "[name] == gildedshield && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 150 # [tier] == tierscore(item, 100000)", + // Gloves - Drac's + "[name] == vampirebonegloves && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 100 && [strength] >= 12 && [lifeleech] >= 9 # [tier] == tierscore(item, 100000)", + // Amulet - Highlords + "[type] == amulet && [quality] == unique # [lightresist] == 35 # [tier] == 100000", + // Final Rings - Perfect Raven Frost & Bul-Kathos' Wedding Band + "[type] == ring && [quality] == unique # [dexterity] == 20 && [tohit] == 250 # [tier] == 110000", + "[type] == ring && [quality] == unique # [maxstamina] == 50 && [lifeleech] == 5 # [tier] == 110000", + // Rings - Raven Frost && Bul-Kathos' Wedding Band + "[type] == ring && [quality] == unique # [dexterity] >= 15 && [tohit] >= 150 # [tier] == 100000", + "[type] == ring && [quality] == unique # [maxstamina] == 50 && [lifeleech] >= 3 # [tier] == 100000", + // Switch - CTA + "[minimumsockets] >= 5 && [flag] == runeword # [plusskillbattleorders] >= 1 # [secondarytier] == 100000", + // Merc Final Armor - Fortitude + "[type] == armor && [flag] == runeword # [enhanceddefense] >= 200 && [enhanceddamage] >= 300 # [merctier] == 100000", + // Merc Final Helmet - Eth Andy's + "[name] == demonhead && [quality] == unique && [flag] == ethereal # [strength] >= 25 && [enhanceddefense] >= 100 # [merctier] == 50000 + mercscore(item)", + // Merc Helmet - Andy's + "[name] == demonhead && [quality] == unique && [flag] != ethereal # [strength] >= 25 && [enhanceddefense] >= 100 # [merctier] == 40000 + mercscore(item)", + ]; + + NTIP.buildList(finalGear); + NTIP.buildFinalGear(finalGear); + + return build; + })(); +})(module); diff --git a/libs/SoloPlay/BuildFiles/paladin/paladin.StartBuild.js b/libs/SoloPlay/BuildFiles/paladin/paladin.StartBuild.js index 294088ad..ad5740c5 100644 --- a/libs/SoloPlay/BuildFiles/paladin/paladin.StartBuild.js +++ b/libs/SoloPlay/BuildFiles/paladin/paladin.StartBuild.js @@ -5,85 +5,97 @@ * */ -let build = { - AutoBuildTemplate: {}, - caster: false, - skillstab: sdk.skills.tabs.PalaCombat, - wantedskills: [sdk.skills.Zeal, sdk.skills.HolyFire], - usefulskills: [(me.checkSkill(sdk.skills.HolyFire, sdk.skills.subindex.SoftPoints) ? sdk.skills.Sacrifice : sdk.skills.Might), sdk.skills.ResistFire], - mercDiff: sdk.difficulty.Nightmare, - mercAct: 2, - mercAuraWanted: "Holy Freeze", - stats: [ - ["vitality", 80], - ["dexterity", 27], - ["strength", 47], - ["vitality", "all"], - ], - skills: [ - // Total skills points by respec = 20 - [sdk.skills.Might, 1], // charlevel -> 2 - [sdk.skills.Sacrifice, 1], // charlevel -> 3 - [sdk.skills.HolyFire, 1, false], // charlevel -> 6 - [sdk.skills.ResistFire, 4], // charlevel -> 5 - [sdk.skills.HolyFire, 3], // charlevel -> 8 - [sdk.skills.Smite, 1], // charlevel -> 10 - [sdk.skills.Zeal, 1], // charlevel -> 12 - [sdk.skills.Charge, 1], // charlevel -> 12 - [sdk.skills.Zeal, 4, false], // charlevel -> 15 - [sdk.skills.HolyFire, 6], // charlevel -> 17 - [sdk.skills.ResistFire, 16] // respec at 19 - ], - active: function () { - return me.charlvl < CharInfo.respecOne && !me.checkSkill(sdk.skills.BlessedAim, sdk.skills.subindex.HardPoints); - }, -}; +(function (module, require) { + module.exports = (function () { + const build = { + AutoBuildTemplate: {}, + caster: false, + skillstab: sdk.skills.tabs.PalaCombat, + wantedskills: [sdk.skills.Zeal, sdk.skills.HolyFire], + usefulskills: [ + (me.checkSkill(sdk.skills.HolyFire, sdk.skills.subindex.SoftPoints) + ? sdk.skills.Sacrifice + : sdk.skills.Might), + sdk.skills.ResistFire + ], + wantedMerc: MercData[sdk.skills.HolyFreeze], + stats: [ + ["vitality", 80], + ["dexterity", 27], + ["strength", 47], + ["vitality", "all"], + ], + skills: [ + // Total skills points by respec = 20 + [sdk.skills.Might, 1], // charlevel -> 2 + [sdk.skills.Sacrifice, 1], // charlevel -> 3 + [sdk.skills.HolyFire, 1, false], // charlevel -> 6 + [sdk.skills.ResistFire, 4], // charlevel -> 5 + [sdk.skills.HolyFire, 3], // charlevel -> 8 + [sdk.skills.Smite, 1], // charlevel -> 10 + [sdk.skills.Zeal, 1], // charlevel -> 12 + [sdk.skills.Charge, 1], // charlevel -> 12 + [sdk.skills.Zeal, 4, false], // charlevel -> 15 + [sdk.skills.HolyFire, 6], // charlevel -> 17 + [sdk.skills.ResistFire, 16] // respec at 19 + ], -build.AutoBuildTemplate[1] = buildAutoBuildTempObj(() => { - Config.SkipEnchant.indexOf("cold enchanted") === -1 && Config.SkipEnchant.push("cold enchanted"); - Config.ScanShrines.indexOf(sdk.shrines.Combat) === -1 && Config.ScanShrines.push(sdk.shrines.Combat); - Config.FieldID.Enabled = !Misc.checkQuest(sdk.quest.id.TheSearchForCain, sdk.quest.states.Completed); - Config.FieldID.UsedSpace = 0; + active: function () { + return me.charlvl < CharInfo.respecOne && !me.checkSkill(sdk.skills.BlessedAim, sdk.skills.subindex.HardPoints); + }, + }; - Config.BeltColumn = ["hp", "hp", "hp", "hp"]; - Config.HPBuffer = 8; - Config.AttackSkill = [-1, sdk.skills.Attack, -1, sdk.skills.Attack, -1, -1, -1]; - Config.LowManaSkill = [sdk.skills.Attack, -1]; - SetUp.belt(); -}); -build.AutoBuildTemplate[2] = buildAutoBuildTempObj(() => { - Config.SkipEnchant.indexOf("cold enchanted") === -1 && Config.SkipEnchant.push("cold enchanted"); - Config.ScanShrines.indexOf(sdk.shrines.Combat) === -1 && Config.ScanShrines.push(sdk.shrines.Combat); - Config.FieldID.Enabled = !Misc.checkQuest(sdk.quest.id.TheSearchForCain, sdk.quest.states.Completed); - Config.FieldID.UsedSpace = 0; + const { buildAutoBuildTempObj } = require("../../Utils/General"); + + build.AutoBuildTemplate[1] = buildAutoBuildTempObj(() => { + Config.SkipEnchant.indexOf("cold enchanted") === -1 && Config.SkipEnchant.push("cold enchanted"); + Config.ScanShrines.indexOf(sdk.shrines.Combat) === -1 && Config.ScanShrines.push(sdk.shrines.Combat); + Config.FieldID.Enabled = !Misc.checkQuest(sdk.quest.id.TheSearchForCain, sdk.quest.states.Completed); + Config.FieldID.UsedSpace = 0; - Config.BeltColumn = ["hp", "hp", "hp", "hp"]; - Config.HPBuffer = 8; - const bossSkill = (me.checkSkill(sdk.skills.Sacrifice, sdk.skills.subindex.HardPoints) ? sdk.skills.Sacrifice : sdk.skills.Attack); - Config.AttackSkill = [-1, bossSkill, sdk.skills.Might, sdk.skills.Attack, sdk.skills.Might, -1, -1]; - Config.LowManaSkill = [sdk.skills.Attack, sdk.skills.Might]; -}); -build.AutoBuildTemplate[6] = buildAutoBuildTempObj(() => { - Config.HPBuffer = 8; - const bossSkill = (me.checkSkill(sdk.skills.Sacrifice, sdk.skills.subindex.HardPoints) ? sdk.skills.Sacrifice : sdk.skills.Attack); - Config.AttackSkill = [-1, bossSkill, sdk.skills.HolyFire, sdk.skills.Attack, sdk.skills.HolyFire, sdk.skills.Attack, sdk.skills.Might]; - Config.LowManaSkill = [sdk.skills.Attack, sdk.skills.HolyFire]; -}); -build.AutoBuildTemplate[9] = buildAutoBuildTempObj(() => { - Config.HPBuffer = me.expansion ? 2 : 4; - Config.MPBuffer = 6; - Config.AttackSkill[0] = -1; - Config.AttackSkill[1] = (me.checkSkill(sdk.skills.Sacrifice, sdk.skills.subindex.HardPoints) ? sdk.skills.Sacrifice : sdk.skills.Attack); - Config.AttackSkill[2] = sdk.skills.HolyFire; - Config.AttackSkill[3] = sdk.skills.Attack; - Config.AttackSkill[4] = sdk.skills.HolyFire; - Config.AttackSkill[5] = sdk.skills.Attack; - Config.AttackSkill[6] = sdk.skills.Might; -}); -build.AutoBuildTemplate[12] = buildAutoBuildTempObj(() => { - if (me.checkSkill(sdk.skills.Zeal, sdk.skills.subindex.HardPoints)) { - Config.AttackSkill = [-1, sdk.skills.Zeal, sdk.skills.HolyFire, sdk.skills.Zeal, sdk.skills.HolyFire, 0, sdk.skills.Might]; - } - Config.Charge = true; -}); + Config.BeltColumn = ["hp", "hp", "hp", "hp"]; + Config.HPBuffer = 8; + Config.AttackSkill = [-1, sdk.skills.Attack, -1, sdk.skills.Attack, -1, -1, -1]; + Config.LowManaSkill = [sdk.skills.Attack, -1]; + SetUp.belt(); + }); + build.AutoBuildTemplate[2] = buildAutoBuildTempObj(() => { + Config.SkipEnchant.indexOf("cold enchanted") === -1 && Config.SkipEnchant.push("cold enchanted"); + Config.ScanShrines.indexOf(sdk.shrines.Combat) === -1 && Config.ScanShrines.push(sdk.shrines.Combat); + Config.FieldID.Enabled = !Misc.checkQuest(sdk.quest.id.TheSearchForCain, sdk.quest.states.Completed); + Config.FieldID.UsedSpace = 0; + + Config.BeltColumn = ["hp", "hp", "hp", "hp"]; + Config.HPBuffer = 8; + const bossSkill = (me.checkSkill(sdk.skills.Sacrifice, sdk.skills.subindex.HardPoints) ? sdk.skills.Sacrifice : sdk.skills.Attack); + Config.AttackSkill = [-1, bossSkill, sdk.skills.Might, sdk.skills.Attack, sdk.skills.Might, -1, -1]; + Config.LowManaSkill = [sdk.skills.Attack, sdk.skills.Might]; + }); + build.AutoBuildTemplate[6] = buildAutoBuildTempObj(() => { + Config.HPBuffer = 8; + const bossSkill = (me.checkSkill(sdk.skills.Sacrifice, sdk.skills.subindex.HardPoints) ? sdk.skills.Sacrifice : sdk.skills.Attack); + Config.AttackSkill = [-1, bossSkill, sdk.skills.HolyFire, sdk.skills.Attack, sdk.skills.HolyFire, sdk.skills.Attack, sdk.skills.Might]; + Config.LowManaSkill = [sdk.skills.Attack, sdk.skills.HolyFire]; + }); + build.AutoBuildTemplate[9] = buildAutoBuildTempObj(() => { + Config.HPBuffer = me.expansion ? 2 : 4; + Config.MPBuffer = 6; + Config.AttackSkill[0] = -1; + Config.AttackSkill[1] = (me.checkSkill(sdk.skills.Sacrifice, sdk.skills.subindex.HardPoints) ? sdk.skills.Sacrifice : sdk.skills.Attack); + Config.AttackSkill[2] = sdk.skills.HolyFire; + Config.AttackSkill[3] = sdk.skills.Attack; + Config.AttackSkill[4] = sdk.skills.HolyFire; + Config.AttackSkill[5] = sdk.skills.Attack; + Config.AttackSkill[6] = sdk.skills.Might; + }); + build.AutoBuildTemplate[12] = buildAutoBuildTempObj(() => { + if (me.checkSkill(sdk.skills.Zeal, sdk.skills.subindex.HardPoints)) { + Config.AttackSkill = [-1, sdk.skills.Zeal, sdk.skills.HolyFire, sdk.skills.Zeal, sdk.skills.HolyFire, 0, sdk.skills.Might]; + } + Config.Charge = true; + }); + + return build; + })(); +})(module, require); diff --git a/libs/SoloPlay/BuildFiles/paladin/paladin.TorchadinBuild.js b/libs/SoloPlay/BuildFiles/paladin/paladin.TorchadinBuild.js index 2d3821ff..9af4f0a6 100644 --- a/libs/SoloPlay/BuildFiles/paladin/paladin.TorchadinBuild.js +++ b/libs/SoloPlay/BuildFiles/paladin/paladin.TorchadinBuild.js @@ -5,123 +5,133 @@ * */ -const finalBuild = { - caster: false, - skillstab: sdk.skills.tabs.PalaCombat, - wantedskills: [sdk.skills.Zeal, sdk.skills.Conviction], - usefulskills: [sdk.skills.HolyShield, sdk.skills.ResistFire, sdk.skills.Salvation], - precastSkills: [sdk.skills.HolyShield], - usefulStats: [sdk.stats.PassiveFireMastery, sdk.stats.PassiveFirePierce, sdk.stats.PierceFire], - mercDiff: sdk.difficulty.Nightmare, - mercAct: 2, - mercAuraWanted: "Holy Freeze", - stats: [ - ["strength", 103], ["dexterity", 136], - ["vitality", 300], ["dexterity", "block"], ["vitality", "all"] - ], - skills: [ - [sdk.skills.Conviction, 20], - [sdk.skills.Zeal, 4], - [sdk.skills.Salvation, 20], - [sdk.skills.ResistFire, 20], - [sdk.skills.Redemption, 1], - [sdk.skills.HolyShield, 15], - [sdk.skills.Zeal, 10], - [sdk.skills.Sacrifice, 20], - ], - autoEquipTiers: [ // autoequip final gear - // Final Weapon - HoJ - "[type] == sword && [flag] == runeword # [holyfireaura] >= 16 # [tier] == 120000", - // Temporary Weapon - Crescent Moon - "[type] == sword && [flag] == runeword # [ias] >= 20 && [passiveltngpierce] >= 35 # [tier] == 110000", - // Temporary Weapon - Voice of Reason - "[type] == sword && [flag] == runeword # [passivecoldpierce] >= 24 # [tier] == 102500", - // Final Helm - Upp'ed Vamp Gaze - "[name] == bonevisage && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 100 && [lifeleech] >= 6 # [tier] == tierscore(item, 100000)", - // Helm - Vamp Gaze - "[name] == grimhelm && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 100 # [tier] == tierscore(item, 100000)", - // Belt - TGods - "[name] == warbelt && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 160 # [tier] == tierscore(item, 110000)", - // Boots - Gore Rider - "[name] == warboots && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 160 # [tier] == tierscore(item, 110000)", - // Armor - Dragon - "[type] == armor && [flag] != ethereal && [flag] == runeword # [holyfireaura] >= 14 # [tier] == 110000", - // Shield - Exile - "[type] == auricshields && [flag] == runeword # [defianceaura] >= 13 # [tier] == 110000", - // Gloves - Laying of Hands - "[name] == bramblemitts && [quality] == set && [flag] != ethereal # [ias] >= 20 # [tier] == 110000", - // Amulet - Highlords - "[type] == amulet && [quality] == unique # [lightresist] == 35 # [tier] == 100000", - // Final Rings - Perfect Raven Frost & Bul-Kathos' Wedding Band - "[type] == ring && [quality] == unique # [dexterity] == 20 && [tohit] == 250 # [tier] == 110000", - "[type] == ring && [quality] == unique # [maxstamina] == 50 && [lifeleech] == 5 # [tier] == 110000", - // Rings - Raven Frost && Bul-Kathos' Wedding Band - "[type] == ring && [quality] == unique # [dexterity] >= 15 && [tohit] >= 150 # [tier] == 100000", - "[type] == ring && [quality] == unique # [maxstamina] == 50 && [lifeleech] >= 3 # [tier] == 100000", - // Switch - CTA - "[minimumsockets] >= 5 && [flag] == runeword # [plusskillbattleorders] >= 1 # [secondarytier] == 100000", - // Merc Final Armor - Fortitude - "[type] == armor && [flag] == runeword # [enhanceddefense] >= 200 && [enhanceddamage] >= 300 # [merctier] == 100000", - // Merc Armor - Treachery - "[type] == armor && [flag] == runeword # [ias] == 45 && [coldresist] == 30 # [merctier] == 50000 + mercscore(item)", - // Merc Final Helmet - Eth Andy's - "[name] == demonhead && [quality] == unique && [flag] == ethereal # [strength] >= 25 && [enhanceddefense] >= 100 # [merctier] == 50000 + mercscore(item)", - // Merc Helmet - Andy's - "[name] == demonhead && [quality] == unique && [flag] != ethereal # [strength] >= 25 && [enhanceddefense] >= 100 # [merctier] == 40000 + mercscore(item)", - ], - charms: { - ResLife: { - max: 6, - have: [], - classid: sdk.items.SmallCharm, - stats: function (check) { - return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MaxHp) === 20); - } - }, +(function (module) { + module.exports = (function () { + const build = { + caster: false, + skillstab: sdk.skills.tabs.PalaCombat, + wantedskills: [sdk.skills.Zeal, sdk.skills.Conviction], + usefulskills: [sdk.skills.HolyShield, sdk.skills.ResistFire, sdk.skills.Salvation], + precastSkills: [sdk.skills.HolyShield], + usefulStats: [sdk.stats.PassiveFireMastery, sdk.stats.PassiveFirePierce, sdk.stats.PierceFire], + wantedMerc: MercData[sdk.skills.HolyFreeze], + stats: [ + ["strength", 103], ["dexterity", 136], + ["vitality", 300], ["dexterity", "block"], ["vitality", "all"] + ], + skills: [ + [sdk.skills.Conviction, 20], + [sdk.skills.Zeal, 4], + [sdk.skills.Salvation, 20], + [sdk.skills.ResistFire, 20], + [sdk.skills.Redemption, 1], + [sdk.skills.HolyShield, 15], + [sdk.skills.Zeal, 10], + [sdk.skills.Sacrifice, 20], + ], - ResMf: { - max: 2, - have: [], - classid: sdk.items.SmallCharm, - stats: function (check) { - return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MagicBonus) === 7); - } - }, + charms: { + ResLife: { + max: 6, + have: [], + classid: sdk.items.SmallCharm, + stats: function (check) { + return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MaxHp) === 20); + } + }, - Skiller: { - max: 2, - have: [], - classid: sdk.items.GrandCharm, - stats: function (check) { - return (!check.unique && check.classid === this.classid && check.getStat(sdk.stats.AddSkillTab, sdk.skills.tabs.Offensive) === 1 - && check.getStat(sdk.stats.MaxHp) >= 40); - } - }, - }, + ResMf: { + max: 2, + have: [], + classid: sdk.items.SmallCharm, + stats: function (check) { + return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MagicBonus) === 7); + } + }, - AutoBuildTemplate: { - 1: { - Update: function () { - Config.Vigor = false; - Config.AttackSkill = [-1, sdk.skills.Zeal, sdk.skills.Conviction, sdk.skills.Zeal, sdk.skills.Conviction, -1, -1]; - Config.LowManaSkill = [-1, -1]; - Config.SkipImmune = ["fire and physical"]; - Config.BeltColumn = ["hp", "hp", "mp", "rv"]; - SetUp.belt(); - } - }, - }, + Skiller: { + max: 2, + have: [], + classid: sdk.items.GrandCharm, + stats: function (check) { + return (!check.unique && check.classid === this.classid && check.getStat(sdk.stats.AddSkillTab, sdk.skills.tabs.Offensive) === 1 + && check.getStat(sdk.stats.MaxHp) >= 40); + } + }, + }, - respec: function () { - if (me.classic) { - return false; - } else { - return me.haveAll([{ name: sdk.locale.items.HandofJustice }, { name: sdk.locale.items.Dragon, itemtype: sdk.items.type.Armor }]); - } - }, + AutoBuildTemplate: { + 1: { + Update: function () { + Config.Vigor = false; + Config.AttackSkill = [-1, sdk.skills.Zeal, sdk.skills.Conviction, sdk.skills.Zeal, sdk.skills.Conviction, -1, -1]; + Config.LowManaSkill = [-1, -1]; + Config.SkipImmune = ["fire and physical"]; + Config.BeltColumn = ["hp", "hp", "mp", "rv"]; + SetUp.belt(); + } + }, + }, - active: function () { - return this.respec() && me.getSkill(sdk.skills.Conviction, sdk.skills.subindex.HardPoints) === 20; - }, -}; + respec: function () { + if (me.classic) { + return false; + } else { + return me.haveAll([{ name: sdk.locale.items.HandofJustice }, { name: sdk.locale.items.Dragon, itemtype: sdk.items.type.Armor }]); + } + }, + + active: function () { + return this.respec() && me.getSkill(sdk.skills.Conviction, sdk.skills.subindex.HardPoints) === 20; + }, + }; + + // autoequip final gear + let finalGear = [ + // Final Weapon - HoJ + "[type] == sword && [flag] == runeword # [holyfireaura] >= 16 # [tier] == 120000", + // Temporary Weapon - Crescent Moon + "[type] == sword && [flag] == runeword # [ias] >= 20 && [passiveltngpierce] >= 35 # [tier] == 110000", + // Temporary Weapon - Voice of Reason + "[type] == sword && [flag] == runeword # [passivecoldpierce] >= 24 # [tier] == 102500", + // Final Helm - Upp'ed Vamp Gaze + "[name] == bonevisage && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 100 && [lifeleech] >= 6 # [tier] == tierscore(item, 100000)", + // Helm - Vamp Gaze + "[name] == grimhelm && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 100 # [tier] == tierscore(item, 100000)", + // Belt - TGods + "[name] == warbelt && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 160 # [tier] == tierscore(item, 110000)", + // Boots - Gore Rider + "[name] == warboots && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 160 # [tier] == tierscore(item, 110000)", + // Armor - Dragon + "[type] == armor && [flag] != ethereal && [flag] == runeword # [holyfireaura] >= 14 # [tier] == 110000", + // Shield - Exile + "[type] == auricshields && [flag] == runeword # [defianceaura] >= 13 # [tier] == 110000", + // Gloves - Laying of Hands + "[name] == bramblemitts && [quality] == set && [flag] != ethereal # [ias] >= 20 # [tier] == 110000", + // Amulet - Highlords + "[type] == amulet && [quality] == unique # [lightresist] == 35 # [tier] == 100000", + // Final Rings - Perfect Raven Frost & Bul-Kathos' Wedding Band + "[type] == ring && [quality] == unique # [dexterity] == 20 && [tohit] == 250 # [tier] == 110000", + "[type] == ring && [quality] == unique # [maxstamina] == 50 && [lifeleech] == 5 # [tier] == 110000", + // Rings - Raven Frost && Bul-Kathos' Wedding Band + "[type] == ring && [quality] == unique # [dexterity] >= 15 && [tohit] >= 150 # [tier] == 100000", + "[type] == ring && [quality] == unique # [maxstamina] == 50 && [lifeleech] >= 3 # [tier] == 100000", + // Switch - CTA + "[minimumsockets] >= 5 && [flag] == runeword # [plusskillbattleorders] >= 1 # [secondarytier] == 100000", + // Merc Final Armor - Fortitude + "[type] == armor && [flag] == runeword # [enhanceddefense] >= 200 && [enhanceddamage] >= 300 # [merctier] == 100000", + // Merc Armor - Treachery + "[type] == armor && [flag] == runeword # [ias] == 45 && [coldresist] == 30 # [merctier] == 50000 + mercscore(item)", + // Merc Final Helmet - Eth Andy's + "[name] == demonhead && [quality] == unique && [flag] == ethereal # [strength] >= 25 && [enhanceddefense] >= 100 # [merctier] == 50000 + mercscore(item)", + // Merc Helmet - Andy's + "[name] == demonhead && [quality] == unique && [flag] != ethereal # [strength] >= 25 && [enhanceddefense] >= 100 # [merctier] == 40000 + mercscore(item)", + ]; + + NTIP.buildList(finalGear); + NTIP.buildFinalGear(finalGear); + + return build; + })(); +})(module); diff --git a/libs/SoloPlay/BuildFiles/paladin/paladin.ZealerBuild.js b/libs/SoloPlay/BuildFiles/paladin/paladin.ZealerBuild.js index bebd93ab..3c0435c3 100644 --- a/libs/SoloPlay/BuildFiles/paladin/paladin.ZealerBuild.js +++ b/libs/SoloPlay/BuildFiles/paladin/paladin.ZealerBuild.js @@ -5,119 +5,129 @@ * */ -const finalBuild = { - caster: false, - skillstab: sdk.skills.tabs.PalaCombat, - wantedskills: [sdk.skills.Zeal, sdk.skills.Fanaticism], - usefulskills: [sdk.skills.HolyShield, sdk.skills.ResistFire, sdk.skills.ResistLightning], - precastSkills: [sdk.skills.HolyShield], - mercDiff: sdk.difficulty.Nightmare, - mercAct: 2, - mercAuraWanted: "Holy Freeze", - stats: [ - ["strength", 103], ["dexterity", 136], ["vitality", 300], - ["dexterity", "block"], ["vitality", "all"] - ], - skills: [ - [sdk.skills.Fanaticism, 20], - [sdk.skills.Sacrifice, 20], - [sdk.skills.Salvation, 1], - [sdk.skills.Redemption, 1], - [sdk.skills.Zeal, 10], - [sdk.skills.HolyShield, 15], // lvl 74 w/o quest skill pts - [sdk.skills.ResistLightning, 10, false], - [sdk.skills.ResistFire, 10, false], - [sdk.skills.ResistCold, 10, false], - ], - autoEquipTiers: [ // autoequip final gear - // Weapon - Grief - "[type] == sword && [flag] == runeword # [ias] >= 30 && [itemdeadlystrike] == 20 && [passivepoispierce] >= 20 # [tier] == 100000", - // Final Helm - Upp'ed Vamp Gaze - "[name] == bonevisage && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 100 && [lifeleech] >= 6 # [tier] == tierscore(item, 100000)", - // Helm -Vamp Gaze - "[name] == grimhelm && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 100 # [tier] == tierscore(item, 100000)", - // Belt - TGods - "[name] == warbelt && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 160 # [tier] == tierscore(item, 110000)", - // Boots - Gore Rider - "[name] == warboots && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 160 # [tier] == tierscore(item, 110000)", - // Armor - Fortitude - "[type] == armor && [flag] != ethereal && [flag] == runeword # [enhanceddefense] >= 200 && [enhanceddamage] >= 300 # [tier] == 110000", - // Final Shield - Exile - "[type] == auricshields && [flag] == runeword # [defianceaura] >= 13 # [tier] == 110000", - // Shield - HoZ - "[name] == gildedshield && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 150 # [tier] == tierscore(item, 100000)", - // Gloves - Laying of Hand's - "[name] == bramblemitts && [quality] == set && [flag] != ethereal # [ias] >= 20 # [tier] == 110000", - // Amulet - Highlords - "[type] == amulet && [quality] == unique # [lightresist] == 35 # [tier] == 100000", - // Final Rings - Perfect Raven Frost & Bul-Kathos' Wedding Band - "[type] == ring && [quality] == unique # [dexterity] == 20 && [tohit] == 250 # [tier] == 110000", - "[type] == ring && [quality] == unique # [maxstamina] == 50 && [lifeleech] == 5 # [tier] == 110000", - // Rings - Raven Frost && Bul-Kathos' Wedding Band - "[type] == ring && [quality] == unique # [dexterity] >= 15 && [tohit] >= 150 # [tier] == 100000", - "[type] == ring && [quality] == unique # [maxstamina] == 50 && [lifeleech] >= 3 # [tier] == 100000", - // Switch - CTA - "[minimumsockets] >= 5 && [flag] == runeword # [plusskillbattleorders] >= 1 # [secondarytier] == 100000", - // Merc Final Armor - Fortitude - "[type] == armor && [flag] == runeword # [enhanceddefense] >= 200 && [enhanceddamage] >= 300 # [merctier] == 100000", - // Merc Armor - Treachery - "[type] == armor && [flag] == runeword # [ias] == 45 && [coldresist] == 30 # [merctier] == 50000 + mercscore(item)", - // Merc Final Helmet - Eth Andy's - "[name] == demonhead && [quality] == unique && [flag] == ethereal # [strength] >= 25 && [enhanceddefense] >= 100 # [merctier] == 50000 + mercscore(item)", - // Merc Helmet - Andy's - "[name] == demonhead && [quality] == unique && [flag] != ethereal # [strength] >= 25 && [enhanceddefense] >= 100 # [merctier] == 40000 + mercscore(item)", - ], - charms: { - ResLife: { - max: 6, - have: [], - classid: sdk.items.SmallCharm, - stats: function (check) { - return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MaxHp) === 20); - } - }, +(function (module) { + module.exports = (function () { + const build = { + caster: false, + skillstab: sdk.skills.tabs.PalaCombat, + wantedskills: [sdk.skills.Zeal, sdk.skills.Fanaticism], + usefulskills: [sdk.skills.HolyShield, sdk.skills.ResistFire, sdk.skills.ResistLightning], + precastSkills: [sdk.skills.HolyShield], + wantedMerc: MercData[sdk.skills.HolyFreeze], + stats: [ + ["strength", 103], ["dexterity", 136], ["vitality", 300], + ["dexterity", "block"], ["vitality", "all"] + ], + skills: [ + [sdk.skills.Fanaticism, 20], + [sdk.skills.Sacrifice, 20], + [sdk.skills.Salvation, 1], + [sdk.skills.Redemption, 1], + [sdk.skills.Zeal, 10], + [sdk.skills.HolyShield, 15], // lvl 74 w/o quest skill pts + [sdk.skills.ResistLightning, 10, false], + [sdk.skills.ResistFire, 10, false], + [sdk.skills.ResistCold, 10, false], + ], - ResMf: { - max: 2, - have: [], - classid: sdk.items.SmallCharm, - stats: function (check) { - return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MagicBonus) === 7); - } - }, + charms: { + ResLife: { + max: 6, + have: [], + classid: sdk.items.SmallCharm, + stats: function (check) { + return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MaxHp) === 20); + } + }, - Skiller: { - max: 2, - have: [], - classid: sdk.items.GrandCharm, - stats: function (check) { - return (!check.unique && check.classid === this.classid && check.getStat(sdk.stats.AddSkillTab, sdk.skills.tabs.PalaCombat) === 1 - && check.getStat(sdk.stats.MaxHp) >= 40); - } - }, - }, + ResMf: { + max: 2, + have: [], + classid: sdk.items.SmallCharm, + stats: function (check) { + return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MagicBonus) === 7); + } + }, - AutoBuildTemplate: { - 1: { - Update: function () { - Config.AttackSkill = [-1, sdk.skills.Zeal, sdk.skills.Fanaticism, sdk.skills.Zeal, sdk.skills.Fanaticism, -1, -1]; - Config.LowManaSkill = [-1, -1]; - Config.BeltColumn = ["hp", "hp", "mp", "rv"]; - SetUp.belt(); - } - }, - }, + Skiller: { + max: 2, + have: [], + classid: sdk.items.GrandCharm, + stats: function (check) { + return (!check.unique && check.classid === this.classid && check.getStat(sdk.stats.AddSkillTab, sdk.skills.tabs.PalaCombat) === 1 + && check.getStat(sdk.stats.MaxHp) >= 40); + } + }, + }, - respec: function () { - if (me.classic) { - return me.charlvl >= 75 && me.diablo; - } else { - return me.checkItem({ name: sdk.locale.items.Grief, itemtype: sdk.items.type.Sword }).have; - } - }, + AutoBuildTemplate: { + 1: { + Update: function () { + Config.AttackSkill = [-1, sdk.skills.Zeal, sdk.skills.Fanaticism, sdk.skills.Zeal, sdk.skills.Fanaticism, -1, -1]; + Config.LowManaSkill = [-1, -1]; + Config.BeltColumn = ["hp", "hp", "mp", "rv"]; + SetUp.belt(); + } + }, + }, - active: function () { - return this.respec() && me.getSkill(sdk.skills.Fanaticism, sdk.skills.subindex.HardPoints) === 20; - }, -}; + respec: function () { + if (me.classic) { + return me.charlvl >= 75 && me.diablo; + } else { + return me.checkItem({ name: sdk.locale.items.Grief, itemtype: sdk.items.type.Sword }).have; + } + }, + + active: function () { + return this.respec() && me.getSkill(sdk.skills.Fanaticism, sdk.skills.subindex.HardPoints) === 20; + }, + }; + + // autoequip final gear + let finalGear = [ + // Weapon - Grief + "[type] == sword && [flag] == runeword # [ias] >= 30 && [itemdeadlystrike] == 20 && [passivepoispierce] >= 20 # [tier] == 100000", + // Final Helm - Upp'ed Vamp Gaze + "[name] == bonevisage && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 100 && [lifeleech] >= 6 # [tier] == tierscore(item, 100000)", + // Helm -Vamp Gaze + "[name] == grimhelm && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 100 # [tier] == tierscore(item, 100000)", + // Belt - TGods + "[name] == warbelt && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 160 # [tier] == tierscore(item, 110000)", + // Boots - Gore Rider + "[name] == warboots && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 160 # [tier] == tierscore(item, 110000)", + // Armor - Fortitude + "[type] == armor && [flag] != ethereal && [flag] == runeword # [enhanceddefense] >= 200 && [enhanceddamage] >= 300 # [tier] == 110000", + // Final Shield - Exile + "[type] == auricshields && [flag] == runeword # [defianceaura] >= 13 # [tier] == 110000", + // Shield - HoZ + "[name] == gildedshield && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 150 # [tier] == tierscore(item, 100000)", + // Gloves - Laying of Hand's + "[name] == bramblemitts && [quality] == set && [flag] != ethereal # [ias] >= 20 # [tier] == 110000", + // Amulet - Highlords + "[type] == amulet && [quality] == unique # [lightresist] == 35 # [tier] == 100000", + // Final Rings - Perfect Raven Frost & Bul-Kathos' Wedding Band + "[type] == ring && [quality] == unique # [dexterity] == 20 && [tohit] == 250 # [tier] == 110000", + "[type] == ring && [quality] == unique # [maxstamina] == 50 && [lifeleech] == 5 # [tier] == 110000", + // Rings - Raven Frost && Bul-Kathos' Wedding Band + "[type] == ring && [quality] == unique # [dexterity] >= 15 && [tohit] >= 150 # [tier] == 100000", + "[type] == ring && [quality] == unique # [maxstamina] == 50 && [lifeleech] >= 3 # [tier] == 100000", + // Switch - CTA + "[minimumsockets] >= 5 && [flag] == runeword # [plusskillbattleorders] >= 1 # [secondarytier] == 100000", + // Merc Final Armor - Fortitude + "[type] == armor && [flag] == runeword # [enhanceddefense] >= 200 && [enhanceddamage] >= 300 # [merctier] == 100000", + // Merc Armor - Treachery + "[type] == armor && [flag] == runeword # [ias] == 45 && [coldresist] == 30 # [merctier] == 50000 + mercscore(item)", + // Merc Final Helmet - Eth Andy's + "[name] == demonhead && [quality] == unique && [flag] == ethereal # [strength] >= 25 && [enhanceddefense] >= 100 # [merctier] == 50000 + mercscore(item)", + // Merc Helmet - Andy's + "[name] == demonhead && [quality] == unique && [flag] != ethereal # [strength] >= 25 && [enhanceddefense] >= 100 # [merctier] == 40000 + mercscore(item)", + ]; + + NTIP.buildList(finalGear); + NTIP.buildFinalGear(finalGear); + + return build; + })(); +})(module); diff --git a/libs/SoloPlay/BuildFiles/sorceress/sorceress.BlizzballerBuild.js b/libs/SoloPlay/BuildFiles/sorceress/sorceress.BlizzballerBuild.js index 117caae5..7be602f3 100644 --- a/libs/SoloPlay/BuildFiles/sorceress/sorceress.BlizzballerBuild.js +++ b/libs/SoloPlay/BuildFiles/sorceress/sorceress.BlizzballerBuild.js @@ -5,154 +5,164 @@ * */ -const finalBuild = { - caster: true, - skillstab: sdk.skills.tabs.Cold, - wantedskills: [sdk.skills.Blizzard, sdk.skills.FireBall, sdk.skills.ColdMastery], - usefulskills: [sdk.skills.GlacialSpike, sdk.skills.Meteor, sdk.skills.FireMastery, sdk.skills.StaticField], - precastSkills: [sdk.skills.FrozenArmor], - usefulStats: [sdk.stats.PassiveColdPierce, sdk.stats.PassiveColdMastery, sdk.stats.PassiveFireMastery, sdk.stats.PassiveFirePierce], - mercDiff: sdk.difficulty.Nightmare, - mercAct: 2, - mercAuraWanted: "Holy Freeze", - stats: [ - ["energy", 50], ["strength", 48], ["vitality", 165], - ["strength", 61], ["vitality", 252], ["strength", 127], - ["dexterity", "block"], ["vitality", "all"] - ], - skills: [ - [sdk.skills.Warmth, 1], - [sdk.skills.FrozenArmor, 1], - [sdk.skills.StaticField, 1], - [sdk.skills.Teleport, 1], - [sdk.skills.Meteor, 1], - [sdk.skills.FireMastery, 1, false], - [sdk.skills.ColdMastery, 1, false], - [sdk.skills.FireBall, 20], - [sdk.skills.Blizzard, 20], - [sdk.skills.GlacialSpike, 20], // lvl 75 w/o quest skills pts - [sdk.skills.Meteor, 20], - [sdk.skills.ColdMastery, 5], - [sdk.skills.FireBolt, 20], - ], - autoEquipTiers: [ // autoequip final gear - // Weapon - Tals Orb - "[name] == swirlingcrystal && [quality] == set && [flag] != ethereal # [skilllightningmastery]+[skillfiremastery]+[skillcoldmastery] >= 3 # [tier] == tierscore(item, 100000)", - // Helmet - Tals Mask - "[name] == deathmask && [quality] == set && [flag] != ethereal # [coldresist]+[lightresist]+[fireresist]+[poisonresist] >= 60 # [tier] == tierscore(item, 100000)", - // Belt - Tals Belt - "[name] == meshbelt && [quality] == set && [flag] != ethereal # [itemmagicbonus] >= 10 # [tier] == tierscore(item, 100000)", - // Final Boots - Sandstorm Treks - "[name] == scarabshellboots && [quality] == unique # [strength]+[vitality] >= 20 # [tier] == tierscore(item, 100000)", - // Boots - War Traveler - "[name] == battleboots && [quality] == unique && [flag] != ethereal # [itemmagicbonus] >= 50 # [tier] == tierscore(item, 5000)", - // Armor - Tals Armor - "[name] == lacqueredplate && [quality] == set # [coldresist] >= 1 # [tier] == 100000", - // Final Shield - Sanctuary - "[name] == hyperion && [flag] == runeword # [fhr] >= 20 && [enhanceddefense] >= 130 && [fireresist] >= 50 # [tier] == 100000", - // Shield - Mosers - "[name] == roundshield && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 180 # [tier] == tierscore(item, 50000)", - // Final Gloves - Perfect Upp'ed Magefist - "[name] == battlegauntlets && [quality] == unique && [flag] != ethereal # [enhanceddefense] == 30 && [addfireskills] == 1 && [defense] == 71 # [tier] == 110000", - // Gloves - Upp'ed Magefist - "[name] == battlegauntlets && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 20 && [addfireskills] == 1 # [tier] == tierscore(item, 100000)", - // Gloves - Magefist - "[name] == lightgauntlets && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 20 && [addfireskills] == 1 # [tier] == tierscore(item, 100000)", - // Amulet - Tals Ammy - "[name] == amulet && [quality] == set # [lightresist] == 33 # [tier] == 100000", - // Final Rings - Perfect Raven Frost & Nagelring - "[type] == ring && [quality] == unique # [dexterity] == 20 # [tier] == 100000", - "[type] == ring && [quality] == unique # [itemmagicbonus] == 30 # [tier] == 100000", - // Rings - Raven Frost - "[type] == ring && [quality] == unique # [dexterity] >= 15 # [tier] == 90000", - // Switch - CTA - "[minimumsockets] >= 5 && [flag] == runeword # [plusskillbattleorders] >= 1 # [secondarytier] == 100000", - // Switch Shield - 1+ all skill - "[type] == shield # [itemallskills] >= 1 # [secondarytier] == tierscore(item, 100000)", - // Merc Armor - Fortitude - "[type] == armor && [flag] == runeword # [enhanceddefense] >= 200 && [enhanceddamage] >= 300 # [merctier] == 100000", - // Merc Final Helmet - Eth Andy's - "[name] == demonhead && [quality] == unique && [flag] == ethereal # [strength] >= 25 && [enhanceddefense] >= 100 # [merctier] == 50000 + mercscore(item)", - // Merc Helmet - Andy's - "[name] == demonhead && [quality] == unique && [flag] != ethereal # [strength] >= 25 && [enhanceddefense] >= 100 # [merctier] == 40000 + mercscore(item)", - ], - charms: { - ResLife: { - max: 3, - have: [], - classid: sdk.items.SmallCharm, - stats: function (check) { - return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MaxHp) === 20); - } - }, +(function (module) { + module.exports = (function () { + const build = { + caster: true, + skillstab: sdk.skills.tabs.Cold, + wantedskills: [sdk.skills.Blizzard, sdk.skills.FireBall, sdk.skills.ColdMastery], + usefulskills: [sdk.skills.GlacialSpike, sdk.skills.Meteor, sdk.skills.FireMastery, sdk.skills.StaticField], + precastSkills: [sdk.skills.FrozenArmor], + usefulStats: [sdk.stats.PassiveColdPierce, sdk.stats.PassiveColdMastery, sdk.stats.PassiveFireMastery, sdk.stats.PassiveFirePierce], + wantedMerc: MercData[sdk.skills.HolyFreeze], + stats: [ + ["energy", 50], ["strength", 48], ["vitality", 165], + ["strength", 61], ["vitality", 252], ["strength", 127], + ["dexterity", "block"], ["vitality", "all"] + ], + skills: [ + [sdk.skills.Warmth, 1], + [sdk.skills.FrozenArmor, 1], + [sdk.skills.StaticField, 1], + [sdk.skills.Teleport, 1], + [sdk.skills.Meteor, 1], + [sdk.skills.FireMastery, 1, false], + [sdk.skills.ColdMastery, 1, false], + [sdk.skills.FireBall, 20], + [sdk.skills.Blizzard, 20], + [sdk.skills.GlacialSpike, 20], // lvl 75 w/o quest skills pts + [sdk.skills.Meteor, 20], + [sdk.skills.ColdMastery, 5], + [sdk.skills.FireBolt, 20], + ], - ResMf: { - max: 2, - have: [], - classid: sdk.items.SmallCharm, - stats: function (check) { - return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MagicBonus) === 7); - } - }, + charms: { + ResLife: { + max: 3, + have: [], + classid: sdk.items.SmallCharm, + stats: function (check) { + return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MaxHp) === 20); + } + }, - ResFHR: { - max: 3, - have: [], - classid: sdk.items.SmallCharm, - stats: function (check) { - return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.FHR) === 5); - } - }, + ResMf: { + max: 2, + have: [], + classid: sdk.items.SmallCharm, + stats: function (check) { + return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MagicBonus) === 7); + } + }, - SkillerFire: { - max: 1, - have: [], - classid: sdk.items.GrandCharm, - stats: function (check) { - return (!check.unique && check.classid === this.classid && check.getStat(sdk.stats.AddSkillTab, sdk.skills.tabs.Fire) === 1 - && check.getStat(sdk.stats.MaxHp) >= 40); - } - }, + ResFHR: { + max: 3, + have: [], + classid: sdk.items.SmallCharm, + stats: function (check) { + return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.FHR) === 5); + } + }, - SkillerCold: { - max: 1, - have: [], - classid: sdk.items.GrandCharm, - stats: function (check) { - return (!check.unique && check.classid === this.classid && check.getStat(sdk.stats.AddSkillTab, sdk.skills.tabs.Cold) === 1 - && check.getStat(sdk.stats.MaxHp) >= 40); - } - }, - }, + SkillerFire: { + max: 1, + have: [], + classid: sdk.items.GrandCharm, + stats: function (check) { + return (!check.unique && check.classid === this.classid && check.getStat(sdk.stats.AddSkillTab, sdk.skills.tabs.Fire) === 1 + && check.getStat(sdk.stats.MaxHp) >= 40); + } + }, - AutoBuildTemplate: { - 1: { - Update: function () { - Config.AttackSkill = [-1, sdk.skills.Blizzard, sdk.skills.FireBall, sdk.skills.Blizzard, sdk.skills.FireBall, sdk.skills.Meteor, sdk.skills.GlacialSpike]; - Config.LowManaSkill = [-1, -1]; - Config.SkipImmune = ["fire and cold"]; - Config.HPBuffer = me.expansion ? 1 : 5; - Config.MPBuffer = me.expansion ? 1 : 5; - } - }, - }, + SkillerCold: { + max: 1, + have: [], + classid: sdk.items.GrandCharm, + stats: function (check) { + return (!check.unique && check.classid === this.classid && check.getStat(sdk.stats.AddSkillTab, sdk.skills.tabs.Cold) === 1 + && check.getStat(sdk.stats.MaxHp) >= 40); + } + }, + }, - respec: function () { - if (me.classic) { - return me.charlvl >= 75 && me.diablo; - } else { - return me.haveAll([ - { name: sdk.locale.items.TalRashasBelt, quality: sdk.items.quality.Set }, - { name: sdk.locale.items.TalRashasAmulet, quality: sdk.items.quality.Set }, - { name: sdk.locale.items.TalRashasArmor, quality: sdk.items.quality.Set }, - { name: sdk.locale.items.TalRashasOrb, quality: sdk.items.quality.Set }, - { name: sdk.locale.items.TalRashasHelmet, quality: sdk.items.quality.Set }, - ]); - } - }, + AutoBuildTemplate: { + 1: { + Update: function () { + Config.AttackSkill = [-1, sdk.skills.Blizzard, sdk.skills.FireBall, sdk.skills.Blizzard, sdk.skills.FireBall, sdk.skills.Meteor, sdk.skills.GlacialSpike]; + Config.LowManaSkill = [-1, -1]; + Config.SkipImmune = ["fire and cold"]; + Config.HPBuffer = me.expansion ? 1 : 5; + Config.MPBuffer = me.expansion ? 1 : 5; + } + }, + }, - active: function () { - return this.respec() && me.getSkill(sdk.skills.FireBall, sdk.skills.subindex.HardPoints) === 20 && me.getSkill(sdk.skills.Blizzard, sdk.skills.subindex.HardPoints) === 20; - }, -}; + respec: function () { + if (me.classic) { + return me.charlvl >= 75 && me.diablo; + } else { + return me.haveAll([ + { name: sdk.locale.items.TalRashasBelt, quality: sdk.items.quality.Set }, + { name: sdk.locale.items.TalRashasAmulet, quality: sdk.items.quality.Set }, + { name: sdk.locale.items.TalRashasArmor, quality: sdk.items.quality.Set }, + { name: sdk.locale.items.TalRashasOrb, quality: sdk.items.quality.Set }, + { name: sdk.locale.items.TalRashasHelmet, quality: sdk.items.quality.Set }, + ]); + } + }, + + active: function () { + return this.respec() && me.getSkill(sdk.skills.FireBall, sdk.skills.subindex.HardPoints) === 20 && me.getSkill(sdk.skills.Blizzard, sdk.skills.subindex.HardPoints) === 20; + }, + }; + + // autoequip final gear + let finalGear = [ + // Weapon - Tals Orb + "[name] == swirlingcrystal && [quality] == set && [flag] != ethereal # [skilllightningmastery]+[skillfiremastery]+[skillcoldmastery] >= 3 # [tier] == tierscore(item, 100000)", + // Helmet - Tals Mask + "[name] == deathmask && [quality] == set && [flag] != ethereal # [coldresist]+[lightresist]+[fireresist]+[poisonresist] >= 60 # [tier] == tierscore(item, 100000)", + // Belt - Tals Belt + "[name] == meshbelt && [quality] == set && [flag] != ethereal # [itemmagicbonus] >= 10 # [tier] == tierscore(item, 100000)", + // Final Boots - Sandstorm Treks + "[name] == scarabshellboots && [quality] == unique # [strength]+[vitality] >= 20 # [tier] == tierscore(item, 100000)", + // Boots - War Traveler + "[name] == battleboots && [quality] == unique && [flag] != ethereal # [itemmagicbonus] >= 50 # [tier] == tierscore(item, 5000)", + // Armor - Tals Armor + "[name] == lacqueredplate && [quality] == set # [coldresist] >= 1 # [tier] == 100000", + // Final Shield - Sanctuary + "[name] == hyperion && [flag] == runeword # [fhr] >= 20 && [enhanceddefense] >= 130 && [fireresist] >= 50 # [tier] == 100000", + // Shield - Mosers + "[name] == roundshield && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 180 # [tier] == tierscore(item, 50000)", + // Final Gloves - Perfect Upp'ed Magefist + "[name] == battlegauntlets && [quality] == unique && [flag] != ethereal # [enhanceddefense] == 30 && [addfireskills] == 1 && [defense] == 71 # [tier] == 110000", + // Gloves - Upp'ed Magefist + "[name] == battlegauntlets && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 20 && [addfireskills] == 1 # [tier] == tierscore(item, 100000)", + // Gloves - Magefist + "[name] == lightgauntlets && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 20 && [addfireskills] == 1 # [tier] == tierscore(item, 100000)", + // Amulet - Tals Ammy + "[name] == amulet && [quality] == set # [lightresist] == 33 # [tier] == 100000", + // Final Rings - Perfect Raven Frost & Nagelring + "[type] == ring && [quality] == unique # [dexterity] == 20 # [tier] == 100000", + "[type] == ring && [quality] == unique # [itemmagicbonus] == 30 # [tier] == 100000", + // Rings - Raven Frost + "[type] == ring && [quality] == unique # [dexterity] >= 15 # [tier] == 90000", + // Switch - CTA + "[minimumsockets] >= 5 && [flag] == runeword # [plusskillbattleorders] >= 1 # [secondarytier] == 100000", + // Switch Shield - 1+ all skill + "[type] == shield # [itemallskills] >= 1 # [secondarytier] == tierscore(item, 100000)", + // Merc Armor - Fortitude + "[type] == armor && [flag] == runeword # [enhanceddefense] >= 200 && [enhanceddamage] >= 300 # [merctier] == 100000", + // Merc Final Helmet - Eth Andy's + "[name] == demonhead && [quality] == unique && [flag] == ethereal # [strength] >= 25 && [enhanceddefense] >= 100 # [merctier] == 50000 + mercscore(item)", + // Merc Helmet - Andy's + "[name] == demonhead && [quality] == unique && [flag] != ethereal # [strength] >= 25 && [enhanceddefense] >= 100 # [merctier] == 40000 + mercscore(item)", + ]; + + NTIP.buildList(finalGear); + NTIP.buildFinalGear(finalGear); + + return build; + })(); +})(module); diff --git a/libs/SoloPlay/BuildFiles/sorceress/sorceress.BlovaBuild.js b/libs/SoloPlay/BuildFiles/sorceress/sorceress.BlovaBuild.js index c984841f..1f15e828 100644 --- a/libs/SoloPlay/BuildFiles/sorceress/sorceress.BlovaBuild.js +++ b/libs/SoloPlay/BuildFiles/sorceress/sorceress.BlovaBuild.js @@ -5,144 +5,154 @@ * */ -const finalBuild = { - caster: true, - skillstab: sdk.skills.tabs.Cold, - wantedskills: [sdk.skills.Blizzard, sdk.skills.Nova], - usefulskills: [sdk.skills.LightningMastery, sdk.skills.ColdMastery, sdk.skills.GlacialSpike], - precastSkills: [sdk.skills.FrozenArmor], - usefulStats: [sdk.stats.PassiveColdPierce, sdk.stats.PassiveColdMastery, sdk.stats.PassiveLightningMastery, sdk.stats.PassiveLightningPierce], - mercDiff: sdk.difficulty.Nightmare, - mercAct: 2, - mercAuraWanted: "Holy Freeze", - stats: [ - ["strength", 156], ["dexterity", 35], ["vitality", "all"] - ], - skills: [ - [sdk.skills.Warmth, 1], - [sdk.skills.FrozenArmor, 1], - [sdk.skills.Teleport, 1], - [sdk.skills.Blizzard, 1], - [sdk.skills.Nova, 20], - [sdk.skills.LightningMastery, 20], - [sdk.skills.Blizzard, 20], - [sdk.skills.ColdMastery, 5], - [sdk.skills.IceBlast, 20], - [sdk.skills.GlacialSpike, 5], - [sdk.skills.IceBolt, 14], - ], - autoEquipTiers: [ // autoequip final gear - // Weapon - HotO - "[type] == mace && [flag] == runeword # [itemallskills] == 3 # [tier] == 100000", - // Helmet - Griffons - "[name] == diadem && [quality] == unique && [flag] != ethereal # [fcr] == 25 # [tier] == tierscore(item, 100000)", - // Belt - Arach's - "[name] == spiderwebsash && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 90 # [tier] == tierscore(item, 100000)", - // Final Boots - Sandstorm Treks - "[name] == scarabshellboots && [quality] == unique # [strength]+[vitality] >= 20 # [tier] == tierscore(item, 100000)", - // Boots - War Traveler - "[name] == battleboots && [quality] == unique && [flag] != ethereal # [itemmagicbonus] >= 50 # [tier] == tierscore(item, 5000)", - // Armor - CoH - "[type] == armor && [flag] == runeword && [flag] != ethereal # [fireresist] == 65 && [hpregen] == 7 # [tier] == 100000", - // Shield - Spirit - "[type] == shield # [fcr] >= 25 && [maxmana] >= 89 # [tier] == tierscore(item, 100000)", - // Final Gloves - Perfect Upp'ed Magefist - "[name] == battlegauntlets && [quality] == unique && [flag] != ethereal # [enhanceddefense] == 30 && [addfireskills] == 1 && [defense] == 71 # [tier] == 110000", - // Gloves - Upp'ed Magefist - "[name] == battlegauntlets && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 20 && [addfireskills] == 1 # [tier] == tierscore(item, 100000)", - // Gloves - Magefist - "[name] == lightgauntlets && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 20 && [addfireskills] == 1 # [tier] == tierscore(item, 100000)", - // Amulet - Maras - "[type] == amulet && [quality] == unique # [strength] == 5 && [coldresist] >= 30 # [tier] == tierscore(item, 100000)", - // Final Rings - SoJ & Perfect Bul-Kathos' Wedding Band - "[type] == ring && [quality] == unique # [itemmaxmanapercent] == 25 # [tier] == 100000", - "[name] == ring && [quality] == unique # [maxstamina] == 50 && [lifeleech] == 5 # [tier] == 100000", - // Ring - Bul-Kathos' Wedding Band - "[name] == ring && [quality] == unique # [maxstamina] == 50 && [lifeleech] >= 3 # [tier] == 90000", - // Switch - CTA - "[minimumsockets] >= 5 && [flag] == runeword # [plusskillbattleorders] >= 1 # [secondarytier] == 100000", - // Switch Final Shield - Spirit - "[name] == monarch && [flag] == runeword # [fcr] >= 25 && [maxmana] >= 89 # [secondarytier] == 110000", - // Switch Temporary Shield - 1+ all skill - "[type] == shield # [itemallskills] >= 1 # [secondarytier] == tierscore(item, 50000)", - // Merc Armor - Fortitude - "[type] == armor && [flag] == runeword # [enhanceddefense] >= 200 && [enhanceddamage] >= 300 # [merctier] == 100000", - // Merc Final Helmet - Eth Andy's - "[name] == demonhead && [quality] == unique && [flag] == ethereal # [strength] >= 25 && [enhanceddefense] >= 100 # [merctier] == 50000 + mercscore(item)", - // Merc Helmet - Andy's - "[name] == demonhead && [quality] == unique && [flag] != ethereal # [strength] >= 25 && [enhanceddefense] >= 100 # [merctier] == 40000 + mercscore(item)", - ], - charms: { - ResLife: { - max: 3, - have: [], - classid: sdk.items.SmallCharm, - stats: function (check) { - return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MaxHp) === 20); - } - }, +(function (module) { + module.exports = (function () { + const build = { + caster: true, + skillstab: sdk.skills.tabs.Cold, + wantedskills: [sdk.skills.Blizzard, sdk.skills.Nova], + usefulskills: [sdk.skills.LightningMastery, sdk.skills.ColdMastery, sdk.skills.GlacialSpike], + precastSkills: [sdk.skills.FrozenArmor], + usefulStats: [sdk.stats.PassiveColdPierce, sdk.stats.PassiveColdMastery, sdk.stats.PassiveLightningMastery, sdk.stats.PassiveLightningPierce], + wantedMerc: MercData[sdk.skills.HolyFreeze], + stats: [ + ["strength", 156], ["dexterity", 35], ["vitality", "all"] + ], + skills: [ + [sdk.skills.Warmth, 1], + [sdk.skills.FrozenArmor, 1], + [sdk.skills.Teleport, 1], + [sdk.skills.Blizzard, 1], + [sdk.skills.Nova, 20], + [sdk.skills.LightningMastery, 20], + [sdk.skills.Blizzard, 20], + [sdk.skills.ColdMastery, 5], + [sdk.skills.IceBlast, 20], + [sdk.skills.GlacialSpike, 5], + [sdk.skills.IceBolt, 14], + ], - ResMf: { - max: 2, - have: [], - classid: sdk.items.SmallCharm, - stats: function (check) { - return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MagicBonus) === 7); - } - }, + charms: { + ResLife: { + max: 3, + have: [], + classid: sdk.items.SmallCharm, + stats: function (check) { + return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MaxHp) === 20); + } + }, - ResFHR: { - max: 3, - have: [], - classid: sdk.items.SmallCharm, - stats: function (check) { - return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.FHR) === 5); - } - }, + ResMf: { + max: 2, + have: [], + classid: sdk.items.SmallCharm, + stats: function (check) { + return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MagicBonus) === 7); + } + }, - SkillerLight: { - max: 1, - have: [], - classid: sdk.items.GrandCharm, - stats: function (check) { - return (!check.unique && check.classid === this.classid && check.getStat(sdk.stats.AddSkillTab, sdk.skills.tabs.Lightning) === 1 - && check.getStat(sdk.stats.MaxHp) >= 40); - } - }, + ResFHR: { + max: 3, + have: [], + classid: sdk.items.SmallCharm, + stats: function (check) { + return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.FHR) === 5); + } + }, - SkillerCold: { - max: 1, - have: [], - classid: sdk.items.GrandCharm, - stats: function (check) { - return (!check.unique && check.classid === this.classid && check.getStat(sdk.stats.AddSkillTab, sdk.skills.tabs.Cold) === 1 - && check.getStat(sdk.stats.MaxHp) >= 40); - } - }, - }, + SkillerLight: { + max: 1, + have: [], + classid: sdk.items.GrandCharm, + stats: function (check) { + return (!check.unique && check.classid === this.classid && check.getStat(sdk.stats.AddSkillTab, sdk.skills.tabs.Lightning) === 1 + && check.getStat(sdk.stats.MaxHp) >= 40); + } + }, - AutoBuildTemplate: { - 1: { - Update: function () { - Config.AttackSkill = [-1, sdk.skills.Blizzard, sdk.skills.Nova, sdk.skills.Blizzard, sdk.skills.Nova, -1, sdk.skills.IceBlast]; - Config.LowManaSkill = [-1, -1]; - Config.SkipImmune = ["lightning and cold"]; - Config.HPBuffer = me.expansion ? 1 : 5; - Config.MPBuffer = me.expansion ? 1 : 5; - } - }, - }, + SkillerCold: { + max: 1, + have: [], + classid: sdk.items.GrandCharm, + stats: function (check) { + return (!check.unique && check.classid === this.classid && check.getStat(sdk.stats.AddSkillTab, sdk.skills.tabs.Cold) === 1 + && check.getStat(sdk.stats.MaxHp) >= 40); + } + }, + }, - respec: function () { - if (me.classic) { - return me.charlvl >= 75 && me.diablo; - } else { - return (Attack.checkInfinity() || (myData.merc.gear.includes(sdk.locale.items.Infinity) && !Misc.poll(() => me.getMerc(), 200, 50))); - } - }, + AutoBuildTemplate: { + 1: { + Update: function () { + Config.AttackSkill = [-1, sdk.skills.Blizzard, sdk.skills.Nova, sdk.skills.Blizzard, sdk.skills.Nova, -1, sdk.skills.IceBlast]; + Config.LowManaSkill = [-1, -1]; + Config.SkipImmune = ["lightning and cold"]; + Config.HPBuffer = me.expansion ? 1 : 5; + Config.MPBuffer = me.expansion ? 1 : 5; + } + }, + }, - active: function () { - return this.respec() && me.getSkill(sdk.skills.Nova, sdk.skills.subindex.HardPoints) === 20 && me.getSkill(sdk.skills.Blizzard, sdk.skills.subindex.HardPoints) >= 1; - }, -}; + respec: function () { + if (me.classic) { + return me.charlvl >= 75 && me.diablo; + } else { + return (Attack.checkInfinity() || (myData.merc.gear.includes(sdk.locale.items.Infinity) && !Misc.poll(() => me.getMerc(), 200, 50))); + } + }, + + active: function () { + return this.respec() && me.getSkill(sdk.skills.Nova, sdk.skills.subindex.HardPoints) === 20 && me.getSkill(sdk.skills.Blizzard, sdk.skills.subindex.HardPoints) >= 1; + }, + }; + + // autoequip final gear + let finalGear = [ + // Weapon - HotO + "[type] == mace && [flag] == runeword # [itemallskills] == 3 # [tier] == 100000", + // Helmet - Griffons + "[name] == diadem && [quality] == unique && [flag] != ethereal # [fcr] == 25 # [tier] == tierscore(item, 100000)", + // Belt - Arach's + "[name] == spiderwebsash && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 90 # [tier] == tierscore(item, 100000)", + // Final Boots - Sandstorm Treks + "[name] == scarabshellboots && [quality] == unique # [strength]+[vitality] >= 20 # [tier] == tierscore(item, 100000)", + // Boots - War Traveler + "[name] == battleboots && [quality] == unique && [flag] != ethereal # [itemmagicbonus] >= 50 # [tier] == tierscore(item, 5000)", + // Armor - CoH + "[type] == armor && [flag] == runeword && [flag] != ethereal # [fireresist] == 65 && [hpregen] == 7 # [tier] == 100000", + // Shield - Spirit + "[type] == shield # [fcr] >= 25 && [maxmana] >= 89 # [tier] == tierscore(item, 100000)", + // Final Gloves - Perfect Upp'ed Magefist + "[name] == battlegauntlets && [quality] == unique && [flag] != ethereal # [enhanceddefense] == 30 && [addfireskills] == 1 && [defense] == 71 # [tier] == 110000", + // Gloves - Upp'ed Magefist + "[name] == battlegauntlets && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 20 && [addfireskills] == 1 # [tier] == tierscore(item, 100000)", + // Gloves - Magefist + "[name] == lightgauntlets && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 20 && [addfireskills] == 1 # [tier] == tierscore(item, 100000)", + // Amulet - Maras + "[type] == amulet && [quality] == unique # [strength] == 5 && [coldresist] >= 30 # [tier] == tierscore(item, 100000)", + // Final Rings - SoJ & Perfect Bul-Kathos' Wedding Band + "[type] == ring && [quality] == unique # [itemmaxmanapercent] == 25 # [tier] == 100000", + "[name] == ring && [quality] == unique # [maxstamina] == 50 && [lifeleech] == 5 # [tier] == 100000", + // Ring - Bul-Kathos' Wedding Band + "[name] == ring && [quality] == unique # [maxstamina] == 50 && [lifeleech] >= 3 # [tier] == 90000", + // Switch - CTA + "[minimumsockets] >= 5 && [flag] == runeword # [plusskillbattleorders] >= 1 # [secondarytier] == 100000", + // Switch Final Shield - Spirit + "[name] == monarch && [flag] == runeword # [fcr] >= 25 && [maxmana] >= 89 # [secondarytier] == 110000", + // Switch Temporary Shield - 1+ all skill + "[type] == shield # [itemallskills] >= 1 # [secondarytier] == tierscore(item, 50000)", + // Merc Armor - Fortitude + "[type] == armor && [flag] == runeword # [enhanceddefense] >= 200 && [enhanceddamage] >= 300 # [merctier] == 100000", + // Merc Final Helmet - Eth Andy's + "[name] == demonhead && [quality] == unique && [flag] == ethereal # [strength] >= 25 && [enhanceddefense] >= 100 # [merctier] == 50000 + mercscore(item)", + // Merc Helmet - Andy's + "[name] == demonhead && [quality] == unique && [flag] != ethereal # [strength] >= 25 && [enhanceddefense] >= 100 # [merctier] == 40000 + mercscore(item)", + ]; + + NTIP.buildList(finalGear); + NTIP.buildFinalGear(finalGear); + + return build; + })(); +})(module); diff --git a/libs/SoloPlay/BuildFiles/sorceress/sorceress.ColdBuild.js b/libs/SoloPlay/BuildFiles/sorceress/sorceress.ColdBuild.js index 528c7410..a8ba1899 100644 --- a/libs/SoloPlay/BuildFiles/sorceress/sorceress.ColdBuild.js +++ b/libs/SoloPlay/BuildFiles/sorceress/sorceress.ColdBuild.js @@ -5,140 +5,149 @@ * */ -const finalBuild = { - caster: true, - skillstab: sdk.skills.tabs.Cold, - wantedskills: [sdk.skills.Blizzard, sdk.skills.ColdMastery], - usefulskills: [sdk.skills.GlacialSpike, sdk.skills.IceBlast, sdk.skills.StaticField], - precastSkills: [sdk.skills.FrozenArmor], - usefulStats: [sdk.stats.PassiveColdPierce, sdk.stats.PassiveColdMastery], - mercDiff: sdk.difficulty.Nightmare, - mercAct: 2, - mercAuraWanted: "Holy Freeze", - stats: [ - ["strength", 48], ["vitality", 165], ["strength", 61], - ["vitality", 252], ["strength", 127], ["dexterity", "block"], ["vitality", "all"] - ], - skills: [ - [sdk.skills.Warmth, 1], - [sdk.skills.StaticField, 1], - [sdk.skills.Teleport, 1], - [sdk.skills.FrozenArmor, 1], - [sdk.skills.Blizzard, 20], - [sdk.skills.ColdMastery, 17], - [sdk.skills.IceBlast, 20], // lvl 66 w/o quest skills pts - [sdk.skills.GlacialSpike, 20], - [sdk.skills.IceBolt, 20], - [sdk.skills.ColdMastery, 20] - ], - autoEquipTiers: [ // autoequip final gear - // Weapon - Tals Orb - "[name] == swirlingcrystal && [quality] == set && [flag] != ethereal # [skilllightningmastery]+[skillfiremastery]+[skillcoldmastery] >= 3 # [tier] == tierscore(item, 100000)", - // Helmet - Tals Mask - "[name] == deathmask && [quality] == set && [flag] != ethereal # [coldresist]+[lightresist]+[fireresist]+[poisonresist] >= 60 # [tier] == tierscore(item, 100000)", - // Belt - Tals Belt - "[name] == meshbelt && [quality] == set && [flag] != ethereal # [itemmagicbonus] >= 10 # [tier] == tierscore(item, 100000)", - // Final Boots - Sandstorm Treks - "[name] == scarabshellboots && [quality] == unique # [strength]+[vitality] >= 20 # [tier] == tierscore(item, 100000)", - // Boots - War Traveler - "[name] == battleboots && [quality] == unique && [flag] != ethereal # [itemmagicbonus] >= 50 # [tier] == tierscore(item, 5000)", - // Armor - Tals Armor - "[name] == lacqueredplate && [quality] == set # [coldresist] >= 1 # [tier] == 100000", - // Final Shield - Sanctuary - "[name] == hyperion && [flag] == runeword # [fhr] >= 20 && [enhanceddefense] >= 130 && [fireresist] >= 50 # [tier] == 100000", - // Shield - Mosers - "[name] == roundshield && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 180 # [tier] == tierscore(item, 50000)", - // Final Gloves - Perfect Upp'ed Magefist - "[name] == battlegauntlets && [quality] == unique && [flag] != ethereal # [enhanceddefense] == 30 && [addfireskills] == 1 && [defense] == 71 # [tier] == 110000", - // Gloves - Upp'ed Magefist - "[name] == battlegauntlets && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 20 && [addfireskills] == 1 # [tier] == tierscore(item, 100000)", - // Gloves - Magefist - "[name] == lightgauntlets && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 20 && [addfireskills] == 1 # [tier] == tierscore(item, 100000)", - // Amulet - Tals Ammy - "[name] == amulet && [quality] == set # [lightresist] == 33 # [tier] == 100000", - // Final Rings - Perfect Raven Frost & Nagelring - "[type] == ring && [quality] == unique # [dexterity] == 20 # [tier] == 100000", - "[type] == ring && [quality] == unique # [itemmagicbonus] == 30 # [tier] == 100000", - // Rings - Raven Frost - "[type] == ring && [quality] == unique # [dexterity] >= 15 # [tier] == 90000", - // Switch - CTA - "[minimumsockets] >= 5 && [flag] == runeword # [plusskillbattleorders] >= 1 # [secondarytier] == 100000", - // Switch Shield - 1+ all skill - "[type] == shield # [itemallskills] >= 1 # [secondarytier] == tierscore(item, 100000)", - // Merc Armor - Fortitude - "[type] == armor && [flag] == runeword # [enhanceddefense] >= 200 && [enhanceddamage] >= 300 # [merctier] == 100000", - // Merc Final Helmet - Eth Andy's - "[name] == demonhead && [quality] == unique && [flag] == ethereal # [strength] >= 25 && [enhanceddefense] >= 100 # [merctier] == 50000 + mercscore(item)", - // Merc Helmet - Andy's - "[name] == demonhead && [quality] == unique && [flag] != ethereal # [strength] >= 25 && [enhanceddefense] >= 100 # [merctier] == 40000 + mercscore(item)", - ], - charms: { - ResLife: { - max: 3, - have: [], - classid: sdk.items.SmallCharm, - stats: function (check) { - return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MaxHp) === 20); - } - }, +(function (module) { + module.exports = (function () { + const build = { + caster: true, + skillstab: sdk.skills.tabs.Cold, + wantedskills: [sdk.skills.Blizzard, sdk.skills.ColdMastery], + usefulskills: [sdk.skills.GlacialSpike, sdk.skills.IceBlast, sdk.skills.StaticField], + precastSkills: [sdk.skills.FrozenArmor], + usefulStats: [sdk.stats.PassiveColdPierce, sdk.stats.PassiveColdMastery], + wantedMerc: MercData[sdk.skills.HolyFreeze], + stats: [ + ["strength", 48], ["vitality", 165], ["strength", 61], + ["vitality", 252], ["strength", 127], ["dexterity", "block"], ["vitality", "all"] + ], + skills: [ + [sdk.skills.Warmth, 1], + [sdk.skills.StaticField, 1], + [sdk.skills.Teleport, 1], + [sdk.skills.FrozenArmor, 1], + [sdk.skills.Blizzard, 20], + [sdk.skills.ColdMastery, 17], + [sdk.skills.IceBlast, 20], // lvl 66 w/o quest skills pts + [sdk.skills.GlacialSpike, 20], + [sdk.skills.IceBolt, 20], + [sdk.skills.ColdMastery, 20] + ], + + charms: { + ResLife: { + max: 3, + have: [], + classid: sdk.items.SmallCharm, + stats: function (check) { + return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MaxHp) === 20); + } + }, - ResMf: { - max: 3, - have: [], - classid: sdk.items.SmallCharm, - stats: function (check) { - return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MagicBonus) === 7); - } - }, + ResMf: { + max: 3, + have: [], + classid: sdk.items.SmallCharm, + stats: function (check) { + return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MagicBonus) === 7); + } + }, - ResFHR: { - max: 1, - have: [], - classid: sdk.items.SmallCharm, - stats: function (check) { - return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.FHR) === 5); - } - }, + ResFHR: { + max: 1, + have: [], + classid: sdk.items.SmallCharm, + stats: function (check) { + return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.FHR) === 5); + } + }, - Skiller: { - max: 2, - have: [], - classid: sdk.items.GrandCharm, - stats: function (check) { - return (!check.unique && check.classid === this.classid && check.getStat(sdk.stats.AddSkillTab, sdk.skills.tabs.Cold) === 1 - && check.getStat(sdk.stats.MaxHp) >= 40); - } - }, - }, + Skiller: { + max: 2, + have: [], + classid: sdk.items.GrandCharm, + stats: function (check) { + return (!check.unique && check.classid === this.classid && check.getStat(sdk.stats.AddSkillTab, sdk.skills.tabs.Cold) === 1 + && check.getStat(sdk.stats.MaxHp) >= 40); + } + }, + }, - AutoBuildTemplate: { - 1: { - Update: function () { - Config.AttackSkill = [-1, sdk.skills.Blizzard, sdk.skills.IceBlast, sdk.skills.Blizzard, sdk.skills.GlacialSpike, -1, -1]; - Config.LowManaSkill = [-1, -1]; - Config.SkipImmune = ["cold"]; - Config.HPBuffer = me.expansion ? 1 : 5; - Config.MPBuffer = me.expansion ? 1 : 5; - } - }, - }, + AutoBuildTemplate: { + 1: { + Update: function () { + Config.AttackSkill = [-1, sdk.skills.Blizzard, sdk.skills.IceBlast, sdk.skills.Blizzard, sdk.skills.GlacialSpike, -1, -1]; + Config.LowManaSkill = [-1, -1]; + Config.SkipImmune = ["cold"]; + Config.HPBuffer = me.expansion ? 1 : 5; + Config.MPBuffer = me.expansion ? 1 : 5; + } + }, + }, - respec: function () { - if (me.classic) { - return me.charlvl >= 75 && me.diablo; - } else { - return me.haveAll([ - { name: sdk.locale.items.TalRashasBelt, quality: sdk.items.quality.Set }, - { name: sdk.locale.items.TalRashasAmulet, quality: sdk.items.quality.Set }, - { name: sdk.locale.items.TalRashasArmor, quality: sdk.items.quality.Set }, - { name: sdk.locale.items.TalRashasOrb, quality: sdk.items.quality.Set }, - { name: sdk.locale.items.TalRashasHelmet, quality: sdk.items.quality.Set }, - ]) && me.hell && me.baal; - } - }, + respec: function () { + if (me.classic) { + return me.charlvl >= 75 && me.diablo; + } else { + return me.haveAll([ + { name: sdk.locale.items.TalRashasBelt, quality: sdk.items.quality.Set }, + { name: sdk.locale.items.TalRashasAmulet, quality: sdk.items.quality.Set }, + { name: sdk.locale.items.TalRashasArmor, quality: sdk.items.quality.Set }, + { name: sdk.locale.items.TalRashasOrb, quality: sdk.items.quality.Set }, + { name: sdk.locale.items.TalRashasHelmet, quality: sdk.items.quality.Set }, + ]) && me.hell && me.baal; + } + }, - active: function () { - return this.respec() && !me.checkSkill(sdk.skills.Meteor, sdk.skills.subindex.HardPoints); - }, -}; + active: function () { + return this.respec() && !me.checkSkill(sdk.skills.Meteor, sdk.skills.subindex.HardPoints); + }, + }; + + let finalGear = [ // autoequip final gear + // Weapon - Tals Orb + "[name] == swirlingcrystal && [quality] == set && [flag] != ethereal # [skilllightningmastery]+[skillfiremastery]+[skillcoldmastery] >= 3 # [tier] == tierscore(item, 100000)", + // Helmet - Tals Mask + "[name] == deathmask && [quality] == set && [flag] != ethereal # [coldresist]+[lightresist]+[fireresist]+[poisonresist] >= 60 # [tier] == tierscore(item, 100000)", + // Belt - Tals Belt + "[name] == meshbelt && [quality] == set && [flag] != ethereal # [itemmagicbonus] >= 10 # [tier] == tierscore(item, 100000)", + // Final Boots - Sandstorm Treks + "[name] == scarabshellboots && [quality] == unique # [strength]+[vitality] >= 20 # [tier] == tierscore(item, 100000)", + // Boots - War Traveler + "[name] == battleboots && [quality] == unique && [flag] != ethereal # [itemmagicbonus] >= 50 # [tier] == tierscore(item, 5000)", + // Armor - Tals Armor + "[name] == lacqueredplate && [quality] == set # [coldresist] >= 1 # [tier] == 100000", + // Final Shield - Sanctuary + "[name] == hyperion && [flag] == runeword # [fhr] >= 20 && [enhanceddefense] >= 130 && [fireresist] >= 50 # [tier] == 100000", + // Shield - Mosers + "[name] == roundshield && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 180 # [tier] == tierscore(item, 50000)", + // Final Gloves - Perfect Upp'ed Magefist + "[name] == battlegauntlets && [quality] == unique && [flag] != ethereal # [enhanceddefense] == 30 && [addfireskills] == 1 && [defense] == 71 # [tier] == 110000", + // Gloves - Upp'ed Magefist + "[name] == battlegauntlets && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 20 && [addfireskills] == 1 # [tier] == tierscore(item, 100000)", + // Gloves - Magefist + "[name] == lightgauntlets && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 20 && [addfireskills] == 1 # [tier] == tierscore(item, 100000)", + // Amulet - Tals Ammy + "[name] == amulet && [quality] == set # [lightresist] == 33 # [tier] == 100000", + // Final Rings - Perfect Raven Frost & Nagelring + "[type] == ring && [quality] == unique # [dexterity] == 20 # [tier] == 100000", + "[type] == ring && [quality] == unique # [itemmagicbonus] == 30 # [tier] == 100000", + // Rings - Raven Frost + "[type] == ring && [quality] == unique # [dexterity] >= 15 # [tier] == 90000", + // Switch - CTA + "[minimumsockets] >= 5 && [flag] == runeword # [plusskillbattleorders] >= 1 # [secondarytier] == 100000", + // Switch Shield - 1+ all skill + "[type] == shield # [itemallskills] >= 1 # [secondarytier] == tierscore(item, 100000)", + // Merc Armor - Fortitude + "[type] == armor && [flag] == runeword # [enhanceddefense] >= 200 && [enhanceddamage] >= 300 # [merctier] == 100000", + // Merc Final Helmet - Eth Andy's + "[name] == demonhead && [quality] == unique && [flag] == ethereal # [strength] >= 25 && [enhanceddefense] >= 100 # [merctier] == 50000 + mercscore(item)", + // Merc Helmet - Andy's + "[name] == demonhead && [quality] == unique && [flag] != ethereal # [strength] >= 25 && [enhanceddefense] >= 100 # [merctier] == 40000 + mercscore(item)", + ]; + + NTIP.buildList(finalGear); + NTIP.buildFinalGear(finalGear); + + return build; + })(); +})(module); diff --git a/libs/SoloPlay/BuildFiles/sorceress/sorceress.EsorbBuild.js b/libs/SoloPlay/BuildFiles/sorceress/sorceress.EsorbBuild.js index 6a1f1b65..7907301b 100644 --- a/libs/SoloPlay/BuildFiles/sorceress/sorceress.EsorbBuild.js +++ b/libs/SoloPlay/BuildFiles/sorceress/sorceress.EsorbBuild.js @@ -5,177 +5,184 @@ * */ -const finalBuild = { - caster: true, - skillstab: sdk.skills.tabs.Lightning, - wantedskills: [sdk.skills.FrozenOrb, sdk.skills.ColdMastery], - usefulskills: [sdk.skills.Telekinesis, sdk.skills.EnergyShield, sdk.skills.StaticField], - precastSkills: [sdk.skills.FrozenArmor], - mercDiff: sdk.difficulty.Nightmare, - mercAct: 2, - mercAuraWanted: "Holy Freeze", - classicStats: [ - ["dexterity", 51], ["strength", 80], ["energy", 100], ["vitality", 125], - ["energy", 150], ["vitality", 150], ["energy", "all"] - ], - expansionStats: [ - ["strength", 48], ["vitality", 165], ["strength", 61], - ["vitality", 252], ["strength", 127], ["dexterity", "block"], ["vitality", "all"] - ], - skills: [ - [sdk.skills.Warmth, 1], - [sdk.skills.FrozenArmor, 1], - [sdk.skills.StaticField, 1], - [sdk.skills.Teleport, 10], - [sdk.skills.EnergyShield, 8], - [sdk.skills.Telekinesis, 20], - [sdk.skills.FrozenOrb, 20], - [sdk.skills.ColdMastery, 17], - [sdk.skills.EnergyShield, 10], - [sdk.skills.StaticField, 20], - ], - classicTiers: [ - // Weapon - Spectral Shard - "[name] == blade && [quality] == unique # [fcr] == 20 && [allres] == 10 # [tier] == 100000", - // Helm - Tarnhelm - "[name] == skullcap && [quality] == unique # [itemallskills] == 1 && [itemmagicbonus] >= 25 # [tier] == tierscore(item, 100000)", - // Shield - "[type] == shield && [quality] >= magic # [sorceressskills] == 2 && [allres] >= 16 # [tier] == tierscore(item, 100000)", - // Rings - SoJ - "[type] == ring && [quality] == unique # [itemmaxmanapercent] == 25 # [tier] == 100000", - // Amulet - "[type] == amulet && [quality] >= magic # [sorceressskills] == 2 && [fcr] == 10 # [tier] == tierscore(item, 100000)", - // Boots - "[type] == boots && [quality] >= magic # [frw] >= 20 && [fhr] == 10 && [coldresist]+[lightresist] >= 10 # [tier] == tierscore(item, 100000)", - // Belt - "[type] == belt && [quality] >= magic # [fhr] >= 20 && [maxhp] >= 40 && [fireresist]+[lightresist] >= 20 # [tier] == tierscore(item, 100000)", - // Gloves - Magefist - "[name] == lightgauntlets && [quality] == unique # [fcr] >= 20 && [addfireskills] == 1 # [tier] == tierscore(item, 100000)", - ], - expansionTiers: [ - // Weapon - Tals Orb - "[name] == swirlingcrystal && [quality] == set && [flag] != ethereal # [skilllightningmastery]+[skillfiremastery]+[skillcoldmastery] >= 3 # [tier] == tierscore(item, 100000)", - // Helmet - Tals Mask - "[name] == deathmask && [quality] == set && [flag] != ethereal # [coldresist]+[lightresist]+[fireresist]+[poisonresist] >= 60 # [tier] == tierscore(item, 100000)", - // Belt - Tals Belt - "[name] == meshbelt && [quality] == set && [flag] != ethereal # [itemmagicbonus] >= 10 # [tier] == tierscore(item, 100000)", - // Final Boots - Sandstorm Treks - "[name] == scarabshellboots && [quality] == unique # [strength]+[vitality] >= 20 # [tier] == tierscore(item, 100000)", - // Boots - War Traveler - "[name] == battleboots && [quality] == unique && [flag] != ethereal # [itemmagicbonus] >= 50 # [tier] == tierscore(item, 5000)", - // Armor - Tals Armor - "[name] == lacqueredplate && [quality] == set # [coldresist] >= 1 # [tier] == 100000", - // Final Shield - Sanctuary - "[name] == hyperion && [flag] == runeword # [fhr] >= 20 && [enhanceddefense] >= 130 && [fireresist] >= 50 # [tier] == 100000", - // Shield - Mosers - "[name] == roundshield && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 180 # [tier] == tierscore(item, 50000)", - // Final Gloves - Perfect Upp'ed Magefist - "[name] == battlegauntlets && [quality] == unique && [flag] != ethereal # [enhanceddefense] == 30 && [addfireskills] == 1 && [defense] == 71 # [tier] == 110000", - // Gloves - Upp'ed Magefist - "[name] == battlegauntlets && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 20 && [addfireskills] == 1 # [tier] == tierscore(item, 100000)", - // Gloves - Magefist - "[name] == lightgauntlets && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 20 && [addfireskills] == 1 # [tier] == tierscore(item, 100000)", - // Amulet - Tals Ammy - "[name] == amulet && [quality] == set # [lightresist] == 33 # [tier] == 100000", - // Final Rings - Perfect Raven Frost & Nagelring - "[type] == ring && [quality] == unique # [dexterity] == 20 # [tier] == 100000", - "[type] == ring && [quality] == unique # [itemmagicbonus] == 30 # [tier] == 100000", - // Rings - Raven Frost - "[type] == ring && [quality] == unique # [dexterity] >= 15 # [tier] == 90000", - // Switch - CTA - "[minimumsockets] >= 5 && [flag] == runeword # [plusskillbattleorders] >= 1 # [secondarytier] == 100000", - // Switch Shield - 1+ all skill - "[type] == shield # [itemallskills] >= 1 # [secondarytier] == tierscore(item, 100000)", - // Merc Armor - Fortitude - "[type] == armor && [flag] == runeword # [enhanceddefense] >= 200 && [enhanceddamage] >= 300 # [merctier] == 100000", - // Merc Final Helmet - Eth Andy's - "[name] == demonhead && [quality] == unique && [flag] == ethereal # [strength] >= 25 && [enhanceddefense] >= 100 # [merctier] == 50000 + mercscore(item)", - // Merc Helmet - Andy's - "[name] == demonhead && [quality] == unique && [flag] != ethereal # [strength] >= 25 && [enhanceddefense] >= 100 # [merctier] == 40000 + mercscore(item)", - ], - stats: undefined, - autoEquipTiers: undefined, - charms: { - ResLife: { - max: 3, - have: [], - classid: sdk.items.SmallCharm, - stats: function (check) { - return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MaxHp) === 20); - } - }, +(function (module) { + module.exports = (function () { + const build = { + caster: true, + skillstab: sdk.skills.tabs.Lightning, + wantedskills: [sdk.skills.FrozenOrb, sdk.skills.ColdMastery], + usefulskills: [sdk.skills.Telekinesis, sdk.skills.EnergyShield, sdk.skills.StaticField], + precastSkills: [sdk.skills.FrozenArmor], + wantedMerc: MercData[sdk.skills.HolyFreeze], + skills: [ + [sdk.skills.Warmth, 1], + [sdk.skills.FrozenArmor, 1], + [sdk.skills.StaticField, 1], + [sdk.skills.Teleport, 10], + [sdk.skills.EnergyShield, 8], + [sdk.skills.Telekinesis, 20], + [sdk.skills.FrozenOrb, 20], + [sdk.skills.ColdMastery, 17], + [sdk.skills.EnergyShield, 10], + [sdk.skills.StaticField, 20], + ], + stats: [], - ResMf: { - max: 2, - have: [], - classid: sdk.items.SmallCharm, - stats: function (check) { - return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MagicBonus) === 7); - } - }, + charms: { + ResLife: { + max: 3, + have: [], + classid: sdk.items.SmallCharm, + stats: function (check) { + return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MaxHp) === 20); + } + }, - ResFHR: { - max: 3, - have: [], - classid: sdk.items.SmallCharm, - stats: function (check) { - return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.FHR) === 5); - } - }, + ResMf: { + max: 2, + have: [], + classid: sdk.items.SmallCharm, + stats: function (check) { + return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MagicBonus) === 7); + } + }, - SkillerLight: { - max: 1, - have: [], - classid: sdk.items.GrandCharm, - stats: function (check) { - return (!check.unique && check.classid === this.classid && check.getStat(sdk.stats.AddSkillTab, sdk.skills.tabs.Lightning) === 1 - && check.getStat(sdk.stats.MaxHp) >= 40); - } - }, + ResFHR: { + max: 3, + have: [], + classid: sdk.items.SmallCharm, + stats: function (check) { + return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.FHR) === 5); + } + }, - SkillerCold: { - max: 1, - have: [], - classid: sdk.items.GrandCharm, - stats: function (check) { - return (!check.unique && check.classid === this.classid && check.getStat(sdk.stats.AddSkillTab, sdk.skills.tabs.Cold) === 1 - && check.getStat(sdk.stats.MaxHp) >= 40); - } - }, - }, - - AutoBuildTemplate: { - 1: { - Update: function () { - Config.AttackSkill = [-1, sdk.skills.FrozenOrb, sdk.skills.StaticField, sdk.skills.FrozenOrb, sdk.skills.StaticField, -1, -1]; - Config.LowManaSkill = [-1, -1]; - Config.SkipImmune = ["cold"]; - Config.HPBuffer = me.expansion ? 1 : 5; - Config.MPBuffer = me.expansion ? 1 : 5; - } - }, - }, + SkillerLight: { + max: 1, + have: [], + classid: sdk.items.GrandCharm, + stats: function (check) { + return (!check.unique && check.classid === this.classid && check.getStat(sdk.stats.AddSkillTab, sdk.skills.tabs.Lightning) === 1 + && check.getStat(sdk.stats.MaxHp) >= 40); + } + }, - respec: function () { - if (me.classic) { - return me.charlvl >= 75 && me.diablo; - } else { - return me.haveAll([ - { name: sdk.locale.items.TalRashasBelt, quality: sdk.items.quality.Set }, - { name: sdk.locale.items.TalRashasAmulet, quality: sdk.items.quality.Set }, - { name: sdk.locale.items.TalRashasArmor, quality: sdk.items.quality.Set }, - { name: sdk.locale.items.TalRashasOrb, quality: sdk.items.quality.Set }, - { name: sdk.locale.items.TalRashasHelmet, quality: sdk.items.quality.Set }, - ]) && me.hell && me.baal; - } - }, + SkillerCold: { + max: 1, + have: [], + classid: sdk.items.GrandCharm, + stats: function (check) { + return (!check.unique && check.classid === this.classid && check.getStat(sdk.stats.AddSkillTab, sdk.skills.tabs.Cold) === 1 + && check.getStat(sdk.stats.MaxHp) >= 40); + } + }, + }, + + AutoBuildTemplate: { + 1: { + Update: function () { + Config.AttackSkill = [-1, sdk.skills.FrozenOrb, sdk.skills.StaticField, sdk.skills.FrozenOrb, sdk.skills.StaticField, -1, -1]; + Config.LowManaSkill = [-1, -1]; + Config.SkipImmune = ["cold"]; + Config.HPBuffer = me.expansion ? 1 : 5; + Config.MPBuffer = me.expansion ? 1 : 5; + } + }, + }, - active: function () { - return this.respec() && me.getSkill(sdk.skills.Telekinesis, sdk.skills.subindex.HardPoints) === 20; - }, -}; + respec: function () { + if (me.classic) { + return me.charlvl >= 75 && me.diablo; + } else { + return me.haveAll([ + { name: sdk.locale.items.TalRashasBelt, quality: sdk.items.quality.Set }, + { name: sdk.locale.items.TalRashasAmulet, quality: sdk.items.quality.Set }, + { name: sdk.locale.items.TalRashasArmor, quality: sdk.items.quality.Set }, + { name: sdk.locale.items.TalRashasOrb, quality: sdk.items.quality.Set }, + { name: sdk.locale.items.TalRashasHelmet, quality: sdk.items.quality.Set }, + ]) && me.hell && me.baal; + } + }, -// Has to be set after its loaded -finalBuild.stats = me.classic ? finalBuild.classicStats : finalBuild.expansionStats; -finalBuild.autoEquipTiers = me.classic ? finalBuild.classicTiers : finalBuild.expansionTiers; + active: function () { + return this.respec() && me.getSkill(sdk.skills.Telekinesis, sdk.skills.subindex.HardPoints) === 20; + }, + }; + + build.stats = me.classic + ? [ + ["dexterity", 51], ["strength", 80], ["energy", 100], ["vitality", 125], + ["energy", 150], ["vitality", 150], ["energy", "all"] + ] + : [ + ["strength", 48], ["vitality", 165], ["strength", 61], + ["vitality", 252], ["strength", 127], ["dexterity", "block"], ["vitality", "all"] + ]; + + let finalGear = me.classic + ? [ + // Weapon - Spectral Shard + "[name] == blade && [quality] == unique # [fcr] == 20 && [allres] == 10 # [tier] == 100000", + // Helm - Tarnhelm + "[name] == skullcap && [quality] == unique # [itemallskills] == 1 && [itemmagicbonus] >= 25 # [tier] == tierscore(item, 100000)", + // Shield + "[type] == shield && [quality] >= magic # [sorceressskills] == 2 && [allres] >= 16 # [tier] == tierscore(item, 100000)", + // Rings - SoJ + "[type] == ring && [quality] == unique # [itemmaxmanapercent] == 25 # [tier] == 100000", + // Amulet + "[type] == amulet && [quality] >= magic # [sorceressskills] == 2 && [fcr] == 10 # [tier] == tierscore(item, 100000)", + // Boots + "[type] == boots && [quality] >= magic # [frw] >= 20 && [fhr] == 10 && [coldresist]+[lightresist] >= 10 # [tier] == tierscore(item, 100000)", + // Belt + "[type] == belt && [quality] >= magic # [fhr] >= 20 && [maxhp] >= 40 && [fireresist]+[lightresist] >= 20 # [tier] == tierscore(item, 100000)", + // Gloves - Magefist + "[name] == lightgauntlets && [quality] == unique # [fcr] >= 20 && [addfireskills] == 1 # [tier] == tierscore(item, 100000)", + ] + : [ + // Weapon - Tals Orb + "[name] == swirlingcrystal && [quality] == set && [flag] != ethereal # [skilllightningmastery]+[skillfiremastery]+[skillcoldmastery] >= 3 # [tier] == tierscore(item, 100000)", + // Helmet - Tals Mask + "[name] == deathmask && [quality] == set && [flag] != ethereal # [coldresist]+[lightresist]+[fireresist]+[poisonresist] >= 60 # [tier] == tierscore(item, 100000)", + // Belt - Tals Belt + "[name] == meshbelt && [quality] == set && [flag] != ethereal # [itemmagicbonus] >= 10 # [tier] == tierscore(item, 100000)", + // Final Boots - Sandstorm Treks + "[name] == scarabshellboots && [quality] == unique # [strength]+[vitality] >= 20 # [tier] == tierscore(item, 100000)", + // Boots - War Traveler + "[name] == battleboots && [quality] == unique && [flag] != ethereal # [itemmagicbonus] >= 50 # [tier] == tierscore(item, 5000)", + // Armor - Tals Armor + "[name] == lacqueredplate && [quality] == set # [coldresist] >= 1 # [tier] == 100000", + // Final Shield - Sanctuary + "[name] == hyperion && [flag] == runeword # [fhr] >= 20 && [enhanceddefense] >= 130 && [fireresist] >= 50 # [tier] == 100000", + // Shield - Mosers + "[name] == roundshield && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 180 # [tier] == tierscore(item, 50000)", + // Final Gloves - Perfect Upp'ed Magefist + "[name] == battlegauntlets && [quality] == unique && [flag] != ethereal # [enhanceddefense] == 30 && [addfireskills] == 1 && [defense] == 71 # [tier] == 110000", + // Gloves - Upp'ed Magefist + "[name] == battlegauntlets && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 20 && [addfireskills] == 1 # [tier] == tierscore(item, 100000)", + // Gloves - Magefist + "[name] == lightgauntlets && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 20 && [addfireskills] == 1 # [tier] == tierscore(item, 100000)", + // Amulet - Tals Ammy + "[name] == amulet && [quality] == set # [lightresist] == 33 # [tier] == 100000", + // Final Rings - Perfect Raven Frost & Nagelring + "[type] == ring && [quality] == unique # [dexterity] == 20 # [tier] == 100000", + "[type] == ring && [quality] == unique # [itemmagicbonus] == 30 # [tier] == 100000", + // Rings - Raven Frost + "[type] == ring && [quality] == unique # [dexterity] >= 15 # [tier] == 90000", + // Switch - CTA + "[minimumsockets] >= 5 && [flag] == runeword # [plusskillbattleorders] >= 1 # [secondarytier] == 100000", + // Switch Shield - 1+ all skill + "[type] == shield # [itemallskills] >= 1 # [secondarytier] == tierscore(item, 100000)", + // Merc Armor - Fortitude + "[type] == armor && [flag] == runeword # [enhanceddefense] >= 200 && [enhanceddamage] >= 300 # [merctier] == 100000", + // Merc Final Helmet - Eth Andy's + "[name] == demonhead && [quality] == unique && [flag] == ethereal # [strength] >= 25 && [enhanceddefense] >= 100 # [merctier] == 50000 + mercscore(item)", + // Merc Helmet - Andy's + "[name] == demonhead && [quality] == unique && [flag] != ethereal # [strength] >= 25 && [enhanceddefense] >= 100 # [merctier] == 40000 + mercscore(item)", + ]; + + NTIP.buildList(finalGear); + NTIP.buildFinalGear(finalGear); + + return build; + })(); +})(module); diff --git a/libs/SoloPlay/BuildFiles/sorceress/sorceress.LevelingBuild.js b/libs/SoloPlay/BuildFiles/sorceress/sorceress.LevelingBuild.js index b04fac04..49af8cd9 100644 --- a/libs/SoloPlay/BuildFiles/sorceress/sorceress.LevelingBuild.js +++ b/libs/SoloPlay/BuildFiles/sorceress/sorceress.LevelingBuild.js @@ -5,79 +5,86 @@ * */ -let build = { - AutoBuildTemplate: {}, - caster: true, - skillstab: sdk.skills.tabs.Cold, - wantedskills: [sdk.skills.Blizzard, sdk.skills.FireBall, sdk.skills.ColdMastery], - usefulskills: [sdk.skills.GlacialSpike, sdk.skills.Meteor, sdk.skills.FireMastery, sdk.skills.StaticField], - usefulStats: [sdk.stats.PassiveColdPierce, sdk.stats.PassiveColdMastery, sdk.stats.PassiveFireMastery, sdk.stats.PassiveFirePierce], - mercDiff: sdk.difficulty.Nightmare, - mercAct: 2, - mercAuraWanted: "Holy Freeze", - classicStats: [ - ["dexterity", 51], ["strength", 80], ["energy", 100], ["vitality", "all"] - ], - expansionStats: [ - ["energy", 50], ["strength", 48], ["vitality", 165], - ["strength", 61], ["vitality", 200], ["strength", 127], - ["vitality", 252], ["dexterity", "block"], ["vitality", "all"] - ], - classicSkills: [ - // Total skills at respec = 70 - [sdk.skills.Warmth, 1], // points left 69 - [sdk.skills.FrozenArmor, 1], // points left 68 - [sdk.skills.StaticField, 6], // points left 62 - [sdk.skills.Teleport, 4], // points left 57 - [sdk.skills.Meteor, 8], // points left 44 - [sdk.skills.FireMastery, 1], // points left 43 - [sdk.skills.ColdMastery, 1], // points left 42 - [sdk.skills.FrozenOrb, 1], // points left 36 - [sdk.skills.FireBall, 10], // points left 27 - [sdk.skills.Blizzard, 20], // points left 8 - [sdk.skills.IceBlast, 12], // points left 0 - [sdk.skills.Meteor, 10], - [sdk.skills.IceBlast, 20], - [sdk.skills.ColdMastery, 17], - [sdk.skills.FireBolt, 20], - ], - expansionSkills: [ - // Total skills at respec = 70 - [sdk.skills.Warmth, 1], // points left 69 - [sdk.skills.FrozenArmor, 1], // points left 68 - [sdk.skills.StaticField, 1], // points left 67 - [sdk.skills.Teleport, 1], // points left 65 - [sdk.skills.Meteor, 1], // points left 59 - [sdk.skills.FireMastery, 1], // points left 58 - [sdk.skills.ColdMastery, 1], // points left 57 - [sdk.skills.FrozenOrb, 1], // points left 51 - [sdk.skills.FireBall, 20], // points left 32 - [sdk.skills.Blizzard, 20], // points left 13 - [sdk.skills.IceBlast, 15], // points left 0 - [sdk.skills.Meteor, 15], - [sdk.skills.IceBlast, 20], - [sdk.skills.Meteor, 20], - [sdk.skills.ColdMastery, 5], - [sdk.skills.FireBolt, 20], - ], - stats: undefined, - skills: undefined, - active: function () { - return (me.charlvl > CharInfo.respecOne && me.charlvl > CharInfo.respecTwo && me.checkSkill(sdk.skills.FireMastery, sdk.skills.subindex.HardPoints) && !Check.finalBuild().active()); - }, -}; +(function (module) { + module.exports = (function () { + const build = { + caster: true, + skillstab: sdk.skills.tabs.Cold, + wantedskills: [sdk.skills.Blizzard, sdk.skills.FireBall, sdk.skills.ColdMastery], + usefulskills: [sdk.skills.GlacialSpike, sdk.skills.Meteor, sdk.skills.FireMastery, sdk.skills.StaticField], + usefulStats: [sdk.stats.PassiveColdPierce, sdk.stats.PassiveColdMastery, sdk.stats.PassiveFireMastery, sdk.stats.PassiveFirePierce], + wantedMerc: MercData[sdk.skills.HolyFreeze], + stats: [], + skills: [], -// Has to be set after its loaded -build.stats = me.classic ? build.classicStats : build.expansionStats; -build.skills = me.classic ? build.classicSkills : build.expansionSkills; + active: function () { + return (me.charlvl > CharInfo.respecOne && me.charlvl > CharInfo.respecTwo && me.checkSkill(sdk.skills.FireMastery, sdk.skills.subindex.HardPoints) && !Check.finalBuild().active()); + }, -build.AutoBuildTemplate[1] = buildAutoBuildTempObj(() => { - Config.AttackSkill = [-1, sdk.skills.Blizzard, sdk.skills.IceBlast, sdk.skills.Blizzard, sdk.skills.IceBlast, sdk.skills.Meteor, sdk.skills.FireBall]; - Config.LowManaSkill = [-1, -1]; - Config.SkipImmune = ["fire and cold"]; - Config.BeltColumn = ["hp", "hp", "mp", "mp"]; - Config.HPBuffer = me.expansion ? 2 : 5; - Config.MPBuffer = me.expansion && me.charlvl < 80 ? 6 : me.classic ? 5 : 2; - SetUp.belt(); -}); + AutoBuildTemplate: { + 1: { + Update: function () { + Config.AttackSkill = [-1, sdk.skills.Blizzard, sdk.skills.IceBlast, sdk.skills.Blizzard, sdk.skills.IceBlast, sdk.skills.Meteor, sdk.skills.FireBall]; + Config.LowManaSkill = [-1, -1]; + Config.SkipImmune = ["fire and cold"]; + Config.BeltColumn = ["hp", "hp", "mp", "mp"]; + Config.HPBuffer = me.expansion ? 2 : 5; + Config.MPBuffer = me.expansion && me.charlvl < 80 ? 6 : me.classic ? 5 : 2; + SetUp.belt(); + } + } + }, + }; + + // Has to be set after its loaded + build.stats = me.classic + ? [ + ["dexterity", 51], ["strength", 80], ["energy", 100], ["vitality", "all"] + ] : [ + ["energy", 50], ["strength", 48], ["vitality", 165], + ["strength", 61], ["vitality", 200], ["strength", 127], + ["vitality", 252], ["dexterity", "block"], ["vitality", "all"] + ]; + + build.skills = me.classic + ? [ + // Total skills at respec = 70 + [sdk.skills.Warmth, 1], // points left 69 + [sdk.skills.FrozenArmor, 1], // points left 68 + [sdk.skills.StaticField, 6], // points left 62 + [sdk.skills.Teleport, 4], // points left 57 + [sdk.skills.Meteor, 8], // points left 44 + [sdk.skills.FireMastery, 1], // points left 43 + [sdk.skills.ColdMastery, 1], // points left 42 + [sdk.skills.FrozenOrb, 1], // points left 36 + [sdk.skills.FireBall, 10], // points left 27 + [sdk.skills.Blizzard, 20], // points left 8 + [sdk.skills.IceBlast, 12], // points left 0 + [sdk.skills.Meteor, 10], + [sdk.skills.IceBlast, 20], + [sdk.skills.ColdMastery, 17], + [sdk.skills.FireBolt, 20], + ] : [ + // Total skills at respec = 70 + [sdk.skills.Warmth, 1], // points left 69 + [sdk.skills.FrozenArmor, 1], // points left 68 + [sdk.skills.StaticField, 1], // points left 67 + [sdk.skills.Teleport, 1], // points left 65 + [sdk.skills.Meteor, 1], // points left 59 + [sdk.skills.FireMastery, 1], // points left 58 + [sdk.skills.ColdMastery, 1], // points left 57 + [sdk.skills.FrozenOrb, 1], // points left 51 + [sdk.skills.FireBall, 20], // points left 32 + [sdk.skills.Blizzard, 20], // points left 13 + [sdk.skills.IceBlast, 15], // points left 0 + [sdk.skills.Meteor, 15], + [sdk.skills.IceBlast, 20], + [sdk.skills.Meteor, 20], + [sdk.skills.ColdMastery, 5], + [sdk.skills.FireBolt, 20], + ]; + + return build; + })(); +})(module); diff --git a/libs/SoloPlay/BuildFiles/sorceress/sorceress.LightningBuild.js b/libs/SoloPlay/BuildFiles/sorceress/sorceress.LightningBuild.js index b95558ee..a652fd71 100644 --- a/libs/SoloPlay/BuildFiles/sorceress/sorceress.LightningBuild.js +++ b/libs/SoloPlay/BuildFiles/sorceress/sorceress.LightningBuild.js @@ -5,142 +5,152 @@ * */ -const finalBuild = { - caster: true, - skillstab: sdk.skills.tabs.Lightning, - wantedskills: [sdk.skills.ChainLightning, sdk.skills.Lightning], - usefulskills: [sdk.skills.LightningMastery, sdk.skills.ChargedBolt, sdk.skills.Nova], - precastSkills: [sdk.skills.FrozenArmor], - usefulStats: [sdk.stats.PassiveLightningMastery, sdk.stats.PassiveLightningPierce], - mercDiff: sdk.difficulty.Nightmare, - mercAct: 2, - mercAuraWanted: "Holy Freeze", - stats: [ - ["strength", 156], ["dexterity", 35], ["vitality", "all"] - ], - skills: [ - [sdk.skills.Warmth, 1], - [sdk.skills.StaticField, 1], - [sdk.skills.Teleport, 1], - [sdk.skills.FrozenArmor, 1], - [sdk.skills.ThunderStorm, 1], - [sdk.skills.LightningMastery, 1], - [sdk.skills.Lightning, 20], - [sdk.skills.ChainLightning, 20], - [sdk.skills.LightningMastery, 20], // lvl 69 w/o quest skill pts - [sdk.skills.Nova, 20], - [sdk.skills.ChargedBolt, 20], - ], - autoEquipTiers: [ // autoequip final gear - // Weapon - HotO - "[type] == mace && [flag] == runeword # [fcr] == 40 # [tier] == 100000", - // Helmet - Harlequin's Crest - "[name] == shako && [quality] == unique && [flag] != ethereal # [damageresist] >= 10 # [tier] == tierscore(item, 100000)", - // Belt - Arach's - "[name] == spiderwebsash && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 90 # [tier] == tierscore(item, 100000)", - // Boots - "[name] == battleboots && [quality] == unique && [flag] != ethereal # [itemmagicbonus] >= 50 # [tier] == tierscore(item, 5000)", // War Traveler - "[name] == scarabshellboots && [quality] == unique # [strength]+[vitality] >= 20 # [tier] == tierscore(item, 100000)", // Sandstorm Treks - // Armor - CoH - "[type] == armor && [flag] == runeword && [flag] != ethereal # [fireresist] == 65 && [hpregen] == 7 # [tier] == 100000", - // Shield - Spirit - "[type] == shield # [fcr] >= 25 && [maxmana] >= 89 # [tier] == tierscore(item, 100000)", - // Final Gloves - Perfect 2x Upp'ed Magefist - "[name] == crusadergauntlets && [quality] == unique && [flag] != ethereal # [enhanceddefense] == 30 && [addfireskills] == 1 # [tier] == 110000", - // Gloves - 2x Upp'ed Magefist - "[name] == crusadergauntlets && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 20 && [addfireskills] == 1 # [tier] == tierscore(item, 100000)", - // Gloves - Upp'ed Magefist - "[name] == battlegauntlets && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 20 && [addfireskills] == 1 # [tier] == tierscore(item, 100000)", - // Gloves - Magefist - "[name] == lightgauntlets && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 20 && [addfireskills] == 1 # [tier] == tierscore(item, 100000)", - // Amulet - Maras - "[type] == amulet && [quality] == unique # [strength] == 5 && [coldresist] >= 30 # [tier] == tierscore(item, 100000)", - // Final Rings - SoJ & Perfect Wisp - "[type] == ring && [quality] == unique # [itemmaxmanapercent] == 25 # [tier] == 100000", - "[name] == ring && [quality] == unique # [itemabsorblightpercent] == 20 # [tier] == tierscore(item, 110000)", - // Rings - Wisp - "[name] == ring && [quality] == unique # [itemabsorblightpercent] >= 10 # [tier] == tierscore(item, 100000)", - // Switch - CTA - "[minimumsockets] >= 5 && [flag] == runeword # [plusskillbattleorders] >= 1 # [secondarytier] == 100000", - // Switch Final Shield - Spirit - "[name] == monarch && [flag] == runeword # [fcr] >= 25 && [maxmana] >= 89 # [secondarytier] == 110000", - // Switch Temporary Shield - 1+ all skill - "[type] == shield # [itemallskills] >= 1 # [secondarytier] == tierscore(item, 50000)", - // Merc Armor - Fortitude - "[type] == armor && [flag] == runeword # [enhanceddefense] >= 200 && [enhanceddamage] >= 300 # [merctier] == 100000", - // Merc Final Helmet - Eth Andy's - "[name] == demonhead && [quality] == unique && [flag] == ethereal # [strength] >= 25 && [enhanceddefense] >= 100 # [merctier] == 50000 + mercscore(item)", - // Merc Helmet - Andy's - "[name] == demonhead && [quality] == unique && [flag] != ethereal # [strength] >= 25 && [enhanceddefense] >= 100 # [merctier] == 40000 + mercscore(item)", - ], - charms: { - ResLife: { - max: 3, - have: [], - classid: sdk.items.SmallCharm, - stats: function (check) { - return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MaxHp) === 20); - } - }, +(function (module) { + module.exports = (function () { + const build = { + caster: true, + skillstab: sdk.skills.tabs.Lightning, + wantedskills: [sdk.skills.ChainLightning, sdk.skills.Lightning], + usefulskills: [sdk.skills.LightningMastery, sdk.skills.ChargedBolt, sdk.skills.Nova], + precastSkills: [sdk.skills.FrozenArmor], + usefulStats: [sdk.stats.PassiveLightningMastery, sdk.stats.PassiveLightningPierce], + wantedMerc: MercData[sdk.skills.HolyFreeze], + stats: [ + ["strength", 156], ["dexterity", 35], ["vitality", "all"] + ], + skills: [ + [sdk.skills.Warmth, 1], + [sdk.skills.StaticField, 1], + [sdk.skills.Teleport, 1], + [sdk.skills.FrozenArmor, 1], + [sdk.skills.ThunderStorm, 1], + [sdk.skills.LightningMastery, 1], + [sdk.skills.Lightning, 20], + [sdk.skills.ChainLightning, 20], + [sdk.skills.LightningMastery, 20], // lvl 69 w/o quest skill pts + [sdk.skills.Nova, 20], + [sdk.skills.ChargedBolt, 20], + ], - ResMf: { - max: 2, - have: [], - classid: sdk.items.SmallCharm, - stats: function (check) { - return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MagicBonus) === 7); - } - }, + charms: { + ResLife: { + max: 3, + have: [], + classid: sdk.items.SmallCharm, + stats: function (check) { + return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MaxHp) === 20); + } + }, - ResFHR: { - max: 1, - have: [], - classid: sdk.items.SmallCharm, - stats: function (check) { - return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.FHR) === 5); - } - }, + ResMf: { + max: 2, + have: [], + classid: sdk.items.SmallCharm, + stats: function (check) { + return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MagicBonus) === 7); + } + }, - LifeMana: { - max: 2, - have: [], - classid: sdk.items.SmallCharm, - stats: function (check) { - return (!check.unique && check.classid === this.classid && check.getStat(sdk.stats.MaxHp) === 20 && check.getStat(sdk.stats.MaxMana) === 17); - } - }, + ResFHR: { + max: 1, + have: [], + classid: sdk.items.SmallCharm, + stats: function (check) { + return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.FHR) === 5); + } + }, - Skiller: { - max: 2, - have: [], - classid: sdk.items.GrandCharm, - stats: function (check) { - return (!check.unique && check.classid === this.classid && check.getStat(sdk.stats.AddSkillTab, sdk.skills.tabs.Lightning) === 1 - && check.getStat(sdk.stats.MaxHp) >= 40); - } - }, - }, + LifeMana: { + max: 2, + have: [], + classid: sdk.items.SmallCharm, + stats: function (check) { + return (!check.unique && check.classid === this.classid && check.getStat(sdk.stats.MaxHp) === 20 && check.getStat(sdk.stats.MaxMana) === 17); + } + }, - AutoBuildTemplate: { - 1: { - Update: function () { - Config.AttackSkill = [-1, sdk.skills.Lightning, sdk.skills.ChainLightning, sdk.skills.ChainLightning, sdk.skills.Lightning, -1, -1]; - Config.LowManaSkill = [-1, -1]; - Config.SkipImmune = ["lightning"]; - } - }, - }, + Skiller: { + max: 2, + have: [], + classid: sdk.items.GrandCharm, + stats: function (check) { + return (!check.unique && check.classid === this.classid && check.getStat(sdk.stats.AddSkillTab, sdk.skills.tabs.Lightning) === 1 + && check.getStat(sdk.stats.MaxHp) >= 40); + } + }, + }, - respec: function () { - if (me.classic) { - return me.charlvl >= 75 && me.diablo; - } else { - return (Attack.checkInfinity() || (myData.merc.gear.includes(sdk.locale.items.Infinity) && !Misc.poll(() => me.getMerc(), 200, 50))); - } - }, + AutoBuildTemplate: { + 1: { + Update: function () { + Config.AttackSkill = [-1, sdk.skills.Lightning, sdk.skills.ChainLightning, sdk.skills.ChainLightning, sdk.skills.Lightning, -1, -1]; + Config.LowManaSkill = [-1, -1]; + Config.SkipImmune = ["lightning"]; + } + }, + }, - active: function () { - return this.respec() && me.getSkill(sdk.skills.Lightning, sdk.skills.subindex.HardPoints) === 20 && !me.checkSkill(sdk.skills.Blizzard, sdk.skills.subindex.HardPoints); - }, -}; + respec: function () { + if (me.classic) { + return me.charlvl >= 75 && me.diablo; + } else { + return (Attack.checkInfinity() || (myData.merc.gear.includes(sdk.locale.items.Infinity) && !Misc.poll(() => me.getMerc(), 200, 50))); + } + }, + + active: function () { + return this.respec() && me.getSkill(sdk.skills.Lightning, sdk.skills.subindex.HardPoints) === 20 && !me.checkSkill(sdk.skills.Blizzard, sdk.skills.subindex.HardPoints); + }, + }; + + // autoequip final gear + let finalGear = [ + // Weapon - HotO + "[type] == mace && [flag] == runeword # [fcr] == 40 # [tier] == 100000", + // Helmet - Harlequin's Crest + "[name] == shako && [quality] == unique && [flag] != ethereal # [damageresist] >= 10 # [tier] == tierscore(item, 100000)", + // Belt - Arach's + "[name] == spiderwebsash && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 90 # [tier] == tierscore(item, 100000)", + // Boots + "[name] == battleboots && [quality] == unique && [flag] != ethereal # [itemmagicbonus] >= 50 # [tier] == tierscore(item, 5000)", // War Traveler + "[name] == scarabshellboots && [quality] == unique # [strength]+[vitality] >= 20 # [tier] == tierscore(item, 100000)", // Sandstorm Treks + // Armor - CoH + "[type] == armor && [flag] == runeword && [flag] != ethereal # [fireresist] == 65 && [hpregen] == 7 # [tier] == 100000", + // Shield - Spirit + "[type] == shield # [fcr] >= 25 && [maxmana] >= 89 # [tier] == tierscore(item, 100000)", + // Final Gloves - Perfect 2x Upp'ed Magefist + "[name] == crusadergauntlets && [quality] == unique && [flag] != ethereal # [enhanceddefense] == 30 && [addfireskills] == 1 # [tier] == 110000", + // Gloves - 2x Upp'ed Magefist + "[name] == crusadergauntlets && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 20 && [addfireskills] == 1 # [tier] == tierscore(item, 100000)", + // Gloves - Upp'ed Magefist + "[name] == battlegauntlets && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 20 && [addfireskills] == 1 # [tier] == tierscore(item, 100000)", + // Gloves - Magefist + "[name] == lightgauntlets && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 20 && [addfireskills] == 1 # [tier] == tierscore(item, 100000)", + // Amulet - Maras + "[type] == amulet && [quality] == unique # [strength] == 5 && [coldresist] >= 30 # [tier] == tierscore(item, 100000)", + // Final Rings - SoJ & Perfect Wisp + "[type] == ring && [quality] == unique # [itemmaxmanapercent] == 25 # [tier] == 100000", + "[name] == ring && [quality] == unique # [itemabsorblightpercent] == 20 # [tier] == tierscore(item, 110000)", + // Rings - Wisp + "[name] == ring && [quality] == unique # [itemabsorblightpercent] >= 10 # [tier] == tierscore(item, 100000)", + // Switch - CTA + "[minimumsockets] >= 5 && [flag] == runeword # [plusskillbattleorders] >= 1 # [secondarytier] == 100000", + // Switch Final Shield - Spirit + "[name] == monarch && [flag] == runeword # [fcr] >= 25 && [maxmana] >= 89 # [secondarytier] == 110000", + // Switch Temporary Shield - 1+ all skill + "[type] == shield # [itemallskills] >= 1 # [secondarytier] == tierscore(item, 50000)", + // Merc Armor - Fortitude + "[type] == armor && [flag] == runeword # [enhanceddefense] >= 200 && [enhanceddamage] >= 300 # [merctier] == 100000", + // Merc Final Helmet - Eth Andy's + "[name] == demonhead && [quality] == unique && [flag] == ethereal # [strength] >= 25 && [enhanceddefense] >= 100 # [merctier] == 50000 + mercscore(item)", + // Merc Helmet - Andy's + "[name] == demonhead && [quality] == unique && [flag] != ethereal # [strength] >= 25 && [enhanceddefense] >= 100 # [merctier] == 40000 + mercscore(item)", + ]; + + NTIP.buildList(finalGear); + NTIP.buildFinalGear(finalGear); + + return build; + })(); +})(module); diff --git a/libs/SoloPlay/BuildFiles/sorceress/sorceress.MeteorbBuild.js b/libs/SoloPlay/BuildFiles/sorceress/sorceress.MeteorbBuild.js index c74f30ed..5c2872fa 100644 --- a/libs/SoloPlay/BuildFiles/sorceress/sorceress.MeteorbBuild.js +++ b/libs/SoloPlay/BuildFiles/sorceress/sorceress.MeteorbBuild.js @@ -5,180 +5,187 @@ * */ -const finalBuild = { - caster: true, - skillstab: sdk.skills.tabs.Fire, - wantedskills: [sdk.skills.FrozenOrb, sdk.skills.Meteor, sdk.skills.ColdMastery], - usefulskills: [sdk.skills.FireBall, sdk.skills.FireMastery, sdk.skills.StaticField], - precastSkills: [sdk.skills.FrozenArmor], - usefulStats: [sdk.stats.PassiveColdPierce, sdk.stats.PassiveColdMastery, sdk.stats.PassiveFireMastery, sdk.stats.PassiveFirePierce], - mercDiff: sdk.difficulty.Nightmare, - mercAct: 2, - mercAuraWanted: "Holy Freeze", - classicStats: [ - ["dexterity", 51], ["strength", 80], ["energy", 100], ["vitality", "all"] - ], - expansionStats: [ - ["strength", 48], ["vitality", 165], ["strength", 61], - ["vitality", 252], ["strength", 127], ["dexterity", "block"], ["vitality", "all"] - ], - skills: [ - [sdk.skills.Warmth, 1], - [sdk.skills.FrozenArmor, 1], - [sdk.skills.StaticField, 1], - [sdk.skills.Teleport, 1], - [sdk.skills.FireMastery, 1], - [sdk.skills.ColdMastery, 1], - [sdk.skills.FireBall, 14], - [sdk.skills.Meteor, 20], - [sdk.skills.FrozenOrb, 20], // 71 points w/o quest skill pts - [sdk.skills.ColdMastery, 12], - [sdk.skills.FireBall, 20], - [sdk.skills.FireMastery, 20], - [sdk.skills.FireBolt, 20], - ], - classicTiers: [ - // Weapon - Spectral Shard - "[name] == blade && [quality] == unique # [fcr] == 20 && [allres] == 10 # [tier] == 100000", - // Helm - Tarnhelm - "[name] == skullcap && [quality] == unique # [itemallskills] == 1 && [itemmagicbonus] >= 25 # [tier] == tierscore(item, 100000)", - // Shield - "[type] == shield && [quality] >= magic # [sorceressskills] == 2 && [allres] >= 16 # [tier] == tierscore(item, 100000)", - // Rings - SoJ - "[type] == ring && [quality] == unique # [itemmaxmanapercent] == 25 # [tier] == 100000", - // Amulet - "[type] == amulet && [quality] >= magic # [sorceressskills] == 2 && [fcr] == 10 # [tier] == tierscore(item, 100000)", - // Boots - "[type] == boots && [quality] >= magic # [frw] >= 20 && [fhr] == 10 && [coldresist]+[lightresist] >= 10 # [tier] == tierscore(item, 100000)", - // Belt - "[type] == belt && [quality] >= magic # [fhr] >= 20 && [maxhp] >= 40 && [fireresist]+[lightresist] >= 20 # [tier] == tierscore(item, 100000)", - // Gloves - Magefist - "[name] == lightgauntlets && [quality] == unique # [fcr] >= 20 && [addfireskills] == 1 # [tier] == tierscore(item, 100000)", - ], - expansionTiers: [ - // Weapon - Tals Orb - "[name] == swirlingcrystal && [quality] == set && [flag] != ethereal # [skilllightningmastery]+[skillfiremastery]+[skillcoldmastery] >= 3 # [tier] == tierscore(item, 100000)", - // Helmet - Tals Mask - "[name] == deathmask && [quality] == set && [flag] != ethereal # [coldresist]+[lightresist]+[fireresist]+[poisonresist] >= 60 # [tier] == tierscore(item, 100000)", - // Belt - Tals Belt - "[name] == meshbelt && [quality] == set && [flag] != ethereal # [itemmagicbonus] >= 10 # [tier] == tierscore(item, 100000)", - // Final Boots - Sandstorm Treks - "[name] == scarabshellboots && [quality] == unique # [strength]+[vitality] >= 20 # [tier] == tierscore(item, 100000)", - // Boots - War Traveler - "[name] == battleboots && [quality] == unique && [flag] != ethereal # [itemmagicbonus] >= 50 # [tier] == tierscore(item, 5000)", - // Armor - Tals Armor - "[name] == lacqueredplate && [quality] == set # [coldresist] >= 1 # [tier] == 100000", - // Final Shield - Sanctuary - "[name] == hyperion && [flag] == runeword # [fhr] >= 20 && [enhanceddefense] >= 130 && [fireresist] >= 50 # [tier] == 100000", - // Shield - Mosers - "[name] == roundshield && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 180 # [tier] == tierscore(item, 50000)", - // Final Gloves - Perfect Upp'ed Magefist - "[name] == battlegauntlets && [quality] == unique && [flag] != ethereal # [enhanceddefense] == 30 && [addfireskills] == 1 && [defense] == 71 # [tier] == 110000", - // Gloves - Upp'ed Magefist - "[name] == battlegauntlets && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 20 && [addfireskills] == 1 # [tier] == tierscore(item, 100000)", - // Gloves - Magefist - "[name] == lightgauntlets && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 20 && [addfireskills] == 1 # [tier] == tierscore(item, 100000)", - // Amulet - Tals Ammy - "[name] == amulet && [quality] == set # [lightresist] == 33 # [tier] == 100000", - // Final Rings - Perfect Raven Frost & Nagelring - "[type] == ring && [quality] == unique # [dexterity] >= 20 # [tier] == 100000", - "[type] == ring && [quality] == unique # [itemmagicbonus] >= 30 # [tier] == 100000", - // Rings - Raven Frost - "[type] == ring && [quality] == unique # [dexterity] >= 15 # [tier] == 100000", - // Switch - CTA - "[minimumsockets] >= 5 && [flag] == runeword # [plusskillbattleorders] >= 1 # [secondarytier] == 100000", - // Switch Shield - 1+ all skill - "[type] == shield # [itemallskills] >= 1 # [secondarytier] == tierscore(item, 100000)", - // Merc Final Armor - Fortitude - "[type] == armor && [flag] == runeword # [enhanceddefense] >= 200 && [enhanceddamage] >= 300 # [merctier] == 100000", - // Merc Final Helmet - Eth Andy's - "[name] == demonhead && [quality] == unique && [flag] == ethereal # [strength] >= 25 && [enhanceddefense] >= 100 # [merctier] == 50000 + mercscore(item)", - // Merc Helmet - Andy's - "[name] == demonhead && [quality] == unique && [flag] != ethereal # [strength] >= 25 && [enhanceddefense] >= 100 # [merctier] == 40000 + mercscore(item)", - ], - stats: undefined, - autoEquipTiers: undefined, - charms: { - ResLife: { - max: 3, - have: [], - classid: sdk.items.SmallCharm, - stats: function (check) { - return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MaxHp) === 20); - } - }, +(function (module) { + module.exports = (function () { + const build = { + caster: true, + skillstab: sdk.skills.tabs.Fire, + wantedskills: [sdk.skills.FrozenOrb, sdk.skills.Meteor, sdk.skills.ColdMastery], + usefulskills: [sdk.skills.FireBall, sdk.skills.FireMastery, sdk.skills.StaticField], + precastSkills: [sdk.skills.FrozenArmor], + usefulStats: [sdk.stats.PassiveColdPierce, sdk.stats.PassiveColdMastery, sdk.stats.PassiveFireMastery, sdk.stats.PassiveFirePierce], + wantedMerc: MercData[sdk.skills.HolyFreeze], + skills: [ + [sdk.skills.Warmth, 1], + [sdk.skills.FrozenArmor, 1], + [sdk.skills.StaticField, 1], + [sdk.skills.Teleport, 1], + [sdk.skills.FireMastery, 1], + [sdk.skills.ColdMastery, 1], + [sdk.skills.FireBall, 14], + [sdk.skills.Meteor, 20], + [sdk.skills.FrozenOrb, 20], // 71 points w/o quest skill pts + [sdk.skills.ColdMastery, 12], + [sdk.skills.FireBall, 20], + [sdk.skills.FireMastery, 20], + [sdk.skills.FireBolt, 20], + ], + stats: [], - ResMf: { - max: 2, - have: [], - classid: sdk.items.SmallCharm, - stats: function (check) { - return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MagicBonus) === 7); - } - }, + charms: { + ResLife: { + max: 3, + have: [], + classid: sdk.items.SmallCharm, + stats: function (check) { + return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MaxHp) === 20); + } + }, - ResFHR: { - max: 3, - have: [], - classid: sdk.items.SmallCharm, - stats: function (check) { - return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.FHR) === 5); - } - }, + ResMf: { + max: 2, + have: [], + classid: sdk.items.SmallCharm, + stats: function (check) { + return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MagicBonus) === 7); + } + }, - SkillerFire: { - max: 1, - have: [], - classid: sdk.items.GrandCharm, - stats: function (check) { - return (!check.unique && check.classid === this.classid && check.getStat(sdk.stats.AddSkillTab, sdk.skills.tabs.Fire) === 1 - && check.getStat(sdk.stats.MaxHp) >= 40); - } - }, + ResFHR: { + max: 3, + have: [], + classid: sdk.items.SmallCharm, + stats: function (check) { + return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.FHR) === 5); + } + }, - SkillerCold: { - max: 1, - have: [], - classid: sdk.items.GrandCharm, - stats: function (check) { - return (!check.unique && check.classid === this.classid && check.getStat(sdk.stats.AddSkillTab, sdk.skills.tabs.Cold) === 1 - && check.getStat(sdk.stats.MaxHp) >= 40); - } - }, - }, - - AutoBuildTemplate: { - 1: { - Update: function () { - Config.AttackSkill = [-1, sdk.skills.Meteor, sdk.skills.FireBall, sdk.skills.Meteor, sdk.skills.FireBall, sdk.skills.FrozenOrb, sdk.skills.GlacialSpike]; - Config.LowManaSkill = [-1, -1]; - Config.SkipImmune = ["fire and cold"]; - Config.HPBuffer = me.expansion ? 1 : 5; - Config.MPBuffer = me.expansion ? 1 : 5; - } - }, - }, + SkillerFire: { + max: 1, + have: [], + classid: sdk.items.GrandCharm, + stats: function (check) { + return (!check.unique && check.classid === this.classid && check.getStat(sdk.stats.AddSkillTab, sdk.skills.tabs.Fire) === 1 + && check.getStat(sdk.stats.MaxHp) >= 40); + } + }, - respec: function () { - if (me.classic) { - return me.charlvl >= 75 && me.diablo; - } else { - return me.haveAll([ - { name: sdk.locale.items.TalRashasBelt, quality: sdk.items.quality.Set }, - { name: sdk.locale.items.TalRashasAmulet, quality: sdk.items.quality.Set }, - { name: sdk.locale.items.TalRashasArmor, quality: sdk.items.quality.Set }, - { name: sdk.locale.items.TalRashasOrb, quality: sdk.items.quality.Set }, - { name: sdk.locale.items.TalRashasHelmet, quality: sdk.items.quality.Set }, - ]); - } - }, + SkillerCold: { + max: 1, + have: [], + classid: sdk.items.GrandCharm, + stats: function (check) { + return (!check.unique && check.classid === this.classid && check.getStat(sdk.stats.AddSkillTab, sdk.skills.tabs.Cold) === 1 + && check.getStat(sdk.stats.MaxHp) >= 40); + } + }, + }, + + AutoBuildTemplate: { + 1: { + Update: function () { + Config.AttackSkill = [-1, sdk.skills.Meteor, sdk.skills.FireBall, sdk.skills.Meteor, sdk.skills.FireBall, sdk.skills.FrozenOrb, sdk.skills.GlacialSpike]; + Config.LowManaSkill = [-1, -1]; + Config.SkipImmune = ["fire and cold"]; + Config.HPBuffer = me.expansion ? 1 : 5; + Config.MPBuffer = me.expansion ? 1 : 5; + } + }, + }, - active: function () { - return this.respec() && me.getSkill(sdk.skills.Meteor, sdk.skills.subindex.HardPoints) === 20 && me.getSkill(sdk.skills.FrozenOrb, sdk.skills.subindex.HardPoints) === 20; - }, -}; + respec: function () { + if (me.classic) { + return me.charlvl >= 75 && me.diablo; + } else { + return me.haveAll([ + { name: sdk.locale.items.TalRashasBelt, quality: sdk.items.quality.Set }, + { name: sdk.locale.items.TalRashasAmulet, quality: sdk.items.quality.Set }, + { name: sdk.locale.items.TalRashasArmor, quality: sdk.items.quality.Set }, + { name: sdk.locale.items.TalRashasOrb, quality: sdk.items.quality.Set }, + { name: sdk.locale.items.TalRashasHelmet, quality: sdk.items.quality.Set }, + ]); + } + }, -// Has to be set after its loaded -finalBuild.stats = me.classic ? finalBuild.classicStats : finalBuild.expansionStats; -finalBuild.autoEquipTiers = me.classic ? finalBuild.classicTiers : finalBuild.expansionTiers; + active: function () { + return this.respec() && me.getSkill(sdk.skills.Meteor, sdk.skills.subindex.HardPoints) === 20 && me.getSkill(sdk.skills.FrozenOrb, sdk.skills.subindex.HardPoints) === 20; + }, + }; + + build.stats = me.classic + ? [ + ["dexterity", 51], ["strength", 80], ["energy", 100], ["vitality", "all"] + ] + : [ + ["strength", 48], ["vitality", 165], ["strength", 61], + ["vitality", 252], ["strength", 127], ["dexterity", "block"], ["vitality", "all"] + ]; + + let finalGear = me.classic + ? [ + // Weapon - Spectral Shard + "[name] == blade && [quality] == unique # [fcr] == 20 && [allres] == 10 # [tier] == 100000", + // Helm - Tarnhelm + "[name] == skullcap && [quality] == unique # [itemallskills] == 1 && [itemmagicbonus] >= 25 # [tier] == tierscore(item, 100000)", + // Shield + "[type] == shield && [quality] >= magic # [sorceressskills] == 2 && [allres] >= 16 # [tier] == tierscore(item, 100000)", + // Rings - SoJ + "[type] == ring && [quality] == unique # [itemmaxmanapercent] == 25 # [tier] == 100000", + // Amulet + "[type] == amulet && [quality] >= magic # [sorceressskills] == 2 && [fcr] == 10 # [tier] == tierscore(item, 100000)", + // Boots + "[type] == boots && [quality] >= magic # [frw] >= 20 && [fhr] == 10 && [coldresist]+[lightresist] >= 10 # [tier] == tierscore(item, 100000)", + // Belt + "[type] == belt && [quality] >= magic # [fhr] >= 20 && [maxhp] >= 40 && [fireresist]+[lightresist] >= 20 # [tier] == tierscore(item, 100000)", + // Gloves - Magefist + "[name] == lightgauntlets && [quality] == unique # [fcr] >= 20 && [addfireskills] == 1 # [tier] == tierscore(item, 100000)", + ] + : [ + // Weapon - Tals Orb + "[name] == swirlingcrystal && [quality] == set && [flag] != ethereal # [skilllightningmastery]+[skillfiremastery]+[skillcoldmastery] >= 3 # [tier] == tierscore(item, 100000)", + // Helmet - Tals Mask + "[name] == deathmask && [quality] == set && [flag] != ethereal # [coldresist]+[lightresist]+[fireresist]+[poisonresist] >= 60 # [tier] == tierscore(item, 100000)", + // Belt - Tals Belt + "[name] == meshbelt && [quality] == set && [flag] != ethereal # [itemmagicbonus] >= 10 # [tier] == tierscore(item, 100000)", + // Final Boots - Sandstorm Treks + "[name] == scarabshellboots && [quality] == unique # [strength]+[vitality] >= 20 # [tier] == tierscore(item, 100000)", + // Boots - War Traveler + "[name] == battleboots && [quality] == unique && [flag] != ethereal # [itemmagicbonus] >= 50 # [tier] == tierscore(item, 5000)", + // Armor - Tals Armor + "[name] == lacqueredplate && [quality] == set # [coldresist] >= 1 # [tier] == 100000", + // Final Shield - Sanctuary + "[name] == hyperion && [flag] == runeword # [fhr] >= 20 && [enhanceddefense] >= 130 && [fireresist] >= 50 # [tier] == 100000", + // Shield - Mosers + "[name] == roundshield && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 180 # [tier] == tierscore(item, 50000)", + // Final Gloves - Perfect Upp'ed Magefist + "[name] == battlegauntlets && [quality] == unique && [flag] != ethereal # [enhanceddefense] == 30 && [addfireskills] == 1 && [defense] == 71 # [tier] == 110000", + // Gloves - Upp'ed Magefist + "[name] == battlegauntlets && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 20 && [addfireskills] == 1 # [tier] == tierscore(item, 100000)", + // Gloves - Magefist + "[name] == lightgauntlets && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 20 && [addfireskills] == 1 # [tier] == tierscore(item, 100000)", + // Amulet - Tals Ammy + "[name] == amulet && [quality] == set # [lightresist] == 33 # [tier] == 100000", + // Final Rings - Perfect Raven Frost & Nagelring + "[type] == ring && [quality] == unique # [dexterity] >= 20 # [tier] == 100000", + "[type] == ring && [quality] == unique # [itemmagicbonus] >= 30 # [tier] == 100000", + // Rings - Raven Frost + "[type] == ring && [quality] == unique # [dexterity] >= 15 # [tier] == 100000", + // Switch - CTA + "[minimumsockets] >= 5 && [flag] == runeword # [plusskillbattleorders] >= 1 # [secondarytier] == 100000", + // Switch Shield - 1+ all skill + "[type] == shield # [itemallskills] >= 1 # [secondarytier] == tierscore(item, 100000)", + // Merc Final Armor - Fortitude + "[type] == armor && [flag] == runeword # [enhanceddefense] >= 200 && [enhanceddamage] >= 300 # [merctier] == 100000", + // Merc Final Helmet - Eth Andy's + "[name] == demonhead && [quality] == unique && [flag] == ethereal # [strength] >= 25 && [enhanceddefense] >= 100 # [merctier] == 50000 + mercscore(item)", + // Merc Helmet - Andy's + "[name] == demonhead && [quality] == unique && [flag] != ethereal # [strength] >= 25 && [enhanceddefense] >= 100 # [merctier] == 40000 + mercscore(item)", + ]; + + NTIP.buildList(finalGear); + NTIP.buildFinalGear(finalGear); + + return build; + })(); +})(module); diff --git a/libs/SoloPlay/BuildFiles/sorceress/sorceress.StartBuild.js b/libs/SoloPlay/BuildFiles/sorceress/sorceress.StartBuild.js index b6bc6c58..02621969 100644 --- a/libs/SoloPlay/BuildFiles/sorceress/sorceress.StartBuild.js +++ b/libs/SoloPlay/BuildFiles/sorceress/sorceress.StartBuild.js @@ -5,86 +5,93 @@ * */ -let build = { - AutoBuildTemplate: {}, - caster: true, - skillstab: sdk.skills.tabs.Lightning, - wantedskills: [sdk.skills.ChargedBolt, sdk.skills.StaticField], - usefulskills: [sdk.skills.FrozenArmor, sdk.skills.Lightning], - mercDiff: sdk.difficulty.Nightmare, - mercAct: 2, - mercAuraWanted: "Holy Freeze", - stats: [ - ["energy", 40], ["vitality", 15], ["energy", 45], - ["vitality", 20], ["energy", 50], ["strength", 15], - ["vitality", 25], ["energy", 60], ["vitality", 40], - ["strength", 35], ["vitality", "all"] - ], - skills: [ - [sdk.skills.ChargedBolt, 3, false], // charlvl 4 - [sdk.skills.IceBolt, 1], // charlvl 5 - [sdk.skills.FrozenArmor, 1], // charlvl 6 - [sdk.skills.Telekinesis, 1], // charlvl 7 - [sdk.skills.FrostNova, 1], // charlvl 8 - [sdk.skills.StaticField, 1, false], - [sdk.skills.IceBlast, 1, false], - [sdk.skills.StaticField, 4], // charlvl 10 - [sdk.skills.Teleport, 1, false], // charlvl 18 - [sdk.skills.Nova, 7], // charlvl 17 - [sdk.skills.StaticField, 6], // charlvl 20 - [sdk.skills.IceBlast, 1], // charlvl 22 - [sdk.skills.GlacialSpike, 1], // charlvl - [sdk.skills.IceBlast, 4, false], // charlvl 23 - [sdk.skills.Blizzard, 6, false], // charlvl 29 (never gets here) - [sdk.skills.ColdMastery, 1, false], // charlvl 30 (never gets here) - ], - active: function () { - return me.charlvl < CharInfo.respecOne && !me.checkSkill(sdk.skills.ColdMastery, sdk.skills.subindex.HardPoints); - }, -}; +(function (module, require) { + module.exports = (function () { + const build = { + AutoBuildTemplate: {}, + caster: true, + skillstab: sdk.skills.tabs.Lightning, + wantedskills: [sdk.skills.ChargedBolt, sdk.skills.StaticField], + usefulskills: [sdk.skills.FrozenArmor, sdk.skills.Lightning], + wantedMerc: MercData[sdk.skills.HolyFreeze], + stats: [ + ["energy", 40], ["vitality", 15], ["energy", 45], + ["vitality", 20], ["energy", 50], ["strength", 15], + ["vitality", 25], ["energy", 60], ["vitality", 40], + ["strength", 35], ["vitality", "all"] + ], + skills: [ + [sdk.skills.ChargedBolt, 3, false], // charlvl 4 + [sdk.skills.IceBolt, 1], // charlvl 5 + [sdk.skills.FrozenArmor, 1], // charlvl 6 + [sdk.skills.Telekinesis, 1], // charlvl 7 + [sdk.skills.FrostNova, 1], // charlvl 8 + [sdk.skills.StaticField, 1, false], + [sdk.skills.IceBlast, 1, false], + [sdk.skills.StaticField, 4], // charlvl 10 + [sdk.skills.Teleport, 1, false], // charlvl 18 + [sdk.skills.Nova, 7], // charlvl 17 + [sdk.skills.StaticField, 6], // charlvl 20 + [sdk.skills.IceBlast, 1], // charlvl 22 + [sdk.skills.GlacialSpike, 1], // charlvl + [sdk.skills.IceBlast, 4, false], // charlvl 23 + [sdk.skills.Blizzard, 6, false], // charlvl 29 (never gets here) + [sdk.skills.ColdMastery, 1, false], // charlvl 30 (never gets here) + ], -build.AutoBuildTemplate[1] = buildAutoBuildTempObj(() => { - Config.ScanShrines.indexOf(sdk.shrines.Combat) === -1 && Config.ScanShrines.push(sdk.shrines.Combat); - Config.FieldID.Enabled = !Misc.checkQuest(sdk.quest.id.TheSearchForCain, sdk.quest.states.Completed); - Config.FieldID.UsedSpace = 0; + active: function () { + return me.charlvl < CharInfo.respecOne && !me.checkSkill(sdk.skills.ColdMastery, sdk.skills.subindex.HardPoints); + }, + }; - Config.BeltColumn = ["hp", "hp", "hp", "hp"]; - Config.HPBuffer = 4; - Config.MPBuffer = 10; - Config.AttackSkill = [-1, sdk.skills.FireBolt, -1, sdk.skills.FireBolt, -1, 0, 0]; - Config.LowManaSkill = [0, 0]; - SetUp.belt(); -}); -build.AutoBuildTemplate[2] = buildAutoBuildTempObj(() => { - Config.ScanShrines.indexOf(sdk.shrines.Combat) === -1 && Config.ScanShrines.push(sdk.shrines.Combat); - Config.FieldID.Enabled = !Misc.checkQuest(sdk.quest.id.TheSearchForCain, sdk.quest.states.Completed); - Config.FieldID.UsedSpace = 0; + const { buildAutoBuildTempObj } = require("../../Utils/General"); + + build.AutoBuildTemplate[1] = buildAutoBuildTempObj(() => { + Config.ScanShrines.indexOf(sdk.shrines.Combat) === -1 && Config.ScanShrines.push(sdk.shrines.Combat); + Config.FieldID.Enabled = !Misc.checkQuest(sdk.quest.id.TheSearchForCain, sdk.quest.states.Completed); + Config.FieldID.UsedSpace = 0; - Config.TownHP = me.hardcore ? 0 : 35; - Config.LowManaSkill = [0, 0]; - Config.BeltColumn = ["hp", "hp", "mp", "mp"]; - Config.HPBuffer = 4; - Config.MPBuffer = 10; - if (me.checkSkill(sdk.skills.IceBlast, sdk.skills.subindex.SoftPoints)) { - Config.AttackSkill = [-1, sdk.skills.ChargedBolt, sdk.skills.IceBlast, sdk.skills.ChargedBolt, sdk.skills.IceBlast, sdk.skills.IceBlast, 0]; - } else if (me.checkSkill(sdk.skills.IceBolt, sdk.skills.subindex.SoftPoints)) { - Config.AttackSkill = [-1, sdk.skills.ChargedBolt, sdk.skills.IceBolt, sdk.skills.ChargedBolt, sdk.skills.IceBolt, sdk.skills.IceBolt, 0]; - } else { - let secondSkill = Skill.canUse(sdk.skills.FireBolt) ? sdk.skills.FireBolt : sdk.skills.Attack; - Config.AttackSkill = [-1, sdk.skills.ChargedBolt, secondSkill, sdk.skills.ChargedBolt, secondSkill, 0, 0]; - } - SetUp.belt(); -}); -build.AutoBuildTemplate[12] = buildAutoBuildTempObj(() => { - Config.HPBuffer = 4; - Config.MPBuffer = 8; - Config.AttackSkill = [-1, sdk.skills.Nova, sdk.skills.ChargedBolt, sdk.skills.Nova, sdk.skills.ChargedBolt, sdk.skills.FrostNova, sdk.skills.IceBlast]; - Config.DodgeHP = 50; - Config.DodgeRange = me.checkSkill(sdk.skills.Blizzard, sdk.skills.subindex.SoftPoints) ? 15 : 7; -}); -build.AutoBuildTemplate[24] = buildAutoBuildTempObj(() => { - if (me.checkSkill(sdk.skills.Blizzard, sdk.skills.subindex.HardPoints)) { - Config.AttackSkill = [-1, sdk.skills.Blizzard, sdk.skills.Nova, sdk.skills.Blizzard, sdk.skills.Nova, sdk.skills.Nova, sdk.skills.ChargedBolt]; - } -}); + Config.BeltColumn = ["hp", "hp", "hp", "hp"]; + Config.HPBuffer = 4; + Config.MPBuffer = 10; + Config.AttackSkill = [-1, sdk.skills.FireBolt, -1, sdk.skills.FireBolt, -1, 0, 0]; + Config.LowManaSkill = [0, 0]; + SetUp.belt(); + }); + build.AutoBuildTemplate[2] = buildAutoBuildTempObj(() => { + Config.ScanShrines.indexOf(sdk.shrines.Combat) === -1 && Config.ScanShrines.push(sdk.shrines.Combat); + Config.FieldID.Enabled = !Misc.checkQuest(sdk.quest.id.TheSearchForCain, sdk.quest.states.Completed); + Config.FieldID.UsedSpace = 0; + + Config.TownHP = me.hardcore ? 0 : 35; + Config.LowManaSkill = [0, 0]; + Config.BeltColumn = ["hp", "hp", "mp", "mp"]; + Config.HPBuffer = 4; + Config.MPBuffer = 10; + if (me.checkSkill(sdk.skills.IceBlast, sdk.skills.subindex.SoftPoints)) { + Config.AttackSkill = [-1, sdk.skills.ChargedBolt, sdk.skills.IceBlast, sdk.skills.ChargedBolt, sdk.skills.IceBlast, sdk.skills.IceBlast, 0]; + } else if (me.checkSkill(sdk.skills.IceBolt, sdk.skills.subindex.SoftPoints)) { + Config.AttackSkill = [-1, sdk.skills.ChargedBolt, sdk.skills.IceBolt, sdk.skills.ChargedBolt, sdk.skills.IceBolt, sdk.skills.IceBolt, 0]; + } else { + let secondSkill = Skill.canUse(sdk.skills.FireBolt) ? sdk.skills.FireBolt : sdk.skills.Attack; + Config.AttackSkill = [-1, sdk.skills.ChargedBolt, secondSkill, sdk.skills.ChargedBolt, secondSkill, 0, 0]; + } + SetUp.belt(); + }); + build.AutoBuildTemplate[12] = buildAutoBuildTempObj(() => { + Config.HPBuffer = 4; + Config.MPBuffer = 8; + Config.AttackSkill = [-1, sdk.skills.Nova, sdk.skills.ChargedBolt, sdk.skills.Nova, sdk.skills.ChargedBolt, sdk.skills.FrostNova, sdk.skills.IceBlast]; + Config.DodgeHP = 50; + Config.DodgeRange = me.checkSkill(sdk.skills.Blizzard, sdk.skills.subindex.SoftPoints) ? 15 : 7; + }); + build.AutoBuildTemplate[24] = buildAutoBuildTempObj(() => { + if (me.checkSkill(sdk.skills.Blizzard, sdk.skills.subindex.HardPoints)) { + Config.AttackSkill = [-1, sdk.skills.Blizzard, sdk.skills.Nova, sdk.skills.Blizzard, sdk.skills.Nova, sdk.skills.Nova, sdk.skills.ChargedBolt]; + } + }); + + return build; + })(); +})(module, require); diff --git a/libs/SoloPlay/BuildFiles/sorceress/sorceress.SteppingBuild.js b/libs/SoloPlay/BuildFiles/sorceress/sorceress.SteppingBuild.js index e20e2203..76e363ab 100644 --- a/libs/SoloPlay/BuildFiles/sorceress/sorceress.SteppingBuild.js +++ b/libs/SoloPlay/BuildFiles/sorceress/sorceress.SteppingBuild.js @@ -5,73 +5,80 @@ * */ -let build = { - AutoBuildTemplate: {}, - caster: true, - skillstab: sdk.skills.tabs.Cold, - wantedskills: [sdk.skills.Blizzard, sdk.skills.GlacialSpike, sdk.skills.ColdMastery], - usefulskills: [sdk.skills.IceBlast, sdk.skills.Warmth, sdk.skills.StaticField], - usefulStats: [sdk.stats.PassiveColdPierce, sdk.stats.PassiveColdMastery], - mercDiff: sdk.difficulty.Nightmare, - mercAct: 2, - mercAuraWanted: "Holy Freeze", - classicStats: [ - ["energy", 60], ["vitality", 40], ["strength", 55], - ["energy", 80], ["vitality", 80], ["strength", 80], - ["energy", 100], ["vitality", "all"] - ], - expansionStats: [ - ["energy", 69], ["strength", 48], ["vitality", 165], - ["strength", 61], ["vitality", 200], ["strength", 100], - ["vitality", 252], ["dexterity", "block"], ["vitality", "all"] - ], - classicSkills: [ - // Total skills at respec = 27 (assume no izual quest points) - [sdk.skills.Warmth, 1], // points left 26 - [sdk.skills.FrozenArmor, 1], // points left 25 - [sdk.skills.StaticField, 6], // points left 19 - [sdk.skills.Teleport, 4], // points left 14 - [sdk.skills.Blizzard, 3], // points left 7 - [sdk.skills.IceBlast, 8], // points left 0 - [sdk.skills.ColdMastery, 1, false], - [sdk.skills.FrozenOrb, 1, false], - [sdk.skills.Blizzard, 20, false], - [sdk.skills.IceBlast, 20, false], - [sdk.skills.ColdMastery, 5], - [sdk.skills.GlacialSpike, 20, false], - ], - expansionSkills: [ - // Total skills at respec = 27 (assume no izual quest points) - [sdk.skills.Warmth, 1], // points left 24 - [sdk.skills.FrozenArmor, 1], // points left 23 - [sdk.skills.StaticField, 1], // points left 22 - [sdk.skills.Teleport, 4], // points left 17 - [sdk.skills.Blizzard, 1], // points left 12 - [sdk.skills.IceBlast, 15], // points left 0 - [sdk.skills.ColdMastery, 1, false], - [sdk.skills.FrozenOrb, 1, false], - [sdk.skills.Blizzard, 20, false], - [sdk.skills.IceBlast, 20, false], - [sdk.skills.ColdMastery, 5], - [sdk.skills.GlacialSpike, 20, false], - ], - stats: undefined, - skills: undefined, - active: function () { - return me.charlvl > CharInfo.respecOne && me.charlvl < CharInfo.respecTwo && me.checkSkill(sdk.skills.Blizzard, sdk.skills.subindex.HardPoints) && !me.checkSkill(sdk.skills.Nova, sdk.skills.subindex.HardPoints) && !me.checkSkill(sdk.skills.FireMastery, sdk.skills.subindex.HardPoints); - }, -}; +(function (module) { + module.exports = (function () { + const build = { + caster: true, + skillstab: sdk.skills.tabs.Cold, + wantedskills: [sdk.skills.Blizzard, sdk.skills.GlacialSpike, sdk.skills.ColdMastery], + usefulskills: [sdk.skills.IceBlast, sdk.skills.Warmth, sdk.skills.StaticField], + usefulStats: [sdk.stats.PassiveColdPierce, sdk.stats.PassiveColdMastery], + wantedMerc: MercData[sdk.skills.HolyFreeze], + stats: [], + skills: [], -// Has to be set after its loaded -build.stats = me.classic ? build.classicStats : build.expansionStats; -build.skills = me.classic ? build.classicSkills : build.expansionSkills; + active: function () { + return me.charlvl > CharInfo.respecOne && me.charlvl < CharInfo.respecTwo && me.checkSkill(sdk.skills.Blizzard, sdk.skills.subindex.HardPoints) && !me.checkSkill(sdk.skills.Nova, sdk.skills.subindex.HardPoints) && !me.checkSkill(sdk.skills.FireMastery, sdk.skills.subindex.HardPoints); + }, -build.AutoBuildTemplate[1] = buildAutoBuildTempObj(() => { - Config.AttackSkill = [-1, sdk.skills.Blizzard, sdk.skills.IceBlast, sdk.skills.Blizzard, sdk.skills.IceBlast, -1, -1]; - Config.BeltColumn = ["hp", "hp", "mp", "mp"]; - Config.HPBuffer = me.expansion && !me.normal ? 2 : 5; - Config.MPBuffer = (me.expansion && !me.normal || Item.getMercEquipped(sdk.body.RightArm).prefixnum === sdk.locale.items.Insight) ? 2 : 5; - Config.SkipImmune = ["cold"]; - SetUp.belt(); -}); + AutoBuildTemplate: { + 1: { + Update: function () { + Config.AttackSkill = [-1, sdk.skills.Blizzard, sdk.skills.IceBlast, sdk.skills.Blizzard, sdk.skills.IceBlast, -1, -1]; + Config.BeltColumn = ["hp", "hp", "mp", "mp"]; + Config.HPBuffer = me.expansion && !me.normal ? 2 : 5; + Config.MPBuffer = (me.expansion && !me.normal || Item.getMercEquipped(sdk.body.RightArm).prefixnum === sdk.locale.items.Insight) ? 2 : 5; + Config.SkipImmune = ["cold"]; + SetUp.belt(); + } + } + }, + }; + + // Has to be set after its loaded + build.stats = me.classic + ? [ + ["energy", 60], ["vitality", 40], ["strength", 55], + ["energy", 80], ["vitality", 80], ["strength", 80], + ["energy", 100], ["vitality", "all"] + ] : [ + ["energy", 69], ["strength", 48], ["vitality", 165], + ["strength", 61], ["vitality", 200], ["strength", 100], + ["vitality", 252], ["dexterity", "block"], ["vitality", "all"] + ]; + + build.skills = me.classic + ? [ + // Total skills at respec = 27 (assume no izual quest points) + [sdk.skills.Warmth, 1], // points left 26 + [sdk.skills.FrozenArmor, 1], // points left 25 + [sdk.skills.StaticField, 6], // points left 19 + [sdk.skills.Teleport, 4], // points left 14 + [sdk.skills.Blizzard, 3], // points left 7 + [sdk.skills.IceBlast, 8], // points left 0 + [sdk.skills.ColdMastery, 1, false], + [sdk.skills.FrozenOrb, 1, false], + [sdk.skills.Blizzard, 20, false], + [sdk.skills.IceBlast, 20, false], + [sdk.skills.ColdMastery, 5], + [sdk.skills.GlacialSpike, 20, false], + ] : [ + // Total skills at respec = 27 (assume no izual quest points) + [sdk.skills.Warmth, 1], // points left 24 + [sdk.skills.FrozenArmor, 1], // points left 23 + [sdk.skills.StaticField, 1], // points left 22 + [sdk.skills.Teleport, 4], // points left 17 + [sdk.skills.Blizzard, 1], // points left 12 + [sdk.skills.IceBlast, 15], // points left 0 + [sdk.skills.ColdMastery, 1, false], + [sdk.skills.FrozenOrb, 1, false], + [sdk.skills.Blizzard, 20, false], + [sdk.skills.IceBlast, 20, false], + [sdk.skills.ColdMastery, 5], + [sdk.skills.GlacialSpike, 20, false], + ]; + + return build; + })(); +})(module); diff --git a/libs/SoloPlay/Config/Amazon.js b/libs/SoloPlay/Config/Amazon.js index 19c07773..ef8ab5b8 100644 --- a/libs/SoloPlay/Config/Amazon.js +++ b/libs/SoloPlay/Config/Amazon.js @@ -21,71 +21,24 @@ includeIfNotIncluded("SoloPlay/Functions/Globals.js"); SetUp.include(); - - /* Script */ SetUp.config(); - /* Chicken configuration. */ - Config.LifeChicken = me.hardcore ? 45 : 10; - Config.ManaChicken = 0; - Config.MercChicken = 0; - Config.TownHP = me.hardcore ? 0 : 35; - Config.TownMP = 0; - - /* Potions configuration. */ - Config.UseHP = me.hardcore ? 90 : 75; - Config.UseRejuvHP = me.hardcore ? 65 : 40; - Config.UseMP = me.hardcore ? 75 : 55; - Config.UseMercHP = 75; - - /* Belt configuration. */ - Config.BeltColumn = ["hp", "mp", "mp", "rv"]; - SetUp.belt(); - /* Pickit configuration. */ Config.PickRange = 40; // Config.PickitFiles.push("kolton.nip"); // Config.PickitFiles.push("LLD.nip"); /* Gambling configuration. */ - Config.Gamble = true; - Config.GambleGoldStart = 1250000; - Config.GambleGoldStop = 750000; Config.GambleItems.push("Amulet"); Config.GambleItems.push("Ring"); Config.GambleItems.push("Circlet"); Config.GambleItems.push("Coronet"); - /* AutoMule configuration. */ - Config.AutoMule.Trigger = []; - Config.AutoMule.Force = []; - Config.AutoMule.Exclude = [ - "[name] >= Elrune && [name] <= Lemrune", - ]; - - /* AutoEquip configuration. */ - Config.AutoEquip = true; - // AutoEquip setup const levelingTiers = [ // Weapon "([type] == javelin || [type] == amazonjavelin) && [quality] >= normal && [flag] != ethereal && [wsm] <= 10 && [2handed] == 0 # [itemchargedskill] >= 0 # [tier] == tierscore(item)", - "[name] == ceremonialjavelin && [quality] == unique && [flag] == ethereal # [itemchargedskill] >= 0 # [tier] == tierscore(item)", - // Helmet - "([type] == circlet || [type] == helm) && ([quality] >= magic || [flag] == runeword) && [flag] != ethereal # [itemchargedskill] >= 0 # [tier] == tierscore(item)", - // Belt - "[type] == belt && [quality] >= magic && [flag] != ethereal # [itemchargedskill] >= 0 # [tier] == tierscore(item)", - "me.normal && [type] == belt && [quality] >= lowquality && [flag] != ethereal # [itemchargedskill] >= 0 # [tier] == tierscore(item)", - // Boots - "[type] == boots && [quality] >= magic && [flag] != ethereal # [itemchargedskill] >= 0 # [tier] == tierscore(item)", - // Armor - "[type] == armor && ([quality] >= magic || [flag] == runeword) && [flag] != ethereal # [itemchargedskill] >= 0 # [tier] == tierscore(item)", - // Gloves - "[type] == gloves && [quality] >= magic && [flag] != ethereal # [itemchargedskill] >= 0 # [tier] == tierscore(item)", - // Amulet - "[type] == amulet && [quality] >= magic # [itemchargedskill] >= 0 # [tier] == tierscore(item)", - // Rings - "[type] == ring && [quality] >= magic # [itemchargedskill] >= 0 # [tier] == tierscore(item)", + // "[name] == ceremonialjavelin && [quality] == unique && [flag] == ethereal # [itemchargedskill] >= 0 # [tier] == tierscore(item)", // too many issues with eth titans ]; const expansionTiers = [ @@ -102,6 +55,7 @@ if (["Witchyzon", "Wfzon", "Faithbowzon"].indexOf(SetUp.currentBuild) === -1) { NTIP.addLine("[type] == shield && ([quality] >= magic || [flag] == runeword) && [flag] != ethereal # [itemchargedskill] >= 0 # [tier] == tierscore(item)"); + NTIP.addLine("([type] == shield) && [quality] >= normal && [flag] != ethereal # [itemchargedskill] >= 0 && ([sockets] == 1 || [sockets] == 2) # [tier] == tierscore(item)"); NTIP.addLine("me.classic && [type] == shield && [quality] >= normal # [itemchargedskill] >= 0 # [tier] == tierscore(item)"); } @@ -117,11 +71,6 @@ Config.LightningFuryDelay = 10; // Lightning fury interval in seconds. LF is treated as timed skill. Config.SummonValkyrie = true; // Summon Valkyrie - /* Gear */ - let finalGear = Check.finalBuild().finalGear; - !!finalGear && NTIP.buildList(finalGear); - NTIP.buildFinalGear(finalGear); - Config.imbueables = [ { name: sdk.items.MaidenJavelin, condition: () => me.normal && me.expansion }, { name: sdk.items.CeremonialJavelin, condition: () => !me.normal && (me.charlvl < 48 || me.trueStr < 107 || me.trueDex < 151) && me.expansion }, diff --git a/libs/SoloPlay/Config/Assassin.js b/libs/SoloPlay/Config/Assassin.js index bff3e3f6..025eaa9f 100644 --- a/libs/SoloPlay/Config/Assassin.js +++ b/libs/SoloPlay/Config/Assassin.js @@ -19,27 +19,8 @@ includeIfNotIncluded("SoloPlay/Functions/Globals.js"); SetUp.include(); - - /* Script */ SetUp.config(); - /* Chicken configuration. */ - Config.LifeChicken = me.hardcore ? 45 : 10; - Config.ManaChicken = 0; - Config.MercChicken = 0; - Config.TownHP = me.hardcore ? 0 : 35; - Config.TownMP = 0; - - /* Potions configuration. */ - Config.UseHP = me.hardcore ? 90 : 75; - Config.UseRejuvHP = me.hardcore ? 65 : 40; - Config.UseMP = me.hardcore ? 75 : 55; - Config.UseMercHP = 75; - - /* Belt configuration. */ - Config.BeltColumn = ["hp", "mp", "mp", "rv"]; - SetUp.belt(); - /* Pickit configuration. */ Config.PickRange = 40; // Config.PickitFiles.push("kolton.nip"); @@ -47,20 +28,10 @@ NTIP.addLine("[name] >= VexRune && [name] <= ZodRune"); /* Gambling configuration. */ - Config.Gamble = true; - Config.GambleGoldStart = 2000000; - Config.GambleGoldStop = 750000; Config.GambleItems.push("Amulet"); Config.GambleItems.push("Ring"); - //Config.GambleItems.push("Circlet"); - //Config.GambleItems.push("Coronet"); - - /* AutoMule configuration. */ - Config.AutoMule.Trigger = []; - Config.AutoMule.Force = []; - Config.AutoMule.Exclude = [ - "[name] >= Elrune && [name] <= Lemrune", - ]; + // Config.GambleItems.push("Circlet"); + // Config.GambleItems.push("Coronet"); /* AutoEquip configuration. */ Config.AutoEquip = true; @@ -69,23 +40,8 @@ const levelingTiers = [ // Weapon "([type] == knife || [type] == sword && [flag] == runeword || ([type] == handtohand || [type] == assassinclaw) && [quality] >= magic) && [flag] != ethereal && [2handed] == 0 # [itemchargedskill] >= 0 # [tier] == tierscore(item)", - // Helmet - "([type] == helm || [type] == circlet) && ([quality] >= magic || [flag] == runeword) && [flag] != ethereal # [itemchargedskill] >= 0 # [tier] == tierscore(item)", - // Belt - "[type] == belt && [quality] >= magic && [flag] != ethereal # [itemchargedskill] >= 0 # [tier] == tierscore(item)", - "me.normal && [type] == belt && [quality] >= lowquality && [flag] != ethereal # [itemchargedskill] >= 0 # [tier] == tierscore(item)", - // Boots - "[type] == boots && [quality] >= magic && [flag] != ethereal # [itemchargedskill] >= 0 # [tier] == tierscore(item)", - // Srmor - "[type] == armor && ([quality] >= magic || [flag] == runeword) && [flag] != ethereal # [itemchargedskill] >= 0 # [tier] == tierscore(item)", // Shield "[type] == shield && ([quality] >= magic || [flag] == runeword) && [flag] != ethereal # [itemchargedskill] >= 0 # [tier] == tierscore(item)", - // Gloves - "[type] == gloves && [quality] >= magic && [flag] != ethereal # [itemchargedskill] >= 0 # [tier] == tierscore(item)", - // Amulet - "[type] == amulet && [quality] >= magic # [itemchargedskill] >= 0 # [tier] == tierscore(item)", - // Rings - "[type] == ring && [quality] >= magic # [itemchargedskill] >= 0 # [tier] == tierscore(item)", // Switch "[type] == wand && [quality] >= normal # [itemchargedskill] == 91 # [secondarytier] == 50000 + chargeditemscore(item, 91)", // Lower Resist charged wand // Charms @@ -93,6 +49,8 @@ "[name] == smallcharm && [quality] == magic # [itemmagicbonus] >= 1 # [invoquantity] == 2 && [charmtier] == charmscore(item)", "[name] == smallcharm && [quality] == magic # # [invoquantity] == 2 && [charmtier] == charmscore(item)", "[name] == grandcharm && [quality] == magic # # [invoquantity] == 2 && [charmtier] == charmscore(item)", + // non runeword white items + "([type] == shield) && [quality] >= normal && [flag] != ethereal # [itemchargedskill] >= 0 && ([sockets] == 1 || [sockets] == 2) # [tier] == tierscore(item)", ]; NTIP.buildList(levelingTiers); @@ -122,11 +80,6 @@ Config.DodgeRange = 10; Config.DodgeHP = 75; - /* Gear */ - let finalGear = Check.finalBuild().finalGear; - !!finalGear && NTIP.buildList(finalGear); - NTIP.buildFinalGear(finalGear); - Config.imbueables = [ { name: sdk.items.Claws, condition: () => (me.normal) }, { name: sdk.items.HandScythe, condition: () => (!me.normal && Item.getEquipped(sdk.body.RightArm).tier < 777 && (me.trueStr < 79 || me.trueDex < 79)) }, diff --git a/libs/SoloPlay/Config/Barbarian.js b/libs/SoloPlay/Config/Barbarian.js index ab59f1ae..e7401307 100644 --- a/libs/SoloPlay/Config/Barbarian.js +++ b/libs/SoloPlay/Config/Barbarian.js @@ -21,27 +21,8 @@ includeIfNotIncluded("SoloPlay/Functions/Globals.js"); SetUp.include(); - - /* Script */ SetUp.config(); - /* Chicken configuration. */ - Config.LifeChicken = me.hardcore ? 45 : 10; - Config.ManaChicken = 0; - Config.MercChicken = 0; - Config.TownHP = me.hardcore ? 0 : 35; - Config.TownMP = 0; - - /* Potions configuration. */ - Config.UseHP = me.hardcore ? 90 : 75; - Config.UseRejuvHP = me.hardcore ? 65 : 40; - Config.UseMP = me.hardcore ? 75 : 45; - Config.UseMercHP = 75; - - /* Belt configuration. */ - Config.BeltColumn = ["hp", "mp", "mp", "rv"]; - SetUp.belt(); - /* Pickit configuration. */ Config.PickRange = 40; Config.FieldID.UsedSpace = 80; // how much space has been used before trying to field id, set to 0 to id after every item picked @@ -49,19 +30,9 @@ // Config.PickitFiles.push("LLD.nip"); /* Gambling configuration. */ - Config.Gamble = true; - Config.GambleGoldStart = 1250000; - Config.GambleGoldStop = 750000; Config.GambleItems.push("Amulet"); Config.GambleItems.push("Ring"); - /* AutoMule configuration. */ - Config.AutoMule.Trigger = []; - Config.AutoMule.Force = []; - Config.AutoMule.Exclude = [ - "[name] >= Elrune && [name] <= Lemrune", - ]; - /* AutoEquip configuration. */ Config.AutoEquip = true; @@ -72,21 +43,8 @@ "[type] == sword && ([quality] >= magic || [flag] == runeword) && [flag] != ethereal && [wsm] <= 10 # [itemchargedskill] >= 0 # [tier] == tierscore(item)", "[name] == phaseblade && [quality] == unique && [flag] == ethereal # [enhanceddamage] >= 100 && [ias] == 30 && [magicdamagereduction] >= 7 # [tier] == tierscore(item)", // Helmet - "([type] == helm || [type] == primalhelm) && ([quality] >= magic || [flag] == runeword) && [flag] != ethereal # [itemchargedskill] >= 0 # [tier] == tierscore(item)", - "[type] == primalhelm && [quality] >= normal && [flag] != ethereal # [itemchargedskill] >= 0 && [sockets] == 1 # [tier] == tierscore(item)", - // Belt - "[type] == belt && [quality] >= magic && [flag] != ethereal # [itemchargedskill] >= 0 # [tier] == tierscore(item)", - "me.normal && [type] == belt && [quality] >= lowquality && [flag] != ethereal # [itemchargedskill] >= 0 # [tier] == tierscore(item)", - // Boots - "[type] == boots && [quality] >= magic && [flag] != ethereal # [itemchargedskill] >= 0 # [tier] == tierscore(item)", - // Armor - "[type] == armor && ([quality] >= magic || [flag] == runeword) && [flag] != ethereal # [itemchargedskill] >= 0 # [tier] == tierscore(item)", - // Gloves - "[type] == gloves && [quality] >= magic && [flag] != ethereal # [itemchargedskill] >= 0 # [tier] == tierscore(item)", - // Amulet - "[type] == amulet && [quality] >= magic # [itemchargedskill] >= 0 # [tier] == tierscore(item)", - // Rings - "[type] == ring && [quality] >= magic # [itemchargedskill] >= 0 # [tier] == tierscore(item)", + "([type] == primalhelm) && ([quality] >= magic || [flag] == runeword) && [flag] != ethereal # [itemchargedskill] >= 0 # [tier] == tierscore(item)", + "([type] == primalhelm) && [quality] >= normal && [flag] != ethereal # [itemchargedskill] >= 0 && ([sockets] == 1 || [sockets] == 3) # [tier] == tierscore(item)", ]; const expansionTiers = [ @@ -110,11 +68,6 @@ Config.FindItem = true; // Use Find Item skill on corpses after clearing. Config.FindItemSwitch = false; // Switch to non-primary slot when using Find Item skills - /* Gear */ - let finalGear = Check.finalBuild().finalGear; - !!finalGear && NTIP.buildList(finalGear); - NTIP.buildFinalGear(finalGear); - Config.imbueables = [ { name: sdk.items.AvengerGuard, condition: () => (me.normal && me.expansion) }, { name: sdk.items.SlayerGuard, condition: () => (!me.normal && me.trueStr >= 118 && me.expansion) }, diff --git a/libs/SoloPlay/Config/Druid.js b/libs/SoloPlay/Config/Druid.js index 0e3085aa..e97dfc46 100644 --- a/libs/SoloPlay/Config/Druid.js +++ b/libs/SoloPlay/Config/Druid.js @@ -21,27 +21,8 @@ includeIfNotIncluded("SoloPlay/Functions/Globals.js"); SetUp.include(); - - /* Script */ SetUp.config(); - /* Chicken configuration. */ - Config.LifeChicken = me.hardcore ? 45 : 10; - Config.ManaChicken = 0; - Config.MercChicken = 0; - Config.TownHP = me.hardcore ? 0 : 35; - Config.TownMP = 0; - - /* Potions configuration. */ - Config.UseHP = me.hardcore ? 90 : 75; - Config.UseRejuvHP = me.hardcore ? 65 : 40; - Config.UseMP = me.hardcore ? 75 : 55; - Config.UseMercHP = 75; - - /* Belt configuration. */ - Config.BeltColumn = ["hp", "mp", "mp", "rv"]; - SetUp.belt(); - /* Pickit configuration. */ Config.PickRange = 40; // Config.PickitFiles.push("kolton.nip"); @@ -49,47 +30,20 @@ NTIP.addLine("[name] >= VexRune && [name] <= ZodRune"); /* Gambling configuration. */ - Config.Gamble = true; - Config.GambleGoldStart = 2000000; - Config.GambleGoldStop = 750000; Config.GambleItems.push("amulet"); Config.GambleItems.push("ring"); Config.GambleItems.push("circlet"); Config.GambleItems.push("coronet"); - /* AutoMule configuration. */ - Config.AutoMule.Trigger = []; - Config.AutoMule.Force = []; - Config.AutoMule.Exclude = [ - "[name] >= Elrune && [name] <= Lemrune", - ]; - - /* AutoEquip configuration. */ - Config.AutoEquip = true; - // AutoEquip setup const levelingTiers = [ // Weapon "([type] == wand || [type] == sword || [type] == mace || [type] == knife) && ([quality] >= magic || [flag] == runeword) && [flag] != ethereal && [2handed] == 0 # [itemchargedskill] >= 0 # [tier] == tierscore(item)", // Helmet - "([type] == helm || [type] == circlet || [type] == pelt) && ([quality] >= magic || [flag] == runeword) && [flag] != ethereal # [itemchargedskill] >= 0 # [tier] == tierscore(item)", - "[type] == pelt && [quality] >= normal && [flag] != ethereal # [itemchargedskill] >= 0 && [sockets] == 1 # [tier] == tierscore(item)", - // Belt - "[type] == belt && [quality] >= magic && [flag] != ethereal # [itemchargedskill] >= 0 # [tier] == tierscore(item)", - "me.normal && [type] == belt && [quality] >= lowquality && [flag] != ethereal # [itemchargedskill] >= 0 # [tier] == tierscore(item)", - // Boots - "[type] == boots && [quality] >= magic && [flag] != ethereal # [itemchargedskill] >= 0 # [tier] == tierscore(item)", - // Armor - "[type] == armor && ([quality] >= magic || [flag] == runeword) && [flag] != ethereal # [itemchargedskill] >= 0 # [tier] == tierscore(item)", + "([type] == pelt) && ([quality] >= magic || [flag] == runeword) && [flag] != ethereal # [itemchargedskill] >= 0 # [tier] == tierscore(item)", + "([type] == pelt) && [quality] >= normal && [flag] != ethereal # [itemchargedskill] >= 0 && ([sockets] == 1 || [sockets] == 3) # [tier] == tierscore(item)", // Shield "[type] == shield && ([quality] >= magic || [flag] == runeword) && [flag] != ethereal # [itemchargedskill] >= 0 # [tier] == tierscore(item)", - // Gloves - "[type] == gloves && [quality] >= magic && [flag] != ethereal # [itemchargedskill] >= 0 # [tier] == tierscore(item)", - // Amulet - "[type] == amulet && [quality] >= magic # [itemchargedskill] >= 0 # [tier] == tierscore(item)", - // Rings - "[type] == ring && [quality] >= magic # [itemchargedskill] >= 0 # [tier] == tierscore(item)", - // Switch // Charms "[name] == smallcharm && [quality] == magic # [maxhp] >= 1 # [invoquantity] == 2 && [charmtier] == charmscore(item)", "[name] == smallcharm && [quality] == magic # [itemmagicbonus] >= 1 # [invoquantity] == 2 && [charmtier] == charmscore(item)", @@ -129,11 +83,6 @@ Config.SummonSpirit = 0; // 0 = disabled, 1 / "Oak Sage", 2 / "Heart of Wolverine", 3 / "Spirit of Barbs" Config.SummonAnimal = 0; // 0 = disabled, 1 or "Spirit Wolf" = summon spirit wolf, 2 or "Dire Wolf" = summon dire wolf, 3 or "Grizzly" = summon grizzly - /* Gear */ - let finalGear = Check.finalBuild().finalGear; - !!finalGear && NTIP.buildList(finalGear); - NTIP.buildFinalGear(finalGear); - Config.imbueables = [ { name: sdk.items.SpiritMask, condition: () => (me.normal) }, { name: sdk.items.TotemicMask, condition: () => (!me.normal && Item.getEquipped(sdk.body.Head).tier < 100000 && (me.charlvl < 66 || me.trueStr < 118)) }, diff --git a/libs/SoloPlay/Config/Necromancer.js b/libs/SoloPlay/Config/Necromancer.js index 6b7e50c0..351c8c44 100644 --- a/libs/SoloPlay/Config/Necromancer.js +++ b/libs/SoloPlay/Config/Necromancer.js @@ -20,27 +20,10 @@ includeIfNotIncluded("SoloPlay/Functions/Globals.js"); SetUp.include(); - - /* Script */ SetUp.config(); - /* Chicken configuration. */ - Config.LifeChicken = me.hardcore ? 45 : 10; - Config.ManaChicken = 0; - Config.MercChicken = 0; + /* Necro specific Chicken configuration. */ Config.IronGolemChicken = 30; - Config.TownHP = me.hardcore ? 0 : 35; - Config.TownMP = 0; - - /* Potions configuration. */ - Config.UseHP = me.hardcore ? 90 : 75; - Config.UseRejuvHP = me.hardcore ? 65 : 40; - Config.UseMP = me.hardcore ? 75 : 55; - Config.UseMercHP = 75; - - /* Belt configuration. */ - Config.BeltColumn = ["hp", "mp", "mp", "rv"]; - SetUp.belt(); /* Pickit configuration. */ Config.PickRange = 40; @@ -48,48 +31,22 @@ // Config.PickitFiles.push("LLD.nip"); /* Gambling configuration. */ - Config.Gamble = true; - Config.GambleGoldStart = 2000000; - Config.GambleGoldStop = 750000; Config.GambleItems.push("Amulet"); Config.GambleItems.push("Ring"); Config.GambleItems.push("Circlet"); Config.GambleItems.push("Coronet"); - /* AutoMule configuration. */ - Config.AutoMule.Trigger = []; - Config.AutoMule.Force = []; - Config.AutoMule.Exclude = [ - "[name] >= Elrune && [name] <= Lemrune", - ]; - - /* AutoEquip configuration. */ - Config.AutoEquip = true; - // AutoEquip setup const levelingTiers = [ // Weapon "([type] == wand || [type] == sword || [type] == knife) && ([quality] >= magic || [flag] == runeword) && [flag] != ethereal && [2handed] == 0 # [itemchargedskill] >= 0 # [tier] == tierscore(item)", "[type] == wand && [quality] >= normal && [flag] != ethereal && [2handed] == 0 # [itemchargedskill] >= 0 && [sockets] != 2 # [tier] == tierscore(item)", - // Helmet - "([type] == helm || [type] == circlet) && ([quality] >= magic || [flag] == runeword) && [flag] != ethereal # [itemchargedskill] >= 0 # [tier] == tierscore(item)", - // Belt - "[type] == belt && [quality] >= magic && [flag] != ethereal # [itemchargedskill] >= 0 # [tier] == tierscore(item)", - "me.normal && [type] == belt && [quality] >= lowquality && [flag] != ethereal # [itemchargedskill] >= 0 # [tier] == tierscore(item)", - // Boots - "[type] == boots && [quality] >= magic && [flag] != ethereal # [itemchargedskill] >= 0 # [tier] == tierscore(item)", - // Armor - "[type] == armor && ([quality] >= magic || [flag] == runeword) && [flag] != ethereal # [itemchargedskill] >= 0 # [tier] == tierscore(item)", // Shield "([type] == shield && ([quality] >= magic || [flag] == runeword) || [type] == voodooheads) && [flag] != ethereal # [itemchargedskill] >= 0 # [tier] == tierscore(item)", "[type] == voodooheads && [quality] >= normal && [flag] != ethereal # [itemchargedskill] >= 0 && [sockets] == 1 # [tier] == tierscore(item)", "me.classic && [type] == shield && [quality] >= normal # [itemchargedskill] >= 0 # [tier] == tierscore(item)", - // Gloves - "[type] == gloves && [quality] >= magic && [flag] != ethereal # [itemchargedskill] >= 0 # [tier] == tierscore(item)", - // Amulet - "[type] == amulet && [quality] >= magic # [itemchargedskill] >= 0 # [tier] == tierscore(item)", - // Rings - "[type] == ring && [quality] >= magic # [itemchargedskill] >= 0 # [tier] == tierscore(item)", + // non runeword white items + "([type] == shield) && [quality] >= normal && [flag] != ethereal && [flag] != runeword # [itemchargedskill] >= 0 && ([sockets] == 1 || [sockets] == 2) # [tier] == tierscore(item)", ]; const expansionTiers = [ @@ -127,12 +84,11 @@ /* Skill Specific */ Config.PoisonNovaDelay = 1; // In Seconds - Config.ExplodeCorpses = me.checkSkill(sdk.skills.CorpseExplosion, sdk.skills.subindex.HardPoints) ? sdk.skills.CorpseExplosion : me.checkSkill(sdk.skills.PoisonExplosion, sdk.skills.subindex.HardPoints) ? sdk.skills.PoisonExplosion : 0; - - /* Gear */ - let finalGear = Check.finalBuild().finalGear; - !!finalGear && NTIP.buildList(finalGear); - NTIP.buildFinalGear(finalGear); + Config.ExplodeCorpses = me.checkSkill(sdk.skills.CorpseExplosion, sdk.skills.subindex.HardPoints) + ? sdk.skills.CorpseExplosion + : me.checkSkill(sdk.skills.PoisonExplosion, sdk.skills.subindex.HardPoints) + ? sdk.skills.PoisonExplosion + : 0; Config.imbueables = [ { name: sdk.items.DemonHead, condition: () => (me.normal && me.expansion) }, diff --git a/libs/SoloPlay/Config/Paladin.js b/libs/SoloPlay/Config/Paladin.js index 72cbff72..c54f4e31 100644 --- a/libs/SoloPlay/Config/Paladin.js +++ b/libs/SoloPlay/Config/Paladin.js @@ -1,7 +1,6 @@ /** * @filename Paladin.js * @author theBGuy -* @credit isid0re * @desc Config Settings for SoloPlay Paladin * * @FinalBuild @@ -29,77 +28,38 @@ includeIfNotIncluded("SoloPlay/Functions/Globals.js"); SetUp.include(); - - /* Script */ SetUp.config(); - /* Chicken configuration. */ - Config.LifeChicken = me.hardcore ? 45 : 10; - Config.ManaChicken = 0; - Config.MercChicken = 0; - Config.TownHP = me.hardcore ? 0 : 35; - Config.TownMP = 0; - - /* Potions configuration. */ - Config.UseHP = me.hardcore ? 90 : 75; - Config.UseRejuvHP = me.hardcore ? 65 : 40; - Config.UseMP = me.hardcore ? 75 : 55; - Config.UseMercHP = 75; - - /* Belt configuration. */ - Config.BeltColumn = ["hp", "mp", "mp", "rv"]; - SetUp.belt(); - /* Pickit configuration. */ Config.PickRange = 40; // Config.PickitFiles.push("kolton.nip"); // Config.PickitFiles.push("LLD.nip"); /* Gambling configuration. */ - Config.Gamble = true; - Config.GambleGoldStart = 2000000; - Config.GambleGoldStop = 750000; Config.GambleItems.push("Amulet"); Config.GambleItems.push("Ring"); Config.GambleItems.push("Circlet"); Config.GambleItems.push("Coronet"); - /* AutoMule configuration. */ - Config.AutoMule.Trigger = []; - Config.AutoMule.Force = []; - Config.AutoMule.Exclude = [ - "[name] >= Elrune && [name] <= Lemrune", - ]; - - /* AutoEquip configuration. */ - Config.AutoEquip = true; + let weapons = [ + sdk.items.type.Scepter, sdk.items.type.Mace, + sdk.items.type.Sword, sdk.items.type.Knife, sdk.items.type.Axe, + sdk.items.type.Wand, sdk.items.type.Hammer, sdk.items.type.Club + ].map(el => "[type] == " + el).join(" || "); // AutoEquip setup const levelingTiers = [ // Weapon - "([type] == scepter || [type] == mace || [type] == sword || [type] == knife) && ([quality] >= magic || [flag] == runeword) && [flag] != ethereal && [2handed] == 0 # [itemchargedskill] >= 0 # [tier] == tierscore(item)", - "[type] == scepter && [quality] >= normal && [flag] != ethereal # [itemchargedskill] >= 0 && [sockets] == 1 # [tier] == tierscore(item)", - // Helmet - "([type] == helm || [type] == circlet) && ([quality] >= magic || [flag] == runeword) && [flag] != ethereal # [itemchargedskill] >= 0 # [tier] == tierscore(item)", - // Belt - "[type] == belt && [quality] >= magic && [flag] != ethereal # [itemchargedskill] >= 0 # [tier] == tierscore(item)", - "me.normal && [type] == belt && [quality] >= lowquality && [flag] != ethereal # [itemchargedskill] >= 0 # [tier] == tierscore(item)", - // Boots - "[type] == boots && [quality] >= magic && [flag] != ethereal # [itemchargedskill] >= 0 # [tier] == tierscore(item)", - // Armor - "[type] == armor && ([quality] >= magic || [flag] == runeword) && [flag] != ethereal # [itemchargedskill] >= 0 # [tier] == tierscore(item)", + "(" + weapons + ") && ([quality] >= magic || [flag] == runeword) && [flag] != ethereal && [2handed] == 0 # [itemchargedskill] >= 0 # [tier] == tierscore(item)", // Shield "([type] == shield || [type] == auricshields) && ([quality] >= magic || [flag] == runeword) && [flag] != ethereal # [itemchargedskill] >= 0 # [tier] == tierscore(item)", - "[type] == auricshields && [quality] >= normal && [flag] != ethereal # [itemchargedskill] >= 0 && [sockets] == 1 # [tier] == tierscore(item)", "me.classic && [type] == shield && [quality] >= normal # [itemchargedskill] >= 0 # [tier] == tierscore(item)", - // Gloves - "[type] == gloves && [quality] >= magic && [flag] != ethereal # [itemchargedskill] >= 0 # [tier] == tierscore(item)", - // Amulet - "[type] == amulet && [quality] >= magic # [itemchargedskill] >= 0 # [tier] == tierscore(item)", - // Rings - "[type] == ring && [quality] >= magic # [itemchargedskill] >= 0 # [tier] == tierscore(item)", + // non runeword white items + "(" + weapons + ") && [quality] >= normal && [flag] != ethereal && [flag] != runeword && [2handed] == 0 # [itemchargedskill] >= 0 && ([sockets] == 1 || [sockets] == 3) # [tier] == tierscore(item)", + "([type] == shield || [type] == auricshields) && [quality] >= normal && [flag] != ethereal && [flag] != runeword # [itemchargedskill] >= 0 && ([sockets] == 1 || [sockets] == 2) # [tier] == tierscore(item)", ]; + let miscCharmQuantity = me.charlvl < 40 ? 6 : 3; const expansionTiers = [ // Switch "[type] == wand && [quality] >= normal # [itemchargedskill] == 72 # [secondarytier] == 25000", // Weaken charged wand @@ -107,11 +67,11 @@ // Charms "[name] == smallcharm && [quality] == magic # [maxhp] >= 1 # [invoquantity] == 2 && [charmtier] == charmscore(item)", "[name] == smallcharm && [quality] == magic # [itemmagicbonus] >= 1 # [invoquantity] == 2 && [charmtier] == charmscore(item)", - "[name] == smallcharm && [quality] == magic # # [invoquantity] == 2 && [charmtier] == charmscore(item)", - "me.charlvl < 40 && [name] == smallcharm && [quality] == magic ## [invoquantity] == 4 && [charmtier] == charmscore(item)", + "[name] == smallcharm && [quality] == magic # # [invoquantity] == " + miscCharmQuantity + " && [charmtier] == charmscore(item)", "[name] == grandcharm && [quality] == magic # # [invoquantity] == 2 && [charmtier] == charmscore(item)", ]; + /* Gear */ NTIP.buildList(levelingTiers); me.expansion && NTIP.buildList(expansionTiers); @@ -129,11 +89,6 @@ Config.Charge = true; Config.Redemption = [45, 25]; - /* Gear */ - let finalGear = Check.finalBuild().finalGear; - !!finalGear && NTIP.buildList(finalGear); - NTIP.buildFinalGear(finalGear); - // Maybe add auric shield? Config.imbueables = [ { name: sdk.items.WarScepter, condition: () => me.normal }, @@ -511,7 +466,7 @@ } // Merc Fortitude - if (Item.getMercEquipped(sdk.body.Armor).prefixnum !== sdk.locale.items.Fortitude && ["Hammerdin", "Smiter"].indexOf(SetUp.finalBuild) > -1) { + if (Item.getMercEquipped(sdk.body.Armor).prefixnum !== sdk.locale.items.Fortitude && ["Hammerdin", "Smiter"].includes(SetUp.finalBuild)) { includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/MercFortitude.js"); } @@ -530,6 +485,24 @@ includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/Stealth.js"); } + /* if (SetUp.currentBuild === "Start") { + let { maxStr, maxDex } = Check.currentBuild(); + let basic = "[flag] != ethereal && [quality] >= normal && [quality] <= superior"; + let myStatsReq = "[strreq] <= " + maxStr + " && [dexreq] <= " + maxDex; + let statsReq = "[sockets] == 2"; + let quantityReq = "[maxquantity] == 1"; + // add Steel RW + NTIP.buildList([ + "[name] == TirRune # # [maxquantity] == 2", + "[name] == ElRune # # [maxquantity] == 2", + "([type] == sword || [type] == mace || [type] == axe) && " + basic + " && " + myStatsReq + " # " + statsReq + " # " + quantityReq, + ]); + + // need to make runewords able to process types, maybe a hacky method of just taking a nip line? + // or could create a base item data module and loop through whatever meets the reqs but that feels ugly too + Config.KeepRunewords.push("([type] == sword || [type] == mace || [type] == axe) # [enhanceddamage] >= 20 && [ias] >= 25"); + } */ + SoloWants.buildList(); break; diff --git a/libs/SoloPlay/Config/Sorceress.js b/libs/SoloPlay/Config/Sorceress.js index 9373924a..7a175827 100644 --- a/libs/SoloPlay/Config/Sorceress.js +++ b/libs/SoloPlay/Config/Sorceress.js @@ -22,52 +22,21 @@ includeIfNotIncluded("SoloPlay/Functions/Globals.js"); SetUp.include(); - - /* Script */ SetUp.config(); - /* Chicken configuration. */ - Config.LifeChicken = me.hardcore ? 45 : 10; - Config.ManaChicken = 0; - Config.MercChicken = 0; - Config.TownHP = me.hardcore ? 0 : 35; - Config.TownMP = 0; - - /* Potions configuration. */ - Config.UseHP = me.hardcore ? 90 : 80; - Config.UseRejuvHP = me.hardcore ? 65 : 50; - Config.UseMP = me.hardcore ? 75 : 65; - Config.UseMercHP = 75; - - /* Belt configuration. */ - Config.BeltColumn = ["hp", "mp", "mp", "rv"]; - SetUp.belt(); - /* Pickit configuration. */ Config.PickRange = 40; // Config.PickitFiles.push("kolton.nip"); // Config.PickitFiles.push("test.nip"); /* Gambling configuration. */ - Config.Gamble = true; - Config.GambleGoldStart = 1250000; - Config.GambleGoldStop = 750000; // TODO: should gambling be re-written to try and gamble for our current lowest tier'd item // for example if our gloves are the lowest tier then only gamble gloves or maybe just make the others conditional like why include // gambling for rings/ammys if we have our end game one Config.GambleItems.push("Amulet"); Config.GambleItems.push("Ring"); - Config.GambleItems.push("Circlet") && Config.GambleItems.push("Coronet"); - - /* AutoMule configuration. */ - Config.AutoMule.Trigger = []; - Config.AutoMule.Force = []; - Config.AutoMule.Exclude = [ - "[name] >= Elrune && [name] <= Lemrune", - ]; - - /* AutoEquip configuration. */ - Config.AutoEquip = true; + Config.GambleItems.push("Circlet"); + Config.GambleItems.push("Coronet"); // AutoEquip setup const levelingTiers = [ @@ -75,29 +44,16 @@ "me.normal && [type] == orb && [quality] >= normal && [flag] != ethereal # [itemchargedskill] >= 0 # [tier] == tierscore(item)", "me.charlvl > 1 && ([type] == orb || [type] == wand || [type] == sword || [type] == knife) && ([quality] >= magic || [flag] == runeword) && [flag] != ethereal && [2handed] == 0 # [itemchargedskill] >= 0 # [tier] == tierscore(item)", "me.classic && [type] == staff && [quality] >= magic # [itemchargedskill] >= 0 # [tier] == tierscore(item)", - // Helmet - "([type] == helm || [type] == circlet) && ([quality] >= magic || [flag] == runeword) && [flag] != ethereal # [itemchargedskill] >= 0 # [tier] == tierscore(item)", - // Belt - "[type] == belt && [quality] >= magic && [flag] != ethereal # [itemchargedskill] >= 0 # [tier] == tierscore(item)", - "me.normal && [type] == belt && [quality] >= lowquality && [flag] != ethereal # [itemchargedskill] >= 0 # [tier] == tierscore(item)", - // Boots - "[type] == boots && [quality] >= magic && [flag] != ethereal # [itemchargedskill] >= 0 # [tier] == tierscore(item)", - // Armor - "[type] == armor && ([quality] >= magic || [flag] == runeword) && [flag] != ethereal # [itemchargedskill] >= 0 # [tier] == tierscore(item)", // Shield "[type] == shield && ([quality] >= magic || [flag] == runeword) && [flag] != ethereal # [itemchargedskill] >= 0 # [tier] == tierscore(item)", "me.classic && [type] == shield && [quality] >= normal # [itemchargedskill] >= 0 # [tier] == tierscore(item)", - // Gloves - "[type] == gloves && [quality] >= magic && [flag] != ethereal # [itemchargedskill] >= 0 # [tier] == tierscore(item)", - // Amulet - "[type] == amulet && [quality] >= magic # [itemchargedskill] >= 0 # [tier] == tierscore(item)", - // Rings - "[type] == ring && [quality] >= magic # [itemchargedskill] >= 0 # [tier] == tierscore(item)", + // non runeword white items + "([type] == shield) && [quality] >= normal && [flag] != ethereal && [flag] != runeword # [itemchargedskill] >= 0 && ([sockets] == 1 || [sockets] == 2) # [tier] == tierscore(item)", ]; const expansionTiers = [ // Switch - "[type] == wand && [quality] >= Normal # [itemchargedskill] == 72 # [secondarytier] == 25000", // Weaken charged wand + "[type] == wand && [quality] >= Normal # [itemchargedskill] == 72 # [secondarytier] == 25000", // Weaken charged wand "[type] == wand && [quality] >= Normal # [itemchargedskill] == 91 # [secondarytier] == 50000 + chargeditemscore(item, 91)", // Lower Resist charged wand // Charms "[name] == smallcharm && [quality] == magic # [maxhp] >= 1 # [invoquantity] == 2 && [charmtier] == charmscore(item)", @@ -135,10 +91,6 @@ /* Gear */ NTIP.buildList(levelingTiers); me.expansion && NTIP.buildList(expansionTiers); - let finalGear = Check.finalBuild().finalGear; - NTIP.buildList(finalGear); - NTIP.buildFinalGear(finalGear); - Config.imbueables = [ { name: sdk.items.JaredsStone, condition: () => (me.normal && me.expansion) }, diff --git a/libs/SoloPlay/Functions/AutoBuildOverrides.js b/libs/SoloPlay/Functions/AutoBuild.js similarity index 54% rename from libs/SoloPlay/Functions/AutoBuildOverrides.js rename to libs/SoloPlay/Functions/AutoBuild.js index 041efb71..f0f6484f 100644 --- a/libs/SoloPlay/Functions/AutoBuildOverrides.js +++ b/libs/SoloPlay/Functions/AutoBuild.js @@ -1,5 +1,5 @@ /** -* @filename AutoBuildOverrides.js +* @filename AutoBuild.js * @author theBGuy * @credit alogwe - orignal author * @desc modified AutoBuild for easier use with Kolbot-SoloPlay @@ -7,22 +7,18 @@ */ js_strict(true); -includeIfNotIncluded("SoloPlay/Functions/Globals.js"); -includeIfNotIncluded("SoloPlay/Functions/MiscOverrides.js"); -includeIfNotIncluded("SoloPlay/Functions/CubingOverrides.js"); -includeIfNotIncluded("SoloPlay/Functions/PrototypeOverrides.js"); -includeIfNotIncluded("SoloPlay/Functions/RunewordsOverrides.js"); - const AutoBuild = new function AutoBuild () { Config.AutoBuild.DebugMode && (Config.AutoBuild.Verbose = true); const debug = !!Config.AutoBuild.DebugMode; const verbose = !!Config.AutoBuild.Verbose; - let currAutoBuild; - let configUpdateLevel = 0, lastSuccessfulUpdateLevel = 0; const log = (message) => FileTools.appendText(getLogFilename(), message + "\n"); const getCurrentScript = () => getScript(true).name.toLowerCase(); + const buildTemplate = me.currentBuild.AutoBuildTemplate; + let configUpdateLevel = 0; + let lastSuccessfulUpdateLevel = 0; + // Apply all Update functions from the build template in order from level 1 to me.charlvl. // By reapplying all of the changes to the Config object, we preserve // the state of the Config file without altering the saved char config. @@ -34,59 +30,32 @@ const AutoBuild = new function AutoBuild () { while (configUpdateLevel < cLvl) { configUpdateLevel += 1; Skill.init(); - if (currAutoBuild[configUpdateLevel] !== undefined) { - currAutoBuild[configUpdateLevel].Update.apply(Config); + if (buildTemplate[configUpdateLevel] !== undefined) { + buildTemplate[configUpdateLevel].Update.apply(Config); lastSuccessfulUpdateLevel = configUpdateLevel; } else if (reapply) { // re-apply from the last successful update - this is helpful if inside the build file there are conditional statements - currAutoBuild[lastSuccessfulUpdateLevel].Update.apply(Config); + buildTemplate[lastSuccessfulUpdateLevel].Update.apply(Config); reapply = false; } } } - function getBuildType () { - let build = CharInfo.getActiveBuild(); - if (!build) { - this.print("Config.AutoBuild.Template is either 'false', or invalid (" + build + ")"); - throw new Error("Invalid build template, read libs/config/Builds/README.txt for information"); - } - return build; - } - function getLogFilename () { let d = new Date(); let dateString = d.getMonth() + "_" + d.getDate() + "_" + d.getFullYear(); return "logs/AutoBuild." + me.realm + "." + me.charname + "." + dateString + ".log"; } - function getTemplateFilename () { - let className = sdk.player.class.nameOf(me.classid); - let build = getBuildType(); - let template = "SoloPlay/BuildFiles/" + className + "/" + className + "." + build + "Build.js"; - return template.toLowerCase(); - } - function initialize () { let currentScript = getCurrentScript(); - let template = getTemplateFilename(); - this.print("Including build template " + template + " into " + currentScript); - if (!include(template)) throw new Error("Failed to include template: " + template); - if (["Start", "Stepping", "Leveling"].includes(CharInfo.getActiveBuild())) { - currAutoBuild = build.AutoBuildTemplate; - } else { - currAutoBuild = finalBuild.AutoBuildTemplate; - } + this.print("Including build template " + SetUp._buildTemplate + " into " + currentScript); - // Only load() helper thread from default.dbj if it isn't loaded - if (currentScript === "libs\\soloplay\\soloplay.js" && !getScript("libs\\SoloPlay\\Threads\\AutoBuildThread.js")) { - load("libs/SoloPlay/Threads/AutoBuildThread.js"); - delay(500); - } + if (!buildTemplate) throw new Error("Failed to include template: " + SetUp._buildTemplate); - // All threads except autobuildthread.js use this event listener + // All threads except soloplay.js use this event listener // to update their thread-local Config object - if (currentScript !== "libs\\SoloPlay\\Threads\\AutoBuildThread.js") { + if (currentScript !== "libs\\soloplay\\soloplay.js") { addEventListener("scriptmsg", levelUpHandler); } @@ -104,6 +73,7 @@ const AutoBuild = new function AutoBuild () { // Only print to console from autobuildthread.js, // but log from all scripts function myPrint () { + if (!debug && !verbose) return; let args = Array.prototype.slice.call(arguments); args.unshift("AutoBuild:"); let result = args.join(" "); diff --git a/libs/SoloPlay/Functions/ConfigOverrides.js b/libs/SoloPlay/Functions/ConfigOverrides.js index 17c41117..f973cff5 100644 --- a/libs/SoloPlay/Functions/ConfigOverrides.js +++ b/libs/SoloPlay/Functions/ConfigOverrides.js @@ -49,11 +49,11 @@ Config.init = function (notify) { } try { - if (Config.AutoBuild.Enabled === true && include("SoloPlay/Functions/AutoBuildOverrides.js")) { + if (Config.AutoBuild.Enabled === true && include("SoloPlay/Functions/AutoBuild.js")) { AutoBuild.initialize(); } } catch (e3) { - console.log("ÿc8Error in libs/SoloPlay/Functions/AutoBuildOverrides.js (AutoBuild system is not active!)"); - console.log(e3.toSource()); + console.log("ÿc8Error in libs/SoloPlay/Functions/AutoBuild.js (AutoBuild system is not active!)"); + console.error(e3); } }; diff --git a/libs/SoloPlay/Functions/DynamicTiers.js b/libs/SoloPlay/Functions/DynamicTiers.js index bbb77257..8552e6e0 100644 --- a/libs/SoloPlay/Functions/DynamicTiers.js +++ b/libs/SoloPlay/Functions/DynamicTiers.js @@ -599,7 +599,7 @@ tier += ctcScore(); tier += chargeditemscore(item, -1, buildInfo); - if (item.isBaseType && !item.isRuneword) { + if (item.isBaseType && !item.isRuneword && me.charlvl > 10) { for (let x = 0; x < Config.Runewords.length; x += 1) { let [sockets, baseCID] = [Config.Runewords[x][0].length, Config.Runewords[x][1]]; if (item.classid === baseCID && item.sockets === sockets && !item.getItemsEx().length) return -1; diff --git a/libs/SoloPlay/Functions/Globals.js b/libs/SoloPlay/Functions/Globals.js index fd604a4b..b98268d1 100644 --- a/libs/SoloPlay/Functions/Globals.js +++ b/libs/SoloPlay/Functions/Globals.js @@ -23,11 +23,11 @@ const Overrides = require("../../modules/Override"); /** @global */ const Coords_1 = require("../Modules/Coords"); /** @global */ -const PotData = require("../modules/PotData"); +const PotData = require("../Modules/GameData/PotData"); /** @global */ -const GameData = require("../Modules/GameData"); +const GameData = require("../Modules/GameData/GameData"); /** @global */ -const AreaData = require("../Modules/AreaData"); +const AreaData = require("../Modules/GameData/AreaData"); const MYCLASSNAME = sdk.player.class.nameOf(me.classid).toLowerCase(); includeIfNotIncluded("SoloPlay/BuildFiles/" + MYCLASSNAME + "/" + MYCLASSNAME + ".js"); @@ -35,6 +35,7 @@ includeIfNotIncluded("SoloPlay/BuildFiles/" + MYCLASSNAME + "/" + MYCLASSNAME + /** * @global * @type {charData} + * @todo redo how I handle this, maybe make a prop on "me" instead */ let myData = CharData.getStats(); @@ -75,9 +76,59 @@ function updateMyData () { // general settings const SetUp = { mercEnabled: true, + _buildTemplate: "", init: function () { - let myData = CharData.getStats(); + // ensure finalBuild is properly formatted + let checkBuildTemplate = () => { + let build = (["Bumper", "Socketmule", "Imbuemule"].includes(SetUp.finalBuild) + ? ["Javazon", "Cold", "Bone", "Hammerdin", "Whirlwind", "Wind", "Trapsin"][me.classid] + : SetUp.finalBuild) + "Build"; + return ("libs/SoloPlay/BuildFiles/" + MYCLASSNAME + "/" + MYCLASSNAME + "." + build + ".js").toLowerCase(); + }; + SetUp._buildTemplate = checkBuildTemplate(); + + if (!FileTools.exists(SetUp._buildTemplate)) { + let errors = []; + /** @type {string[]} */ + let possibleBuilds = dopen("libs/SoloPlay/BuildFiles/" + MYCLASSNAME + "/") + .getFiles() + .filter(file => file.includes("Build")) + .map(file => file.substring(file.indexOf(".") + 1, file.indexOf("Build"))); + + // try to see if we can correct the finalBuild + for (let build of possibleBuilds) { + let match = myData.me.finalBuild.match(build, "gi"); + + if (match) { + console.log(match); + let old = myData.me.finalBuild; + myData.me.finalBuild = match[0].trim().capitalize(true); + errors.push( + "~Info tag :: " + old + " was incorrect, I have attempted to remedy this." + + " If it is still giving you an error please re-read the documentation. \n" + + "New InfoTag/finalBuild :: " + SetUp.finalBuild + ); + + break; + } + } + + if (errors.length) { + D2Bot.printToConsole("Kolbot-SoloPlay Final Build Error :: \n" + errors.join("\n"), sdk.colors.D2Bot.Red); + SetUp._buildTemplate = checkBuildTemplate(); // check again + if (!FileTools.exists(SetUp._buildTemplate)) { + console.error( + "ÿc8Kolbot-SoloPlayÿc0: Failed to find finalBuild template." + + " Please check that you have actually entered it in correctly," + + " and that you have the build in to BuildFiles folder." + + " Here is what you currently have: " + SetUp.finalBuild); + throw new Error("finalBuild(): Failed to find template: " + SetUp._buildTemplate); + } + D2Bot.setProfile(null, null, null, null, null, SetUp.finalBuild); + CharData.updateData("me", "finalBuild", SetUp.finalBuild); + } + } if (!myData.initialized) { myData.me.startTime = me.gamestarttime; @@ -247,7 +298,7 @@ const SetUp = { // setter for Developer option to stop a profile once it reaches a certain level stopAtLevel: (function () { if (!Developer.stopAtLevel.enabled) return false; - let level = Developer.stopAtLevel.profiles.find(profile => profile[0].toLowerCase() === me.profile.toLowerCase()) || false; + let level = Developer.stopAtLevel.profiles.find(prof => String.isEqual(prof[0], me.profile)) || false; return level ? level[1] : false; })(), @@ -265,37 +316,29 @@ const SetUp = { return respec; }, - getTemplate: function () { - let build = SetUp.currentBuild + "Build" ; - let template = "SoloPlay/BuildFiles/" + MYCLASSNAME + "/" + MYCLASSNAME + "." + build + ".js"; - - return { - buildType: SetUp.currentBuild, - template: template.toLowerCase() - }; - }, - - specPush: function (specType) { - let buildInfo = SetUp.getTemplate(); - if (!includeIfNotIncluded(buildInfo.template)) throw new Error("Failed to include template: " + buildInfo.template); - - let specCheck = []; - let final = buildInfo.buildType === SetUp.finalBuild; + autoBuild: function () { + let build = me.currentBuild; + if (!build) throw new Error("Failed to include template: " + SetUp._buildTemplate); - switch (specType) { - case "skills": - // Push skills value from template file - specCheck = JSON.parse(JSON.stringify((final ? finalBuild.skills : build.skills))); + /* AutoStat configuration. */ + Config.AutoStat.Enabled = true; + Config.AutoStat.Save = 0; + Config.AutoStat.BlockChance = me.paladin ? 75 : 57; + Config.AutoStat.UseBulk = true; + Config.AutoStat.Build = JSON.parse(JSON.stringify(build.stats)); - break; - case "stats": - // Push stats value from template file - specCheck = JSON.parse(JSON.stringify((final ? finalBuild.stats : build.stats))); + /* AutoSkill configuration. */ + Config.AutoSkill.Enabled = true; + Config.AutoSkill.Save = 0; + Config.AutoSkill.Build = JSON.parse(JSON.stringify(build.skills)); - break; - } + /* AutoBuild configuration. */ + Config.AutoBuild.Enabled = true; + Config.AutoBuild.Verbose = false; + Config.AutoBuild.DebugMode = false; + Config.AutoBuild.Template = SetUp.currentBuild; - return specCheck; + return true; }, makeNext: function () { @@ -360,7 +403,34 @@ const SetUp = { config: function () { Config.socketables = []; + Config.AutoEquip = true; + if (me.ladder > 0 || Developer.addLadderRW) { + // Runewords.ladderOverride = true; + Config.LadderOveride = true; + } + + // common items + NTIP.buildList([ + "([type] == helm || [type] == circlet) && ([quality] >= magic || [flag] == runeword) && [flag] != ethereal # [itemchargedskill] >= 0 # [tier] == tierscore(item)", + // Belt + "[type] == belt && [quality] >= magic && [flag] != ethereal # [itemchargedskill] >= 0 # [tier] == tierscore(item)", + "me.normal && [type] == belt && [quality] >= lowquality && [flag] != ethereal # [itemchargedskill] >= 0 # [tier] == tierscore(item)", + // Boots + "[type] == boots && [quality] >= normal && [flag] != ethereal # [itemchargedskill] >= 0 # [tier] == tierscore(item)", + // Armor + "[type] == armor && ([quality] >= magic || [flag] == runeword) && [flag] != ethereal # [itemchargedskill] >= 0 # [tier] == tierscore(item)", + // Gloves + "[type] == gloves && [quality] >= normal && [flag] != ethereal # [itemchargedskill] >= 0 # [tier] == tierscore(item)", + // Amulet + "[type] == amulet && [quality] >= magic # [itemchargedskill] >= 0 # [tier] == tierscore(item)", + // Rings + "[type] == ring && [quality] >= magic # [itemchargedskill] >= 0 # [tier] == tierscore(item)", + // non runeword white items + "([type] == armor) && [quality] >= normal && [flag] != ethereal # [itemchargedskill] >= 0 && [sockets] == 1 # [tier] == tierscore(item)", + "([type] == helm || [type] == circlet) && [quality] >= normal && [flag] != ethereal # [itemchargedskill] >= 0 && ([sockets] == 1 || [sockets] == 3) # [tier] == tierscore(item)", + ]); + if (me.expansion) { if (Storage.Stash === undefined) { Storage.Init(); @@ -407,6 +477,35 @@ const SetUp = { Config.Cubing = !!me.getItem(sdk.items.quest.Cube); Config.MakeRunewords = true; + /* Chicken configuration. */ + Config.LifeChicken = me.hardcore ? 45 : 10; + Config.ManaChicken = 0; + Config.MercChicken = 0; + Config.TownHP = me.hardcore ? 0 : 35; + Config.TownMP = 0; + + /* Potions configuration. */ + Config.UseHP = me.hardcore ? 90 : 80; + Config.UseRejuvHP = me.hardcore ? 65 : 50; + Config.UseMP = me.hardcore ? 75 : 65; + Config.UseMercHP = 75; + + /* Belt configuration. */ + Config.BeltColumn = ["hp", "mp", "mp", "rv"]; + SetUp.belt(); + + /* Gambling configuration. */ + Config.Gamble = true; + Config.GambleGoldStart = 1250000; + Config.GambleGoldStop = 750000; + + /* AutoMule configuration. */ + Config.AutoMule.Trigger = []; + Config.AutoMule.Force = []; + Config.AutoMule.Exclude = [ + "[name] >= Elrune && [name] <= Lemrune", + ]; + /* Shrine scan configuration. */ if (Check.currentBuild().caster) { Config.ScanShrines = [ @@ -462,23 +561,7 @@ const SetUp = { Config.FBR = 0; Config.IAS = 0; - /* AutoStat configuration. */ - Config.AutoStat.Enabled = true; - Config.AutoStat.Save = 0; - Config.AutoStat.BlockChance = me.paladin ? 75 : 57; - Config.AutoStat.UseBulk = true; - Config.AutoStat.Build = SetUp.specPush("stats"); - - /* AutoSkill configuration. */ - Config.AutoSkill.Enabled = true; - Config.AutoSkill.Save = 0; - Config.AutoSkill.Build = SetUp.specPush("skills"); - - /* AutoBuild configuration. */ - Config.AutoBuild.Enabled = true; - Config.AutoBuild.Verbose = false; - Config.AutoBuild.DebugMode = false; - Config.AutoBuild.Template = SetUp.currentBuild; + SetUp.autoBuild(); } }; @@ -545,12 +628,6 @@ const goToDifficulty = function (diff = undefined, reason = "") { return true; }; -const buildAutoBuildTempObj = (update = () => {}) => ({ - SkillPoints: [-1], - StatPoints: [-1, -1, -1, -1, -1], - Update: update -}); - // General Game functions const Check = { lowGold: false, @@ -891,88 +968,33 @@ const Check = { return highest; }, + // repetitive code - FIX THIS currentBuild: function () { - let buildInfo = SetUp.getTemplate(); - - if (!includeIfNotIncluded(buildInfo.template)) throw new Error("currentBuild(): Failed to include template: " + buildInfo.template); - - let final = buildInfo.buildType === SetUp.finalBuild; + let build = me.currentBuild; + + if (!build) throw new Error("currentBuild(): Failed to include template: " + SetUp._buildTemplate); return { - caster: final ? finalBuild.caster : build.caster, - tabSkills: final ? finalBuild.skillstab : build.skillstab, - wantedSkills: final ? finalBuild.wantedskills : build.wantedskills, - usefulSkills: final ? finalBuild.usefulskills : build.usefulskills, - precastSkills: final ? finalBuild.precastSkills : [], - usefulStats: final ? (!!finalBuild.usefulStats ? finalBuild.usefulStats : []) : (!!build.usefulStats ? build.usefulStats : []), - mercDiff: final ? finalBuild.mercDiff : null, - mercAct: final ? finalBuild.mercAct : null, - mercAuraWanted: final ? finalBuild.mercAuraWanted : null, - finalGear: final ? finalBuild.autoEquipTiers : [], - finalCharms: final ? (finalBuild.charms || {}) : {}, - respec: final ? finalBuild.respec : () => {}, - active: final ? finalBuild.active : build.active, + caster: build.caster, + tabSkills: build.skillstab, + wantedSkills: build.wantedskills, + usefulSkills: build.usefulskills, + precastSkills: build.hasOwnProperty("precastSkills") ? build.precastSkills : [], + usefulStats: build.hasOwnProperty("usefulStats") ? build.usefulStats : [], + wantedMerc: build.hasOwnProperty("wantedMerc") ? build.wantedMerc : null, + finalCharms: build.hasOwnProperty("charms") ? (build.charms || {}) : {}, + maxStr: Check.getMaxValue(build, "strength"), + maxDex: Check.getMaxValue(build, "dexterity"), + respec: build.hasOwnProperty("respec") ? build.respec : () => {}, + active: build.active, }; }, + // repetitive code - FIX THIS finalBuild: function () { - function getBuildTemplate () { - let build; - let buildType = SetUp.finalBuild; - - if (["Bumper", "Socketmule", "Imbuemule"].includes(buildType)) { - build = ["Javazon", "Cold", "Bone", "Hammerdin", "Whirlwind", "Wind", "Trapsin"][me.classid] + "Build"; - } else { - build = buildType + "Build"; - } - - return ("SoloPlay/BuildFiles/" + MYCLASSNAME + "/" + MYCLASSNAME + "." + build + ".js").toLowerCase(); - } - - let template = getBuildTemplate(); - - if (!includeIfNotIncluded(template)) { - let foundError = false; - let buildType; - - // try to see if we can correct the finalBuild - if (myData.me.finalBuild.match("Build", "gi")) { - myData.me.finalBuild = myData.me.finalBuild.substring(0, SetUp.finalBuild.length - 5); - D2Bot.printToConsole("Kolbot-SoloPlay: Info tag contained build which is unecessary. It has been fixed. New InfoTag/finalBuild :: " + SetUp.finalBuild, sdk.colors.D2Bot.Red); - foundError = true; - } - - if (myData.me.finalBuild.includes(".")) { - myData.me.finalBuild = myData.me.finalBuild.substring(myData.me.finalBuild.indexOf(".") + 1).capitalize(true); - D2Bot.printToConsole("Kolbot-SoloPlay: Info tag was incorrect, it contained '.' which is unecessary and means you likely entered something along the lines of Classname.finalBuild. I have attempted to remedy this. If it is still giving you an error please re-read the documentation. New InfoTag/finalBuild :: " + SetUp.finalBuild, sdk.colors.D2Bot.Red); - foundError = true; - } - - if (myData.me.finalBuild.includes(" ")) { - myData.me.finalBuild = myData.me.finalBuild.trim().capitalize(true); - D2Bot.printToConsole("Kolbot-SoloPlay: Info tag was incorrect, it contained a trailing space. I have attempted to remedy this. If it is still giving you an error please re-read the documentation. New InfoTag/finalBuild :: " + SetUp.finalBuild, sdk.colors.D2Bot.Red); - foundError = true; - } - - if (myData.me.finalBuild.includes("-")) { - myData.me.finalBuild = myData.me.finalBuild.substring(myData.me.finalBuild.indexOf("-") + 1).capitalize(true); - D2Bot.printToConsole("Kolbot-SoloPlay: Info tag was incorrect, it contained '-' which is unecessary and means you likely entered something along the lines of Classname-finalBuild. I have attempted to remedy this. If it is still giving you an error please re-read the documentation. New InfoTag/finalBuild :: " + SetUp.finalBuild, sdk.colors.D2Bot.Red); - foundError = true; - } + let finalBuild = me.finalBuild; - if (foundError) { - D2Bot.setProfile(null, null, null, null, null, SetUp.finalBuild); - CharData.updateData("me", "finalBuild", SetUp.finalBuild); - buildType = myData.me.finalBuild; - template = ("SoloPlay/BuildFiles/" + sdk.player.class.nameOf(me.classid) + "." + buildType + "Build.js").toLowerCase(); - } - - // try-again - if it fails again throw error - if (!include(template)) { - console.debug("ÿc8Kolbot-SoloPlayÿc0: Failed to include finalBuild template. Please check that you have actually entered it in correctly. Here is what you currently have: " + SetUp.finalBuild); - throw new Error("finalBuild(): Failed to include template: " + template); - } - } + if (!finalBuild) throw new Error("finalBuild(): Failed to include template: " + SetUp._buildTemplate); return { caster: finalBuild.caster, @@ -981,10 +1003,7 @@ const Check = { usefulSkills: finalBuild.usefulskills, precastSkills: finalBuild.precastSkills, usefulStats: (!!finalBuild.usefulStats ? finalBuild.usefulStats : []), - mercDiff: finalBuild.mercDiff, - mercAct: finalBuild.mercAct, - mercAuraWanted: finalBuild.mercAuraWanted, - finalGear: finalBuild.autoEquipTiers, + wantedMerc: finalBuild.wantedMerc, finalCharms: (finalBuild.charms || {}), maxStr: Check.getMaxValue(finalBuild, "strength"), maxDex: Check.getMaxValue(finalBuild, "dexterity"), @@ -1049,235 +1068,3 @@ const Check = { } }, }; - -const SoloWants = { - needList: [], - validGids: [], - - checkItem: function (item) { - if (!item) return false; - if (this.validGids.includes(item.gid)) return true; - let i = 0; - for (let el of this.needList) { - if ([sdk.items.type.Jewel, sdk.items.type.Rune].includes(item.itemType) || (item.itemType >= sdk.items.type.Amethyst && item.itemType <= sdk.items.type.Skull)) { - if (el.needed.includes(item.classid)) { - this.validGids.push(item.gid); - this.needList[i].needed.splice(this.needList[i].needed.indexOf(item.classid), 1); - if (this.needList[i].needed.length === 0) { - // no more needed items so remove from list - this.needList.splice(i, 1); - } - return true; - } - } - i++; // keep track of index - } - - return false; - }, - - keepItem: function (item) { - if (!item) return false; - return this.validGids.includes(item.gid); - }, - - buildList: function () { - let myItems = me.getItemsEx() - .filter(function (item) { - return !item.isRuneword && !item.questItem && item.quality >= sdk.items.quality.Magic && (item.sockets > 0 || getBaseStat("items", item.classid, "gemsockets") > 0); - }); - myItems - .filter(item => item.isEquipped) - .forEach(item => SoloWants.addToList(item)); - myItems - .filter(item => item.isInStorage && item.getItemType() && AutoEquip.wanted(item)) - .forEach(item => SoloWants.addToList(item)); - - return myItems.forEach(item => SoloWants.checkItem(item)); - }, - - addToList: function (item) { - if (!item || me.classic || item.isRuneword) return false; - if (SoloWants.needList.some(check => item.classid === check.classid)) return false; - let hasWantedItems; - let list = []; - let socketedWith = item.getItemsEx(); - let numSockets = item.sockets; - let curr = Config.socketables.find(({ classid }) => item.classid === classid); - - if (curr && curr.socketWith.length > 0) { - hasWantedItems = socketedWith.some(el => curr.socketWith.includes(el.classid)); - if (hasWantedItems && socketedWith.length === numSockets) { - return true; // this item is full - } - - if (curr.socketWith.includes(sdk.items.runes.Hel)) { - let merc = me.getMerc(); - switch (true) { - case Item.autoEquipCheck(item, true) && me.trueStr >= item.strreq && me.trueDex >= item.dexreq: - case Item.autoEquipCheckMerc(item, true) && !!merc && merc.rawStrength >= item.strreq && merc.rawDexterity >= item.dexreq: - curr.socketWith.splice(curr.socketWith.indexOf(sdk.items.runes.Hel), 1); - break; - } - } - - if (curr.socketWith.length > 1 && hasWantedItems) { - // handle different wanted socketables, if we already have a wanted socketable inserted then remove it from the check list - socketedWith.forEach(function (socketed) { - if (curr.socketWith.length > 1 && curr.socketWith.includes(socketed.classid)) { - curr.socketWith.splice(curr.socketWith.indexOf(socketed.classid), 1); - } - }); - } - - // add the wanted items to the list - for (let i = 0; i < numSockets - (hasWantedItems ? socketedWith.length : 0); i++) { - // handle different wanted socketables - curr.socketWith.length === numSockets ? list.push(curr.socketWith[i]) : list.push(curr.socketWith[0]); - } - - // currently no sockets but we might use our socket quest on it - numSockets === 0 && curr.useSocketQuest && list.push(curr.socketWith[0]); - - // if temp socketables are used for this item and its not already socketed with wanted items add the temp items too - if (!hasWantedItems && !!curr.temp && !!curr.temp.length > 0) { - for (let i = 0; i < numSockets - socketedWith.length; i++) { - list.push(curr.temp[0]); - } - // Make sure we keep a hel rune so we can unsocket temp socketables if needed - if (!SoloWants.needList.some(check => sdk.items.runes.Hel === check.classid)) { - let hel = me.getItemsEx(sdk.items.runes.Hel, sdk.items.mode.inStorage); - // we don't have any hel runes and its not already in our needList - if ((!hel || hel.length === 0)) { - SoloWants.needList.push({ classid: sdk.items.runes.Hel, needed: [sdk.items.runes.Hel] }); - } else if (!hel.some(check => SoloWants.validGids.includes(check.gid))) { - SoloWants.needList.push({ classid: sdk.items.runes.Hel, needed: [sdk.items.runes.Hel] }); - } - } - } - } else { - let itemtype = item.getItemType(); - if (!itemtype) return false; - let gemType = ["Helmet", "Armor"].includes(itemtype) ? "Ruby" : itemtype === "Shield" ? "Diamond" : itemtype === "Weapon" && !Check.currentBuild().caster ? "Skull" : ""; - let runeType; - - // Tir rune in normal, Io rune otherwise and Shael's if assassin TODO: use jewels too - !gemType && (runeType = me.normal ? "Tir" : me.assassin ? "Shael" : "Io"); - - hasWantedItems = socketedWith.some(el => gemType ? el.itemType === sdk.items.type[gemType] : el.classid === sdk.items.runes[runeType]); - if (hasWantedItems && socketedWith.length === numSockets) { - return true; // this item is full - } - - for (let i = 0; i < numSockets - socketedWith.length; i++) { - list.push(gemType ? sdk.items.gems.Perfect[gemType] : sdk.items.runes[runeType]); - } - } - - // add to our needList so we pick the items - return list.length > 0 ? this.needList.push({ classid: item.classid, needed: list }) : false; - }, - - update: function (item) { - if (!item) return false; - if (this.validGids.includes(item.gid)) return true; // already in the list - let i = 0; - for (let el of this.needList) { - if (!me.getItem(el.classid)) { - // We no longer have the item we wanted socketables for - this.needList.splice(i, 1); - continue; - } - if ([sdk.items.type.Jewel, sdk.items.type.Rune].includes(item.itemType) || (item.itemType >= sdk.items.type.Amethyst && item.itemType <= sdk.items.type.Skull)) { - if (el.needed.includes(item.classid)) { - this.validGids.push(item.gid); - this.needList[i].needed.splice(this.needList[i].needed.indexOf(item.classid), 1); - if (this.needList[i].needed.length === 0) { - // no more needed items so remove from list - this.needList.splice(i, 1); - } - return true; - } - } - i++; // keep track of index - } - - return false; - }, - - ensureList: function () { - let i = 0; - for (let el of this.needList) { - if (!me.getItem(el.classid)) { - // We no longer have the item we wanted socketables for - this.needList.splice(i, 1); - continue; - } - i++; // keep track of index - } - }, - - // Cube ingredients - checkSubrecipes: function () { - for (let el of this.needList) { - for (let i = 0; i < el.needed.length; i++) { - switch (true) { - case [ - sdk.items.gems.Perfect.Ruby, sdk.items.gems.Perfect.Sapphire, sdk.items.gems.Perfect.Topaz, sdk.items.gems.Perfect.Emerald, - sdk.items.gems.Perfect.Amethyst, sdk.items.gems.Perfect.Diamond, sdk.items.gems.Perfect.Skull].includes(el.needed[i]): - if (Cubing.subRecipes.indexOf(el.needed[i]) === -1) { - Cubing.subRecipes.push(el.needed[i]); - Cubing.recipes.push({ - Ingredients: [el.needed[i] - 1, el.needed[i] - 1, el.needed[i] - 1], - Index: 0, - AlwaysEnabled: true, - MainRecipe: "Crafting" - }); - } - - break; - case el.needed[i] >= sdk.items.runes.El && el.needed[i] <= sdk.items.runes.Ort: - if (Cubing.subRecipes.indexOf(el.needed[i]) === -1) { - Cubing.subRecipes.push(el.needed[i]); - Cubing.recipes.push({ - Ingredients: [el.needed[i] - 1, el.needed[i] - 1, el.needed[i] - 1], - Index: Recipe.Rune, - AlwaysEnabled: true, - MainRecipe: "Crafting" - }); - } - - break; - // case el.needed[i] >= sdk.items.runes.Thul && el.needed[i] <= sdk.items.runes.Lem: - // // gems repeat so should be able to math this out chipped (TASRED) -> repeat flawed (TASRED) - // if (Cubing.subRecipes.indexOf(el.needed[i]) === -1) { - // Cubing.subRecipes.push(el.needed[i]); - // Cubing.recipes.push({ - // Ingredients: [el.needed[i] - 1, el.needed[i] - 1, el.needed[i] - 1], - // Index: Recipe.Rune, - // AlwaysEnabled: true, - // MainRecipe: "Crafting" - // }); - // } - - // break; - // case el.needed[i] >= sdk.items.runes.Mal && el.needed[i] <= sdk.items.runes.Zod: - // // gems repeat so should be able to math this out Base (TASRED) -> repeat Flawless (TASRE) (stops at Emerald) - // if (Cubing.subRecipes.indexOf(el.needed[i]) === -1) { - // Cubing.subRecipes.push(el.needed[i]); - // Cubing.recipes.push({ - // Ingredients: [el.needed[i] - 1, el.needed[i] - 1], - // Index: Recipe.Rune, - // AlwaysEnabled: true, - // MainRecipe: "Crafting" - // }); - // } - - // break; - } - } - } - - return true; - }, -}; diff --git a/libs/SoloPlay/Functions/Me.js b/libs/SoloPlay/Functions/Me.js index e955f5c1..f7f91645 100644 --- a/libs/SoloPlay/Functions/Me.js +++ b/libs/SoloPlay/Functions/Me.js @@ -113,10 +113,52 @@ if (!me.hasOwnProperty("onFinalBuild")) { }); } +if (!me.hasOwnProperty("finalBuild")) { + let _finalBuild = null; + + Object.defineProperty(me, "finalBuild", { + get: function () { + if (_finalBuild) return _finalBuild; + let className = me.className.toLowerCase(); + let build = (["Bumper", "Socketmule", "Imbuemule"].includes(SetUp.finalBuild) + ? ["Javazon", "Cold", "Bone", "Hammerdin", "Whirlwind", "Wind", "Trapsin"][me.classid] + : SetUp.finalBuild) + "Build"; + _finalBuild = require("../BuildFiles/" + className + "/" + className + "." + build); + return _finalBuild; + }, + set: function (v) { + if (v.hasOwnProperty("AutoBuildTemplate")) { + // Object.assign(this.finalBuild, v); + _finalBuild = v; + } + }, + }); +} + +if (!me.hasOwnProperty("currentBuild")) { + let _currentBuild = null; + + Object.defineProperty(me, "currentBuild", { + get: function () { + if (_currentBuild) return _currentBuild; + let className = me.className.toLowerCase(); + let build = SetUp.currentBuild + "Build"; + _currentBuild = require("../BuildFiles/" + className + "/" + className + "." + build); + return _currentBuild; + }, + set: function (v) { + if (v.hasOwnProperty("AutoBuildTemplate")) { + // Object.assign(this.currentBuild, v); + _currentBuild = v; + } + }, + }); +} + /** @returns {boolean} */ me.canTpToTown = function () { // can't tp if dead - or not currently enabled to - if (me.dead || !Misc.townEnabled) return false; + if (me.dead || SoloEvents.townChicken.disabled) return false; const myArea = me.area; let badAreas = [ sdk.areas.RogueEncampment, sdk.areas.LutGholein, sdk.areas.KurastDocktown, diff --git a/libs/SoloPlay/Functions/SoloWants.js b/libs/SoloPlay/Functions/SoloWants.js new file mode 100644 index 00000000..3b22441f --- /dev/null +++ b/libs/SoloPlay/Functions/SoloWants.js @@ -0,0 +1,258 @@ +/** +* @filename SoloWants.js +* @author theBGuy +* @desc SoloWants system for Kolbot-SoloPlay, handles inserting socketables +* +*/ + +const SoloWants = { + needList: [], + validGids: [], + + /** + * @param {ItemUnit} item + * @returns {boolean} + */ + checkItem: function (item) { + if (!item) return false; + if (this.validGids.includes(item.gid)) return true; + let i = 0; + for (let el of this.needList) { + if (item.isInsertable) { + if (el.needed.includes(item.classid)) { + this.validGids.push(item.gid); + this.needList[i].needed.splice(this.needList[i].needed.indexOf(item.classid), 1); + if (this.needList[i].needed.length === 0) { + // no more needed items so remove from list + this.needList.splice(i, 1); + } + return true; + } + } + i++; // keep track of index + } + + return false; + }, + + /** + * @param {ItemUnit} item + * @returns {boolean} + */ + keepItem: function (item) { + if (!item) return false; + return this.validGids.includes(item.gid); + }, + + buildList: function () { + let myItems = me.getItemsEx() + .filter(function (item) { + if (item.isRuneword || item.questItem) return false; + return item.quality >= sdk.items.quality.Normal && (item.sockets > 0 || getBaseStat("items", item.classid, "gemsockets") > 0); + }); + myItems + .filter(item => item.isEquipped) + .forEach(item => SoloWants.addToList(item)); + myItems + .filter(item => item.isInStorage && item.quality >= sdk.items.quality.Magic && item.getItemType() && AutoEquip.wanted(item)) + .forEach(item => SoloWants.addToList(item)); + + return myItems.forEach(item => SoloWants.checkItem(item)); + }, + + /** + * @param {ItemUnit} item + * @returns {boolean} + */ + addToList: function (item) { + if (!item || me.classic || item.isRuneword) return false; + if (SoloWants.needList.some(check => item.classid === check.classid)) return false; + let hasWantedItems; + let list = []; + let socketedWith = item.getItemsEx(); + let numSockets = item.sockets; + let curr = Config.socketables.find(({ classid }) => item.classid === classid); + + if (curr && curr.socketWith.length > 0) { + hasWantedItems = socketedWith.some(el => curr.socketWith.includes(el.classid)); + if (hasWantedItems && socketedWith.length === numSockets) { + return true; // this item is full + } + + if (curr.socketWith.includes(sdk.items.runes.Hel)) { + let merc = me.getMerc(); + switch (true) { + case Item.autoEquipCheck(item, true) && me.trueStr >= item.strreq && me.trueDex >= item.dexreq: + case Item.autoEquipCheckMerc(item, true) && !!merc && merc.rawStrength >= item.strreq && merc.rawDexterity >= item.dexreq: + curr.socketWith.splice(curr.socketWith.indexOf(sdk.items.runes.Hel), 1); + break; + } + } + + if (curr.socketWith.length > 1 && hasWantedItems) { + // handle different wanted socketables, if we already have a wanted socketable inserted then remove it from the check list + socketedWith.forEach(function (socketed) { + if (curr.socketWith.length > 1 && curr.socketWith.includes(socketed.classid)) { + curr.socketWith.splice(curr.socketWith.indexOf(socketed.classid), 1); + } + }); + } + + // add the wanted items to the list + for (let i = 0; i < numSockets - (hasWantedItems ? socketedWith.length : 0); i++) { + // handle different wanted socketables + curr.socketWith.length === numSockets ? list.push(curr.socketWith[i]) : list.push(curr.socketWith[0]); + } + + // currently no sockets but we might use our socket quest on it + numSockets === 0 && curr.useSocketQuest && list.push(curr.socketWith[0]); + + // if temp socketables are used for this item and its not already socketed with wanted items add the temp items too + if (!hasWantedItems && !!curr.temp && !!curr.temp.length > 0) { + for (let i = 0; i < numSockets - socketedWith.length; i++) { + list.push(curr.temp[0]); + } + // Make sure we keep a hel rune so we can unsocket temp socketables if needed + if (!SoloWants.needList.some(check => sdk.items.runes.Hel === check.classid)) { + let hel = me.getItemsEx(sdk.items.runes.Hel, sdk.items.mode.inStorage); + // we don't have any hel runes and its not already in our needList + if ((!hel || hel.length === 0)) { + SoloWants.needList.push({ classid: sdk.items.runes.Hel, needed: [sdk.items.runes.Hel] }); + } else if (!hel.some(check => SoloWants.validGids.includes(check.gid))) { + SoloWants.needList.push({ classid: sdk.items.runes.Hel, needed: [sdk.items.runes.Hel] }); + } + } + } + } else { + let itemtype = item.getItemType(); + if (!itemtype) return false; + let gemType = ["Helmet", "Armor"].includes(itemtype) + ? "Ruby" : itemtype === "Shield" + ? "Diamond" : itemtype === "Weapon" && !Check.currentBuild().caster + ? "Skull" : ""; + let runeType; + + // Tir rune in normal, Io rune otherwise and Shael's if assassin TODO: use jewels too + !gemType && (runeType = me.normal ? "Tir" : me.assassin ? "Shael" : "Io"); + + hasWantedItems = socketedWith.some(el => gemType ? el.itemType === sdk.items.type[gemType] : el.classid === sdk.items.runes[runeType]); + if (hasWantedItems && socketedWith.length === numSockets) { + return true; // this item is full + } + + for (let i = 0; i < numSockets - socketedWith.length; i++) { + list.push(gemType ? sdk.items.gems.Perfect[gemType] : sdk.items.runes[runeType]); + } + } + + // add to our needList so we pick the items + return list.length > 0 ? this.needList.push({ classid: item.classid, needed: list }) : false; + }, + + /** + * @param {ItemUnit} item + * @returns {boolean} + */ + update: function (item) { + if (!item) return false; + if (this.validGids.includes(item.gid)) return true; // already in the list + let i = 0; + for (let el of this.needList) { + if (!me.getItem(el.classid)) { + // We no longer have the item we wanted socketables for + this.needList.splice(i, 1); + continue; + } + if (item.isInsertable) { + if (el.needed.includes(item.classid)) { + this.validGids.push(item.gid); + this.needList[i].needed.splice(this.needList[i].needed.indexOf(item.classid), 1); + if (this.needList[i].needed.length === 0) { + // no more needed items so remove from list + this.needList.splice(i, 1); + } + return true; + } + } + i++; // keep track of index + } + + return false; + }, + + ensureList: function () { + let i = 0; + for (let el of this.needList) { + if (!me.getItem(el.classid)) { + // We no longer have the item we wanted socketables for + this.needList.splice(i, 1); + continue; + } + i++; // keep track of index + } + }, + + // Cube ingredients + checkSubrecipes: function () { + for (let el of this.needList) { + for (let i = 0; i < el.needed.length; i++) { + switch (true) { + case [ + sdk.items.gems.Perfect.Ruby, sdk.items.gems.Perfect.Sapphire, sdk.items.gems.Perfect.Topaz, sdk.items.gems.Perfect.Emerald, + sdk.items.gems.Perfect.Amethyst, sdk.items.gems.Perfect.Diamond, sdk.items.gems.Perfect.Skull].includes(el.needed[i]): + if (Cubing.subRecipes.indexOf(el.needed[i]) === -1) { + Cubing.subRecipes.push(el.needed[i]); + Cubing.recipes.push({ + Ingredients: [el.needed[i] - 1, el.needed[i] - 1, el.needed[i] - 1], + Index: 0, + AlwaysEnabled: true, + MainRecipe: "Crafting" + }); + } + + break; + case el.needed[i] >= sdk.items.runes.El && el.needed[i] <= sdk.items.runes.Ort: + if (Cubing.subRecipes.indexOf(el.needed[i]) === -1) { + Cubing.subRecipes.push(el.needed[i]); + Cubing.recipes.push({ + Ingredients: [el.needed[i] - 1, el.needed[i] - 1, el.needed[i] - 1], + Index: Recipe.Rune, + AlwaysEnabled: true, + MainRecipe: "Crafting" + }); + } + + break; + // case el.needed[i] >= sdk.items.runes.Thul && el.needed[i] <= sdk.items.runes.Lem: + // // gems repeat so should be able to math this out chipped (TASRED) -> repeat flawed (TASRED) + // if (Cubing.subRecipes.indexOf(el.needed[i]) === -1) { + // Cubing.subRecipes.push(el.needed[i]); + // Cubing.recipes.push({ + // Ingredients: [el.needed[i] - 1, el.needed[i] - 1, el.needed[i] - 1], + // Index: Recipe.Rune, + // AlwaysEnabled: true, + // MainRecipe: "Crafting" + // }); + // } + + // break; + // case el.needed[i] >= sdk.items.runes.Mal && el.needed[i] <= sdk.items.runes.Zod: + // // gems repeat so should be able to math this out Base (TASRED) -> repeat Flawless (TASRE) (stops at Emerald) + // if (Cubing.subRecipes.indexOf(el.needed[i]) === -1) { + // Cubing.subRecipes.push(el.needed[i]); + // Cubing.recipes.push({ + // Ingredients: [el.needed[i] - 1, el.needed[i] - 1], + // Index: Recipe.Rune, + // AlwaysEnabled: true, + // MainRecipe: "Crafting" + // }); + // } + + // break; + } + } + } + + return true; + }, +}; diff --git a/libs/SoloPlay/Utils/General.js b/libs/SoloPlay/Utils/General.js index 4e77d10e..052527d5 100644 --- a/libs/SoloPlay/Utils/General.js +++ b/libs/SoloPlay/Utils/General.js @@ -104,11 +104,18 @@ item.unique && (item.isInStorage || (item.isEquipped && !item.isOnSwap)) && !item.ethereal )); + const buildAutoBuildTempObj = (update = () => {}) => ({ + SkillPoints: [-1], + StatPoints: [-1, -1, -1, -1, -1], + Update: update + }); + module.exports = { impossibleClassicBuilds: impossibleClassicBuilds, impossibleNonLadderBuilds: impossibleNonLadderBuilds, nipItems: nipItems, basicSocketables: basicSocketables, addSocketableObj: addSocketableObj, + buildAutoBuildTempObj: buildAutoBuildTempObj, }; })(module); diff --git a/libs/SoloPlay/index.d.ts b/libs/SoloPlay/index.d.ts index bd405173..977899d0 100644 --- a/libs/SoloPlay/index.d.ts +++ b/libs/SoloPlay/index.d.ts @@ -1,5 +1,9 @@ declare global { + interface Math { + percentDifference(value1: number, value2: number): number; + } + interface ItemUnit { readonly isCharm: boolean; readonly isGem: boolean; @@ -29,6 +33,34 @@ declare global { haveRunes(itemInfo: number[]): boolean; } + type MercObj = { + classid: number, + skill: number, + skillName: string, + act: number, + difficulty: number, + }; + + interface Build { + caster: boolean; + skillstab: number; + wantedskills: number[]; + usefulskills: number[]; + precastSkills: number[]; + wantedMerc: MercObj; + stats: Array<[string, number | "block" | "all"]>; + skills: Array<[number, number, boolean?]>; + charms: Record boolean; + }>; + AutoBuildTemplate: Record void }>; + respec: () => boolean; + active: () => boolean; + } + interface MeType { readonly maxNearMonsters: number; readonly dualWielding: boolean; @@ -42,6 +74,9 @@ declare global { readonly PR: number; readonly onFinalBuild: boolean; + finalBuild: Build; + currentBuild: Build; + canTpToTown(): boolean; getMercEx(): MercUnit | null; getEquippedItem(bodyLoc: number): ItemUnit | null; @@ -60,6 +95,7 @@ declare global { needMerc(): boolean; clearBelt(): boolean; sortInventory(): boolean; + cleanUpScrolls(tome: ItemUnit, scrollId: number): number; } interface Container { @@ -100,6 +136,31 @@ declare global { MoveToSpot(item: ItemUnit, mX: number, mY: number): boolean; } + class Merc { + constructor(classid: number, skill: number, act: number, difficulty?: number); + classid: number; + skill: number; + skillName: string; + act: number; + difficulty: number; + } + + class MercData { + [sdk.skills.FireArrow]: Merc; + [sdk.skills.ColdArrow]: Merc; + [sdk.skills.Prayer]: Merc; + [sdk.skills.BlessedAim]: Merc; + [sdk.skills.Defiance]: Merc; + [sdk.skills.HolyFreeze]: Merc; + [sdk.skills.Might]: Merc; + [sdk.skills.Thorns]: Merc; + [sdk.skills.IceBlast]: Merc; + [sdk.skills.FireBall]: Merc; + [sdk.skills.Lightning]: Merc; + [sdk.skills.Bash]: Merc; + actMap: Map; + } + namespace Mercenary { let minCost: number; @@ -113,16 +174,33 @@ declare global { namespace Misc { let townEnabled: boolean; + let openChestsEnabled: boolean; + const presetShrineIds: number[]; + const presetChestIds: number[]; + + function openChestsInArea(area: number, chestIds: number[], sort?: Function): boolean; + function getExpShrine(shrineLocs: number[]): boolean; + function unsocketItem(item: ItemUnit): boolean; + function checkItemsForSocketing(): ItemUnit | boolean; + function checkItemsForImbueing(): ItemUnit | boolean; + function addSocketablesToItem(item: ItemUnit, runes: ItemUnit[]): boolean; + function getSocketables( + item: ItemUnit, + itemInfo?: { + classid: number, + socketWith: number[], + temp: number[], + useSocketQuest: boolean, + condition: Function + } + ): boolean; + function checkSocketables(): void; } namespace Skill { function switchCast(skillId: number, givenSettings: { hand?: number, x?: number, y?: number, switchBack?: boolean, oSkill?: boolean }): boolean; } - namespace Attack { - function clearPos(x: number, y: number, range: number, pickit: boolean): boolean; - } - interface charData { initialized: boolean; normal: { @@ -210,22 +288,15 @@ declare global { function nextDifficulty(announce: boolean): string | false; function runes(): boolean; function haveItem(type: string | number, flag?: string | number, iName?: string): boolean; + function currentBuild(): Build; + function finalBuild(): Build; } namespace SoloWants { - const needList: any[]; - const validGids: number[]; - - function checkItem(item: ItemUnit): boolean; - function keepItem(item: ItemUnit): boolean; - function buildList(): void; - function addToList(item: ItemUnit): boolean; - function update(item: ItemUnit): boolean; - function ensureList(): void; - function checkSubrecipes(): boolean; } namespace NPCAction { + function shopAt(npcName: string): boolean; function buyPotions(): boolean; function fillTome(classid: number, force?: boolean): boolean; function cainID(force?: boolean): boolean; @@ -238,13 +309,6 @@ declare global { namespace AutoEquip { } - namespace SoloEvents { - namespace townChicken { - let disabled: boolean; - let running: boolean; - } - } - type extraTasks = { thawing?: boolean, antidote?: boolean, From 6e89f6f7f7662d1787bf62b97ea87051f2e25ced Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Sun, 12 Mar 2023 14:50:56 -0400 Subject: [PATCH 076/263] Fix Town.sell check - copyUnit was causing it to bug thinking we still had a item when we didn't --- libs/SoloPlay/Functions/TownOverrides.js | 14 ++++++++++++-- libs/SoloPlay/Scripts/heart.js | 6 +++--- 2 files changed, 15 insertions(+), 5 deletions(-) diff --git a/libs/SoloPlay/Functions/TownOverrides.js b/libs/SoloPlay/Functions/TownOverrides.js index f7e4ef1c..919ea10a 100644 --- a/libs/SoloPlay/Functions/TownOverrides.js +++ b/libs/SoloPlay/Functions/TownOverrides.js @@ -96,8 +96,10 @@ Town.haveItemsToSell = function () { let temp = []; while (Town.sell.length) { let i = Town.sell.shift(); - if (i && i.isInStorage && !Town.ignoreType(i.itemType) && i.sellable && !Town.systemsKeep(i)) { - temp.push(i); + if (typeof i === "undefined") continue; + let realItem = me.getItem(i.classid, -1, i.gid); + if (realItem && realItem.isInStorage && !Town.ignoreType(realItem.itemType) && realItem.sellable && !Town.systemsKeep(realItem)) { + temp.push(realItem); } } Town.sell = temp.slice(0); @@ -115,6 +117,7 @@ Town.sellItems = function (itemList = []) { if (this.initNPC("Shop", "sell")) { while (itemList.length) { let item = itemList.shift(); + if (!item.isInStorage) continue; try { @@ -513,6 +516,8 @@ Town.clearInventory = function () { } }); + Town.sell = []; + console.log("ÿc8Exit clearInventory ÿc0- ÿc7Duration: ÿc0" + Time.format(getTickCount() - clearInvoTick)); return true; @@ -702,6 +707,11 @@ Town.doChores = function (repair = false, givenTasks = {}) { NPCAction.repair(repair); NPCAction.reviveMerc(); NPCAction.gamble(); + + // if (me.inArea(sdk.areas.LutGholein) && me.normal && me.gold > 10000) { + // // shop at Elzix - what about others? + // NPCAction.shopAt(NPC.Elzix); + // } Cubing.emptyCube(); Runewords.makeRunewords(); Cubing.doCubing(); diff --git a/libs/SoloPlay/Scripts/heart.js b/libs/SoloPlay/Scripts/heart.js index d92fd503..ca0d0dd0 100644 --- a/libs/SoloPlay/Scripts/heart.js +++ b/libs/SoloPlay/Scripts/heart.js @@ -1,6 +1,6 @@ /** * @filename heart.js -* @author isid0re, theBGuy +* @author theBGuy * @desc get the heart for khalims will * */ @@ -12,8 +12,8 @@ function heart () { Pather.checkWP(sdk.areas.KurastBazaar, true) ? Pather.useWaypoint(sdk.areas.KurastBazaar) : Pather.getWP(sdk.areas.KurastBazaar); Precast.doPrecast(true); - if (!Pather.moveToExit([sdk.areas.KurastBazaar, sdk.areas.A3SewersLvl1, sdk.areas.A3SewersLvl2], true) - || !Pather.moveToPreset(me.area, sdk.unittype.Object, sdk.quest.chest.KhalimsHeartChest)) { + if (!Pather.journeyTo(sdk.areas.A3SewersLvl2) + || !Pather.moveToPresetObject(me.area, sdk.quest.chest.KhalimsHeartChest)) { if (!me.getItem(sdk.items.quest.KhalimsHeart)) { console.log("ÿc8Kolbot-SoloPlayÿc0: Failed to get the heart"); return false; From 52f87cca6769ca495d89ca7406c5e0fef98d5c41 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Sun, 12 Mar 2023 15:48:10 -0400 Subject: [PATCH 077/263] Update MiscOverrides.js - Fix typo in itemInfo check --- libs/SoloPlay/Functions/MiscOverrides.js | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/libs/SoloPlay/Functions/MiscOverrides.js b/libs/SoloPlay/Functions/MiscOverrides.js index 3cc1c884..ac40aaee 100644 --- a/libs/SoloPlay/Functions/MiscOverrides.js +++ b/libs/SoloPlay/Functions/MiscOverrides.js @@ -569,7 +569,8 @@ Misc.getSocketables = function (item, itemInfo) { let [multiple, temp] = [[], []]; let itemSocketInfo = item.getItemsEx(); let preSockets = itemSocketInfo.length; - let allowTemp = (itemInfo.hasOwnProperty("temp") && itemInfo.temp.length > 0 && (preSockets === 0 || preSockets > 0 && itemSocketInfo.some(el => !itemInfo.socketWith.includes(el.classid)))); + let allowTemp = (itemInfo.hasOwnProperty("temp") && itemInfo.temp.length > 0 + && (preSockets === 0 || preSockets > 0 && itemSocketInfo.some(el => !itemInfo.socketWith.includes(el.classid)))); let sockets = item.sockets; let openSockets = sockets - preSockets; let { classid, quality } = item; @@ -593,7 +594,7 @@ Misc.getSocketables = function (item, itemInfo) { return false; } - if (itemInfo.hasOwnProperty("socketWith") && itemInfo.socketWith.length === 0) { + if (!itemInfo.hasOwnProperty("socketWith") || (itemInfo.hasOwnProperty("socketWith") && itemInfo.socketWith.length === 0)) { itemtype = item.getItemType(); if (!itemtype) return false; gemType = ["Helmet", "Armor"].includes(itemtype) @@ -699,7 +700,7 @@ Misc.getSocketables = function (item, itemInfo) { Misc.checkSocketables = function () { let items = me.getItemsEx() .filter(item => item.sockets > 0 && AutoEquip.hasTier(item) - && (item.quality >= sdk.items.quality.Magic || [sdk.items.quality.Normal, sdk.items.quality.Superior].includes(item.quality) && item.isEquipped)) + && (item.quality >= sdk.items.quality.Magic || ((item.normal || item.superior) && item.isEquipped))) .sort((a, b) => NTIP.GetTier(b) - NTIP.GetTier(a)); if (!items) return; From 2e1add2b3d53444477259e4968efb46fec5fe26b Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Sun, 12 Mar 2023 19:19:14 -0400 Subject: [PATCH 078/263] me.emit instead of sendToScript - Swap out sendToScript because we are on the same thread now just need to emit the event - Possible namespace collision with Messaging? Just to be safe changes Guards internal to `_Messaging` - Got an oom with error message to Pather,clearUIFlags, don't see why the forEach loop would cause issues but changed to for loop anyway with a gameReady wait delay --- libs/SoloPlay/Functions/PatherOverrides.js | 13 ++++++++++--- libs/SoloPlay/Functions/SoloEvents.js | 6 ++++-- libs/SoloPlay/Modules/Guard.js | 6 +++--- 3 files changed, 17 insertions(+), 8 deletions(-) diff --git a/libs/SoloPlay/Functions/PatherOverrides.js b/libs/SoloPlay/Functions/PatherOverrides.js index 6ea00760..6536c9af 100644 --- a/libs/SoloPlay/Functions/PatherOverrides.js +++ b/libs/SoloPlay/Functions/PatherOverrides.js @@ -330,9 +330,16 @@ Pather.changeAct = function () { }; Pather.clearUIFlags = function () { - Pather.cancelFlags.forEach(flag => { - getUIFlag(flag) && me.cancel(); - }); + while (!me.gameReady) { + delay(3); + } + + for (let i = 0; i < Pather.cancelFlags.length; i++) { + if (getUIFlag(Pather.cancelFlags[i]) && me.cancel()) { + delay(250); + i = 0; // Reset + } + } }; /** diff --git a/libs/SoloPlay/Functions/SoloEvents.js b/libs/SoloPlay/Functions/SoloEvents.js index 1d30605f..8d90ef33 100644 --- a/libs/SoloPlay/Functions/SoloEvents.js +++ b/libs/SoloPlay/Functions/SoloEvents.js @@ -410,7 +410,8 @@ if (!bytes.length) return; // dia lightning if (bytes[0] === 0x4C && bytes[6] === 193) { - Messaging.sendToScript(SoloEvents.filePath, "dodge"); + // Messaging.sendToScript(SoloEvents.filePath, "dodge"); + me.emit("soloEvent", "dodge"); } }, @@ -460,7 +461,8 @@ case waveBoss.LISTER: if ((me.barbarian && (me.charlvl < CharInfo.levelCap || !me.baal || me.hardcore)) || (me.charlvl < CharInfo.levelCap && (me.gold < 5000 || (!me.baal && SetUp.finalBuild !== "Bumper")))) { - Messaging.sendToScript(SoloEvents.filePath, "skip"); + // Messaging.sendToScript(SoloEvents.filePath, "skip"); + me.emit("soloEvent", "skip"); SoloEvents.skippedWaves.push(wave); } diff --git a/libs/SoloPlay/Modules/Guard.js b/libs/SoloPlay/Modules/Guard.js index 6183e8ac..db3f7074 100644 --- a/libs/SoloPlay/Modules/Guard.js +++ b/libs/SoloPlay/Modules/Guard.js @@ -1,6 +1,6 @@ (function (module, require, thread) { "use strict"; - const Messaging = require("../../modules/Messaging"); + const _Messaging = require("../../modules/Messaging"); const Worker = require("../../modules/Worker"); const sdk = require("../../modules/sdk"); @@ -13,7 +13,7 @@ let myStack = ""; // recv stack - Messaging.on("Guard", (data => typeof data === "object" && data && data.hasOwnProperty("stack") && (myStack = data.stack))); + _Messaging.on("Guard", (data => typeof data === "object" && data && data.hasOwnProperty("stack") && (myStack = data.stack))); /** * @constructor @@ -71,7 +71,7 @@ Worker.push(function highPrio() { Worker.push(highPrio); if ((getTickCount() - sendStack) < 200 || (sendStack = getTickCount()) && false) return true; - Messaging.send({ Guard: { stack: (new Error).stack } }); + _Messaging.send({ Guard: { stack: (new Error).stack } }); return true; }); From ce3c66dbc060df23f2494a6836aec11f8551ef1e Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Sun, 12 Mar 2023 20:53:55 -0400 Subject: [PATCH 079/263] Update DynamicTiers.js - Had forgotten to fix this check with the new Runeword data structure --- libs/SoloPlay/Functions/DynamicTiers.js | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/libs/SoloPlay/Functions/DynamicTiers.js b/libs/SoloPlay/Functions/DynamicTiers.js index 8552e6e0..a4f6908e 100644 --- a/libs/SoloPlay/Functions/DynamicTiers.js +++ b/libs/SoloPlay/Functions/DynamicTiers.js @@ -129,9 +129,11 @@ mercRating += item.getStatEx(sdk.stats.SkillOnHit, 5631) * mercWeights.CTCOSDECREP; // add CTC decrepify on strikng (magic items) } - for (let x = 0; x < Config.Runewords.length; x += 1) { - let [sockets, baseCID] = [Config.Runewords[x][0].length, Config.Runewords[x][1]]; - if (item.classid === baseCID && item.isBaseType && item.sockets === sockets && !item.isRuneword) return -1; + if (item.isBaseType && !item.isRuneword) { + for (let runeword of Config.Runewords) { + let [sockets, baseCID] = [runeword[0].sockets, runeword[1]]; + if (item.classid === baseCID && item.sockets === sockets && !item.getItemsEx().length) return -1; + } } return mercRating; @@ -337,6 +339,7 @@ /** * @param {ItemUnit} item * @param {number} [bodyloc] + * @todo Breakpoint scoring similar to how res is scored */ const tierscore = function (item, tier, bodyloc) { const buildInfo = Check.currentBuild(); @@ -600,8 +603,8 @@ tier += chargeditemscore(item, -1, buildInfo); if (item.isBaseType && !item.isRuneword && me.charlvl > 10) { - for (let x = 0; x < Config.Runewords.length; x += 1) { - let [sockets, baseCID] = [Config.Runewords[x][0].length, Config.Runewords[x][1]]; + for (let runeword of Config.Runewords) { + let [sockets, baseCID] = [runeword[0].sockets, runeword[1]]; if (item.classid === baseCID && item.sockets === sockets && !item.getItemsEx().length) return -1; } } From 1769118f8f2158ab24c8c4aceb679bb360f245ab Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Mon, 13 Mar 2023 01:42:39 -0400 Subject: [PATCH 080/263] Add Reload.js - Reload.js is a simple thread that ensures all the main threads get shutdown correctly then reloads default.dbj and shuts itself down. This way everything is properly reloaded --- libs/SoloPlay/Threads/Reload.js | 35 ++++++++++++++++++++++++++++ libs/SoloPlay/Threads/ToolsThread.js | 5 ++-- 2 files changed, 37 insertions(+), 3 deletions(-) create mode 100644 libs/SoloPlay/Threads/Reload.js diff --git a/libs/SoloPlay/Threads/Reload.js b/libs/SoloPlay/Threads/Reload.js new file mode 100644 index 00000000..bf08c11c --- /dev/null +++ b/libs/SoloPlay/Threads/Reload.js @@ -0,0 +1,35 @@ +/** +* @filename Reload.js +* @author theBGuy +* @desc Simple thread that when loaded will shutdown all other threads and reload the bot +* +*/ +js_strict(true); +include("critical.js"); + +function main () { + let tick = getTickCount(); + let scripts = [ + "default.dbj", "libs/SoloPlay/SoloPlay.js", "libs/SoloPlay/Threads/Toolsthread.js", + "libs/SoloPlay/Modules/Guard.js", "libs/SoloPlay/Modules/TownGuard.js" + ]; + + while (scripts.length) { + let script = getScript(scripts[0]); + + if (script && script.running) { + script.stop(); + + while (script.running) { + delay(100); + } + } + scripts.shift(); + } + + while (getTickCount() - tick < 5000) { + delay(100); + } + console.log("All threads shutdown ~ Reloading bot"); + load("default.dbj"); +} diff --git a/libs/SoloPlay/Threads/ToolsThread.js b/libs/SoloPlay/Threads/ToolsThread.js index 7e966948..6356b582 100644 --- a/libs/SoloPlay/Threads/ToolsThread.js +++ b/libs/SoloPlay/Threads/ToolsThread.js @@ -396,9 +396,8 @@ function main () { break; case sdk.keys.NumpadSlash: // re-load default console.log("ÿc8ToolsThread :: " + sdk.colors.Red + "Stopping threads and waiting 5 seconds to restart"); - this.stopDefault() && delay(5e3); - console.log("Starting libs/SoloPlay/SoloPlay.js"); - load("libs/SoloPlay/SoloPlay.js"); + this.stopDefault() && delay(1e3); + load("libs/SoloPlay/Threads/Reload.js"); break; } From 9232dcae8fb35d78428b8e6912c9410f672fa50d Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Mon, 13 Mar 2023 09:43:31 -0400 Subject: [PATCH 081/263] Update barbarian.SingerBuild.js - Update Singer build to new format --- .../barbarian/barbarian.SingerBuild.js | 215 +++++++++--------- 1 file changed, 113 insertions(+), 102 deletions(-) diff --git a/libs/SoloPlay/BuildFiles/barbarian/barbarian.SingerBuild.js b/libs/SoloPlay/BuildFiles/barbarian/barbarian.SingerBuild.js index 6d5884a5..e074573c 100644 --- a/libs/SoloPlay/BuildFiles/barbarian/barbarian.SingerBuild.js +++ b/libs/SoloPlay/BuildFiles/barbarian/barbarian.SingerBuild.js @@ -6,112 +6,123 @@ * */ -const finalBuild = { - caster: true, - skillstab: sdk.skills.tabs.Warcries, - wantedskills: [sdk.skills.WarCry, sdk.skills.Shout], - usefulskills: [sdk.skills.IncreasedSpeed, sdk.skills.NaturalResistance], - precastSkills: [sdk.skills.BattleOrders, sdk.skills.BattleCommand], - wantedMerc: MercData[sdk.skills.Might], - stats: [ - ["dexterity", 35], ["strength", 103], ["vitality", "all"] - ], - skills: [ - [sdk.skills.WarCry, 20, true], - [sdk.skills.NaturalResistance, 4, true], - [sdk.skills.BattleCry, 20, true], - [sdk.skills.BattleCommand, 1, true], - [sdk.skills.BattleOrders, 20, true], - [sdk.skills.Taunt, 20, true], - [sdk.skills.Shout, 11, false], - [sdk.skills.Howl, 15, false], - ], - autoEquipTiers: [ // autoequip final gear - // Weapon - HotO x2 dual wield - "[type] == mace && [flag] == runeword # [itemallskills] == 3 # [tier] == 100000", - // Helmet - Harlequin's Crest - "[name] == shako && [quality] == unique && [flag] != ethereal # [damageresist] == 10 # [tier] == tierscore(item, 100000)", - // Belt - Arach's - "[name] == spiderwebsash && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 90 # [tier] == tierscore(item, 100000)", - // Boots - Silkweave - "[name] == meshboots && [quality] == unique && [flag] != ethereal # [frw] >= 30 # [tier] == tierscore(item, 100000)", - // Armor - Enigma - "[type] == armor && [flag] != ethereal && [flag] == runeword # [itemallskills] == 2 # [tier] == 100000", - // Gloves - Frostburns - "[name] == gauntlets && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 10 # [tier] == 100000", - // Amulet - Maras - "[type] == amulet && [quality] == unique # [strength] == 5 && [coldresist] >= 30 # [tier] == tierscore(item, 100000)", - // Rings - SoJ - "[type] == ring && [quality] == unique # [itemmaxmanapercent] == 25 # [tier] == 100000", - // Switch - BO sticks - "([type] == club || [type] == sword || [type] == knife || [type] == throwingknife || [type] == mace) && ([quality] == magic || [flag] == runeword) && [2handed] == 0 # [itemallskills]+[warcriesskilltab]+[barbarianskills] >= 1 # [secondarytier] == 100000 + secondaryscore(item)", - // Merc Armor - Fortitude - "[type] == armor && [flag] == runeword # [enhanceddefense] >= 200 && [enhanceddamage] >= 300 # [merctier] == 100000", - // Merc Final Helmet - Eth Andy's - "[name] == demonhead && [quality] == unique && [flag] == ethereal # [strength] >= 25 && [enhanceddefense] >= 100 # [merctier] == 50000 + mercscore(item)", - // Merc Helmet - Andy's - "[name] == demonhead && [quality] == unique && [flag] != ethereal # [strength] >= 25 && [enhanceddefense] >= 100 # [merctier] == 40000 + mercscore(item)", - ], - charms: { - ResLife: { - max: 3, - have: [], - classid: sdk.items.SmallCharm, - stats: function (check) { - return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MaxHp) === 20); - } - }, +(function (module) { + module.exports = (function () { + const build = { + caster: true, + skillstab: sdk.skills.tabs.Warcries, + wantedskills: [sdk.skills.WarCry, sdk.skills.Shout], + usefulskills: [sdk.skills.IncreasedSpeed, sdk.skills.NaturalResistance], + precastSkills: [sdk.skills.BattleOrders, sdk.skills.BattleCommand], + wantedMerc: MercData[sdk.skills.Might], + stats: [ + ["dexterity", 35], ["strength", 103], ["vitality", "all"] + ], + skills: [ + [sdk.skills.WarCry, 20, true], + [sdk.skills.NaturalResistance, 4, true], + [sdk.skills.BattleCry, 20, true], + [sdk.skills.BattleCommand, 1, true], + [sdk.skills.BattleOrders, 20, true], + [sdk.skills.Taunt, 20, true], + [sdk.skills.Shout, 11, false], + [sdk.skills.Howl, 15, false], + ], - ResMf: { - max: 2, - have: [], - classid: sdk.items.SmallCharm, - stats: function (check) { - return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MagicBonus) === 7); - } - }, + charms: { + ResLife: { + max: 3, + have: [], + classid: sdk.items.SmallCharm, + stats: function (check) { + return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MaxHp) === 20); + } + }, - ResFHR: { - max: 1, - have: [], - classid: sdk.items.SmallCharm, - stats: function (check) { - return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.FHR) === 5); - } - }, + ResMf: { + max: 2, + have: [], + classid: sdk.items.SmallCharm, + stats: function (check) { + return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MagicBonus) === 7); + } + }, - Skiller: { - max: 2, - have: [], - classid: sdk.items.GrandCharm, - stats: function (check) { - return (!check.unique && check.classid === this.classid && check.getStat(sdk.stats.AddSkillTab, sdk.skills.tabs.Warcries) === 1 - && check.getStat(sdk.stats.MaxHp) >= 40); - } - }, - }, + ResFHR: { + max: 1, + have: [], + classid: sdk.items.SmallCharm, + stats: function (check) { + return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.FHR) === 5); + } + }, - AutoBuildTemplate: { - 1: { - Update: function () { - Config.AttackSkill = [sdk.skills.BattleCry, sdk.skills.WarCry, -1, sdk.skills.WarCry, -1]; - Config.BeltColumn = ["hp", "hp", "mp", "rv"]; - Config.MPBuffer = 4; - Config.HPBuffer = 2; - } - }, - }, + Skiller: { + max: 2, + have: [], + classid: sdk.items.GrandCharm, + stats: function (check) { + return (!check.unique && check.classid === this.classid && check.getStat(sdk.stats.AddSkillTab, sdk.skills.tabs.Warcries) === 1 + && check.getStat(sdk.stats.MaxHp) >= 40); + } + }, + }, - respec: function () { - if (me.classic) { - return me.charlvl >= 75 && me.diablo; - } else { - return me.haveAll([{ name: sdk.locale.items.Enigma }, { name: sdk.locale.items.HeartoftheOak }]); - } - }, + AutoBuildTemplate: { + 1: { + Update: function () { + Config.AttackSkill = [sdk.skills.BattleCry, sdk.skills.WarCry, -1, sdk.skills.WarCry, -1]; + Config.BeltColumn = ["hp", "hp", "mp", "rv"]; + Config.MPBuffer = 4; + Config.HPBuffer = 2; + } + }, + }, - active: function () { - return this.respec() && me.getSkill(sdk.skills.WarCry, sdk.skills.subindex.HardPoints) === 20; - }, -}; + respec: function () { + if (me.classic) { + return me.charlvl >= 75 && me.diablo; + } else { + return me.haveAll([{ name: sdk.locale.items.Enigma }, { name: sdk.locale.items.HeartoftheOak }]); + } + }, + + active: function () { + return this.respec() && me.getSkill(sdk.skills.WarCry, sdk.skills.subindex.HardPoints) === 20; + }, + }; + + let finalGear = [ // autoequip final gear + // Weapon - HotO x2 dual wield + "[type] == mace && [flag] == runeword # [itemallskills] == 3 # [tier] == 100000", + // Helmet - Harlequin's Crest + "[name] == shako && [quality] == unique && [flag] != ethereal # [damageresist] == 10 # [tier] == tierscore(item, 100000)", + // Belt - Arach's + "[name] == spiderwebsash && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 90 # [tier] == tierscore(item, 100000)", + // Boots - Silkweave + "[name] == meshboots && [quality] == unique && [flag] != ethereal # [frw] >= 30 # [tier] == tierscore(item, 100000)", + // Armor - Enigma + "[type] == armor && [flag] != ethereal && [flag] == runeword # [itemallskills] == 2 # [tier] == 100000", + // Gloves - Frostburns + "[name] == gauntlets && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 10 # [tier] == 100000", + // Amulet - Maras + "[type] == amulet && [quality] == unique # [strength] == 5 && [coldresist] >= 30 # [tier] == tierscore(item, 100000)", + // Rings - SoJ + "[type] == ring && [quality] == unique # [itemmaxmanapercent] == 25 # [tier] == 100000", + // Switch - BO sticks + "([type] == club || [type] == sword || [type] == knife || [type] == throwingknife || [type] == mace) && ([quality] == magic || [flag] == runeword) && [2handed] == 0 # [itemallskills]+[warcriesskilltab]+[barbarianskills] >= 1 # [secondarytier] == 100000 + secondaryscore(item)", + // Merc Armor - Fortitude + "[type] == armor && [flag] == runeword # [enhanceddefense] >= 200 && [enhanceddamage] >= 300 # [merctier] == 100000", + // Merc Final Helmet - Eth Andy's + "[name] == demonhead && [quality] == unique && [flag] == ethereal # [strength] >= 25 && [enhanceddefense] >= 100 # [merctier] == 50000 + mercscore(item)", + // Merc Helmet - Andy's + "[name] == demonhead && [quality] == unique && [flag] != ethereal # [strength] >= 25 && [enhanceddefense] >= 100 # [merctier] == 40000 + mercscore(item)", + ]; + + NTIP.buildList(finalGear); + NTIP.buildFinalGear(finalGear); + + return build; + })(); +})(module); From f84117b1db27537c502ad1dd377e5c8411debdde Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Wed, 15 Mar 2023 03:31:46 -0400 Subject: [PATCH 082/263] Speed up tierscore a bit - removed some duplicate checks, only perform resist check section if the item has resists --- libs/SoloPlay/Functions/DynamicTiers.js | 98 ++++++++++++++---------- libs/SoloPlay/Functions/ItemOverrides.js | 57 +++++++------- 2 files changed, 84 insertions(+), 71 deletions(-) diff --git a/libs/SoloPlay/Functions/DynamicTiers.js b/libs/SoloPlay/Functions/DynamicTiers.js index a4f6908e..c1353922 100644 --- a/libs/SoloPlay/Functions/DynamicTiers.js +++ b/libs/SoloPlay/Functions/DynamicTiers.js @@ -342,19 +342,18 @@ * @todo Breakpoint scoring similar to how res is scored */ const tierscore = function (item, tier, bodyloc) { + const itembodyloc = Item.getBodyLoc(item); + if (!itembodyloc.length) return -1; + bodyloc === undefined && (bodyloc = itembodyloc.last()); + const buildInfo = Check.currentBuild(); + const canTele = Pather.canTeleport(); + const eqItem = me.getEquippedItem(bodyloc); const generalScore = () => { let generalRating = 0; - let canTele = Pather.canTeleport(); - // get item body location - let itembodyloc = Item.getBodyLoc(item); - bodyloc === undefined && (bodyloc = itembodyloc.last()); // extract bodyloc from array - // get item cbf stat from olditem equipped on body location - let eqItem = me.getEquippedItem(bodyloc); - if (!canTele && item.getStatEx(sdk.stats.CannotbeFrozen)) { // check if we have cbf but make sure its not from the item we are trying to un-equip let haveCBF = (me.getStat(sdk.stats.CannotbeFrozen) > 0 && (eqItem && !eqItem.getStatEx(sdk.stats.CannotbeFrozen))); @@ -386,37 +385,54 @@ }; const resistScore = () => { - let itembodyloc = Item.getBodyLoc(item); - if (!itembodyloc) return 0; - bodyloc === undefined && (bodyloc = itembodyloc.last()); // extract bodyloc from array - let resistRating = 0; - // current total resists - let [currFR, currCR, currLR, currPR] = [me.fireRes, me.coldRes, me.lightRes, me.poisonRes]; - // get item resists stats from olditem equipped on body location - let eqItem = me.getEquippedItem(bodyloc); - let [olditemFR, olditemCR, olditemLR, olditemPR] = [0, 0, 0, 0]; - if (eqItem) { - // equipped resists - [olditemFR, olditemCR] = [eqItem.getStatEx(sdk.stats.FireResist), eqItem.getStatEx(sdk.stats.ColdResist)]; - [olditemLR, olditemPR] = [eqItem.getStatEx(sdk.stats.LightResist), eqItem.getStatEx(sdk.stats.PoisonResist)]; - } - // subtract olditem resists from current total resists - let [baseFR, baseCR, baseLR, basePR] = [(currFR - olditemFR), (currCR - olditemCR), (currLR - olditemLR), (currPR - olditemPR)]; - // if baseRes < max resists give score value upto max resists reached - const maxRes = me.hell ? 75 : (75 + me.getResPenalty(me.diff + 1) - me.getResPenalty(me.diff)); - let [FRlimit, CRlimit, LRlimit, PRlimit] = [Math.max(maxRes - baseFR, 0), Math.max(maxRes - baseCR, 0), Math.max(maxRes - baseLR, 0), Math.max(maxRes - basePR, 0)]; + // get new item stats - let [newitemFR, newitemCR] = [Math.max(item.getStatEx(sdk.stats.FireResist), 0), Math.max(item.getStatEx(sdk.stats.ColdResist), 0)]; - let [newitemLR, newitemPR] = [Math.max(item.getStatEx(sdk.stats.LightResist), 0), Math.max(item.getStatEx(sdk.stats.PoisonResist), 0)]; - // newitemRes upto reslimit - let [effectiveFR, effectiveCR] = [Math.min(newitemFR, FRlimit), Math.min(newitemCR, CRlimit)]; - let [effectiveLR, effectivePR] = [Math.min(newitemLR, LRlimit), Math.min(newitemPR, PRlimit)]; - // sum resistRatings - resistRating += effectiveFR * tierWeights.resistWeights.FR; // add fireresist - resistRating += effectiveCR * tierWeights.resistWeights.CR; // add coldresist - resistRating += effectiveLR * tierWeights.resistWeights.LR; // add literesist - resistRating += effectivePR * tierWeights.resistWeights.PR; // add poisonresist + let [newitemFR, newitemCR, newitemLR, newitemPR] = [ + Math.max(item.getStatEx(sdk.stats.FireResist), 0), + Math.max(item.getStatEx(sdk.stats.ColdResist), 0), + Math.max(item.getStatEx(sdk.stats.LightResist), 0), + Math.max(item.getStatEx(sdk.stats.PoisonResist), 0) + ]; + // only enter next block if we have a new item with resists + if (newitemFR || newitemCR || newitemLR || newitemPR) { + const maxRes = me.hell ? 75 : (75 + me.getResPenalty(me.diff + 1) - me.getResPenalty(me.diff)); + // get item resists stats from olditem equipped on body location + let [olditemFR, olditemCR, olditemLR, olditemPR] = [0, 0, 0, 0]; + if (eqItem) { + // equipped resists + [olditemFR, olditemCR, olditemLR, olditemPR] = [ + eqItem.getStatEx(sdk.stats.FireResist), eqItem.getStatEx(sdk.stats.ColdResist), + eqItem.getStatEx(sdk.stats.LightResist), eqItem.getStatEx(sdk.stats.PoisonResist) + ]; + } + // subtract olditem resists from current total resists + const [baseFR, baseCR, baseLR, basePR] = [ + me.fireRes - olditemFR, + me.coldRes - olditemCR, + me.lightRes - olditemLR, + me.poisonRes - olditemPR, + ]; + // if baseRes < max resists give score value upto max resists reached + const [FRlimit, CRlimit, LRlimit, PRlimit] = [ + Math.max(maxRes - baseFR, 0), + Math.max(maxRes - baseCR, 0), + Math.max(maxRes - baseLR, 0), + Math.max(maxRes - basePR, 0) + ]; + // newitemRes upto reslimit + let [effectiveFR, effectiveCR, effectiveLR, effectivePR] = [ + Math.min(newitemFR, FRlimit), + Math.min(newitemCR, CRlimit), + Math.min(newitemLR, LRlimit), + Math.min(newitemPR, PRlimit) + ]; + // sum resistRatings + resistRating += effectiveFR * tierWeights.resistWeights.FR; // add fireresist + resistRating += effectiveCR * tierWeights.resistWeights.CR; // add coldresist + resistRating += effectiveLR * tierWeights.resistWeights.LR; // add literesist + resistRating += effectivePR * tierWeights.resistWeights.PR; // add poisonresist + } // sum max resists weights resistRating += (item.getStatEx(sdk.stats.MaxFireResist) * tierWeights.resistWeights.MAXFR); resistRating += (item.getStatEx(sdk.stats.MaxLightResist) * tierWeights.resistWeights.MAXLR); @@ -454,16 +470,15 @@ || Config.LowManaSkill.includes(sdk.skills.Attack) || ([sdk.items.type.Bow, sdk.items.type.AmazonBow, sdk.items.type.Crossbow].includes(item.itemType) && CharData.skillData.bowData.bowOnSwitch)) { let meleeRating = 0; - let eleDmgModifer = [sdk.items.type.Ring, sdk.items.type.Amulet].includes(item.itemType) ? 2 : 1; - - item.getStatEx(sdk.stats.ReplenishDurability) && (meleeRating += 15); - item.getStatEx(sdk.stats.IgnoreTargetDefense) && (meleeRating += 50); // dirty fix maybe? if (me.barbarian && SetUp.currentBuild !== "Immortalwhirl" && item.strictlyTwoHanded) { return 0; } - + let eleDmgModifer = [sdk.items.type.Ring, sdk.items.type.Amulet].includes(item.itemType) ? 2 : 1; + + item.getStatEx(sdk.stats.ReplenishDurability) && (meleeRating += 15); + item.getStatEx(sdk.stats.IgnoreTargetDefense) && (meleeRating += 50); // should these be added and calc avg dmg instead? // Sometimes we replace good weps with 2-300 ED weps that may be high dmg but aren't as good as the item we replaced //buildRating += item.getStatEx(sdk.stats.MinDamage) * buildWeights.MINDMG; // add MIN damage @@ -471,7 +486,6 @@ //buildRating += item.getStatEx(sdk.stats.SecondaryMinDamage) * buildWeights.SECMINDMG; // add MIN damage //buildRating += item.getStatEx(sdk.stats.SecondaryMaxDamage) * buildWeights.SECMAXDMG; // add MAX damage meleeRating += ((item.getStatEx(sdk.stats.MaxDamage) + item.getStatEx(sdk.stats.MinDamage)) / 2) * tierWeights.meleeWeights.AVGDMG; - meleeRating += sumElementalDmg(item) * (tierWeights.meleeWeights.ELEDMG / eleDmgModifer); // add elemental damage meleeRating += item.getStatEx(sdk.stats.ToHit) * tierWeights.meleeWeights.AR; // add AR meleeRating += item.getStatEx(sdk.stats.CrushingBlow) * tierWeights.meleeWeights.CB; // add crushing blow diff --git a/libs/SoloPlay/Functions/ItemOverrides.js b/libs/SoloPlay/Functions/ItemOverrides.js index 921a6eef..89d323d0 100644 --- a/libs/SoloPlay/Functions/ItemOverrides.js +++ b/libs/SoloPlay/Functions/ItemOverrides.js @@ -73,36 +73,35 @@ Item.identify = function (item) { * @param {ItemUnit} item */ Item.getBodyLoc = function (item) { - let bodyLoc = (() => { - switch (true) { - case Item.shieldTypes.includes(item.itemType): - return sdk.body.LeftArm; - case item.itemType === sdk.items.type.Armor: - return sdk.body.Armor; - case item.itemType === sdk.items.type.Ring: - return [sdk.body.RingRight, sdk.body.RingLeft]; - case item.itemType === sdk.items.type.Amulet: - return sdk.body.Neck; - case item.itemType === sdk.items.type.Boots: - return sdk.body.Feet; - case item.itemType === sdk.items.type.Gloves: - return sdk.body.Gloves; - case item.itemType === sdk.items.type.Belt: - return sdk.body.Belt; - case Item.helmTypes.includes(item.itemType): - return sdk.body.Head; - case Item.weaponTypes.includes(item.itemType): - return me.barbarian && item.twoHanded && !item.strictlyTwoHanded - ? [sdk.body.RightArm, sdk.body.LeftArm] - : sdk.body.RightArm; - case [sdk.items.type.HandtoHand, sdk.items.type.AssassinClaw].includes(item.itemType): - return !Check.currentBuild().caster && me.assassin ? [sdk.body.RightArm, sdk.body.LeftArm] : sdk.body.RightArm; - default: - return false; - } - })(); + if (!item || item.isInsertable) return []; + if (Item.shieldTypes.includes(item.itemType)) return [sdk.body.LeftArm]; + if (Item.helmTypes.includes(item.itemType)) return [sdk.body.Head]; + if (Item.weaponTypes.includes(item.itemType)) { + return me.barbarian && item.twoHanded && !item.strictlyTwoHanded + ? [sdk.body.RightArm, sdk.body.LeftArm] + : [sdk.body.RightArm]; + } - return Array.isArray(bodyLoc) ? bodyLoc : [bodyLoc]; + switch (item.itemType) { + case sdk.items.type.Armor: + return [sdk.body.Armor]; + case sdk.items.type.Ring: + return [sdk.body.RingRight, sdk.body.RingLeft]; + case sdk.items.type.Amulet: + return [sdk.body.Neck]; + case sdk.items.type.Boots: + return [sdk.body.Feet]; + case sdk.items.type.Gloves: + return [sdk.body.Gloves]; + case sdk.items.type.Belt: + return [sdk.body.Belt]; + case sdk.items.type.AssassinClaw: + case sdk.items.type.HandtoHand: + return !Check.currentBuild().caster && me.assassin + ? [sdk.body.RightArm, sdk.body.LeftArm] : [sdk.body.RightArm]; + } + + return []; }; // todo: clean this up From df9a7bee81be3d1a2f435110ad86f2afcf1551b1 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Sun, 19 Mar 2023 11:59:09 -0400 Subject: [PATCH 083/263] Update AutoMuleOverrides.js - Fix automule for excluded items, the check got removed during the structure change --- libs/SoloPlay/Functions/AutoMuleOverrides.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/libs/SoloPlay/Functions/AutoMuleOverrides.js b/libs/SoloPlay/Functions/AutoMuleOverrides.js index c58d7134..22535a47 100644 --- a/libs/SoloPlay/Functions/AutoMuleOverrides.js +++ b/libs/SoloPlay/Functions/AutoMuleOverrides.js @@ -49,7 +49,9 @@ AutoMule.getMuleItems = function () { let items = me.getItemsEx() .filter(function (item) { // we don't mule items that are equipped or are junk - if (!item.isInStorage || Town.ignoredItemTypes.includes(item.itemType)) return false; + if (!item.isInStorage || Town.ignoreType(item.itemType)) return false; + // don't mule excluded items + if (AutoMule.matchItem(item, Config.AutoMule.Exclude)) return false; // don't mule quest items if (questItem(item)) return false; // don't mule wanted autoequip items From e4b555d0799276de9dd77f65a662893b3b494aef Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Sun, 19 Mar 2023 17:50:03 -0400 Subject: [PATCH 084/263] Update PotData.js - Restructured it, made _mpPots and _hpPots static private members as they never change so the old method of refetching them every time was wasteful. Made everything read-only --- libs/SoloPlay/Modules/GameData/PotData.js | 237 ++++++++++++---------- 1 file changed, 134 insertions(+), 103 deletions(-) diff --git a/libs/SoloPlay/Modules/GameData/PotData.js b/libs/SoloPlay/Modules/GameData/PotData.js index 22da0c7a..6ad3f5c3 100644 --- a/libs/SoloPlay/Modules/GameData/PotData.js +++ b/libs/SoloPlay/Modules/GameData/PotData.js @@ -15,110 +15,141 @@ } }(this, function () { "use strict"; - const PotData = { - pots: [], - getMpPots: function () { - return this.pots.filter((el) => el.type === "mp"); - }, - getHpPots: function () { - return this.pots.filter((el) => el.type === "hp"); - }, - }; + const PotData = (function () { + /** + * @typedef {Object} Pot + * @property {string} type + * @property {number[]} effect + * @property {number} cost + * @property {number} duration + * @property {Array} [recipe] + */ - PotData.pots[sdk.items.MinorHealingPotion] = { - type: "hp", - effect: [45, 30, 30, 45, 60, 30, 45], - cost: 30, - duration: 7.68 - }; - PotData.pots[sdk.items.LightHealingPotion] = { - type: "hp", - effect: [90, 60, 60, 90, 120, 60, 90], - cost: 67, - duration: 6.4 - }; - PotData.pots[sdk.items.HealingPotion] = { - type: "hp", - effect: [150, 100, 100, 150, 200, 100, 150], - cost: 112, - duration: 6.84 - }; - PotData.pots[sdk.items.GreaterHealingPotion] = { - type: "hp", - effect: [270, 180, 180, 270, 360, 180, 270], - cost: 225, - duration: 7.68 - }; - PotData.pots[sdk.items.SuperHealingPotion] = { - type: "hp", - effect: [480, 320, 320, 480, 640, 320, 480], - cost: undefined, - duration: 10.24 - }; - PotData.pots[sdk.items.MinorManaPotion] = { - type: "mp", - effect: [30, 40, 40, 30, 20, 40, 30], - cost: 60, - duration: 5.12 - }; - PotData.pots[sdk.items.LightManaPotion] = { - type: "mp", - effect: [60, 80, 80, 60, 40, 80, 60], - cost: 135, - duration: 5.12 - }; - PotData.pots[sdk.items.ManaPotion] = { - type: "mp", - effect: [120, 160, 160, 120, 80, 160, 120], - cost: 270, - duration: 5.12 - }; - PotData.pots[sdk.items.GreaterManaPotion] = { - type: "mp", - effect: [225, 300, 300, 225, 150, 300, 225], - cost: 450, - duration: 5.12 - }; - PotData.pots[sdk.items.SuperManaPotion] = { - type: "mp", - effect: [375, 500, 500, 375, 250, 500, 375], - cost: undefined, - duration: 5.12 - }; - PotData.pots[sdk.items.RejuvenationPotion] = { - type: "rv", - effect: [35, 35, 35, 35, 35, 35, 35], - cost: undefined, - duration: 0.04, - recipe: [ - [ - sdk.items.HealingPotion, sdk.items.HealingPotion, sdk.items.HealingPotion, - sdk.items.ManaPotion, sdk.items.ManaPotion, sdk.items.ManaPotion, - function (item) { - return item.itemType === sdk.items.type.ChippedGem; - } + /** @type {Object.} */ + const _pots = {}; + + _pots[sdk.items.MinorHealingPotion] = { + type: "hp", + effect: [45, 30, 30, 45, 60, 30, 45], + cost: 30, + duration: 7.68 + }; + _pots[sdk.items.LightHealingPotion] = { + type: "hp", + effect: [90, 60, 60, 90, 120, 60, 90], + cost: 67, + duration: 6.4 + }; + _pots[sdk.items.HealingPotion] = { + type: "hp", + effect: [150, 100, 100, 150, 200, 100, 150], + cost: 112, + duration: 6.84 + }; + _pots[sdk.items.GreaterHealingPotion] = { + type: "hp", + effect: [270, 180, 180, 270, 360, 180, 270], + cost: 225, + duration: 7.68 + }; + _pots[sdk.items.SuperHealingPotion] = { + type: "hp", + effect: [480, 320, 320, 480, 640, 320, 480], + cost: undefined, + duration: 10.24 + }; + _pots[sdk.items.MinorManaPotion] = { + type: "mp", + effect: [30, 40, 40, 30, 20, 40, 30], + cost: 60, + duration: 5.12 + }; + _pots[sdk.items.LightManaPotion] = { + type: "mp", + effect: [60, 80, 80, 60, 40, 80, 60], + cost: 135, + duration: 5.12 + }; + _pots[sdk.items.ManaPotion] = { + type: "mp", + effect: [120, 160, 160, 120, 80, 160, 120], + cost: 270, + duration: 5.12 + }; + _pots[sdk.items.GreaterManaPotion] = { + type: "mp", + effect: [225, 300, 300, 225, 150, 300, 225], + cost: 450, + duration: 5.12 + }; + _pots[sdk.items.SuperManaPotion] = { + type: "mp", + effect: [375, 500, 500, 375, 250, 500, 375], + cost: undefined, + duration: 5.12 + }; + _pots[sdk.items.RejuvenationPotion] = { + type: "rv", + effect: [35, 35, 35, 35, 35, 35, 35], + cost: undefined, + duration: 0.04, + recipe: [ + [ + sdk.items.HealingPotion, sdk.items.HealingPotion, sdk.items.HealingPotion, + sdk.items.ManaPotion, sdk.items.ManaPotion, sdk.items.ManaPotion, + function (item) { + return item.itemType === sdk.items.type.ChippedGem; + } + ] + ] + }; + _pots[sdk.items.FullRejuvenationPotion] = { + type: "rv", + effect: [100, 100, 100, 100, 100, 100, 100], + cost: undefined, + duration: 0.04, + recipe: [ + // Recipe is either an classid, or an function that returns true on the correct item + [ + sdk.items.RejuvenationPotion, sdk.items.RejuvenationPotion, sdk.items.RejuvenationPotion // 3 normal rv's + ], + [ + sdk.items.HealingPotion, sdk.items.HealingPotion, sdk.items.HealingPotion, + sdk.items.ManaPotion, sdk.items.ManaPotion, sdk.items.ManaPotion, + function (item) { + return item.itemType === sdk.items.type.Gem; + } + ], ] - ] - }; - PotData.pots[sdk.items.FullRejuvenationPotion] = { - type: "rv", - effect: [100, 100, 100, 100, 100, 100, 100], - cost: undefined, - duration: 0.04, - recipe: [ - // Recipe is either an classid, or an function that returns true on the correct item - [ - sdk.items.RejuvenationPotion, sdk.items.RejuvenationPotion, sdk.items.RejuvenationPotion // 3 normal rv's - ], - [ - sdk.items.HealingPotion, sdk.items.HealingPotion, sdk.items.HealingPotion, - sdk.items.ManaPotion, sdk.items.ManaPotion, sdk.items.ManaPotion, - function (item) { - return item.itemType === sdk.items.type.Gem; - } - ], - ] - }; - + }; + + /** @type {Pot[]} */ + const _mpPots = [ + _pots[sdk.items.MinorManaPotion], _pots[sdk.items.LightManaPotion], _pots[sdk.items.ManaPotion], + _pots[sdk.items.GreaterManaPotion], _pots[sdk.items.SuperManaPotion] + ]; + /** @type {Pot[]} */ + const _hpPots = [ + _pots[sdk.items.MinorHealingPotion], _pots[sdk.items.LightHealingPotion], _pots[sdk.items.HealingPotion], + _pots[sdk.items.GreaterHealingPotion], _pots[sdk.items.SuperHealingPotion] + ]; + + Object.keys(_pots) + .forEach(key => Object.freeze(_pots[key])); + Object.freeze(_mpPots); + Object.freeze(_hpPots); + + return { + pots: _pots, + + getMpPots: function () { + return _mpPots; + }, + getHpPots: function () { + return _hpPots; + }, + }; + })(); + return PotData; })); From 42c2a2be260bf22c84c5f447f8d79c2008e1af67 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Mon, 20 Mar 2023 14:06:11 -0400 Subject: [PATCH 085/263] Fix needStash for equipped charms - Speeds up check, we were always hitting needStash because the charms aren't in locked spots add a set to keep track of our current charms to ensure they get properly excluded. --- libs/SoloPlay/Functions/ItemOverrides.js | 3 +++ libs/SoloPlay/Functions/TownOverrides.js | 19 +++++++++++++++++++ 2 files changed, 22 insertions(+) diff --git a/libs/SoloPlay/Functions/ItemOverrides.js b/libs/SoloPlay/Functions/ItemOverrides.js index 89d323d0..df66730c 100644 --- a/libs/SoloPlay/Functions/ItemOverrides.js +++ b/libs/SoloPlay/Functions/ItemOverrides.js @@ -1317,9 +1317,11 @@ Item.removeItemsMerc = function () { changed && updateMyData(); }; + Item.keptCharmGids = new Set(); Item.autoEquipCharms = function () { // No charms in classic if (me.classic) return; + Item.keptCharmGids.clear(); console.log("ÿc8Kolbot-SoloPlayÿc0: Entering charm auto equip"); let tick = getTickCount(); @@ -1376,6 +1378,7 @@ Item.removeItemsMerc = function () { if (totalKeep.length > 0) { for (let i = 0; i < totalKeep.length; i++) { + Item.keptCharmGids.add(totalKeep[i].gid); if (totalKeep[i].isInStash && !Cubing.checkItem(totalKeep[i])) { !getUIFlag(sdk.uiflags.Stash) && Town.openStash() && delay(300 + me.ping); if (Storage.Inventory.CanFit(totalKeep[i]) && Storage.Inventory.MoveTo(totalKeep[i])) { diff --git a/libs/SoloPlay/Functions/TownOverrides.js b/libs/SoloPlay/Functions/TownOverrides.js index 919ea10a..0d7a2aaa 100644 --- a/libs/SoloPlay/Functions/TownOverrides.js +++ b/libs/SoloPlay/Functions/TownOverrides.js @@ -311,6 +311,25 @@ Town.identify = function () { return true; }; +Town.needStash = function () { + if (Config.StashGold + && me.getStat(sdk.stats.Gold) >= Config.StashGold + && me.getStat(sdk.stats.GoldBank) < 25e5) { + return true; + } + + let items = (Storage.Inventory.Compare(Config.Inventory) || []) + .filter(item => !Town.ignoreType(item.itemType) && (!item.isCharm || !Item.keptCharmGids.has(item.gid))); + + for (let i = 0; i < items.length; i += 1) { + if (Storage.Stash.CanFit(items[i])) { + return true; + } + } + + return false; +}; + /** * @param {ItemUnit} item */ From 34e8efecdd43ca362c578b257e207c44ec7ceb63 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Tue, 21 Mar 2023 00:09:01 -0400 Subject: [PATCH 086/263] Speed up townChores - quite a bit actually, from 4seconds to ~1s when nothing is actually done. - Charms were causing some of the slow down because the kept ones weren't cached so the list was being rebuilt everytime - prebuild the charmlist once in the main charm equip function then pass it to the sub functions so we don't need to refetch the items - Faster Town.identify, pre-filter to only have the unid items in the list, move me.fieldId check after so we don't run to npc for no reason --- libs/SoloPlay/Functions/ItemOverrides.js | 52 +++++++++++++++++------- libs/SoloPlay/Functions/TownOverrides.js | 18 ++++---- 2 files changed, 48 insertions(+), 22 deletions(-) diff --git a/libs/SoloPlay/Functions/ItemOverrides.js b/libs/SoloPlay/Functions/ItemOverrides.js index df66730c..cb8989f3 100644 --- a/libs/SoloPlay/Functions/ItemOverrides.js +++ b/libs/SoloPlay/Functions/ItemOverrides.js @@ -938,10 +938,15 @@ Item.removeItemsMerc = function () { } }; - Item.autoEquipSC = function () { + /** + * Handle small charm autoequip + * @param {ItemUnit[]} charmList + * @returns {{ keep: ItemUnit[], sell: ItemUnit[] }} + */ + Item.autoEquipSC = function (charmList = []) { let verbose = Developer.debugging.smallCharm; // build list of our charms - let items = me.getItemsEx() + let items = (charmList.length ? charmList : me.getItemsEx()) .filter((charm) => charm.isInStorage && charm.classid === sdk.items.SmallCharm && charm.magic); if (!items.length) { @@ -958,9 +963,14 @@ Item.removeItemsMerc = function () { return { keep: charms.keep, sell: charms.checkList }; }; - Item.autoEquipLC = function () { + /** + * Handle large charm autoequip + * @param {ItemUnit[]} charmList + * @returns {{ keep: ItemUnit[], sell: ItemUnit[] }} + */ + Item.autoEquipLC = function (charmList = []) { let verbose = Developer.debugging.largeCharm; - let items = me.getItemsEx() + let items = (charmList.length ? charmList : me.getItemsEx()) .filter((charm) => charm.isInStorage && charm.classid === sdk.items.LargeCharm && charm.magic); if (!items.length) { @@ -978,12 +988,13 @@ Item.removeItemsMerc = function () { }; /** - * @memberof Item - * @returns { { keep: ItemUnit[], sell: ItemUnit[] } } + * Handle grand charm autoequip + * @param {ItemUnit[]} charmList + * @returns {{ keep: ItemUnit[], sell: ItemUnit[] }} */ - Item.autoEquipGC = function () { + Item.autoEquipGC = function (charmList = []) { let verbose = Developer.debugging.largeCharm; - let items = me.getItemsEx() + let items = (charmList.length ? charmList : me.getItemsEx()) .filter((charm) => charm.isInStorage && charm.classid === sdk.items.GrandCharm && charm.magic); if (!items.length) { @@ -1173,6 +1184,8 @@ Item.removeItemsMerc = function () { if (item.isCharm && item.unique) return true; // is one of our final charms if (myData.me.charmGids.includes(item.gid)) return true; + // is in our checkList + if (Item.keptCharmGids.has(item.gid)) return true; let lowestCharm; let items = me.getItemsEx() @@ -1319,18 +1332,27 @@ Item.removeItemsMerc = function () { Item.keptCharmGids = new Set(); Item.autoEquipCharms = function () { - // No charms in classic + // No charms in classic if (me.classic) return; - Item.keptCharmGids.clear(); console.log("ÿc8Kolbot-SoloPlayÿc0: Entering charm auto equip"); let tick = getTickCount(); + let charms = me.getItemsEx() + .filter(item => item.isInStorage && item.isCharm && item.magic); + // don't do anything if we don't have any charms + if ((!charms.length) + // don't do anything if we have the same charms as last time + || ((Item.keptCharmGids.size && charms.every(c => Item.keptCharmGids.has(c.gid))))) { + console.log("ÿc8Kolbot-SoloPlayÿc0: Exiting charm auto equip. Time elapsed: " + Tracker.formatTime(getTickCount() - tick)); + return; + } + if (Item.keptCharmGids.size && charms.every(c => Item.keptCharmGids.has(c.gid))) return; + Item.keptCharmGids.clear(); let totalKeep = [], totalSell = []; - let GCs = Item.autoEquipGC(); - let LCs = Item.autoEquipLC(); - let SCs = Item.autoEquipSC(); - let specialCharms = me.getItemsEx() - .filter((charm) => charm.isCharm && charm.unique); + let GCs = Item.autoEquipGC(charms); + let LCs = Item.autoEquipLC(charms); + let SCs = Item.autoEquipSC(charms); + let specialCharms = charms.filter((charm) => charm.unique); let verbose = !!(Developer.debugging.smallCharm || Developer.debugging.largeCharm || Developer.debugging.grandCharm); if (verbose) { diff --git a/libs/SoloPlay/Functions/TownOverrides.js b/libs/SoloPlay/Functions/TownOverrides.js index 0d7a2aaa..532042f6 100644 --- a/libs/SoloPlay/Functions/TownOverrides.js +++ b/libs/SoloPlay/Functions/TownOverrides.js @@ -230,15 +230,10 @@ Town.identify = function () { */ if (me.gold < 15000 && NPCAction.cainID(true)) return true; - let list = (Storage.Inventory.Compare(Config.Inventory) || []); + let list = (Storage.Inventory.Compare(Config.Inventory) || []) + .filter(item => !item.identified); if (!list.length) return false; - let tome = me.getTome(sdk.items.TomeofIdentify); - // if we have a tome might as well use it - this might prevent us from having to run from one npc to another - if (tome && tome.getStat(sdk.stats.Quantity) > 0 && Town.getDistance(Town.tasks[me.act - 1].Shop) > 5) { - me.fieldID(); // not in the field but oh well no need to repeat the code - } - // Avoid unnecessary NPC visits // Only unid items or sellable junk (low level) should trigger a NPC visit if (!list.some(item => { @@ -249,6 +244,15 @@ Town.identify = function () { return false; } + let tome = me.getTome(sdk.items.TomeofIdentify); + // if we have a tome might as well use it - this might prevent us from having to run from one npc to another + if (tome && tome.getStat(sdk.stats.Quantity) > 0 && Town.getDistance(Town.tasks[me.act - 1].Shop) > 5) { + // not in the field but oh well no need to repeat the code + if (me.fieldID() && !me.getUnids().length) { + return true; + } + } + let npc = Town.initNPC("Shop", "identify"); if (!npc) return false; From 90d5ab0cd68f898502560f1791c063bbffbb0606 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Tue, 21 Mar 2023 14:30:23 -0400 Subject: [PATCH 087/263] Update Globals.js - Clean up `Check.nextDifficulty`, - fix useless checking when we are already in hell. --- libs/SoloPlay/Functions/Globals.js | 45 ++++++++++++++++++------------ 1 file changed, 27 insertions(+), 18 deletions(-) diff --git a/libs/SoloPlay/Functions/Globals.js b/libs/SoloPlay/Functions/Globals.js index b98268d1..8fe4687b 100644 --- a/libs/SoloPlay/Functions/Globals.js +++ b/libs/SoloPlay/Functions/Globals.js @@ -741,30 +741,39 @@ const Check = { }, nextDifficulty: function (announce = true) { - let diffShift = me.diff; + let currDiff = me.diff; + if (currDiff === sdk.difficulty.Hell) return false; + if (["Bumper", "Socketmule"].includes(SetUp.finalBuild)) return false; + if (me.charlvl < CharInfo.levelCap) return false; + if (!me.diffCompleted) return false; + let nextDiff = null; let res = this.resistance(); - let lvlReq = !!((me.charlvl >= CharInfo.levelCap) && !["Bumper", "Socketmule"].includes(SetUp.finalBuild) && !this.broken()); - - if (me.diffCompleted) { - if (lvlReq) { - if (res.Status) { - diffShift = me.diff + 1; - announce && D2Bot.printToConsole("Kolbot-SoloPlay: next difficulty requirements met. Starting: " + sdk.difficulty.nameOf(diffShift), sdk.colors.D2Bot.Blue); + let lvlReq = !!(!this.broken()); + let [str, color] = ["", sdk.colors.D2Bot.Black]; + + if (lvlReq) { + if (res.Status) { + nextDiff = currDiff + 1; + [str, color] = ["next difficulty requirements met. Starting: " + sdk.difficulty.nameOf(nextDiff), sdk.colors.D2Bot.Blue]; + } else { + if (me.charlvl >= CharInfo.levelCap + (!me.normal ? 5 : 2)) { + nextDiff = currDiff + 1; + str = "Over leveled. Starting: " + sdk.difficulty.nameOf(nextDiff); } else { - if (me.charlvl >= CharInfo.levelCap + (!me.normal ? 5 : 2)) { - diffShift = me.diff + 1; - announce && D2Bot.printToConsole("Kolbot-SoloPlay: Over leveled. Starting: " + sdk.difficulty.nameOf(diffShift)); - } else { - announce && myPrint(sdk.difficulty.nameOf(diffShift + 1) + " requirements not met. Negative resistance. FR: " + res.FR + " | CR: " + res.CR + " | LR: " + res.LR); - return false; - } + announce && myPrint( + sdk.difficulty.nameOf(currDiff + 1) + + " requirements not met. Negative resistance. FR: " + res.FR + " | CR: " + res.CR + " | LR: " + res.LR + ); } } - } else { - return false; } - return sdk.difficulty.nameOf(diffShift); + if (!nextDiff) return false; + if (announce && str) { + D2Bot.printToConsole("Kolbot-SoloPlay: " + str, color); + } + + return sdk.difficulty.nameOf(nextDiff); }, runes: function () { From 19792419d39337299e8db3be266aa4cc75dd2c1c Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Wed, 22 Mar 2023 19:27:21 -0400 Subject: [PATCH 088/263] Re-work CharData, Refactor ItemOverrides - Removed the myData global from Globals, created me.data to use instead - Refactored CharData to reduce the repetitive code - Changed buffData to pots - Changed charmData to charms - Changed CharData.skillData.bowData to CharData.skillData.bow, (likely going to be changed later as it is still long) - Create CharmEquip and factored the whol charm section out of ItemOverrides --- D2BotSoloPlay.dbj | 8 +- .../BuildFiles/amazon/amazon.JavazonBuild.js | 2 +- .../assassin/assassin.TrapsinBuild.js | 2 +- .../sorceress/sorceress.BlovaBuild.js | 2 +- .../sorceress/sorceress.LightningBuild.js | 2 +- libs/SoloPlay/Functions/AttackOverrides.js | 18 +- libs/SoloPlay/Functions/AutoStatOverrides.js | 9 +- libs/SoloPlay/Functions/CharmEquip.js | 722 +++++++++++++++++ .../ClassAttackOverrides/AmazonAttacks.js | 2 +- .../ClassAttackOverrides/BarbarianAttacks.js | 57 +- .../NecromancerAttacks.js | 188 ++--- .../ClassAttackOverrides/PaladinAttacks.js | 2 +- .../ClassAttackOverrides/SorceressAttacks.js | 2 +- libs/SoloPlay/Functions/CubingOverrides.js | 4 +- libs/SoloPlay/Functions/DynamicTiers.js | 46 +- libs/SoloPlay/Functions/Globals.js | 165 ++-- libs/SoloPlay/Functions/ItemOverrides.js | 725 +----------------- libs/SoloPlay/Functions/Me.js | 87 ++- libs/SoloPlay/Functions/Mercenary.js | 64 +- libs/SoloPlay/Functions/NPCAction.js | 6 +- libs/SoloPlay/Functions/PickitOverrides.js | 8 +- libs/SoloPlay/Functions/Quest.js | 14 +- libs/SoloPlay/Functions/TownOverrides.js | 20 +- libs/SoloPlay/Scripts/tristram.js | 13 +- libs/SoloPlay/SoloPlay.js | 8 +- libs/SoloPlay/Threads/EventThread.js | 6 +- libs/SoloPlay/Threads/ToolsThread.js | 60 +- libs/SoloPlay/Threads/TownChicken.js | 6 +- libs/SoloPlay/Tools/CharData.js | 577 +++++++------- libs/SoloPlay/Tools/OOGOverrides.js | 24 +- libs/SoloPlay/Tools/Overlay.js | 2 +- libs/SoloPlay/Utils/Init.js | 2 +- libs/SoloPlay/Workers/EventEmitter.js | 7 +- libs/SoloPlay/index.d.ts | 102 +-- 34 files changed, 1499 insertions(+), 1463 deletions(-) create mode 100644 libs/SoloPlay/Functions/CharmEquip.js diff --git a/D2BotSoloPlay.dbj b/D2BotSoloPlay.dbj index 2340702d..0d29bdb1 100644 --- a/D2BotSoloPlay.dbj +++ b/D2BotSoloPlay.dbj @@ -60,15 +60,15 @@ let gameTracker; // initialize data files if (!FileTools.exists("data/" + me.profile + ".json") && DataFile.create()) { Starter.firstRun = true; - delay(Math.floor(rand(1, 20))); + delay(rand(10, 100)); } if (!FileTools.exists(CharData.filePath) && CharData.create()) { - delay(Math.floor(rand(1, 20))); + delay(rand(10, 100)); } -if (!FileTools.exists(CharData.filePath) && CharData.loginData.create()) { - delay(Math.floor(rand(1, 20))); +if (!FileTools.exists(CharData.login.filePath) && CharData.login.create()) { + delay(rand(10, 100)); } Developer.logPerformance && Tracker.initialize(); diff --git a/libs/SoloPlay/BuildFiles/amazon/amazon.JavazonBuild.js b/libs/SoloPlay/BuildFiles/amazon/amazon.JavazonBuild.js index ef322b66..7c86d94e 100644 --- a/libs/SoloPlay/BuildFiles/amazon/amazon.JavazonBuild.js +++ b/libs/SoloPlay/BuildFiles/amazon/amazon.JavazonBuild.js @@ -73,7 +73,7 @@ if (me.classic) { return me.charlvl >= 75 && me.diablo; } else { - return (Attack.checkInfinity() || (myData.merc.gear.includes(sdk.locale.items.Infinity) && !Misc.poll(() => me.getMerc(), 200, 50))); + return (Attack.checkInfinity() || (me.data.merc.gear.includes(sdk.locale.items.Infinity) && !Misc.poll(() => me.getMerc(), 200, 50))); } }, diff --git a/libs/SoloPlay/BuildFiles/assassin/assassin.TrapsinBuild.js b/libs/SoloPlay/BuildFiles/assassin/assassin.TrapsinBuild.js index c7ebc741..9a200a79 100644 --- a/libs/SoloPlay/BuildFiles/assassin/assassin.TrapsinBuild.js +++ b/libs/SoloPlay/BuildFiles/assassin/assassin.TrapsinBuild.js @@ -100,7 +100,7 @@ }, respec: function () { - return (Attack.checkInfinity() || (myData.merc.gear.includes(sdk.locale.items.Infinity) && !Misc.poll(() => me.getMerc(), 200, 50))) && Check.haveItem("armor", "runeword", "Enigma"); + return (Attack.checkInfinity() || (me.data.merc.gear.includes(sdk.locale.items.Infinity) && !Misc.poll(() => me.getMerc(), 200, 50))) && Check.haveItem("armor", "runeword", "Enigma"); }, active: function () { diff --git a/libs/SoloPlay/BuildFiles/sorceress/sorceress.BlovaBuild.js b/libs/SoloPlay/BuildFiles/sorceress/sorceress.BlovaBuild.js index 1f15e828..a5448499 100644 --- a/libs/SoloPlay/BuildFiles/sorceress/sorceress.BlovaBuild.js +++ b/libs/SoloPlay/BuildFiles/sorceress/sorceress.BlovaBuild.js @@ -98,7 +98,7 @@ if (me.classic) { return me.charlvl >= 75 && me.diablo; } else { - return (Attack.checkInfinity() || (myData.merc.gear.includes(sdk.locale.items.Infinity) && !Misc.poll(() => me.getMerc(), 200, 50))); + return (Attack.checkInfinity() || (me.data.merc.gear.includes(sdk.locale.items.Infinity) && !Misc.poll(() => me.getMerc(), 200, 50))); } }, diff --git a/libs/SoloPlay/BuildFiles/sorceress/sorceress.LightningBuild.js b/libs/SoloPlay/BuildFiles/sorceress/sorceress.LightningBuild.js index a652fd71..0dbf62eb 100644 --- a/libs/SoloPlay/BuildFiles/sorceress/sorceress.LightningBuild.js +++ b/libs/SoloPlay/BuildFiles/sorceress/sorceress.LightningBuild.js @@ -95,7 +95,7 @@ if (me.classic) { return me.charlvl >= 75 && me.diablo; } else { - return (Attack.checkInfinity() || (myData.merc.gear.includes(sdk.locale.items.Infinity) && !Misc.poll(() => me.getMerc(), 200, 50))); + return (Attack.checkInfinity() || (me.data.merc.gear.includes(sdk.locale.items.Infinity) && !Misc.poll(() => me.getMerc(), 200, 50))); } }, diff --git a/libs/SoloPlay/Functions/AttackOverrides.js b/libs/SoloPlay/Functions/AttackOverrides.js index 4144c01e..b9da93a1 100644 --- a/libs/SoloPlay/Functions/AttackOverrides.js +++ b/libs/SoloPlay/Functions/AttackOverrides.js @@ -812,25 +812,25 @@ Attack.clearCoordList = function (list, pick) { }; Attack.checkBowOnSwitch = function (firstInit = false) { - const preBow = CharData.skillData.bowData.bowOnSwitch; + const preBow = CharData.skillData.bow.onSwitch; const checkTypes = [sdk.items.type.AmazonBow, sdk.items.type.Bow, sdk.items.type.Crossbow, sdk.items.type.BowQuiver, sdk.items.type.CrossbowQuiver]; me.weaponswitch !== sdk.player.slot.Main && me.switchWeapons(sdk.player.slot.Main); const items = me.getItemsEx().filter(item => item && item.isEquipped && item.isOnSwap && checkTypes.includes(item.itemType)); if (preBow && !items.some(item => [sdk.items.type.AmazonBow, sdk.items.type.Bow, sdk.items.type.Crossbow].includes(item.itemType))) { - CharData.skillData.bowData.resetBowData(); + CharData.skillData.bow.resetBowData(); return; } items.forEach(item => { if ([sdk.items.type.AmazonBow, sdk.items.type.Bow, sdk.items.type.Crossbow].includes(item.itemType)) { - CharData.skillData.bowData.bowOnSwitch = true; - if (CharData.skillData.bowData.bowGid !== item.gid) { - CharData.skillData.bowData.setBowInfo(item, firstInit); + CharData.skillData.bow.onSwitch = true; + if (CharData.skillData.bow.bowGid !== item.gid) { + CharData.skillData.bow.setBowInfo(item, firstInit); } } if ([sdk.items.type.BowQuiver, sdk.items.type.CrossbowQuiver].includes(item.itemType)) { - if (CharData.skillData.bowData.quiverType !== item.itemType) { - CharData.skillData.bowData.setArrowInfo(item, firstInit); + if (CharData.skillData.bow.quiverType !== item.itemType) { + CharData.skillData.bow.setArrowInfo(item, firstInit); } } }); @@ -841,8 +841,8 @@ Attack.haveDependancy = function (itemType) { }; Attack.useBowOnSwitch = function (unit, skillId = 0, switchBack = true) { - if (!CharData.skillData.bowData.bowOnSwitch) return false; - if (!this.haveDependancy(CharData.skillData.bowData.bowType)) return false; + if (!CharData.skillData.bow.onSwitch) return false; + if (!this.haveDependancy(CharData.skillData.bow.bowType)) return false; return Skill.switchCast(skillId, { hand: sdk.skills.hand.Left, x: unit, switchBack: switchBack }); }; diff --git a/libs/SoloPlay/Functions/AutoStatOverrides.js b/libs/SoloPlay/Functions/AutoStatOverrides.js index dc180b0d..ff4c4af1 100644 --- a/libs/SoloPlay/Functions/AutoStatOverrides.js +++ b/libs/SoloPlay/Functions/AutoStatOverrides.js @@ -37,11 +37,10 @@ AutoStat.init = function (statBuildOrder, save = 0, block = 0, bulkStat = true) } if (usedStatPoints) { - myData = CharData.getStats(); - myData.me.level = me.charlvl; - myData.me.strength = me.rawStrength; - myData.me.dexterity = me.rawDexterity; - CharData.updateData("me", myData) && updateMyData(); + me.data.level = me.charlvl; + me.data.strength = me.rawStrength; + me.data.dexterity = me.rawDexterity; + CharData.updateData("me", me.data) && me.update(); } console.log("AutoStat: Finished allocating stat points"); diff --git a/libs/SoloPlay/Functions/CharmEquip.js b/libs/SoloPlay/Functions/CharmEquip.js new file mode 100644 index 00000000..cd43d92a --- /dev/null +++ b/libs/SoloPlay/Functions/CharmEquip.js @@ -0,0 +1,722 @@ +/** +* @filename CharmEquip.js +* @author theBGuy +* @desc AutoEquip for charms +* +*/ + +const CharmEquip = (function () { + /** + * Goals: + * need to be able to define what types of charms we want while leveling, and upgrade based on that + * need to be able to define what types of charms we want for final build, upgrade to that + * need to be able to handle different invoquantity values of final charms vs leveling charms + * need to be abel to handle final charms and leveling charms being the same type, in situation where we have enough of a final charm so compare it as a noraml leveling charm + * need to differentiate bewtween cubing charm or pickit wanted charm vs autoequip charm + * example: + * Imagine we are an auradin and we have 9 small charms in our inventory, Seven 5allres/20life and Two random life charms. Our build tells us we should keep 6 of the 5/20s + * so we should keep those. That leaves us with One 5/20 and Two random life charms, we should then compare the tier values and keep the highest of the two then sell or drop the third. + * As it is now, what happens is we don't compare the 7th 5/20 and we add that to the sell list while keeping the 2 lower charms. If we directly add it to the backup then the invoquantity + * gets read from the finalBuild file so instead of only keeping two it says we should keep 6. + */ + + /** + * Iterate over charm checklist, pickit result 0 and 4 get sold + * Otherwise if its not in the stash already and not a final charm try and stash it. I don't remember why I checked if it wasn't a final charm + * @param {ItemUnit[]} checkList + * @param {boolean} verbose + */ + const spliceCharmCheckList = function (checkList = [], verbose = false) { + for (let i = 0; i < checkList.length; i++) { + const currCharm = checkList[i]; + if (!currCharm || [Pickit.Result.UNWANTED, Pickit.Result.TRASH].includes(Pickit.checkItem(currCharm).result)) continue; + if (!currCharm.isInStash && !me.data.charmGids.includes(currCharm.gid)) { + if (!Storage.Stash.MoveTo(currCharm)) { + verbose && Item.logger("Dropped", currCharm); + currCharm.drop(); + } else { + if (verbose) { + Cubing.checkItem(currCharm) ? Item.logItem("Stashed Cubing Ingredient", currCharm) : Item.logItem("Stashed", currCharm); + } + } + } + + checkList.splice(i, 1); + i -= 1; + } + }; + + const spliceCharmKeepList = function (keep = [], sell = [], verbose = false) { + if (!keep.length) return; + const id = keep[0].classid; + const cInfo = (() => CharData.charms.get(id).count() || { max: 0 })(); + + // sort through kept charms + if (keep.length > cInfo.max) { + keep.sort((a, b) => NTIP.GetCharmTier(b) - NTIP.GetCharmTier(a)); + + // everything after the cap (need a better method for this in the instances where the max cap is less then leveling wanted cap) + for (let i = cInfo.max; i < keep.length; i++) { + if (!!keep[i].classid && !CharmEquip.check(keep[i])) { + sell.push(keep[i]); + verbose && console.log("ÿc8Kolbot-SoloPlayÿc0: CharmEquip Add " + keep[i].fname + " to checkList"); + keep.splice(i, 1); + i -= 1; + } + } + } + }; + + /** + * @constructor + * @param {number} classid + */ + function CharmTypeEquip (classid) { + this.classid = classid; + this.name = (() => { + switch (classid) { + case sdk.items.SmallCharm: + return "Small"; + case sdk.items.LargeCharm: + return "Large"; + case sdk.items.GrandCharm: + return "Grand"; + default: + return "Unknown"; + } + })(); + /** @type {boolean} */ + this.debugging = Developer.debugging[this.name.toLowerCase() + "Charm"]; + } + + /** + * Handle charm autoequip + * @param {ItemUnit[]} charmList + * @returns {{ keep: ItemUnit[], sell: ItemUnit[] }} + */ + CharmTypeEquip.prototype.autoEquip = function (charmList = []) { + let items = (charmList.length ? charmList : me.getItemsEx()) + .filter((charm) => charm.isInStorage && charm.classid === this.classid && charm.magic); + + if (!items.length) { + this.debugging && console.debug("No charms found"); + return { keep: [], sell: [] }; + } + + let charms = CharmEquip.sort(items, this.debugging); + spliceCharmKeepList(charms.keep, charms.checkList, this.debugging); + + this.debugging && console.log(this.name + " charm checklist length: " + charms.checkList.length); + spliceCharmCheckList(charms.checkList, this.debugging); + + return { keep: charms.keep, sell: charms.checkList }; + }; + + const _smallCharm = new CharmTypeEquip(sdk.items.SmallCharm); + const _largeCharm = new CharmTypeEquip(sdk.items.LargeCharm); + const _grandCharm = new CharmTypeEquip(sdk.items.GrandCharm); + + return { + /** @type {Set} */ + keptGids: new Set(), + + /** + * @param {ItemUnit} item + * @returns {boolean} + */ + hasCharmTier: (item) => me.expansion && Config.AutoEquip && NTIP.GetCharmTier(item) > 0, + + /** + * @param {ItemUnit} item + * @returns {boolean} + */ + isFinalCharm: (item) => me.data.charmGids.includes(item.gid), + + init: function () { + // No charms in classic + if (me.classic) return; + let myCharms = me.getItemsEx().filter(item => item.isInStorage && item.isCharm && item.magic); + let changed = false; + + const finalCharmKeys = Object.keys(me.data.charms); + const check = function (list = [], charms = []) { + for (let i = 0; i < list.length; i++) { + if (!charms.some(c => c.gid === list[i])) { + console.log("A charm was removed from our final list - updated it"); + me.data.charmGids.remove(list[i]); + list.splice(i, 1); + i--; + changed = true; + } + } + }; + + for (let i = 0; i < finalCharmKeys.length; i++) { + let cKey = finalCharmKeys[i]; + switch (me.data.charms[cKey].classid) { + case sdk.items.SmallCharm: + check(me.data.charms[cKey].have, myCharms); + + break; + case sdk.items.LargeCharm: + check(me.data.charms[cKey].have, myCharms); + + break; + case sdk.items.GrandCharm: + check(me.data.charms[cKey].have, myCharms); + + break; + } + } + + changed && me.update(); + }, + + /** + * @param {ItemUnit} charm + * @returns {string | false} + */ + getCharmType: function (charm) { + if (!charm || !charm.isCharm) return false; + if (charm.unique) return "unique"; + if (!NTIP.hasStats(charm) && NTIP.GetCharmTier(charm) > 0) return "misc"; + + let charmType = ""; + const skillerStats = me.getSkillTabs(); + + if (charm.getStat(sdk.stats.AddSkillTab, skillerStats[0])) { + charmType = "skillerTypeA"; + } else if (charm.getStat(sdk.stats.AddSkillTab, skillerStats[1])) { + charmType = "skillerTypeB"; + } else if (charm.getStat(sdk.stats.AddSkillTab, skillerStats[2])) { + charmType = "skillerTypeC"; + } + + switch (charm.prefix) { + case "Shimmering": + case "Azure": + case "Lapis": + case "Cobalt": + case "Sapphire": + case "Crimson": + case "Russet": + case "Garnet": + case "Ruby": + case "Tangerine": + case "Ocher": + case "Coral": + case "Amber": + case "Beryl": + case "Viridian": + case "Jade": + case "Emerald": + charmType = "resist"; + break; + } + + if (!charmType || charmType === "") { + switch (charm.suffix) { + case "of Fortune": + case "of Good Luck": + charmType = "magicfind"; + break; + case "of Life": + case "of Substinence": // Odd issue, seems to be misspelled wherever item.suffix pulls info from + case "of Vita": + charmType = "life"; + break; + } + } + + if (!charmType || charmType === "") { + switch (charm.prefix) { + case "Red": + case "Sanguinary": + case "Bloody": + case "Jagged": + case "Forked": + case "Serrated": + case "Bronze": + case "Iron": + case "Steel": + case "Fine": + case "Sharp": + charmType = "damage"; + break; + case "Snowy": + case "Shivering": + case "Boreal": + case "Hibernal": + case "Ember": + case "Smoldering": + case "Smoking": + case "Flaming": + case "Static": + case "Glowing": + case "Arcing": + case "Shocking": + case "Septic": + case "Foul": + case "Toxic": + case "Pestilant": + charmType = "elemental"; + break; + } + } + + if (!charmType || charmType === "") { + switch (charm.suffix) { + case "of Craftmanship": + case "of Quality": + case "of Maiming": + charmType = "damage"; + break; + case "of Strength": + case "of Dexterity": + charmType = "stats"; + break; + case "of Blight": + case "of Venom": + case "of Pestilence": + case "of Anthrax": + case "of Frost": + case "of Icicle": + case "of Glacier": + case "of Winter": + case "of Flame": + case "of Burning": + case "of Incineration": + case "of Shock": + case "of Lightning": + case "of Thunder": + case "of Storms": + charmType = "elemental"; + break; + } + } + + if (!charmType || charmType === "") { + switch (charm.prefix) { + case "Stout": + case "Burly": + case "Stalwart": + charmType = "misc"; + break; + case "Rugged": + charmType = "misc"; + break; + case "Lizard's": + case "Snake's": + case "Serpent's": + charmType = "mana"; + break; + } + } + + if (!charmType || charmType === "") { + switch (charm.suffix) { + case "of Balance": + case "of Greed": + case "of Inertia": + charmType = "misc"; + break; + } + } + + return charmType; + }, + + /** + * Handle small charm autoequip + * @param {ItemUnit[]} charmList + * @returns {{ keep: ItemUnit[], sell: ItemUnit[] }} + */ + smallCharm: function (charmList = []) { + return _smallCharm.autoEquip(charmList); + }, + /** + * Handle large charm autoequip + * @param {ItemUnit[]} charmList + * @returns {{ keep: ItemUnit[], sell: ItemUnit[] }} + */ + largeCharm: function (charmList = []) { + return _largeCharm.autoEquip(charmList); + }, + /** + * Handle grand charm autoequip + * @param {ItemUnit[]} charmList + * @returns {{ keep: ItemUnit[], sell: ItemUnit[] }} + */ + grandCharm: function (charmList = []) { + return _grandCharm.autoEquip(charmList); + }, + + /** + * @param {ItemUnit[]} items + * @param {boolean} verbose + * @returns {{ skillerTypeA: ItemUnit[], skillerTypeB: ItemUnit[], skillerTypeC: ItemUnit[], resist: ItemUnit[], life: ItemUnit[], magicfind: ItemUnit[], damage: ItemUnit[], elemental: ItemUnit[], backup: ItemUnit[], keep: ItemUnit[], checkList: ItemUnit[] }}} + */ + sort: function (items = [], verbose = false) { + let charms = { + skillerTypeA: [], + skillerTypeB: [], + skillerTypeC: [], + resist: [], + life: [], + magicfind: [], + damage: [], + elemental: [], + backup: [], + keep: [], + checkList: [] + }; + + if (!items.length) { + verbose && console.log("No charms found"); + return charms; + } + + const addToCheckList = (item) => charms.checkList.indexOf(item) === -1 && charms.checkList.push(item); + const addToBackUp = (item) => charms.backup.indexOf(item) === -1 && charms.backup.push(item); + + const sortCharms = (arr = [], verbose = false, backUpCheck = true) => { + let invoquantity = NTIP.getInvoQuantity(arr[0]); + (invoquantity === undefined || invoquantity === -1) && (invoquantity = 2); + let charmType = CharmEquip.getCharmType(arr[0]); + verbose && console.log("Amount of " + charmType + " Charms: " + arr.length + " invoquantity: " + invoquantity); + arr.length > 1 && arr.sort((a, b) => NTIP.GetCharmTier(b) - NTIP.GetCharmTier(a)); + + if (arr.length > invoquantity) { + verbose && arr.forEach((el, index) => console.log(charmType + "[" + index + "] = " + NTIP.GetCharmTier(el))); + + for (let i = invoquantity; i < arr.length; i++) { + backUpCheck ? addToBackUp(arr[i]) : addToCheckList(arr[i]); + + arr.splice(i, 1); + i -= 1; + } + } + }; + + verbose && console.log("Amount of items: " + items.length); + items.length > 1 && items.sort((a, b) => NTIP.GetCharmTier(b) - NTIP.GetCharmTier(a)); + + const finalCharmInfo = Check.finalBuild().finalCharms; + const finalCharmKeys = Object.keys(finalCharmInfo); + + let found = false; + + while (items.length > 0) { + let gid = items[0].gid; + let item = items.shift(); + + if (!item.identified) { + let idTool = me.getIdTool(); + + if (idTool) { + item.isInStash && Town.openStash(); + Town.identifyItem(item, idTool); + + } else if (item.isInStash && (getUIFlag(sdk.uiflags.Stash) || Town.openStash())) { + Storage.Inventory.MoveTo(item); + Town.identify(); + } + + if (!Game.getItem(-1, -1, gid)) { + verbose && console.log("Sold charm during Town.identify()"); + items.shift(); + + continue; + } + } + + if (me.data.charmGids.includes(item.gid)) { + charms.keep.push(item); + + continue; + } + + let next = false; + + for (let i = 0; i < finalCharmKeys.length; i++) { + let cKey = finalCharmKeys[i]; + try { + if (!!me.data.charms[cKey] && me.data.charms[cKey].have.indexOf(item.gid) === -1 + && me.data.charms[cKey].have.length < me.data.charms[cKey].max) { + if (finalCharmInfo[cKey].stats(item)) { + console.debug(item.fname); + me.data.charmGids.push(item.gid); + me.data.charms[cKey].have.push(item.gid); + charms.keep.push(item); + found = true; + next = true; + + break; + } + } + } catch (e) { + console.error(e); + } + } + + if (next) { + continue; + } + + if (NTIP.GetCharmTier(item) <= 0) { + verbose && console.log("No tier. Adding to checkList: " + item.fname); + addToCheckList(item); + } else if (!NTIP.hasStats(item) && NTIP.GetCharmTier(item) > 0) { + verbose && console.log("Multiple Misc charm: " + item.fname); + charms.backup.push(item); + } else { + let charmType = CharmEquip.getCharmType(item); + switch (charmType) { + case "skillerTypeA": + case "skillerTypeB": + case "skillerTypeC": + case "resist": + case "life": + case "magicfind": + case "damage": + case "elemental": + charms[charmType].push(item); + verbose && console.log(charmType + ": " + item.fname); + + break; + default: + addToCheckList(item); + verbose && console.log("Failed all checks. Adding to checkList: " + item.fname); + + break; + } + } + } + + if (found) { + me.update(); + } + + if (!charms.skillerTypeA.length && !charms.skillerTypeB.length && !charms.skillerTypeC.length + && !charms.damage.length && !charms.resist.length && !charms.elemental.length && !charms.life.length && !charms.backup.length) { + verbose && console.log("No Charms"); + return charms; + } + + charms.skillerTypeA.length > 0 && sortCharms(charms.skillerTypeA, verbose); + charms.skillerTypeB.length > 0 && sortCharms(charms.skillerTypeB, verbose); + charms.skillerTypeC.length > 0 && sortCharms(charms.skillerTypeC, verbose); + charms.resist.length > 0 && sortCharms(charms.resist, verbose); + charms.life.length > 0 && sortCharms(charms.life, verbose); + charms.magicfind.length > 0 && sortCharms(charms.magicfind, verbose); + charms.damage.length > 0 && sortCharms(charms.damage, verbose); + charms.elemental.length > 0 && sortCharms(charms.elemental, verbose); + + // If stats are unspecifed, this will filter charms and keep highest based on invoquantity. If no invoquantity defined it will keep two of that type + charms.backup.length > 0 && sortCharms(charms.backup, verbose, false); + charms.keep = charms.keep.concat(charms.skillerTypeA, charms.skillerTypeB, charms.skillerTypeC, charms.resist, charms.life, charms.magicfind, charms.damage, charms.elemental, charms.backup); + verbose && charms.checkList.forEach((el, index) => console.log("checkList[" + index + "] = " + NTIP.GetCharmTier(el) + " " + el.fname)); + + return charms; + }, + + /** + * @param {ItemUnit} item + * @returns {boolean} + */ + check: function (item) { + if (!item || NTIP.GetCharmTier(item) <= 0 || !item.isCharm) return false; + // Annhilus, Hellfire Torch, Gheeds - Handled by a different function so return true to keep + if (item.isCharm && item.unique) return true; + // is one of our final charms + if (me.data.charmGids.includes(item.gid)) return true; + // is in our checkList + if (CharmEquip.keptGids.has(item.gid)) return true; + + let lowestCharm; + let items = me.getItemsEx() + .filter(charm => charm.classid === item.classid && charm.isInStorage && charm.magic && NTIP.GetCharmTier(charm) > 0); + if (!items.length) return true; + + let quantityCap = NTIP.getInvoQuantity(item); + let have = 0; + let charms = CharmEquip.sort(items); + let charmType = CharmEquip.getCharmType(item); + let cInfo, newList = []; + + switch (item.classid) { + case sdk.items.SmallCharm: + cInfo = CharData.charms.get("small").count(); + + if (cInfo.curr && (cInfo.curr / cInfo.max) * 100 >= 75) { + // chop off past our cap + newList = charms.keep + .sort((a, b) => NTIP.GetCharmTier(b) - NTIP.GetCharmTier(a)) + .slice(0, cInfo.max); + // check if it made the cut + if (!newList.find(i => i.gid === item.gid)) return false; + lowestCharm = newList.last(); + return !!(NTIP.GetCharmTier(item) >= NTIP.GetCharmTier(lowestCharm) || item.gid === lowestCharm.gid); + } + + break; + case sdk.items.LargeCharm: + cInfo = CharData.charms.get("large").count(); + + if (cInfo.curr && (cInfo.curr / cInfo.max) * 100 >= 75) { + // chop off past our cap + newList = charms.keep + .sort((a, b) => NTIP.GetCharmTier(b) - NTIP.GetCharmTier(a)) + .slice(0, cInfo.max); + // check if it made the cut + if (!newList.find(i => i.gid === item.gid)) return false; + lowestCharm = newList.last(); + return !!(NTIP.GetCharmTier(item) >= NTIP.GetCharmTier(lowestCharm) || item.gid === lowestCharm.gid); + } + + break; + case sdk.items.GrandCharm: + cInfo = CharData.charms.get("grand").count(); + + if (cInfo.curr && (cInfo.curr / cInfo.max) * 100 >= 50) { + // chop off past our cap + newList = charms.keep + .sort((a, b) => NTIP.GetCharmTier(b) - NTIP.GetCharmTier(a)) + .slice(0, cInfo.max); + // check if it made the cut + if (!newList.find(i => i.gid === item.gid)) return false; + lowestCharm = newList.last(); + return !!(NTIP.GetCharmTier(item) >= NTIP.GetCharmTier(lowestCharm) || item.gid === lowestCharm.gid); + } + + break; + } + + switch (charmType) { + case "skillerTypeA": + case "skillerTypeB": + case "skillerTypeC": + case "resist": + case "life": + case "magicfind": + case "damage": + case "elemental": + have = charms[charmType].length; + lowestCharm = charms[charmType].last(); + if ((charms[charmType].findIndex(c => c.gid === lowestCharm.gid) + 1) > quantityCap) return false; + + break; + default: + have = charms.backup.length; + lowestCharm = charms.backup.last(); + if ((charms.backup.findIndex(c => c.gid === lowestCharm.gid) + 1) > quantityCap) return false; + // console.debug("Lowest Charm index " + (charms.backup.findIndex(c => c.gid === lowestCharm.gid)) + " out of " + charms.backup.length); + + break; + } + + if (!lowestCharm) { + // console.debug("Didn't find any other charms of this type " + charmType); + return true; + } + + if (item.gid === lowestCharm.gid) { + // console.debug("Same charm"); + return true; + } + + let [tierParamItem, tierLowestItem] = [NTIP.GetCharmTier(item), NTIP.GetCharmTier(lowestCharm)]; + + if (tierParamItem === tierLowestItem) { + // console.debug("Same tier value"); + // super hacky - arbritrary comparsion of xpos if the tier value is the same + return (have < quantityCap) || (item.isInInventory && lowestCharm.isInInventory && item.x > lowestCharm.y) || (item.isInInventory && !lowestCharm.isInInventory); + } + + return (tierParamItem >= tierLowestItem); + }, + + run: function () { + // No charms in classic + if (me.classic) return; + + console.log("ÿc8Kolbot-SoloPlayÿc0: Entering charm auto equip"); + let tick = getTickCount(); + let charms = me.getItemsEx() + .filter(item => item.isInStorage && item.isCharm && item.magic); + // don't do anything if we don't have any charms + if ((!charms.length) + // don't do anything if we have the same charms as last time + || ((CharmEquip.keptGids.size && charms.every(c => CharmEquip.keptGids.has(c.gid))))) { + console.log("ÿc8Kolbot-SoloPlayÿc0: Exiting charm auto equip. Time elapsed: " + Tracker.formatTime(getTickCount() - tick)); + return; + } + CharmEquip.keptGids.clear(); + let totalKeep = [], totalSell = []; + let GCs = CharmEquip.grandCharm(charms); + let LCs = CharmEquip.largeCharm(charms); + let SCs = CharmEquip.smallCharm(charms); + let specialCharms = charms.filter((charm) => charm.unique); + let verbose = !!(Developer.debugging.smallCharm || Developer.debugging.largeCharm || Developer.debugging.grandCharm); + + if (verbose) { + console.log("Grand Charms Keep: " + GCs.keep.length + ", Sell: " + GCs.sell.length); + console.log("Large Charms Keep: " + LCs.keep.length + ", Sell: " + LCs.sell.length); + console.log("Small Charms Keep: " + SCs.keep.length + ", Sell: " + SCs.sell.length); + } + + totalKeep = totalKeep.concat(SCs.keep, LCs.keep, GCs.keep, specialCharms); + for (let i = 0; i < totalKeep.length; i++) { + if (!CharmEquip.check(totalKeep[i])) { + totalSell.push(totalKeep[i]); + totalKeep.splice(i, 1); + i--; + } + } + totalSell = totalSell + .concat(SCs.sell, LCs.sell, GCs.sell) + .filter((charm) => NTIP.CheckItem(charm, NTIP_CheckListNoTier) === Pickit.Result.UNWANTED); + totalKeep.length > 0 && console.log("ÿc8Kolbot-SoloPlayÿc0: Total Charms Kept: " + totalKeep.length); + + if (totalSell.length > 0) { + console.log("ÿc8Kolbot-SoloPlayÿc0: Total Charms Sell: " + totalSell.length); + + for (let i = 0; i < totalSell.length; i++) { + totalSell[i].isInStash && !getUIFlag(sdk.uiflags.Stash) && Town.openStash(); + if (totalSell[i].isInStash && (!totalSell[i].sellable || !Storage.Inventory.MoveTo(totalSell[i]))) { + totalSell[i].drop(); + totalSell.splice(i, 1); + i -= 1; + } + } + + Town.initNPC("Shop", "clearInventory"); + + if (getUIFlag(sdk.uiflags.Shop) || (Config.PacketShopping && getInteractedNPC() && getInteractedNPC().itemcount > 0)) { + for (let i = 0; i < totalSell.length; i++) { + console.log("ÿc8Kolbot-SoloPlayÿc0: Sell old charm " + totalSell[i].name); + verbose && Item.logger("Sold", totalSell[i]); + verbose && Item.logItem("CharmEquip Sold", totalSell[i]); + totalSell[i].sell(); + } + } + } + + if (totalKeep.length > 0) { + for (let i = 0; i < totalKeep.length; i++) { + CharmEquip.keptGids.add(totalKeep[i].gid); + if (totalKeep[i].isInStash && !Cubing.checkItem(totalKeep[i])) { + !getUIFlag(sdk.uiflags.Stash) && Town.openStash() && delay(300 + me.ping); + if (Storage.Inventory.CanFit(totalKeep[i]) && Storage.Inventory.MoveTo(totalKeep[i])) { + verbose && Item.logItem("CharmEquip Equipped", totalKeep[i]); + } + } + } + } + + me.cancelUIFlags(); + + console.log("ÿc8Kolbot-SoloPlayÿc0: Exiting charm auto equip. Time elapsed: " + Tracker.formatTime(getTickCount() - tick)); + }, + }; +})(); diff --git a/libs/SoloPlay/Functions/ClassAttackOverrides/AmazonAttacks.js b/libs/SoloPlay/Functions/ClassAttackOverrides/AmazonAttacks.js index 08ccce84..9da657e8 100644 --- a/libs/SoloPlay/Functions/ClassAttackOverrides/AmazonAttacks.js +++ b/libs/SoloPlay/Functions/ClassAttackOverrides/AmazonAttacks.js @@ -262,7 +262,7 @@ ClassAttack.doAttack = function (unit, preattack, once) { }; // @todo damage/effort comparison vs our normal skill - if (CharData.skillData.bowData.bowOnSwitch + if (CharData.skillData.bow.onSwitch && (index !== 1 || !unit.name.includes(getLocaleString(sdk.locale.text.Ghostly))) && (unit.distance >= 8 || (unit.isMoving && (unit.targetx !== me.x || unit.targety !== me.y))) && ([sdk.skills.Attack, sdk.skills.Jab].includes(skills.timed) || Skill.getManaCost(skills.timed) > me.mp)) { diff --git a/libs/SoloPlay/Functions/ClassAttackOverrides/BarbarianAttacks.js b/libs/SoloPlay/Functions/ClassAttackOverrides/BarbarianAttacks.js index 5fc2904f..e9613c47 100644 --- a/libs/SoloPlay/Functions/ClassAttackOverrides/BarbarianAttacks.js +++ b/libs/SoloPlay/Functions/ClassAttackOverrides/BarbarianAttacks.js @@ -13,7 +13,7 @@ includeIfNotIncluded("core/Attacks/Barbarian.js"); * - use leap attack with getIntoPosition, long distance or when targetting summoners * - use leap/leap attack with dodge, useful if we can't tele it provides a similar benefit */ -(function() { +(function () { ClassAttack.warCryTick = 0; const howlCheck = function () { @@ -419,51 +419,34 @@ includeIfNotIncluded("core/Attacks/Barbarian.js"); return true; }; - ClassAttack.grimWard = function (range = 10) { + /** + * @param {Monster} unit + * @param {number} [range] + * @returns {boolean} + */ + ClassAttack.grimWard = function (unit, range = 10) { if (!Skill.canUse(sdk.skills.GrimWard)) return false; - let corpseList = [], orgX = me.x, orgY = me.y; - - for (let i = 0; i < 3; i += 1) { - let corpse = Game.getMonster(); - - if (corpse) { - do { - if (corpse.dead && getDistance(corpse, orgX, orgY) <= range && this.checkCorpse(corpse)) { - corpseList.push(copyUnit(corpse)); - } - } while (corpse.getNext()); - } - - if (corpseList.length > 0) { - while (corpseList.length > 0) { - corpseList.sort(Sort.units); - corpse = corpseList.shift(); - - if (this.checkCorpse(corpse)) { - if (corpse.distance > 30 || Coords_1.isBlockedBetween(me, corpse)) { - Pather.moveToUnit(corpse); - } + if (!unit || !unit.dead) return false; - CorpseLoop: - for (let j = 0; j < 3; j += 1) { - Skill.cast(sdk.skills.GrimWard, sdk.skills.hand.Right, corpse); + let corpseList = getUnits(sdk.unittype.Monster) + .filter(mon => mon.dead && mon.distance < 30 && getDistance(mon, unit) <= range && this.checkCorpse(mon)) + .sort(((a, b) => getDistance(a, unit) - getDistance(b, unit))); - let tick = getTickCount(); + for (let corpse of corpseList) { + // corpseList uses copyUnit, so we need to get the actual corpse + let checkCorpse = Game.getMonster(corpse.classid, -1, corpse.gid); - while (getTickCount() - tick < 1000) { - if (corpse.getState(sdk.states.CorpseNoSelect)) { - - break CorpseLoop; - } - - delay(10); - } + if (checkCorpse && this.checkCorpse(checkCorpse)) { + for (let j = 0; j < 3; j += 1) { + if (Skill.cast(sdk.skills.GrimWard, sdk.skills.hand.Right, checkCorpse)) { + if (Misc.poll(() => checkCorpse.getState(sdk.states.CorpseNoSelect), 1000)) { + return true; } } } } } - return true; + return false; }; })(); diff --git a/libs/SoloPlay/Functions/ClassAttackOverrides/NecromancerAttacks.js b/libs/SoloPlay/Functions/ClassAttackOverrides/NecromancerAttacks.js index e1213c60..eeaa227f 100644 --- a/libs/SoloPlay/Functions/ClassAttackOverrides/NecromancerAttacks.js +++ b/libs/SoloPlay/Functions/ClassAttackOverrides/NecromancerAttacks.js @@ -7,114 +7,94 @@ includeIfNotIncluded("core/Attacks/Necromancer.js"); -ClassAttack.curseIndex = []; -// @todo refactor this -ClassAttack.curseIndex[sdk.skills.AmplifyDamage] = { - skillId: sdk.skills.AmplifyDamage, - state: sdk.states.AmplifyDamage, - priority: 2, - skipIf: () => false, - useIf: function (unit) { - return Skill.canUse(this.skillId) && !unit.getState(sdk.states.Decrepify) && !Attack.checkResist(unit, "magic") && !Attack.checkResist(unit, "physical"); +ClassAttack.curseIndex = (function () { + /** + * @constructor + * @param {number} skillId + * @param {number} state + * @param {number} priority + * @param {Function} useIf + */ + function Curse (skillId, state, priority, useIf) { + this.skillId = skillId; + this.state = state; + this.priority = priority; + this.useIf = useIf; } -}; - -ClassAttack.curseIndex[sdk.skills.DimVision] = { - skillId: sdk.skills.DimVision, - state: sdk.states.DimVision, - priority: 1, - skipIf: function (unit) { - return unit.isSpecial || [sdk.monsters.OblivionKnight1, sdk.monsters.OblivionKnight2, sdk.monsters.OblivionKnight3].includes(unit.classid); - }, - useIf: function (unit) { - return !this.skipIf(unit) && Skill.canUse(this.skillId) && unit.distance > 15; - } -}; - -ClassAttack.curseIndex[sdk.skills.Weaken] = { - skillId: sdk.skills.Weaken, - state: sdk.states.Weaken, - priority: 3, - skipIf: () => false, - useIf: function (unit) { - return Skill.canUse(this.skillId) && !unit.getState(sdk.states.Decrepify) && !unit.getState(sdk.states.AmplifyDamage); - } -}; - -ClassAttack.curseIndex[sdk.skills.IronMaiden] = { - skillId: sdk.skills.IronMaiden, - state: sdk.states.IronMaiden, - priority: 1, - skipIf: () => false, - useIf: function (unit) { - return Skill.canUse(this.skillId) && me.inArea(sdk.areas.DurielsLair) && me.normal && unit; - } -}; - -ClassAttack.curseIndex[sdk.skills.Terror] = { - skillId: sdk.skills.Terror, - state: sdk.states.Terror, - priority: 1, - skipIf: () => false, - useIf: function (unit) { - return unit.scareable && Skill.canUse(this.skillId) && me.getMobCount(6, Coords_1.Collision.BLOCK_MISSILE | Coords_1.BlockBits.BlockWall | Coords_1.BlockBits.Casting, 0, true) >= 3 - && Skill.getManaCost(sdk.skills.Terror) < me.mp && me.hpPercent < 75; - } -}; - -ClassAttack.curseIndex[sdk.skills.Confuse] = { - skillId: sdk.skills.Confuse, - state: sdk.states.Confuse, - priority: 2, - skipIf: () => false, - useIf: function (unit) { - return unit.scareable && unit.distance > 8 && Skill.canUse(this.skillId); - } -}; - -ClassAttack.curseIndex[sdk.skills.LifeTap] = { - skillId: sdk.skills.LifeTap, - state: sdk.states.LifeTap, - priority: 10, - skipIf: () => false, - useIf: function () { - // false for now, maybe based on health check on merc or would this be summonmancer viable? - return false; - } -}; - -ClassAttack.curseIndex[sdk.skills.Attract] = { - skillId: sdk.skills.Attract, - state: sdk.states.Attract, - priority: 1, - skipIf: () => false, - useIf: function (unit) { - return unit.scareable && me.inArea(sdk.areas.ThroneofDestruction) && unit.distance > 8 - && Skill.canUse(this.skillId); - } -}; -ClassAttack.curseIndex[sdk.skills.Decrepify] = { - skillId: sdk.skills.Decrepify, - state: sdk.states.Decrepify, - priority: 1, - skipIf: () => false, - useIf: function () { + Curse.prototype.canUse = function () { return Skill.canUse(this.skillId); - } -}; + }; -ClassAttack.curseIndex[sdk.skills.LowerResist] = { - skillId: sdk.skills.LowerResist, - state: sdk.states.LowerResist, - priority: 1, - skipIf: () => SetUp.currentBuild !== "Poison", - useIf: function (unit) { - return !this.skipIf() && Skill.canUse(this.skillId) && Attack.checkResist(unit, "poison"); - } -}; + let curseIndex = []; + curseIndex.push(new Curse(sdk.skills.AmplifyDamage, sdk.states.AmplifyDamage, 2, + /** @param {Monster} unit */ + function (unit) { + if (!unit || !Skill.canUse(this.skillId) || unit.getState(sdk.states.Decrepify)) return false; + return !Attack.checkResist(unit, "magic") && !Attack.checkResist(unit, "physical"); + } + )); + curseIndex.push(new Curse(sdk.skills.DimVision, sdk.states.DimVision, 1, + /** @param {Monster} unit */ + function (unit) { + if (!unit || !this.canUse() || unit.isSpecial) return false; + if ([sdk.monsters.OblivionKnight1, sdk.monsters.OblivionKnight2, sdk.monsters.OblivionKnight3].includes(unit.classid)) { + return false; + } + return unit.distance > 15; + } + )); + curseIndex.push(new Curse(sdk.skills.Weaken, sdk.states.Weaken, 3, + /** @param {Monster} unit */ + function (unit) { + return this.canUse() && !unit.getState(sdk.states.Decrepify) && !unit.getState(sdk.states.AmplifyDamage); + } + )); + curseIndex.push(new Curse(sdk.skills.IronMaiden, sdk.states.IronMaiden, 1, + function () { + return this.canUse() && me.inArea(sdk.areas.DurielsLair) && me.normal; + } + )); + curseIndex.push(new Curse(sdk.skills.Terror, sdk.states.Terror, 1, + /** @param {Monster} unit */ + function (unit) { + if (!this.canUse() || !unit || !unit.scareable) return false; + return me.getMobCount(6, Coords_1.Collision.BLOCK_MISSILE | Coords_1.BlockBits.BlockWall | Coords_1.BlockBits.Casting, 0, true) >= 3 + && Skill.getManaCost(sdk.skills.Terror) < me.mp && me.hpPercent < 75; + } + )); + curseIndex.push(new Curse(sdk.skills.Confuse, sdk.states.Confuse, 2, + /** @param {Monster} unit */ + function (unit) { + return this.canUse() && unit.scareable && unit.distance > 8; + } + )); + curseIndex.push(new Curse(sdk.skills.Attract, sdk.states.Attract, 1, + /** @param {Monster} unit */ + function (unit) { + return this.canUse() && me.inArea(sdk.areas.ThroneofDestruction) && unit.distance > 8 && unit.scareable; + } + )); + curseIndex.push(new Curse(sdk.skills.Decrepify, sdk.states.Decrepify, 1, + function () { + return this.canUse(); + } + )); + curseIndex.push(new Curse(sdk.skills.LowerResist, sdk.states.LowerResist, 1, + /** @param {Monster} unit */ + function (unit) { + if (SetUp.currentBuild !== "Poison") return false; + return this.canUse() && Attack.checkResist(unit, "poison"); + } + )); + + return curseIndex; +})(); -// todo - need to re-work this a bit so we don't focus too much on curses when we should be attacking +/** + * @param {Monster} unit + * @returns {boolean} + */ ClassAttack.smartCurse = function (unit) { if (unit === undefined || unit.dead || !unit.curseable) return false; @@ -229,7 +209,7 @@ ClassAttack.doAttack = function (unit, preattack, once) { return unit.dead ? Attack.Result.SUCCESS : Attack.Result.FAILED; }; - if (CharData.skillData.bowData.bowOnSwitch + if (CharData.skillData.bow.onSwitch && (index !== 1 || !unit.name.includes(getLocaleString(sdk.locale.text.Ghostly))) && ([-1, sdk.skills.Attack].includes(skills.timed) || Skill.getManaCost(skills.timed) > me.mp diff --git a/libs/SoloPlay/Functions/ClassAttackOverrides/PaladinAttacks.js b/libs/SoloPlay/Functions/ClassAttackOverrides/PaladinAttacks.js index 6a47f529..e4d2d634 100644 --- a/libs/SoloPlay/Functions/ClassAttackOverrides/PaladinAttacks.js +++ b/libs/SoloPlay/Functions/ClassAttackOverrides/PaladinAttacks.js @@ -150,7 +150,7 @@ ClassAttack.doAttack = function (unit = undefined, preattack = false, once = fal return unit.dead ? Attack.Result.SUCCESS : Attack.Result.FAILED; }; - if (CharData.skillData.bowData.bowOnSwitch + if (CharData.skillData.bow.onSwitch && (index !== 1 || !unit.name.includes(getLocaleString(sdk.locale.text.Ghostly))) && (unit.distance >= 12 || (unit.distance >= 8 && unit.isMoving && (unit.targetx !== me.x || unit.targety !== me.y))) && ([-1, sdk.skills.Attack].includes(attackSkill) || Skill.getManaCost(attackSkill) > me.mp)) { diff --git a/libs/SoloPlay/Functions/ClassAttackOverrides/SorceressAttacks.js b/libs/SoloPlay/Functions/ClassAttackOverrides/SorceressAttacks.js index c518d674..7e68424c 100644 --- a/libs/SoloPlay/Functions/ClassAttackOverrides/SorceressAttacks.js +++ b/libs/SoloPlay/Functions/ClassAttackOverrides/SorceressAttacks.js @@ -401,7 +401,7 @@ includeIfNotIncluded("core/Attacks/Sorceress.js"); return unit.dead ? Attack.Result.SUCCESS : Attack.Result.FAILED; }; - if (CharData.skillData.bowData.bowOnSwitch + if (CharData.skillData.bow.onSwitch && (index !== 1 || !unit.name.includes(getLocaleString(sdk.locale.text.Ghostly))) && ([-1, sdk.skills.Attack].includes(selectedSkill.skill) || selectedSkill.mana > me.mp diff --git a/libs/SoloPlay/Functions/CubingOverrides.js b/libs/SoloPlay/Functions/CubingOverrides.js index c6b41837..3e388052 100644 --- a/libs/SoloPlay/Functions/CubingOverrides.js +++ b/libs/SoloPlay/Functions/CubingOverrides.js @@ -776,13 +776,13 @@ Cubing.validItem = function (unit, recipe) { if (unit.magic && unit.ilvl >= recipe.Level) { if (ntipResult === Pickit.Result.UNWANTED) return true; // should allow for charms that aren't immeaditly wanted by equip and not nip wanted - if (unit.isCharm && !Item.autoEquipCharmCheck(unit) && ntipNoTierResult === Pickit.Result.UNWANTED) return true; + if (unit.isCharm && !CharmEquip.check(unit) && ntipNoTierResult === Pickit.Result.UNWANTED) return true; return true; } return false; } else if (recipe.Index === Recipe.Reroll.Charm) { - if (unit.isCharm && unit.magic && (ntipResult === Pickit.Result.UNWANTED || (!Item.autoEquipCharmCheck(unit) && ntipNoTierResult === Pickit.Result.UNWANTED))) { + if (unit.isCharm && unit.magic && (ntipResult === Pickit.Result.UNWANTED || (!CharmEquip.check(unit) && ntipNoTierResult === Pickit.Result.UNWANTED))) { switch (unit.itemType) { case sdk.items.type.SmallCharm: if (unit.ilvl >= recipe.Level[unit.code].ilvl) { diff --git a/libs/SoloPlay/Functions/DynamicTiers.js b/libs/SoloPlay/Functions/DynamicTiers.js index c1353922..072fc11e 100644 --- a/libs/SoloPlay/Functions/DynamicTiers.js +++ b/libs/SoloPlay/Functions/DynamicTiers.js @@ -91,11 +91,7 @@ mercRating += item.getStatEx(sdk.stats.MagicDamageReduction) * mercWeights.MR; // add integer magic damage resist mercRating += item.getStatEx(sdk.stats.MagicResist) * mercWeights.MR * 2; // add magic damage resist % - if (!myData) { - myData = CharData.getStats(); - } - - switch (myData.merc.classid) { + switch (me.data.merc.classid) { case sdk.mercs.Rogue: case sdk.mercs.IronWolf: mercRating += item.getStatEx(sdk.stats.MinDamage) * mercWeights.MINDMG; // add MIN damage @@ -344,7 +340,14 @@ const tierscore = function (item, tier, bodyloc) { const itembodyloc = Item.getBodyLoc(item); if (!itembodyloc.length) return -1; - bodyloc === undefined && (bodyloc = itembodyloc.last()); + bodyloc = bodyloc || itembodyloc.last(); + + if (item.isBaseType && !item.isRuneword && me.charlvl > 10) { + for (let runeword of Config.Runewords) { + let [sockets, baseCID] = [runeword[0].sockets, runeword[1]]; + if (item.classid === baseCID && item.sockets === sockets && !item.getItemsEx().length) return -1; + } + } const buildInfo = Check.currentBuild(); const canTele = Pather.canTeleport(); @@ -355,10 +358,11 @@ // get item cbf stat from olditem equipped on body location if (!canTele && item.getStatEx(sdk.stats.CannotbeFrozen)) { - // check if we have cbf but make sure its not from the item we are trying to un-equip - let haveCBF = (me.getStat(sdk.stats.CannotbeFrozen) > 0 && (eqItem && !eqItem.getStatEx(sdk.stats.CannotbeFrozen))); - // Cannot be frozen is very important for Melee chars - !haveCBF && (generalRating += buildInfo.caster ? tierWeights.generalWeights.CBF : tierWeights.generalWeights.CBF * 4); + // check if we have cbf but make sure its not from the item we are trying to un-equip + if (!me.getStat(sdk.stats.CannotbeFrozen)) { + // Cannot be frozen is very important for Melee chars + generalRating += buildInfo.caster ? tierWeights.generalWeights.CBF : tierWeights.generalWeights.CBF * 4; + } } // faster run/walk @@ -366,7 +370,8 @@ // belt slots if (item.itemType === sdk.items.type.Belt) { - const beltSize = ["lbl", "vbl"].includes(item.code) ? 2 : ["mbl", "tbl"].includes(item.code) ? 3 : 4; + const beltSizes = { lbl: 2, vbl: 2, mbl: 3, tbl: 3 }; + const beltSize = beltSizes[item.code] || 4; // if our current belt-size is better, don't down-grade even if the other stats on the new item are better, not worth the town visits generalRating += (Storage.BeltSize() > beltSize ? -50 : (beltSize * 4 * tierWeights.generalWeights.BELTSLOTS)); } @@ -389,10 +394,10 @@ // get new item stats let [newitemFR, newitemCR, newitemLR, newitemPR] = [ - Math.max(item.getStatEx(sdk.stats.FireResist), 0), - Math.max(item.getStatEx(sdk.stats.ColdResist), 0), - Math.max(item.getStatEx(sdk.stats.LightResist), 0), - Math.max(item.getStatEx(sdk.stats.PoisonResist), 0) + item.getStatEx(sdk.stats.FireResist), + item.getStatEx(sdk.stats.ColdResist), + item.getStatEx(sdk.stats.LightResist), + item.getStatEx(sdk.stats.PoisonResist) ]; // only enter next block if we have a new item with resists if (newitemFR || newitemCR || newitemLR || newitemPR) { @@ -468,7 +473,7 @@ if (!buildInfo.caster || Config.AttackSkill.includes(sdk.skills.Attack) || Config.LowManaSkill.includes(sdk.skills.Attack) - || ([sdk.items.type.Bow, sdk.items.type.AmazonBow, sdk.items.type.Crossbow].includes(item.itemType) && CharData.skillData.bowData.bowOnSwitch)) { + || ([sdk.items.type.Bow, sdk.items.type.AmazonBow, sdk.items.type.Crossbow].includes(item.itemType) && CharData.skillData.bow.onSwitch)) { let meleeRating = 0; // dirty fix maybe? @@ -616,13 +621,6 @@ tier += ctcScore(); tier += chargeditemscore(item, -1, buildInfo); - if (item.isBaseType && !item.isRuneword && me.charlvl > 10) { - for (let runeword of Config.Runewords) { - let [sockets, baseCID] = [runeword[0].sockets, runeword[1]]; - if (item.classid === baseCID && item.sockets === sockets && !item.getItemsEx().length) return -1; - } - } - if (tier > 1 && tier < NTIP.MAX_TIER && NTIP.CheckItem(item, NTIP.FinalGear.list) === Pickit.Result.WANTED) { // console.debug(item.prettyPrint + "~~~" + tier); tier += NTIP.MAX_TIER; @@ -656,7 +654,7 @@ * @param {ItemUnit} item */ const charmscore = function (item) { - if (myData.me.charmGids.includes(item.gid)) return 1000; + if (me.data.charmGids.includes(item.gid)) return 1000; // depending on invo space it might be worth it early on to keep 1 or 2 non-skiller grandcharms - @todo test that out if (!item.unique && item.classid === sdk.items.GrandCharm && !me.getSkillTabs().some(s => item.getStatEx(sdk.stats.AddSkillTab, s))) return -1; const buildInfo = Check.currentBuild(); diff --git a/libs/SoloPlay/Functions/Globals.js b/libs/SoloPlay/Functions/Globals.js index 8fe4687b..0eb0997b 100644 --- a/libs/SoloPlay/Functions/Globals.js +++ b/libs/SoloPlay/Functions/Globals.js @@ -32,25 +32,6 @@ const AreaData = require("../Modules/GameData/AreaData"); const MYCLASSNAME = sdk.player.class.nameOf(me.classid).toLowerCase(); includeIfNotIncluded("SoloPlay/BuildFiles/" + MYCLASSNAME + "/" + MYCLASSNAME + ".js"); -/** - * @global - * @type {charData} - * @todo redo how I handle this, maybe make a prop on "me" instead - */ -let myData = CharData.getStats(); - -Unit.prototype.__defineGetter__("mercid", function () { - return !!myData ? myData.merc.classid : me.getMerc().classid; -}); - -Unit.prototype.__defineGetter__("trueStr", function () { - return !!myData ? myData.me.strength : me.rawStrength; -}); - -Unit.prototype.__defineGetter__("trueDex", function () { - return !!myData ? myData.me.dexterity : me.rawDexterity; -}); - function myPrint (str = "", toConsole = false, color = 0) { console.log("ÿc8Kolbot-SoloPlayÿc0: " + str); me.overhead(str); @@ -62,17 +43,6 @@ function myPrint (str = "", toConsole = false, color = 0) { toConsole && D2Bot.printToConsole("Kolbot-SoloPlay :: " + str, color); } -function updateMyData () { - let obj = JSON.stringify(copyObj(myData)); - let myThread = getScript(true).name; - CharData.threads.forEach(function (script) { - let curr = getScript(script); - if (curr && myThread !== curr.name) { - curr.send("data--" + obj); - } - }); -} - // general settings const SetUp = { mercEnabled: true, @@ -98,12 +68,12 @@ const SetUp = { // try to see if we can correct the finalBuild for (let build of possibleBuilds) { - let match = myData.me.finalBuild.match(build, "gi"); + let match = me.data.finalBuild.match(build, "gi"); if (match) { console.log(match); - let old = myData.me.finalBuild; - myData.me.finalBuild = match[0].trim().capitalize(true); + let old = me.data.finalBuild; + me.data.finalBuild = match[0].trim().capitalize(true); errors.push( "~Info tag :: " + old + " was incorrect, I have attempted to remedy this." + " If it is still giving you an error please re-read the documentation. \n" @@ -130,52 +100,52 @@ const SetUp = { } } - if (!myData.initialized) { - myData.me.startTime = me.gamestarttime; - myData.me.level = me.charlvl; - myData.me.classid = me.classid; - myData.me.charName = me.name; - myData.me.strength = me.rawStrength; - myData.me.dexterity = me.rawDexterity; + if (!me.data.initialized) { + me.data.startTime = me.gamestarttime; + me.data.level = me.charlvl; + me.data.classid = me.classid; + me.data.charName = me.name; + me.data.strength = me.rawStrength; + me.data.dexterity = me.rawDexterity; if (me.expansion) { - myData.me.charms = Check.finalBuild().finalCharms; + me.data.charms = Check.finalBuild().finalCharms; } - myData.initialized = true; - CharData.updateData("me", myData); + me.data.initialized = true; + CharData.updateData("me", me.data); } - let temp = copyObj(myData); + let temp = copyObj(me.data); - if (myData.me.currentBuild !== CharInfo.getActiveBuild()) { - myData.me.currentBuild = CharInfo.getActiveBuild(); + if (me.data.currentBuild !== CharInfo.getActiveBuild()) { + me.data.currentBuild = CharInfo.getActiveBuild(); } let currDiffStr = sdk.difficulty.nameOf(me.diff).toLowerCase(); - if (sdk.difficulty.Difficulties.indexOf(myData.me.highestDifficulty) < sdk.difficulty.Difficulties.indexOf(sdk.difficulty.nameOf(me.diff))) { - myData.me.highestDifficulty = sdk.difficulty.nameOf(me.diff); + if (sdk.difficulty.Difficulties.indexOf(me.data.highestDifficulty) < sdk.difficulty.Difficulties.indexOf(sdk.difficulty.nameOf(me.diff))) { + me.data.highestDifficulty = sdk.difficulty.nameOf(me.diff); } - if (!!me.smith && myData[currDiffStr].imbueUsed === false) { - myData[currDiffStr].imbueUsed = true; + if (me.smith && me.data[currDiffStr].imbueUsed === false) { + me.data[currDiffStr].imbueUsed = true; } - if (!!me.respec && myData[currDiffStr].respecUsed === false) { - myData[currDiffStr].respecUsed = true; + if (me.respec && me.data[currDiffStr].respecUsed === false) { + me.data[currDiffStr].respecUsed = true; } - myData.me.level !== me.charlvl && (myData.me.level = me.charlvl); - myData.me.strength !== me.rawStrength && (myData.me.strength = me.rawStrength); - myData.me.dexterity !== me.rawDexterity && (myData.me.dexterity = me.rawDexterity); + me.data.level !== me.charlvl && (me.data.level = me.charlvl); + me.data.strength !== me.rawStrength && (me.data.strength = me.rawStrength); + me.data.dexterity !== me.rawDexterity && (me.data.dexterity = me.rawDexterity); // expansion check let [cUpdate, mUpdate] = [false, false]; if (me.expansion) { - if (!myData.merc.gear) { - myData.merc.gear = []; + if (!me.data.merc.gear) { + me.data.merc.gear = []; mUpdate = true; } @@ -185,61 +155,72 @@ const SetUp = { // can't do an aura check as merc auras are bugged, only useful info from getUnit is the classid let merc = me.getMercEx(); let mercItems = merc.getItemsEx(); - let preLength = myData.merc.gear.length; - let check = myData.merc.gear.filter(i => mercItems.some(item => item.prefixnum === i)); + let preLength = me.data.merc.gear.length; + let check = me.data.merc.gear.filter(i => mercItems.some(item => item.prefixnum === i)); if (check !== preLength) { mUpdate = true; - myData.merc.gear = check; + me.data.merc.gear = check; } let mercInfo = Mercenary.getMercInfo(merc); - mercInfo.classid !== myData.merc.classid && (myData.merc.classid = mercInfo.classid); - mercInfo.act !== myData.merc.act && (myData.merc.act = mercInfo.act); - mercInfo.difficulty !== myData.merc.difficulty && (myData.merc.difficulty = mercInfo.difficulty); - - if (merc.classid === sdk.mercs.Guard && !Mercenary.checkMercSkill(myData.merc.type)) { - // go back, need to make sure this works properly. - // only "go back" if we are past the difficulty we need to be in to hire merc. Ex. In hell but want holy freeze merc - // only if we have enough gold on hand to hire said merc - // return to our orignal difficulty afterwards + mercInfo.classid !== me.data.merc.classid && (me.data.merc.classid = mercInfo.classid); + mercInfo.act !== me.data.merc.act && (me.data.merc.act = mercInfo.act); + mercInfo.difficulty !== me.data.merc.difficulty && (me.data.merc.difficulty = mercInfo.difficulty); + + if (merc.classid !== sdk.mercs.Guard) { + try { + if (mercInfo.skillName !== me.data.merc.skillName) { + me.data.merc.skillName = mercInfo.skillName; + me.data.merc.skill = MercData.findByName(me.data.merc.skillName, me.data.merc.act).skill; + } + } catch (e) { + // + } } + + // if (merc.classid === sdk.mercs.Guard && !Mercenary.checkMercSkill(me.data.merc.type)) { + // // go back, need to make sure this works properly. + // // only "go back" if we are past the difficulty we need to be in to hire merc. Ex. In hell but want holy freeze merc + // // only if we have enough gold on hand to hire said merc + // // return to our orignal difficulty afterwards + // } } // charm check - if (!myData.me.charms || !Object.keys(myData.me.charms).length) { - myData.me.charms = Check.finalBuild().finalCharms; + if (!me.data.charms || !Object.keys(me.data.charms).length) { + me.data.charms = Check.finalBuild().finalCharms; cUpdate = true; } - if (!myData.me.charmGids || myData.me.charmGids.length > 0) { - myData.me.charmGids = []; + if (!me.data.charmGids || me.data.charmGids.length > 0) { + me.data.charmGids = []; cUpdate = true; } - const finalCharmKeys = Object.keys(myData.me.charms); + const finalCharmKeys = Object.keys(me.data.charms); // gids change from game to game so reset our list for (let i = 0; i < finalCharmKeys.length; i++) { let cKey = finalCharmKeys[i]; - if (myData.me.charms[cKey].have.length) { - myData.me.charms[cKey].have = []; + if (me.data.charms[cKey].have.length) { + me.data.charms[cKey].have = []; cUpdate = true; } } - if (!!me.shenk && myData[currDiffStr].socketUsed === false) { - myData[currDiffStr].socketUsed = true; + if (!!me.shenk && me.data[currDiffStr].socketUsed === false) { + me.data[currDiffStr].socketUsed = true; } if (mUpdate) { - CharData.updateData("merc", myData); + CharData.updateData("merc", me.data); } } - let changed = Misc.recursiveSearch(myData, temp); + let changed = Misc.recursiveSearch(me.data, temp); if (Object.keys(changed).length > 0 || cUpdate) { - CharData.updateData("me", myData); + CharData.updateData("me", me.data); } }, @@ -368,17 +349,17 @@ const SetUp = { const beltModifer = 4 - Storage.BeltSize(); const mpFactor = isCaster ? 80 : 50; Config.MPBuffer = Math.floor(mpFactor / Math.sqrt(me.mpmax)) + (beltModifer * 2); - !myData.merc.gear.includes(sdk.locale.items.Insight) && (Config.MPBuffer += 2); + !me.data.merc.gear.includes(sdk.locale.items.Insight) && (Config.MPBuffer += 2); const hpFactor = isCaster ? 65 : 80; Config.HPBuffer = Math.floor(hpFactor / Math.sqrt(me.hpmax)) + (beltModifer * 2); }, bowQuiver: function () { NTIP.resetRuntimeList(); - if (CharData.skillData.bowData.bowOnSwitch) { - if ([sdk.items.type.Bow, sdk.items.type.AmazonBow].includes(CharData.skillData.bowData.bowType)) { + if (CharData.skillData.bow.onSwitch) { + if ([sdk.items.type.Bow, sdk.items.type.AmazonBow].includes(CharData.skillData.bow.bowType)) { NTIP.addToRuntime("[type] == bowquiver # # [maxquantity] == 1"); - } else if (CharData.skillData.bowData.bowType === sdk.items.type.Crossbow) { + } else if (CharData.skillData.bow.bowType === sdk.items.type.Crossbow) { NTIP.addToRuntime("[type] == crossbowquiver # # [maxquantity] == 1"); } else if (me.charlvl < 10) { NTIP.addToRuntime("[type] == bowquiver # # [maxquantity] == 1"); @@ -436,7 +417,7 @@ const SetUp = { Storage.Init(); } // sometimes it seems hard to find skillers, if we have the room lets try to cube some - if (Storage.Stash.UsedSpacePercent() < 60 && Item.autoEquipGC().keep.length < CharData.charmData.grand.getCountInfo().max) { + if (Storage.Stash.UsedSpacePercent() < 60 && CharmEquip.grandCharm().keep.length < CharData.charms.get("grand").count().max) { Config.Recipes.push([Recipe.Reroll.Magic, "Grand Charm"]); } // switch bow - only for zon/sorc/pal/necro classes right now @@ -568,12 +549,12 @@ const SetUp = { Object.defineProperties(SetUp, { currentBuild: { get: function () { - return myData.me.currentBuild; + return me.data.currentBuild; }, }, finalBuild: { get: function () { - return myData.me.finalBuild; + return me.data.finalBuild; }, }, mercwatch: { @@ -612,7 +593,7 @@ const goToDifficulty = function (diff = undefined, reason = "") { CharData.updateData("me", "setDifficulty", diffString); myPrint("Going to " + diffString + " " + reason, true); delay(1000); - if (CharData.getStats().me.setDifficulty !== diffString) { + if (CharData.getStats().setDifficulty !== diffString) { throw new Error("Failed to set difficulty"); } scriptBroadcast("quit"); @@ -1038,7 +1019,7 @@ const Check = { goalReached = true; break; - case sdk.difficulty.Difficulties.indexOf(sdk.difficulty.nameOf(me.diff)) < sdk.difficulty.Difficulties.indexOf(myData.me.highestDifficulty): + case sdk.difficulty.Difficulties.indexOf(sdk.difficulty.nameOf(me.diff)) < sdk.difficulty.Difficulties.indexOf(me.data.highestDifficulty): // TODO: fill this out, if we go back to normal from hell I want to be able to do whatever it was imbue/socket/respec then return to our orignal difficulty // as it is right now if we go back it would take 2 games to get back to hell // but this needs a check to ensure that one of the above reasons are why we went back in case we had gone back because low gold in which case we need to stay in the game @@ -1070,8 +1051,8 @@ const Check = { if (!Check.resistance().Status) { if (me.weaponswitch === 0 && Item.getEquipped(sdk.body.LeftArm).fname.includes("Lidless Wall") && !Item.getEquipped(sdk.body.LeftArm).socketed) { if (!me.normal) { - if (!myData.normal.socketUsed) goToDifficulty(sdk.difficulty.Normal, " to use socket quest"); - if (me.hell && !myData.nightmare.socketUsed) goToDifficulty(sdk.difficulty.Nightmare, " to use socket quest"); + if (!me.data.normal.socketUsed) goToDifficulty(sdk.difficulty.Normal, " to use socket quest"); + if (me.hell && !me.data.nightmare.socketUsed) goToDifficulty(sdk.difficulty.Nightmare, " to use socket quest"); } } } diff --git a/libs/SoloPlay/Functions/ItemOverrides.js b/libs/SoloPlay/Functions/ItemOverrides.js index cb8989f3..3abcb028 100644 --- a/libs/SoloPlay/Functions/ItemOverrides.js +++ b/libs/SoloPlay/Functions/ItemOverrides.js @@ -507,8 +507,8 @@ Item.secondaryEquip = function (item, bodyLoc) { if (item.bodylocation === bodyLoc - 7) { equipped = true; - [sdk.items.Arrows, sdk.items.Bolts].includes(item.classid) && CharData.skillData.bowData.setArrowInfo(item); - [sdk.items.type.Bow, sdk.items.type.AmazonBow, sdk.items.type.Crossbow].includes(item.itemType) && CharData.skillData.bowData.setBowInfo(item); + [sdk.items.Arrows, sdk.items.Bolts].includes(item.classid) && CharData.skillData.bow.setArrowInfo(item); + [sdk.items.type.Bow, sdk.items.type.AmazonBow, sdk.items.type.Crossbow].includes(item.itemType) && CharData.skillData.bow.setBowInfo(item); if (getCursorType() === 3) { let cursorItem = Game.getCursorUnit(); @@ -664,8 +664,8 @@ Item.equipMerc = function (item, bodyLoc) { if (check && check.bodylocation === bodyLoc) { if (check.runeword) { // just track runewords for now - myData.merc.gear.push(check.prefixnum); - CharData.updateData("merc", myData); + me.data.merc.gear.push(check.prefixnum); + CharData.updateData("merc", me.data); } if (getCursorType() === 3) { @@ -862,713 +862,6 @@ Item.removeItemsMerc = function () { return !!mercenary.getItem(); }; -(function() { - // Charm Autoequip - TODO: clean this section up...sigh - // goals - /* - * need to be able to define what types of charms we want while leveling, and upgrade based on that - * need to be able to define what types of charms we want for final build, upgrade to that - * need to be able to handle different invoquantity values of final charms vs leveling charms - * need to be abel to handle final charms and leveling charms being the same type, in situation where we have enough of a final charm so compare it as a noraml leveling charm - * need to differentiate bewtween cubing charm or pickit wanted charm vs autoequip charm - * example: - * Imagine we are an auradin and we have 9 small charms in our inventory, Seven 5allres/20life and Two random life charms. Our build tells us we should keep 6 of the 5/20s - * so we should keep those. That leaves us with One 5/20 and Two random life charms, we should then compare the tier values and keep the highest of the two then sell or drop the third. - * As it is now, what happens is we don't compare the 7th 5/20 and we add that to the sell list while keeping the 2 lower charms. If we directly add it to the backup then the invoquantity - * gets read from the finalBuild file so instead of only keeping two it says we should keep 6. - */ - Item.hasCharmTier = (item) => me.expansion && Config.AutoEquip && NTIP.GetCharmTier(item) > 0; - Item.isFinalCharm = (item) => myData.me.charmGids.includes(item.gid); - - /** - * Iterate over charm checklist, pickit result 0 and 4 get sold - * Otherwise if its not in the stash already and not a final charm try and stash it. I don't remember why I checked if it wasn't a final charm - * @param {ItemUnit[]} checkList - * @param {boolean} verbose - */ - const spliceCharmCheckList = function (checkList = [], verbose = false) { - for (let i = 0; i < checkList.length; i++) { - const currCharm = checkList[i]; - if (!currCharm || [Pickit.Result.UNWANTED, Pickit.Result.TRASH].includes(Pickit.checkItem(currCharm).result)) continue; - if (!currCharm.isInStash && !myData.me.charmGids.includes(currCharm.gid)) { - if (!Storage.Stash.MoveTo(currCharm)) { - verbose && Item.logger("Dropped", currCharm); - currCharm.drop(); - } else { - if (verbose) { - Cubing.checkItem(currCharm) ? Item.logItem("Stashed Cubing Ingredient", currCharm) : Item.logItem("Stashed", currCharm); - } - } - } - - checkList.splice(i, 1); - i -= 1; - } - }; - - const spliceCharmKeepList = function (keep = [], sell = [], verbose = false) { - if (!keep.length) return; - const id = keep[0].classid; - const cInfo = (() => { - switch (id) { - case sdk.items.SmallCharm: - return CharData.charmData.small.getCountInfo(); - case sdk.items.LargeCharm: - return CharData.charmData.large.getCountInfo(); - case sdk.items.GrandCharm: - return CharData.charmData.grand.getCountInfo(); - default: - return { max: 0 }; - } - })(); - - // sort through kept charms - if (keep.length > cInfo.max) { - keep.sort((a, b) => NTIP.GetCharmTier(b) - NTIP.GetCharmTier(a)); - - // everything after the cap (need a better method for this in the instances where the max cap is less then leveling wanted cap) - for (let i = cInfo.max; i < keep.length; i++) { - if (!!keep[i].classid && !Item.autoEquipCharmCheck(keep[i])) { - sell.push(keep[i]); - verbose && console.log("ÿc8Kolbot-SoloPlayÿc0: CharmEquip Add " + keep[i].fname + " to checkList"); - keep.splice(i, 1); - i -= 1; - } - } - } - }; - - /** - * Handle small charm autoequip - * @param {ItemUnit[]} charmList - * @returns {{ keep: ItemUnit[], sell: ItemUnit[] }} - */ - Item.autoEquipSC = function (charmList = []) { - let verbose = Developer.debugging.smallCharm; - // build list of our charms - let items = (charmList.length ? charmList : me.getItemsEx()) - .filter((charm) => charm.isInStorage && charm.classid === sdk.items.SmallCharm && charm.magic); - - if (!items.length) { - verbose && console.debug("No charms found"); - return { keep: [], sell: [] }; - } - - let charms = Item.autoEquipCharmSort(items, verbose); - spliceCharmKeepList(charms.keep, charms.checkList, verbose); - - verbose && console.log("Small Charm checklist length: " + charms.checkList.length); - spliceCharmCheckList(charms.checkList, verbose); - - return { keep: charms.keep, sell: charms.checkList }; - }; - - /** - * Handle large charm autoequip - * @param {ItemUnit[]} charmList - * @returns {{ keep: ItemUnit[], sell: ItemUnit[] }} - */ - Item.autoEquipLC = function (charmList = []) { - let verbose = Developer.debugging.largeCharm; - let items = (charmList.length ? charmList : me.getItemsEx()) - .filter((charm) => charm.isInStorage && charm.classid === sdk.items.LargeCharm && charm.magic); - - if (!items.length) { - verbose && console.debug("No charms found"); - return { keep: [], sell: [] }; - } - - let charms = Item.autoEquipCharmSort(items, verbose); - spliceCharmKeepList(charms.keep, charms.checkList, verbose); - - verbose && console.log("Large charm checklist length: " + charms.checkList.length); - spliceCharmCheckList(charms.checkList, verbose); - - return { keep: charms.keep, sell: charms.checkList }; - }; - - /** - * Handle grand charm autoequip - * @param {ItemUnit[]} charmList - * @returns {{ keep: ItemUnit[], sell: ItemUnit[] }} - */ - Item.autoEquipGC = function (charmList = []) { - let verbose = Developer.debugging.largeCharm; - let items = (charmList.length ? charmList : me.getItemsEx()) - .filter((charm) => charm.isInStorage && charm.classid === sdk.items.GrandCharm && charm.magic); - - if (!items.length) { - verbose && console.debug("No charms found"); - return { keep: [], sell: [] }; - } - - let charms = Item.autoEquipCharmSort(items, verbose); - spliceCharmKeepList(charms.keep, verbose); - - verbose && console.log("Grand charm checklist length: " + charms.checkList.length); - spliceCharmKeepList(charms.keep, charms.checkList, verbose); - - return { keep: charms.keep, sell: charms.checkList }; - }; - - Item.autoEquipCharmSort = function (items = [], verbose = false) { - let charms = { - skillerTypeA: [], - skillerTypeB: [], - skillerTypeC: [], - resist: [], - life: [], - magicfind: [], - damage: [], - elemental: [], - backup: [], - keep: [], - checkList: [] - }; - - if (!items.length) { - verbose && console.log("No charms found"); - return charms; - } - - const addToCheckList = (item) => charms.checkList.indexOf(item) === -1 && charms.checkList.push(item); - const addToBackUp = (item) => charms.backup.indexOf(item) === -1 && charms.backup.push(item); - - const sortCharms = (arr = [], verbose = false, backUpCheck = true) => { - let invoquantity = NTIP.getInvoQuantity(arr[0]); - (invoquantity === undefined || invoquantity === -1) && (invoquantity = 2); - let charmType = Item.getCharmType(arr[0]); - verbose && console.log("Amount of " + charmType + " Charms: " + arr.length + " invoquantity: " + invoquantity); - arr.length > 1 && arr.sort((a, b) => NTIP.GetCharmTier(b) - NTIP.GetCharmTier(a)); - - if (arr.length > invoquantity) { - verbose && arr.forEach((el, index) => console.log(charmType + "[" + index + "] = " + NTIP.GetCharmTier(el))); - - for (let i = invoquantity; i < arr.length; i++) { - backUpCheck ? addToBackUp(arr[i]) : addToCheckList(arr[i]); - - arr.splice(i, 1); - i -= 1; - } - } - }; - - verbose && console.log("Amount of items: " + items.length); - items.length > 1 && items.sort((a, b) => NTIP.GetCharmTier(b) - NTIP.GetCharmTier(a)); - - const finalCharmInfo = Check.finalBuild().finalCharms; - const finalCharmKeys = Object.keys(finalCharmInfo); - - let found = false; - - while (items.length > 0) { - let gid = items[0].gid; - let item = items.shift(); - - if (!item.identified) { - let idTool = me.getIdTool(); - - if (idTool) { - item.isInStash && Town.openStash(); - Town.identifyItem(item, idTool); - - } else if (item.isInStash && (getUIFlag(sdk.uiflags.Stash) || Town.openStash())) { - Storage.Inventory.MoveTo(item); - Town.identify(); - } - - if (!Game.getItem(-1, -1, gid)) { - verbose && console.log("Sold charm during Town.identify()"); - items.shift(); - - continue; - } - } - - if (myData.me.charmGids.includes(item.gid)) { - charms.keep.push(item); - - continue; - } - - let next = false; - - for (let i = 0; i < finalCharmKeys.length; i++) { - let cKey = finalCharmKeys[i]; - try { - if (!!myData.me.charms[cKey] && myData.me.charms[cKey].have.indexOf(item.gid) === -1 - && myData.me.charms[cKey].have.length < myData.me.charms[cKey].max) { - if (finalCharmInfo[cKey].stats(item)) { - console.debug(item.fname); - myData.me.charmGids.push(item.gid); - myData.me.charms[cKey].have.push(item.gid); - charms.keep.push(item); - found = true; - next = true; - - break; - } - } - } catch (e) { - console.error(e); - } - } - - if (next) { - continue; - } - - if (NTIP.GetCharmTier(item) <= 0) { - verbose && console.log("No tier. Adding to checkList: " + item.fname); - addToCheckList(item); - } else if (!NTIP.hasStats(item) && NTIP.GetCharmTier(item) > 0) { - verbose && console.log("Multiple Misc charm: " + item.fname); - charms.backup.push(item); - } else { - let charmType = Item.getCharmType(item); - switch (charmType) { - case "skillerTypeA": - case "skillerTypeB": - case "skillerTypeC": - case "resist": - case "life": - case "magicfind": - case "damage": - case "elemental": - charms[charmType].push(item); - verbose && console.log(charmType + ": " + item.fname); - - break; - default: - addToCheckList(item); - verbose && console.log("Failed all checks. Adding to checkList: " + item.fname); - - break; - } - } - } - - if (found) { - updateMyData(); - } - - if (!charms.skillerTypeA.length && !charms.skillerTypeB.length && !charms.skillerTypeC.length - && !charms.damage.length && !charms.resist.length && !charms.elemental.length && !charms.life.length && !charms.backup.length) { - verbose && console.log("No Charms"); - return charms; - } - - charms.skillerTypeA.length > 0 && sortCharms(charms.skillerTypeA, verbose); - charms.skillerTypeB.length > 0 && sortCharms(charms.skillerTypeB, verbose); - charms.skillerTypeC.length > 0 && sortCharms(charms.skillerTypeC, verbose); - charms.resist.length > 0 && sortCharms(charms.resist, verbose); - charms.life.length > 0 && sortCharms(charms.life, verbose); - charms.magicfind.length > 0 && sortCharms(charms.magicfind, verbose); - charms.damage.length > 0 && sortCharms(charms.damage, verbose); - charms.elemental.length > 0 && sortCharms(charms.elemental, verbose); - - // If stats are unspecifed, this will filter charms and keep highest based on invoquantity. If no invoquantity defined it will keep two of that type - charms.backup.length > 0 && sortCharms(charms.backup, verbose, false); - charms.keep = charms.keep.concat(charms.skillerTypeA, charms.skillerTypeB, charms.skillerTypeC, charms.resist, charms.life, charms.magicfind, charms.damage, charms.elemental, charms.backup); - verbose && charms.checkList.forEach((el, index) => console.log("checkList[" + index + "] = " + NTIP.GetCharmTier(el) + " " + el.fname)); - - return charms; - }; - - /** - * @param {ItemUnit} item - */ - Item.autoEquipCharmCheck = function (item) { - if (!item || NTIP.GetCharmTier(item) <= 0 || !item.isCharm) return false; - // Annhilus, Hellfire Torch, Gheeds - Handled by a different function so return true to keep - if (item.isCharm && item.unique) return true; - // is one of our final charms - if (myData.me.charmGids.includes(item.gid)) return true; - // is in our checkList - if (Item.keptCharmGids.has(item.gid)) return true; - - let lowestCharm; - let items = me.getItemsEx() - .filter(charm => charm.classid === item.classid && charm.isInStorage && charm.magic && NTIP.GetCharmTier(charm) > 0); - if (!items.length) return true; - - let quantityCap = NTIP.getInvoQuantity(item); - let have = 0; - let charms = Item.autoEquipCharmSort(items); - let charmType = Item.getCharmType(item); - let cInfo, newList = []; - - switch (item.classid) { - case sdk.items.SmallCharm: - cInfo = CharData.charmData.small.getCountInfo(); - - if (cInfo.curr && (cInfo.curr / cInfo.max) * 100 >= 75) { - // chop off past our cap - newList = charms.keep - .sort((a, b) => NTIP.GetCharmTier(b) - NTIP.GetCharmTier(a)) - .slice(0, cInfo.max); - // check if it made the cut - if (!newList.find(i => i.gid === item.gid)) return false; - lowestCharm = newList.last(); - return !!(NTIP.GetCharmTier(item) >= NTIP.GetCharmTier(lowestCharm) || item.gid === lowestCharm.gid); - } - - break; - case sdk.items.LargeCharm: - cInfo = CharData.charmData.large.getCountInfo(); - - if (cInfo.curr && (cInfo.curr / cInfo.max) * 100 >= 75) { - // chop off past our cap - newList = charms.keep - .sort((a, b) => NTIP.GetCharmTier(b) - NTIP.GetCharmTier(a)) - .slice(0, cInfo.max); - // check if it made the cut - if (!newList.find(i => i.gid === item.gid)) return false; - lowestCharm = newList.last(); - return !!(NTIP.GetCharmTier(item) >= NTIP.GetCharmTier(lowestCharm) || item.gid === lowestCharm.gid); - } - - break; - case sdk.items.GrandCharm: - cInfo = CharData.charmData.grand.getCountInfo(); - - if (cInfo.curr && (cInfo.curr / cInfo.max) * 100 >= 50) { - // chop off past our cap - newList = charms.keep - .sort((a, b) => NTIP.GetCharmTier(b) - NTIP.GetCharmTier(a)) - .slice(0, cInfo.max); - // check if it made the cut - if (!newList.find(i => i.gid === item.gid)) return false; - lowestCharm = newList.last(); - return !!(NTIP.GetCharmTier(item) >= NTIP.GetCharmTier(lowestCharm) || item.gid === lowestCharm.gid); - } - - break; - } - - switch (charmType) { - case "skillerTypeA": - case "skillerTypeB": - case "skillerTypeC": - case "resist": - case "life": - case "magicfind": - case "damage": - case "elemental": - have = charms[charmType].length; - lowestCharm = charms[charmType].last(); - if ((charms[charmType].findIndex(c => c.gid === lowestCharm.gid) + 1) > quantityCap) return false; - - break; - default: - have = charms.backup.length; - lowestCharm = charms.backup.last(); - if ((charms.backup.findIndex(c => c.gid === lowestCharm.gid) + 1) > quantityCap) return false; - // console.debug("Lowest Charm index " + (charms.backup.findIndex(c => c.gid === lowestCharm.gid)) + " out of " + charms.backup.length); - - break; - } - - if (!lowestCharm) { - // console.debug("Didn't find any other charms of this type " + charmType); - return true; - } - - if (item.gid === lowestCharm.gid) { - // console.debug("Same charm"); - return true; - } - - let [tierParamItem, tierLowestItem] = [NTIP.GetCharmTier(item), NTIP.GetCharmTier(lowestCharm)]; - - if (tierParamItem === tierLowestItem) { - // console.debug("Same tier value"); - // super hacky - arbritrary comparsion of xpos if the tier value is the same - return (have < quantityCap) || (item.isInInventory && lowestCharm.isInInventory && item.x > lowestCharm.y) || (item.isInInventory && !lowestCharm.isInInventory); - } - - return (tierParamItem >= tierLowestItem); - }; - - Item.initCharms = function () { - // No charms in classic - if (me.classic) return; - let myCharms = me.getItemsEx().filter(item => item.isInStorage && item.isCharm && item.magic); - let changed = false; - - const finalCharmKeys = Object.keys(myData.me.charms); - const check = function (list = [], charms = []) { - for (let i = 0; i < list.length; i++) { - if (!charms.some(c => c.gid === list[i])) { - console.log("A charm was removed from our final list - updated it"); - myData.me.charmGids.remove(list[i]); - list.splice(i, 1); - i--; - changed = true; - } - } - }; - - for (let i = 0; i < finalCharmKeys.length; i++) { - let cKey = finalCharmKeys[i]; - switch (myData.me.charms[cKey].classid) { - case sdk.items.SmallCharm: - check(myData.me.charms[cKey].have, myCharms); - - break; - case sdk.items.LargeCharm: - check(myData.me.charms[cKey].have, myCharms); - - break; - case sdk.items.GrandCharm: - check(myData.me.charms[cKey].have, myCharms); - - break; - } - } - - changed && updateMyData(); - }; - - Item.keptCharmGids = new Set(); - Item.autoEquipCharms = function () { - // No charms in classic - if (me.classic) return; - - console.log("ÿc8Kolbot-SoloPlayÿc0: Entering charm auto equip"); - let tick = getTickCount(); - let charms = me.getItemsEx() - .filter(item => item.isInStorage && item.isCharm && item.magic); - // don't do anything if we don't have any charms - if ((!charms.length) - // don't do anything if we have the same charms as last time - || ((Item.keptCharmGids.size && charms.every(c => Item.keptCharmGids.has(c.gid))))) { - console.log("ÿc8Kolbot-SoloPlayÿc0: Exiting charm auto equip. Time elapsed: " + Tracker.formatTime(getTickCount() - tick)); - return; - } - if (Item.keptCharmGids.size && charms.every(c => Item.keptCharmGids.has(c.gid))) return; - Item.keptCharmGids.clear(); - let totalKeep = [], totalSell = []; - let GCs = Item.autoEquipGC(charms); - let LCs = Item.autoEquipLC(charms); - let SCs = Item.autoEquipSC(charms); - let specialCharms = charms.filter((charm) => charm.unique); - let verbose = !!(Developer.debugging.smallCharm || Developer.debugging.largeCharm || Developer.debugging.grandCharm); - - if (verbose) { - console.log("Grand Charms Keep: " + GCs.keep.length + ", Sell: " + GCs.sell.length); - console.log("Large Charms Keep: " + LCs.keep.length + ", Sell: " + LCs.sell.length); - console.log("Small Charms Keep: " + SCs.keep.length + ", Sell: " + SCs.sell.length); - } - - totalKeep = totalKeep.concat(SCs.keep, LCs.keep, GCs.keep, specialCharms); - for (let i = 0; i < totalKeep.length; i++) { - if (!Item.autoEquipCharmCheck(totalKeep[i])) { - totalSell.push(totalKeep[i]); - totalKeep.splice(i, 1); - i--; - } - } - totalSell = totalSell - .concat(SCs.sell, LCs.sell, GCs.sell) - .filter((charm) => NTIP.CheckItem(charm, NTIP_CheckListNoTier) === Pickit.Result.UNWANTED); - totalKeep.length > 0 && console.log("ÿc8Kolbot-SoloPlayÿc0: Total Charms Kept: " + totalKeep.length); - - if (totalSell.length > 0) { - console.log("ÿc8Kolbot-SoloPlayÿc0: Total Charms Sell: " + totalSell.length); - - for (let i = 0; i < totalSell.length; i++) { - totalSell[i].isInStash && !getUIFlag(sdk.uiflags.Stash) && Town.openStash(); - if (totalSell[i].isInStash && (!totalSell[i].sellable || !Storage.Inventory.MoveTo(totalSell[i]))) { - totalSell[i].drop(); - totalSell.splice(i, 1); - i -= 1; - } - } - - Town.initNPC("Shop", "clearInventory"); - - if (getUIFlag(sdk.uiflags.Shop) || (Config.PacketShopping && getInteractedNPC() && getInteractedNPC().itemcount > 0)) { - for (let i = 0; i < totalSell.length; i++) { - console.log("ÿc8Kolbot-SoloPlayÿc0: Sell old charm " + totalSell[i].name); - verbose && Item.logger("Sold", totalSell[i]); - verbose && Item.logItem("CharmEquip Sold", totalSell[i]); - totalSell[i].sell(); - } - } - } - - if (totalKeep.length > 0) { - for (let i = 0; i < totalKeep.length; i++) { - Item.keptCharmGids.add(totalKeep[i].gid); - if (totalKeep[i].isInStash && !Cubing.checkItem(totalKeep[i])) { - !getUIFlag(sdk.uiflags.Stash) && Town.openStash() && delay(300 + me.ping); - if (Storage.Inventory.CanFit(totalKeep[i]) && Storage.Inventory.MoveTo(totalKeep[i])) { - verbose && Item.logItem("CharmEquip Equipped", totalKeep[i]); - } - } - } - } - - me.cancelUIFlags(); - - console.log("ÿc8Kolbot-SoloPlayÿc0: Exiting charm auto equip. Time elapsed: " + Tracker.formatTime(getTickCount() - tick)); - }; - - // Write charm equip version that checks by item prefix/suffix using a switch case with the various prefixes and suffixes to sort them - // improve this, not sure how but still needs work - // maybe do this by prefix/suffix number instead? - Item.getCharmType = function (charm = undefined) { - if (!charm || !charm.isCharm) return false; - if (charm.unique) return "unique"; - if (!NTIP.hasStats(charm) && NTIP.GetCharmTier(charm) > 0) return "misc"; - - let charmType = ""; - const skillerStats = me.getSkillTabs(); - - if (charm.getStat(sdk.stats.AddSkillTab, skillerStats[0])) { - charmType = "skillerTypeA"; - } else if (charm.getStat(sdk.stats.AddSkillTab, skillerStats[1])) { - charmType = "skillerTypeB"; - } else if (charm.getStat(sdk.stats.AddSkillTab, skillerStats[2])) { - charmType = "skillerTypeC"; - } - - switch (charm.prefix) { - case "Shimmering": - case "Azure": - case "Lapis": - case "Cobalt": - case "Sapphire": - case "Crimson": - case "Russet": - case "Garnet": - case "Ruby": - case "Tangerine": - case "Ocher": - case "Coral": - case "Amber": - case "Beryl": - case "Viridian": - case "Jade": - case "Emerald": - charmType = "resist"; - break; - } - - if (!charmType || charmType === "") { - switch (charm.suffix) { - case "of Fortune": - case "of Good Luck": - charmType = "magicfind"; - break; - case "of Life": - case "of Substinence": // Odd issue, seems to be misspelled wherever item.suffix pulls info from - case "of Vita": - charmType = "life"; - break; - } - } - - if (!charmType || charmType === "") { - switch (charm.prefix) { - case "Red": - case "Sanguinary": - case "Bloody": - case "Jagged": - case "Forked": - case "Serrated": - case "Bronze": - case "Iron": - case "Steel": - case "Fine": - case "Sharp": - charmType = "damage"; - break; - case "Snowy": - case "Shivering": - case "Boreal": - case "Hibernal": - case "Ember": - case "Smoldering": - case "Smoking": - case "Flaming": - case "Static": - case "Glowing": - case "Arcing": - case "Shocking": - case "Septic": - case "Foul": - case "Toxic": - case "Pestilant": - charmType = "elemental"; - break; - } - } - - if (!charmType || charmType === "") { - switch (charm.suffix) { - case "of Craftmanship": - case "of Quality": - case "of Maiming": - charmType = "damage"; - break; - case "of Strength": - case "of Dexterity": - charmType = "stats"; - break; - case "of Blight": - case "of Venom": - case "of Pestilence": - case "of Anthrax": - case "of Frost": - case "of Icicle": - case "of Glacier": - case "of Winter": - case "of Flame": - case "of Burning": - case "of Incineration": - case "of Shock": - case "of Lightning": - case "of Thunder": - case "of Storms": - charmType = "elemental"; - break; - } - } - - if (!charmType || charmType === "") { - switch (charm.prefix) { - case "Stout": - case "Burly": - case "Stalwart": - charmType = "misc"; - break; - case "Rugged": - charmType = "misc"; - break; - case "Lizard's": - case "Snake's": - case "Serpent's": - charmType = "mana"; - break; - } - } - - if (!charmType || charmType === "") { - switch (charm.suffix) { - case "of Balance": - case "of Greed": - case "of Inertia": - charmType = "misc"; - break; - } - } - - return charmType; - }; -})(); - // Log kept item stats in the manager. Item.logItem = function (action, unit, keptLine, force) { if (!this.useItemLog || unit === undefined || !unit || !unit.fname) return false; @@ -1596,7 +889,7 @@ Item.logItem = function (action, unit, keptLine, force) { const mercCheck = action.match("Merc"); const hasTier = AutoEquip.hasTier(unit); - const charmCheck = (unit.isCharm && Item.autoEquipCharmCheck(unit)); + const charmCheck = (unit.isCharm && CharmEquip.check(unit)); const nTResult = NTIP.CheckItem(unit, NTIP_CheckListNoTier) === 1; if (!action.match("kept", "i") && !action.match("Shopped") && hasTier) { @@ -1659,7 +952,7 @@ const AutoEquip = { hasTier: function (item) { if (me.classic) return Item.hasTier(item); if (item.isCharm) { - return Item.hasCharmTier(item); + return CharmEquip.hasCharmTier(item); } return Item.hasMercTier(item) || Item.hasTier(item) || Item.hasSecondaryTier(item); }, @@ -1670,14 +963,16 @@ const AutoEquip = { wanted: function (item) { if (me.classic) return Item.autoEquipCheck(item, true); if (item.isCharm) { - return Item.autoEquipCharmCheck(item); + return CharmEquip.check(item); } return Item.autoEquipCheckMerc(item, true) || Item.autoEquipCheck(item, true) || Item.autoEquipCheckSecondary(item); }, run: function () { + console.time("AutoEquip"); Item.autoEquip(); Item.autoEquipSecondary(); - Item.autoEquipCharms(); + CharmEquip.run(); + console.timeEnd("AutoEquip"); } }; diff --git a/libs/SoloPlay/Functions/Me.js b/libs/SoloPlay/Functions/Me.js index f7f91645..a7d2295e 100644 --- a/libs/SoloPlay/Functions/Me.js +++ b/libs/SoloPlay/Functions/Me.js @@ -107,8 +107,44 @@ if (!me.hasOwnProperty("className")) { if (!me.hasOwnProperty("onFinalBuild")) { Object.defineProperty(me, "onFinalBuild", { get: function () { - myData === undefined && (myData = CharData.getStats()); - return myData.me.currentBuild === myData.me.finalBuild; + return me.data.currentBuild === me.data.finalBuild; + } + }); +} + +if (!me.hasOwnProperty("mercid")) { + Object.defineProperty(me, "mercid", { + get: function () { + return me.data.merc.classid || (() => { + let merc = me.getMercEx(); + if (!merc) return 0; + me.data.merc.classid = merc.classid; + return merc.classid; + })(); + } + }); +} + +if (!me.hasOwnProperty("trueStr")) { + Object.defineProperty(me, "trueStr", { + get: function () { + return me.data.strength || (() => { + let str = me.rawStrength; + me.data.strength = str; + return str; + })(); + } + }); +} + +if (!me.hasOwnProperty("trueDex")) { + Object.defineProperty(me, "trueDex", { + get: function () { + return me.data.dexterity || (() => { + let dex = me.rawDexterity; + me.data.dexterity = dex; + return dex; + })(); } }); } @@ -155,6 +191,47 @@ if (!me.hasOwnProperty("currentBuild")) { }); } +if (!me.hasOwnProperty("data")) { + let _data = null; + + Object.defineProperty(me, "data", { + get: function () { + if (_data) return _data; + _data = CharData.getStats(); + // handle if it was an old data file + if (!_data.hasOwnProperty("startTime")) { + let oldData = copyObj(_data); + _data = CharData.create(); + Object.assign(_data, oldData.me); + Object.assign(_data.merc, oldData.merc); + if (oldData.merc.hasOwnProperty("type")) { + _data.merc.skillName = oldData.merc.type; + _data.merc.skill = MercData.findByName(_data.merc.skillName, _data.merc.act).skill; + } + } + return _data; + }, + set: function (v) { + if (v.hasOwnProperty("startTime")) { + _data = v; + } + }, + }); + + Object.defineProperty(me, "update", { + value: function () { + let obj = JSON.stringify(copyObj(me.data)); + let myThread = getScript(true).name; + CharData.threads.forEach(function (script) { + let curr = getScript(script); + if (curr && myThread !== curr.name) { + curr.send("data--" + obj); + } + }); + }, + }); +} + /** @returns {boolean} */ me.canTpToTown = function () { // can't tp if dead - or not currently enabled to @@ -172,7 +249,7 @@ me.canTpToTown = function () { me.getMercEx = function () { if (!Config.UseMerc || me.classic || me.mercrevivecost) return null; - if (!myData.merc.type) return null; + if (!me.data.merc.type) return null; let merc = Misc.poll(() => me.getMerc(), 250, 50); return !!merc && !merc.dead ? merc : null; @@ -551,10 +628,10 @@ me.getItemsForRepair = function (repairPercent, chargedItems) { me.needRepair = function () { let repairAction = []; let bowCheck = Attack.usingBow(); - let switchBowCheck = CharData.skillData.bowData.bowOnSwitch; + let switchBowCheck = CharData.skillData.bow.onSwitch; let canAfford = me.gold >= me.getRepairCost(); !bowCheck && switchBowCheck && (bowCheck = (() => { - switch (CharData.skillData.bowData.bowType) { + switch (CharData.skillData.bow.bowType) { case sdk.items.type.Bow: case sdk.items.type.AmazonBow: return "bow"; diff --git a/libs/SoloPlay/Functions/Mercenary.js b/libs/SoloPlay/Functions/Mercenary.js index cb5a6a04..985f1c13 100644 --- a/libs/SoloPlay/Functions/Mercenary.js +++ b/libs/SoloPlay/Functions/Mercenary.js @@ -22,9 +22,11 @@ const MercData = new function MercData () { this.difficulty = difficulty || sdk.difficulty.Normal; } + // Act 1 this[sdk.skills.FireArrow] = new Merc(sdk.mercs.Rogue, sdk.skills.FireArrow, 1); this[sdk.skills.ColdArrow] = new Merc(sdk.mercs.Rogue, sdk.skills.ColdArrow, 1); + // Act 2 this[sdk.skills.Prayer] = new Merc(sdk.mercs.Guard, sdk.skills.Prayer, 2, sdk.difficulty.Normal); this[sdk.skills.BlessedAim] = new Merc(sdk.mercs.Guard, sdk.skills.BlessedAim, 2, sdk.difficulty.Normal); this[sdk.skills.Defiance] = new Merc(sdk.mercs.Guard, sdk.skills.Defiance, 2, sdk.difficulty.Normal); @@ -33,12 +35,15 @@ const MercData = new function MercData () { this[sdk.skills.Might] = new Merc(sdk.mercs.Guard, sdk.skills.Might, 2, sdk.difficulty.Nightmare); this[sdk.skills.Thorns] = new Merc(sdk.mercs.Guard, sdk.skills.Thorns, 2, sdk.difficulty.Nightmare); + // Act 3 this[sdk.skills.IceBlast] = new Merc(sdk.mercs.IronWolf, sdk.skills.IceBlast, 3, sdk.difficulty.Normal); this[sdk.skills.FireBall] = new Merc(sdk.mercs.IronWolf, sdk.skills.FireBall, 3, sdk.difficulty.Normal); this[sdk.skills.Lightning] = new Merc(sdk.mercs.IronWolf, sdk.skills.Lightning, 3, sdk.difficulty.Normal); + // Act 5 this[sdk.skills.Bash] = new Merc(sdk.mercs.A5Barb, sdk.skills.Bash, 5, sdk.difficulty.Normal); + /** @type {Map} */ this.actMap = new Map(); this.actMap.set(sdk.mercs.Rogue, 1); this.actMap.set(1, [this[sdk.skills.FireArrow], this[sdk.skills.ColdArrow]]); @@ -55,6 +60,12 @@ const MercData = new function MercData () { this.actMap.set(sdk.mercs.A5Barb, 5); this.actMap.set(5, [this[sdk.skills.Bash]]); + + this.findByName = function (name, act) { + let merc = this.actMap.get(act) + .find(m => m.skillName === name); + return merc; + }; }; const Mercenary = { @@ -152,7 +163,7 @@ const Mercenary = { classid: merc.classid, act: this.getMercAct(merc), difficulty: this.getMercDifficulty(merc), - type: this.getMercSkill(merc) + skillName: this.getMercSkill(merc) }; }, @@ -179,8 +190,9 @@ const Mercenary = { if (me.classic) return true; let _a; let { wantedMerc } = Check.finalBuild(); - let typeOfMerc = (!Pather.accessToAct(2) && me.normal ? 1 : wantedMerc.act); + let mercAct = (!Pather.accessToAct(2) && me.normal ? 1 : wantedMerc.act); let tmpAuraName = "Defiance"; + let currMerc = me.data.merc; // don't hire if using correct a1 merc, or passed merc hire difficulty // we've already gotten the correct a1 merc or haven't yet completed Bloodraven @@ -192,12 +204,12 @@ const Mercenary = { // we don't have enough spare gold to buy a1 merc // we don't have enough gold to hire our wanted merc switch (true) { - case typeOfMerc === 1 && (myData.merc.type === "Cold Arrow" || !Misc.checkQuest(sdk.quest.id.SistersBurialGrounds, sdk.quest.states.Completed)): - case myData.merc.type === wantedMerc.skillName: + case mercAct === 1 && (currMerc.skillName === "Cold Arrow" || !Misc.checkQuest(sdk.quest.id.SistersBurialGrounds, sdk.quest.states.Completed)): + case currMerc.skillName === wantedMerc.skillName: case me.diff > wantedMerc.difficulty: case me.diff === wantedMerc.difficulty && !Pather.accessToAct(wantedMerc.act): - case me.diff !== wantedMerc.difficulty && myData.merc.type === "Defiance": - case (me.charlvl > CharInfo.levelCap + 10 && Mercenary.checkMercSkill(MercData[getSkillByName(myData.merc.type)])): + case me.diff !== wantedMerc.difficulty && currMerc.skillName === "Defiance": + case (me.charlvl > CharInfo.levelCap + 10 && Mercenary.checkMercSkill(wantedMerc)): case me.gold < Math.round((((me.charlvl - 1) * (me.charlvl - 1)) / 2) * 7.5): case this.minCost > 0 && me.gold < this.minCost: return true; @@ -207,7 +219,7 @@ const Mercenary = { /** @type {MercUnit} */ let checkMyMerc = Misc.poll(() => me.getMerc(), 50, 500); - const wantedSkill = (typeOfMerc === 1 + const wantedSkill = (mercAct === 1 ? "Fire Arrow" === wantedMerc.skillName ? wantedMerc.skillName : "Cold Arrow" @@ -218,11 +230,13 @@ const Mercenary = { if (checkMyMerc && Mercenary.checkMercSkill(wantedMerc, checkMyMerc)) { // we have our wanted merc, data file was probably erased so lets re-update it - myData.merc.act = Mercenary.getMercAct(checkMyMerc); - myData.merc.classid = checkMyMerc.classid; - myData.merc.difficulty = Mercenary.getMercDifficulty(checkMyMerc); - myData.merc.type = wantedMerc.skillName; - CharData.updateData("merc", myData) && updateMyData(); + me.data.merc.act = Mercenary.getMercAct(checkMyMerc); + me.data.merc.classid = checkMyMerc.classid; + me.data.merc.difficulty = Mercenary.getMercDifficulty(checkMyMerc); + me.data.merc.skillName = wantedMerc.skillName; + me.data.merc.skill = MercData.findByName(me.data.merc.skillName, me.act).skill; + CharData.updateData("merc", me.data) && me.update(); + return true; } else if (!!checkMyMerc && checkMyMerc.classid === sdk.mercs.Guard) { let checkSkill = checkMyMerc.getStat(sdk.stats.ModifierListSkill); @@ -230,12 +244,12 @@ const Mercenary = { if (!checkSkill) return true; // or we might have multiple aura's going if ([sdk.skills.Meditation, sdk.skills.Conviction, sdk.skills.Concentration].includes(checkSkill)) return true; - if (checkSkill > 123) return true; + if (checkSkill > sdk.skills.Conviction) return true; } let MercLib_1 = require("../Modules/MercLib"); try { - Town.goToTown(typeOfMerc); + Town.goToTown(mercAct); myPrint("ÿc9Mercenaryÿc0 :: getting merc"); Town.move(Town.tasks[me.act - 1].Merc); me.sortInventory(); @@ -245,6 +259,10 @@ const Mercenary = { addEventListener("gamepacket", MercLib_1.mercPacket); Town.initNPC("Merc", "getMerc"); + delay(500); + + if (!MercLib_1.default.length) throw new Error("No mercs found"); + let wantedMerc = MercLib_1.default .filter((merc) => merc.skills.some((skill) => (skill === null || skill === void 0 ? void 0 : skill.name) === wantedSkill)) .sort((a, b) => b.level - a.level) @@ -258,7 +276,9 @@ const Mercenary = { let oldGid_1 = (_a = me.getMercEx()) === null || _a === void 0 ? void 0 : _a.gid; console.log("ÿc9Mercenaryÿc0 :: Found a merc to hire " + JSON.stringify(wantedMerc)); - wantedMerc === null || wantedMerc === void 0 ? void 0 : wantedMerc.hire(); + (wantedMerc === null || wantedMerc === void 0) + ? void 0 + : wantedMerc.hire(); let newMerc = Misc.poll(function () { let merc = me.getMerc(); if (!merc) return false; @@ -269,13 +289,13 @@ const Mercenary = { console.log("Hired a merc?"); if (newMerc) { console.log("Yep"); - myData.merc.act = me.act; - myData.merc.classid = newMerc.classid; - myData.merc.difficulty = me.diff; - myData.merc.type = wantedMerc.skills.find(sk => sk.name === wantedSkill).name; - // myData.merc.skills = getSkillByName(myData.merc.type); - CharData.updateData("merc", myData) && updateMyData(); - console.log("ÿc9Mercenaryÿc0 :: " + myData.merc.type + " merc hired."); + me.data.merc.act = me.act; + me.data.merc.classid = newMerc.classid; + me.data.merc.difficulty = me.diff; + me.data.merc.skillName = wantedMerc.skills.find(sk => sk.name === wantedSkill).name; + me.data.merc.skill = MercData.findByName(me.data.merc.skillName, me.act).skill; + CharData.updateData("merc", me.data) && me.update(); + console.log("ÿc9Mercenaryÿc0 :: " + me.data.merc.skillName + " merc hired."); } me.cancelUIFlags(); while (getInteractedNPC()) { diff --git a/libs/SoloPlay/Functions/NPCAction.js b/libs/SoloPlay/Functions/NPCAction.js index ed61513d..834f7d5f 100644 --- a/libs/SoloPlay/Functions/NPCAction.js +++ b/libs/SoloPlay/Functions/NPCAction.js @@ -186,8 +186,8 @@ // keep cold/pois res high with potions if (me.gold > 50000 && npc.getItem(sdk.items.ThawingPotion)) { - CharData.buffData.thawing.need() && Town.buyPots(12, "thawing", true); - CharData.buffData.antidote.need() && Town.buyPots(12, "antidote", true); + CharData.pots.get("thawing").need() && Town.buyPots(12, "thawing", true); + CharData.pots.get("antidote").need() && Town.buyPots(12, "antidote", true); } return true; @@ -352,7 +352,7 @@ if (me.charlvl < 6 && startingGold > 200) { Storage.BeltSize() === 1 && itemTypes.push(sdk.items.type.Belt); - !CharData.skillData.bowData.bowOnSwitch && itemTypes.push(sdk.items.type.Bow, sdk.items.type.Crossbow); + !CharData.skillData.bow.onSwitch && itemTypes.push(sdk.items.type.Bow, sdk.items.type.Crossbow); if (!itemTypes.length) return true; lowLevelShop = true; } diff --git a/libs/SoloPlay/Functions/PickitOverrides.js b/libs/SoloPlay/Functions/PickitOverrides.js index 3798d174..1381ebee 100644 --- a/libs/SoloPlay/Functions/PickitOverrides.js +++ b/libs/SoloPlay/Functions/PickitOverrides.js @@ -57,10 +57,10 @@ Pickit.checkItem = function (unit) { /** * Need to redo this */ - if (CharData.skillData.bowData.bowOnSwitch && [sdk.items.type.BowQuiver, sdk.items.type.CrossbowQuiver].includes(unit.itemType) && rval === Pickit.Result.WANTED) { - if ([sdk.items.type.Bow, sdk.items.type.AmazonBow].includes(CharData.skillData.bowData.bowType) && unit.itemType === sdk.items.type.BowQuiver) { + if (CharData.skillData.bow.onSwitch && [sdk.items.type.BowQuiver, sdk.items.type.CrossbowQuiver].includes(unit.itemType) && rval === Pickit.Result.WANTED) { + if ([sdk.items.type.Bow, sdk.items.type.AmazonBow].includes(CharData.skillData.bow.bowType) && unit.itemType === sdk.items.type.BowQuiver) { return resultObj(Pickit.Result.SOLOWANTS, "Switch-Arrows"); - } else if (CharData.skillData.bowData.bowType === sdk.items.type.Crossbow && unit.itemType === sdk.items.type.CrossbowQuiver) { + } else if (CharData.skillData.bow.bowType === sdk.items.type.Crossbow && unit.itemType === sdk.items.type.CrossbowQuiver) { return resultObj(Pickit.Result.SOLOWANTS, "Switch-Bolts"); } } @@ -91,7 +91,7 @@ Pickit.checkItem = function (unit) { if (AutoEquip.hasTier(unit) && !unit.identified) return resultObj(Pickit.Result.UNID); if (unit.isCharm/* && NTIP.GetCharmTier(unit) > 0 && unit.identified */) { - if (Item.autoEquipCharmCheck(unit)) { + if (CharmEquip.check(unit)) { return resultObj(Pickit.Result.SOLOWANTS, "Autoequip charm Tier: " + NTIP.GetCharmTier(unit)); } diff --git a/libs/SoloPlay/Functions/Quest.js b/libs/SoloPlay/Functions/Quest.js index c852ad52..6e5e6b21 100644 --- a/libs/SoloPlay/Functions/Quest.js +++ b/libs/SoloPlay/Functions/Quest.js @@ -360,9 +360,9 @@ const Quest = { delay(10 + me.ping * 2); if (me.respec || (me.getStat(sdk.stats.NewSkills) > preSkillAmount && me.getStat(sdk.stats.StatPts) > preStatAmount)) { - myData.me.currentBuild = CharInfo.getActiveBuild(); - myData[sdk.difficulty.nameOf(me.diff).toLowerCase()].respecUsed = true; - CharData.updateData("me", myData); + me.data.currentBuild = CharInfo.getActiveBuild(); + me.data[sdk.difficulty.nameOf(me.diff).toLowerCase()].respecUsed = true; + CharData.updateData("me", me.data); delay(750 + me.ping * 2); Town.clearBelt(); myPrint("respec done, restarting"); @@ -427,8 +427,8 @@ const Quest = { Item.logItem("Used my " + sdk.difficulty.nameOf(me.diff) + " socket quest on : ", item, null, true); D2Bot.printToConsole("Kolbot-SoloPlay :: Used my " + sdk.difficulty.nameOf(me.diff) + " socket quest on : " + item.name, sdk.colors.D2Bot.Gold); CharData.updateData(sdk.difficulty.nameOf(me.diff), "socketUsed", true); - myData[sdk.difficulty.nameOf(me.diff).toLowerCase()].socketUsed = true; - updateMyData(); + me.data[sdk.difficulty.nameOf(me.diff).toLowerCase()].socketUsed = true; + me.update(); if (!slot && !item.isInStash) { // Move item back to stash @@ -503,8 +503,8 @@ const Quest = { Item.logItem("Used my " + sdk.difficulty.nameOf(me.diff) + " imbue quest on : ", item, null, true); D2Bot.printToConsole("Kolbot-SoloPlay :: Used my " + sdk.difficulty.nameOf(me.diff) + " imbue quest on : " + item.name, sdk.colors.D2Bot.Gold); CharData.updateData(sdk.difficulty.nameOf(me.diff), "imbueUsed", true); - myData[sdk.difficulty.nameOf(me.diff).toLowerCase()].imbueUsed = true; - updateMyData(); + me.data[sdk.difficulty.nameOf(me.diff).toLowerCase()].imbueUsed = true; + me.update(); if (!slot && !item.isInStash) { // Move item back to stash diff --git a/libs/SoloPlay/Functions/TownOverrides.js b/libs/SoloPlay/Functions/TownOverrides.js index 532042f6..0b1556d9 100644 --- a/libs/SoloPlay/Functions/TownOverrides.js +++ b/libs/SoloPlay/Functions/TownOverrides.js @@ -27,18 +27,18 @@ new Overrides.Override(Town, Town.drinkPots, function(orignal, type) { if (objID) { // non-english version - if (!CharData.buffData[objID]) { + if (!CharData.pots.has(objID)) { typeof type === "number" ? (objID = pots[objID]) : (objID = type.toLowerCase()); } - if (!CharData.buffData[objID].active() || CharData.buffData[objID].timeLeft() <= 0) { - CharData.buffData[objID].tick = getTickCount(); - CharData.buffData[objID].duration = objDrank.quantity * 30 * 1000; + if (!CharData.pots.get(objID).active() || CharData.pots.get(objID).timeLeft() <= 0) { + CharData.pots.get(objID).tick = getTickCount(); + CharData.pots.get(objID).duration = objDrank.quantity * 30 * 1000; } else { - CharData.buffData[objID].duration += (objDrank.quantity * 30 * 1000) - (getTickCount() - CharData.buffData[objID].tick); + CharData.pots.get(objID).duration += (objDrank.quantity * 30 * 1000) - (getTickCount() - CharData.pots.get(objID).tick); } - console.log("ÿc9DrinkPotsÿc0 :: drank " + objDrank.quantity + " " + objDrank.potName + "s. Timer [" + Tracker.formatTime(CharData.buffData[objID].duration) + "]"); + console.log("ÿc9DrinkPotsÿc0 :: drank " + objDrank.quantity + " " + objDrank.potName + "s. Timer [" + Tracker.formatTime(CharData.pots.get(objID).duration) + "]"); } } } catch (e) { @@ -323,7 +323,7 @@ Town.needStash = function () { } let items = (Storage.Inventory.Compare(Config.Inventory) || []) - .filter(item => !Town.ignoreType(item.itemType) && (!item.isCharm || !Item.keptCharmGids.has(item.gid))); + .filter(item => !Town.ignoreType(item.itemType) && (!item.isCharm || !CharmEquip.keptGids.has(item.gid))); for (let i = 0; i < items.length; i += 1) { if (Storage.Stash.CanFit(items[i])) { @@ -340,7 +340,7 @@ Town.needStash = function () { Town.canStash = function (item) { if (Town.ignoreType(item.itemType) || [sdk.items.quest.HoradricStaff, sdk.items.quest.KhalimsWill].includes(item.classid) - || (item.isCharm && Item.autoEquipCharmCheck(item))) { + || (item.isCharm && CharmEquip.check(item))) { return false; } @@ -723,8 +723,8 @@ Town.doChores = function (repair = false, givenTasks = {}) { Town.fillTomes(); NPCAction.buyPotions(); this.buyKeys(); - extraTasks.thawing && CharData.buffData.thawing.need() && Town.buyPots(12, "Thawing", true); - extraTasks.antidote && CharData.buffData.antidote.need() && Town.buyPots(12, "Antidote", true); + extraTasks.thawing && CharData.pots.get("thawing").need() && Town.buyPots(12, "Thawing", true); + extraTasks.antidote && CharData.pots.get("antidote").need() && Town.buyPots(12, "Antidote", true); extraTasks.stamina && Town.buyPots(12, "Stamina", true); NPCAction.shopItems(); NPCAction.repair(repair); diff --git a/libs/SoloPlay/Scripts/tristram.js b/libs/SoloPlay/Scripts/tristram.js index 42e931b0..ff5f0c90 100644 --- a/libs/SoloPlay/Scripts/tristram.js +++ b/libs/SoloPlay/Scripts/tristram.js @@ -49,9 +49,14 @@ function tristram () { } } - Pather.checkWP(sdk.areas.StonyField, true) ? Pather.useWaypoint(sdk.areas.StonyField) : Pather.getWP(sdk.areas.StonyField); + Pather.checkWP(sdk.areas.StonyField, true) + ? Pather.useWaypoint(sdk.areas.StonyField) + : Pather.getWP(sdk.areas.StonyField); Precast.doPrecast(true); - Pather.moveToPreset(sdk.areas.StonyField, sdk.unittype.Monster, sdk.monsters.preset.Rakanishu, 10, 10, false, true); + Pather.moveToPresetMonster(sdk.areas.StonyField, sdk.monsters.preset.Rakanishu, { callback: () => { + let rak = Game.getMonster(getLocaleString(sdk.locale.monsters.Rakanishu)); + return rak && (rak.dead || rak.distance < 20); + }, offX: 10, offY: 10 }); Attack.killTarget(getLocaleString(sdk.locale.monsters.Rakanishu)); Pather.moveToPreset(sdk.areas.StonyField, sdk.unittype.Object, sdk.quest.chest.StoneAlpha, null, null, true); @@ -136,7 +141,9 @@ function tristram () { Attack.clearLocations(spots); } - delete Common.Cain; + if (me.cain) { + delete Common.Cain; + } return true; } diff --git a/libs/SoloPlay/SoloPlay.js b/libs/SoloPlay/SoloPlay.js index 4dc9fe3d..1ad590ac 100644 --- a/libs/SoloPlay/SoloPlay.js +++ b/libs/SoloPlay/SoloPlay.js @@ -96,9 +96,9 @@ function main () { return; case "data--": - console.debug("update myData"); + console.debug("update me.data"); obj = JSON.parse(msg.split("data--")[1]); - Misc.updateRecursively(myData, obj); + Misc.updateRecursively(me.data, obj); return; } @@ -147,8 +147,8 @@ function main () { { console.debug(sdk.colors.Green + "//-----------DataDump Start-----------//", "\nÿc8ThreadData ::\n", getScript(true), - "\nÿc8MainData ::\n", myData, - "\nÿc8BuffData ::\n", CharData.buffData, + "\nÿc8MainData ::\n", me.data, + "\nÿc8BuffData ::\n", CharData.pots, "\nÿc8SkillData ::\n", CharData.skillData, "\nÿc8GlobalVariabls ::\n", Object.keys(global), "\n" + sdk.colors.Red + "//-----------DataDump End-----------//"); diff --git a/libs/SoloPlay/Threads/EventThread.js b/libs/SoloPlay/Threads/EventThread.js index 8a6c9d6d..a0f22453 100644 --- a/libs/SoloPlay/Threads/EventThread.js +++ b/libs/SoloPlay/Threads/EventThread.js @@ -147,14 +147,14 @@ function main () { break; case msg.substring(0, 6) === "data--": - console.debug("update myData"); + console.debug("update me.data"); obj = JSON.parse(msg.split("data--")[1]); - Misc.updateRecursively(myData, obj); + Misc.updateRecursively(me.data, obj); break; case msg.toLowerCase() === "test": console.debug(sdk.colors.Green + "//-----------DataDump Start-----------//\nÿc8MainData ::\n", - myData, "\nÿc8BuffData ::\n", CharData.buffData, "\nÿc8SkillData ::\n", CharData.skillData, "\n" + sdk.colors.Red + "//-----------DataDump End-----------//"); + me.data, "\nÿc8BuffData ::\n", CharData.pots, "\nÿc8SkillData ::\n", CharData.skillData, "\n" + sdk.colors.Red + "//-----------DataDump End-----------//"); break; } diff --git a/libs/SoloPlay/Threads/ToolsThread.js b/libs/SoloPlay/Threads/ToolsThread.js index 6356b582..0e3b04a9 100644 --- a/libs/SoloPlay/Threads/ToolsThread.js +++ b/libs/SoloPlay/Threads/ToolsThread.js @@ -119,7 +119,8 @@ function main () { this.getPotion = function (pottype = -1, type = -1) { if (pottype === undefined) return false; - let items = me.getItemsEx().filter(item => item.itemType === pottype && (type > Common.Toolsthread.pots.Rejuv ? item.isInBelt : true)); + let items = me.getItemsEx() + .filter(item => item.itemType === pottype && (type > Common.Toolsthread.pots.Rejuv ? item.isInBelt : true)); if (items.length === 0) return false; let invoFirst = [Common.Toolsthread.pots.Health, Common.Toolsthread.pots.Mana].includes(type); @@ -223,42 +224,37 @@ function main () { return false; }; + /** + * Handles thawing/antidote/stamina potions + * @param {number} type + * @returns {boolean} + */ this.drinkSpecialPotion = function (type) { if (type === undefined) return false; - let objID; - let name = (() => { - switch (type) { - case sdk.items.ThawingPotion: - return "thawing"; - case sdk.items.AntidotePotion: - return "antidote"; - case sdk.items.StaminaPotion: - return "stamina"; - default: - return ""; - } - })(); + if (!CharData.pots.has(type)) return false; + // give at least a second delay between pots + if (CharData.pots.get(type).tick < 1000) return false; // mode 18 - can't drink while leaping/whirling etc. - // give at least a second delay between pots - if (me.dead || me.mode === sdk.player.mode.SkillActionSequence || (getTickCount() - CharData.buffData[name].tick < 1000)) { + if (me.dead || me.mode === sdk.player.mode.SkillActionSequence) { return false; } - let pot = me.getItemsEx(-1, sdk.items.mode.inStorage).filter((p) => p.isInInventory && p.classid === type).first(); - !!pot && (objID = pot.name.split(" ")[0].toLowerCase()); + let pot = me.getItemsEx(-1, sdk.items.mode.inStorage) + .filter((p) => p.isInInventory && p.classid === type).first(); - if (objID) { + if (pot) { try { pot.interact(); - if (!CharData.buffData[objID].active() || CharData.buffData[objID].timeLeft() <= 0) { - CharData.buffData[objID].tick = getTickCount(); - CharData.buffData[objID].duration = 3e4; + + if (!CharData.pots.get(type).active() || CharData.pots.get(type).timeLeft() <= 0) { + CharData.pots.get(type).tick = getTickCount(); + CharData.pots.get(type).duration = 3e4; } else { - CharData.buffData[objID].duration += 3e4 - (getTickCount() - CharData.buffData[objID].tick); + CharData.pots.get(type).duration += 3e4 - (getTickCount() - CharData.pots.get(type).tick); } - console.debug(CharData.buffData); + console.debug(CharData.pots); } catch (e) { console.warn(e); } @@ -370,8 +366,8 @@ function main () { + " | ÿc4AutoEquipKeepCheckMerc: ÿc0" + Item.autoEquipCheckMerc(itemToCheck, true) + "\nÿc4Cubing Item: ÿc0" + Cubing.keepItem(itemToCheck) + " | ÿc4Runeword Item: ÿc0" + Runewords.keepItem(itemToCheck) + " | ÿc4Crafting Item: ÿc0" + CraftingSystem.keepItem(itemToCheck) + " | ÿc4SoloWants Item: ÿc0" + SoloWants.keepItem(itemToCheck) + "\nÿc4ItemType: ÿc0" + itemToCheck.itemType + "| ÿc4Classid: ÿc0" + itemToCheck.classid + "| ÿc4Quality: ÿc0" + itemToCheck.quality; - charmString = "ÿc4InvoQuantity: ÿc0" + NTIP.getInvoQuantity(itemToCheck) + " | ÿc4hasStats: ÿc0" + NTIP.hasStats(itemToCheck) + " | ÿc4FinalCharm: ÿc0" + Item.isFinalCharm(itemToCheck) + "\n" - + "ÿc4CharmType: ÿc0" + Item.getCharmType(itemToCheck) + " | ÿc4AutoEquipCharmCheck: ÿc0" + Item.autoEquipCharmCheck(itemToCheck) + " | ÿc4CharmTier: ÿc0" + NTIP.GetCharmTier(itemToCheck); + charmString = "ÿc4InvoQuantity: ÿc0" + NTIP.getInvoQuantity(itemToCheck) + " | ÿc4hasStats: ÿc0" + NTIP.hasStats(itemToCheck) + " | ÿc4FinalCharm: ÿc0" + CharmEquip.isFinalCharm(itemToCheck) + "\n" + + "ÿc4CharmType: ÿc0" + CharmEquip.getCharmType(itemToCheck) + " | ÿc4AutoEquipCharmCheck: ÿc0" + CharmEquip.check(itemToCheck) + " | ÿc4CharmTier: ÿc0" + NTIP.GetCharmTier(itemToCheck); generalString = "ÿc4ItemName: ÿc0" + itemToCheck.fname.split("\n").reverse().join(" ").replace(/ÿc[0-9!"+<;.*]/, "") + "\nÿc4Pickit: ÿc0" + Pickit.checkItem(itemToCheck).result + " | ÿc4NTIP.CheckItem: ÿc0" + NTIP.CheckItem(itemToCheck, false, true).result + " | ÿc4NTIP.CheckItem No Tier: ÿc0" + NTIP.CheckItem(itemToCheck, NTIP_CheckListNoTier, true).result; } @@ -514,9 +510,9 @@ function main () { return; case "data--": - console.debug("update myData"); + console.debug("update me.data"); obj = JSON.parse(msg.split("data--")[1]); - Misc.updateRecursively(myData, obj); + Misc.updateRecursively(me.data, obj); return; } @@ -543,8 +539,8 @@ function main () { { console.debug(sdk.colors.Green + "//-----------DataDump Start-----------//", "\nÿc8ThreadData ::\n", getScript(true), - "\nÿc8MainData ::\n", myData, - "\nÿc8BuffData ::\n", CharData.buffData, + "\nÿc8MainData ::\n", me.data, + "\nÿc8BuffData ::\n", CharData.pots, "\nÿc8SkillData ::\n", CharData.skillData, "\nÿc8GlobalVariabls ::\n", Object.keys(global), "\n" + sdk.colors.Red + "//-----------DataDump End-----------//"); @@ -607,7 +603,9 @@ function main () { Config.UseRejuvHP > 0 && me.hpPercent < Config.UseRejuvHP && this.drinkPotion(Common.Toolsthread.pots.Rejuv); if (Config.LifeChicken > 0 && me.hpPercent <= Config.LifeChicken && !me.inTown) { - !Developer.hideChickens && D2Bot.printToConsole("Life Chicken (" + me.hp + "/" + me.hpmax + ")" + Attack.getNearestMonster() + " in " + getAreaName(me.area) + ". Ping: " + me.ping, sdk.colors.D2Bot.Red); + if (!Developer.hideChickens) { + D2Bot.printToConsole("Life Chicken (" + me.hp + "/" + me.hpmax + ")" + Attack.getNearestMonster() + " in " + getAreaName(me.area) + ". Ping: " + me.ping, sdk.colors.D2Bot.Red); + } this.exit(true); return true; diff --git a/libs/SoloPlay/Threads/TownChicken.js b/libs/SoloPlay/Threads/TownChicken.js index 81bd91f2..c0ea681d 100644 --- a/libs/SoloPlay/Threads/TownChicken.js +++ b/libs/SoloPlay/Threads/TownChicken.js @@ -383,14 +383,14 @@ function main() { break; case msg.substring(0, 6) === "data--": - console.debug("update myData"); + console.debug("update me.data"); obj = JSON.parse(msg.split("data--")[1]); - Misc.updateRecursively(myData, obj); + Misc.updateRecursively(me.data, obj); break; case msg.toLowerCase() === "test": console.debug(sdk.colors.Green + "//-----------DataDump Start-----------//\nÿc8MainData ::\n", - myData, "\nÿc8BuffData ::\n", CharData.buffData, "\nÿc8SkillData ::\n", CharData.skillData, "\n" + sdk.colors.Red + "//-----------DataDump End-----------//"); + me.data, "\nÿc8BuffData ::\n", CharData.pots, "\nÿc8SkillData ::\n", CharData.skillData, "\n" + sdk.colors.Red + "//-----------DataDump End-----------//"); break; } diff --git a/libs/SoloPlay/Tools/CharData.js b/libs/SoloPlay/Tools/CharData.js index 6b28b35b..adcb6ee5 100644 --- a/libs/SoloPlay/Tools/CharData.js +++ b/libs/SoloPlay/Tools/CharData.js @@ -7,353 +7,320 @@ includeIfNotIncluded("SoloPlay/Tools/Tracker.js"); -const CharData = { - filePath: "libs/SoloPlay/Data/" + me.profile + "/" + me.profile + "-CharData.json", - threads: ["libs/SoloPlay/SoloPlay.js", "libs/SoloPlay/Threads/TownChicken.js", "libs/SoloPlay/Threads/ToolsThread.js", "libs/SoloPlay/Threads/EventThread.js"], - default: { - initialized: false, - normal: { - respecUsed: false, - imbueUsed: false, - socketUsed: false, - }, - nightmare: { - respecUsed: false, - imbueUsed: false, - socketUsed: false, - }, - hell: { - respecUsed: false, - imbueUsed: false, - socketUsed: false, - }, - me: { - task: "", - startTime: 0, - charName: "", - classid: -1, - level: 1, - strength: 0, - dexterity: 0, - currentBuild: "Start", - finalBuild: "", - highestDifficulty: "Normal", - setDifficulty: "Normal", - charms: {}, - charmGids: [], - }, - merc: { - act: 1, - classid: 271, - difficulty: 0, - strength: 0, - dexterity: 0, - type: "", - gear: [], - } - }, +const CharData = (function () { + const _create = function () { + let obj = Object.assign({}, this._default); + let string = JSON.stringify(obj, null, 2); - loginData: { - filePath: "libs/SoloPlay/Data/" + me.profile + "/" + me.profile + "-LoginData.json", - default: { Acc: "", Pass: "", Char: "", existing: false }, + if (!FileTools.exists("libs/SoloPlay/Data/" + me.profile)) { + let folder = dopen("libs/SoloPlay/Data"); + folder && folder.create(me.profile); + } - create: function () { - let obj = Object.assign({}, this.default); - let string = JSON.stringify(obj, null, 2); + FileAction.write(this.filePath, string); - if (!FileTools.exists("libs/SoloPlay/Data/" + me.profile)) { - let folder = dopen("libs/SoloPlay/Data"); - folder && folder.create(me.profile); - } + return obj; + }; - FileAction.write(this.filePath, string); + const _getObj = function () { + if (!FileTools.exists(this.filePath)) return this.create(); - return obj; - }, + let obj; + let string = FileAction.read(this.filePath); - getObj: function () { - if (!FileTools.exists(this.filePath)) return CharData.loginData.create(); + try { + obj = JSON.parse(string); + } catch (e) { + // If we failed, file might be corrupted, so create a new one + obj = this.create(); + } - let obj; - let string = FileAction.read(this.filePath); + return obj ? obj : this._default; + }; - try { - obj = JSON.parse(string); - } catch (e) { - // If we failed, file might be corrupted, so create a new one - obj = this.create(); - } + const _getStats = function () { + let obj = this.getObj(); + return clone(obj); + }; - return obj ? obj : this.default; - }, + const _updateData = function (arg, property, value) { + let obj = this.getObj(); + typeof arg !== "string" && (arg = arg.toString()); + typeof arg === "string" && (arg = arg.toLowerCase()); - getStats: function () { - let obj = this.getObj(); - return clone(obj); - }, + if (typeof property === "object") { + obj = Object.assign(obj, property); + return FileAction.write(this.filePath, JSON.stringify(obj, null, 2)); + } - updateData: function (arg, property, value) { - let obj = this.getObj(); - typeof arg !== "string" && (arg = arg.toString()); - typeof arg === "string" && (arg = arg.toLowerCase()); + if (obj.hasOwnProperty(property)) { + obj[property] = value; + return FileAction.write(this.filePath, JSON.stringify(obj, null, 2)); + } else if (obj.hasOwnProperty(arg) && obj[arg].hasOwnProperty(property)) { + obj[arg][property] = value; + return FileAction.write(this.filePath, JSON.stringify(obj, null, 2)); + } - if (typeof property === "object") { - obj = Object.assign(obj, property); - return FileAction.write(this.filePath, JSON.stringify(obj, null, 2)); - } + return false; + }; + + return { + filePath: "libs/SoloPlay/Data/" + me.profile + "/" + me.profile + "-CharData.json", + threads: [ + "libs/SoloPlay/SoloPlay.js", "libs/SoloPlay/Threads/TownChicken.js", + "libs/SoloPlay/Threads/ToolsThread.js", "libs/SoloPlay/Threads/EventThread.js" + ], + _default: (function () { + let diffObj = { respecUsed: false, imbueUsed: false, socketUsed: false }; + return { + initialized: false, + normal: Object.assign({}, diffObj), + nightmare: Object.assign({}, diffObj), + hell: Object.assign({}, diffObj), + task: "", + startTime: 0, + charName: "", + classid: -1, + level: 1, + strength: 0, + dexterity: 0, + currentBuild: "Start", + finalBuild: "", + highestDifficulty: "Normal", + setDifficulty: "Normal", + charms: {}, + charmGids: [], + merc: { + act: 1, + classid: sdk.mercs.Rogue, + difficulty: sdk.difficulty.Normal, + strength: 0, + dexterity: 0, + skill: 0, + skillName: "", + gear: [], + } + }; + })(), - if (!!obj[arg] && obj[arg].hasOwnProperty(property)) { - obj[arg][property] = value; - return FileAction.write(this.filePath, JSON.stringify(obj, null, 2)); + login: (function () { + return { + filePath: "libs/SoloPlay/Data/" + me.profile + "/" + me.profile + "-LoginData.json", + _default: { account: "", pass: "", currentChar: "", tag: "", charCount: 0, existing: false }, + + create: function () { + return _create.call(this); + }, + getObj: function () { + return _getObj.call(this); + }, + + getStats: function () { + return _getStats.call(this); + }, + + updateData: function (arg, property, value) { + return _updateData.call(this, arg, property, value); + }, + }; + })(), + + charms: (function () { + /** + * @constructor + * @param {number} classid + */ + function Charm (classid) { + this.classid = classid; } - return false; - }, - }, - - charmData: { - getCountInfo: function () { - const finalCharmKeys = Object.keys(myData.me.charms); - let [curr, max] = [0, 0]; - - for (let i = 0; i < finalCharmKeys.length; i++) { - let cKey = finalCharmKeys[i]; - if (myData.me.charms[cKey].classid === this.id) { - curr += myData.me.charms[cKey].have.length; - max += myData.me.charms[cKey].max; - } + Charm.prototype.count = function () { + let [curr, max] = [0, 0]; + Object.keys(me.data.charms).forEach(cKey => { + if (me.data.charms[cKey].classid === this.classid) { + curr += me.data.charms[cKey].have.length; + max += me.data.charms[cKey].max; + } + }); + + return { + curr: curr, + max: max + }; + }; + /** @type {Map true); + this.tick = 0; + this.duration = 0; } - return { - curr: curr, - max: max + BuffPot.prototype.active = function () { + return me.getState(this.state); }; - }, - small: { - id: sdk.items.SmallCharm, - }, - large: { - id: sdk.items.LargeCharm, - }, - grand: { - id: sdk.items.GrandCharm, - } - }, - - buffData: { - stamina: { - tick: 0, - duration: 0, - active: function () { - return me.getState(sdk.states.StaminaPot); - }, - timeLeft: function () { - return this.duration > 0 ? this.duration - (getTickCount() - this.tick) : 0; - }, - need: function () { - return (!this.active() || this.timeLeft() < Time.minutes(5)); - }, - }, - thawing: { - tick: 0, - duration: 0, - active: function () { - return me.getState(sdk.states.Thawing); - }, - timeLeft: function () { + BuffPot.prototype.timeLeft = function () { return this.duration > 0 ? this.duration - (getTickCount() - this.tick) : 0; - }, - need: function () { - return (me.coldRes < 75 && (!this.active() || this.timeLeft() < Time.minutes(5))); - }, - }, + }; - antidote: { - tick: 0, - duration: 0, - active: function () { - return me.getState(sdk.states.Antidote); - }, - timeLeft: function () { - return this.duration > 0 ? this.duration - (getTickCount() - this.tick) : 0; - }, - need: function () { - // don't really like the hardcoded time value of 5 minutes, its okay but feel like it should be more dynamic - return (me.poisonRes < 75 && (!this.active() || this.timeLeft() < Time.minutes(5))); + BuffPot.prototype.need = function () { + return (this.check() && (!this.active() || this.timeLeft() < Time.minutes(5))); + }; + + /** @type {Map} */ + const _buffPots = new Map(); + _buffPots.set(sdk.items.StaminaPotion, new BuffPot(sdk.states.StaminaPot, + () => Skill.canUse(sdk.skills.Vigor) || Pather.canTeleport()) + ); + _buffPots.set(sdk.items.ThawingPotion, new BuffPot(sdk.states.Thawing, () => me.coldRes < 75)); + _buffPots.set(sdk.items.AntidotePotion, new BuffPot(sdk.states.Antidote, () => me.poisonRes < 75)); + + // hacky for now - just to handle the old way of accessing buff pots + _buffPots.set("stamina", _buffPots.get(sdk.items.StaminaPotion)); + _buffPots.set("thawing", _buffPots.get(sdk.items.ThawingPotion)); + _buffPots.set("antidote", _buffPots.get(sdk.items.AntidotePotion)); + + return _buffPots; + }()), + + skillData: { + skills: [], + currentChargedSkills: [], + chargedSkills: [], + chargedSkillsOnSwitch: [], + /** + * @todo fix this, it's ugly + */ + bow: { + initialized: false, + onSwitch: false, + bowGid: 0, + bowType: 0, + arrows: 0, + quiverType: 0, + setBowInfo: function (bow, init = false) { + if (bow === undefined) return; + this.bowGid = bow.gid; + this.bowType = bow.itemType; + this.bowOnSwitch = bow.isOnSwap; + SetUp.bowQuiver(); + init && (this.initialized = true); + !init && CharData.skillData.update(); + }, + setArrowInfo: function (quiver) { + if (quiver === undefined) return; + this.arrows = Math.floor((quiver.getStat(sdk.stats.Quantity) * 100) / getBaseStat("items", quiver.classid, "maxstack")); + this.quiverType = quiver.itemType; + }, + resetBowData: function () { + this.bowOnSwitch = false; + [this.bowGid, this.bowType, this.arrows, this.quiverType] = [0, 0, 0, 0]; + NTIP.resetRuntimeList(); + CharData.skillData.update(); + }, }, - }, - update: function () { - const obj = JSON.stringify(copyObj(this)); - const myThread = getScript(true).name; - CharData.threads.forEach(function (script) { - let curr = getScript(script); - if (curr && myThread !== curr.name) { - curr.send("buff--" + obj); - } - }); - }, - }, - - skillData: { - skills: [], - currentChargedSkills: [], - chargedSkills: [], - chargedSkillsOnSwitch: [], - bowData: { - initialized: false, - bowOnSwitch: false, - bowGid: 0, - bowType: 0, - arrows: 0, - quiverType: 0, - setBowInfo: function (bow, init = false) { - if (bow === undefined) return; - this.bowGid = bow.gid; - this.bowType = bow.itemType; - this.bowOnSwitch = bow.isOnSwap; - SetUp.bowQuiver(); - init && (this.initialized = true); - !init && CharData.skillData.update(); + init: function (skillIds, mainSkills, switchSkills) { + this.currentChargedSkills = skillIds.slice(0); + this.chargedSkills = mainSkills.slice(0); + this.chargedSkillsOnSwitch = switchSkills.slice(0); + this.skills = me.getSkill(4).map((skill) => skill[0]); }, - setArrowInfo: function (quiver) { - if (quiver === undefined) return; - this.arrows = Math.floor((quiver.getStat(sdk.stats.Quantity) * 100) / getBaseStat("items", quiver.classid, "maxstack")); - this.quiverType = quiver.itemType; + + update: function () { + let obj = JSON.stringify(copyObj(this)); + let myThread = getScript(true).name; + CharData.threads.forEach(function (script) { + let curr = getScript(script); + if (curr && myThread !== curr.name) { + curr.send("skill--" + obj); + } + }); }, - resetBowData: function () { - this.bowOnSwitch = false; - [this.bowGid, this.bowType, this.arrows, this.quiverType] = [0, 0, 0, 0]; - NTIP.resetRuntimeList(); - CharData.skillData.update(); + + haveChargedSkill: function (skillid = []) { + // convert to array if not one + !Array.isArray(skillid) && (skillid = [skillid]); + return this.currentChargedSkills.some(s => skillid.includes(s)); }, - }, - init: function (skillIds, mainSkills, switchSkills) { - this.currentChargedSkills = skillIds.slice(0); - this.chargedSkills = mainSkills.slice(0); - this.chargedSkillsOnSwitch = switchSkills.slice(0); - this.skills = me.getSkill(4).map((skill) => skill[0]); + haveChargedSkillOnSwitch: function (skillid = 0) { + return this.chargedSkillsOnSwitch.some(chargeSkill => chargeSkill.skill === skillid); + } }, - update: function () { - let obj = JSON.stringify(copyObj(this)); + // updates config obj across all threads - excluding our current + updateConfig: function () { + let obj = JSON.stringify(copyObj(Config)); let myThread = getScript(true).name; CharData.threads.forEach(function (script) { let curr = getScript(script); if (curr && myThread !== curr.name) { - curr.send("skill--" + obj); + curr.send("config--" + obj); } }); }, - haveChargedSkill: function (skillid = []) { - // convert to array if not one - !Array.isArray(skillid) && (skillid = [skillid]); - return this.currentChargedSkills.some(s => skillid.includes(s)); + /** + * @returns {MyData} + */ + create: function () { + return _create.call(this); }, - haveChargedSkillOnSwitch: function (skillid = 0) { - return this.chargedSkillsOnSwitch.some(chargeSkill => chargeSkill.skill === skillid); - } - }, - - // updates config obj across all threads - excluding our current - updateConfig: function () { - let obj = JSON.stringify(copyObj(Config)); - let myThread = getScript(true).name; - CharData.threads.forEach(function (script) { - let curr = getScript(script); - if (curr && myThread !== curr.name) { - curr.send("config--" + obj); - } - }); - }, - - /** - * - * @returns {charData} - */ - create: function () { - let obj = Object.assign({}, this.default); - let string = JSON.stringify(obj, null, 2); - - if (!FileTools.exists("libs/SoloPlay/Data/" + me.profile)) { - let folder = dopen("libs/SoloPlay/Data"); - folder && folder.create(me.profile); - } - - FileAction.write(this.filePath, string); - - return obj; - }, - - /** - * - * @returns {charData} - */ - getObj: function () { - if (!FileTools.exists(this.filePath)) return CharData.create(); - - let obj; - let string = FileAction.read(this.filePath); - - try { - obj = JSON.parse(string); - } catch (e) { - // If we failed, file might be corrupted, so create a new one - obj = this.create(); - } - - return obj ? obj : this.default; - }, - - /** - * - * @returns {charData} - */ - getStats: function () { - let obj = this.getObj(); - return clone(obj); - }, - - updateData: function (arg, property, value) { - while (me.ingame && !me.gameReady) { - delay(100); - } - - console.trace(); - - let obj = this.getObj(); - typeof arg !== "string" && (arg = arg.toString()); - typeof arg === "string" && (arg = arg.toLowerCase()); + /** + * @returns {MyData} + */ + getObj: function () { + return _getObj.call(this); + }, - if (typeof property === "object") { - obj = Object.assign(obj, property); - return FileAction.write(this.filePath, JSON.stringify(obj, null, 2)); - } + /** + * @returns {MyData} + */ + getStats: function () { + return _getStats.call(this); + }, - if (!!obj[arg] && obj[arg].hasOwnProperty(property)) { - obj[arg][property] = value; - return FileAction.write(this.filePath, JSON.stringify(obj, null, 2)); - } + updateData: function (arg, property, value) { + while (me.ingame && !me.gameReady) { + delay(100); + } - return false; - }, + console.trace(); - delete: function (deleteMain = false) { - if (deleteMain && FileTools.exists("data/" + me.profile + ".json")) { - FileTools.remove("data/" + me.profile + ".json"); - } - - FileTools.exists(this.filePath) && FileTools.remove(this.filePath); - FileTools.exists(Tracker.GTPath) && FileTools.remove(Tracker.GTPath); + return _updateData.call(this, arg, property, value); + }, - return !(FileTools.exists(this.filePath) && FileTools.exists("libs/SoloPlay/Data/" + me.profile + ".GameTime" + ".json")); - }, -}; + delete: function (deleteMain = false) { + if (deleteMain && FileTools.exists("data/" + me.profile + ".json")) { + FileTools.remove("data/" + me.profile + ".json"); + } + + FileTools.exists(this.filePath) && FileTools.remove(this.filePath); + FileTools.exists(Tracker.GTPath) && FileTools.remove(Tracker.GTPath); -CharData.charmData.small.getCountInfo = CharData.charmData.getCountInfo.bind(CharData.charmData.small); -CharData.charmData.large.getCountInfo = CharData.charmData.getCountInfo.bind(CharData.charmData.large); -CharData.charmData.grand.getCountInfo = CharData.charmData.getCountInfo.bind(CharData.charmData.grand); + return !(FileTools.exists(this.filePath) && FileTools.exists("libs/SoloPlay/Data/" + me.profile + ".GameTime" + ".json")); + }, + }; +})(); diff --git a/libs/SoloPlay/Tools/OOGOverrides.js b/libs/SoloPlay/Tools/OOGOverrides.js index 3dfe85fd..5abfe1aa 100644 --- a/libs/SoloPlay/Tools/OOGOverrides.js +++ b/libs/SoloPlay/Tools/OOGOverrides.js @@ -87,16 +87,16 @@ const locations = {}; { let soloStats = CharData.getStats(); - if (!soloStats.me.finalBuild || soloStats.me.finalBuild !== Starter.profileInfo.tag) { + if (!soloStats.finalBuild || soloStats.finalBuild !== Starter.profileInfo.tag) { D2Bot.setProfile(null, null, null, null, null, Starter.profileInfo.tag); - soloStats.me.finalBuild = Starter.profileInfo.tag; - soloStats.me.charms = {}; + soloStats.finalBuild = Starter.profileInfo.tag; + soloStats.charms = {}; CharData.updateData("me", soloStats); } - if (!["Start", "Stepping", "Leveling"].includes(soloStats.me.currentBuild) && soloStats.me.currentBuild !== soloStats.me.finalBuild) { - soloStats.me.currentBuild = "Leveling"; - soloStats.me.charms = {}; + if (!["Start", "Stepping", "Leveling"].includes(soloStats.currentBuild) && soloStats.currentBuild !== soloStats.finalBuild) { + soloStats.currentBuild = "Leveling"; + soloStats.charms = {}; CharData.updateData("me", soloStats); } } @@ -148,11 +148,11 @@ const locations = {}; let soloStats = CharData.getStats(); const NameGen = require("./NameGen"); - soloStats.me.startTime !== 0 && Tracker.reset(); - if (soloStats.me.currentBuild !== "Start" || soloStats.me.level > 1) { - let finalBuild = soloStats.me.finalBuild; - Object.assign(soloStats, CharData.default); - soloStats.me.finalBuild = finalBuild; + soloStats.startTime !== 0 && Tracker.reset(); + if (soloStats.currentBuild !== "Start" || soloStats.level > 1) { + let finalBuild = soloStats.finalBuild; + Object.assign(soloStats, CharData._default); + soloStats.finalBuild = finalBuild; CharData.updateData("me", soloStats); } @@ -683,7 +683,7 @@ const locations = {}; }; Starter.checkDifficulty = function () { - let setDiff = CharData.getStats().me.setDifficulty; + let setDiff = CharData.getStats().setDifficulty; if (setDiff) { console.debug(setDiff); Starter.gameInfo.difficulty = setDiff; diff --git a/libs/SoloPlay/Tools/Overlay.js b/libs/SoloPlay/Tools/Overlay.js index c50b7bc0..16f1acac 100644 --- a/libs/SoloPlay/Tools/Overlay.js +++ b/libs/SoloPlay/Tools/Overlay.js @@ -19,7 +19,7 @@ const Overlay = { build: SetUp.currentBuild, realm: (me.realm ? me.realm : "SinglePlayer"), difficulty: () => sdk.difficulty.nameOf(me.diff), - level: () => myData.me.level, + level: () => me.data.level, text: { hooks: [], GameTracker: Tracker.readObj(Tracker.GTPath), diff --git a/libs/SoloPlay/Utils/Init.js b/libs/SoloPlay/Utils/Init.js index 8e96e76d..5346fec0 100644 --- a/libs/SoloPlay/Utils/Init.js +++ b/libs/SoloPlay/Utils/Init.js @@ -58,7 +58,7 @@ me.cancelUIFlags(); // initialize final charms if we have any - Item.initCharms(); + CharmEquip.init(); } return true; })(); diff --git a/libs/SoloPlay/Workers/EventEmitter.js b/libs/SoloPlay/Workers/EventEmitter.js index 4217db9f..ab2e13a6 100644 --- a/libs/SoloPlay/Workers/EventEmitter.js +++ b/libs/SoloPlay/Workers/EventEmitter.js @@ -1,8 +1,9 @@ /** * @filename EventEmitter.js - * @author Jaenster - * @desc Transpiled Global modifying UMD module to handle emitting events and add prototypes ("on", "emit", "once", "off") - * to Unit + * @author theBGuy + * @credit jaenster + * @desc Global modifying UMD module to handle emitting events + * and add prototypes ("on", "emit", "once", "off") to Unit * */ diff --git a/libs/SoloPlay/index.d.ts b/libs/SoloPlay/index.d.ts index 977899d0..95712618 100644 --- a/libs/SoloPlay/index.d.ts +++ b/libs/SoloPlay/index.d.ts @@ -1,4 +1,4 @@ - +// @ts-nocheck declare global { interface Math { percentDifference(value1: number, value2: number): number; @@ -61,6 +61,48 @@ declare global { active: () => boolean; } + interface MyData { + initialized: boolean; + normal: { + respecUsed: boolean; + imbueUsed: boolean; + socketUsed: boolean; + }; + nightmare: { + respecUsed: boolean; + imbueUsed: boolean; + socketUsed: boolean; + }; + hell: { + respecUsed: boolean; + imbueUsed: boolean; + socketUsed: boolean; + }; + task: string; + startTime: number; + charName: string; + classid: number; + level: number; + strength: number; + dexterity: number; + currentBuild: string; + finalBuild: string; + highestDifficulty: string; + setDifficulty: string; + charms: Record boolean; }>; + charmGids: number[]; + merc: { + act: number; + classid: number; + difficulty: number; + strength: number; + dexterity: number; + skill: number; + skillName: string; + gear: number[]; + }; + } + interface MeType { readonly maxNearMonsters: number; readonly dualWielding: boolean; @@ -76,6 +118,7 @@ declare global { finalBuild: Build; currentBuild: Build; + data: MyData; canTpToTown(): boolean; getMercEx(): MercUnit | null; @@ -96,6 +139,7 @@ declare global { clearBelt(): boolean; sortInventory(): boolean; cleanUpScrolls(tome: ItemUnit, scrollId: number): number; + update(): void; } interface Container { @@ -201,58 +245,22 @@ declare global { function switchCast(skillId: number, givenSettings: { hand?: number, x?: number, y?: number, switchBack?: boolean, oSkill?: boolean }): boolean; } - interface charData { - initialized: boolean; - normal: { - respecUsed: boolean; - imbueUsed: boolean; - socketUsed: boolean; - }; - nightmare: { - respecUsed: boolean; - imbueUsed: boolean; - socketUsed: boolean; - }; - hell: { - respecUsed: boolean; - imbueUsed: boolean; - socketUsed: boolean; - }; - me: { - task: string; - startTime: number; - charName: string; - classid: number; - level: number; - strength: number; - dexterity: number; - currentBuild: string; - finalBuild: string; - highestDifficulty: string; - setDifficulty: string; - charms: object; - charmGids: number[]; - }; - merc: { - act: number; - classid: number; - difficulty: number; - strength: number; - dexterity: number; - type: string; - gear: number[]; - }; - } - namespace CharData { const filePath: string; const threads: string[]; + namespace login { + function create(): any; + function getObj(): any; + function getStats(): any; + function updateData(arg: string, property: object | string, value: any): boolean; + } + // ignoring the sub objs for now function updateConfig(): void; - function create(): charData; - function getObj(): charData; - function getStats(): charData; + function create(): MyData; + function getObj(): MyData; + function getStats(): MyData; function updateData(arg: string, property: object | string, value: any): boolean; /** @alias CharData.delete */ function _delete(deleteMain: boolean): boolean; From 3c3656ec64c3b330f9c9f202ecbba282d3e25446 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Wed, 22 Mar 2023 19:28:19 -0400 Subject: [PATCH 089/263] Cleanup MercInsight.js - Want to do more with this but fixed the long pickit line --- .../BuildFiles/Runewords/MercInsight.js | 50 +++++++++++++------ .../BuildFiles/Runewords/SpiritSword.js | 3 +- 2 files changed, 36 insertions(+), 17 deletions(-) diff --git a/libs/SoloPlay/BuildFiles/Runewords/MercInsight.js b/libs/SoloPlay/BuildFiles/Runewords/MercInsight.js index ccc81ae0..9fa3346e 100644 --- a/libs/SoloPlay/BuildFiles/Runewords/MercInsight.js +++ b/libs/SoloPlay/BuildFiles/Runewords/MercInsight.js @@ -1,16 +1,24 @@ -(function() { - let highTier = "([name] == thresher || [name] == crypticaxe || [name] == greatpoleaxe || [name] == giantthresher)"; - let lowTier = "([name] == voulge || [name] == scythe || [name] == poleaxe || [name] == halberd || [name] == warscythe || [name] == bill || [name] == battlescythe || [name] == partizan || [name] == grimscythe)"; +(function () { + let low = [ + sdk.items.Voulge, sdk.items.Scythe, sdk.items.Poleaxe, + sdk.items.Halberd, sdk.items.WarScythe + ].map(el => "[name] == " + el).join(" || "); + + let mid = [ + sdk.items.Bill, sdk.items.BattleScythe, + sdk.items.Partizan, sdk.items.GrimScythe + ].map(el => "[name] == " + el).join(" || "); + + let high = [ + sdk.items.Thresher, sdk.items.CrypticAxe, + sdk.items.GreatPoleaxe, sdk.items.GiantThresher + ].map(el => "[name] == " + el).join(" || "); + const Insight = [ - (highTier + " && [flag] == ethereal && [quality] == normal # [sockets] == 0 # [maxquantity] == 1"), - "!me.hell && " + lowTier + " && [quality] >= normal && [quality] <= superior && [flag] != runeword # [sockets] == 4 # [maxquantity] == 1", - (highTier + " && [quality] >= normal && [quality] <= superior && [flag] != runeword # [sockets] == 4 # [maxquantity] == 1"), + (high + " && [flag] == ethereal && [quality] == normal # [sockets] == 0 # [maxquantity] == 1"), + (high + " && [quality] >= normal && [quality] <= superior && [flag] != runeword # [sockets] == 4 # [maxquantity] == 1"), + mid + " && [quality] >= normal && [quality] <= superior && [flag] != runeword # [sockets] == 4 # [maxquantity] == 1", ]; - NTIP.buildList(Insight); - - if (!me.hell && Item.getMercEquipped(sdk.body.RightArm).prefixnum !== sdk.locale.items.Insight && !Check.haveBase("polearm", 4)) { - NTIP.addLine("[name] == voulge && [flag] != ethereal && [quality] == normal && [level] >= 26 && [level] <= 40 # [sockets] == 0 # [maxquantity] == 1"); - } Config.Recipes.push([Recipe.Socket.Weapon, "giantthresher"]); Config.Recipes.push([Recipe.Socket.Weapon, "greatpoleaxe"]); @@ -25,11 +33,21 @@ Config.Runewords.push([Runeword.Insight, "partizan"]); Config.Runewords.push([Runeword.Insight, "battlescythe"]); Config.Runewords.push([Runeword.Insight, "bill"]); - Config.Runewords.push([Runeword.Insight, "Warscythe"]); - Config.Runewords.push([Runeword.Insight, "halberd"]); - Config.Runewords.push([Runeword.Insight, "poleaxe"]); - Config.Runewords.push([Runeword.Insight, "scythe"]); - Config.Runewords.push([Runeword.Insight, "voulge"]); + + if (!me.hell) { + Insight.push(low + " && [quality] >= normal && [quality] <= superior && [flag] != runeword # [sockets] == 4 # [maxquantity] == 1"); + Config.Runewords.push([Runeword.Insight, "Warscythe"]); + Config.Runewords.push([Runeword.Insight, "halberd"]); + Config.Runewords.push([Runeword.Insight, "poleaxe"]); + Config.Runewords.push([Runeword.Insight, "scythe"]); + Config.Runewords.push([Runeword.Insight, "voulge"]); + } + + NTIP.buildList(Insight); + + if (!me.hell && Item.getMercEquipped(sdk.body.RightArm).prefixnum !== sdk.locale.items.Insight && !Check.haveBase("polearm", 4)) { + NTIP.addLine("[name] == voulge && [flag] != ethereal && [quality] == normal && [level] >= 26 && [level] <= 40 # [sockets] == 0 # [maxquantity] == 1"); + } Config.KeepRunewords.push("[type] == polearm # [meditationaura] >= 12"); })(); diff --git a/libs/SoloPlay/BuildFiles/Runewords/SpiritSword.js b/libs/SoloPlay/BuildFiles/Runewords/SpiritSword.js index 973db829..3dee1dc0 100644 --- a/libs/SoloPlay/BuildFiles/Runewords/SpiritSword.js +++ b/libs/SoloPlay/BuildFiles/Runewords/SpiritSword.js @@ -30,7 +30,8 @@ if (!me.barbarian) { NTIP.addLine("([name] == broadsword || [name] == crystalsword) && [flag] != ethereal && [quality] >= normal && [quality] <= superior # [sockets] == 4 # [maxquantity] == 1"); } else { - // Have Thul and Amn before looking for base + !me.getItem(sdk.items.runes.Amn) && Config.Recipes.push([Recipe.Rune, "Thul Rune"]); + // Have Thul and Amn before looking for base if (me.getItem(sdk.items.runes.Thul) && me.getItem(sdk.items.runes.Amn)) { NTIP.addLine("([name] == broadsword || [name] == crystalsword) && [flag] != ethereal && [quality] >= normal && [quality] <= superior # [sockets] == 4 # [maxquantity] == 1"); } From da1e5bbb290094a0cce16e3b41066bc838c3d58c Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Wed, 22 Mar 2023 19:28:57 -0400 Subject: [PATCH 090/263] Update RunewordsOverrides.js - Don't make a runeword we are unable to use, this fixes making a runeword and tossing it or stashing to many --- libs/SoloPlay/Functions/RunewordsOverrides.js | 42 +++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/libs/SoloPlay/Functions/RunewordsOverrides.js b/libs/SoloPlay/Functions/RunewordsOverrides.js index 8ab42bc8..c7c7f07e 100644 --- a/libs/SoloPlay/Functions/RunewordsOverrides.js +++ b/libs/SoloPlay/Functions/RunewordsOverrides.js @@ -13,6 +13,48 @@ Runeword.PDiamondShield = Runeword.addRuneword( [sdk.items.type.AnyShield] ); +Runewords.checkRunewords = function () { + // keep a const reference of our items so failed checks don't remove items from the list + const itemsRef = me.findItems(-1, sdk.items.mode.inStorage); + + for (let i = 0; i < Config.Runewords.length; i += 1) { + let itemList = []; // reset item list + let items = itemsRef.slice(); // copy itemsRef + + const [runeword, wantedBase, ethFlag] = Config.Runewords[i]; + if (runeword.reqLvl > me.charlvl) continue; // skip runeword if we don't meet the level requirement + let base = this.getBase(runeword, wantedBase, (ethFlag || 0)); // check base + + if (base) { + itemList.push(base); // push the base + + for (let j = 0; j < runeword.runes.length; j += 1) { + for (let k = 0; k < items.length; k += 1) { + if (items[k].classid === runeword.runes[j]) { // rune matched + itemList.push(items[k]); // push into the item list + items.splice(k, 1); // remove from item list as to not count it twice + + k -= 1; + + break; // stop item cycle - we found the item + } + } + + // can't complete runeword - go to next one + if (itemList.length !== j + 2) { + break; + } + + if (itemList.length === runeword.runes.length + 1) { // runes + base + return itemList; // these items are our runeword + } + } + } + } + + return false; +}; + /** * Get the base item based on classid and runeword recipe * @param {runeword} runeword From 63349c8a01de0ecf8e7bb57cd63ef05a00f79a56 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Thu, 23 Mar 2023 13:15:01 -0400 Subject: [PATCH 091/263] Clean up threads - Remove display logic from the entry script, add it to a background worker in toolsthread that now also handles the overlay - Remove unused threads AutoBuild/Event/TownChicken --- D2BotSoloPlay.dbj | 49 +-- libs/SoloPlay/Threads/AutoBuildThread.js | 284 -------------- libs/SoloPlay/Threads/EventThread.js | 255 ------------ libs/SoloPlay/Threads/ToolsThread.js | 95 ++++- libs/SoloPlay/Threads/TownChicken.js | 470 ----------------------- 5 files changed, 80 insertions(+), 1073 deletions(-) delete mode 100644 libs/SoloPlay/Threads/AutoBuildThread.js delete mode 100644 libs/SoloPlay/Threads/EventThread.js delete mode 100644 libs/SoloPlay/Threads/TownChicken.js diff --git a/D2BotSoloPlay.dbj b/D2BotSoloPlay.dbj index 0d29bdb1..5c53d109 100644 --- a/D2BotSoloPlay.dbj +++ b/D2BotSoloPlay.dbj @@ -35,9 +35,6 @@ Starter.Config.GlobalAccountPassword = ""; // Set value for a global password fo // the only things we really need from these are their oog checks includeSystemLibs(); -// what do I need from here? -const { getAreaName, Time } = require("./libs/core/Util"); -include("core/experience.js"); // solo specific include("SoloPlay/Tools/Developer.js"); include("SoloPlay/Tools/CharData.js"); @@ -54,9 +51,6 @@ if (typeof Starter.AdvancedConfig[me.profile] === "object") { delete Starter.AdvancedConfig; } -let joinInfo; -let gameTracker; - // initialize data files if (!FileTools.exists("data/" + me.profile + ".json") && DataFile.create()) { Starter.firstRun = true; @@ -73,23 +67,6 @@ if (!FileTools.exists(CharData.login.filePath) && CharData.login.create()) { Developer.logPerformance && Tracker.initialize(); -function timer (tick) { - const currInGame = (getTickCount() - tick); - let timeStr = " (Time: " + Time.format(currInGame) + ") "; - - if (Developer.displayClockInConsole && Developer.logPerformance) { - try { - gameTracker === undefined && (gameTracker = Tracker.readObj(Tracker.GTPath)); - let [tTime, tInGame, tDays] = [(gameTracker.Total + currInGame), (gameTracker.InGame + currInGame), (gameTracker.Total + currInGame)]; - let [totalTime, totalInGame, totalDays] = [Tracker.formatTime(tTime), Tracker.formatTime(tInGame), Tracker.totalDays(tDays)]; - timeStr += ("(Days: " + totalDays + ") (Total: " + totalTime + ") (IG: " + totalInGame + ") (OOG: " + Tracker.formatTime(gameTracker.OOG) + ")"); - } catch (e) { - console.log(e); - } - } - return timeStr; -} - function main () { debugLog(me.profile); addEventListener("copydata", Starter.receiveCopyData); @@ -140,14 +117,6 @@ function main () { delay(50); } - let resPenalty, areaName, diffName; - const xp = () => me.getStat(sdk.stats.Experience) > 0 ? Experience.progress() : 0; - const tGold = () => me.getStat(sdk.stats.Gold) + me.getStat(sdk.stats.GoldBank); - const fireRes = (resPenalty) => Math.min(75 + me.getStat(sdk.stats.MaxFireResist), me.getStat(sdk.stats.FireResist) - resPenalty); - const coldRes = (resPenalty) => Math.min(75 + me.getStat(sdk.stats.MaxColdResist), me.getStat(sdk.stats.ColdResist) - resPenalty); - const lightRes = (resPenalty) => Math.min(75 + me.getStat(sdk.stats.MaxLightResist), me.getStat(sdk.stats.LightResist) - resPenalty); - const poisRes = (resPenalty) => Math.min(75 + me.getStat(sdk.stats.MaxPoisonResist), me.getStat(sdk.stats.PoisonResist) - resPenalty); - while (true) { // returns true before actually in game so we can't only use this check while (me.ingame) { @@ -159,27 +128,11 @@ function main () { Starter.gameStart = getTickCount(); Starter.lastGameStatus = "ingame"; Starter.inGame = true; - resPenalty = me.gametype === 0 ? [0, 20, 50][me.diff] : [0, 40, 100][me.diff]; - diffName = ["Norm", "Night", "Hell"][me.diff]; DataFile.updateStats("runs", Starter.gameCount); DataFile.updateStats("ingameTick"); Developer.logPerformance && Tracker.update((getTickCount() - oogTick)); - Developer.displayClockInConsole && (gameTracker = Tracker.readObj(Tracker.GTPath)); oogTick = 0; - } - - if (me.ingame && me.gameReady) { - let statusString = ""; - - try { - let [exp, myGold, fr, cr, lr, pr] = [xp(), tGold(), fireRes(resPenalty), coldRes(resPenalty), lightRes(resPenalty), poisRes(resPenalty)]; - areaName = !!me.area ? getAreaName(me.area) : ""; - statusString = me.name + " | Lvl: " + me.charlvl + " (" + exp + "%) (Diff: " + diffName + ") (A: " + areaName + ") (G: " + myGold + ") (F: " + fr + "/C: " + cr + "/L: " + lr + "/P: " + pr + ")"; - } catch (e) { - console.error(e); - } - - D2Bot.updateStatus(statusString + timer(Starter.gameStart)); + D2Bot.updateStatus("In-Game :: Initializing threads..."); } } diff --git a/libs/SoloPlay/Threads/AutoBuildThread.js b/libs/SoloPlay/Threads/AutoBuildThread.js deleted file mode 100644 index 5efe2694..00000000 --- a/libs/SoloPlay/Threads/AutoBuildThread.js +++ /dev/null @@ -1,284 +0,0 @@ -/** -* @filename AutoBuildThread.js -* @author alogwe, theBGuy -* @desc modified AutoBuildThread for use with Kolbot-SoloPlay -* -*/ -js_strict(true); -include("critical.js"); - -// globals needed for core gameplay -includeCoreLibs({ exclude: ["Storage.js"] }); -// needed for this thread -include("core/Auto/AutoSkill.js"); -include("core/Auto/AutoStat.js"); - -// system libs -includeSystemLibs(); -include("systems/mulelogger/MuleLogger.js"); - -// Include SoloPlay's librarys -include("SoloPlay/Tools/Developer.js"); -include("SoloPlay/Tools/Tracker.js"); -include("SoloPlay/Tools/CharData.js"); -include("SoloPlay/Tools/SoloIndex.js"); -include("SoloPlay/Functions/ConfigOverrides.js"); -include("SoloPlay/Functions/Globals.js"); - -SetUp.include(); -Config.init(); // includes libs/SoloPlay/Functions/AutoBuildOverrides.js - -let debug = !!Config.AutoBuild.DebugMode, prevLevel = me.charlvl; -const usingFinalBuild = !["Start", "Stepping", "Leveling"].includes(Config.AutoBuild.Template); -const SPEND_POINTS = true; // For testing, it actually allows skill and stat point spending. -const STAT_ID_TO_NAME = [ - getLocaleString(sdk.locale.text.Strength), - getLocaleString(sdk.locale.text.Energy), - getLocaleString(sdk.locale.text.Dexterity), - getLocaleString(sdk.locale.text.Vitality) -]; -let currAutoBuild; - -// Will check if value exists in an Array -Array.prototype.contains = (val) => this.indexOf(val) > -1; - -function skillInValidRange (id) { - switch (me.classid) { - case sdk.player.class.Amazon: - return sdk.skills.MagicArrow <= id && id <= sdk.skills.LightningFury; - case sdk.player.class.Sorceress: - return sdk.skills.FireBolt <= id && id <= sdk.skills.ColdMastery; - case sdk.player.class.Necromancer: - return sdk.skills.AmplifyDamage <= id && id <= sdk.skills.Revive; - case sdk.player.class.Paladin: - return sdk.skills.Sacrifice <= id && id <= sdk.skills.Salvation; - case sdk.player.class.Barbarian: - return sdk.skills.Bash <= id && id <= sdk.skills.BattleCommand; - case sdk.player.class.Druid: - return sdk.skills.Raven <= id && id <= sdk.skills.Hurricane; - case sdk.player.class.Assassin: - return sdk.skills.FireBlast <= id && id <= sdk.skills.PhoenixStrike; - default: - return false; - } -} - -const gainedLevels = () => me.charlvl - prevLevel; - -function canSpendPoints () { - let unusedStatPoints = me.getStat(sdk.stats.StatPts); - let haveUnusedStatpoints = unusedStatPoints >= 5; // We spend 5 stat points per level up - let unusedSkillPoints = me.getStat(sdk.stats.NewSkills); - let haveUnusedSkillpoints = unusedSkillPoints >= 1; // We spend 1 skill point per level up - debug && AutoBuild.print("Stat points:", unusedStatPoints, " Skill points:", unusedSkillPoints); - return haveUnusedStatpoints && haveUnusedSkillpoints; -} - -function spendStatPoint (id) { - let unusedStatPoints = me.getStat(sdk.stats.StatPts); - if (SPEND_POINTS) { - useStatPoint(id); - AutoBuild.print("useStatPoint(" + id + "): " + STAT_ID_TO_NAME[id]); - } else { - AutoBuild.print("Fake useStatPoint(" + id + "): " + STAT_ID_TO_NAME[id]); - } - delay(100); // TODO: How long should we wait... if at all? - return (unusedStatPoints - me.getStat(sdk.stats.StatPts) === 1); // Check if we spent one point -} - -// TODO: What do we do if it fails? report/ignore/continue? -function spendStatPoints () { - if (currAutoBuild[me.charlvl] === undefined) return true; - let stats = currAutoBuild[me.charlvl].StatPoints; - let errorMessage = "\nInvalid stat point set in build template " + getTemplateFilename() + " at level " + me.charlvl; - let spentEveryPoint = true; - let unusedStatPoints = me.getStat(sdk.stats.StatPts); - let len = stats.length; - - if (Config.AutoStat.Enabled) { - return spentEveryPoint; - } - - if (len > unusedStatPoints) { - len = unusedStatPoints; - AutoBuild.print("Warning: Number of stats specified in your build template at level " + me.charlvl + " exceeds the available unused stat points" + - "\nOnly the first " + len + " stats " + stats.slice(0, len).join(", ") + " will be added"); - } - - // We silently ignore stats set to -1 - for (let i = 0; i < len; i++) { - let id = stats[i]; - let statIsValid = (typeof id === "number") && (0 <= id && id <= 3); - - if (id === -1) { - continue; - } else if (statIsValid) { - let preStatValue = me.getStat(id); - let pointSpent = spendStatPoint(id); - if (SPEND_POINTS) { - if (!pointSpent) { - spentEveryPoint = false; - AutoBuild.print("Attempt to spend point " + (i + 1) + " in " + STAT_ID_TO_NAME[id] + " may have failed!"); - } else if (debug) { - AutoBuild.print("Stat (" + (i + 1) + "/" + len + ") Increased " + STAT_ID_TO_NAME[id] + " from " + preStatValue + " to " + me.getStat(id)); - } - } - } else { - throw new Error("Stat id must be one of the following:\n0:" + STAT_ID_TO_NAME[0] + - ",\t1:" + STAT_ID_TO_NAME[1] + ",\t2:" + STAT_ID_TO_NAME[2] + ",\t3:" + STAT_ID_TO_NAME[3] + errorMessage); - } - } - - return spentEveryPoint; -} - -function getTemplateFilename () { - let buildType = Config.AutoBuild.Template; - let className = sdk.player.class.nameOf(me.classid); - let templateFilename = "SoloPlay/BuildFiles/" + className + "/" + className + "." + buildType + "Build.js"; - return templateFilename; -} - -function getRequiredSkills (id) { - function searchSkillTree (id) { - let results = []; - let skillTreeRight = getBaseStat("skills", id, sdk.stats.PreviousSkillRight); - let skillTreeMiddle = getBaseStat("skills", id, sdk.stats.PreviousSkillMiddle); - let skillTreeLeft = getBaseStat("skills", id, sdk.stats.PreviousSkillLeft); - - results.push(skillTreeRight); - results.push(skillTreeMiddle); - results.push(skillTreeLeft); - - for (let i = 0; i < results.length; i++) { - let skill = results[i]; - let skillInValidRange = (sdk.skills.Attack < skill && skill <= sdk.skills.PhoenixStrike) && (![sdk.skills.IdentifyScroll, sdk.skills.BookofIdentify, sdk.skills.TownPortalScroll, sdk.skills.BookofTownPortal].contains(skill)); - let hardPointsInSkill = me.getSkill(skill, sdk.skills.subindex.HardPoints); - - if (skillInValidRange && !hardPointsInSkill) { - requirements.push(skill); - searchSkillTree(skill); // search children; - } - } - } - - let requirements = []; - searchSkillTree(id); - const increasing = () => a - b; - return requirements.sort(increasing); -} - -function spendSkillPoint (id) { - let unusedSkillPoints = me.getStat(sdk.stats.NewSkills); - let skillName = getSkillById(id) + " (" + id + ")"; - if (SPEND_POINTS) { - useSkillPoint(id); - AutoBuild.print("useSkillPoint(): " + skillName); - } else { - AutoBuild.print("Fake useSkillPoint(): " + skillName); - } - delay(200); // TODO: How long should we wait... if at all? - return (unusedSkillPoints - me.getStat(sdk.stats.NewSkills) === 1); // Check if we spent one point -} - -function spendSkillPoints () { - if (currAutoBuild[me.charlvl] === undefined) return true; - let skills = currAutoBuild[me.charlvl].SkillPoints; - let errInvalidSkill = "\nInvalid skill point set in build template " + getTemplateFilename() + " for level " + me.charlvl; - let spentEveryPoint = true; - let unusedSkillPoints = me.getStat(sdk.stats.NewSkills); - let len = skills.length; - - if (Config.AutoSkill.Enabled) { - return spentEveryPoint; - } - - if (len > unusedSkillPoints) { - len = unusedSkillPoints; - AutoBuild.print("Warning: Number of skills specified in your build template at level " + me.charlvl + " exceeds the available unused skill points" + - "\nOnly the first " + len + " skills " + skills.slice(0, len).join(", ") + " will be added"); - } - - // We silently ignore skills set to -1 - for (let i = 0; i < len; i++) { - let id = skills[i]; - - if (id === -1) { - continue; - } else if (!skillInValidRange(id)) { - throw new Error("Skill id " + id + " is not a skill for your character class" + errInvalidSkill); - } - - let skillName = getSkillById(id) + " (" + id + ")"; - let requiredSkills = getRequiredSkills(id); - if (requiredSkills.length > 0) { - throw new Error("You need prerequisite skills " + requiredSkills.join(", ") + " before adding " + skillName + errInvalidSkill); - } - - let requiredLevel = getBaseStat("skills", id, sdk.stats.MinimumRequiredLevel); - if (me.charlvl < requiredLevel) { - throw new Error("You need to be at least level " + requiredLevel + " before you get " + skillName + errInvalidSkill); - } - - let pointSpent = spendSkillPoint(id); - - if (SPEND_POINTS) { - if (!pointSpent) { - spentEveryPoint = false; - AutoBuild.print("Attempt to spend skill point " + (i + 1) + " in " + skillName + " may have failed!"); - } else if (debug) { - let actualSkillLevel = me.getSkill(id, sdk.skills.subindex.SoftPoints); - AutoBuild.print("Skill (" + (i + 1) + "/" + len + ") Increased " + skillName + " by one (level: ", actualSkillLevel + ")"); - } - } - - delay(200); // TODO: How long should we wait... if at all? - } - - return spentEveryPoint; -} - -/* -* TODO: determine if changes need to be made for -* the case of gaining multiple levels at once so as -* not to bombard the d2bs event system -*/ - -function main () { - try { - AutoBuild.print("Loaded helper thread"); - console.log("ÿc8Kolbot-SoloPlayÿc0: Start AutoBuildThread"); - currAutoBuild = usingFinalBuild ? finalBuild.AutoBuildTemplate : build.AutoBuildTemplate; - - while (true) { - let levels = gainedLevels(); - - if (levels > 0 && (canSpendPoints() || Config.AutoSkill.Enabled || Config.AutoStat.Enabled)) { - scriptBroadcast("toggleQuitlist"); - AutoBuild.print("Level up detected (", prevLevel, "-->", me.charlvl, ")"); - spendSkillPoints(); - spendStatPoints(); - Config.AutoSkill.Enabled && AutoSkill.init(Config.AutoSkill.Build, Config.AutoSkill.Save); - Config.AutoStat.Enabled && AutoStat.init(Config.AutoStat.Build, Config.AutoStat.Save, Config.AutoStat.BlockChance, Config.AutoStat.UseBulk); - scriptBroadcast({ event: "level up" }); - AutoBuild.applyConfigUpdates(); // scriptBroadcast() won't trigger listener on this thread. - - debug && AutoBuild.print("Incrementing cached character level to", prevLevel + 1); - - // prevLevel doesn't get set to me.charlvl because - // we may have gained multiple levels at once - prevLevel += 1; - - scriptBroadcast("toggleQuitlist"); - } - - delay(1e3); - } - } catch (err) { - print("Something broke!"); - print("Error:" + err.toSource()); - print("Stack trace: \n" + err.stack); - - return; - } -} diff --git a/libs/SoloPlay/Threads/EventThread.js b/libs/SoloPlay/Threads/EventThread.js deleted file mode 100644 index a0f22453..00000000 --- a/libs/SoloPlay/Threads/EventThread.js +++ /dev/null @@ -1,255 +0,0 @@ -/** -* @filename EventThread.js -* @author theBGuy -* @desc thread to handle in game events for Kolbot-SoloPlay -* -*/ -js_strict(true); -include("critical.js"); - -// globals needed for core gameplay -includeCoreLibs({ exclude: ["Storage.js"] }); - -// system libs -includeSystemLibs(); -include("systems/mulelogger/MuleLogger.js"); - -// Include SoloPlay's librarys -include("SoloPlay/Tools/Developer.js"); -include("SoloPlay/Tools/Tracker.js"); -include("SoloPlay/Tools/CharData.js"); -include("SoloPlay/Tools/SoloIndex.js"); -include("SoloPlay/Functions/ConfigOverrides.js"); -include("SoloPlay/Functions/Globals.js"); - -SetUp.include(); - -function main () { - let tickDelay = 0; - let townChicken = false; - let [action, profiles] = [[], []]; - const threads = ["libs/SoloPlay/SoloPlay.js", "libs/SoloPlay/Threads/TownChicken.js", "threads/antihostile.js", "threads/party.js"]; - - console.log("ÿc8Kolbot-SoloPlayÿc0: Start EventThread"); - D2Bot.init(); - Config.init(false); - Pickit.init(false); - Attack.init(); - Storage.Init(); - CraftingSystem.buildLists(); - Runewords.init(); - Cubing.init(); - - this.pauseForEvent = function () { - for (let i = 0; i < threads.length; i += 1) { - let script = getScript(threads[i]); - - if (townChicken) { - console.warn("ÿc8EventThread :: ÿc1Trying to townChicken, don't interfere."); - return false; - } - - if (script) { - if (script.running) { - threads[i] === "libs/SoloPlay/SoloPlay.js" && console.log("ÿc8EventThread :: ÿc1Pausing " + threads[i]); - threads[i] === "libs/SoloPlay/Threads/TownChicken.js" && !SoloEvents.cloneWalked && console.log("ÿc8EventThread :: ÿc1Pausing " + threads[i]); - - // don't pause townchicken during clone walk - if (threads[i] !== "libs/SoloPlay/Threads/TownChicken.js" || !SoloEvents.cloneWalked) { - script.pause(); - } - } - } - } - - return true; - }; - - this.resumeThreadsAfterEvent = function () { - for (let i = 0; i < threads.length; i += 1) { - let script = getScript(threads[i]); - - if (script) { - if (!script.running) { - threads[i] === "libs/SoloPlay/SoloPlay.js" && console.log("ÿc8EventThread :: ÿc2Resuming " + threads[i]); - threads[i] === "libs/SoloPlay/Threads/TownChicken.js" && console.log("ÿc8EventThread :: ÿc2Resuming " + threads[i]); - - script.resume(); - } - } - } - - return true; - }; - - this.scriptEvent = function (msg) { - let obj; - - if (msg && typeof msg === "string" && msg !== "") { - switch (msg) { - case "townchickenOn": - townChicken = true; - - break; - case "townchickenOff": - townChicken = false; - - break; - case "testing": - case "finishDen": - case "dodge": - case "skip": - case "killdclone": - action.push(msg); - - break; - case "addDiaEvent": - console.log("Added dia lightning listener"); - addEventListener("gamepacket", SoloEvents.diaEvent); - - break; - case "removeDiaEvent": - console.log("Removed dia lightning listener"); - removeEventListener("gamepacket", SoloEvents.diaEvent); - - break; - case "addBaalEvent": - console.log("Added baal wave listener"); - addEventListener("gamepacket", SoloEvents.baalEvent); - - break; - case "removeBaalEvent": - console.log("Removed baal wave listener"); - removeEventListener("gamepacket", SoloEvents.baalEvent); - - break; - // ignore common scriptBroadcast messages that aren't relevent to this thread - case "mule": - case "muleTorch": - case "muleAnni": - case "torch": - case "crafting": - case "getMuleMode": - case "pingquit": - return; - } - - switch (true) { - case msg.substring(0, 8) === "config--": - console.debug("update config"); - Config = JSON.parse(msg.split("config--")[1]); - - break; - case msg.substring(0, 7) === "skill--": - console.debug("update skillData"); - obj = JSON.parse(msg.split("skill--")[1]); - Misc.updateRecursively(CharData.skillData, obj); - - break; - case msg.substring(0, 6) === "data--": - console.debug("update me.data"); - obj = JSON.parse(msg.split("data--")[1]); - Misc.updateRecursively(me.data, obj); - - break; - case msg.toLowerCase() === "test": - console.debug(sdk.colors.Green + "//-----------DataDump Start-----------//\nÿc8MainData ::\n", - me.data, "\nÿc8BuffData ::\n", CharData.pots, "\nÿc8SkillData ::\n", CharData.skillData, "\n" + sdk.colors.Red + "//-----------DataDump End-----------//"); - - break; - } - } - }; - - this.receiveCopyData = function (id, info) { - // Torch - if (id === 55) { - let { profile, ladder, torchType } = JSON.parse(info); - console.log("Mesage recived for torch...processing"); - - if (profile !== me.profile && (me.hell || (me.nightmare && me.baal)) && me.ladder === ladder) { - if (torchType === me.classid && !me.findItem(604, 0, null, 7)) { - console.log("Sent Response"); - SoloEvents.sendToProfile(profile, { profile: me.profile, level: me.charlvl, event: 604 }); - } - } - - return; - } - - // Annhilus - if (id === 60) { - let { profile, ladder } = JSON.parse(info); - console.log("Mesage recived for Annhilus...processing"); - - if (profile !== me.profile && (me.hell || (me.nightmare && me.baal)) && me.ladder === ladder) { - if (!me.findItem(603, 0, null, 7)) { - console.log("Sent Response"); - SoloEvents.sendToProfile(profile, { profile: me.profile, level: me.charlvl, event: 603 }); - } - } - - return; - } - - if (id === 65) { - let { profile, level, event } = JSON.parse(info); - - console.log("Sucess: profile that contacted me: " + profile + " level of char: " + level); - SoloEvents.profileResponded = true; - profiles.push({ profile: profile, level: level, event: event }); - tickDelay += 1000; - } - - if (id === 70) { - Messaging.sendToScript("D2BotSoloPlay.dbj", "event"); - delay(100 + me.ping); - scriptBroadcast("quit"); - } - }; - - addEventListener("scriptmsg", this.scriptEvent); - addEventListener("copydata", this.receiveCopyData); // should this just be added to the starter? would remove needing 3 copydata event listeners (entry, default, and here) - - // Start - // test for getUnit bug - let test = Game.getMonster(); - test === null && console.warn("getUnit is bugged"); - - while (true) { - try { - while (action.length) { - if (this.pauseForEvent()) { - try { - SoloEvents[action.shift()](); - } catch (e) { - console.log(e); - } - - this.resumeThreadsAfterEvent(); - } else { - action.shift(); - } - } - - if (profiles.length > 0) { - let tick = getTickCount(); - - while (getTickCount() - tick < tickDelay) { - delay(500); - } - - let lowestLevelProf = profiles.sort((a, b) => a.level - b.level).first(); - - SoloEvents.sendToProfile(lowestLevelProf.profile, lowestLevelProf.event, 70); - D2Bot.joinMe(lowestLevelProf.profile, me.gamename.toLowerCase(), "", me.gamepassword.toLowerCase(), true); - profiles = []; - } - } catch (e) { - D2Bot.printToConsole(JSON.stringify(e)); - console.log(e); - } - - delay(20); - } -} diff --git a/libs/SoloPlay/Threads/ToolsThread.js b/libs/SoloPlay/Threads/ToolsThread.js index 0e3b04a9..f991b831 100644 --- a/libs/SoloPlay/Threads/ToolsThread.js +++ b/libs/SoloPlay/Threads/ToolsThread.js @@ -566,7 +566,7 @@ function main () { if (obj) { obj.hasOwnProperty("currScript") && (debugInfo.currScript = obj.currScript); obj.hasOwnProperty("lastAction") && (debugInfo.lastAction = obj.lastAction); - //D2Bot.store(JSON.stringify(debugInfo)); + // D2Bot.store(JSON.stringify(debugInfo)); DataFile.updateStats("debugInfo", JSON.stringify(debugInfo)); } @@ -578,6 +578,9 @@ function main () { Config = copyObj(Config); tick = getTickCount(); + // getUnit test + getUnit(-1) === null && console.warn("getUnit bug detected"); + addEventListener("keyup", keyEvent); addEventListener("gameevent", gameEvent); addEventListener("scriptmsg", scriptEvent); @@ -591,10 +594,82 @@ function main () { console.warn("Without logPerformance set, the overlay will only show partial values"); } - // getUnit test - getUnit(-1) === null && console.warn("getUnit bug detected"); + const Worker = require("../../modules/Worker"); + const diffShort = ["Norm", "Night", "Hell"][me.diff]; + + // Start worker - handles overlay and d2bot# profile display + Worker.runInBackground.display = (new function () { + let _timeout = 0; + let gameTracker; + + function timer () { + const currInGame = (getTickCount() - me.gamestarttime); + let timeStr = " (Time: " + Time.format(currInGame) + ") "; + + if (Developer.displayClockInConsole && Developer.logPerformance) { + try { + gameTracker === undefined && (gameTracker = Tracker.readObj(Tracker.GTPath)); + let [tTime, tInGame, tDays] = [ + (gameTracker.Total + currInGame), + (gameTracker.InGame + currInGame), + (gameTracker.Total + currInGame) + ]; + let [totalTime, totalInGame, totalDays] = [ + Tracker.formatTime(tTime), + Tracker.formatTime(tInGame), + Tracker.totalDays(tDays) + ]; + timeStr += ("(Days: " + totalDays + ") (Total: " + totalTime + ") (IG: " + totalInGame + ") (OOG: " + Tracker.formatTime(gameTracker.OOG) + ")"); + } catch (e) { + console.log(e); + } + } + return timeStr; + } + + this.run = () => { + if (getTickCount() - _timeout < 500) return true; + _timeout = getTickCount(); + + if (me.gameReady) { + // handle d2bot# profile display + let statusString = ""; + + try { + statusString = [ + (me.name + " | "), + ("Lvl: " + me.charlvl), + (" (" + Experience.progress() + "%) "), + ("(Diff: " + diffShort + ") "), + ("(A: " + getAreaName(me.area) + ") "), + ("(G: " + me.gold + ") "), + ("(F: " + me.FR + "/C: " + me.CR + "/L: " + me.LR + "/P: " + me.PR + ")"), + ].join(""); + + D2Bot.updateStatus(statusString + timer()); + } catch (e) { + console.error(e); + } + + // handle overlay + if (Developer.overlay) { + if (me.ingame && me.gameReady && me.area) { + Overlay.update(quitFlag); + + if (me.act !== myAct) { + Overlay.flush(); + myAct = me.act; + Overlay.update(quitFlag); + } + } + } + } + + return true; + }; + }).run; - // Start + // Start ToolsThread while (true) { try { if (me.gameReady && !me.inTown) { @@ -682,18 +757,6 @@ function main () { quitFlag = true; } - // should overlay be moved to be a background worker? - if (Developer.overlay) { - if (me.ingame && me.gameReady && me.area) { - Overlay.update(quitFlag); - - if (me.act !== myAct) { - Overlay.flush(); - myAct = me.act; - } - } - } - if (quitFlag && canQuit && (typeof quitListDelayTime === "undefined" || getTickCount() >= quitListDelayTime)) { Common.Toolsthread.checkPing(false); // In case of quitlist triggering first this.exit(); diff --git a/libs/SoloPlay/Threads/TownChicken.js b/libs/SoloPlay/Threads/TownChicken.js deleted file mode 100644 index c0ea681d..00000000 --- a/libs/SoloPlay/Threads/TownChicken.js +++ /dev/null @@ -1,470 +0,0 @@ -/** -* @filename TownChicken.js -* @author kolton, theBGuy -* @desc modified TownChicken for use with Kolbot-SoloPlay -* -*/ -js_strict(true); -include("critical.js"); - -// globals needed for core gameplay -includeCoreLibs({ exclude: ["Storage.js"] }); - -// system libs -includeSystemLibs(); -include("systems/mulelogger/MuleLogger.js"); - -// Include SoloPlay's librarys -include("SoloPlay/Tools/Developer.js"); -include("SoloPlay/Tools/Tracker.js"); -include("SoloPlay/Tools/CharData.js"); -include("SoloPlay/Tools/SoloIndex.js"); -include("SoloPlay/Functions/ConfigOverrides.js"); -include("SoloPlay/Functions/Globals.js"); - -SetUp.include(); - -function main() { - let [townCheck, fastTown] = [false, false]; - console.log("ÿc8Kolbot-SoloPlayÿc0: Start TownChicken thread"); - - // Init config and attacks - D2Bot.init(); - Config.init(); - Pickit.init(); - Attack.init(); - Storage.Init(); - CraftingSystem.buildLists(); - Runewords.init(); - Cubing.init(); - - new Overrides.Override(Attack, Attack.getNearestMonster, function (orignal, givenSettings = {}) { - const settings = Object.assign({ - skipBlocked: false, - skipImmune: false - }, givenSettings); - let monster = orignal(settings); - - if (monster) { - console.log("ÿc9TownChickenÿc0 :: Closest monster to me: " + monster.name + " | Monster classid: " + monster.classid); - return monster.classid; - } - - return -1; - }).apply(); - - NodeAction.go = function () { - return; - }; - - Pather.usePortal = function (targetArea, owner, unit, dummy) { - if (targetArea && me.area === targetArea) return true; - - me.cancelUIFlags(); - - const townAreaCheck = (area = 0) => sdk.areas.Towns.includes(area); - const preArea = me.area; - const leavingTown = townAreaCheck(preArea); - - for (let i = 0; i < 13; i += 1) { - if (me.dead) return false; - if (targetArea ? me.inArea(targetArea) : me.area !== preArea) return true; - - (i > 0 && owner && me.inTown) && Town.move("portalspot"); - - let portal = unit ? copyUnit(unit) : Pather.getPortal(targetArea, owner); - - if (portal && portal.area === me.area) { - const useTk = me.inTown && Skill.useTK(portal) && i < 3; - if (useTk) { - portal.distance > 21 && (me.inTown && me.act === 5 ? Town.move("portalspot") : Pather.moveNearUnit(portal, 20)); - if (Skill.cast(sdk.skills.Telekinesis, sdk.skills.hand.Right, portal) - && Misc.poll(() => targetArea ? me.inArea(targetArea) : me.area !== preArea)) { - Pather.lastPortalTick = getTickCount(); - delay(100); - - return true; - } - } else { - portal.distance > 5 && (i < 3 ? Pather.moveNearUnit(portal, 4, false) : Pather.moveToUnit(portal)); - - if (getTickCount() - this.lastPortalTick > (leavingTown ? 2500 : 1000)) { - i < 2 ? Packet.entityInteract(portal) : Misc.click(0, 0, portal); - } else { - // only delay if we are in town and leaving town, don't delay if we are attempting to portal from out of town since this is the chicken thread - // and we are likely being attacked - leavingTown && delay(300); - - continue; - } - } - - let tick = getTickCount(); - - while (getTickCount() - tick < 500) { - if (me.area !== preArea) { - this.lastPortalTick = getTickCount(); - delay(100); - - return true; - } - - delay(10); - } - // try clicking dummy portal - !!dummy && portal.area === 1 && Misc.click(0, 0, portal); - - i > 1 && (i % 3) === 0 && Packet.flash(me.gid); - } else { - console.log("Didn't find portal, retry: " + i); - i > 3 && me.inTown && Town.move("portalspot", false); - if (i === 12) { - let p = Game.getObject("portal"); - console.debug(p); - if (!!p && Misc.click(0, 0, p) && Misc.poll(() => me.area !== preArea, 1000, 100)) { - this.lastPortalTick = getTickCount(); - delay(100); - - return true; - } - } - Packet.flash(me.gid); - } - - delay(250); - } - - return (targetArea ? me.inArea(targetArea) : me.area !== preArea); - }; - - Pather.makePortal = function (use = false) { - if (me.inTown) return true; - - let oldGid = -1; - - for (let i = 0; i < 5; i += 1) { - if (me.dead) return false; - - let tpTool = me.getTpTool(); - if (!tpTool) return false; - - let oldPortal = Game.getObject(sdk.objects.BluePortal); - if (oldPortal) { - do { - if (oldPortal.getParent() === me.name) { - oldGid = oldPortal.gid; - break; - } - } while (oldPortal.getNext()); - - // old portal is close to use, we should try to use it - if (oldPortal.getParent() === me.name && oldPortal.distance < 4) { - if (use) { - if (this.usePortal(null, null, copyUnit(oldPortal))) return true; - break; // don't spam usePortal - } else { - return copyUnit(oldPortal); - } - } - } - - let pingDelay = me.getPingDelay(); - - if (tpTool.use() || Game.getObject("portal")) { - let tick = getTickCount(); - - while (getTickCount() - tick < Math.max(500 + i * 100, pingDelay * 2 + 100)) { - const portal = getUnits(sdk.unittype.Object, "portal") - .filter((p) => p.getParent() === me.name && p.gid !== oldGid).first(); - - if (portal) { - if (use) { - if (this.usePortal(null, null, copyUnit(portal))) return true; - break; // don't spam usePortal - } else { - return copyUnit(portal); - } - } else { - // check dummy - let dummy = getUnits(sdk.unittype.Object, "portal").filter(p => p.name === "Dummy").first(); - if (dummy) { - console.debug(dummy); - if (use) return Pather.usePortal(null, null, dummy, true); - return copyUnit(dummy); - } - } - - delay(10); - } - } else { - console.log("Failed to use tp tool"); - Packet.flash(me.gid, pingDelay); - delay(200 + pingDelay); - } - - delay(40); - } - - return false; - }; - - Town.goToTown = function (act = 0, wpmenu = false) { - if (!me.inTown) { - const townArea = sdk.areas.townOf(me.act); - try { - !Pather.makePortal(true) && console.warn("Town.goToTown: Failed to make TP"); - if (!me.inTown && !Pather.usePortal(townArea, me.name)) { - console.warn("Town.goToTown: Failed to take TP"); - if (!me.inTown && !Pather.usePortal(sdk.areas.townOf(me.area))) throw new Error("Town.goToTown: Failed to take TP"); - } - } catch (e) { - let tpTool = me.getTpTool(); - if (!tpTool && Misc.getPlayerCount() <= 1) { - Misc.errorReport(new Error("Town.goToTown: Failed to go to town and no tps available. Restart.")); - scriptBroadcast("quit"); - } else { - if (!Misc.poll(() => { - if (me.inTown) return true; - let p = Game.getObject("portal"); - console.debug(p); - !!p && Misc.click(0, 0, p) && delay(100); - Misc.poll(() => me.idle, 1000, 100); - console.debug("inTown? " + me.inTown); - return me.inTown; - }, 700, 100)) { - Misc.errorReport(new Error("Town.goToTown: Failed to go to town. Quiting.")); - scriptBroadcast("quit"); - } - } - } - } - - if (!act) return true; - if (act < 1 || act > 5) throw new Error("Town.goToTown: Invalid act"); - if (act > me.highestAct) return false; - - if (act !== me.act) { - try { - Pather.useWaypoint(sdk.areas.townOfAct(act), wpmenu); - } catch (WPError) { - throw new Error("Town.goToTown: Failed use WP"); - } - } - - return true; - }; - - Town.visitTown = function () { - console.log("ÿc8Start ÿc0:: ÿc8visitTown"); - - const preArea = me.area; - const preAct = sdk.areas.actOf(preArea); - - if (!me.inTown && !me.getTpTool()) { - console.warn("Can't chicken to town. Quit"); - scriptBroadcast("quit"); - } - - // not an essential function -> handle thrown errors - me.cancelUIFlags(); - try { - Town.goToTown(); - } catch (e) { - return false; - } - - Town.doChores(); - - console.debug("Current act: " + me.act + " Prev Act: " + preAct); - me.act !== preAct && Town.goToTown(preAct); - Town.move("portalspot"); - - if (!Pather.usePortal(preArea, me.name)) { - try { - Pather.usePortal(null, me.name); - } catch (e) { - throw new Error("Town.visitTown: Failed to go back from town"); - } - } - - console.log("ÿc8End ÿc0:: ÿc8visitTown - currentArea: " + getAreaName(me.area)); - - return me.area === preArea; - }; - - const pause = function () { - let script = getScript("libs/SoloPlay/SoloPlay.js"); - - if (!script) { - !!getScript("libs/SoloPlay/threads/toolsthread.js") ? scriptBroadcast("quit") : quit(); - } - - if (script && script.running) { - script.pause(); - console.log("ÿc8TownChicken:: ÿc1Pausing SoloPlay"); - - return true; - } - - return false; - }; - - const resume = function () { - let script = getScript("libs/SoloPlay/SoloPlay.js"); - - // resume only if clonekilla isn't running - if (!SoloEvents.cloneWalked) { - if (script && !script.running) { - console.log("ÿc8TownChicken :: ÿc2Resuming threads"); - script.resume(); - - return true; - } else { - if (!script) { - // soloplay has crashed? We shouldn't be running then. Is toolsthread still up? - // if yes try to still quit normally, otherwise quit from here - !!getScript("libs/SoloPlay/threads/toolsthread.js") ? scriptBroadcast("quit") : quit(); - } - } - } - - return false; - }; - - this.scriptEvent = function (msg) { - let obj; - - if (msg && typeof msg === "string" && msg !== "") { - switch (msg) { - // ignore common scriptBroadcast messages that aren't relevent to this thread - case "mule": - case "muleTorch": - case "muleAnni": - case "torch": - case "crafting": - case "getMuleMode": - case "pingquit": - return; - case "fastTown": - fastTown = true; - - return; - case "townCheck": - switch (me.area) { - case sdk.areas.ArreatSummit: - case sdk.areas.UberTristram: - console.warn("Don't tp from " + getAreaName(me.area)); - return; - default: - console.log("townCheck message recieved. First check passed."); - townCheck = true; - - return; - } - case "quit": - //quitFlag = true; - // Maybe stop townChicken thread? Would that keep us from the crash that happens when we try to leave game while townChickening - - break; - default: - break; - } - - switch (true) { - case msg.substring(0, 8) === "config--": - console.debug("update config"); - Config = JSON.parse(msg.split("config--")[1]); - - break; - case msg.substring(0, 7) === "skill--": - console.debug("update skillData"); - obj = JSON.parse(msg.split("skill--")[1]); - Misc.updateRecursively(CharData.skillData, obj); - - break; - case msg.substring(0, 6) === "data--": - console.debug("update me.data"); - obj = JSON.parse(msg.split("data--")[1]); - Misc.updateRecursively(me.data, obj); - - break; - case msg.toLowerCase() === "test": - console.debug(sdk.colors.Green + "//-----------DataDump Start-----------//\nÿc8MainData ::\n", - me.data, "\nÿc8BuffData ::\n", CharData.pots, "\nÿc8SkillData ::\n", CharData.skillData, "\n" + sdk.colors.Red + "//-----------DataDump End-----------//"); - - break; - } - } - }; - - addEventListener("scriptmsg", this.scriptEvent); - let tGuard = getScript("libs/SoloPlay/Modules/TownGuard.js"); - !!tGuard && tGuard.running && tGuard.stop(); - Developer.debugging.showStack.profiles.some(prof => String.isEqual(prof, me.profile) || String.isEqual(prof, "all")) && require("../Modules/TownGuard"); - - // START - // test for getUnit bug - let test = Game.getMonster(); - test === null && console.warn("getUnit is bugged"); - - const useHowl = Skill.canUse(sdk.skills.Howl); - const useTerror = Skill.canUse(sdk.skills.Terror); - - Config.DebugMode.Stack = true; - - while (true) { - if (!me.inTown && (townCheck || fastTown - || ((Config.TownHP > 0 && me.hpPercent < Config.TownHP) - || (Config.TownMP > 0 && me.mpPercent < Config.TownMP)))) { - // should we exit if we can't tp to town? - if (townCheck && !me.canTpToTown()) { - townCheck = false; - - continue; - } - pause(); - - while (!me.gameReady) { - if (me.dead) { - scriptBroadcast("quit"); - return false; - } - delay(40); - } - - let t4 = getTickCount(); - try { - myPrint("ÿc8TownChicken :: ÿc0Going to town"); - Messaging.sendToScript("libs/SoloPlay/Threads/EventThread.js", "townchickenOn"); - [Attack.stopClear, SoloEvents.townChicken.running] = [true, true]; - - // determine if this is really worth it - if (useHowl || useTerror) { - if ([156, 211, 242, 243, 544, 571, 345].indexOf(Attack.getNearestMonster()) === -1) { - if (useHowl && Skill.getManaCost(130) < me.mp) { - Skill.cast(130, sdk.skills.hand.Right); - } - - if (useTerror && Skill.getManaCost(77) < me.mp) { - Skill.cast(77, sdk.skills.hand.Right, Attack.getNearestMonster({ skipImmune: false })); - } - } - } - - Town.visitTown(); - } catch (e) { - Misc.errorReport(e, "TownChicken.js"); - scriptBroadcast("quit"); - - return false; - } finally { - resume(); - Messaging.sendToScript("libs/SoloPlay/Threads/EventThread.js", "townchickenOff"); - [Attack.stopClear, SoloEvents.townChicken.running, townCheck, fastTown] = [false, false, false, false]; - console.log("ÿc8TownChicken :: Took: " + Time.format(getTickCount() - t4) + " to visit town"); - } - } - - delay(50); - } -} From 3b1b61d6c08997395ac62c415372de87047dcc28 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Thu, 23 Mar 2023 14:18:19 -0400 Subject: [PATCH 092/263] Update EventEmitter and Events - Move the Unit.prototype additions to `Events.js`. Refactor the two background workers in EventEmitter into one - Fix testing case for soloevent - Fix typo in guard --- libs/SoloPlay/Modules/Events.js | 12 +++- libs/SoloPlay/Modules/Guard.js | 4 +- libs/SoloPlay/Workers/EventEmitter.js | 97 +++++++++++++-------------- libs/SoloPlay/Workers/EventHandler.js | 3 + libs/SoloPlay/Workers/TownChicken.js | 1 - 5 files changed, 63 insertions(+), 54 deletions(-) diff --git a/libs/SoloPlay/Modules/Events.js b/libs/SoloPlay/Modules/Events.js index 3e483790..9297de5e 100644 --- a/libs/SoloPlay/Modules/Events.js +++ b/libs/SoloPlay/Modules/Events.js @@ -1,7 +1,7 @@ /** * @filename Events.js * @author Jaenster - * @desc Transpiled UMD event module + * @desc Transpiled UMD event module. Adds prototypes ("on", "emit", "once", "off") to Unit * */ @@ -86,5 +86,15 @@ }; return Events; }()); + + // @ts-ignore + Unit.prototype.on = Events.prototype.on; + // @ts-ignore + Unit.prototype.off = Events.prototype.off; + // @ts-ignore + Unit.prototype.once = Events.prototype.once; + // @ts-ignore + Unit.prototype.emit = Events.prototype.emit; + exports.Events = Events; }); diff --git a/libs/SoloPlay/Modules/Guard.js b/libs/SoloPlay/Modules/Guard.js index db3f7074..2ce60486 100644 --- a/libs/SoloPlay/Modules/Guard.js +++ b/libs/SoloPlay/Modules/Guard.js @@ -24,11 +24,9 @@ self.hooks.push(element); this.update = () => { element.text = callback(); - element.visible = element.visible = [sdk.uiflags.Inventory, + element.visible = [sdk.uiflags.Inventory, sdk.uiflags.SkillWindow, sdk.uiflags.TradePrompt, - sdk.uiflags.Stash, - sdk.uiflags.Cube, sdk.uiflags.QuickSkill].every(f => !getUIFlag(f)); }; } diff --git a/libs/SoloPlay/Workers/EventEmitter.js b/libs/SoloPlay/Workers/EventEmitter.js index ab2e13a6..eb14d205 100644 --- a/libs/SoloPlay/Workers/EventEmitter.js +++ b/libs/SoloPlay/Workers/EventEmitter.js @@ -3,7 +3,6 @@ * @author theBGuy * @credit jaenster * @desc Global modifying UMD module to handle emitting events - * and add prototypes ("on", "emit", "once", "off") to Unit * */ @@ -19,7 +18,7 @@ Object.defineProperty(exports, "__esModule", { value: true }); const Worker = require("../../modules/Worker"); - const Events = require("../Modules/Events"); + require("../Modules/Events"); const old = { level: me.charlvl, }; @@ -27,64 +26,64 @@ let levelTimeout = getTickCount(); - // Start - Worker.runInBackground.AutoBuild = function () { - if (getTickCount() - levelTimeout < 1000) return true; - levelTimeout = getTickCount(); + const _AutoBuild = new function () { + this.enabled = true; + + this.run = function () { + if (!this.enabled) return; - try { - let levels = gainedLevels(); + try { + let levels = gainedLevels(); - if (levels > 0 && (Config.AutoSkill.Enabled || Config.AutoStat.Enabled)) { - scriptBroadcast("toggleQuitlist"); - AutoBuild.print("Level up detected (", old.level, "-->", me.charlvl, ")"); - Config.AutoSkill.Enabled && AutoSkill.init(Config.AutoSkill.Build, Config.AutoSkill.Save); - Config.AutoStat.Enabled && AutoStat.init(Config.AutoStat.Build, Config.AutoStat.Save, Config.AutoStat.BlockChance, Config.AutoStat.UseBulk); - scriptBroadcast({ event: "level up" }); - AutoBuild.applyConfigUpdates(); // scriptBroadcast() won't trigger listener on this thread. + if (levels > 0 && (Config.AutoSkill.Enabled || Config.AutoStat.Enabled)) { + scriptBroadcast("toggleQuitlist"); + AutoBuild.print("Level up detected (", old.level, "-->", me.charlvl, ")"); + Config.AutoSkill.Enabled && AutoSkill.init(Config.AutoSkill.Build, Config.AutoSkill.Save); + Config.AutoStat.Enabled && AutoStat.init(Config.AutoStat.Build, Config.AutoStat.Save, Config.AutoStat.BlockChance, Config.AutoStat.UseBulk); + scriptBroadcast({ event: "level up" }); + AutoBuild.applyConfigUpdates(); // scriptBroadcast() won't trigger listener on this thread. - AutoBuild.print("Incrementing cached character level to", old.level + 1); - Tracker.leveling(); + AutoBuild.print("Incrementing cached character level to", old.level + 1); + Tracker.leveling(); - // prevLevel doesn't get set to me.charlvl because - // we may have gained multiple levels at once - old.level += 1; + // prevLevel doesn't get set to me.charlvl because + // we may have gained multiple levels at once + old.level += 1; - scriptBroadcast("toggleQuitlist"); + scriptBroadcast("toggleQuitlist"); + } + } catch (e) { + this.enabled = false; + console.error(e); + console.warn("Something broke! StackWalk :: ", e.stack); } - } catch (e) { - console.error(e); - console.warn("Something broke! StackWalk :: ", e.stack); + }; + }; + + // Start + Worker.runInBackground.EventWatcher = function () { + // AutoBuild + if (getTickCount() - levelTimeout > 1000) { + levelTimeout = getTickCount(); + _AutoBuild.run(); + } + + // Tracker + if (Developer.logPerformance) { + if (getTickCount() - Tracker.tick > Time.minutes(3)) { + Tracker.tick = getTickCount(); - return false; + try { + Tracker.update(); + } catch (e) { + console.warn(e.message); + } + } } return true; }; + AutoBuild.print("Loaded AutoBuild"); console.log("ÿc8Kolbot-SoloPlayÿc0: Start AutoBuild"); - - if (Developer.logPerformance && getScript(true).name.toString() === "libs\\soloplay\\soloplay.js") { - Worker.runInBackground.intervalUpdate = function () { - if (getTickCount() - Tracker.tick < Time.minutes(3)) return true; - Tracker.tick = getTickCount(); - - try { - Tracker.update(); - } catch (e) { - console.warn(e.message); - } - - return true; - }; - } - - // @ts-ignore - Unit.prototype.on = Events.Events.prototype.on; - // @ts-ignore - Unit.prototype.off = Events.Events.prototype.off; - // @ts-ignore - Unit.prototype.once = Events.Events.prototype.once; - // @ts-ignore - Unit.prototype.emit = Events.Events.prototype.emit; }); diff --git a/libs/SoloPlay/Workers/EventHandler.js b/libs/SoloPlay/Workers/EventHandler.js index b0ab0dc2..52f6ab40 100644 --- a/libs/SoloPlay/Workers/EventHandler.js +++ b/libs/SoloPlay/Workers/EventHandler.js @@ -15,6 +15,9 @@ me.on("soloEvent", function msgEvent (msg) { switch (msg) { case "testing": + console.debug(msg, actions); + + break; case "finishDen": case "dodge": case "skip": diff --git a/libs/SoloPlay/Workers/TownChicken.js b/libs/SoloPlay/Workers/TownChicken.js index 05d7db4f..3eae4540 100644 --- a/libs/SoloPlay/Workers/TownChicken.js +++ b/libs/SoloPlay/Workers/TownChicken.js @@ -13,7 +13,6 @@ (function (module, require, Worker) { // Only load this in global scope if (getScript(true).name.toLowerCase() === "libs\\soloplay\\soloplay.js") { - const getNearestMonster = () => { let gid = null; let monster = Game.getMonster(); From 6bd7aecbb04129c8f3fbfdc7759a88f71a75aa46 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Thu, 23 Mar 2023 14:19:44 -0400 Subject: [PATCH 093/263] Update SoloPlay.js - Fix false run incrementation when reloading --- libs/SoloPlay/SoloPlay.js | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/libs/SoloPlay/SoloPlay.js b/libs/SoloPlay/SoloPlay.js index 1ad590ac..33457645 100644 --- a/libs/SoloPlay/SoloPlay.js +++ b/libs/SoloPlay/SoloPlay.js @@ -264,8 +264,10 @@ function main () { AutoStat.init(Config.AutoStat.Build, Config.AutoStat.Save, Config.AutoStat.BlockChance, Config.AutoStat.UseBulk); } - // offline - !me.realm && D2Bot.updateRuns(); + // offline - ensure we didn't just reload the thread and are still in the same game + if (!me.realm && getTickCount() - me.gamestarttime < Time.minutes(1)) { + D2Bot.updateRuns(); + } // Start Running Script includeIfNotIncluded("SoloPlay/Utils/Init.js"); @@ -350,7 +352,8 @@ function main () { // Anni handler. Mule Anni if it's in unlocked space and profile is set to mule torch/anni. let anni = me.findItem(sdk.items.SmallCharm, sdk.items.mode.inStorage, -1, sdk.items.quality.Unique); - if (anni && !Storage.Inventory.IsLocked(anni, Config.Inventory) && AutoMule.getInfo() && AutoMule.getInfo().hasOwnProperty("torchMuleInfo")) { + if (anni && !Storage.Inventory.IsLocked(anni, Config.Inventory) + && AutoMule.getInfo() && AutoMule.getInfo().hasOwnProperty("torchMuleInfo")) { scriptBroadcast("muleAnni"); } From 559dfc982b58265f1d0ecb5d8a3df93e93567a78 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Thu, 23 Mar 2023 15:18:21 -0400 Subject: [PATCH 094/263] Fix old reference to merc.type - remove TownGuard as well, its no longer used --- libs/SoloPlay/Functions/Me.js | 2 +- libs/SoloPlay/Modules/TownGuard.js | 84 ------------------------------ 2 files changed, 1 insertion(+), 85 deletions(-) delete mode 100644 libs/SoloPlay/Modules/TownGuard.js diff --git a/libs/SoloPlay/Functions/Me.js b/libs/SoloPlay/Functions/Me.js index a7d2295e..67d1502b 100644 --- a/libs/SoloPlay/Functions/Me.js +++ b/libs/SoloPlay/Functions/Me.js @@ -249,7 +249,7 @@ me.canTpToTown = function () { me.getMercEx = function () { if (!Config.UseMerc || me.classic || me.mercrevivecost) return null; - if (!me.data.merc.type) return null; + if (!me.data.merc.skill) return null; let merc = Misc.poll(() => me.getMerc(), 250, 50); return !!merc && !merc.dead ? merc : null; diff --git a/libs/SoloPlay/Modules/TownGuard.js b/libs/SoloPlay/Modules/TownGuard.js deleted file mode 100644 index 42a9b78d..00000000 --- a/libs/SoloPlay/Modules/TownGuard.js +++ /dev/null @@ -1,84 +0,0 @@ -(function (module, require, thread) { - const Messaging = require("../../modules/Messaging"); - const Worker = require("../../modules/Worker"); - const sdk = require("../../modules/sdk"); - - switch (thread) { - case "thread": { - Worker.runInBackground.stackTrace = (new function () { - let self = this; - let stack; - - let myStack = ""; - - // recv stack - Messaging.on("TownGuard", (data => typeof data === "object" && data && data.hasOwnProperty("stack") && (myStack = data.stack))); - - /** - * @constructor - * @param {function():string} callback - */ - function UpdateableText(callback) { - let element = new Text(callback(), self.x + 15, self.y + (7 * self.hooks.length), 0, 12, 0); - self.hooks.push(element); - this.update = () => { - element.text = callback(); - element.visible = element.visible = [sdk.uiflags.Inventory, - sdk.uiflags.SkillWindow, - sdk.uiflags.TradePrompt, - sdk.uiflags.Stash, - sdk.uiflags.Cube, - sdk.uiflags.QuickSkill].every(f => !getUIFlag(f)); - }; - } - - this.hooks = []; - this.x = 150; - this.y = 600 - (400 + (self.hooks.length * 15)); - // this.box = new Box(this.x-2, this.y-20, 250, (self.hooks.length * 15), 0, 0.2); - - - for (let i = 0; i < 20; i++) { - (i => this.hooks.push(new UpdateableText(() => stack && stack.length > i && stack[i] || "")))(i); - } - - this.update = () => { - stack = myStack.match(/[^\r\n]+/g); - stack = stack && stack.slice(6/*skip path to here*/).map(el => { - let line = el.substr(el.lastIndexOf(":") + 1); - let functionName = el.substr(0, el.indexOf("@")); - let filename = el.substr(el.lastIndexOf("\\") + 1); - - filename = filename.substr(0, filename.indexOf(".")); - - return filename + "ÿc::ÿc0" + line + "ÿc:@ÿc0" + functionName; - }).reverse(); - this.hooks.filter(hook => hook.hasOwnProperty("update") && typeof hook.update === "function" && hook.update()); - return true; - }; - - }).update; - - let quiting = false; - addEventListener("scriptmsg", data => data === "quit" && (quiting = true)); - - while (!quiting) delay(1000); - break; - } - case "started": { - let sendStack = getTickCount(); - Worker.push(function highPrio() { - Worker.push(highPrio); - if ((getTickCount() - sendStack) < 200 || (sendStack = getTickCount()) && false) return true; - Messaging.send({ TownGuard: { stack: (new Error).stack } }); - return true; - }); - - break; - } - case "loaded": { - break; - } - } - -}).call(null, typeof module === "object" && module || {}, typeof require === "undefined" && (include("require.js") && require) || require, getScript.startAsThread()); From ba5e18f65b85c0a0916449aa8a96f3c2029966ec Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Sun, 26 Mar 2023 12:41:33 -0400 Subject: [PATCH 095/263] Update druid.StartBuild.js - Missed redoing this file during the structure change --- .../BuildFiles/druid/druid.StartBuild.js | 122 ++++++++++-------- 1 file changed, 67 insertions(+), 55 deletions(-) diff --git a/libs/SoloPlay/BuildFiles/druid/druid.StartBuild.js b/libs/SoloPlay/BuildFiles/druid/druid.StartBuild.js index e0f61dd5..6fcba3af 100644 --- a/libs/SoloPlay/BuildFiles/druid/druid.StartBuild.js +++ b/libs/SoloPlay/BuildFiles/druid/druid.StartBuild.js @@ -5,62 +5,74 @@ * */ -let build = { - AutoBuildTemplate: {}, - caster: true, - skillstab: 42, // elemental - wantedskills: [sdk.skills.Firestorm, sdk.skills.Fissure], - usefulskills: [sdk.skills.MoltenBoulder], - wantedMerc: MercData[sdk.skills.BlessedAim], - stats: [ - ["vitality", 70], - ["strength", 35], - ["energy", 85], - ["vitality", "all"] - ], - skills: [ - [sdk.skills.PoisonCreeper, 2, false], - [sdk.skills.Firestorm, 4, false], - [sdk.skills.MoltenBoulder, 1], - [sdk.skills.Firestorm, 7, false], - [sdk.skills.Fissure, 1], - [sdk.skills.CarrionVine, 1], - [sdk.skills.Fissure, 8, false], - [sdk.skills.Firestorm, 11, false], - [sdk.skills.Fissure, 20, false], - [sdk.skills.Firestorm, 18, false], - ], - active: function () { - return me.charlvl < CharInfo.respecOne && !me.checkSkill(sdk.skills.Tornado, sdk.skills.subindex.HardPoints); - }, -}; +(function (module, require) { + module.exports = (function () { + /** + * @todo Test summoner/elemental build + */ + const build = { + AutoBuildTemplate: {}, + caster: true, + skillstab: sdk.skills.tabs.Elemental, + wantedskills: [sdk.skills.Firestorm, sdk.skills.Fissure], + usefulskills: [sdk.skills.MoltenBoulder], + wantedMerc: MercData[sdk.skills.BlessedAim], + stats: [ + ["vitality", 70], + ["strength", 35], + ["energy", 85], + ["vitality", "all"] + ], + skills: [ + [sdk.skills.PoisonCreeper, 2, false], + [sdk.skills.Firestorm, 4, false], + [sdk.skills.MoltenBoulder, 1], + [sdk.skills.Firestorm, 7, false], + [sdk.skills.Fissure, 1], + [sdk.skills.CarrionVine, 1], + [sdk.skills.Fissure, 8, false], + [sdk.skills.Firestorm, 11, false], + [sdk.skills.Fissure, 20, false], + [sdk.skills.Firestorm, 18, false], + ], -build.AutoBuildTemplate[1] = buildAutoBuildTempObj(() => { - Config.TownHP = me.hardcore ? 0 : 35; - Config.SkipImmune = ["fire"]; - Config.BeltColumn = me.charlvl < 7 ? ["hp", "hp", "hp", "mp"] : ["hp", "hp", "mp", "mp"]; - Config.HPBuffer = 4; - Config.MPBuffer = 4; - Config.AttackSkill = [-1, 0, 0, 0, 0, 0, 0]; - Config.LowManaSkill = [0, 0]; - Config.SummonVine = 1; // "Poison Creeper" - if (me.checkSkill(sdk.skills.Firestorm, sdk.skills.subindex.HardPoints)) { - Config.AttackSkill = [-1, sdk.skills.Firestorm, -1, sdk.skills.Firestorm, -1, 0, 0]; - } else { - Config.AttackSkill = [-1, 0, 0, 0, 0, 0, 0]; - } - SetUp.belt(); -}); + active: function () { + return me.charlvl < CharInfo.respecOne && !me.checkSkill(sdk.skills.Tornado, sdk.skills.subindex.HardPoints); + }, + }; -build.AutoBuildTemplate[12] = buildAutoBuildTempObj(() => { - if (me.checkSkill(sdk.skills.Fissure, sdk.skills.subindex.HardPoints)) { - Config.AttackSkill = [-1, sdk.skills.Fissure, sdk.skills.Firestorm, sdk.skills.Fissure, sdk.skills.Firestorm, 0, 0]; - } -}); + const { buildAutoBuildTempObj } = require("../../Utils/General"); -build.AutoBuildTemplate[13] = buildAutoBuildTempObj(() => { - if (Skill.canUse(sdk.skills.Fissure) && Skill.skills.all[sdk.skills.Fissure].hardpoints) { - Config.AttackSkill = [-1, sdk.skills.Fissure, sdk.skills.Firestorm, sdk.skills.Fissure, sdk.skills.Firestorm, 0, 0]; - } -}); + build.AutoBuildTemplate[1] = buildAutoBuildTempObj(() => { + Config.TownHP = me.hardcore ? 0 : 35; + Config.SkipImmune = ["fire"]; + Config.BeltColumn = me.charlvl < 7 ? ["hp", "hp", "hp", "mp"] : ["hp", "hp", "mp", "mp"]; + Config.HPBuffer = 4; + Config.MPBuffer = 4; + Config.AttackSkill = [-1, 0, 0, 0, 0, 0, 0]; + Config.LowManaSkill = [0, 0]; + Config.SummonVine = 1; // "Poison Creeper" + if (me.checkSkill(sdk.skills.Firestorm, sdk.skills.subindex.HardPoints)) { + Config.AttackSkill = [-1, sdk.skills.Firestorm, -1, sdk.skills.Firestorm, -1, 0, 0]; + } else { + Config.AttackSkill = [-1, 0, 0, 0, 0, 0, 0]; + } + SetUp.belt(); + }); + + build.AutoBuildTemplate[12] = buildAutoBuildTempObj(() => { + if (me.checkSkill(sdk.skills.Fissure, sdk.skills.subindex.HardPoints)) { + Config.AttackSkill = [-1, sdk.skills.Fissure, sdk.skills.Firestorm, sdk.skills.Fissure, sdk.skills.Firestorm, 0, 0]; + } + }); + + build.AutoBuildTemplate[13] = buildAutoBuildTempObj(() => { + if (Skill.canUse(sdk.skills.Fissure) && Skill.skills.all[sdk.skills.Fissure].hardpoints) { + Config.AttackSkill = [-1, sdk.skills.Fissure, sdk.skills.Firestorm, sdk.skills.Fissure, sdk.skills.Firestorm, 0, 0]; + } + }); + + return build; + })(); +})(module, require); From 7ae8ec633399dd4bb2a0e3d10578b6949fb0832a Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Sun, 26 Mar 2023 16:21:41 -0400 Subject: [PATCH 096/263] Update Me.js - Fix circular dependency --- libs/SoloPlay/Functions/Me.js | 1 - 1 file changed, 1 deletion(-) diff --git a/libs/SoloPlay/Functions/Me.js b/libs/SoloPlay/Functions/Me.js index 67d1502b..a67bf8a7 100644 --- a/libs/SoloPlay/Functions/Me.js +++ b/libs/SoloPlay/Functions/Me.js @@ -249,7 +249,6 @@ me.canTpToTown = function () { me.getMercEx = function () { if (!Config.UseMerc || me.classic || me.mercrevivecost) return null; - if (!me.data.merc.skill) return null; let merc = Misc.poll(() => me.getMerc(), 250, 50); return !!merc && !merc.dead ? merc : null; From a232208fd039462e81ed969ead45dde95d371815 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Sun, 2 Apr 2023 19:46:54 -0400 Subject: [PATCH 097/263] Update OOGOverrides.js - Keep updated with the core Control module --- libs/SoloPlay/Tools/OOGOverrides.js | 38 ++++++++++++++--------------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/libs/SoloPlay/Tools/OOGOverrides.js b/libs/SoloPlay/Tools/OOGOverrides.js index 5abfe1aa..5847e4d7 100644 --- a/libs/SoloPlay/Tools/OOGOverrides.js +++ b/libs/SoloPlay/Tools/OOGOverrides.js @@ -226,7 +226,7 @@ const locations = {}; !info.ladder && Controls.CharCreateLadder.click(); info.hardcore && Controls.CharCreateHardcore.click(); - Controls.CreateNewAccountOk.click(); + Controls.BottomRightOk.click(); } break; @@ -281,7 +281,7 @@ const locations = {}; let spCheck = Profile().type === sdk.game.profiletype.Battlenet; let realmControl = !!Controls.CharSelectCurrentRealm.control; if ((spCheck && !realmControl) || ((!spCheck && realmControl))) { - Controls.CharSelectExit.click(); + Controls.BottomLeftExit.click(); return false; // what about a recursive call to loginCharacter? } } @@ -365,7 +365,7 @@ const locations = {}; if (control) { control.click(); - Controls.CreateNewAccountOk.click(); + Controls.BottomRightOk.click(); me.blockMouse = false; if (getLocation() === sdk.game.locations.SelectDifficultySP) { @@ -388,7 +388,7 @@ const locations = {}; break MainLoop; case sdk.game.locations.CharSelectNoChars: - Controls.CharSelectExit.click(); // why exit rather than returning false? + Controls.BottomLeftExit.click(); // why exit rather than returning false? break; case sdk.game.locations.Disconnected: @@ -446,7 +446,7 @@ const locations = {}; break; case sdk.game.locations.CharacterCreate: - Controls.CharSelectExit.click(); + Controls.BottomLeftExit.click(); break; case sdk.game.locations.OkCenteredErrorPopUp: @@ -461,10 +461,10 @@ const locations = {}; break; case sdk.game.locations.CreateNewAccount: - Controls.CreateNewAccountName.setText(info.account); - Controls.CreateNewAccountPassword.setText(info.password); - Controls.CreateNewAccountConfirmPassword.setText(info.password); - Controls.CreateNewAccountOk.click(); + Controls.EnterAccountName.setText(info.account); + Controls.EnterAccountPassword.setText(info.password); + Controls.ConfirmPassword.setText(info.password); + Controls.BottomRightOk.click(); break; case sdk.game.locations.PleaseRead: @@ -513,7 +513,7 @@ const locations = {}; control.click(); Controls.CharSelectDelete.click(); delay(500); - Controls.CharDeleteYes.click(); + Controls.PopupYes.click(); break MainLoop; } @@ -589,8 +589,8 @@ const locations = {}; break; case sdk.game.locations.Login: - Controls.LoginUsername.setText(info.account); - Controls.LoginPassword.setText(info.password); + Controls.EnterAccountName.setText(info.account); + Controls.EnterAccountPassword.setText(info.password); Controls.Login.click(); break; @@ -669,7 +669,7 @@ const locations = {}; // bugged? lets see if we can unbug it // Click create char button on infinite "connecting" screen Controls.CharSelectCreate.click() && delay(1000); - Controls.CharSelectExit.click() && delay(1000); + Controls.BottomLeftExit.click() && delay(1000); return (getLocation() !== sdk.game.locations.CharSelectConnecting); } else { @@ -706,14 +706,14 @@ const locations = {}; let spCheck = pType === sdk.game.profiletype.Battlenet; let realmControl = !!Controls.CharSelectCurrentRealm.control; if ((spCheck && !realmControl) || ((!spCheck && realmControl))) { - Controls.CharSelectExit.click(); + Controls.BottomLeftExit.click(); return; } } // Multiple realm botting fix in case of R/D or disconnect - Starter.firstLogin && getLocation() === sdk.game.locations.Login && Controls.CharSelectExit.click(); + Starter.firstLogin && getLocation() === sdk.game.locations.Login && Controls.BottomLeftExit.click(); D2Bot.updateStatus("Logging In"); @@ -869,7 +869,7 @@ const locations = {}; Starter.accountExists = true; Control.LoginErrorOk.click(); delay(100); - Control.CreateNewAccountExit.click(); + Control.BottomLeftExit.click(); Starter.LocationEvents.login(); return; } @@ -959,7 +959,7 @@ const locations = {}; Controls.LoginErrorOk.click(); delay(1000); - Controls.CharSelectExit.click(); + Controls.BottomLeftExit.click(); }; Starter.LocationEvents.charSelect = function (loc) { @@ -1024,7 +1024,7 @@ const locations = {}; } if (!Starter.locationTimeout(Starter.Config.ConnectingTimeout * 1e3, loc)) { - Controls.CharSelectExit.click(); + Controls.BottomLeftExit.click(); Starter.gameInfo.rdBlocker && D2Bot.restart(); } }; @@ -1137,7 +1137,7 @@ const locations = {}; locations[sdk.game.locations.CharSelectConnecting] = (loc) => Starter.LocationEvents.charSelect(loc); locations[sdk.game.locations.CharSelectNoChars] = (loc) => Starter.LocationEvents.charSelect(loc); locations[sdk.game.locations.SelectDifficultySP] = () => Starter.LocationEvents.selectDifficultySP(); - locations[sdk.game.locations.CharacterCreate] = (loc) => !Starter.locationTimeout(5e3, loc) && Controls.CharSelectExit.click(); + locations[sdk.game.locations.CharacterCreate] = (loc) => !Starter.locationTimeout(5e3, loc) && Controls.BottomLeftExit.click(); locations[sdk.game.locations.ServerDown] = () => { ControlAction.timeoutDelay("Server Down", Time.minutes(5)); Controls.OkCentered.click(); From 4e73cc617f174034a008e68ccb82376a2cfcefb4 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Mon, 3 Apr 2023 12:49:14 -0400 Subject: [PATCH 098/263] Update OOGOverrides.js - Small bit of cleanup, better error checking/handling during get profile process - Cleanup for deleteAndRemkeChar func - Fix gamename bug if the initial character name was taken and we had to gen a new one - Fix handle transfer when remaking a character --- libs/SoloPlay/Tools/OOGOverrides.js | 236 ++++++++++++++-------------- 1 file changed, 121 insertions(+), 115 deletions(-) diff --git a/libs/SoloPlay/Tools/OOGOverrides.js b/libs/SoloPlay/Tools/OOGOverrides.js index 5847e4d7..56ae329d 100644 --- a/libs/SoloPlay/Tools/OOGOverrides.js +++ b/libs/SoloPlay/Tools/OOGOverrides.js @@ -45,63 +45,96 @@ const locations = {}; break; case 1638: try { + /** @type {Map} */ + const classMap = new Map(); + classMap.set("ZON", "amazon"); + classMap.set("SOR", "sorceress"); + classMap.set("NEC", "necromancer"); + classMap.set("PAL", "paladin"); + classMap.set("BAR", "barbarian"); + classMap.set("DRU", "druid"); + classMap.set("SIN", "assassin"); + let [modePrefix, charClass] = me.profile.toUpperCase().split("-"); + + if (!modePrefix || !charClass || !(charClass = classMap.get(charClass.substring(0, 3)))) { + D2Bot.printToConsole( + "*** Invalid profile name ***\n" + + "Profile :: " + me.profile + " | Prefix: " + modePrefix + " | CharClass: " + charClass + "\n" + + "@see https://github.com/blizzhackers/kolbot-SoloPlay#possible-profile-names \n" + + "**********************************************************************************", + sdk.colors.D2Bot.Red + ); + CharData.delete(true); + throw new Error("Invalid profile name :: " + me.profile); + } + let obj = JSON.parse(msg); - Starter.profileInfo.profile = me.profile.toUpperCase(); + let infoTag = obj.Tag.trim().capitalize(true) || ""; + if (!infoTag) { + D2Bot.printToConsole( + "*** Invalid profile InfoTag ***\n" + + "Tag :: " + obj.Tag + "\n" + + "@see https://github.com/blizzhackers/kolbot-SoloPlay#available-characters-and-builds \n" + + "**********************************************************************************", + sdk.colors.D2Bot.Red + ); + throw new Error("Invalid profile InfoTag :: " + obj.Tag); + } + + Starter.profileInfo.profile = me.profile; Starter.profileInfo.account = obj.Account; + if (Starter.profileInfo.account.length < 2 || Starter.profileInfo.account.length > 15) { + // console.warn("Invalid account name length"); + Starter.profileInfo.account = ""; + } Starter.profileInfo.password = ""; Starter.profileInfo.charName = obj.Character; - Starter.profileInfo.tag = (obj.Tag.trim().capitalize(true) || ""); Starter.profileInfo.difficulty = obj.Difficulty; obj.Realm = obj.Realm.toLowerCase(); Starter.profileInfo.realm = ["east", "west"].includes(obj.Realm) ? "us" + obj.Realm : obj.Realm; - - let buildCheck = Starter.profileInfo.profile.split("-"); // SCL-ZON123 - Starter.profileInfo.hardcore = buildCheck[0].includes("HC"); // SC softcore = false - Starter.profileInfo.expansion = buildCheck[0].indexOf("CC") === -1; // not CC so not classic - true - Starter.profileInfo.ladder = buildCheck[0].indexOf("NL") === -1; // not NL so its ladder - true - - if (buildCheck.length <= 1) { - D2Bot.printToConsole('Please update profile name. Example: "HCCNL-PAL" will make a Hardcore Classic NonLadder Paladin', sdk.colors.D2Bot.Gold); - D2Bot.printToConsole("If you are still confused please read the included readMe. https://github.com/blizzhackers/kolbot-SoloPlay/blob/main/README.md", sdk.colors.D2Bot.Gold); - D2Bot.stop(); + Starter.profileInfo.mode = Profile().type; + Starter.profileInfo.tag = infoTag; + + /** + * @example SCL-ZON123 + */ + Starter.profileInfo.hardcore = modePrefix.includes("HC"); // SC softcore = false + Starter.profileInfo.expansion = modePrefix.indexOf("CC") === -1; // not CC so not classic - true + Starter.profileInfo.ladder = modePrefix.indexOf("NL") === -1; // not NL so its ladder - true + Starter.profileInfo.charClass = charClass; + + if (["druid", "assassin"].includes(charClass) && !Starter.profileInfo.expansion) { + D2Bot.printToConsole( + "*** Invalid character class for mode ***\n" + + "CharClass :: " + charClass + "\n" + + "Expansion characters cannot be made in classic\n" + + "**********************************************************************************", + sdk.colors.D2Bot.Red + ); + throw new Error("Expansion characters cannot be made in classic"); } - const charClassMap = { - "ZON": "amazon", - "SOR": "sorceress", - "NEC": "necromancer", - "PAL": "paladin", - "BAR": "barbarian", - "DRU": "druid", - "SIN": "assassin" - }; - buildCheck[1] = buildCheck[1].toString().substring(0, 3); - - if (charClassMap[buildCheck[1]]) { - Starter.profileInfo.charClass = charClassMap[buildCheck[1]]; - } else { - throw new Error("Invalid profile name, couldn't set character class"); - } - - if (Starter.profileInfo.tag !== "") { - { - let soloStats = CharData.getStats(); + { + let soloStats = CharData.getStats(); + let update = false; + // new profile + if (!soloStats.finalBuild) { + soloStats.finalBuild = Starter.profileInfo.tag; + CharData.updateData("me", soloStats); + } else if (soloStats.finalBuild !== Starter.profileInfo.tag) { + soloStats.finalBuild = Starter.profileInfo.tag; + update = true; + } - if (!soloStats.finalBuild || soloStats.finalBuild !== Starter.profileInfo.tag) { - D2Bot.setProfile(null, null, null, null, null, Starter.profileInfo.tag); - soloStats.finalBuild = Starter.profileInfo.tag; - soloStats.charms = {}; - CharData.updateData("me", soloStats); - } + if (soloStats.currentBuild !== soloStats.finalBuild + && !["Start", "Stepping", "Leveling"].includes(soloStats.currentBuild)) { + soloStats.currentBuild = "Leveling"; + } - if (!["Start", "Stepping", "Leveling"].includes(soloStats.currentBuild) && soloStats.currentBuild !== soloStats.finalBuild) { - soloStats.currentBuild = "Leveling"; - soloStats.charms = {}; - CharData.updateData("me", soloStats); - } + if (update) { + soloStats.charms = {}; + CharData.updateData("me", soloStats); } - } else { - throw new Error("Please update profile InfoTag. Missing the finalBuild."); } } catch (e) { console.error(e); @@ -121,10 +154,14 @@ const locations = {}; } else if (msg === "diffChange") { Starter.checkDifficulty(); } else if (msg === "test") { - console.debug(sdk.colors.Green + "//-----------DataDump Start-----------//", + console.debug( + sdk.colors.Green + + "//-----------DataDump Start-----------//", "\nÿc8ThreadData ::\n", getScript(true), "\nÿc8GlobalVariabls ::\n", Object.keys(global), - "\n" + sdk.colors.Red + "//-----------DataDump End-----------//"); + "\n" + sdk.colors.Red + + "//-----------DataDump End-----------//" + ); } else if (msg === "deleteAndRemake") { Starter.deadCheck = true; } else { @@ -141,25 +178,36 @@ const locations = {}; }; ControlAction.makeCharacter = function (info) { - me.blockMouse = true; + const NameGen = require("./NameGen"); !info.charClass && (info.charClass = "barbarian"); + !info.charName && (info.charName = NameGen()); + me.blockMouse = true; let clickCoords = []; let soloStats = CharData.getStats(); - const NameGen = require("./NameGen"); + let timeout = getTickCount() + Time.minutes(5); + + /** @type {Map 1) { - let finalBuild = soloStats.finalBuild; - Object.assign(soloStats, CharData._default); - soloStats.finalBuild = finalBuild; - CharData.updateData("me", soloStats); + CharData.delete(false); + CharData.updateData("me", "finalBuild", Starter.profileInfo.tag); + Developer.logPerformance && Tracker.initialize(); } D2Bot.updateStatus("Making Character: " + info.charName); // cycle until in lobby - while (getLocation() !== sdk.game.locations.Lobby) { + while (getLocation() !== sdk.game.locations.Lobby && !me.ingame) { switch (getLocation()) { case sdk.game.locations.CharSelect: case sdk.game.locations.CharSelectConnecting: @@ -181,26 +229,7 @@ const locations = {}; break; case sdk.game.locations.CharacterCreate: - clickCoords = (() => { - switch (info.charClass) { - case "barbarian": - return [400, 280]; - case "amazon": - return [100, 280]; - case "necromancer": - return [300, 290]; - case "sorceress": - return [620, 270]; - case "assassin": - return [200, 280]; - case "druid": - return [700, 280]; - case "paladin": - default: - return [521, 260]; - } - })(); - + clickCoords = coords.get(info.charClass.toLowerCase()) || coords.get("paladin"); getControl().click(clickCoords[0], clickCoords[1]); delay(500); @@ -233,8 +262,7 @@ const locations = {}; case sdk.game.locations.OkCenteredErrorPopUp: // char name exists (text box 4, 268, 320, 264, 120) ControlAction.timeoutDelay("Character Name exists: " + info.charName + ". Making new Name.", 5e3); - info.charName = NameGen(); - delay(500); + Starter.profileInfo.charName = info.charName = NameGen(); Controls.OkCentered.click(); D2Bot.updateStatus("Making Character: " + info.charName); @@ -248,6 +276,11 @@ const locations = {}; break; } + if (getTickCount() > timeout) { + D2Bot.printToConsole("Failed to create character: " + info.charName + " Location: " + getLocation(), sdk.colors.D2Bot.Red); + return false; + } + delay(500); } @@ -494,56 +527,29 @@ const locations = {}; }; ControlAction.deleteAndRemakeChar = function (info) { + /** @type {Control} */ + let control = ControlAction.findCharacter(info); + if (!control) return false; + let cInfo = control.getText(); + console.debug(cInfo); + me.blockMouse = true; - ControlAction.findCharacter(info); - - MainLoop: - // Cycle until in lobby - while (getLocation() !== sdk.game.locations.Lobby) { - switch (getLocation()) { - case sdk.game.locations.CharSelect: - let control = Controls.CharSelectCharInfo0.control; - - if (control) { - do { - let text = control.getText(); - - if (text instanceof Array && typeof text[1] === "string" && text[1].toLowerCase() === info.charName.toLowerCase()) { - control.click(); - Controls.CharSelectDelete.click(); - delay(500); - Controls.PopupYes.click(); - - break MainLoop; - } - } while (control.getNext()); - } - - break; - case sdk.game.locations.CharSelectNoChars: - break MainLoop; - - case sdk.game.locations.Disconnected: - case sdk.game.locations.OkCenteredErrorPopUp: - me.blockMouse = false; - - return false; - default: - break; - } - - delay(100); - } + control.click(); + Controls.CharSelectDelete.click(); + delay(500); + Controls.PopupYes.click(); me.blockMouse = false; // Delete old files - leaving csv file's for now as I don't think they interfere with the overlay CharData.delete(true); DataFile.create(); + DataFile.updateStats("handle", Starter.handle); CharData.updateData("me", "finalBuild", Starter.profileInfo.tag); Developer.logPerformance && Tracker.initialize(); D2Bot.printToConsole("Deleted: " + info.charName + ". Now remaking...", sdk.colors.D2Bot.Gold); + Starter.deadCheck = false; return ControlAction.makeCharacter(Starter.profileInfo); }; From 3af241325c0df5c7d45fbab9ca0460c24f79e094 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Mon, 3 Apr 2023 13:21:33 -0400 Subject: [PATCH 099/263] Update MercInsight.js - small tweak in the nip line --- libs/SoloPlay/BuildFiles/Runewords/MercInsight.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/libs/SoloPlay/BuildFiles/Runewords/MercInsight.js b/libs/SoloPlay/BuildFiles/Runewords/MercInsight.js index 9fa3346e..9b0d4f12 100644 --- a/libs/SoloPlay/BuildFiles/Runewords/MercInsight.js +++ b/libs/SoloPlay/BuildFiles/Runewords/MercInsight.js @@ -15,9 +15,9 @@ ].map(el => "[name] == " + el).join(" || "); const Insight = [ - (high + " && [flag] == ethereal && [quality] == normal # [sockets] == 0 # [maxquantity] == 1"), - (high + " && [quality] >= normal && [quality] <= superior && [flag] != runeword # [sockets] == 4 # [maxquantity] == 1"), - mid + " && [quality] >= normal && [quality] <= superior && [flag] != runeword # [sockets] == 4 # [maxquantity] == 1", + ("(" + high + ") && [flag] == ethereal && [quality] == normal # [sockets] == 0 # [maxquantity] == 1"), + ("(" + high + ") && [quality] >= normal && [quality] <= superior && [flag] != runeword # [sockets] == 4 # [maxquantity] == 1"), + ("(" + mid + ") && [quality] >= normal && [quality] <= superior && [flag] != runeword # [sockets] == 4 # [maxquantity] == 1"), ]; Config.Recipes.push([Recipe.Socket.Weapon, "giantthresher"]); @@ -35,7 +35,7 @@ Config.Runewords.push([Runeword.Insight, "bill"]); if (!me.hell) { - Insight.push(low + " && [quality] >= normal && [quality] <= superior && [flag] != runeword # [sockets] == 4 # [maxquantity] == 1"); + Insight.push("(" + low + ") && [quality] >= normal && [quality] <= superior && [flag] != runeword # [sockets] == 4 # [maxquantity] == 1"); Config.Runewords.push([Runeword.Insight, "Warscythe"]); Config.Runewords.push([Runeword.Insight, "halberd"]); Config.Runewords.push([Runeword.Insight, "poleaxe"]); From 8fd94de151b69bad883f84a07bbd78f718edcf26 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Mon, 3 Apr 2023 16:27:48 -0400 Subject: [PATCH 100/263] Remove Tracker.formatTime and timer - Update to use `Time.format` and `Time.elapsed` --- libs/SoloPlay/Functions/CharmEquip.js | 4 +-- libs/SoloPlay/Functions/Globals.js | 4 +-- libs/SoloPlay/Functions/ItemOverrides.js | 4 +-- libs/SoloPlay/Functions/LoaderOverrides.js | 6 ++-- libs/SoloPlay/Functions/PatherOverrides.js | 2 +- libs/SoloPlay/Functions/TownOverrides.js | 2 +- libs/SoloPlay/Threads/ToolsThread.js | 8 ++--- libs/SoloPlay/Tools/Overlay.js | 6 ++-- libs/SoloPlay/Tools/Tracker.js | 40 ++++++---------------- 9 files changed, 28 insertions(+), 48 deletions(-) diff --git a/libs/SoloPlay/Functions/CharmEquip.js b/libs/SoloPlay/Functions/CharmEquip.js index cd43d92a..3a564247 100644 --- a/libs/SoloPlay/Functions/CharmEquip.js +++ b/libs/SoloPlay/Functions/CharmEquip.js @@ -648,7 +648,7 @@ const CharmEquip = (function () { if ((!charms.length) // don't do anything if we have the same charms as last time || ((CharmEquip.keptGids.size && charms.every(c => CharmEquip.keptGids.has(c.gid))))) { - console.log("ÿc8Kolbot-SoloPlayÿc0: Exiting charm auto equip. Time elapsed: " + Tracker.formatTime(getTickCount() - tick)); + console.log("ÿc8Kolbot-SoloPlayÿc0: Exiting charm auto equip. Time elapsed: " + Time.format(getTickCount() - tick)); return; } CharmEquip.keptGids.clear(); @@ -716,7 +716,7 @@ const CharmEquip = (function () { me.cancelUIFlags(); - console.log("ÿc8Kolbot-SoloPlayÿc0: Exiting charm auto equip. Time elapsed: " + Tracker.formatTime(getTickCount() - tick)); + console.log("ÿc8Kolbot-SoloPlayÿc0: Exiting charm auto equip. Time elapsed: " + Time.format(getTickCount() - tick)); }, }; })(); diff --git a/libs/SoloPlay/Functions/Globals.js b/libs/SoloPlay/Functions/Globals.js index 0eb0997b..a883ce6d 100644 --- a/libs/SoloPlay/Functions/Globals.js +++ b/libs/SoloPlay/Functions/Globals.js @@ -329,7 +329,7 @@ const SetUp = { // log info myPrint(this.finalBuild + " goal reached. On to the next."); - D2Bot.printToConsole("Kolbot-SoloPlay: " + this.finalBuild + " goal reached" + (printTotalTime ? " (" + (Tracker.formatTime(gameObj.Total + Tracker.timer(gameObj.LastSave))) + "). " : ". ") + "Making next...", sdk.colors.D2Bot.Gold); + D2Bot.printToConsole("Kolbot-SoloPlay: " + this.finalBuild + " goal reached" + (printTotalTime ? " (" + (Time.format(gameObj.Total + Time.elapsed(gameObj.LastSave))) + "). " : ". ") + "Making next...", sdk.colors.D2Bot.Gold); D2Bot.setProfile(null, null, require("../Tools/NameGen")()); CharData.delete(true); @@ -1038,7 +1038,7 @@ const Check = { break; default: - D2Bot.printToConsole("Kolbot-SoloPlay " + goal + " goal reached." + (gameObj ? " (" + (Tracker.formatTime(gameObj.Total + Tracker.timer(gameObj.LastSave))) + ")" : ""), sdk.colors.D2Bot.Gold); + D2Bot.printToConsole("Kolbot-SoloPlay " + goal + " goal reached." + (gameObj ? " (" + (Time.format(gameObj.Total + Time.elapsed(gameObj.LastSave))) + ")" : ""), sdk.colors.D2Bot.Gold); Developer.logPerformance && Tracker.update(); D2Bot.stop(); } diff --git a/libs/SoloPlay/Functions/ItemOverrides.js b/libs/SoloPlay/Functions/ItemOverrides.js index 3abcb028..e2c19eb8 100644 --- a/libs/SoloPlay/Functions/ItemOverrides.js +++ b/libs/SoloPlay/Functions/ItemOverrides.js @@ -351,7 +351,7 @@ Item.autoEquip = function (task = "") { } } - console.log("ÿc8Kolbot-SoloPlayÿc0: Exiting ÿc9" + task + "ÿc0. Time elapsed: " + Tracker.formatTime(getTickCount() - tick)); + console.log("ÿc8Kolbot-SoloPlayÿc0: Exiting ÿc9" + task + "ÿc0. Time elapsed: " + Time.format(getTickCount() - tick)); return true; }; @@ -607,7 +607,7 @@ Item.autoEquipSecondary = function (task = "") { } } - console.log("ÿc8Kolbot-SoloPlayÿc0: Exiting secondary auto equip. Time elapsed: " + Tracker.formatTime(getTickCount() - tick)); + console.log("ÿc8Kolbot-SoloPlayÿc0: Exiting secondary auto equip. Time elapsed: " + Time.format(getTickCount() - tick)); return true; }; diff --git a/libs/SoloPlay/Functions/LoaderOverrides.js b/libs/SoloPlay/Functions/LoaderOverrides.js index 300ba5f0..66a4f133 100644 --- a/libs/SoloPlay/Functions/LoaderOverrides.js +++ b/libs/SoloPlay/Functions/LoaderOverrides.js @@ -74,10 +74,10 @@ Loader.run = function () { SoloIndex.doneList.push(scriptName); // skip logging if we didn't actually finish it !SoloIndex.retryList.includes(scriptName) && Developer.logPerformance && Tracker.script(tick, scriptName, currentExp); - console.log("ÿc8Kolbot-SoloPlayÿc0: Old maxgametime: " + Tracker.formatTime(me.maxgametime)); + console.log("ÿc8Kolbot-SoloPlayÿc0: Old maxgametime: " + Time.format(me.maxgametime)); me.maxgametime += (getTickCount() - tick); - console.log("ÿc8Kolbot-SoloPlayÿc0: New maxgametime: " + Tracker.formatTime(me.maxgametime)); - console.log("ÿc8Kolbot-SoloPlayÿc0 :: ÿc8" + scriptName + "ÿc0 - ÿc7Duration: ÿc0" + Tracker.formatTime(getTickCount() - tick)); + console.log("ÿc8Kolbot-SoloPlayÿc0: New maxgametime: " + Time.format(me.maxgametime)); + console.log("ÿc8Kolbot-SoloPlayÿc0 :: ÿc8" + scriptName + "ÿc0 - ÿc7Duration: ÿc0" + Time.format(getTickCount() - tick)); // remove script function from function scope, so it can be cleared by GC if (this.scriptIndex < SoloIndex.scripts.length) { diff --git a/libs/SoloPlay/Functions/PatherOverrides.js b/libs/SoloPlay/Functions/PatherOverrides.js index 6536c9af..fdbc0d44 100644 --- a/libs/SoloPlay/Functions/PatherOverrides.js +++ b/libs/SoloPlay/Functions/PatherOverrides.js @@ -879,7 +879,7 @@ Pather.clearToExit = function (currentarea, targetarea, cleartype = true) { } while (me.area !== targetarea); } - console.log("ÿc8Kolbot-SoloPlayÿc0: End clearToExit. Time elapsed: " + Tracker.formatTime(getTickCount() - tick)); + console.log("ÿc8Kolbot-SoloPlayÿc0: End clearToExit. Time elapsed: " + Time.format(getTickCount() - tick)); return (me.area === targetarea); }; diff --git a/libs/SoloPlay/Functions/TownOverrides.js b/libs/SoloPlay/Functions/TownOverrides.js index 0b1556d9..5667e381 100644 --- a/libs/SoloPlay/Functions/TownOverrides.js +++ b/libs/SoloPlay/Functions/TownOverrides.js @@ -38,7 +38,7 @@ new Overrides.Override(Town, Town.drinkPots, function(orignal, type) { CharData.pots.get(objID).duration += (objDrank.quantity * 30 * 1000) - (getTickCount() - CharData.pots.get(objID).tick); } - console.log("ÿc9DrinkPotsÿc0 :: drank " + objDrank.quantity + " " + objDrank.potName + "s. Timer [" + Tracker.formatTime(CharData.pots.get(objID).duration) + "]"); + console.log("ÿc9DrinkPotsÿc0 :: drank " + objDrank.quantity + " " + objDrank.potName + "s. Timer [" + Time.format(CharData.pots.get(objID).duration) + "]"); } } } catch (e) { diff --git a/libs/SoloPlay/Threads/ToolsThread.js b/libs/SoloPlay/Threads/ToolsThread.js index f991b831..b2897459 100644 --- a/libs/SoloPlay/Threads/ToolsThread.js +++ b/libs/SoloPlay/Threads/ToolsThread.js @@ -104,7 +104,7 @@ function main () { chickenExit && D2Bot.updateChickens(); Config.LogExperience && Experience.log(); Developer.logPerformance && Tracker.update(); - console.log("ÿc8Run duration ÿc2" + Tracker.formatTime(getTickCount() - me.gamestarttime)); + console.log("ÿc8Run duration ÿc2" + Time.format(getTickCount() - me.gamestarttime)); this.stopDefault(); quit(); }; @@ -615,11 +615,11 @@ function main () { (gameTracker.Total + currInGame) ]; let [totalTime, totalInGame, totalDays] = [ - Tracker.formatTime(tTime), - Tracker.formatTime(tInGame), + Time.format(tTime), + Time.format(tInGame), Tracker.totalDays(tDays) ]; - timeStr += ("(Days: " + totalDays + ") (Total: " + totalTime + ") (IG: " + totalInGame + ") (OOG: " + Tracker.formatTime(gameTracker.OOG) + ")"); + timeStr += ("(Days: " + totalDays + ") (Total: " + totalTime + ") (IG: " + totalInGame + ") (OOG: " + Time.format(gameTracker.OOG) + ")"); } catch (e) { console.log(e); } diff --git a/libs/SoloPlay/Tools/Overlay.js b/libs/SoloPlay/Tools/Overlay.js index 16f1acac..94f37099 100644 --- a/libs/SoloPlay/Tools/Overlay.js +++ b/libs/SoloPlay/Tools/Overlay.js @@ -32,10 +32,10 @@ const Overlay = { this.GameTracker === undefined && (this.GameTracker = Tracker.readObj(Tracker.GTPath)); this.tick = getTickCount(); let currInGame = getTickCount() - me.gamestarttime; - let totalTime = Tracker.formatTime(this.GameTracker.Total + currInGame); - let totalInGame = Tracker.formatTime(this.GameTracker.InGame + currInGame); + let totalTime = Time.format(this.GameTracker.Total + currInGame); + let totalInGame = Time.format(this.GameTracker.InGame + currInGame); - return ("Total: ÿc0" + totalTime + "ÿc4 InGame: ÿc0" + totalInGame + "ÿc4 OOG: ÿc0" + Tracker.formatTime(this.GameTracker.OOG)); + return ("Total: ÿc0" + totalTime + "ÿc4 InGame: ÿc0" + totalInGame + "ÿc4 OOG: ÿc0" + Time.format(this.GameTracker.OOG)); }, timer: function () { diff --git a/libs/SoloPlay/Tools/Tracker.js b/libs/SoloPlay/Tools/Tracker.js index b32475d9..4fd3e120 100644 --- a/libs/SoloPlay/Tools/Tracker.js +++ b/libs/SoloPlay/Tools/Tracker.js @@ -112,31 +112,11 @@ const Tracker = { found && Tracker.writeObj(GameTracker, this.GTPath); }, - timer: function (tick) { - return getTickCount() - tick; - }, - - formatTime: function (milliseconds) { - let seconds = milliseconds / 1000; - let sec = (seconds % 60).toFixed(0); - let minutes = (Math.floor(seconds / 60) % 60).toFixed(0); - let hours = Math.floor(seconds / 3600).toFixed(0); - let timeString = hours.toString().padStart(2, "0") + ":" + minutes.toString().padStart(2, "0") + ":" + sec.toString().padStart(2, "0"); - - return timeString; - }, - totalDays: function (milliseconds) { let days = Math.floor(milliseconds / 86.4e6).toFixed(0); return days.toString().padStart(1, "0"); }, - logLeveling: function (obj) { - if (typeof obj === "object" && obj.hasOwnProperty("event") && obj.event === "level up") { - Tracker.leveling(); - } - }, - script: function (starttime, subscript, startexp) { const GameTracker = Tracker.readObj(Tracker.GTPath); @@ -145,13 +125,13 @@ const Tracker = { GameTracker.LastSave > getTickCount() && (GameTracker.LastSave = getTickCount()); const newTick = me.gamestarttime >= GameTracker.LastSave ? me.gamestarttime : GameTracker.LastSave; - GameTracker.InGame += Tracker.timer(newTick); - GameTracker.Total += Tracker.timer(newTick); + GameTracker.InGame += Time.elapsed(newTick); + GameTracker.Total += Time.elapsed(newTick); GameTracker.LastSave = getTickCount(); Tracker.writeObj(GameTracker, Tracker.GTPath); // csv file - const scriptTime = Tracker.timer(starttime); + const scriptTime = Time.elapsed(starttime); const currLevel = me.charlvl; const diffString = sdk.difficulty.nameOf(me.diff); const gainAMT = me.getStat(sdk.stats.Experience) - startexp; @@ -160,7 +140,7 @@ const Tracker = { const currentBuild = SetUp.currentBuild; const [GOLD, FR, CR, LR, PR] = [me.gold, me.realFR, me.realCR, me.realLR, me.realPR]; const string = ( - Tracker.formatTime(GameTracker.Total) + "," + Tracker.formatTime(GameTracker.InGame) + "," + Tracker.formatTime(scriptTime) + Time.format(GameTracker.Total) + "," + Time.format(GameTracker.InGame) + "," + Time.format(scriptTime) + "," + subscript + "," + currLevel + "," + gainAMT + "," + gainTime + "," + gainPercent + "," + diffString + "," + GOLD + "," + FR + "," + CR + "," + LR + "," + PR + "," + currentBuild + "\n" ); @@ -180,9 +160,9 @@ const Tracker = { const newSave = getTickCount(); const newTick = me.gamestarttime > GameTracker.LastSave ? me.gamestarttime : GameTracker.LastSave; - const splitTime = Tracker.timer(GameTracker.LastLevel); - GameTracker.InGame += Tracker.timer(newTick); - GameTracker.Total += Tracker.timer(newTick); + const splitTime = Time.elapsed(GameTracker.LastLevel); + GameTracker.InGame += Time.elapsed(newTick); + GameTracker.Total += Time.elapsed(newTick); GameTracker.LastLevel = newSave; GameTracker.LastSave = newSave; Tracker.writeObj(GameTracker, Tracker.GTPath); @@ -195,7 +175,7 @@ const Tracker = { const gainTime = gainAMT / (splitTime / 60000); const [GOLD, FR, CR, LR, PR] = [me.gold, me.realFR, me.realCR, me.realLR, me.realPR]; const string = ( - Tracker.formatTime(GameTracker.Total) + "," + Tracker.formatTime(GameTracker.InGame) + "," + Tracker.formatTime(splitTime) + "," + Time.format(GameTracker.Total) + "," + Time.format(GameTracker.InGame) + "," + Time.format(splitTime) + "," + areaName + "," + me.charlvl + "," + gainAMT + "," + gainTime + "," + diffString + "," + GOLD + "," + FR + "," + CR + "," + LR + "," + PR + "," + currentBuild + "\n" ); @@ -226,8 +206,8 @@ const Tracker = { const newTick = me.gamestarttime > GameTracker.LastSave ? me.gamestarttime : GameTracker.LastSave; GameTracker.OOG += oogTick; - GameTracker.InGame += Tracker.timer(newTick); - GameTracker.Total += (Tracker.timer(newTick) + oogTick); + GameTracker.InGame += Time.elapsed(newTick); + GameTracker.Total += (Time.elapsed(newTick) + oogTick); GameTracker.LastSave = getTickCount(); Tracker.writeObj(GameTracker, Tracker.GTPath); Tracker.tick = GameTracker.LastSave; From eae8e2dbf25a5fbe5158efe51fe8554fdc7a3ee4 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Thu, 6 Apr 2023 00:57:31 -0400 Subject: [PATCH 101/263] Update ItemOverrides.js - little bit of cleanup to fix redundant code - add some logging with easier to read format for equip --- libs/SoloPlay/Functions/ItemOverrides.js | 71 ++++++++++++++---------- 1 file changed, 43 insertions(+), 28 deletions(-) diff --git a/libs/SoloPlay/Functions/ItemOverrides.js b/libs/SoloPlay/Functions/ItemOverrides.js index e2c19eb8..c304cb78 100644 --- a/libs/SoloPlay/Functions/ItemOverrides.js +++ b/libs/SoloPlay/Functions/ItemOverrides.js @@ -305,47 +305,48 @@ Item.autoEquip = function (task = "") { while (items.length > 0) { items.sort(sortEq); const item = items.shift(); + if (!item.isInStorage) continue; let tier = NTIP.GetTier(item); + if (tier <= 0) continue; let bodyLoc = this.getBodyLoc(item); - if (tier > 0 && bodyLoc) { - for (let j = 0; j < bodyLoc.length; j += 1) { - const equippedItem = Item.getEquipped(bodyLoc[j]); - // rings are special - if (item.isInStorage && item.itemType === sdk.items.type.Ring) { + for (let loc of bodyLoc) { + const equippedItem = Item.getEquipped(loc); + if (equippedItem.classid === sdk.items.quest.KhalimsWill) continue; + // rings are special + if (item.itemType === sdk.items.type.Ring) { + Item.identify(item); + // have to pass in the specific location + tier = tierscore(item, 1, loc); + + if (tier > equippedItem.tierScore) { + if (!runEquip(item, loc, tier)) { + continue; + } + } + } else { + if (tier > equippedItem.tier) { Item.identify(item); - // have to pass in the specific location - tier = tierscore(item, 1, bodyLoc[j]); - if (tier > equippedItem.tierScore) { - if (!runEquip(item, bodyLoc[j], tier)) { + if (item.twoHanded && !me.barbarian) { + if (tier < Item.getEquipped(sdk.body.RightArm).tier + Item.getEquipped(sdk.body.LeftArm).tier) { continue; } + console.log("ÿc9" + task + "ÿc0 :: TwoHandedWep better than sum tier of currently equipped main + shield hand : " + item.fname + " Tier: " + tier); } - } else { - if (item.isInStorage && tier > equippedItem.tier && equippedItem.classid !== sdk.items.quest.KhalimsWill) { - Item.identify(item); - - if (item.twoHanded && !me.barbarian) { - if (tier < Item.getEquipped(sdk.body.RightArm).tier + Item.getEquipped(sdk.body.LeftArm).tier) { - continue; - } - console.log("ÿc9" + task + "ÿc0 :: TwoHandedWep better than sum tier of currently equipped main + shield hand : " + item.fname + " Tier: " + tier); - } - if (!me.barbarian && bodyLoc[j] === sdk.body.LeftArm && equippedItem.tier === -1 && Item.getEquipped(sdk.body.RightArm).twoHanded) { - if (tier < Item.getEquipped(sdk.body.RightArm).tier) { - continue; - } - console.log("ÿc9" + task + "ÿc0 :: TwoHandedWep not as good as what we want to equip on our shield hand : " + item.fname + " Tier: " + tier); - } - - if (!runEquip(item, bodyLoc[j], tier)) { + if (!me.barbarian && loc === sdk.body.LeftArm && equippedItem.tier === -1 && Item.getEquipped(sdk.body.RightArm).twoHanded) { + if (tier < Item.getEquipped(sdk.body.RightArm).tier) { continue; } + console.log("ÿc9" + task + "ÿc0 :: TwoHandedWep not as good as what we want to equip on our shield hand : " + item.fname + " Tier: " + tier); + } - break; + if (!runEquip(item, loc, tier)) { + continue; } + + break; } } } @@ -370,6 +371,19 @@ Item.equip = function (item, bodyLoc) { // failed to open cube if (item.isInCube && !Cubing.openCube()) return false; + let currentEquipped = me.getItemsEx() + .filter(item => item.isEquipped && item.bodylocation === bodyLoc) + .first(); + if (currentEquipped) { + console.debug( + "ÿc9Equipÿc0 ::\n" + + "ÿc9 - Equippingÿc0: " + item.prettyPrint + "\n" + + "ÿc9 - Tierÿc0: " + NTIP.GetTier(item) + "\n" + + "ÿc9 - Currently Equippedÿc0: " + currentEquipped.prettyPrint + "\n" + + "ÿc9 - Tierÿc0: " + NTIP.GetTier(currentEquipped) + "\n" + + "ÿc9 - BodyLocÿc0: " + bodyLoc + ); + } let rolledBack = false; // todo: sometimes rings get bugged with the higher tier ring ending up on the wrong finger, if this happens swap them @@ -411,6 +425,7 @@ Item.equip = function (item, bodyLoc) { } if (cursorItem && !cursorItem.shouldKeep()) { + console.debug("ÿc9Equipÿc0 :: Dropping " + cursorItem.prettyPrint); cursorItem.drop(); } } From 01a977206679852bf8862090271b0e2fc6eec79d Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Thu, 6 Apr 2023 16:59:00 -0400 Subject: [PATCH 102/263] Keep updated with the core libs - don't need these overrides anymore --- .../BuildFiles/druid/druid.StartBuild.js | 2 +- libs/SoloPlay/Functions/SkillOverrides.js | 39 ------------------- 2 files changed, 1 insertion(+), 40 deletions(-) diff --git a/libs/SoloPlay/BuildFiles/druid/druid.StartBuild.js b/libs/SoloPlay/BuildFiles/druid/druid.StartBuild.js index 6fcba3af..e4fa3e28 100644 --- a/libs/SoloPlay/BuildFiles/druid/druid.StartBuild.js +++ b/libs/SoloPlay/BuildFiles/druid/druid.StartBuild.js @@ -68,7 +68,7 @@ }); build.AutoBuildTemplate[13] = buildAutoBuildTempObj(() => { - if (Skill.canUse(sdk.skills.Fissure) && Skill.skills.all[sdk.skills.Fissure].hardpoints) { + if (Skill.canUse(sdk.skills.Fissure)) { Config.AttackSkill = [-1, sdk.skills.Fissure, sdk.skills.Firestorm, sdk.skills.Fissure, sdk.skills.Firestorm, 0, 0]; } }); diff --git a/libs/SoloPlay/Functions/SkillOverrides.js b/libs/SoloPlay/Functions/SkillOverrides.js index a0032dc0..a10c906e 100644 --- a/libs/SoloPlay/Functions/SkillOverrides.js +++ b/libs/SoloPlay/Functions/SkillOverrides.js @@ -19,45 +19,6 @@ Skill.casterSkills = [ /* sdk.skills.WakeofFire, */ sdk.skills.LightningSentry, sdk.skills.DeathSentry ]; -new Overrides.Override(Skill, Skill.getRange, function (orignal, skillId) { - switch (skillId) { - case sdk.skills.ChargedBolt: - return !!this.usePvpRange ? 11 : 6; - case sdk.skills.Lightning: - case sdk.skills.BoneSpear: - case sdk.skills.BoneSpirit: - return !!this.usePvpRange ? 30 : 15; - case sdk.skills.FireBall: - case sdk.skills.FireWall: - case sdk.skills.ChainLightning: - case sdk.skills.Meteor: - case sdk.skills.Blizzard: - case sdk.skills.MindBlast: - return !!this.usePvpRange ? 30 : 20; - default: - return orignal(skillId); - } -}).apply(); - -// Thank you @sakana -Skill.getManaCost = function (skillId = -1) { - // first skills dont use mana - if (skillId < 6) return 0; - // Decoy wasn't reading from skill bin - if (skillId === sdk.skills.Decoy) return Math.max(19.75 - (0.75 * me.getSkill(sdk.skills.Decoy, sdk.skills.subindex.SoftPoints)), 1); - if (this.manaCostList.hasOwnProperty(skillId)) return this.manaCostList[skillId]; - - let skillLvl = me.getSkill(skillId, sdk.skills.subindex.SoftPoints), effectiveShift = [1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024]; - let lvlmana = getBaseStat("skills", skillId, "lvlmana") === 65535 ? -1 : getBaseStat("skills", skillId, "lvlmana"); // Correction for skills that need less mana with levels (kolton) - let ret = Math.max((getBaseStat("skills", skillId, "mana") + lvlmana * (skillLvl - 1)) * (effectiveShift[getBaseStat(3, skillId, "manashift")] / 256), getBaseStat("skills", skillId, "minmana")); - - if (!this.manaCostList.hasOwnProperty(skillId)) { - this.manaCostList[skillId] = ret; - } - - return ret; -}; - // Cast a skill on self, Unit or coords. Always use packet casting for caster skills becasue it's more stable. Skill.cast = function (skillId, hand, x, y, item) { switch (true) { From 9b5b56fe5cc7ca57757be3b0d68e45b5b02e6ca4 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Tue, 18 Apr 2023 16:07:03 -0400 Subject: [PATCH 103/263] Update NTIPOverrides.js - fix linecheck --- libs/SoloPlay/Functions/NTIPOverrides.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/libs/SoloPlay/Functions/NTIPOverrides.js b/libs/SoloPlay/Functions/NTIPOverrides.js index b9452bb0..fc76377f 100644 --- a/libs/SoloPlay/Functions/NTIPOverrides.js +++ b/libs/SoloPlay/Functions/NTIPOverrides.js @@ -145,9 +145,9 @@ NTIP.buildFinalGear = function (arr) { switch (true) { case !lineCheck.includes("tier"): - case line.includes("merctier"): - case line.includes("secondarytier"): - case line.includes("charmtier"): + case lineCheck.includes("merctier"): + case lineCheck.includes("secondarytier"): + case lineCheck.includes("charmtier"): continue; } From 00eae284f2206ddd7fbc094b3482d36551163948 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Wed, 19 Apr 2023 16:45:06 -0400 Subject: [PATCH 104/263] Update ItemOverrides.js - fix hordeing final gear items --- libs/SoloPlay/Functions/ItemOverrides.js | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/libs/SoloPlay/Functions/ItemOverrides.js b/libs/SoloPlay/Functions/ItemOverrides.js index c304cb78..7cb7379e 100644 --- a/libs/SoloPlay/Functions/ItemOverrides.js +++ b/libs/SoloPlay/Functions/ItemOverrides.js @@ -193,9 +193,21 @@ Item.autoEquipCheck = function (item, basicCheck = false) { return true; } else { + /** + * @param {ItemUnit} item + * @returns {boolean} + */ + const checkForBetterItem = (item) => { + let betterItem = me.getItemsEx() + .filter(el => el.isInStorage && el.gid !== item.gid && el.identified && Item.getBodyLoc(el).includes(loc)) + .sort((a, b) => NTIP.GetTier(b) - NTIP.GetTier(a)) + .find(el => NTIP.GetTier(el) > tier); + return !!betterItem; + }; // keep wanted final gear items if (NTIP.CheckItem(item, NTIP.FinalGear.list) === Pickit.Result.WANTED) { - return true; + // don't horde items we can't equip + return !checkForBetterItem(item); } let [lvlReq, strReq, dexReq] = [item.getStat(sdk.stats.LevelReq), item.strreq, item.dexreq]; @@ -207,10 +219,7 @@ Item.autoEquipCheck = function (item, basicCheck = false) { // if we can't equip it, but it's a good item, keep it as long as we have space for it // lets double check that this is the highest tied'd item of this type in our storage - let betterItem = me.getItemsEx() - .filter(el => el.isInStorage && el.gid !== item.gid && el.identified && Item.getBodyLoc(el).includes(bodyLoc[i])) - .sort((a, b) => NTIP.GetTier(b) - NTIP.GetTier(a)) - .find(el => NTIP.GetTier(el) > tier); + let betterItem = checkForBetterItem(item); if (!betterItem) return true; return Storage.Stash.CanFit(item) && Storage.Stash.UsedSpacePercent() < 65; From d0272a0fa7617b8ba2b0bcb550420d4e76c02956 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Wed, 19 Apr 2023 21:18:48 -0400 Subject: [PATCH 105/263] Update ItemOverrides.js - Fix `Item.autoEquipCheck`, changed to `for...of` loop and removed unnecessary nesting --- libs/SoloPlay/Functions/ItemOverrides.js | 91 ++++++++++++------------ 1 file changed, 45 insertions(+), 46 deletions(-) diff --git a/libs/SoloPlay/Functions/ItemOverrides.js b/libs/SoloPlay/Functions/ItemOverrides.js index 7cb7379e..6466e497 100644 --- a/libs/SoloPlay/Functions/ItemOverrides.js +++ b/libs/SoloPlay/Functions/ItemOverrides.js @@ -167,63 +167,62 @@ Item.autoEquipCheck = function (item, basicCheck = false) { if (!Config.AutoEquip) return true; let tier = NTIP.GetTier(item); + if (tier <= 0) return false; let bodyLoc = this.getBodyLoc(item); - if (tier > 0 && bodyLoc) { - for (let i = 0; i < bodyLoc.length; i += 1) { - let equippedItem = Item.getEquipped(bodyLoc[i]); + for (let loc of bodyLoc) { + let equippedItem = Item.getEquipped(loc); - // rings are special - if (item.isInStorage && item.itemType === sdk.items.type.Ring) { - // have to pass in the specific location - tier = tierscore(item, 1, bodyLoc[i]); + // rings are special + if (item.isInStorage && item.itemType === sdk.items.type.Ring) { + // have to pass in the specific location + tier = tierscore(item, 1, loc); - if (tier > equippedItem.tierScore) { - return true; + if (tier > equippedItem.tierScore) { + return true; + } + } else if (tier > equippedItem.tier && (basicCheck ? true : this.canEquip(item) || !item.identified)) { + if (Item.canEquip(item)) { + if (item.twoHanded && !me.barbarian) { + if (tier < Item.getEquipped(sdk.body.RightArm).tier + Item.getEquipped(sdk.body.LeftArm).tier) return false; } - } else if (tier > equippedItem.tier && (basicCheck ? true : this.canEquip(item) || !item.identified)) { - if (Item.canEquip(item)) { - if (item.twoHanded && !me.barbarian) { - if (tier < Item.getEquipped(sdk.body.RightArm).tier + Item.getEquipped(sdk.body.LeftArm).tier) return false; - } - if (!me.barbarian && bodyLoc[i] === sdk.body.LeftArm && Item.getEquipped(bodyLoc[i]).tier === -1) { - if (Item.getEquipped(sdk.body.RightArm).twoHanded && tier < Item.getEquipped(sdk.body.RightArm).tier) return false; - } + if (!me.barbarian && loc === sdk.body.LeftArm && Item.getEquipped(loc).tier === -1) { + if (Item.getEquipped(sdk.body.RightArm).twoHanded && tier < Item.getEquipped(sdk.body.RightArm).tier) return false; + } - return true; - } else { - /** - * @param {ItemUnit} item - * @returns {boolean} - */ - const checkForBetterItem = (item) => { - let betterItem = me.getItemsEx() - .filter(el => el.isInStorage && el.gid !== item.gid && el.identified && Item.getBodyLoc(el).includes(loc)) - .sort((a, b) => NTIP.GetTier(b) - NTIP.GetTier(a)) - .find(el => NTIP.GetTier(el) > tier); - return !!betterItem; - }; - // keep wanted final gear items - if (NTIP.CheckItem(item, NTIP.FinalGear.list) === Pickit.Result.WANTED) { - // don't horde items we can't equip - return !checkForBetterItem(item); - } + return true; + } else { + /** + * @param {ItemUnit} item + * @returns {boolean} + */ + const checkForBetterItem = (item) => { + let betterItem = me.getItemsEx() + .filter(el => el.isInStorage && el.gid !== item.gid && el.identified && Item.getBodyLoc(el).includes(loc)) + .sort((a, b) => NTIP.GetTier(b) - NTIP.GetTier(a)) + .find(el => NTIP.GetTier(el) > tier); + return !!betterItem; + }; + // keep wanted final gear items + if (NTIP.CheckItem(item, NTIP.FinalGear.list) === Pickit.Result.WANTED) { + // don't horde items we can't equip + return !checkForBetterItem(item); + } - let [lvlReq, strReq, dexReq] = [item.getStat(sdk.stats.LevelReq), item.strreq, item.dexreq]; + let [lvlReq, strReq, dexReq] = [item.getStat(sdk.stats.LevelReq), item.strreq, item.dexreq]; - // todo - bit hacky, better way would be to track what stats are going to be allocated next - if ((lvlReq - me.charlvl > 5) || (strReq - me.trueStr > 10) || (dexReq - me.trueDex > 10)) { - return false; - } + // todo - bit hacky, better way would be to track what stats are going to be allocated next + if ((lvlReq - me.charlvl > 5) || (strReq - me.trueStr > 10) || (dexReq - me.trueDex > 10)) { + return false; + } - // if we can't equip it, but it's a good item, keep it as long as we have space for it - // lets double check that this is the highest tied'd item of this type in our storage - let betterItem = checkForBetterItem(item); - if (!betterItem) return true; + // if we can't equip it, but it's a good item, keep it as long as we have space for it + // lets double check that this is the highest tied'd item of this type in our storage + let betterItem = checkForBetterItem(item); + if (!betterItem) return true; - return Storage.Stash.CanFit(item) && Storage.Stash.UsedSpacePercent() < 65; - } + return Storage.Stash.CanFit(item) && Storage.Stash.UsedSpacePercent() < 65; } } } From 0c9341b7e7b1c5644963c3a71a94cc6fde9116f5 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Thu, 20 Apr 2023 11:31:38 -0400 Subject: [PATCH 106/263] Update Me.js - create `me.equipped` map to keep track of our items easier --- libs/SoloPlay/Functions/Me.js | 163 ++++++++++++++++++++++++++++++++++ 1 file changed, 163 insertions(+) diff --git a/libs/SoloPlay/Functions/Me.js b/libs/SoloPlay/Functions/Me.js index a67bf8a7..de2486d9 100644 --- a/libs/SoloPlay/Functions/Me.js +++ b/libs/SoloPlay/Functions/Me.js @@ -232,6 +232,169 @@ if (!me.hasOwnProperty("data")) { }); } +if (!me.hasOwnProperty("equipped")) { + me.equipped = (function () { + const defaultsMap = new Map([ + ["gid", -1], + ["classid", -1], + ["name", ""], + ["code", ""], + ["fname", ""], + ["quality", -1], + ["ethereal", false], + ["itemType", -1], + ["strreq", -1], + ["dexreq", -1], + ["lvlreq", -1], + ["sockets", -1], + ["prefixnum", -1], + ["suffixnum", -1], + ]); + + /** + * @constructor + * @param {ItemUnit} item + */ + function EquippedItem (item) { + /** @type {ItemUnit} */ + this._item = (item instanceof Unit) + ? copyUnit(item) + : null; + this._gid = item ? item.gid : -1; + this._bodylocation = item ? item.bodylocation : -1; + + return new Proxy(this, { + get: function (target, prop) { + if (prop in target) { + return target[prop]; + } + + if (target._item && prop in target._item) { + return target._item[prop]; + } + + return defaultsMap.get(prop); + } + }); + } + + Object.defineProperties(EquippedItem.prototype, { + location: { + /** @this EquippedItem */ + get: function () { + return this._item ? this._item.bodylocation : -1; + }, + }, + durability: { + /** @this EquippedItem */ + get: function () { + return this._item ? this._item.durabilityPercent : -1; + }, + }, + tier: { + /** @this EquippedItem */ + get: function () { + return this._item ? NTIP.GetTier(this._item) : -1; + }, + }, + tierScore: { + /** @this EquippedItem */ + get: function () { + return this._item ? tierscore(this._item, 1, this._bodylocation) : -1; + }, + }, + secondaryTier: { + /** @this EquippedItem */ + get: function () { + return this._item ? NTIP.GetSecondaryTier(this._item) : -1; + }, + }, + socketed: { + /** @this EquippedItem */ + get: function () { + return this._item ? this._item.getItemsEx().length > 0 : false; + }, + }, + }); + + EquippedItem.prototype.twoHandedCheck = function (strict = false) { + return this._item + ? strict + ? this._item.strictlyTwoHanded + : this._item.twoHanded + : false; + }; + + EquippedItem.prototype.getStat = function (stat, subid) { + return this._item ? this._item.getStat(stat, subid) : -1; + }; + + EquippedItem.prototype.getStatEx = function (stat, subid) { + return this._item ? this._item.getStatEx(stat, subid) : -1; + }; + + /** @type {Map} */ + const bodyMap = new Map(); + bodyMap.set(sdk.body.Head, new EquippedItem()); + bodyMap.set(sdk.body.Neck, new EquippedItem()); + bodyMap.set(sdk.body.Armor, new EquippedItem()); + bodyMap.set(sdk.body.RightArm, new EquippedItem()); + bodyMap.set(sdk.body.LeftArm, new EquippedItem()); + bodyMap.set(sdk.body.Gloves, new EquippedItem()); + bodyMap.set(sdk.body.RingRight, new EquippedItem()); + bodyMap.set(sdk.body.RingLeft, new EquippedItem()); + bodyMap.set(sdk.body.Belt, new EquippedItem()); + bodyMap.set(sdk.body.Feet, new EquippedItem()); + bodyMap.set(sdk.body.RightArmSecondary, new EquippedItem()); + bodyMap.set(sdk.body.LeftArmSecondary, new EquippedItem()); + + // const _dummy = new EquippedItem(); + + return { + /** + * @param {number} bodylocation + * @returns {EquippedItem} + */ + get: function (bodylocation) { + // if (!bodyMap.has(bodylocation)) return _dummy; + let item = bodyMap.get(bodylocation); + if (item._gid === -1 || (item._gid !== item.gid || (item._bodylocation !== item.location && me.weaponswitch !== sdk.player.slot.Secondary))) { + // item has changed - find the new item + let newItem = me.getItemsEx() + .filter((el) => el.isEquipped && el.bodylocation === bodylocation) + .first(); + bodyMap.set(bodylocation, new EquippedItem(newItem)); + } + return bodyMap.get(bodylocation); + }, + /** + * @param {number} bodylocation + * @returns {boolean} + */ + has: function (bodylocation) { + return bodyMap.has(bodylocation); + }, + /** + * @param {number} bodylocation + * @param {ItemUnit} item + */ + set: function (bodylocation, item) { + if (bodyMap.has(bodylocation) && item instanceof Unit) { + bodyMap.set(bodylocation, new EquippedItem(item)); + } + }, + /** + * @description Initializes the equipped item map with the items currently equipped + */ + init: function () { + me.getItemsEx() + .filter(item => item.isEquipped) + .forEach(item => bodyMap.set(item.bodylocation, new EquippedItem(item))); + }, + }; + })(); +} + /** @returns {boolean} */ me.canTpToTown = function () { // can't tp if dead - or not currently enabled to From a693a8ce1c2c41c7ded48831756ff3d023847ad7 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Thu, 20 Apr 2023 16:09:08 -0400 Subject: [PATCH 107/263] Update to use `me.equipped` and `me.accessToAct` - `me.accessToAct` was added a couple commits back to the core and depreciates the use of `Pather.accessToAct`, swap over to using it - Replace usage of `Item.getEquipped` with using the new `me.equipped` map, it provides more efficient get capability with cacheing our equipped items while retaining the ability to handle default values - re-write the tierWieghts object to be a map instead and clean up most of the calculations --- libs/SoloPlay/BuildFiles/Runewords/Honor.js | 2 +- libs/SoloPlay/BuildFiles/Runewords/Lore.js | 4 +- libs/SoloPlay/BuildFiles/Runewords/Myth.js | 2 +- libs/SoloPlay/BuildFiles/Runewords/Stealth.js | 2 +- libs/SoloPlay/BuildFiles/Runewords/Steel.js | 4 +- .../paladin/paladin.HammerdinBuild.js | 2 +- .../paladin/paladin.LevelingBuild.js | 2 +- libs/SoloPlay/Config/Amazon.js | 32 +- libs/SoloPlay/Config/Assassin.js | 26 +- libs/SoloPlay/Config/Barbarian.js | 56 +- libs/SoloPlay/Config/Druid.js | 28 +- libs/SoloPlay/Config/Necromancer.js | 34 +- libs/SoloPlay/Config/Paladin.js | 43 +- libs/SoloPlay/Config/Sorceress.js | 32 +- .../ClassAttackOverrides/AmazonAttacks-WIP.js | 2 +- libs/SoloPlay/Functions/DynamicTiers.js | 600 ++++++++---------- libs/SoloPlay/Functions/Globals.js | 19 +- libs/SoloPlay/Functions/ItemOverrides.js | 283 ++++----- libs/SoloPlay/Functions/ItemPrototypes.js | 20 +- libs/SoloPlay/Functions/NPCAction.js | 6 +- libs/SoloPlay/Functions/Quest.js | 67 +- libs/SoloPlay/Functions/SoloEvents.js | 2 +- libs/SoloPlay/Scripts/bloodraven.js | 6 +- libs/SoloPlay/Tools/SoloIndex.js | 76 +-- libs/SoloPlay/index.d.ts | 33 + 25 files changed, 652 insertions(+), 731 deletions(-) diff --git a/libs/SoloPlay/BuildFiles/Runewords/Honor.js b/libs/SoloPlay/BuildFiles/Runewords/Honor.js index dbe3612e..3f8dce5a 100644 --- a/libs/SoloPlay/BuildFiles/Runewords/Honor.js +++ b/libs/SoloPlay/BuildFiles/Runewords/Honor.js @@ -16,7 +16,7 @@ // Have Sol rune before looking for base if (me.getItem(sdk.items.runes.Sol)) { if (!Check.haveBase("sword", 5)) { - if (Pather.accessToAct(5) && !me.getQuest(sdk.quest.id.SiegeOnHarrogath, sdk.quest.states.Completed)) { + if (me.accessToAct(5) && !me.getQuest(sdk.quest.id.SiegeOnHarrogath, sdk.quest.states.Completed)) { NTIP.addLine("((me.diff == 0 && [name] == flamberge) || (me.diff > 0 && [name] == zweihander) || (me.diff == 2 && [name] == colossussword)) && [flag] != ethereal && [quality] >= normal && [quality] <= superior && [level] >= 41 # [sockets] == 0 # [maxquantity] == 1"); } else { NTIP.addLine("([name] == flamberge || [name] == zweihander || [name] == dimensionalblade || [name] == phaseblade || [name] == colossussword) && [flag] != ethereal && [quality] == normal && [level] >= 41 # [sockets] == 0 # [maxquantity] == 1"); diff --git a/libs/SoloPlay/BuildFiles/Runewords/Lore.js b/libs/SoloPlay/BuildFiles/Runewords/Lore.js index cc85c49c..a1aec71d 100644 --- a/libs/SoloPlay/BuildFiles/Runewords/Lore.js +++ b/libs/SoloPlay/BuildFiles/Runewords/Lore.js @@ -35,7 +35,7 @@ NTIP.buildList(classLoreHelm); // Normal Helms - if (Item.getEquipped(sdk.body.Head).tier < 150) { + if (me.equipped.get(sdk.body.Head).tier < 150) { NTIP.buildList(loreHelm); } @@ -84,7 +84,7 @@ NTIP.buildList(classLoreHelm); // Normal Helms - if (Item.getEquipped(sdk.body.Head).tier < 150) { + if (me.equipped.get(sdk.body.Head).tier < 150) { NTIP.buildList(loreHelm); } diff --git a/libs/SoloPlay/BuildFiles/Runewords/Myth.js b/libs/SoloPlay/BuildFiles/Runewords/Myth.js index 26335ef6..dde3feb9 100644 --- a/libs/SoloPlay/BuildFiles/Runewords/Myth.js +++ b/libs/SoloPlay/BuildFiles/Runewords/Myth.js @@ -17,7 +17,7 @@ } // Have Hel rune and currently equipped armor is low tier - if (me.getItem(sdk.items.runes.Hel) && Item.getEquipped(sdk.body.Armor).tier < 200) { + if (me.getItem(sdk.items.runes.Hel) && me.equipped.get(sdk.body.Armor).tier < 200) { NTIP.addLine("[name] == breastplate && [flag] != ethereal && [quality] >= normal && [quality] <= superior # [sockets] == 3 # [maxquantity] == 1"); } diff --git a/libs/SoloPlay/BuildFiles/Runewords/Stealth.js b/libs/SoloPlay/BuildFiles/Runewords/Stealth.js index cfb3bc23..a49ec5f5 100644 --- a/libs/SoloPlay/BuildFiles/Runewords/Stealth.js +++ b/libs/SoloPlay/BuildFiles/Runewords/Stealth.js @@ -13,7 +13,7 @@ ]; NTIP.buildList(stealthArmor); - if (Item.getEquipped(sdk.body.Armor).tier < 200) { + if (me.equipped.get(sdk.body.Armor).tier < 200) { NTIP.addLine("[name] == breastplate && [flag] != ethereal && [quality] >= normal && [quality] <= superior # [sockets] == 2 # [maxquantity] == 1"); } diff --git a/libs/SoloPlay/BuildFiles/Runewords/Steel.js b/libs/SoloPlay/BuildFiles/Runewords/Steel.js index bc8558f3..3e554211 100644 --- a/libs/SoloPlay/BuildFiles/Runewords/Steel.js +++ b/libs/SoloPlay/BuildFiles/Runewords/Steel.js @@ -5,9 +5,9 @@ ]; NTIP.buildList(Steel); - if (Item.getEquipped(sdk.body.LeftArm).tier < 500 && Item.getEquipped(sdk.body.LeftArm).tier > 395) { + if (me.equipped.get(sdk.body.LeftArm).tier < 500 && me.equipped.get(sdk.body.LeftArm).tier > 395) { NTIP.addLine("[type] == sword && [flag] != ethereal && [quality] >= normal && [quality] <= superior && [wsm] <= 10 && [strreq] <= 150 && [class] == elite # [sockets] == 2 # [maxquantity] == 1"); - } else if (Item.getEquipped(sdk.body.LeftArm).tier < 500 && Item.getEquipped(sdk.body.LeftArm).tier > 278) { + } else if (me.equipped.get(sdk.body.LeftArm).tier < 500 && me.equipped.get(sdk.body.LeftArm).tier > 278) { NTIP.addLine("[type] == sword && [flag] != ethereal && [quality] >= normal && [quality] <= superior && [wsm] <= 10 && [strreq] <= 150 && [class] > normal # [sockets] == 2 # [maxquantity] == 1"); } else { NTIP.addLine("[type] == sword && [flag] != ethereal && [quality] == superior && [wsm] <= 10 && [strreq] <= 150 # [enhanceddamage] >= 10 && [sockets] == 2 # [maxquantity] == 1"); diff --git a/libs/SoloPlay/BuildFiles/paladin/paladin.HammerdinBuild.js b/libs/SoloPlay/BuildFiles/paladin/paladin.HammerdinBuild.js index 188e5584..968e6dee 100644 --- a/libs/SoloPlay/BuildFiles/paladin/paladin.HammerdinBuild.js +++ b/libs/SoloPlay/BuildFiles/paladin/paladin.HammerdinBuild.js @@ -73,7 +73,7 @@ Config.AttackSkill = [-1, sdk.skills.BlessedHammer, sdk.skills.Concentration, sdk.skills.BlessedHammer, sdk.skills.Concentration, sdk.skills.HolyBolt, sdk.skills.Concentration]; Config.LowManaSkill = [0, sdk.skills.Concentration]; - if (me.hell && !Pather.accessToAct(5)) { + if (me.hell && !me.accessToAct(5)) { Config.SkipImmune = ["magic"]; } Config.BeltColumn = ["hp", "hp", "mp", "rv"]; diff --git a/libs/SoloPlay/BuildFiles/paladin/paladin.LevelingBuild.js b/libs/SoloPlay/BuildFiles/paladin/paladin.LevelingBuild.js index bd72395b..dd3fa37f 100644 --- a/libs/SoloPlay/BuildFiles/paladin/paladin.LevelingBuild.js +++ b/libs/SoloPlay/BuildFiles/paladin/paladin.LevelingBuild.js @@ -92,7 +92,7 @@ Config.BeltColumn = ["hp", "hp", "mp", "mp"]; Config.HPBuffer = me.expansion ? 2 : 4; Config.MPBuffer = me.expansion && me.charlvl < 80 ? 6 : me.classic ? 5 : 2; - (me.hell && !Pather.accessToAct(5)) && (Config.SkipImmune = ["magic"]); + (me.hell && !me.accessToAct(5)) && (Config.SkipImmune = ["magic"]); SetUp.belt(); } } diff --git a/libs/SoloPlay/Config/Amazon.js b/libs/SoloPlay/Config/Amazon.js index ef8ab5b8..a47743cc 100644 --- a/libs/SoloPlay/Config/Amazon.js +++ b/libs/SoloPlay/Config/Amazon.js @@ -74,17 +74,17 @@ Config.imbueables = [ { name: sdk.items.MaidenJavelin, condition: () => me.normal && me.expansion }, { name: sdk.items.CeremonialJavelin, condition: () => !me.normal && (me.charlvl < 48 || me.trueStr < 107 || me.trueDex < 151) && me.expansion }, - { name: sdk.items.MatriarchalJavelin, condition: () => (Item.getEquipped(sdk.body.RightArm).tier < 100000 && me.trueStr >= 107 && me.trueDex >= 151 && me.expansion) }, - { name: sdk.items.Belt, condition: () => (me.normal && (Item.getEquipped(sdk.body.RightArm).tier > 100000 || me.classic)) }, - { name: sdk.items.MeshBelt, condition: () => (!me.normal && me.charlvl < 46 && me.trueStr > 58 && (Item.getEquipped(sdk.body.RightArm).tier > 100000 || me.classic)) }, - { name: sdk.items.SpiderwebSash, condition: () => (!me.normal && me.trueStr > 50 && (Item.getEquipped(sdk.body.RightArm).tier > 100000 || me.classic)) }, + { name: sdk.items.MatriarchalJavelin, condition: () => (me.equipped.get(sdk.body.RightArm).tier < 100000 && me.trueStr >= 107 && me.trueDex >= 151 && me.expansion) }, + { name: sdk.items.Belt, condition: () => (me.normal && (me.equipped.get(sdk.body.RightArm).tier > 100000 || me.classic)) }, + { name: sdk.items.MeshBelt, condition: () => (!me.normal && me.charlvl < 46 && me.trueStr > 58 && (me.equipped.get(sdk.body.RightArm).tier > 100000 || me.classic)) }, + { name: sdk.items.SpiderwebSash, condition: () => (!me.normal && me.trueStr > 50 && (me.equipped.get(sdk.body.RightArm).tier > 100000 || me.classic)) }, ]; let imbueArr = SetUp.imbueItems(); !me.smith && NTIP.buildList(imbueArr); - if (Item.getEquipped(sdk.body.RightArm).tier < 100000) { + if (me.equipped.get(sdk.body.RightArm).tier < 100000) { Config.GambleItems.push("Javelin"); Config.GambleItems.push("Pilum"); Config.GambleItems.push("Short Spear"); @@ -94,7 +94,7 @@ switch (me.gametype) { case sdk.game.gametype.Classic: // Res shield - if (Item.getEquipped(sdk.body.LeftArm).tier < 487) { + if (me.equipped.get(sdk.body.LeftArm).tier < 487) { includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/PDiamondShield.js"); } @@ -116,11 +116,11 @@ /* Crafting */ // Going with Blood but TODO is test HitPower vs Blood - if (Item.getEquipped(sdk.body.Neck).tier < 100000) { + if (me.equipped.get(sdk.body.Neck).tier < 100000) { Config.Recipes.push([Recipe.Blood.Amulet]); } - if (Item.getEquipped(sdk.body.RingLeft).tier < 100000) { + if (me.equipped.get(sdk.body.RingLeft).tier < 100000) { Config.Recipes.push([Recipe.Blood.Ring]); } @@ -155,9 +155,9 @@ } // Spirit shield - while lvling and Wf final switch - if ((me.ladder || Developer.addLadderRW) && (Item.getEquipped(5).tier < 1000 + if ((me.ladder || Developer.addLadderRW) && (me.equipped.get(5).tier < 1000 && (["Witchyzon", "Wfzon", "Faithbowzon"].indexOf(SetUp.currentBuild) === -1) - || (SetUp.finalBuild === "Wfzon" && Item.getEquipped(12).prefixnum !== sdk.locale.items.Spirit))) { + || (SetUp.finalBuild === "Wfzon" && me.equipped.get(12).prefixnum !== sdk.locale.items.Spirit))) { includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/SpiritShield.js"); } @@ -185,7 +185,7 @@ } // Spirit shield - if ((me.ladder || Developer.addLadderRW) && (Item.getEquipped(sdk.body.LeftArm).tier < 1000 || Item.getEquipped(sdk.body.LeftArmSecondary).prefixnum !== sdk.locale.items.Spirit)) { + if ((me.ladder || Developer.addLadderRW) && (me.equipped.get(sdk.body.LeftArm).tier < 1000 || me.equipped.get(sdk.body.LeftArmSecondary).prefixnum !== sdk.locale.items.Spirit)) { includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/SpiritShield.js"); } @@ -210,17 +210,17 @@ } // Lore - if (Item.getEquipped(sdk.body.Head).tier < 250) { + if (me.equipped.get(sdk.body.Head).tier < 250) { includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/Lore.js"); } // Ancients' Pledge - if (Item.getEquipped(sdk.body.LeftArm).tier < 500 && ["Witchyzon", "Wfzon", "Faithbowzon"].indexOf(SetUp.currentBuild) === -1) { + if (me.equipped.get(sdk.body.LeftArm).tier < 500 && ["Witchyzon", "Wfzon", "Faithbowzon"].indexOf(SetUp.currentBuild) === -1) { includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/AncientsPledge.js"); } // Treachery - if (Item.getEquipped(sdk.body.Armor).tier < 634) { + if (me.equipped.get(sdk.body.Armor).tier < 634) { includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/Treachery.js"); } @@ -240,12 +240,12 @@ } // Smoke - if (Item.getEquipped(sdk.body.Armor).tier < 634) { + if (me.equipped.get(sdk.body.Armor).tier < 634) { includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/Smoke.js"); } // Stealth - if (Item.getEquipped(sdk.body.Armor).tier < 233) { + if (me.equipped.get(sdk.body.Armor).tier < 233) { includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/Stealth.js"); } diff --git a/libs/SoloPlay/Config/Assassin.js b/libs/SoloPlay/Config/Assassin.js index 025eaa9f..fba7f783 100644 --- a/libs/SoloPlay/Config/Assassin.js +++ b/libs/SoloPlay/Config/Assassin.js @@ -82,11 +82,11 @@ Config.imbueables = [ { name: sdk.items.Claws, condition: () => (me.normal) }, - { name: sdk.items.HandScythe, condition: () => (!me.normal && Item.getEquipped(sdk.body.RightArm).tier < 777 && (me.trueStr < 79 || me.trueDex < 79)) }, - { name: sdk.items.GreaterTalons, condition: () => (Item.getEquipped(sdk.body.RightArm).tier < 777 && me.trueStr >= 79 && me.trueDex >= 79) }, - { name: sdk.items.Belt, condition: () => (me.normal && (Item.getEquipped(sdk.body.RightArm).tier > 777)) }, - { name: sdk.items.MeshBelt, condition: () => (!me.normal && me.charlvl < 46 && me.trueStr > 58 && (Item.getEquipped(sdk.body.RightArm).tier > 777)) }, - { name: sdk.items.SpiderwebSash, condition: () => (!me.normal && me.trueStr > 50 && (Item.getEquipped(sdk.body.RightArm).tier > 777)) }, + { name: sdk.items.HandScythe, condition: () => (!me.normal && me.equipped.get(sdk.body.RightArm).tier < 777 && (me.trueStr < 79 || me.trueDex < 79)) }, + { name: sdk.items.GreaterTalons, condition: () => (me.equipped.get(sdk.body.RightArm).tier < 777 && me.trueStr >= 79 && me.trueDex >= 79) }, + { name: sdk.items.Belt, condition: () => (me.normal && (me.equipped.get(sdk.body.RightArm).tier > 777)) }, + { name: sdk.items.MeshBelt, condition: () => (!me.normal && me.charlvl < 46 && me.trueStr > 58 && (me.equipped.get(sdk.body.RightArm).tier > 777)) }, + { name: sdk.items.SpiderwebSash, condition: () => (!me.normal && me.trueStr > 50 && (me.equipped.get(sdk.body.RightArm).tier > 777)) }, ].filter((item) => item.condition()); let imbueArr = SetUp.imbueItems(); @@ -157,11 +157,11 @@ Check.itemSockables(sdk.items.Shako, "unique", "Harlequin Crest"); /* Crafting */ - if (Item.getEquipped(sdk.body.Neck).tier < 100000) { + if (me.equipped.get(sdk.body.Neck).tier < 100000) { Config.Recipes.push([Recipe.Caster.Amulet]); } - if (Item.getEquipped(sdk.body.RingLeft).tier < 100000) { + if (me.equipped.get(sdk.body.RingLeft).tier < 100000) { Config.Recipes.push([Recipe.Caster.Ring]); } @@ -171,12 +171,12 @@ } // Spirit Sword - if ((me.ladder || Developer.addLadderRW) && Item.getEquipped(sdk.body.RightArm).tier < 777) { + if ((me.ladder || Developer.addLadderRW) && me.equipped.get(sdk.body.RightArm).tier < 777) { includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/SpiritSword.js"); } // Spirit shield - if ((me.ladder || Developer.addLadderRW) && (Item.getEquipped(sdk.body.LeftArm).tier < 1000 || Item.getEquipped(sdk.body.LeftArmSecondary).prefixnum !== sdk.locale.items.Spirit)) { + if ((me.ladder || Developer.addLadderRW) && (me.equipped.get(sdk.body.LeftArm).tier < 1000 || me.equipped.get(sdk.body.LeftArmSecondary).prefixnum !== sdk.locale.items.Spirit)) { includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/SpiritShield.js"); } @@ -186,12 +186,12 @@ } // Lore - if (Item.getEquipped(sdk.body.Head).tier < 315) { + if (me.equipped.get(sdk.body.Head).tier < 315) { includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/Lore.js"); } // Ancients' Pledge - if (Item.getEquipped(sdk.body.LeftArm).tier < 500) { + if (me.equipped.get(sdk.body.LeftArm).tier < 500) { includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/AncientsPledge.js"); } @@ -206,12 +206,12 @@ } // Smoke - if (Item.getEquipped(sdk.body.Armor).tier < 450) { + if (me.equipped.get(sdk.body.Armor).tier < 450) { includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/Smoke.js"); } // Stealth - if (Item.getEquipped(sdk.body.Armor).tier < 233) { + if (me.equipped.get(sdk.body.Armor).tier < 233) { includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/Stealth.js"); } diff --git a/libs/SoloPlay/Config/Barbarian.js b/libs/SoloPlay/Config/Barbarian.js index e7401307..7b5dc187 100644 --- a/libs/SoloPlay/Config/Barbarian.js +++ b/libs/SoloPlay/Config/Barbarian.js @@ -71,10 +71,10 @@ Config.imbueables = [ { name: sdk.items.AvengerGuard, condition: () => (me.normal && me.expansion) }, { name: sdk.items.SlayerGuard, condition: () => (!me.normal && me.trueStr >= 118 && me.expansion) }, - { name: sdk.items.CarnageHelm, condition: () => (Item.getEquipped(sdk.body.Head).tier < 100000 && me.trueStr >= 106 && me.expansion) }, - { name: sdk.items.Belt, condition: () => (me.normal && (Item.getEquipped(sdk.body.Head).tier > 100000 || me.classic)) }, - { name: sdk.items.MeshBelt, condition: () => (!me.normal && me.charlvl < 46 && me.trueStr > 58 && (Item.getEquipped(sdk.body.RightArm).tier > 100000 || me.classic)) }, - { name: sdk.items.SpiderwebSash, condition: () => (!me.normal && me.trueStr > 50 && (Item.getEquipped(sdk.body.RightArm).tier > 100000 || me.classic)) }, + { name: sdk.items.CarnageHelm, condition: () => (me.equipped.get(sdk.body.Head).tier < 100000 && me.trueStr >= 106 && me.expansion) }, + { name: sdk.items.Belt, condition: () => (me.normal && (me.equipped.get(sdk.body.Head).tier > 100000 || me.classic)) }, + { name: sdk.items.MeshBelt, condition: () => (!me.normal && me.charlvl < 46 && me.trueStr > 58 && (me.equipped.get(sdk.body.RightArm).tier > 100000 || me.classic)) }, + { name: sdk.items.SpiderwebSash, condition: () => (!me.normal && me.trueStr > 50 && (me.equipped.get(sdk.body.RightArm).tier > 100000 || me.classic)) }, ].filter((item) => item.condition()); let imbueArr = SetUp.imbueItems(); @@ -90,10 +90,10 @@ Config.socketables = Config.socketables.concat(basicSocketables.all); Config.socketables.push(addSocketableObj(sdk.items.Flamberge, [], [], - true, (item) => me.normal && Item.getEquipped(sdk.body.LeftArm).tier < 600 && !Check.haveBase("sword", 5) && !me.checkItem({ name: sdk.locale.items.Honor }).have && item.ilvl >= 41 && item.isBaseType && !item.ethereal + true, (item) => me.normal && me.equipped.get(sdk.body.LeftArm).tier < 600 && !Check.haveBase("sword", 5) && !me.checkItem({ name: sdk.locale.items.Honor }).have && item.ilvl >= 41 && item.isBaseType && !item.ethereal )); Config.socketables.push(addSocketableObj(sdk.items.Zweihander, [], [], - true, (item) => Item.getEquipped(sdk.body.LeftArm).tier < 1000 && !Check.haveBase("sword", 5) && !me.checkItem({ name: sdk.locale.items.Honor }).have && item.ilvl >= 41 && item.isBaseType && !item.ethereal + true, (item) => me.equipped.get(sdk.body.LeftArm).tier < 1000 && !Check.haveBase("sword", 5) && !me.checkItem({ name: sdk.locale.items.Honor }).have && item.ilvl >= 41 && item.isBaseType && !item.ethereal )); if (SetUp.finalBuild !== "Immortalwhirl") { @@ -104,7 +104,7 @@ if (["Immortalwhirl", "Singer"].indexOf(SetUp.finalBuild) === -1) { // Grief - if ((me.ladder || Developer.addLadderRW) && (!me.checkItem({ name: sdk.locale.items.Grief }).have || (SetUp.finalBuild === "Whirlwind" && Item.getEquipped(sdk.body.LeftArm).prefixnum !== sdk.locale.items.Grief))) { + if ((me.ladder || Developer.addLadderRW) && (!me.checkItem({ name: sdk.locale.items.Grief }).have || (SetUp.finalBuild === "Whirlwind" && me.equipped.get(sdk.body.LeftArm).prefixnum !== sdk.locale.items.Grief))) { includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/Grief.js"); } @@ -142,7 +142,7 @@ break; case "Singer": // Heart of the Oak - if (Item.getEquipped(sdk.body.LeftArm).prefixnum !== sdk.locale.items.HeartoftheOak && me.checkItem({ name: sdk.locale.items.Enigma }).have) { + if (me.equipped.get(sdk.body.LeftArm).prefixnum !== sdk.locale.items.HeartoftheOak && me.checkItem({ name: sdk.locale.items.Enigma }).have) { includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/HeartOfTheOak.js"); } @@ -178,15 +178,15 @@ } /* Crafting */ - if (Item.getEquipped(sdk.body.Neck).tier < 100000) { + if (me.equipped.get(sdk.body.Neck).tier < 100000) { Check.currentBuild().caster ? Config.Recipes.push([Recipe.Caster.Amulet]) : Config.Recipes.push([Recipe.Blood.Amulet]); } - if (Item.getEquipped(sdk.body.RingLeft).tier < 100000) { + if (me.equipped.get(sdk.body.RingLeft).tier < 100000) { Check.currentBuild().caster ? Config.Recipes.push([Recipe.Caster.Ring]) : Config.Recipes.push([Recipe.Blood.Ring]); } - if (Item.getEquipped(sdk.body.LeftArm).tier < 1370) { + if (me.equipped.get(sdk.body.LeftArm).tier < 1370) { if (me.rawStrength >= 150 && me.rawDexterity >= 88) { // Upgrade Bloodletter to Elite Config.Recipes.push([Recipe.Unique.Weapon.ToElite, "Gladius", Roll.NonEth]); @@ -222,21 +222,21 @@ } // Lawbringer - Amn/Lem/Ko - if (Item.getEquipped(sdk.body.LeftArm).tier < 1370) { + if (me.equipped.get(sdk.body.LeftArm).tier < 1370) { includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/Lawbringer.js"); } // Voice Of Reason - Lem/Ko/El/Eld - if (Item.getEquipped(sdk.body.RightArm).tier > 1100 && Item.getEquipped(sdk.body.LeftArm).tier < 1270 && !Check.haveItem("ring", "unique", "Raven Frost")) { + if (me.equipped.get(sdk.body.RightArm).tier > 1100 && me.equipped.get(sdk.body.LeftArm).tier < 1270 && !Check.haveItem("ring", "unique", "Raven Frost")) { includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/VoiceOfReason.js"); } // Crescent Moon - Shael/Um/Tir - if (Item.getEquipped(sdk.body.LeftArm).tier < 1100) { + if (me.equipped.get(sdk.body.LeftArm).tier < 1100) { includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/CrescentMoon.js"); } - if (Item.getEquipped(sdk.body.LeftArm).tier < 1200) { + if (me.equipped.get(sdk.body.LeftArm).tier < 1200) { // Cube to Ko Rune if (!me.getItem(sdk.items.runes.Ko)) { Config.Recipes.push([Recipe.Rune, "Hel Rune"]); @@ -255,7 +255,7 @@ } // Honor - Amn/El/Ith/Tir/Sol - if (Item.getEquipped(sdk.body.LeftArm).tier < 1050) { + if (me.equipped.get(sdk.body.LeftArm).tier < 1050) { includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/Honor.js"); } @@ -265,7 +265,7 @@ } // Lore - if (Item.getEquipped(sdk.body.Head).tier < 100000) { + if (me.equipped.get(sdk.body.Head).tier < 100000) { includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/Lore.js"); } @@ -275,56 +275,56 @@ } // Merc Treachery - if (Item.getMercEquipped(sdk.body.Armor).tier < 15000 && Item.getEquipped(sdk.body.RightArm).tier > 1100) { + if (Item.getMercEquipped(sdk.body.Armor).tier < 15000 && me.equipped.get(sdk.body.RightArm).tier > 1100) { includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/MercTreachery.js"); } // Treachery - if (Item.getEquipped(sdk.body.Armor).tier < 634 && Item.getEquipped(sdk.body.RightArm).tier > 1100) { + if (me.equipped.get(sdk.body.Armor).tier < 634 && me.equipped.get(sdk.body.RightArm).tier > 1100) { includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/Treachery.js"); } // Smoke - if (Item.getEquipped(sdk.body.Armor).tier < 350) { + if (me.equipped.get(sdk.body.Armor).tier < 350) { includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/Smoke.js"); } // Duress - if (Item.getEquipped(sdk.body.Armor).tier < 600 && (me.checkItem({ name: sdk.locale.items.CrescentMoon }).have || Item.getEquipped(sdk.body.LeftArm).tier > 900)) { + if (me.equipped.get(sdk.body.Armor).tier < 600 && (me.checkItem({ name: sdk.locale.items.CrescentMoon }).have || me.equipped.get(sdk.body.LeftArm).tier > 900)) { includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/Duress.js"); } // Myth - if (Item.getEquipped(sdk.body.Armor).tier < 340) { + if (me.equipped.get(sdk.body.Armor).tier < 340) { includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/Myth.js"); } // Kings Grace - Amn/Ral/Thul - if (Item.getEquipped(sdk.body.LeftArm).tier < 770) { + if (me.equipped.get(sdk.body.LeftArm).tier < 770) { includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/KingsGrace.js"); } // Steel - Tir/El - if (Item.getEquipped(sdk.body.LeftArm).tier < 500) { + if (me.equipped.get(sdk.body.LeftArm).tier < 500) { includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/Steel.js"); } // Spirit Sword - if ((me.ladder || Developer.addLadderRW) && Item.getEquipped(sdk.body.LeftArmSecondary).prefixnum !== sdk.locale.items.Spirit) { + if ((me.ladder || Developer.addLadderRW) && me.equipped.get(sdk.body.LeftArmSecondary).prefixnum !== sdk.locale.items.Spirit) { includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/SpiritSword.js"); } // Malice - IthElEth - if (Item.getEquipped(sdk.body.LeftArm).tier < 175) { + if (me.equipped.get(sdk.body.LeftArm).tier < 175) { includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/Malice.js"); } // Stealth - if (Item.getEquipped(sdk.body.Armor).tier < 233) { + if (me.equipped.get(sdk.body.Armor).tier < 233) { includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/Stealth.js"); } - /*if (Item.getEquipped(sdk.body.Gloves).tier < 233) { + /*if (me.equipped.get(sdk.body.Gloves).tier < 233) { NTIP.addLine("[name] == heavygloves && [flag] != ethereal && [quality] == magic # [itemchargedskill] >= 0 # [maxquantity] == 1"); Config.Recipes.push([Recipe.Blood.Gloves, "Heavy Gloves"]); // Craft Blood Gloves }*/ diff --git a/libs/SoloPlay/Config/Druid.js b/libs/SoloPlay/Config/Druid.js index e97dfc46..a84e7f04 100644 --- a/libs/SoloPlay/Config/Druid.js +++ b/libs/SoloPlay/Config/Druid.js @@ -85,11 +85,11 @@ Config.imbueables = [ { name: sdk.items.SpiritMask, condition: () => (me.normal) }, - { name: sdk.items.TotemicMask, condition: () => (!me.normal && Item.getEquipped(sdk.body.Head).tier < 100000 && (me.charlvl < 66 || me.trueStr < 118)) }, - { name: sdk.items.DreamSpirit, condition: () => (Item.getEquipped(sdk.body.Head).tier < 100000 && me.trueStr >= 118) }, - { name: sdk.items.Belt, condition: () => (me.normal && (Item.getEquipped(sdk.body.Head).tier > 100000)) }, - { name: sdk.items.MeshBelt, condition: () => (!me.normal && me.charlvl < 46 && me.trueStr > 58 && (Item.getEquipped(sdk.body.Head).tier > 100000)) }, - { name: sdk.items.SpiderwebSash, condition: () => (!me.normal && me.trueStr > 50 && (Item.getEquipped(sdk.body.Head).tier > 100000)) }, + { name: sdk.items.TotemicMask, condition: () => (!me.normal && me.equipped.get(sdk.body.Head).tier < 100000 && (me.charlvl < 66 || me.trueStr < 118)) }, + { name: sdk.items.DreamSpirit, condition: () => (me.equipped.get(sdk.body.Head).tier < 100000 && me.trueStr >= 118) }, + { name: sdk.items.Belt, condition: () => (me.normal && (me.equipped.get(sdk.body.Head).tier > 100000)) }, + { name: sdk.items.MeshBelt, condition: () => (!me.normal && me.charlvl < 46 && me.trueStr > 58 && (me.equipped.get(sdk.body.Head).tier > 100000)) }, + { name: sdk.items.SpiderwebSash, condition: () => (!me.normal && me.trueStr > 50 && (me.equipped.get(sdk.body.Head).tier > 100000)) }, ].filter((item) => item.condition()); let imbueArr = SetUp.imbueItems(); @@ -115,11 +115,11 @@ } /* Crafting */ - if (Item.getEquipped(sdk.body.Neck).tier < 100000) { + if (me.equipped.get(sdk.body.Neck).tier < 100000) { Check.currentBuild().caster ? Config.Recipes.push([Recipe.Caster.Amulet]) : Config.Recipes.push([Recipe.Blood.Amulet]); } - if (Item.getEquipped(sdk.body.RingLeft).tier < 100000) { + if (me.equipped.get(sdk.body.RingLeft).tier < 100000) { Check.currentBuild().caster ? Config.Recipes.push([Recipe.Caster.Ring]) : Config.Recipes.push([Recipe.Blood.Ring]); } @@ -154,7 +154,7 @@ } // upgrade magefist - if (Item.getEquipped(sdk.body.Gloves).tier < 110000) { + if (me.equipped.get(sdk.body.Gloves).tier < 110000) { Config.Recipes.push([Recipe.Unique.Armor.ToExceptional, "Light Gauntlets", Roll.NonEth]); Config.Recipes.push([Recipe.Unique.Armor.ToElite, "Battle Gauntlets", Roll.NonEth, "magefist"]); } @@ -198,12 +198,12 @@ Check.itemSockables(sdk.items.TotemicMask, "unique", "Jalal's Mane"); // Spirit Sword - if ((me.ladder || Developer.addLadderRW) && Item.getEquipped(sdk.body.RightArm).tier < 777) { + if ((me.ladder || Developer.addLadderRW) && me.equipped.get(sdk.body.RightArm).tier < 777) { includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/SpiritSword.js"); } // Spirit Shield - if ((me.ladder || Developer.addLadderRW) && (Item.getEquipped(sdk.body.LeftArm).tier < 1000 || Item.getEquipped(sdk.body.LeftArmSecondary).prefixnum !== sdk.locale.items.Spirit)) { + if ((me.ladder || Developer.addLadderRW) && (me.equipped.get(sdk.body.LeftArm).tier < 1000 || me.equipped.get(sdk.body.LeftArmSecondary).prefixnum !== sdk.locale.items.Spirit)) { includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/SpiritShield.js"); } @@ -213,12 +213,12 @@ } // Lore - if (Item.getEquipped(sdk.body.Head).tier < 100000) { + if (me.equipped.get(sdk.body.Head).tier < 100000) { includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/Lore.js"); } // Ancients' Pledge - if (Item.getEquipped(sdk.body.LeftArm).tier < 500) { + if (me.equipped.get(sdk.body.LeftArm).tier < 500) { includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/AncientsPledge.js"); } @@ -233,12 +233,12 @@ } // Smoke - if (Item.getEquipped(sdk.body.Armor).tier < 634) { + if (me.equipped.get(sdk.body.Armor).tier < 634) { includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/Smoke.js"); } // Stealth - if (Item.getEquipped(sdk.body.Armor).tier < 233) { + if (me.equipped.get(sdk.body.Armor).tier < 233) { includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/Stealth.js"); } diff --git a/libs/SoloPlay/Config/Necromancer.js b/libs/SoloPlay/Config/Necromancer.js index 351c8c44..de3daeed 100644 --- a/libs/SoloPlay/Config/Necromancer.js +++ b/libs/SoloPlay/Config/Necromancer.js @@ -93,10 +93,10 @@ Config.imbueables = [ { name: sdk.items.DemonHead, condition: () => (me.normal && me.expansion) }, { name: sdk.items.HierophantTrophy, condition: () => (!me.normal && (me.charlvl < 66 || me.trueStr < 106) && me.expansion) }, - { name: sdk.items.BloodlordSkull, condition: () => (Item.getEquipped(sdk.body.LeftArm).tier < 1000 && me.expansion) }, - { name: sdk.items.Belt, condition: () => (me.normal && (Item.getEquipped(sdk.body.LeftArm).tier > 1000 || me.classic)) }, - { name: sdk.items.MeshBelt, condition: () => (!me.normal && me.charlvl < 46 && me.trueStr > 58 && (Item.getEquipped(sdk.body.LeftArm).tier > 1000 || me.classic)) }, - { name: sdk.items.SpiderwebSash, condition: () => (!me.normal && me.trueStr > 50 && (Item.getEquipped(sdk.body.LeftArm).tier > 1000 || me.classic)) }, + { name: sdk.items.BloodlordSkull, condition: () => (me.equipped.get(sdk.body.LeftArm).tier < 1000 && me.expansion) }, + { name: sdk.items.Belt, condition: () => (me.normal && (me.equipped.get(sdk.body.LeftArm).tier > 1000 || me.classic)) }, + { name: sdk.items.MeshBelt, condition: () => (!me.normal && me.charlvl < 46 && me.trueStr > 58 && (me.equipped.get(sdk.body.LeftArm).tier > 1000 || me.classic)) }, + { name: sdk.items.SpiderwebSash, condition: () => (!me.normal && me.trueStr > 50 && (me.equipped.get(sdk.body.LeftArm).tier > 1000 || me.classic)) }, ].filter((item) => item.condition()); let imbueArr = SetUp.imbueItems(); @@ -106,7 +106,7 @@ switch (me.gametype) { case sdk.game.gametype.Classic: // Res shield - if (Item.getEquipped(sdk.body.LeftArm).tier < 487) { + if (me.equipped.get(sdk.body.LeftArm).tier < 487) { includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/PDiamondShield.js"); } @@ -124,12 +124,12 @@ )); /* Crafting */ - if (Item.getEquipped(sdk.body.Neck).tier < 100000) { + if (me.equipped.get(sdk.body.Neck).tier < 100000) { Config.Recipes.push([Recipe.Caster.Amulet]); } // upgrade magefist - if (Item.getEquipped(sdk.body.Gloves).tier < 110000) { + if (me.equipped.get(sdk.body.Gloves).tier < 110000) { Config.Recipes.push([Recipe.Unique.Armor.ToExceptional, "Light Gauntlets", Roll.NonEth]); Config.Recipes.push([Recipe.Unique.Armor.ToElite, "Battle Gauntlets", Roll.NonEth, "magefist"]); } @@ -138,11 +138,11 @@ Check.itemSockables(sdk.items.Shako, "unique", "Harlequin Crest"); /* Crafting */ - if (Item.getEquipped(sdk.body.Neck).tier < 100000) { + if (me.equipped.get(sdk.body.Neck).tier < 100000) { Config.Recipes.push([Recipe.Caster.Amulet]); } - if (Item.getEquipped(sdk.body.RingLeft).tier < 100000) { + if (me.equipped.get(sdk.body.RingLeft).tier < 100000) { Config.Recipes.push([Recipe.Caster.Ring]); } @@ -157,7 +157,7 @@ } // Rhyme - if (Item.getEquipped(sdk.body.LeftArm).tier < 650) { + if (me.equipped.get(sdk.body.LeftArm).tier < 650) { includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/Rhyme.js"); } @@ -167,12 +167,12 @@ } // Spirit Sword - if ((me.ladder || Developer.addLadderRW) && Item.getEquipped(sdk.body.RightArm).tier < 777) { + if ((me.ladder || Developer.addLadderRW) && me.equipped.get(sdk.body.RightArm).tier < 777) { includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/SpiritSword.js"); } // Spirit shield - if ((me.ladder || Developer.addLadderRW) && (Item.getEquipped(sdk.body.LeftArm).tier < 1000 || Item.getEquipped(sdk.body.LeftArmSecondary).prefixnum !== sdk.locale.items.Spirit)) { + if ((me.ladder || Developer.addLadderRW) && (me.equipped.get(sdk.body.LeftArm).tier < 1000 || me.equipped.get(sdk.body.LeftArmSecondary).prefixnum !== sdk.locale.items.Spirit)) { includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/SpiritShield.js"); } @@ -181,17 +181,17 @@ includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/MercInsight.js"); } - if (!me.haveSome([{ name: sdk.locale.items.Enigma }, { name: sdk.locale.items.Bone }]) && Item.getEquipped(sdk.body.Armor).tier < 650) { + if (!me.haveSome([{ name: sdk.locale.items.Enigma }, { name: sdk.locale.items.Bone }]) && me.equipped.get(sdk.body.Armor).tier < 650) { includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/Bone.js"); } // Lore - if (Item.getEquipped(sdk.body.Head).tier < 315) { + if (me.equipped.get(sdk.body.Head).tier < 315) { includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/Lore.js"); } // Ancients' Pledge - if (Item.getEquipped(sdk.body.LeftArm).tier < 500) { + if (me.equipped.get(sdk.body.LeftArm).tier < 500) { includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/AncientsPledge.js"); } @@ -206,12 +206,12 @@ } // Smoke - if (Item.getEquipped(sdk.body.Armor).tier < 450) { + if (me.equipped.get(sdk.body.Armor).tier < 450) { includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/Smoke.js"); } // Stealth - if (Item.getEquipped(sdk.body.Armor).tier < 233) { + if (me.equipped.get(sdk.body.Armor).tier < 233) { includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/Stealth.js"); } diff --git a/libs/SoloPlay/Config/Paladin.js b/libs/SoloPlay/Config/Paladin.js index c54f4e31..99cad1a2 100644 --- a/libs/SoloPlay/Config/Paladin.js +++ b/libs/SoloPlay/Config/Paladin.js @@ -20,8 +20,6 @@ */ // todo: clean-up how cubing to runes is handled -// move shared config settings into its own function call -// make and initialize me.equippedItems object so don't have to do repeated Item.getEquipped(whatever) call (function LoadConfig () { includeIfNotIncluded("SoloPlay/Functions/MiscOverrides.js"); @@ -93,10 +91,10 @@ Config.imbueables = [ { name: sdk.items.WarScepter, condition: () => me.normal }, { name: sdk.items.DivineScepter, condition: () => (!me.normal && (me.trueStr < 125 || me.trueDex < 60)) }, - { name: sdk.items.MightyScepter, condition: () => (Item.getEquipped(sdk.body.RightArm).tier < 777 && (me.trueStr >= 125 || me.trueDex >= 60)) }, - { name: sdk.items.Belt, condition: () => (me.normal && (Item.getEquipped(sdk.body.RightArm).tier > 777 || me.classic)) }, - { name: sdk.items.MeshBelt, condition: () => (!me.normal && me.charlvl < 46 && me.trueStr > 58 && (Item.getEquipped(sdk.body.RightArm).tier > 777 || me.classic)) }, - { name: sdk.items.SpiderwebSash, condition: () => (!me.normal && me.trueStr > 50 && (Item.getEquipped(sdk.body.RightArm).tier > 777 || me.classic)) }, + { name: sdk.items.MightyScepter, condition: () => (me.equipped.get(sdk.body.RightArm).tier < 777 && (me.trueStr >= 125 || me.trueDex >= 60)) }, + { name: sdk.items.Belt, condition: () => (me.normal && (me.equipped.get(sdk.body.RightArm).tier > 777 || me.classic)) }, + { name: sdk.items.MeshBelt, condition: () => (!me.normal && me.charlvl < 46 && me.trueStr > 58 && (me.equipped.get(sdk.body.RightArm).tier > 777 || me.classic)) }, + { name: sdk.items.SpiderwebSash, condition: () => (!me.normal && me.trueStr > 50 && (me.equipped.get(sdk.body.RightArm).tier > 777 || me.classic)) }, ]; let imbueArr = SetUp.imbueItems(); @@ -106,7 +104,7 @@ switch (me.gametype) { case sdk.game.gametype.Classic: // Res shield - if (Item.getEquipped(sdk.body.LeftArm).tier < 487) { + if (me.equipped.get(sdk.body.LeftArm).tier < 487) { includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/PDiamondShield.js"); } @@ -121,15 +119,15 @@ )); /* Crafting */ - if (Item.getEquipped(sdk.body.Neck).tier < 100000) { + if (me.equipped.get(sdk.body.Neck).tier < 100000) { Check.currentBuild().caster ? Config.Recipes.push([Recipe.Caster.Amulet]) : Config.Recipes.push([Recipe.Blood.Amulet]); } - if (Item.getEquipped(sdk.body.RingLeft).tier < 100000) { + if (me.equipped.get(sdk.body.RingLeft).tier < 100000) { Check.currentBuild().caster ? Config.Recipes.push([Recipe.Caster.Ring]) : Config.Recipes.push([Recipe.Blood.Ring]); } - if (Item.getEquipped(sdk.body.Gloves).tier < 110000) { + if (me.equipped.get(sdk.body.Gloves).tier < 110000) { Config.Recipes.push([Recipe.Unique.Armor.ToExceptional, "Light Gauntlets", Roll.NonEth]); } @@ -184,13 +182,16 @@ } // Spirit Shield - if ((me.ladder || Developer.addLadderRW) && Item.getEquipped(sdk.body.LeftArm).tier < 110000) { + if ((me.ladder || Developer.addLadderRW) && me.equipped.get(sdk.body.LeftArm).tier < 110000) { includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/SpiritShield.js"); } break; case "Auradin": - dreamerCheck = me.haveAll([{ name: sdk.locale.items.Dream, itemtype: sdk.items.type.AuricShields }, { name: sdk.locale.items.Dream, itemtype: sdk.items.type.Helm }]); + dreamerCheck = me.haveAll([ + { name: sdk.locale.items.Dream, itemtype: sdk.items.type.AuricShields }, + { name: sdk.locale.items.Dream, itemtype: sdk.items.type.Helm } + ]); // Dream Shield if ((me.ladder || Developer.addLadderRW) && !me.checkItem({ name: sdk.locale.items.Dream, itemtype: sdk.items.type.AuricShields }).have) { @@ -222,7 +223,7 @@ if (!me.checkItem({ name: sdk.locale.items.CalltoArms }).have) { // Cube to Mal rune - if (!me.getItem(sdk.items.runes.Mal) && Item.getEquipped(sdk.body.RightArm).tier >= 110000) { + if (!me.getItem(sdk.items.runes.Mal) && me.equipped.get(sdk.body.RightArm).tier >= 110000) { Config.Recipes.push([Recipe.Rune, "Um Rune"]); } @@ -295,7 +296,7 @@ if (!me.checkItem({ name: sdk.locale.items.CalltoArms }).have) { // Cube to Mal rune - if (!me.getItem(sdk.items.runes.Mal) && Item.getEquipped(sdk.body.RightArm).tier >= 110000) { + if (!me.getItem(sdk.items.runes.Mal) && me.equipped.get(sdk.body.RightArm).tier >= 110000) { Config.Recipes.push([Recipe.Rune, "Um Rune"]); } @@ -392,7 +393,7 @@ if (!me.checkItem({ name: sdk.locale.items.CalltoArms }).have) { // Cube to Mal rune - if (!me.getItem(sdk.items.runes.Mal) && Item.getEquipped(sdk.body.RightArm).tier >= 110000) { + if (!me.getItem(sdk.items.runes.Mal) && me.equipped.get(sdk.body.RightArm).tier >= 110000) { Config.Recipes.push([Recipe.Rune, "Um Rune"]); } @@ -437,12 +438,12 @@ } // Spirit Sword - if ((me.ladder || Developer.addLadderRW) && Item.getEquipped(sdk.body.RightArm).tier < 777) { + if ((me.ladder || Developer.addLadderRW) && me.equipped.get(sdk.body.RightArm).tier < 777) { includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/SpiritSword.js"); } // Spirit Shield - if ((me.ladder || Developer.addLadderRW) && (Item.getEquipped(sdk.body.LeftArm).tier < 1000 || Item.getEquipped(sdk.body.LeftArmSecondary).prefixnum !== sdk.locale.items.Spirit)) { + if ((me.ladder || Developer.addLadderRW) && (me.equipped.get(sdk.body.LeftArm).tier < 1000 || me.equipped.get(sdk.body.LeftArmSecondary).prefixnum !== sdk.locale.items.Spirit)) { includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/SpiritShield.js"); } @@ -452,7 +453,7 @@ } // Lore - if (Item.getEquipped(sdk.body.Head).tier < 315) { + if (me.equipped.get(sdk.body.Head).tier < 315) { includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/Lore.js"); } @@ -461,7 +462,7 @@ */ // Ancients' Pledge - if (Item.getEquipped(sdk.body.LeftArm).tier < 500) { + if (me.equipped.get(sdk.body.LeftArm).tier < 500) { includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/AncientsPledge.js"); } @@ -476,12 +477,12 @@ } // Smoke - if (Item.getEquipped(sdk.body.Armor).tier < 450) { + if (me.equipped.get(sdk.body.Armor).tier < 450) { includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/Smoke.js"); } // Stealth - if (Item.getEquipped(sdk.body.Armor).tier < 233) { + if (me.equipped.get(sdk.body.Armor).tier < 233) { includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/Stealth.js"); } diff --git a/libs/SoloPlay/Config/Sorceress.js b/libs/SoloPlay/Config/Sorceress.js index 7a175827..4715aea9 100644 --- a/libs/SoloPlay/Config/Sorceress.js +++ b/libs/SoloPlay/Config/Sorceress.js @@ -95,10 +95,10 @@ Config.imbueables = [ { name: sdk.items.JaredsStone, condition: () => (me.normal && me.expansion) }, { name: sdk.items.SwirlingCrystal, condition: () => (!me.normal && me.charlvl < 66 && me.expansion) }, - { name: sdk.items.DimensionalShard, condition: () => (Item.getEquipped(sdk.body.RightArm).tier < 777 && me.expansion) }, - { name: sdk.items.Belt, condition: () => (me.normal && (Item.getEquipped(sdk.body.RightArm).tier > 777 || me.classic)) }, - { name: sdk.items.MeshBelt, condition: () => (!me.normal && me.charlvl < 46 && me.trueStr > 58 && (Item.getEquipped(sdk.body.RightArm).tier > 777 || me.classic)) }, - { name: sdk.items.SpiderwebSash, condition: () => (!me.normal && me.trueStr > 50 && (Item.getEquipped(sdk.body.RightArm).tier > 777 || me.classic)) }, + { name: sdk.items.DimensionalShard, condition: () => (me.equipped.get(sdk.body.RightArm).tier < 777 && me.expansion) }, + { name: sdk.items.Belt, condition: () => (me.normal && (me.equipped.get(sdk.body.RightArm).tier > 777 || me.classic)) }, + { name: sdk.items.MeshBelt, condition: () => (!me.normal && me.charlvl < 46 && me.trueStr > 58 && (me.equipped.get(sdk.body.RightArm).tier > 777 || me.classic)) }, + { name: sdk.items.SpiderwebSash, condition: () => (!me.normal && me.trueStr > 50 && (me.equipped.get(sdk.body.RightArm).tier > 777 || me.classic)) }, ].filter((item) => item.condition()); let imbueArr = SetUp.imbueItems(); @@ -108,7 +108,7 @@ switch (me.gametype) { case sdk.game.gametype.Classic: // Res shield - if ((Item.getEquipped(sdk.body.LeftArm).tier < 487 && !Item.getEquipped(sdk.body.RightArm).twoHanded) || (Item.getEquipped(sdk.body.RightArm).tier < 487 && Item.getEquipped(sdk.body.RightArm).twoHanded)) { + if ((me.equipped.get(sdk.body.LeftArm).tier < 487 && !me.equipped.get(sdk.body.RightArm).twoHanded) || (me.equipped.get(sdk.body.RightArm).tier < 487 && me.equipped.get(sdk.body.RightArm).twoHanded)) { includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/PDiamondShield.js"); } @@ -118,7 +118,7 @@ const { basicSocketables, addSocketableObj } = require("../Utils/General"); /* Crafting */ - if (Item.getEquipped(sdk.body.Neck).tier < 100000) { + if (me.equipped.get(sdk.body.Neck).tier < 100000) { Config.Recipes.push([Recipe.Caster.Amulet]); } @@ -145,7 +145,7 @@ } // Spirit Shield - if ((me.ladder || Developer.addLadderRW) && SetUp.currentBuild === SetUp.finalBuild && (Item.getEquipped(sdk.body.LeftArm).tier < 1000 || Item.getEquipped(sdk.body.LeftArmSecondary).prefixnum !== sdk.locale.items.Spirit)) { + if ((me.ladder || Developer.addLadderRW) && SetUp.currentBuild === SetUp.finalBuild && (me.equipped.get(sdk.body.LeftArm).tier < 1000 || me.equipped.get(sdk.body.LeftArmSecondary).prefixnum !== sdk.locale.items.Spirit)) { includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/SpiritShield.js"); } @@ -191,7 +191,8 @@ } // Go ahead and keep two P-diamonds prior to finding a moser's unless already using a better shield - if (!Check.haveItem("shield", "unique", "Moser's Blessed Circle") && !me.haveSome([{ name: sdk.locale.items.Sanctuary }, { name: sdk.locale.items.Spirit, itemtype: sdk.items.type.Shield }])) { + if (!Check.haveItem("shield", "unique", "Moser's Blessed Circle") + && !me.haveSome([{ name: sdk.locale.items.Sanctuary }, { name: sdk.locale.items.Spirit, itemtype: sdk.items.type.Shield }])) { NTIP.addLine("[name] == perfectdiamond # # [maxquantity] == 2"); if (Item.getQuantityOwned(me.getItem(sdk.items.gems.Perfect.Diamond)) < 2) { @@ -202,7 +203,8 @@ Check.itemSockables(sdk.items.RoundShield, "unique", "Moser's Blessed Circle"); // Sanctuary - if (!me.haveSome([{ name: sdk.locale.items.Sanctuary }, { name: sdk.locale.items.Spirit, itemtype: sdk.items.type.Shield }]) && ["Blova", "Lightning"].indexOf(SetUp.currentBuild) === -1) { + if (!me.haveSome([{ name: sdk.locale.items.Sanctuary }, { name: sdk.locale.items.Spirit, itemtype: sdk.items.type.Shield }]) + && ["Blova", "Lightning"].indexOf(SetUp.currentBuild) === -1) { includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/Sanctuary.js"); } @@ -212,7 +214,7 @@ } // Spirit Sword - if ((me.ladder || Developer.addLadderRW) && Item.getEquipped(sdk.body.RightArm).tier < 777) { + if ((me.ladder || Developer.addLadderRW) && me.equipped.get(sdk.body.RightArm).tier < 777) { includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/SpiritSword.js"); } @@ -222,12 +224,12 @@ } // Lore - if (Item.getEquipped(sdk.body.Head).tier < 315) { + if (me.equipped.get(sdk.body.Head).tier < 315) { includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/Lore.js"); } // Ancients' Pledge - if (Item.getEquipped(sdk.body.LeftArm).tier < 500) { + if (me.equipped.get(sdk.body.LeftArm).tier < 500) { includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/AncientsPledge.js"); } @@ -237,7 +239,7 @@ } // Bone - if (Item.getEquipped(sdk.body.Armor).tier < 450) { + if (me.equipped.get(sdk.body.Armor).tier < 450) { includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/Bone.js"); } @@ -247,12 +249,12 @@ } // Smoke - if (Item.getEquipped(sdk.body.Armor).tier < 300) { + if (me.equipped.get(sdk.body.Armor).tier < 300) { includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/Smoke.js"); } // Stealth - if (Item.getEquipped(sdk.body.Armor).tier < 233) { + if (me.equipped.get(sdk.body.Armor).tier < 233) { includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/Stealth.js"); } diff --git a/libs/SoloPlay/Functions/ClassAttackOverrides/AmazonAttacks-WIP.js b/libs/SoloPlay/Functions/ClassAttackOverrides/AmazonAttacks-WIP.js index 18c1ef1a..cae86115 100644 --- a/libs/SoloPlay/Functions/ClassAttackOverrides/AmazonAttacks-WIP.js +++ b/libs/SoloPlay/Functions/ClassAttackOverrides/AmazonAttacks-WIP.js @@ -138,7 +138,7 @@ ClassAttack.doAttack = function (unit) { range: Skill.getRange(sdk.skills.Jab), mana: Skill.getManaCost(sdk.skills.Jab), use: function () { - return (this.level > 0 && Item.getEquipped(sdk.body.RightArm).tier >= 1000); + return (this.level > 0 && me.equipped.get(sdk.body.RightArm).tier >= 1000); } }, }; diff --git a/libs/SoloPlay/Functions/DynamicTiers.js b/libs/SoloPlay/Functions/DynamicTiers.js index 072fc11e..b0f7715e 100644 --- a/libs/SoloPlay/Functions/DynamicTiers.js +++ b/libs/SoloPlay/Functions/DynamicTiers.js @@ -11,7 +11,7 @@ * no point checking for stats that cannot ever exist. Also handle some of the misc stats that appear as they can be helpful. */ -(function() { +(function () { /** * @param {ItemUnit} item */ @@ -146,12 +146,22 @@ let tier = 0; !buildInfo && (buildInfo = Check.currentBuild()); - const chargedWeights = { - Teleport: Pather.canTeleport() ? 0 : 5, - Enchant: buildInfo.caster ? 0 : 10, - InnerSight: me.amazon || buildInfo.caster ? 0 : 10, - SlowMissiles: me.amazon ? 0 : 10, - }; + /** + * @constructor + * @param {{ skill: number, level: number, charges: number, maxcharges: number}} obj + */ + function ChargedItem (obj = {}) { + this.skill = obj.skill; + this.level = obj.level; + this.charges = obj.charges; + this.maxcharges = obj.maxcharges; + } + const _chargedWeights = new Map([ + [sdk.skills.Teleport, (Pather.canTeleport() ? 0 : 5)], + [sdk.skills.Enchant, (buildInfo.caster ? 0 : 10)], + [sdk.skills.InnerSight, (me.amazon || buildInfo.caster ? 0 : 10)], + [sdk.skills.SlowMissiles, (me.amazon ? 0 : 10)], + ]); let stats = item.getStat(-2); let chargedItems = []; @@ -160,40 +170,27 @@ if (stats[sdk.stats.ChargedSkill] instanceof Array) { for (let i = 0; i < stats[sdk.stats.ChargedSkill].length; i++) { if (stats[sdk.stats.ChargedSkill][i] !== undefined) { - chargedItems.push({ - skill: stats[sdk.stats.ChargedSkill][i].skill, - level: stats[sdk.stats.ChargedSkill][i].level, - charges: stats[sdk.stats.ChargedSkill][i].charges, - maxcharges: stats[sdk.stats.ChargedSkill][i].maxcharges - }); + chargedItems.push(new ChargedItem(stats[sdk.stats.ChargedSkill][i])); } } } else { - chargedItems.push({ - skill: stats[sdk.stats.ChargedSkill].skill, - level: stats[sdk.stats.ChargedSkill].level, - charges: stats[sdk.stats.ChargedSkill].charges, - maxcharges: stats[sdk.stats.ChargedSkill].maxcharges - }); + chargedItems.push(new ChargedItem(stats[sdk.stats.ChargedSkill])); } } - chargedItems = chargedItems.filter((v, i, a) => a.findIndex(el => ["skill", "level"].every(k => el[k] === v[k])) === i); + chargedItems = chargedItems + .filter((v, i, a) => a.findIndex(el => ["skill", "level"].every(k => el[k] === v[k])) === i); if (skillId > 0) { - chargedItems = chargedItems.filter(check => check.skill === skillId); - chargedItems.forEach(el => tier += el.level * 5); + chargedItems + .filter(check => check.skill === skillId) + .forEach(el => tier += el.level * 5); } else { chargedItems.forEach(function (el) { - try { - let skillName = getSkillById(el.skill).split(" ").join(""); - if (skillName === "Teleport") { - chargedWeights[skillName] > 0 && (tier += el.maxcharges * 2); - } else if (!!chargedWeights[skillName]) { - tier += el.level * chargedWeights[skillName]; - } - } catch (e) { - return; + if (el.skill === sdk.skills.Teleport) { + tier += el.maxcharges * 2; + } else if (_chargedWeights.has(el.skill)) { + tier += el.level * _chargedWeights.get(el.skill); } }); } @@ -201,136 +198,139 @@ return tier; }; - const tierWeights = { - useHardcoreWeights: !!(me.hardcore), - resistWeights: { - FR: (this.useHardcoreWeights ? 5 : 3), // fire resist - LR: (this.useHardcoreWeights ? 5 : 3), // lightning resist - CR: (this.useHardcoreWeights ? 3 : 1.5), // cold resist - PR: (this.useHardcoreWeights ? 5 : 1), // poison resist - MAXFR: 5, - MAXLR: 5, - MAXCR: 3, - MAXPR: 3, - ABS: (this.useHardcoreWeights ? 5 : 3), // absorb damage (fire light magic cold) - DR: (this.useHardcoreWeights ? 4 : 2), // Damage resist - MR: 3, // Magic damage resist - }, - - generalWeights: { - CBF: 25, // cannot be frozen - FRW: 1, // faster run/walk - FHR: 3, // faster hit recovery - DEF: 0.05, // defense - ICB: 2, // increased chance to block - BELTSLOTS: 2, // belt potion storage - MF: 1, //Magic Find - // base stats - HP: 0.5, - MANA: 0.5, - STR: 1, - DEX: 1, - }, - - casterWeights: { - //breakpoint stats - FCR: (me.assassin ? 2 : 5), - IAS: (me.assassin ? 4 : 0), - // regen - HPREGEN: 2, - MANAREGEN: 2.2, - }, - - meleeWeights: { - // breakpoint stats - todo actually take breakpoints into account - FCR: 0.5, - IAS: (me.barbarian && me.classic ? 2 : 4), - // Attack - MINDMG: 3, - MAXDMG: 3, - SECMINDMG: 2, - SECMAXDMG: 2, - AVGDMG: 3, - ELEDMG: 0.5, - AR: 0.2, - CB: 4, - OW: 1, - DS: 1.5, - DMGTOUNDEAD: 0.5, - DMGTODEMONS: 0.5, - - // leaching - LL: 4, - ML: 2, - - // regen - HPREGEN: 2, - MANAREGEN: 2, - }, - - ctcWeights: { - whenStruck: 2, - onAttack: 2, - onStrike: 1, - skills: { - // Sorc skills - Nova: 2, - FrostNova: 4, - IceBlast: 4, - ChargedBolt: 4, - StaticField: 5, - GlacialSpike: 6, - ChainLightning: 6, - Blizzard: 4, - FrozenOrb: 8, - Hydra: 4, - // Necro skills - AmplifyDamage: 5, - Decrepify: 10, - LifeTap: 10, - BoneArmor: 10, - BoneSpear: 8, - BoneSpirit: 8, - PoisonNova: 10, - // Barb skills - Taunt: 5, - Howl: 5, - // Druid skills - CycloneArmor: 10, - Twister: 5, - // Sin skills - Fade: 10, - Venom: 8, - } - }, - - skillsWeights: { - ALL: 200, - CLASS: 175, - TAB: 125, - WANTED: 45, - USEFUL: 30, // + wanted supportive skills - }, - - charmWeights: { - ALL: 180, // + all skills - CLASS: 175, // + class tab - TAB: 300, // + skill tab - FR: 3, // fire resist - LR: 5, // lightning resist - CR: 2, // cold resist - PR: 0.5, // poison resist - FRW: 1, // faster run/walk - FHR: (me.barbarian ? 4 : 2), // faster hit recovery - DEF: 0.05, // defense - MF: 2, //Magic Find - // base stats - HP: 1.75, - MANA: 0.8, - STR: 1.0, - DEX: 1.0, - } - }; + const _tierWeights = (function () { + const hc = me.hardcore; + const buildInfo = Check.currentBuild(); + /** @type {Map} */ + const res = new Map([ + [sdk.stats.FireResist, hc ? 5 : 3], + [sdk.stats.LightningResist, hc ? 5 : 3], + [sdk.stats.ColdResist, hc ? 3 : 1.5], + [sdk.stats.PoisonResist, hc ? 5 : 1], + [sdk.stats.MaxFireResist, 5], + [sdk.stats.MaxLightResist, 5], + [sdk.stats.MaxColdResist, 3], + [sdk.stats.MaxPoisonResist, 3], + [sdk.stats.AbsorbFire, hc ? 2 : 1], + [sdk.stats.AbsorbFirePercent, hc ? 3 : 1.5], + [sdk.stats.AbsorbLight, hc ? 2 : 1], + [sdk.stats.AbsorbLightPercent, hc ? 3 : 1.5], + [sdk.stats.AbsorbCold, hc ? 1 : 0.5], + [sdk.stats.AbsorbColdPercent, hc ? 1.5 : 0.75], + [sdk.stats.AbsorbMagic, hc ? 2 : 1], + [sdk.stats.AbsorbMagicPercent, hc ? 3 : 1.5], + [sdk.stats.NormalDamageReduction, hc ? 1 : 0.5], + [sdk.stats.MagicDamageReduction, hc ? 1 : 0.5], + [sdk.stats.DamageResist, hc ? 5 : 2], + [sdk.stats.MagicResist, hc ? 6 : 3], + ]); + /** @type {Map} */ + const gen = new Map([ + [sdk.stats.CannotbeFrozen, buildInfo.caster ? 25 : 100], + [sdk.stats.FRW, 1], + [sdk.stats.FHR, 3], + [sdk.stats.FBR, 1], + [sdk.stats.ToBlock, 1], + [sdk.stats.IAS, buildInfo.caster && !me.assassin ? 0 : 4], + [sdk.stats.FCR, buildInfo.caster ? me.assassin ? 2 : 5 : 0.5], + [sdk.stats.Defense, 0.05], + [sdk.stats.MagicBonus, 1], + [sdk.stats.GoldBonus, 0.5], + [sdk.stats.Vitality, 1], + [sdk.stats.MaxHp, 1], + [sdk.stats.PerLevelHp, 1], + [sdk.stats.HpRegen, 2], + [sdk.stats.Energy, 1], + [sdk.stats.MaxMana, 1], + [sdk.stats.PerLevelMana, 1], + [sdk.stats.ManaRecovery, buildInfo.caster ? 2.5 : 1], + [sdk.stats.Strength, 1], + [sdk.stats.Dexterity, me.amazon ? 3 : 1], + [sdk.stats.ReplenishQuantity, me.amazon ? 50 : 0], + [sdk.stats.ToHit, 0.2], + [sdk.stats.CrushingBlow, 4], + [sdk.stats.OpenWounds, 1], + [sdk.stats.DeadlyStrike, 1.5], + [sdk.stats.LifeLeech, 4], + [sdk.stats.ManaLeech, 2], + [sdk.stats.DemonDamagePercent, 0.5], + [sdk.stats.UndeadDamagePercent, 0.5], + [sdk.stats.ReplenishDurability, 15], + [sdk.stats.IgnoreTargetDefense, 50], + [sdk.stats.MinDamage, 3], + [sdk.stats.MaxDamage, 3], + [sdk.stats.SecondaryMinDamage, 2], + [sdk.stats.SecondaryMaxDamage, 2], + ]); + /** @type {Map} */ + const skill = new Map([ + [sdk.stats.AllSkills, 200], + [sdk.stats.AddClassSkills, 175], + [sdk.stats.AddSkillTab, 125], + ]); + /** @type {Map} */ + const charms = new Map([ + [sdk.stats.AllSkills, 180], + [sdk.stats.AddClassSkills, 175], + [sdk.stats.AddSkillTab, 300], + [sdk.stats.FireResist, 3], + [sdk.stats.LightningResist, 5], + [sdk.stats.ColdResist, 2], + [sdk.stats.PoisonResist, 1], + [sdk.stats.FRW, 1], + [sdk.stats.FHR, (me.barbarian ? 4 : 2)], + [sdk.stats.Defense, 0.05], + [sdk.stats.MagicBonus, 2], + [sdk.stats.GoldBonus, 0.5], + [sdk.stats.MaxHp, 1.75], + [sdk.stats.PerLevelHp, 1], + [sdk.stats.HpRegen, 2], + [sdk.stats.MaxMana, 1], + [sdk.stats.PerLevelMana, 1], + [sdk.stats.Strength, 1], + [sdk.stats.Dexterity, me.amazon ? 3 : 1], + [sdk.stats.Vitality, 1], + [sdk.stats.Energy, 0.8], + ]); + + /** @type {Map} */ + const ctc = new Map([ + [sdk.stats.SkillWhenStruck, 2], + [sdk.stats.SkillOnAttack, 2], + [sdk.stats.SkillOnStrike, 1], + [sdk.skills.Nova, 2], + [sdk.skills.FrostNova, 4], + [sdk.skills.IceBlast, 4], + [sdk.skills.ChargedBolt, 4], + [sdk.skills.StaticField, 5], + [sdk.skills.GlacialSpike, 6], + [sdk.skills.ChainLightning, 6], + [sdk.skills.Blizzard, 4], + [sdk.skills.FrozenOrb, 8], + [sdk.skills.Hydra, 4], + [sdk.skills.AmplifyDamage, 5], + [sdk.skills.Decrepify, 10], + [sdk.skills.LifeTap, 10], + [sdk.skills.BoneArmor, 10], + [sdk.skills.BoneSpear, 8], + [sdk.skills.BoneSpirit, 8], + [sdk.skills.PoisonNova, 10], + [sdk.skills.Taunt, 5], + [sdk.skills.Howl, 5], + [sdk.skills.CycloneArmor, 10], + [sdk.skills.Twister, 5], + [sdk.skills.Fade, 10], + [sdk.skills.Venom, 8], + ]); + + return { + res: res, + gen: gen, + skill: skill, + charms: charms, + ctc: ctc, + }; + })(); /** * @param {ItemUnit} item @@ -338,6 +338,7 @@ * @todo Breakpoint scoring similar to how res is scored */ const tierscore = function (item, tier, bodyloc) { + if (item.questItem) return -1; const itembodyloc = Item.getBodyLoc(item); if (!itembodyloc.length) return -1; bodyloc = bodyloc || itembodyloc.last(); @@ -351,6 +352,7 @@ const buildInfo = Check.currentBuild(); const canTele = Pather.canTeleport(); + // const eqItem = me.equipped.get(bodyloc); const eqItem = me.getEquippedItem(bodyloc); const generalScore = () => { @@ -361,32 +363,32 @@ // check if we have cbf but make sure its not from the item we are trying to un-equip if (!me.getStat(sdk.stats.CannotbeFrozen)) { // Cannot be frozen is very important for Melee chars - generalRating += buildInfo.caster ? tierWeights.generalWeights.CBF : tierWeights.generalWeights.CBF * 4; + generalRating += _tierWeights.gen.get(sdk.stats.CannotbeFrozen); } } - // faster run/walk - !canTele && (generalRating += item.getStatEx(sdk.stats.FRW) * tierWeights.generalWeights.FRW); - // belt slots if (item.itemType === sdk.items.type.Belt) { const beltSizes = { lbl: 2, vbl: 2, mbl: 3, tbl: 3 }; const beltSize = beltSizes[item.code] || 4; // if our current belt-size is better, don't down-grade even if the other stats on the new item are better, not worth the town visits - generalRating += (Storage.BeltSize() > beltSize ? -50 : (beltSize * 4 * tierWeights.generalWeights.BELTSLOTS)); + generalRating += (Storage.BeltSize() > beltSize ? -50 : (beltSize * 4 * 2)); } + // pierce/mastery's not sure how I want to weight this so for now just its base value + buildInfo.usefulStats.forEach(stat => generalRating += item.getStatEx(stat)); + // start generalRating - generalRating += item.getStatEx(sdk.stats.MagicBonus) * tierWeights.generalWeights.MF; // add magic find - generalRating += item.getStatEx(sdk.stats.FHR) * tierWeights.generalWeights.FHR; // add faster hit recovery - generalRating += item.getStatEx(sdk.stats.Defense) * tierWeights.generalWeights.DEF; // add Defense - generalRating += (item.getStatEx(sdk.stats.ToBlock) + item.getStatEx(sdk.stats.FBR)) * tierWeights.generalWeights.ICB; //add increased chance to block - generalRating += (item.getStatEx(sdk.stats.Vitality) + item.getStatEx(sdk.stats.MaxHp) + (item.getStatEx(sdk.stats.PerLevelHp) / 2048 * me.charlvl)) * tierWeights.generalWeights.HP; // add HP - generalRating += (item.getStatEx(sdk.stats.Energy) + item.getStatEx(sdk.stats.MaxMana) + (item.getStatEx(sdk.stats.PerLevelMana) / 2048 * me.charlvl)) * tierWeights.generalWeights.MANA;// add mana - generalRating += item.getStatEx(sdk.stats.Strength) * tierWeights.generalWeights.STR; // add STR - generalRating += item.getStatEx(sdk.stats.Dexterity) * tierWeights.generalWeights.DEX; // add DEX - - return generalRating; + !item.isRuneword && (generalRating += (item.sockets * 10)); // priortize sockets + generalRating += ((item.getStatEx(sdk.stats.PerLevelHp) / 2048 * me.charlvl)) * _tierWeights.gen.get(sdk.stats.PerLevelHp); + generalRating += ((item.getStatEx(sdk.stats.PerLevelMana) / 2048 * me.charlvl)) * _tierWeights.gen.get(sdk.stats.PerLevelMana); + + return [ + sdk.stats.FHR, sdk.stats.FRW, sdk.stats.FBR, sdk.stats.FCR, sdk.stats.ToBlock, + sdk.stats.MagicBonus, sdk.stats.GoldBonus, sdk.stats.Defense, sdk.stats.ManaRecovery, + sdk.stats.Strength, sdk.stats.Dexterity, sdk.stats.Vitality, sdk.stats.Energy, + sdk.stats.MaxHp, sdk.stats.MaxMana, sdk.stats.ReplenishQuantity, sdk.stats.HpRegen, + ].reduce((acc, stat) => acc + item.getStatEx(stat) * _tierWeights.gen.get(stat), generalRating); }; const resistScore = () => { @@ -401,7 +403,7 @@ ]; // only enter next block if we have a new item with resists if (newitemFR || newitemCR || newitemLR || newitemPR) { - const maxRes = me.hell ? 75 : (75 + me.getResPenalty(me.diff + 1) - me.getResPenalty(me.diff)); + const maxRes = me.hell ? 80 : (80 + me.getResPenalty(me.diff + 1) - me.getResPenalty(me.diff)); // get item resists stats from olditem equipped on body location let [olditemFR, olditemCR, olditemLR, olditemPR] = [0, 0, 0, 0]; if (eqItem) { @@ -433,95 +435,65 @@ Math.min(newitemPR, PRlimit) ]; // sum resistRatings - resistRating += effectiveFR * tierWeights.resistWeights.FR; // add fireresist - resistRating += effectiveCR * tierWeights.resistWeights.CR; // add coldresist - resistRating += effectiveLR * tierWeights.resistWeights.LR; // add literesist - resistRating += effectivePR * tierWeights.resistWeights.PR; // add poisonresist + resistRating += effectiveFR * _tierWeights.res.get(sdk.stats.FireResist); + resistRating += effectiveCR * _tierWeights.res.get(sdk.stats.ColdResist); + resistRating += effectiveLR * _tierWeights.res.get(sdk.stats.LightResist); + resistRating += effectivePR * _tierWeights.res.get(sdk.stats.PoisonResist); } - // sum max resists weights - resistRating += (item.getStatEx(sdk.stats.MaxFireResist) * tierWeights.resistWeights.MAXFR); - resistRating += (item.getStatEx(sdk.stats.MaxLightResist) * tierWeights.resistWeights.MAXLR); - resistRating += (item.getStatEx(sdk.stats.MaxColdResist) * tierWeights.resistWeights.MAXCR); - resistRating += (item.getStatEx(sdk.stats.MaxPoisonResist) * tierWeights.resistWeights.MAXPR); - // sum absorb and magic/damage reduction - resistRating += (item.getStatEx(sdk.stats.AbsorbFirePercent) + item.getStatEx(sdk.stats.AbsorbLightPercent) + item.getStatEx(sdk.stats.AbsorbMagicPercent) + item.getStatEx(sdk.stats.AbsorbColdPercent)) * tierWeights.resistWeights.ABS; // add absorb damage - resistRating += item.getStatEx(sdk.stats.NormalDamageReduction) * tierWeights.resistWeights.DR / 2; // add integer damage resist - resistRating += item.getStatEx(sdk.stats.DamageResist) * tierWeights.resistWeights.DR * 2; // add damage resist % - resistRating += item.getStatEx(sdk.stats.MagicDamageReduction) * tierWeights.resistWeights.MR / 2; // add integer magic damage resist - resistRating += item.getStatEx(sdk.stats.MagicResist) * tierWeights.resistWeights.MR * 2; // add magic damage resist % - - return resistRating; + + return ([ + sdk.stats.MaxFireResist, sdk.stats.MaxLightResist, sdk.stats.MaxColdResist, sdk.stats.MaxPoisonResist, + sdk.stats.AbsorbFire, sdk.stats.AbsorbLight, sdk.stats.AbsorbMagic, sdk.stats.AbsorbCold, + sdk.stats.AbsorbFirePercent, sdk.stats.AbsorbLightPercent, sdk.stats.AbsorbMagicPercent, sdk.stats.AbsorbColdPercent, + sdk.stats.NormalDamageReduction, sdk.stats.DamageResist, sdk.stats.MagicDamageReduction, sdk.stats.MagicResist + ].reduce((acc, stat) => acc + item.getStatEx(stat) * _tierWeights.res.get(stat), resistRating)); }; const buildScore = () => { - let buildRating = 0; - let buildWeights = buildInfo.caster ? tierWeights.casterWeights : tierWeights.meleeWeights; - - me.amazon && item.getStatEx(sdk.stats.ReplenishQuantity) && (buildRating += 50); - //!Pather.canTeleport() && item.getStatEx(sdk.stats.ChargedSkill, 3461) && (buildRating += 50); - - buildRating += item.getStatEx(sdk.stats.FCR) * buildWeights.FCR; // add FCR - buildRating += item.getStatEx(sdk.stats.IAS) * buildWeights.IAS; // add IAS - buildRating += item.getStatEx(sdk.stats.HpRegen) * buildWeights.HPREGEN; // add hp regeneration - buildRating += item.getStatEx(sdk.stats.ManaRecovery) * buildWeights.MANAREGEN; // add mana recovery - !item.isRuneword && (buildRating += (item.sockets * 10)); // priortize sockets - - // pierce/mastery's not sure how I want to weight this so for now just its base value - buildInfo.usefulStats.forEach(stat => buildRating += item.getStatEx(stat)); - + // dirty fix maybe? + if (me.barbarian && SetUp.currentBuild !== "Immortalwhirl" && item.strictlyTwoHanded) { + return 0; + } // Melee Specific if (!buildInfo.caster || Config.AttackSkill.includes(sdk.skills.Attack) || Config.LowManaSkill.includes(sdk.skills.Attack) || ([sdk.items.type.Bow, sdk.items.type.AmazonBow, sdk.items.type.Crossbow].includes(item.itemType) && CharData.skillData.bow.onSwitch)) { let meleeRating = 0; + const eleDmgWeight = 0.5; + const eleDmgModifer = [sdk.items.type.Ring, sdk.items.type.Amulet].includes(item.itemType) ? 2 : 1; - // dirty fix maybe? - if (me.barbarian && SetUp.currentBuild !== "Immortalwhirl" && item.strictlyTwoHanded) { - return 0; - } - let eleDmgModifer = [sdk.items.type.Ring, sdk.items.type.Amulet].includes(item.itemType) ? 2 : 1; - - item.getStatEx(sdk.stats.ReplenishDurability) && (meleeRating += 15); - item.getStatEx(sdk.stats.IgnoreTargetDefense) && (meleeRating += 50); - // should these be added and calc avg dmg instead? - // Sometimes we replace good weps with 2-300 ED weps that may be high dmg but aren't as good as the item we replaced - //buildRating += item.getStatEx(sdk.stats.MinDamage) * buildWeights.MINDMG; // add MIN damage - //buildRating += item.getStatEx(sdk.stats.MaxDamage) * buildWeights.MAXDMG; // add MAX damage - //buildRating += item.getStatEx(sdk.stats.SecondaryMinDamage) * buildWeights.SECMINDMG; // add MIN damage - //buildRating += item.getStatEx(sdk.stats.SecondaryMaxDamage) * buildWeights.SECMAXDMG; // add MAX damage - meleeRating += ((item.getStatEx(sdk.stats.MaxDamage) + item.getStatEx(sdk.stats.MinDamage)) / 2) * tierWeights.meleeWeights.AVGDMG; - meleeRating += sumElementalDmg(item) * (tierWeights.meleeWeights.ELEDMG / eleDmgModifer); // add elemental damage - meleeRating += item.getStatEx(sdk.stats.ToHit) * tierWeights.meleeWeights.AR; // add AR - meleeRating += item.getStatEx(sdk.stats.CrushingBlow) * tierWeights.meleeWeights.CB; // add crushing blow - meleeRating += item.getStatEx(sdk.stats.OpenWounds) * tierWeights.meleeWeights.OW; // add open wounds - meleeRating += item.getStatEx(sdk.stats.DeadlyStrike) * tierWeights.meleeWeights.DS; // add deadly strike - meleeRating += item.getStatEx(sdk.stats.LifeLeech) * tierWeights.meleeWeights.LL; // add LL - meleeRating += item.getStatEx(sdk.stats.ManaDrainMinDamage) * tierWeights.meleeWeights.ML; // add ML + meleeRating += ((item.getStatEx(sdk.stats.MaxDamage) + item.getStatEx(sdk.stats.MinDamage)) / 2) * 3; + meleeRating += sumElementalDmg(item) * (eleDmgWeight / eleDmgModifer); // add elemental damage meleeRating += item.getStatEx(sdk.stats.SkillOnAura, sdk.skills.Sanctuary) * 25; // sanctuary aura - meleeRating += item.getStatEx(sdk.stats.DemonDamagePercent) * tierWeights.meleeWeights.DMGTODEMONS; // add damage % to demons - meleeRating += item.getStatEx(sdk.stats.UndeadDamagePercent) * tierWeights.meleeWeights.DMGTOUNDEAD; // add damage % to undead - - buildRating += (buildInfo.caster ? (meleeRating / 2) : meleeRating); + + [ + sdk.stats.ReplenishDurability, sdk.stats.IgnoreTargetDefense, sdk.stats.ToHit, sdk.stats.CrushingBlow, + sdk.stats.OpenWounds, sdk.stats.DeadlyStrike, sdk.stats.LifeLeech, sdk.stats.ManaLeech, + sdk.stats.DemonDamagePercent, sdk.stats.UndeadDamagePercent, + ].reduce((acc, stat) => acc + item.getStatEx(stat) * _tierWeights.gen.get(stat), meleeRating); + buildInfo.caster && (meleeRating /= 2); + + return meleeRating; } - return buildRating; + return 0; }; const skillsScore = () => { - let skillsRating = 0; - let weaponModifer = !buildInfo.caster && item.getItemType() === "Weapon" ? 4 : 1; + let skillsRating = [ + [sdk.stats.AllSkills, -1], [sdk.stats.AddClassSkills, me.classid], [sdk.stats.AddSkillTab, buildInfo.tabSkills], + ].reduce((acc, [stat, subId]) => acc + item.getStatEx(stat, subId) * _tierWeights.skill.get(stat), 0); + (!buildInfo.caster && item.getItemType() === "Weapon") && (skillsRating /= 4); + const _misc = { wanted: 40, useful: 35 }; - skillsRating += item.getStatEx(sdk.stats.AllSkills) * (tierWeights.skillsWeights.ALL / weaponModifer); // + all skills - skillsRating += item.getStatEx(sdk.stats.AddClassSkills, me.classid) * (tierWeights.skillsWeights.CLASS / weaponModifer); // + class skills - skillsRating += item.getStatEx(sdk.stats.AddSkillTab, buildInfo.tabSkills) * (tierWeights.skillsWeights.TAB / weaponModifer); // + TAB skills - let selectedWeights = [tierWeights.skillsWeights.WANTED, tierWeights.skillsWeights.USEFUL]; + let selectedWeights = [_misc.wanted, _misc.useful]; let selectedSkills = [buildInfo.wantedSkills, buildInfo.usefulSkills]; for (let i = 0; i < selectedWeights.length; i++) { for (let j = 0; j < selectedSkills.length; j++) { for (let k = 0; k < selectedSkills[j].length; k++) { - skillsRating += item.getStatEx(107, selectedSkills[j][k]) * selectedWeights[i]; + skillsRating += item.getStatEx(sdk.stats.SingleSkill, selectedSkills[j][k]) * selectedWeights[i]; } } } @@ -533,82 +505,53 @@ }; const ctcScore = () => { - // chance to cast doesn't exist in classic + // chance to cast doesn't exist in classic if (me.classic) return 0; + let ctcRating = 0; + let ctcItems = []; + const stats = item.getStat(-2); const ctcSkillObj = (ctcType, skill, level) => ({ ctcType: ctcType, skill: skill, level: level }); const meleeCheck = !buildInfo.caster; - let ctcRating = 0, ctcItems = []; - let skill, level; - let stats = item.getStat(-2); - - if (stats.hasOwnProperty(sdk.stats.SkillWhenStruck)) { - if (stats[sdk.stats.SkillWhenStruck] instanceof Array) { - for (let i = 0; i < stats[sdk.stats.SkillWhenStruck].length; i++) { - if (stats[sdk.stats.SkillWhenStruck][i] !== undefined) { - ({ skill, level } = stats[sdk.stats.SkillWhenStruck][i]); - ctcItems.push(ctcSkillObj(sdk.stats.SkillWhenStruck, skill, level)); - } - } - } else { - ({ skill, level } = stats[sdk.stats.SkillWhenStruck]); - ctcItems.push(ctcSkillObj(sdk.stats.SkillWhenStruck, skill, level)); - } - } - - if (meleeCheck) { - if (stats.hasOwnProperty(sdk.stats.SkillOnAttack)) { - if (stats[sdk.stats.SkillOnAttack] instanceof Array) { - for (let i = 0; i < stats[sdk.stats.SkillOnAttack].length; i++) { - if (stats[sdk.stats.SkillOnAttack][i] !== undefined) { - ({ skill, level } = stats[sdk.stats.SkillOnAttack][i]); - ctcItems.push(ctcSkillObj(sdk.stats.SkillOnAttack, skill, level)); + /** + * @param {number} type + */ + const buildList = function (type) { + let skill, level; + if (stats.hasOwnProperty(type)) { + if (stats[type] instanceof Array) { + for (let i = 0; i < stats[type].length; i++) { + if (stats[type][i] !== undefined) { + ({ skill, level } = stats[type][i]); + ctcItems.push(ctcSkillObj(type, skill, level)); } } } else { - ({ skill, level } = stats[sdk.stats.SkillOnAttack]); - ctcItems.push(ctcSkillObj(sdk.stats.SkillOnAttack, skill, level)); + ({ skill, level } = stats[type]); + ctcItems.push(ctcSkillObj(type, skill, level)); } } + }; - if (stats.hasOwnProperty(sdk.stats.SkillOnStrike)) { - if (stats[sdk.stats.SkillOnStrike] instanceof Array) { - for (let i = 0; i < stats[sdk.stats.SkillOnStrike].length; i++) { - if (stats[sdk.stats.SkillOnStrike][i] !== undefined) { - ({ skill, level } = stats[sdk.stats.SkillOnStrike][i]); - ctcItems.push(ctcSkillObj(sdk.stats.SkillOnStrike, skill, level)); - } - } - } else { - ({ skill, level } = stats[sdk.stats.SkillOnStrike]); - ctcItems.push(ctcSkillObj(sdk.stats.SkillOnStrike, skill, level)); - } - } + buildList(sdk.stats.SkillWhenStruck); + + if (meleeCheck) { + buildList(sdk.stats.SkillOnAttack); + buildList(sdk.stats.SkillOnStrike); } else { - tierWeights.ctcWeights.skills.Venom = 0; + _tierWeights.ctc.set(sdk.skills.Venom, 0); if (me.charlvl > 50) { - tierWeights.ctcWeights.skills.ChargedBolt = 2; + _tierWeights.ctc.set(sdk.skills.ChargedBolt, 2); } } + if (!ctcItems.length) return 0; - ctcItems = ctcItems.filter((v, i, a) => a.findIndex(el => ["ctcType", "skill"].every(k => el[k] === v[k])) === i); - - // might come back to redo the tierWieghts object but quick map for ctc - const ctcType = {}; - ctcType[sdk.stats.SkillOnAttack] = tierWeights.ctcWeights.onAttack; - ctcType[sdk.stats.SkillOnStrike] = tierWeights.ctcWeights.onStrike; - ctcType[sdk.stats.SkillWhenStruck] = tierWeights.ctcWeights.whenStruck; - - for (let i = 0; i < ctcItems.length; i++) { - try { - let skillName = getSkillById(ctcItems[i].skill).split(" ").join(""); - if (!!tierWeights.ctcWeights.skills[skillName] && ctcType[ctcItems[i].ctcType]) { - ctcRating += (ctcItems[i].level * tierWeights.ctcWeights.skills[skillName] * ctcType[ctcItems[i].ctcType]); - } - } catch (e) { - console.error(e); - } - } + ctcItems + .filter((v, i, a) => a.findIndex(el => ["ctcType", "skill"].every(k => el[k] === v[k])) === i) + .forEach(el => { + if (!_tierWeights.ctc.has(el.skill)) return; + ctcRating += (el.level * _tierWeights.ctc.get(el.skill) * _tierWeights.ctc.get(el.ctcType)); + }); return ctcRating; }; @@ -626,7 +569,7 @@ tier += NTIP.MAX_TIER; } - return item.questItem ? -1 : Math.max(1, tier); + return Math.max(1, tier); }; /** @@ -635,19 +578,17 @@ const secondaryscore = function (item) { let tier = 0; - tier += item.getStatEx(sdk.stats.AllSkills) * 200; // + all skills - tier += item.getStatEx(sdk.stats.AddClassSkills, me.classid) * 100; // + class skills - tier += item.getStatEx(sdk.stats.AddSkillTab, Check.finalBuild().tabSkills) * 75; // + TAB skills - let precastSkills = [Check.finalBuild().precastSkills]; - - for (let i = 0; i < precastSkills.length; i++) { - tier += item.getStatEx(107, precastSkills[i]) * 50; - } + Check.finalBuild().precastSkills + .forEach(skill => tier += item.getStatEx(sdk.stats.SingleSkill, skill) * 50); tier += item.getStatEx(sdk.stats.FCR) * 5; // add FCR tier += item.getStatEx(sdk.stats.FHR) * 3; // add faster hit recovery - return tier; + return [ + [sdk.stats.AllSkills, -1], + [sdk.stats.AddClassSkills, me.classid], + [sdk.stats.AddSkillTab, Check.finalBuild().tabSkills], + ].reduce((acc, [stat, subId]) => acc + item.getStatEx(stat, subId) * _tierWeights.skill.get(stat), tier); }; /** @@ -665,31 +606,20 @@ if (item.unique) { charmRating += item.getStatEx(sdk.stats.Strength); // handle +all atrributes charmRating += item.getStatEx(sdk.stats.AllRes); - if (item.isAnni) { - charmRating += item.getStatEx(sdk.stats.AllSkills) * tierWeights.charmWeights.ALL; + charmRating += item.getStatEx(sdk.stats.AllSkills) * _tierWeights.charms.get(sdk.stats.AllSkills); charmRating += item.getStatEx(sdk.stats.AddExperience); } else if (item.isGheeds) { charmRating += item.getStatEx(sdk.stats.GoldBonus); charmRating += item.getStatEx(sdk.stats.ReducedPrices) * 1.5; - charmRating += item.getStatEx(sdk.stats.MagicBonus) * tierWeights.charmWeights.MF; + charmRating += item.getStatEx(sdk.stats.MagicBonus) * _tierWeights.charms.get(sdk.stats.MagicBonus); } else { - charmRating += item.getStatEx(sdk.stats.AddClassSkills, me.classid) * tierWeights.charmWeights.CLASS; // + class skills + charmRating += item.getStatEx(sdk.stats.AddClassSkills, me.classid) * _tierWeights.charms.get(sdk.stats.AddClassSkills); } } else { - charmRating += item.getStatEx(sdk.stats.AddSkillTab, buildInfo.tabSkills) * tierWeights.charmWeights.TAB; // + TAB skills - charmRating += item.getStatEx(sdk.stats.FireResist) * tierWeights.charmWeights.FR; // add FR - charmRating += item.getStatEx(sdk.stats.ColdResist) * tierWeights.charmWeights.CR; // add CR - charmRating += item.getStatEx(sdk.stats.LightResist) * tierWeights.charmWeights.LR; // add LR - charmRating += item.getStatEx(sdk.stats.PoisonResist) * tierWeights.charmWeights.PR; // add PR - charmRating += item.getStatEx(sdk.stats.FRW) * tierWeights.charmWeights.FRW; // add faster run walk - charmRating += item.getStatEx(sdk.stats.FHR) * tierWeights.charmWeights.FHR; // add faster hit recovery - charmRating += item.getStatEx(sdk.stats.Defense) * tierWeights.charmWeights.DEF; // add Defense - charmRating += item.getStatEx(sdk.stats.MagicBonus) * tierWeights.charmWeights.MF; // add magic find - charmRating += (item.getStatEx(sdk.stats.Vitality) + item.getStatEx(sdk.stats.MaxHp) + (item.getStatEx(sdk.stats.PerLevelHp) / 2048 * me.charlvl)) * tierWeights.charmWeights.HP; // add HP - charmRating += (item.getStatEx(sdk.stats.Energy) + item.getStatEx(sdk.stats.MaxMana) + (item.getStatEx(sdk.stats.PerLevelMana) / 2048 * me.charlvl)) * tierWeights.charmWeights.MANA;// add mana - charmRating += item.getStatEx(sdk.stats.Strength) * tierWeights.charmWeights.STR; // add STR - charmRating += item.getStatEx(sdk.stats.Dexterity) * tierWeights.charmWeights.DEX; // add DEX + charmRating += item.getStatEx(sdk.stats.AddSkillTab, buildInfo.tabSkills) * _tierWeights.charms.get(sdk.stats.AddSkillTab); + charmRating += ((item.getStatEx(sdk.stats.PerLevelHp) / 2048 * me.charlvl)) * _tierWeights.charms.get(sdk.stats.PerLevelHp); + charmRating += ((item.getStatEx(sdk.stats.PerLevelMana) / 2048 * me.charlvl)) * _tierWeights.charms.get(sdk.stats.PerLevelMana); if (!buildInfo.caster) { charmRating += item.getStatEx(sdk.stats.MinDamage) * 3; // add MIN damage @@ -697,8 +627,14 @@ charmRating += sumElementalDmg(item); // add elemental damage charmRating += item.getStatEx(sdk.stats.ToHit) * 0.5; // add AR } - } + return [ + sdk.stats.MaxHp, sdk.stats.MaxMana, + sdk.stats.FireResist, sdk.stats.LightResist, sdk.stats.ColdResist, sdk.stats.PoisonResist, + sdk.stats.FHR, sdk.stats.FRW, sdk.stats.MagicBonus, sdk.stats.GoldBonus, sdk.stats.Defense, + sdk.stats.Strength, sdk.stats.Dexterity, sdk.stats.Vitality, sdk.stats.Energy, + ].reduce((acc, stat) => acc + item.getStatEx(stat) * _tierWeights.charms.get(stat), charmRating); + } return charmRating; }; diff --git a/libs/SoloPlay/Functions/Globals.js b/libs/SoloPlay/Functions/Globals.js index a883ce6d..c508931c 100644 --- a/libs/SoloPlay/Functions/Globals.js +++ b/libs/SoloPlay/Functions/Globals.js @@ -383,6 +383,11 @@ const SetUp = { }, config: function () { + me.equipped.init(); + // just initializes the data + Check.currentBuild(); + Check.finalBuild(); + Config.socketables = []; Config.AutoEquip = true; @@ -617,7 +622,7 @@ const Check = { let gold = me.gold; let goldLimit = [25000, 50000, 100000][me.diff]; - if ((me.normal && !Pather.accessToAct(2)) || gold >= goldLimit) { + if ((me.normal && !me.accessToAct(2)) || gold >= goldLimit) { return true; } @@ -632,7 +637,7 @@ const Check = { switch (true) { case (me.charlvl < 15): - case (me.normal && !Pather.accessToAct(2)): + case (me.normal && !me.accessToAct(2)): case (gold >= lowGold): case (me.charlvl >= 15 && gold > Math.floor(lowGold / 2) && gold > me.getRepairCost()): return false; @@ -650,14 +655,14 @@ const Check = { let gold = me.gold; // Almost broken but not quite - if (((Item.getEquipped(sdk.body.RightArm).durability <= 30 && Item.getEquipped(sdk.body.RightArm).durability > 0) - || (Item.getEquipped(sdk.body.LeftArm).durability <= 30 && Item.getEquipped(sdk.body.LeftArm).durability > 0) + if (((me.equipped.get(sdk.body.RightArm).durability <= 30 && me.equipped.get(sdk.body.RightArm).durability > 0) + || (me.equipped.get(sdk.body.LeftArm).durability <= 30 && me.equipped.get(sdk.body.LeftArm).durability > 0) && !me.getMerc() && me.charlvl >= 15 && !me.normal && !me.nightmare && gold < 1000)) { return 1; } // Broken - if ((Item.getEquipped(sdk.body.RightArm).durability === 0 || Item.getEquipped(sdk.body.LeftArm).durability === 0) && me.charlvl >= 15 && !me.normal && gold < 1000) { + if ((me.equipped.get(sdk.body.RightArm).durability === 0 || me.equipped.get(sdk.body.LeftArm).durability === 0) && me.charlvl >= 15 && !me.normal && gold < 1000) { return 2; } @@ -774,7 +779,7 @@ const Check = { || (!me.paladin && me.checkItem({ name: sdk.locale.items.Spirit, itemtype: sdk.items.type.Sword }).have) || (me.paladin && me.haveAll([{ name: sdk.locale.items.Spirit, itemtype: sdk.items.type.Sword }, { name: sdk.locale.items.Spirit, itemtype: sdk.items.type.AuricShields }])) || (me.necromancer && me.checkItem({ name: sdk.locale.items.White }).have - && (me.checkItem({ name: sdk.locale.items.Rhyme, itemtype: sdk.items.type.VoodooHeads }).have || Item.getEquipped(sdk.body.LeftArm).tier > 800)) + && (me.checkItem({ name: sdk.locale.items.Rhyme, itemtype: sdk.items.type.VoodooHeads }).have || me.equipped.get(sdk.body.LeftArm).tier > 800)) || (me.barbarian && (me.checkItem({ name: sdk.locale.items.Lawbringer }).have || me.baal))) { needRunes = false; } @@ -1049,7 +1054,7 @@ const Check = { usePreviousSocketQuest: function () { if (me.classic) return; if (!Check.resistance().Status) { - if (me.weaponswitch === 0 && Item.getEquipped(sdk.body.LeftArm).fname.includes("Lidless Wall") && !Item.getEquipped(sdk.body.LeftArm).socketed) { + if (me.weaponswitch === 0 && me.equipped.get(sdk.body.LeftArm).fname.includes("Lidless Wall") && !me.equipped.get(sdk.body.LeftArm).socketed) { if (!me.normal) { if (!me.data.normal.socketUsed) goToDifficulty(sdk.difficulty.Normal, " to use socket quest"); if (me.hell && !me.data.nightmare.socketUsed) goToDifficulty(sdk.difficulty.Nightmare, " to use socket quest"); diff --git a/libs/SoloPlay/Functions/ItemOverrides.js b/libs/SoloPlay/Functions/ItemOverrides.js index 6466e497..8fd86b7f 100644 --- a/libs/SoloPlay/Functions/ItemOverrides.js +++ b/libs/SoloPlay/Functions/ItemOverrides.js @@ -104,53 +104,6 @@ Item.getBodyLoc = function (item) { return []; }; -// todo: clean this up -Item.getEquipped = function (bodyLoc = -1) { - let item = me.getItemsEx().filter((item) => item.isEquipped && item.bodylocation === bodyLoc).first(); - - if (item) { - return { - classid: item.classid, - name: item.name, - fname: item.fname, - prefixnum: item.prefixnum, - itemType: item.itemType, - quality: item.quality, - tier: NTIP.GetTier(item), - tierScore: tierscore(item, 1, bodyLoc), - secondarytier: NTIP.GetSecondaryTier(item), - str: item.getStatEx(sdk.stats.Strength), - dex: item.getStatEx(sdk.stats.Dexterity), - durability: item.durabilityPercent, - sockets: item.sockets, - socketed: item.getItemsEx().length > 0, - isRuneword: item.runeword, - twoHanded: item.twoHanded, - finalItem: NTIP.GetTier(item) >= NTIP.MAX_TIER, - }; - } - - // Don't have anything equipped in there - return { - classid: -1, - name: "none", - fname: "none", - prefixnum: -1, - itemType: -1, - quality: -1, - tier: -1, - tierScore: -1, - secondarytier: -1, - str: 0, - dex: 0, - durability: 0, - sockets: 0, - socketed: false, - isRuneword: false, - twoHanded: false, - }; -}; - /** * @param {ItemUnit} item */ @@ -171,7 +124,7 @@ Item.autoEquipCheck = function (item, basicCheck = false) { let bodyLoc = this.getBodyLoc(item); for (let loc of bodyLoc) { - let equippedItem = Item.getEquipped(loc); + const equippedItem = me.equipped.get(loc); // rings are special if (item.isInStorage && item.itemType === sdk.items.type.Ring) { @@ -184,19 +137,19 @@ Item.autoEquipCheck = function (item, basicCheck = false) { } else if (tier > equippedItem.tier && (basicCheck ? true : this.canEquip(item) || !item.identified)) { if (Item.canEquip(item)) { if (item.twoHanded && !me.barbarian) { - if (tier < Item.getEquipped(sdk.body.RightArm).tier + Item.getEquipped(sdk.body.LeftArm).tier) return false; + if (tier < me.equipped.get(sdk.body.RightArm).tier + me.equipped.get(sdk.body.LeftArm).tier) return false; } - if (!me.barbarian && loc === sdk.body.LeftArm && Item.getEquipped(loc).tier === -1) { - if (Item.getEquipped(sdk.body.RightArm).twoHanded && tier < Item.getEquipped(sdk.body.RightArm).tier) return false; + if (!me.barbarian && loc === sdk.body.LeftArm && me.equipped.get(loc).tier === -1) { + if (me.equipped.get(sdk.body.RightArm).twoHanded && tier < me.equipped.get(sdk.body.RightArm).tier) return false; } return true; } else { /** - * @param {ItemUnit} item - * @returns {boolean} - */ + * @param {ItemUnit} item + * @returns {boolean} + */ const checkForBetterItem = (item) => { let betterItem = me.getItemsEx() .filter(el => el.isInStorage && el.gid !== item.gid && el.identified && Item.getBodyLoc(el).includes(loc)) @@ -271,7 +224,11 @@ Item.autoEquip = function (task = "") { console.debug(prettyName + " tier: " + tier); if (this.equip(item, bodyLoc)) { - console.log("ÿc9" + task + "ÿc0 :: Equipped: " + prettyName + " Tier: " + tier); + console.log( + "ÿc9" + task + "ÿc0 :: \n" + + "ÿc9 - Equippedÿc0: " + prettyName + "\n" + + "ÿc9 - Tierÿc0: " + tier + ); // item that can have sockets if (item.getItemType()) { SoloWants.addToList(item); @@ -279,9 +236,14 @@ Item.autoEquip = function (task = "") { } Developer.debugging.autoEquip && Item.logItem(task, me.getItem(-1, -1, gid)); Developer.logEquipped && MuleLogger.logEquippedItems(); + me.equipped.set(bodyLoc, item); } else if (!noStash && item.lvlreq > me.charlvl && !item.isInStash) { if (Storage.Stash.CanFit(item)) { - console.log("ÿc9" + task + "ÿc0 :: Item level is to high, attempting to stash for now as its better than what I currently have: " + prettyName + " Tier: " + tier); + console.log( + "ÿc9" + task + "ÿc0 :: \n" + + "- " + prettyName + " Item req is too high (" + item.lvlreq + ") for my level (" + me.charlvl + ") \n" + + "- Stash for now as its better than what I currently have. Tier: " + tier + ); Storage.Stash.MoveTo(item); } } else if (me.getItem(-1, -1, gid)) { @@ -299,13 +261,14 @@ Item.autoEquip = function (task = "") { } // ring check - sometimes a higher tier ring ends up on the wrong finger causing a rollback loop - if (Item.getEquipped(sdk.body.RingLeft).tierScore > Item.getEquipped(sdk.body.RingRight).tierScore) { + if (me.equipped.get(sdk.body.RingLeft).tierScore > me.equipped.get(sdk.body.RingRight).tierScore) { console.log("ÿc9" + task + "ÿc0 :: Swapping rings, higher tier ring is on the wrong finger"); clickItemAndWait(sdk.clicktypes.click.item.Left, sdk.body.RingLeft); delay(200); me.itemoncursor && clickItemAndWait(sdk.clicktypes.click.item.Left, sdk.body.RingRight); delay(200); me.itemoncursor && clickItemAndWait(sdk.clicktypes.click.item.Left, sdk.body.RingLeft); + me.equipped.init(); } !getUIFlag(sdk.uiflags.Shop) && me.cancel(); @@ -319,7 +282,7 @@ Item.autoEquip = function (task = "") { let bodyLoc = this.getBodyLoc(item); for (let loc of bodyLoc) { - const equippedItem = Item.getEquipped(loc); + const equippedItem = me.equipped.get(loc); if (equippedItem.classid === sdk.items.quest.KhalimsWill) continue; // rings are special if (item.itemType === sdk.items.type.Ring) { @@ -334,17 +297,18 @@ Item.autoEquip = function (task = "") { } } else { if (tier > equippedItem.tier) { + console.debug("EquippedItem :: " + equippedItem.prettyPrint + " |ÿc0 Tier: " + equippedItem.tier); Item.identify(item); if (item.twoHanded && !me.barbarian) { - if (tier < Item.getEquipped(sdk.body.RightArm).tier + Item.getEquipped(sdk.body.LeftArm).tier) { + if (tier < me.equipped.get(sdk.body.RightArm).tier + me.equipped.get(sdk.body.LeftArm).tier) { continue; } console.log("ÿc9" + task + "ÿc0 :: TwoHandedWep better than sum tier of currently equipped main + shield hand : " + item.fname + " Tier: " + tier); } - if (!me.barbarian && loc === sdk.body.LeftArm && equippedItem.tier === -1 && Item.getEquipped(sdk.body.RightArm).twoHanded) { - if (tier < Item.getEquipped(sdk.body.RightArm).tier) { + if (!me.barbarian && loc === sdk.body.LeftArm && equippedItem.tier === -1 && me.equipped.get(sdk.body.RightArm).twoHanded) { + if (tier < me.equipped.get(sdk.body.RightArm).tier) { continue; } console.log("ÿc9" + task + "ÿc0 :: TwoHandedWep not as good as what we want to equip on our shield hand : " + item.fname + " Tier: " + tier); @@ -405,14 +369,14 @@ Item.equip = function (item, bodyLoc) { if (cursorItem) { // rollback check - let justEquipped = Item.getEquipped(bodyLoc); + let justEquipped = me.equipped.get(bodyLoc); let checkScore = 0; switch (cursorItem.itemType) { case sdk.items.type.Ring: checkScore = tierscore(cursorItem, 1, bodyLoc); if (checkScore > justEquipped.tierScore) { console.debug("ROLLING BACK TO OLD ITEM BECAUSE IT WAS BETTER"); - console.debug("OldItem: " + checkScore + " Just Equipped Item: " + Item.getEquipped(bodyLoc).tierScore); + console.debug("OldItem: " + checkScore + " Just Equipped Item: " + me.equipped.get(bodyLoc).tierScore); clickItemAndWait(sdk.clicktypes.click.item.Left, bodyLoc); cursorItem = Game.getCursorUnit(); rolledBack = true; @@ -423,7 +387,7 @@ Item.equip = function (item, bodyLoc) { checkScore = NTIP.GetTier(cursorItem); if (checkScore > justEquipped.tier && !item.questItem && !justEquipped.isRuneword/*Wierd bug with runewords that it'll fail to get correct item desc so don't attempt rollback*/) { console.debug("ROLLING BACK TO OLD ITEM BECAUSE IT WAS BETTER"); - console.debug("OldItem: " + checkScore + " Just Equipped Item: " + Item.getEquipped(bodyLoc).tier); + console.debug("OldItem: " + checkScore + " Just Equipped Item: " + me.equipped.get(bodyLoc).tier); clickItemAndWait(sdk.clicktypes.click.item.Left, bodyLoc); cursorItem = Game.getCursorUnit(); rolledBack = true; @@ -492,22 +456,18 @@ Item.hasSecondaryTier = (item) => Config.AutoEquip && me.expansion && NTIP.GetSe * @param {ItemUnit} item */ Item.getSecondaryBodyLoc = function (item) { - let bodyLoc = (() => { - switch (true) { - case Item.shieldTypes.includes(item.itemType): - return sdk.body.LeftArmSecondary; - case Item.weaponTypes.includes(item.itemType): - return me.barbarian && item.twoHanded && !item.strictlyTwoHanded - ? [sdk.body.RightArmSecondary, sdk.body.LeftArmSecondary] - : sdk.body.RightArmSecondary; - case [sdk.items.type.HandtoHand, sdk.items.type.AssassinClaw].includes(item.itemType): - return !Check.currentBuild().caster && me.assassin ? [sdk.body.RightArmSecondary, sdk.body.LeftArmSecondary] : sdk.body.RightArmSecondary; - default: - return false; - } - })(); - - return Array.isArray(bodyLoc) ? bodyLoc : [bodyLoc]; + if (Item.shieldTypes.includes(item.itemType)) return [sdk.body.LeftArmSecondary]; + if ([sdk.items.type.HandtoHand, sdk.items.type.AssassinClaw].includes(item.itemType)) { + return !Check.currentBuild().caster && me.assassin + ? [sdk.body.RightArmSecondary, sdk.body.LeftArmSecondary] + : [sdk.body.RightArmSecondary]; + } + if (Item.weaponTypes.includes(item.itemType)) { + return me.barbarian && item.twoHanded && !item.strictlyTwoHanded + ? [sdk.body.RightArmSecondary, sdk.body.LeftArmSecondary] + : [sdk.body.RightArmSecondary]; + } + return []; }; /** @@ -562,10 +522,11 @@ Item.autoEquipCheckSecondary = function (item) { if (me.classic) return false; let tier = NTIP.GetSecondaryTier(item); + if (tier <= 0) return false; let bodyLoc = Item.getSecondaryBodyLoc(item); - for (let i = 0; tier > 0 && i < bodyLoc.length; i += 1) { - if (tier > Item.getEquipped(bodyLoc[i]).secondarytier && (Item.canEquip(item) || !item.identified)) { + for (let loc of bodyLoc) { + if (tier > me.equipped.get(loc).secondaryTier && (Item.canEquip(item) || !item.identified)) { return true; } } @@ -605,27 +566,30 @@ Item.autoEquipSecondary = function (task = "") { while (items.length > 0) { items.sort(sortEq); const item = items.shift(); + if (!item.isInStorage) continue; const tier = NTIP.GetSecondaryTier(item); + if (tier <= 0) continue; let bodyLoc = Item.getSecondaryBodyLoc(item); - if (tier > 0 && bodyLoc) { - for (let j = 0; j < bodyLoc.length; j += 1) { - const equippedItem = Item.getEquipped(bodyLoc[j]); - if (item.isInStorage && tier > equippedItem.secondarytier && equippedItem.classid !== sdk.items.quest.KhalimsWill) { - Item.identify(item); - - let gid = item.gid; - let prettyName = item.prettyPrint; - console.debug(prettyName + " tier: " + tier); + for (let loc of bodyLoc) { + const equippedItem = me.equipped.get(loc); + // should never happen - but just in case + if (equippedItem.classid === sdk.items.quest.KhalimsWill) continue; + if (tier > equippedItem.secondaryTier) { + Item.identify(item); - if (this.secondaryEquip(item, bodyLoc[j])) { - console.log("ÿc9SecondaryEquipÿc0 :: Equipped: " + prettyName + " SecondaryTier: " + tier); - Developer.debugging.autoEquip && Item.logItem("Equipped switch", me.getItem(-1, -1, gid)); - Developer.logEquipped && MuleLogger.logEquippedItems(); - } + let gid = item.gid; + let prettyName = item.prettyPrint; + console.debug(prettyName + " tier: " + tier); - break; + if (this.secondaryEquip(item, loc)) { + console.log("ÿc9SecondaryEquipÿc0 :: Equipped: " + prettyName + " SecondaryTier: " + tier); + Developer.debugging.autoEquip && Item.logItem("Equipped switch", me.getItem(-1, -1, gid)); + Developer.logEquipped && MuleLogger.logEquippedItems(); + me.equipped.set(loc, item); } + + break; } } } @@ -710,7 +674,9 @@ Item.getMercEquipped = function (bodyLoc = -1) { let mercenary = me.getMercEx(); if (mercenary) { - let item = mercenary.getItemsEx().filter((item) => item.isEquipped && item.bodylocation === bodyLoc).first(); + let item = mercenary.getItemsEx() + .filter((item) => item.isEquipped && item.bodylocation === bodyLoc) + .first(); if (item) { return { @@ -737,37 +703,40 @@ Item.getMercEquipped = function (bodyLoc = -1) { /** * @param {ItemUnit} item + * @returns {number[]} */ Item.getBodyLocMerc = function (item) { - let mercenary = me.getMercEx(); - - // dont have merc or he is dead - if (!mercenary) return false; - - let bodyLoc = (() => { - switch (item.itemType) { - case sdk.items.type.Shield: - return (mercenary.classid === sdk.mercs.IronWolf ? sdk.body.LeftArm : []); - case sdk.items.type.Armor: - return sdk.body.Armor; - case sdk.items.type.Helm: - case sdk.items.type.Circlet: - return sdk.body.Head; - case sdk.items.type.PrimalHelm: - return (mercenary.classid === sdk.mercs.A5Barb ? sdk.body.Head : []); - case sdk.items.type.Bow: - return (mercenary.classid === sdk.mercs.Rogue ? sdk.body.RightArm : []); - case sdk.items.type.Spear: - case sdk.items.type.Polearm: - return (mercenary.classid === sdk.mercs.Guard ? sdk.body.RightArm : []); - case sdk.items.type.Sword: - return ([sdk.mercs.IronWolf, sdk.mercs.A5Barb].includes(mercenary.classid) ? sdk.body.RightArm : []); - default: - return false; - } - })(); + let _mercId = me.data.merc.classid; - return Array.isArray(bodyLoc) ? bodyLoc : [bodyLoc]; + switch (item.itemType) { + case sdk.items.type.Shield: + return (_mercId === sdk.mercs.IronWolf + ? [sdk.body.LeftArm] + : []); + case sdk.items.type.Armor: + return [sdk.body.Armor]; + case sdk.items.type.Helm: + case sdk.items.type.Circlet: + return [sdk.body.Head]; + case sdk.items.type.PrimalHelm: + return (_mercId === sdk.mercs.A5Barb + ? [sdk.body.Head] + : []); + case sdk.items.type.Bow: + return (_mercId === sdk.mercs.Rogue + ? [sdk.body.RightArm] + : []); + case sdk.items.type.Spear: + case sdk.items.type.Polearm: + return (_mercId === sdk.mercs.Guard + ? [sdk.body.RightArm] + : []); + case sdk.items.type.Sword: + return ([sdk.mercs.IronWolf, sdk.mercs.A5Barb].includes(_mercId) + ? sdk.body.RightArm + : []); + } + return []; }; /** @@ -779,23 +748,22 @@ Item.autoEquipCheckMerc = function (item, basicCheck = false) { if (Config.AutoEquip && !me.getMercEx()) return false; let tier = NTIP.GetMercTier(item); + if (tier <= 0) return false; let bodyLoc = Item.getBodyLocMerc(item); - if (tier > 0 && bodyLoc) { - for (let i = 0; i < bodyLoc.length; i += 1) { - let oldTier = Item.getMercEquipped(bodyLoc[i]).tier; // Low tier items shouldn't be kept if they can't be equipped + for (let loc of bodyLoc) { + let oldTier = Item.getMercEquipped(loc).tier; - if (tier > oldTier) { - if (Item.canEquipMerc(item) || !item.identified) { + if (tier > oldTier) { + if (Item.canEquipMerc(item) || !item.identified) { + return true; + } else if (basicCheck) { + // keep wanted final gear items + if (NTIP.CheckItem(item, NTIP.FinalGear.list) === Pickit.Result.WANTED) { return true; - } else if (basicCheck) { - // keep wanted final gear items - if (NTIP.CheckItem(item, NTIP.FinalGear.list) === Pickit.Result.WANTED) { - return true; - } - - return false; } + + return false; } } } @@ -812,17 +780,13 @@ Item.autoEquipMerc = function () { let tier = NTIP.GetMercTier(item); return (item.identified ? tier > 0 : tier !== 0); }); - if (!items.length) return false; function sortEq (a, b) { - if (Item.canEquipMerc(a) && Item.canEquipMerc(b)) { - return NTIP.GetMercTier(b) - NTIP.GetMercTier(a); - } - - if (Item.canEquipMerc(a)) return -1; - if (Item.canEquipMerc(b)) return 1; - + let [prioA, prioB] = [Item.canEquip(a), Item.canEquip(b)]; + if (prioA && prioB) return NTIP.GetMercTier(b) - NTIP.GetMercTier(a); + if (prioA) return -1; + if (prioB) return 1; return 0; } @@ -832,26 +796,25 @@ Item.autoEquipMerc = function () { items.sort(sortEq); const item = items.shift(); const tier = NTIP.GetMercTier(item); + if (tier <= 0) continue; const bodyLoc = Item.getBodyLocMerc(item); const name = item.name; - if (tier > 0 && bodyLoc) { - for (let j = 0; j < bodyLoc.length; j += 1) { - if ([sdk.storage.Inventory, sdk.storage.Stash].includes(item.location) && tier > Item.getMercEquipped(bodyLoc[j]).tier) { - Item.identify(item); - - console.log("Merc " + name); - this.equipMerc(item, bodyLoc[j]) && console.log("ÿc9MercEquipÿc0 :: Equipped: " + name + " MercTier: " + tier); - - let cursorItem = Game.getCursorUnit(); + for (let loc of bodyLoc) { + if ([sdk.storage.Inventory, sdk.storage.Stash].includes(item.location) && tier > Item.getMercEquipped(loc).tier) { + Item.identify(item); - if (cursorItem) { - cursorItem.drop(); - Developer.debugging.autoEquip && Item.logItem("Merc Dropped", cursorItem); - } + console.log("Merc " + name); + this.equipMerc(item, loc) && console.log("ÿc9MercEquipÿc0 :: Equipped: " + name + " MercTier: " + tier); + + let cursorItem = Game.getCursorUnit(); - break; + if (cursorItem) { + cursorItem.drop(); + Developer.debugging.autoEquip && Item.logItem("Merc Dropped", cursorItem); } + + break; } } } diff --git a/libs/SoloPlay/Functions/ItemPrototypes.js b/libs/SoloPlay/Functions/ItemPrototypes.js index 9126ec9e..3b28e1fb 100644 --- a/libs/SoloPlay/Functions/ItemPrototypes.js +++ b/libs/SoloPlay/Functions/ItemPrototypes.js @@ -154,13 +154,13 @@ Unit.prototype.autoEquipCheck = function (checkCanEquip = true) { if (tier > 0 && bodyLoc.length) { for (let i = 0; i < bodyLoc.length; i += 1) { - if (tier > Item.getEquipped(bodyLoc[i]).tier && ((checkCanEquip ? this.canEquip() : true) || !this.identified)) { + if (tier > me.equipped.get(bodyLoc[i]).tier && ((checkCanEquip ? this.canEquip() : true) || !this.identified)) { if (this.twoHanded && !me.barbarian) { - if (tier < Item.getEquipped(sdk.body.RightArm).tier + Item.getEquipped(sdk.body.LeftArm).tier) return false; + if (tier < me.equipped.get(sdk.body.RightArm).tier + me.equipped.get(sdk.body.LeftArm).tier) return false; } - if (!me.barbarian && bodyLoc[i] === 5 && Item.getEquipped(bodyLoc[i]).tier === -1) { - if (Item.getEquipped(sdk.body.RightArm).twoHanded && tier < Item.getEquipped(sdk.body.RightArm).tier) return false; + if (!me.barbarian && bodyLoc[i] === 5 && me.equipped.get(bodyLoc[i]).tier === -1) { + if (me.equipped.get(sdk.body.RightArm).twoHanded && tier < me.equipped.get(sdk.body.RightArm).tier) return false; } return true; @@ -180,13 +180,13 @@ Unit.prototype.autoEquipCheckSecondary = function (checkCanEquip = true) { if (tier > 0 && bodyLoc.length) { for (let i = 0; i < bodyLoc.length; i += 1) { - if (tier > Item.getEquipped(bodyLoc[i]).secondarytier && ((checkCanEquip ? this.canEquip() : true) || !this.identified)) { + if (tier > me.equipped.get(bodyLoc[i]).secondaryTier && ((checkCanEquip ? this.canEquip() : true) || !this.identified)) { if (this.twoHanded && !me.barbarian) { - if (tier < Item.getEquipped(sdk.body.RightArmSecondary).secondarytier + Item.getEquipped(sdk.body.LeftArmSecondary).secondarytier) return false; + if (tier < me.equipped.get(sdk.body.RightArmSecondary).secondaryTier + me.equipped.get(sdk.body.LeftArmSecondary).secondaryTier) return false; } - if (!me.barbarian && bodyLoc[i] === 12 && Item.getEquipped(bodyLoc[i]).secondarytier === -1) { - if (Item.getEquipped(sdk.body.RightArmSecondary).twoHanded && tier < Item.getEquipped(sdk.body.RightArmSecondary).secondarytier) return false; + if (!me.barbarian && bodyLoc[i] === 12 && me.equipped.get(bodyLoc[i]).secondaryTier === -1) { + if (me.equipped.get(sdk.body.RightArmSecondary).twoHanded && tier < me.equipped.get(sdk.body.RightArmSecondary).secondaryTier) return false; } return true; @@ -258,10 +258,10 @@ Unit.prototype.equipItem = function (bodyLoc = -1) { if (cursorItem) { // rollback check - let justEquipped = Item.getEquipped(bodyLoc); + let justEquipped = me.equipped.get(bodyLoc); if (NTIP.GetTier(cursorItem) > justEquipped.tier && !this.questItem && !justEquipped.isRuneword/*Wierd bug with runewords that it'll fail to get correct item desc so don't attempt rollback*/) { console.debug("ROLLING BACK TO OLD ITEM BECAUSE IT WAS BETTER"); - console.debug("OldItem: " + NTIP.GetTier(cursorItem) + " Just Equipped Item: " + Item.getEquipped(bodyLoc).tier); + console.debug("OldItem: " + NTIP.GetTier(cursorItem) + " Just Equipped Item: " + me.equipped.get(bodyLoc).tier); clickItemAndWait(sdk.clicktypes.click.item.Left, bodyLoc); cursorItem = Game.getCursorUnit(); rolledBack = true; diff --git a/libs/SoloPlay/Functions/NPCAction.js b/libs/SoloPlay/Functions/NPCAction.js index 834f7d5f..92fdda9d 100644 --- a/libs/SoloPlay/Functions/NPCAction.js +++ b/libs/SoloPlay/Functions/NPCAction.js @@ -530,7 +530,7 @@ if (Town.gambleIds.length === 0) return true; // avoid Alkor - me.act === 3 && Town.goToTown(Pather.accessToAct(4) ? 4 : 2); + me.act === 3 && Town.goToTown(me.accessToAct(4) ? 4 : 2); let npc = Town.initNPC("Gamble", "gamble"); if (!npc) return false; @@ -609,7 +609,7 @@ for (let i = 0; i < repairAction.length; i += 1) { switch (repairAction[i]) { case "repair": - me.act === 3 && Town.goToTown(Pather.accessToAct(4) ? 4 : 2); + me.act === 3 && Town.goToTown(me.accessToAct(4) ? 4 : 2); npc = Town.initNPC("Repair", "repair"); if (!npc) return false; me.repair(); @@ -650,7 +650,7 @@ let preArea = me.area; // avoid Aheara - me.act === 3 && Town.goToTown(Pather.accessToAct(4) ? 4 : 2); + me.act === 3 && Town.goToTown(me.accessToAct(4) ? 4 : 2); let npc = Town.initNPC("Merc", "reviveMerc"); if (!npc) return false; diff --git a/libs/SoloPlay/Functions/Quest.js b/libs/SoloPlay/Functions/Quest.js index 6e5e6b21..0d26b9e1 100644 --- a/libs/SoloPlay/Functions/Quest.js +++ b/libs/SoloPlay/Functions/Quest.js @@ -8,48 +8,31 @@ const Quest = { preReqs: function () { - if (Pather.accessToAct(2)) { - // cube - if (!me.cube) { - for (let getCube = 0; !me.cube && getCube < 5; getCube++) { - Loader.runScript("amulet"); - } + /** + * @param {string} task + * @param {() => boolean} req + * @returns {boolean} + */ + const getReq = (task, req = () => true) => { + for (let i = 0; i < 5 && !req(); i++) { + Loader.runScript(task); } + return req(); + }; - // horadric staff - if (!me.staff && !me.horadricstaff) { - if (!me.amulet) { - for (let getAmmy = 0; !me.amulet && getAmmy < 5; getAmmy++) { - Loader.runScript("amulet"); - } - } + if (me.accessToAct(2)) { + !me.cube && getReq("cube", () => me.cube); - if (!me.shaft) { - for (let getStaff = 0; !me.shaft && getStaff < 5; getStaff++) { - Loader.runScript("staff"); - } - } + if (!me.staff && !me.horadricstaff) { + !me.amulet && getReq("amulet", () => me.amulet); + !me.shaft && getReq("staff", () => me.shaft); } } - if (Pather.accessToAct(3) && !me.travincal && !me.khalimswill) { - if (!me.eye) { - for (let getEye = 0; !me.eye && getEye < 5; getEye++) { - Loader.runScript("eye"); - } - } - - if (!me.heart) { - for (let getHeart = 0; !me.heart && getHeart < 5; getHeart++) { - Loader.runScript("heart"); - } - } - - if (!me.brain) { - for (let getBrain = 0; !me.brain && getBrain < 5; getBrain++) { - Loader.runScript("brain"); - } - } + if (me.accessToAct(3) && !me.travincal && !me.khalimswill) { + !me.eye && getReq("eye", () => me.eye); + !me.heart && getReq("heart", () => me.heart); + !me.brain && getReq("brain", () => me.brain); } }, @@ -69,12 +52,10 @@ const Quest = { Town.doChores(); Town.openStash(); - me.findItems(-1, -1, sdk.storage.Cube) && Cubing.emptyCube(); - - let cubingItem; + Cubing.emptyCube(); for (let classid of classids) { - cubingItem = me.getItem(classid); + let cubingItem = me.getItem(classid); if (!cubingItem || !Storage.Cube.MoveTo(cubingItem)) { return false; @@ -274,7 +255,7 @@ const Quest = { : null; let smashable = Game.getObject(classid); - if (Item.getEquipped(sdk.body.RightArm).classid !== tool || !me.getItem(tool)) return false; + if (me.equipped.get(sdk.body.RightArm).classid !== tool || !me.getItem(tool)) return false; if (!smashable) return false; let tick = getTickCount(); let questTool = me.getItem(tool); @@ -593,7 +574,7 @@ const Quest = { } // Free Lam Essen quest - if (Pather.accessToAct(3) && !me.getQuest(sdk.quest.id.LamEsensTome, sdk.quest.states.Completed)) { + if (me.accessToAct(3) && !me.getQuest(sdk.quest.id.LamEsensTome, sdk.quest.states.Completed)) { !me.inArea(sdk.areas.KurastDocktown) && Town.goToTown(3); Town.move("alkor"); let unit = getUnit(1, "alkor"); @@ -609,7 +590,7 @@ const Quest = { // Remove Khalim's Will if quest not completed and restarting run. let kw = me.getItem(sdk.items.quest.KhalimsWill); if (kw) { - if (Item.getEquipped(sdk.body.RightArm).classid === sdk.items.quest.KhalimsWill) { + if (me.equipped.get(sdk.body.RightArm).classid === sdk.items.quest.KhalimsWill) { Town.clearInventory(); delay(500); Quest.stashItem(sdk.items.quest.KhalimsWill); diff --git a/libs/SoloPlay/Functions/SoloEvents.js b/libs/SoloPlay/Functions/SoloEvents.js index 8d90ef33..f96294fb 100644 --- a/libs/SoloPlay/Functions/SoloEvents.js +++ b/libs/SoloPlay/Functions/SoloEvents.js @@ -208,7 +208,7 @@ !me.inTown && Town.goToTown(); - if (Pather.accessToAct(2) && Pather.checkWP(sdk.areas.ArcaneSanctuary)) { + if (me.accessToAct(2) && Pather.checkWP(sdk.areas.ArcaneSanctuary)) { Pather.useWaypoint(sdk.areas.ArcaneSanctuary); Precast.doPrecast(true); diff --git a/libs/SoloPlay/Scripts/bloodraven.js b/libs/SoloPlay/Scripts/bloodraven.js index 9b07dc0f..abaf1f7f 100644 --- a/libs/SoloPlay/Scripts/bloodraven.js +++ b/libs/SoloPlay/Scripts/bloodraven.js @@ -32,7 +32,7 @@ function bloodraven () { Precast.doPrecast(true); me.overhead("blood raven"); - Pather.moveToExit(sdk.areas.BurialGrounds, true); + Pather.journeyTo(sdk.areas.BurialGrounds); me.sorceress && !me.normal ? Pather.moveNearPreset(sdk.areas.BurialGrounds, sdk.unittype.Monster, sdk.monsters.preset.BloodRaven, 20) : Pather.moveToPreset(sdk.areas.BurialGrounds, sdk.unittype.Monster, sdk.monsters.preset.BloodRaven); @@ -57,13 +57,13 @@ function bloodraven () { if (me.hell) { switch (me.gametype) { case sdk.game.gametype.Classic: - if (Pather.accessToAct(3)) { + if (me.accessToAct(3)) { return true; } break; case sdk.game.gametype.Expansion: - if ((me.charlvl < 80 || me.charlvl > 85) && !((me.sorceress || me.druid || me.assassin) && Item.getEquipped(sdk.body.RightArm).tier < 100000)) { + if ((me.charlvl < 80 || me.charlvl > 85) && !((me.sorceress || me.druid || me.assassin) && me.equipped.get(sdk.body.RightArm).tier < 100000)) { return true; } diff --git a/libs/SoloPlay/Tools/SoloIndex.js b/libs/SoloPlay/Tools/SoloIndex.js index 1cf0501c..078ec352 100644 --- a/libs/SoloPlay/Tools/SoloIndex.js +++ b/libs/SoloPlay/Tools/SoloIndex.js @@ -102,11 +102,11 @@ const SoloIndex = { skipIf: function () { switch (me.classid) { case sdk.player.class.Paladin: - return Pather.accessToAct(3) || Attack.auradin || me.checkItem({ name: sdk.locale.items.Enigma }).have; + return me.accessToAct(3) || Attack.auradin || me.checkItem({ name: sdk.locale.items.Enigma }).have; case sdk.player.class.Barbarian: - return Pather.accessToAct(3) || me.checkItem({ name: sdk.locale.items.Lawbringer }).have; + return me.accessToAct(3) || me.checkItem({ name: sdk.locale.items.Lawbringer }).have; default: - if (me.hell && me.charlvl > 72 && Pather.accessToAct(2)) return true; + if (me.hell && me.charlvl > 72 && me.accessToAct(2)) return true; return false; } }, @@ -122,7 +122,7 @@ const SoloIndex = { }, "treehead": { preReq: function () { - return (me.hell && !Pather.accessToAct(3)); + return (me.hell && !me.accessToAct(3)); }, skipIf: function () { return !me.paladin || Attack.auradin || me.checkItem({ name: sdk.locale.items.Enigma }).have; @@ -236,8 +236,8 @@ const SoloIndex = { return (!me.classic && !me.normal); }, skipIf: function () { - if (me.barbarian && (!me.hell || Pather.accessToAct(3) - || (Item.getEquipped(sdk.body.LeftArm).tier > 1270 + if (me.barbarian && (!me.hell || me.accessToAct(3) + || (me.equipped.get(sdk.body.LeftArm).tier > 1270 || me.checkItem({ name: sdk.locale.items.Lawbringer }).have))) { return true; } @@ -272,7 +272,7 @@ const SoloIndex = { }, "cube": { preReq: function () { - return Pather.accessToAct(2); + return me.accessToAct(2); }, skipIf: function () { return me.cube; @@ -284,7 +284,7 @@ const SoloIndex = { }, "radament": { preReq: function () { - return Pather.accessToAct(2); + return me.accessToAct(2); }, shouldRun: function () { if (!this.preReq()) return false; @@ -300,7 +300,7 @@ const SoloIndex = { }, "staff": { preReq: function () { - return Pather.accessToAct(2); + return me.accessToAct(2); }, skipIf: function () { return (me.horadricstaff || me.shaft || me.completestaff); @@ -312,7 +312,7 @@ const SoloIndex = { }, "amulet": { preReq: function () { - return Pather.accessToAct(2); + return me.accessToAct(2); }, skipIf: function () { return (me.horadricstaff || me.amulet || me.completestaff); @@ -324,7 +324,7 @@ const SoloIndex = { }, "ancienttunnels": { preReq: function () { - return (me.hell && Pather.accessToAct(2)); + return (me.hell && me.accessToAct(2)); }, skipIf: function () { switch (me.classid) { @@ -341,7 +341,7 @@ const SoloIndex = { }, "beetleburst": { preReq: function () { - return (Pather.accessToAct(2)); + return (me.accessToAct(2)); }, skipIf: function () { return (me.charlvl < 12 || me.charlvl > 20); @@ -353,7 +353,7 @@ const SoloIndex = { }, "summoner": { preReq: function () { - return Pather.accessToAct(2); + return me.accessToAct(2); }, skipIf: function () { return me.summoner; @@ -366,7 +366,7 @@ const SoloIndex = { }, "maggotlair": { preReq: function () { - return Pather.accessToAct(2) && Pather.canTeleport(); + return me.accessToAct(2) && Pather.canTeleport(); }, skipIf: function () { return (!me.normal || me.charlvl > 21); @@ -378,7 +378,7 @@ const SoloIndex = { }, "tombs": { preReq: function () { - return Pather.accessToAct(2); + return me.accessToAct(2); }, skipIf: function () { return (!me.normal || me.charlvl > 22); @@ -390,7 +390,7 @@ const SoloIndex = { }, "duriel": { preReq: function () { - return Pather.accessToAct(2) && (me.horadricstaff || me.completestaff || (me.amulet && me.shaft)); + return me.accessToAct(2) && (me.horadricstaff || me.completestaff || (me.amulet && me.shaft)); }, skipIf: function () { return me.duriel; @@ -403,7 +403,7 @@ const SoloIndex = { }, "eye": { preReq: function () { - return Pather.accessToAct(3); + return me.accessToAct(3); }, skipIf: function () { return (me.eye || me.khalimswill || me.travincal); @@ -415,7 +415,7 @@ const SoloIndex = { }, "templeruns": { preReq: function () { - return Pather.accessToAct(3); + return me.accessToAct(3); }, skipIf: function () { return ((me.paladin && Check.currentBuild().caster) || (me.hell && me.sorceress && me.charlvl < 90)); @@ -433,7 +433,7 @@ const SoloIndex = { }, "lamessen": { preReq: function () { - return Pather.accessToAct(3); + return me.accessToAct(3); }, skipIf: function () { return (me.lamessen); @@ -445,7 +445,7 @@ const SoloIndex = { }, "lowerkurast": { preReq: function () { - return Pather.accessToAct(3); + return me.accessToAct(3); }, skipIf: function () { return (!me.barbarian); @@ -457,7 +457,7 @@ const SoloIndex = { }, "heart": { preReq: function () { - return Pather.accessToAct(3); + return me.accessToAct(3); }, skipIf: function () { return (me.heart || me.khalimswill || me.travincal); @@ -469,7 +469,7 @@ const SoloIndex = { }, "brain": { preReq: function () { - return Pather.accessToAct(3); + return me.accessToAct(3); }, skipIf: function () { return (me.brain || me.khalimswill || me.travincal); @@ -481,7 +481,7 @@ const SoloIndex = { }, "travincal": { preReq: function () { - return Pather.accessToAct(3); + return me.accessToAct(3); }, skipIf: function () { return false; @@ -500,7 +500,7 @@ const SoloIndex = { }, "mephisto": { preReq: function () { - return (Pather.accessToAct(3) && me.travincal); + return (me.accessToAct(3) && me.travincal); }, skipIf: function () { return false; @@ -520,7 +520,7 @@ const SoloIndex = { }, "izual": { preReq: function () { - return Pather.accessToAct(4); + return me.accessToAct(4); }, skipIf: function () { return false; @@ -538,7 +538,7 @@ const SoloIndex = { "river": { preReq: function () { const cLvl = me.charlvl; - return (Pather.accessToAct(4) && ((me.normal && cLvl >= 24) || (me.nightmare && cLvl >= 40) || (me.hell && cLvl >= 80))); + return (me.accessToAct(4) && ((me.normal && cLvl >= 24) || (me.nightmare && cLvl >= 40) || (me.hell && cLvl >= 80))); }, skipIf: function () { return (me.diablo || me.normal); @@ -556,7 +556,7 @@ const SoloIndex = { "hephasto": { preReq: function () { const cLvl = me.charlvl; - return (Pather.accessToAct(4) && ((me.normal && cLvl >= 24) || (me.nightmare && cLvl >= 40) || (me.hell && cLvl >= 80))); + return (me.accessToAct(4) && ((me.normal && cLvl >= 24) || (me.nightmare && cLvl >= 40) || (me.hell && cLvl >= 80))); }, skipIf: function () { return (!me.barbarian || me.normal || me.diablo); @@ -573,7 +573,7 @@ const SoloIndex = { "hellforge": { preReq: function () { const cLvl = me.charlvl; - return (Pather.accessToAct(4) && (me.classic || me.anya) && ((me.normal && cLvl >= 24) || (me.nightmare && cLvl >= 40) || (me.hell && cLvl >= 80))); + return (me.accessToAct(4) && (me.classic || me.anya) && ((me.normal && cLvl >= 24) || (me.nightmare && cLvl >= 40) || (me.hell && cLvl >= 80))); }, skipIf: function () { return (me.hellforge || me.getQuest(sdk.quest.id.HellsForge, sdk.quest.states.ReqComplete)); @@ -586,7 +586,7 @@ const SoloIndex = { "diablo": { preReq: function () { const cLvl = me.charlvl; - return (Pather.accessToAct(4) && ((me.normal && cLvl >= 24) || (me.nightmare && cLvl >= 40) || (me.hell && cLvl >= 80))); + return (me.accessToAct(4) && ((me.normal && cLvl >= 24) || (me.nightmare && cLvl >= 40) || (me.hell && cLvl >= 80))); }, skipIf: function () { return false; @@ -606,7 +606,7 @@ const SoloIndex = { }, "shenk": { preReq: function () { - return (me.expansion && Pather.accessToAct(5)); + return (me.expansion && me.accessToAct(5)); }, skipIf: function () { return false; @@ -623,7 +623,7 @@ const SoloIndex = { }, "savebarby": { preReq: function () { - return (me.expansion && Pather.accessToAct(5)); + return (me.expansion && me.accessToAct(5)); }, skipIf: function () { return me.savebarby; @@ -639,7 +639,7 @@ const SoloIndex = { }, "anya": { preReq: function () { - return (me.expansion && Pather.accessToAct(5)); + return (me.expansion && me.accessToAct(5)); }, skipIf: function () { return me.anya; @@ -651,7 +651,7 @@ const SoloIndex = { }, "pindle": { preReq: function () { - return (me.expansion && Pather.accessToAct(5) && me.anya); + return (me.expansion && me.accessToAct(5) && me.anya); }, skipIf: function () { return false; @@ -663,7 +663,7 @@ const SoloIndex = { }, "ancients": { preReq: function () { - return (me.expansion && Pather.accessToAct(5)); + return (me.expansion && me.accessToAct(5)); }, skipIf: function () { return me.ancients; @@ -675,7 +675,7 @@ const SoloIndex = { }, "baal": { preReq: function () { - return (me.expansion && Pather.accessToAct(5)); + return (me.expansion && me.accessToAct(5)); }, skipIf: function () { return !me.ancients; @@ -687,7 +687,7 @@ const SoloIndex = { }, "a5chests": { preReq: function () { - return (me.expansion && Pather.accessToAct(5) && me.baal); + return (me.expansion && me.accessToAct(5) && me.baal); }, skipIf: function () { return me.normal; @@ -699,7 +699,7 @@ const SoloIndex = { }, "getkeys": { preReq: function () { - return (me.expansion && Pather.accessToAct(5) && me.hell); + return (me.expansion && me.accessToAct(5) && me.hell); }, skipIf: function () { return (["Zealer", "Smiter", "Uberconc"].indexOf(SetUp.currentBuild) === -1); @@ -711,7 +711,7 @@ const SoloIndex = { }, "orgtorch": { preReq: function () { - return (me.expansion && Pather.accessToAct(5) && me.hell); + return (me.expansion && me.accessToAct(5) && me.hell); }, skipIf: function () { return (["Zealer", "Smiter", "Uberconc"].indexOf(SetUp.currentBuild) === -1); diff --git a/libs/SoloPlay/index.d.ts b/libs/SoloPlay/index.d.ts index 95712618..258bdea6 100644 --- a/libs/SoloPlay/index.d.ts +++ b/libs/SoloPlay/index.d.ts @@ -103,6 +103,31 @@ declare global { }; } + interface EquippedItem extends ItemUnit { + location: number; + durability: number; + tier: number; + tierScore: number; + secondaryTier: number; + socketed: boolean; + twoHandedCheck: (strict?: boolean) => boolean; + } + + // type EquippedItem = { + // classid: number; + // name: string; + // fname: string; + // quality: number; + // prefixnum: number; + // suffixnum: number; + // itemType: number; + // strreq: number; + // dexreq: number; + // sockets: number; + // getStat: (stat: number, subid: number) => number; + // }; + type EquippedMap = Map; + interface MeType { readonly maxNearMonsters: number; readonly dualWielding: boolean; @@ -115,10 +140,18 @@ declare global { readonly LR: number; readonly PR: number; readonly onFinalBuild: boolean; + readonly trueStr: number; + readonly trueDex: number; finalBuild: Build; currentBuild: Build; data: MyData; + equipped: { + get: (bodylocation: number) => EquippedItem | undefined; + has: (bodylocation: number) => boolean; + set: (bodylocation: number, item: ItemUnit) => void; + init: () => void; + }; canTpToTown(): boolean; getMercEx(): MercUnit | null; From 910b6aa4d836852b1e7f3713e6ee5151e361eb8b Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Thu, 20 Apr 2023 16:10:10 -0400 Subject: [PATCH 108/263] Update Mercenary.js - Add timeout, in case of failure to prevent subsequent retries during town chores --- libs/SoloPlay/Functions/Mercenary.js | 76 ++++++++++++++-------------- 1 file changed, 38 insertions(+), 38 deletions(-) diff --git a/libs/SoloPlay/Functions/Mercenary.js b/libs/SoloPlay/Functions/Mercenary.js index 985f1c13..8a3a78b4 100644 --- a/libs/SoloPlay/Functions/Mercenary.js +++ b/libs/SoloPlay/Functions/Mercenary.js @@ -70,6 +70,7 @@ const MercData = new function MercData () { const Mercenary = { minCost: -1, + timeout: 0, /** * only a2 mercs for now, need to test others to see if ModifierListSkill returns their skill @@ -188,9 +189,10 @@ const Mercenary = { // only supports act 2 mercs for now hireMerc: function () { if (me.classic) return true; + if (Mercenary.timeout && getTickCount() < Mercenary.timeout) return true; let _a; let { wantedMerc } = Check.finalBuild(); - let mercAct = (!Pather.accessToAct(2) && me.normal ? 1 : wantedMerc.act); + let mercAct = (!me.accessToAct(2) && me.normal ? 1 : wantedMerc.act); let tmpAuraName = "Defiance"; let currMerc = me.data.merc; @@ -207,7 +209,7 @@ const Mercenary = { case mercAct === 1 && (currMerc.skillName === "Cold Arrow" || !Misc.checkQuest(sdk.quest.id.SistersBurialGrounds, sdk.quest.states.Completed)): case currMerc.skillName === wantedMerc.skillName: case me.diff > wantedMerc.difficulty: - case me.diff === wantedMerc.difficulty && !Pather.accessToAct(wantedMerc.act): + case me.diff === wantedMerc.difficulty && !me.accessToAct(wantedMerc.act): case me.diff !== wantedMerc.difficulty && currMerc.skillName === "Defiance": case (me.charlvl > CharInfo.levelCap + 10 && Mercenary.checkMercSkill(wantedMerc)): case me.gold < Math.round((((me.charlvl - 1) * (me.charlvl - 1)) / 2) * 7.5): @@ -267,46 +269,44 @@ const Mercenary = { .filter((merc) => merc.skills.some((skill) => (skill === null || skill === void 0 ? void 0 : skill.name) === wantedSkill)) .sort((a, b) => b.level - a.level) .first(); - if (wantedMerc) { - if (wantedMerc.cost > me.gold) { - Mercenary.minCost = wantedMerc.cost; - throw new Error("Too expensive " + wantedMerc.cost); - } + if (!wantedMerc) throw new Error("No merc found with skill " + wantedSkill); + if (wantedMerc.cost > me.gold) { + Mercenary.minCost = wantedMerc.cost; + throw new Error("Too expensive " + wantedMerc.cost); + } - let oldGid_1 = (_a = me.getMercEx()) === null || _a === void 0 ? void 0 : _a.gid; - console.log("ÿc9Mercenaryÿc0 :: Found a merc to hire " + JSON.stringify(wantedMerc)); - - (wantedMerc === null || wantedMerc === void 0) - ? void 0 - : wantedMerc.hire(); - let newMerc = Misc.poll(function () { - let merc = me.getMerc(); - if (!merc) return false; - if (oldGid_1 && oldGid_1 === merc.gid) return false; - return merc; - }); - - console.log("Hired a merc?"); - if (newMerc) { - console.log("Yep"); - me.data.merc.act = me.act; - me.data.merc.classid = newMerc.classid; - me.data.merc.difficulty = me.diff; - me.data.merc.skillName = wantedMerc.skills.find(sk => sk.name === wantedSkill).name; - me.data.merc.skill = MercData.findByName(me.data.merc.skillName, me.act).skill; - CharData.updateData("merc", me.data) && me.update(); - console.log("ÿc9Mercenaryÿc0 :: " + me.data.merc.skillName + " merc hired."); - } - me.cancelUIFlags(); - while (getInteractedNPC()) { - delay(me.ping || 5); - me.cancel(); - } - } else { - console.warn("Failed to find wanted Merc"); + let oldGid_1 = (_a = me.getMercEx()) === null || _a === void 0 ? void 0 : _a.gid; + console.log("ÿc9Mercenaryÿc0 :: Found a merc to hire " + JSON.stringify(wantedMerc)); + + (wantedMerc === null || wantedMerc === void 0) + ? void 0 + : wantedMerc.hire(); + let newMerc = Misc.poll(function () { + let merc = me.getMerc(); + if (!merc) return false; + if (oldGid_1 && oldGid_1 === merc.gid) return false; + return merc; + }); + + console.log("Hired a merc?"); + if (newMerc) { + console.log("Yep"); + me.data.merc.act = me.act; + me.data.merc.classid = newMerc.classid; + me.data.merc.difficulty = me.diff; + me.data.merc.skillName = wantedMerc.skills.find(sk => sk.name === wantedSkill).name; + me.data.merc.skill = MercData.findByName(me.data.merc.skillName, me.act).skill; + CharData.updateData("merc", me.data) && me.update(); + console.log("ÿc9Mercenaryÿc0 :: " + me.data.merc.skillName + " merc hired."); + } + me.cancelUIFlags(); + while (getInteractedNPC()) { + delay(me.ping || 5); + me.cancel(); } } catch (e) { console.error(e); + Mercenary.timeout = getTickCount() + Time.minutes(3); } finally { removeEventListener("gamepacket", MercLib_1.mercPacket); } From 8a32152fbe794e5a6f107419f7fb626bfa808176 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Thu, 20 Apr 2023 16:14:51 -0400 Subject: [PATCH 109/263] Small bits of cleanup - converted `sendPacket` call to use `PacketBuilder` instead during charge skill casting - removed overridden `Misc.errorReport`, really was never needed. --- libs/SoloPlay/Functions/MiscOverrides.js | 56 +------------------ libs/SoloPlay/Functions/PrototypeOverrides.js | 55 ++++++++++++------ libs/SoloPlay/SoloPlay.js | 2 +- libs/SoloPlay/Threads/ToolsThread.js | 43 ++++++-------- libs/SoloPlay/Workers/EventEmitter.js | 2 +- 5 files changed, 56 insertions(+), 102 deletions(-) diff --git a/libs/SoloPlay/Functions/MiscOverrides.js b/libs/SoloPlay/Functions/MiscOverrides.js index ac40aaee..d0a81237 100644 --- a/libs/SoloPlay/Functions/MiscOverrides.js +++ b/libs/SoloPlay/Functions/MiscOverrides.js @@ -9,6 +9,7 @@ includeIfNotIncluded("core/Misc.js"); Misc.openChestsEnabled = true; +Misc.screenshotErrors = true; Misc.presetChestIds = [ 5, 6, 87, 104, 105, 106, 107, 143, 140, 141, 144, 146, 147, 148, 176, 177, 181, 183, 198, 240, 241, 242, 243, 329, 330, 331, 332, 333, 334, 335, 336, 354, 355, 356, 371, 387, 389, 390, 391, 397, 405, @@ -748,61 +749,6 @@ Misc.checkSocketables = function () { } }; -Misc.errorReport = function (error, script) { - let msg, oogmsg, filemsg, source, stack; - let stackLog = ""; - - let date = new Date(); - let dateString = "[" + new Date(date.getTime() - (date.getTimezoneOffset() * 60000)).toISOString().slice(0, -5).replace(/-/g, "/").replace("T", " ") + "]"; - - if (typeof error === "string") { - msg = error; - oogmsg = error.replace(/ÿc[0-9!"+<:;.*]/gi, ""); - filemsg = dateString + " <" + me.profile + "> " + error.replace(/ÿc[0-9!"+<:;.*]/gi, "") + "\n"; - } else { - source = error.fileName.substring(error.fileName.lastIndexOf("\\") + 1, error.fileName.length); - msg = "ÿc1Error in ÿc0" + script + " ÿc1(" + source + " line ÿc1" + error.lineNumber + "): ÿc1" + error.message; - oogmsg = " Error in " + script + " (" + source + " #" + error.lineNumber + ") " + error.message + " (Area: " + getAreaName(me.area) + ", Ping:" + me.ping + ", Game: " + me.gamename + ")"; - filemsg = dateString + " <" + me.profile + "> " + msg.replace(/ÿc[0-9!"+<:;.*]/gi, "") + "\n"; - - if (error.hasOwnProperty("stack")) { - stack = error.stack; - - if (stack) { - stack = stack.split("\n"); - - if (stack && typeof stack === "object") { - stack.reverse(); - } - - for (let i = 0; i < stack.length; i += 1) { - if (stack[i]) { - stackLog += stack[i].substr(0, stack[i].indexOf("@") + 1) + stack[i].substr(stack[i].lastIndexOf("\\") + 1, stack[i].length - 1); - - if (i < stack.length - 1) { - stackLog += ", "; - } - } - } - } - } - - if (stackLog) { - filemsg += "Stack: " + stackLog + "\n"; - } - } - - if (this.errorConsolePrint) { - D2Bot.printToConsole(oogmsg, sdk.colors.D2Bot.Gray); - } - - showConsole(); - console.log(msg); - FileAction.read("logs/ScriptErrorLog.txt", filemsg); - takeScreenshot(); - delay(500); -}; - Misc.updateRecursively = function (oldObj, newObj, path) { if (path === void 0) { path = []; } Object.keys(newObj).forEach(function (key) { diff --git a/libs/SoloPlay/Functions/PrototypeOverrides.js b/libs/SoloPlay/Functions/PrototypeOverrides.js index e22324a3..af3616f1 100644 --- a/libs/SoloPlay/Functions/PrototypeOverrides.js +++ b/libs/SoloPlay/Functions/PrototypeOverrides.js @@ -307,8 +307,7 @@ Unit.prototype.getItemType = function () { }; Unit.prototype.castChargedSkillEx = function (...args) { - let skillId, x, y, unit, chargedItem, charge; - let chargedItems = []; + let skillId, x, y, unit; switch (args.length) { case 0: // item.castChargedSkill() @@ -348,7 +347,11 @@ Unit.prototype.castChargedSkillEx = function (...args) { unit && ([x, y] = [unit.x, unit.y]); if (this !== me && this.type !== sdk.unittype.Item) { - Developer.debugging.skills && console.log("ÿc9CastChargedSkillÿc0 :: Wierd Error, invalid arguments, expected 'me' object or 'item' unit" + " unit type : " + this.type); + if (Developer.debugging.skills) { + console.debug( + "ÿc9CastChargedSkillÿc0 :: Wierd Error, invalid arguments, expected 'me' object or 'item' unit" + " unit type : " + this.type + ); + } return false; } @@ -356,7 +359,7 @@ Unit.prototype.castChargedSkillEx = function (...args) { if (this === me) { if (!skillId) throw Error("Must supply skillId on me.castChargedSkill"); - chargedItems = []; + let chargedItems = []; CharData.skillData.chargedSkills.forEach(chargeSkill => { if (chargeSkill.skill === skillId) { @@ -375,14 +378,16 @@ Unit.prototype.castChargedSkillEx = function (...args) { return false; } - chargedItem = chargedItems.sort((a, b) => a.charge.level - b.charge.level).first().item; + let chargedItem = chargedItems + .sort((a, b) => a.charge.level - b.charge.level) + .first().item; // Check if item with charges is equipped on the switch spot me.weaponswitch === 0 && chargedItem.isOnSwap && me.switchWeapons(1); return chargedItem.castChargedSkillEx.apply(chargedItem, args); } else if (this.type === sdk.unittype.Item) { - charge = this.getStat(-2)[sdk.stats.ChargedSkill]; // WARNING. Somehow this gives duplicates + let charge = this.getStat(-2)[sdk.stats.ChargedSkill]; // WARNING. Somehow this gives duplicates if (!charge) { console.warn("ÿc9CastChargedSkillÿc0 :: No charged skill on this item"); @@ -391,8 +396,10 @@ Unit.prototype.castChargedSkillEx = function (...args) { if (skillId) { if (charge instanceof Array) { - charge = charge.filter(item => (item && item.skill === skillId) && !!item.charges); // Filter out all other charged skills - charge = charge.first(); + // Filter out all other charged skills + charge = charge + .filter(item => (item && item.skill === skillId) && !!item.charges) + .first(); } else { if (charge.skill !== skillId || !charge.charges) { console.warn("No charges matching skillId"); @@ -405,9 +412,12 @@ Unit.prototype.castChargedSkillEx = function (...args) { if (charge) { const usePacket = ([ - sdk.skills.Valkyrie, sdk.skills.Decoy, sdk.skills.RaiseSkeleton, sdk.skills.ClayGolem, sdk.skills.RaiseSkeletalMage, sdk.skills.BloodGolem, sdk.skills.Shout, - sdk.skills.IronGolem, sdk.skills.Revive, sdk.skills.Werewolf, sdk.skills.Werebear, sdk.skills.OakSage, sdk.skills.SpiritWolf, sdk.skills.PoisonCreeper, sdk.skills.BattleOrders, - sdk.skills.SummonDireWolf, sdk.skills.Grizzly, sdk.skills.HeartofWolverine, sdk.skills.SpiritofBarbs, sdk.skills.ShadowMaster, sdk.skills.ShadowWarrior, sdk.skills.BattleCommand, + sdk.skills.Valkyrie, sdk.skills.Decoy, sdk.skills.RaiseSkeleton, sdk.skills.ClayGolem, + sdk.skills.RaiseSkeletalMage, sdk.skills.BloodGolem, sdk.skills.Shout, + sdk.skills.IronGolem, sdk.skills.Revive, sdk.skills.Werewolf, sdk.skills.Werebear, + sdk.skills.OakSage, sdk.skills.SpiritWolf, sdk.skills.PoisonCreeper, sdk.skills.BattleOrders, + sdk.skills.SummonDireWolf, sdk.skills.Grizzly, sdk.skills.HeartofWolverine, sdk.skills.SpiritofBarbs, + sdk.skills.ShadowMaster, sdk.skills.ShadowWarrior, sdk.skills.BattleCommand, ].indexOf(skillId) === -1); if (!usePacket) { @@ -416,12 +426,22 @@ Unit.prototype.castChargedSkillEx = function (...args) { // Packet casting // Setting skill on hand - sendPacket(1, sdk.packets.send.SelectSkill, 2, charge.skill, 1, 0x0, 1, 0x00, 4, this.gid); + new PacketBuilder() + .byte(sdk.packets.send.SelectSkill) + .word(charge.skill) + .byte(0x00) + .byte(0x00) + .dword(this.gid) + .send(); console.log("Set charge skill " + charge.skill + " on hand"); // No need for a delay, since its TCP, the server recv's the next statement always after the send cast skill packet // Cast the skill - sendPacket(1, sdk.packets.send.RightSkillOnLocation, 2, x || me.x, 2, y || me.y); + new PacketBuilder() + .byte(sdk.packets.send.RightSkillOnLocation) + .word(x || me.x) + .word(y || me.y) + .send(); console.log("Cast charge skill " + charge.skill); // The result of "successfully" casted is different, so we cant wait for it here. We have to assume it worked @@ -474,9 +494,7 @@ Unit.prototype.castSwitchChargedSkill = function (...args) { if (this === me) { if (!skillId) throw Error("Must supply skillId on me.castChargedSkill"); - /** - * @type {{ charge: number, level: number, item: ItemUnit }[]} - */ + /** @type {{ charge: number, level: number, item: ItemUnit }[]} */ let chargedItems = []; CharData.skillData.chargedSkillsOnSwitch.forEach(chargeSkill => { @@ -498,8 +516,9 @@ Unit.prototype.castSwitchChargedSkill = function (...args) { me.weaponswitch === 0 && me.switchWeapons(1); - let chargedItem = chargedItems.sort((a, b) => a.charge.level - b.charge.level).first().item; - + let chargedItem = chargedItems + .sort((a, b) => a.charge.level - b.charge.level) + .first().item; return chargedItem.castChargedSkillEx.apply(chargedItem, args); } diff --git a/libs/SoloPlay/SoloPlay.js b/libs/SoloPlay/SoloPlay.js index 33457645..b353e36b 100644 --- a/libs/SoloPlay/SoloPlay.js +++ b/libs/SoloPlay/SoloPlay.js @@ -217,7 +217,7 @@ function main () { DataFile.updateStats(["experience", "name"]); - // Load threads - for now split between old system with threads or opt in for new system with workers + // Load threads load("libs/SoloPlay/Threads/ToolsThread.js"); require("./Workers/EventEmitter"); diff --git a/libs/SoloPlay/Threads/ToolsThread.js b/libs/SoloPlay/Threads/ToolsThread.js index b2897459..e4aafff9 100644 --- a/libs/SoloPlay/Threads/ToolsThread.js +++ b/libs/SoloPlay/Threads/ToolsThread.js @@ -62,41 +62,30 @@ function main () { // General functions this.togglePause = function () { - let scripts = ["libs/SoloPlay/SoloPlay.js", "libs/SoloPlay/Threads/TownChicken.js", "threads/party.js"]; - - for (let l = 0; l < scripts.length; l += 1) { - let script = getScript(scripts[l]); - - if (script) { - if (script.running) { - scripts[l] === "libs/SoloPlay/SoloPlay.js" && console.log("ÿc8ToolsThread :: ÿc1Pausing " + scripts[l]); - scripts[l] === "libs/SoloPlay/Threads/TownChicken.js" && !SoloEvents.cloneWalked && console.log("ÿc8ToolsThread :: ÿc1Pausing " + scripts[l]); - - // don't pause townchicken during clone walk - if (scripts[l] !== "libs/SoloPlay/Threads/TownChicken.js" || !SoloEvents.cloneWalked) { - script.pause(); - } + ["libs/SoloPlay/SoloPlay.js", "threads/party.js"].forEach((script) => { + let thread = getScript(script); + if (thread) { + if (thread.running) { + script === "libs/SoloPlay/SoloPlay.js" && console.log("ÿc8ToolsThread :: ÿc1Pausing " + script); + thread.pause(); } else { - scripts[l] === "libs/SoloPlay/SoloPlay.js" && console.log("ÿc8ToolsThread :: ÿc2Resuming threads"); - script.resume(); + script === "libs/SoloPlay/SoloPlay.js" && console.log("ÿc8ToolsThread :: ÿc2Resuming threads"); + thread.resume(); } } - } + }); return true; }; this.stopDefault = function () { - let scripts = [ - "libs/SoloPlay/SoloPlay.js", "libs/SoloPlay/Threads/TownChicken.js", "libs/SoloPlay/Threads/EventThread.js", - "libs/SoloPlay/Threads/AutoBuildThread.js", "libs/SoloPlay/Modules/Guard.js", "libs/SoloPlay/Modules/TownGuard.js" - ]; - - for (let l = 0; l < scripts.length; l += 1) { - let script = getScript(scripts[l]); - !!script && script.running && script.stop(); - } - + ["libs/SoloPlay/SoloPlay.js", "libs/SoloPlay/Modules/Guard.js", "threads/party.js"] + .forEach(script => { + let thread = getScript(script); + if (thread && thread.running) { + thread.stop(); + } + }); return true; }; diff --git a/libs/SoloPlay/Workers/EventEmitter.js b/libs/SoloPlay/Workers/EventEmitter.js index eb14d205..a84c2be2 100644 --- a/libs/SoloPlay/Workers/EventEmitter.js +++ b/libs/SoloPlay/Workers/EventEmitter.js @@ -76,7 +76,7 @@ try { Tracker.update(); } catch (e) { - console.warn(e.message); + console.error(e); } } } From 32852a7ddd256bd2cb1745be25e744e4a505db5d Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Thu, 20 Apr 2023 16:15:51 -0400 Subject: [PATCH 110/263] Update PickitOverrides.js - update to use the core `Pickit.ignoreList` which is a set rather than an array --- libs/SoloPlay/Functions/PickitOverrides.js | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/libs/SoloPlay/Functions/PickitOverrides.js b/libs/SoloPlay/Functions/PickitOverrides.js index 1381ebee..8e7784a4 100644 --- a/libs/SoloPlay/Functions/PickitOverrides.js +++ b/libs/SoloPlay/Functions/PickitOverrides.js @@ -605,7 +605,6 @@ Pickit.checkSpotForItems = function (spot, checkVsMyDist = false, range = Config Pickit.pickList = []; Pickit.essentialList = []; -Pickit.ignoreList = []; // Might need to do a global list so this function and pickItems see the same items to prevent an item from being in both Pickit.essessntialsPick = function (clearBeforePick = false, ignoreGold = false, builtList = [], once = false) { @@ -707,7 +706,7 @@ Pickit.pickItems = function (range = Config.PickRange, once = false) { if (item) { do { - if (Pickit.ignoreList.includes(item.gid)) continue; + if (Pickit.ignoreList.has(item.gid)) continue; if (Pickit.pickList.some(el => el.gid === item.gid)) continue; if (item.onGroundOrDropping && getDistance(me, item) <= range) { Pickit.pickList.push(copyUnit(item)); @@ -722,11 +721,10 @@ Pickit.pickItems = function (range = Config.PickRange, once = false) { while (Pickit.pickList.length > 0) { if (me.dead || !Pickit.enabled) return false; - Pickit.pickList.sort(this.sortItems); const currItem = Pickit.pickList[0]; - if (Pickit.ignoreList.includes(currItem.gid)) { + if (Pickit.ignoreList.has(currItem.gid)) { Pickit.pickList.shift(); continue; @@ -771,7 +769,7 @@ Pickit.pickItems = function (range = Config.PickRange, once = false) { if (Town.visitTown()) { // Recursive check after going to town. We need to remake item list because gids can change. // Called only if room can be made so it shouldn't error out or block anything. - Pickit.ignoreList = []; // reset the list of ignored gids + Pickit.ignoreList.clear(); // reset the list of ignored gids return this.pickItems(range, once); } @@ -786,7 +784,7 @@ Pickit.pickItems = function (range = Config.PickRange, once = false) { Item.logger("No room for", currItem); console.warn("ÿc7Not enough room for " + Item.color(currItem) + currItem.name); // ignore the item now - Pickit.ignoreList.push(currItem.gid); + Pickit.ignoreList.add(currItem.gid); needMule = true; break; From 053dd21cc007d1ef019c9ab00724d697ec4a378a Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Thu, 20 Apr 2023 16:37:22 -0400 Subject: [PATCH 111/263] Update PickitOverrides.js - little extra logging info when muling is triggered --- libs/SoloPlay/Functions/PickitOverrides.js | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/libs/SoloPlay/Functions/PickitOverrides.js b/libs/SoloPlay/Functions/PickitOverrides.js index 8e7784a4..7bb96f7e 100644 --- a/libs/SoloPlay/Functions/PickitOverrides.js +++ b/libs/SoloPlay/Functions/PickitOverrides.js @@ -235,7 +235,9 @@ Pickit.canPick = function (unit) { } while (tome.getNext()); } else { // If we don't have a tome, go ahead and keep 2 scrolls - return unit.classid === sdk.items.ScrollofIdentify && me.charlvl > 5 ? false : me.getItemsEx(unit.classid).filter(el => el.isInInventory).length < 2; + return unit.classid === sdk.items.ScrollofIdentify && me.charlvl > 5 + ? false + : me.getItemsEx(unit.classid).filter(el => el.isInInventory).length < 2; } break; @@ -668,7 +670,7 @@ Pickit.essessntialsPick = function (clearBeforePick = false, ignoreGold = false, } // Town visit failed - abort - console.log("ÿc7Not enough room for " + Item.color(currItem) + currItem.name); + console.log("ÿc7Unable to make room for " + Item.color(currItem) + currItem.name); return false; } @@ -695,8 +697,8 @@ Pickit.essessntialsPick = function (clearBeforePick = false, ignoreGold = false, Pickit.pickItems = function (range = Config.PickRange, once = false) { if (me.dead || range < 0 || !Pickit.enabled) return false; - let status, canFit; let needMule = false; + const canUseMule = AutoMule.getInfo() && AutoMule.getInfo().hasOwnProperty("muleInfo"); while (!me.idle) { delay(40); @@ -735,10 +737,10 @@ Pickit.pickItems = function (range = Config.PickRange, once = false) { if (copyUnit(currItem).x !== undefined && currItem.onGroundOrDropping && (Pather.useTeleport() || me.inTown || !checkCollision(me, currItem, sdk.collision.BlockWall))) { // Check if the item should be picked - status = this.checkItem(currItem); + let status = this.checkItem(currItem); if (status.result && Pickit.canPick(currItem)) { - canFit = (Storage.Inventory.CanFit(currItem) || Pickit.canFit(currItem)); + let canFit = (Storage.Inventory.CanFit(currItem) || Pickit.canFit(currItem)); // Field id when our used space is above a certain percent or if we are full try to make room with FieldID if (Config.FieldID.Enabled && (!canFit || Storage.Inventory.UsedSpacePercent() > Config.FieldID.UsedSpace)) { @@ -803,9 +805,16 @@ Pickit.pickItems = function (range = Config.PickRange, once = false) { } // Quit current game and transfer the items to mule - if (needMule && AutoMule.getInfo() && AutoMule.getInfo().hasOwnProperty("muleInfo") && AutoMule.getMuleItems().length > 0) { + if (needMule && canUseMule && AutoMule.getMuleItems().length > 0) { + console.log( + "ÿc7Muling items :: \n" + + "- ÿc7UsedStashSpacePercentÿc0: " + Storage.Stash.UsedSpacePercent() + "\n" + + "- ÿc7UsedInventorySpacePercentÿc0: " + Storage.Inventory.UsedSpacePercent() + ); scriptBroadcast("mule"); scriptBroadcast("quit"); + + return false; } return true; From c0132df649b4dd7574e4837b3fc915251cbae6ee Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Thu, 20 Apr 2023 23:57:59 -0400 Subject: [PATCH 112/263] Ensure swap back to main hand after using charges - some minor formatting as well --- libs/SoloPlay/Functions/AttackOverrides.js | 22 +++++++++++++++------- libs/SoloPlay/Functions/DynamicTiers.js | 16 ++++++++-------- 2 files changed, 23 insertions(+), 15 deletions(-) diff --git a/libs/SoloPlay/Functions/AttackOverrides.js b/libs/SoloPlay/Functions/AttackOverrides.js index b9da93a1..a36c6b15 100644 --- a/libs/SoloPlay/Functions/AttackOverrides.js +++ b/libs/SoloPlay/Functions/AttackOverrides.js @@ -853,7 +853,7 @@ Attack.getCurrentChargedSkillIds = function (init = false) { // Item must be equipped - removed charms as I don't think at any point using hydra from torch has ever been worth it me.getItemsEx(-1) - .filter(item => item && ((item.isEquipped && !item.rare))) + .filter(item => item && ((item.isEquipped /* && !item.rare */))) .forEach(function (item) { let stats = item.getStat(-2); @@ -947,12 +947,16 @@ Attack.getItemCharges = function (skillId) { * @returns {boolean} */ Attack.castCharges = function (skillId, unit) { - if (!skillId || !unit || !Skill.wereFormCheck(skillId) || (me.inTown && !Skill.townSkill(skillId))) { + if (!skillId || !unit || !Skill.wereFormCheck(skillId) + || (me.inTown && !Skill.townSkill(skillId))) { return false; } - me.castChargedSkillEx(skillId, unit) && delay(25); - me.weaponswitch === 1 && me.switchWeapons(0); + try { + me.castChargedSkillEx(skillId, unit) && delay(25); + } finally { + me.weaponswitch === 1 && me.switchWeapons(0); + } return true; }; @@ -963,12 +967,16 @@ Attack.castCharges = function (skillId, unit) { * @returns {boolean} */ Attack.switchCastCharges = function (skillId, unit) { - if (!skillId || !unit || !Skill.wereFormCheck(skillId) || (me.inTown && !Skill.townSkill(skillId))) { + if (!skillId || !unit || !Skill.wereFormCheck(skillId) + || (me.inTown && !Skill.townSkill(skillId))) { return false; } - me.castSwitchChargedSkill(skillId, unit) && delay(25); - me.weaponswitch === 1 && me.switchWeapons(0); + try { + me.castSwitchChargedSkill(skillId, unit) && delay(25); + } finally { + me.weaponswitch === 1 && me.switchWeapons(0); + } return true; }; diff --git a/libs/SoloPlay/Functions/DynamicTiers.js b/libs/SoloPlay/Functions/DynamicTiers.js index b0f7715e..a3fd8489 100644 --- a/libs/SoloPlay/Functions/DynamicTiers.js +++ b/libs/SoloPlay/Functions/DynamicTiers.js @@ -13,8 +13,8 @@ (function () { /** - * @param {ItemUnit} item - */ + * @param {ItemUnit} item + */ const sumElementalDmg = function (item) { if (!item) return 0; let fire = item.getStatEx(sdk.stats.FireMinDamage) + item.getStatEx(sdk.stats.FireMaxDamage); @@ -26,8 +26,8 @@ }; /** - * @param {ItemUnit} item - */ + * @param {ItemUnit} item + */ const mercscore = function (item) { const mercWeights = { IAS: 3.5, @@ -136,8 +136,8 @@ }; /** - * @param {ItemUnit} item - * @param {number} [skillId] + * @param {ItemUnit} item + * @param {number} [skillId] * @param {object} [buildInfo] */ const chargeditemscore = function (item, skillId, buildInfo) { @@ -592,8 +592,8 @@ }; /** - * @param {ItemUnit} item - */ + * @param {ItemUnit} item + */ const charmscore = function (item) { if (me.data.charmGids.includes(item.gid)) return 1000; // depending on invo space it might be worth it early on to keep 1 or 2 non-skiller grandcharms - @todo test that out From 40013f83be5b71d91fe8e8e3a35a1ec9caae7f54 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Tue, 25 Apr 2023 12:53:32 -0400 Subject: [PATCH 113/263] remove some debug logging --- libs/SoloPlay/Functions/PatherOverrides.js | 21 +++++++++---------- libs/SoloPlay/Functions/PrototypeOverrides.js | 2 +- 2 files changed, 11 insertions(+), 12 deletions(-) diff --git a/libs/SoloPlay/Functions/PatherOverrides.js b/libs/SoloPlay/Functions/PatherOverrides.js index fdbc0d44..2ec5632a 100644 --- a/libs/SoloPlay/Functions/PatherOverrides.js +++ b/libs/SoloPlay/Functions/PatherOverrides.js @@ -464,7 +464,7 @@ Pather.move = function (target, givenSettings = {}) { node = path.shift(); if (typeof settings.callback === "function" && settings.callback()) { - console.debug("Callback function passed. Ending path."); + // console.debug("Callback function passed. Ending path."); useTeleport && Config.TeleSwitch && me.switchWeapons(Attack.getPrimarySlot() ^ 1); PathDebug.removeHooks(); return true; @@ -525,23 +525,23 @@ Pather.move = function (target, givenSettings = {}) { // @todo check shrines/chests in proximity to old node vs next node // let otherObjects = getUnits(sdk.unittype.Object).filter(el => getDistance()); if (goBack) { - console.debug("Going back to old node. Distance: " + node.distance); + // console.debug("Going back to old node. Distance: " + node.distance); } else if (nearestNode && nearestNode.distance > 5 && node.distance > 5 && Math.percentDifference(node.distance, nearestNode.distance) > 5/* && 100 / node.distance * nearestNode.distance < 95 */) { - console.debug("Moving to next node. Distance: " + nearestNode.distance); + // console.debug("Moving to next node. Distance: " + nearestNode.distance); let newIndex = path.findIndex(node => nearestNode.x === node.x && nearestNode.y === node.y); if (newIndex > -1) { - console.debug("Found new path index: " + newIndex + " of currentPathLen: " + path.length); + // console.debug("Found new path index: " + newIndex + " of currentPathLen: " + path.length); path = path.slice(newIndex); node = path.shift(); foundNode = true; - console.debug("New path length: " + path.length); + // console.debug("New path length: " + path.length); } else { - console.debug("Couldn't find new path index"); + // console.debug("Couldn't find new path index"); } } if (node.distance > 5) { - !foundNode && console.debug("Path Recursion :: Returning to position " + node.x + "/" + node.y + " distance: " + node.distance); + // !foundNode && console.debug("Path Recursion :: Returning to position " + node.x + "/" + node.y + " distance: " + node.distance); Pather.move(node, settings); } } else { @@ -561,7 +561,7 @@ Pather.move = function (target, givenSettings = {}) { if (me.checkForMobs({ range: tempRange, coll: sdk.collision.BlockWalk })) { // there are at least some, but lets only continue to next iteration if we actually killed something if (Attack.clear(tempRange, null, null, null, settings.allowPicking) === Attack.Result.SUCCESS) { - console.debug("Cleared Node"); + // console.debug("Cleared Node"); continue; } } @@ -577,7 +577,7 @@ Pather.move = function (target, givenSettings = {}) { if (cleared.at === 0 || getTickCount() - cleared.at > Time.seconds(3) && cleared.where.distance > 5 && me.checkForMobs({ range: 10 })) { // only set that we cleared if we actually killed at least 1 mob if (Attack.clear(10, null, null, null, settings.allowPicking)) { - console.debug("Cleared Node"); + // console.debug("Cleared Node"); cleared.at = getTickCount(); [cleared.where.x, cleared.where.y] = [node.x, node.y]; } @@ -671,7 +671,7 @@ Pather.moveToEx = function (x, y, givenSettings = {}) { }; // Add check in case "random" to return false if bot doesn't have cold plains wp yet -Pather.useWaypoint = function useWaypoint(targetArea, check = false) { +Pather.useWaypoint = function useWaypoint(targetArea, check = false, getWP = false) { switch (targetArea) { case undefined: throw new Error("useWaypoint: Invalid targetArea parameter: " + targetArea); @@ -687,7 +687,6 @@ Pather.useWaypoint = function useWaypoint(targetArea, check = false) { break; } - console.log("ÿc7Start ÿc8(useWaypoint) ÿc0:: ÿc7targetArea: ÿc0" + getAreaName(targetArea) + " ÿc7myArea: ÿc0" + getAreaName(me.area)); let wpTick = getTickCount(); for (let i = 0; i < 12; i += 1) { diff --git a/libs/SoloPlay/Functions/PrototypeOverrides.js b/libs/SoloPlay/Functions/PrototypeOverrides.js index af3616f1..cc6155a0 100644 --- a/libs/SoloPlay/Functions/PrototypeOverrides.js +++ b/libs/SoloPlay/Functions/PrototypeOverrides.js @@ -433,7 +433,7 @@ Unit.prototype.castChargedSkillEx = function (...args) { .byte(0x00) .dword(this.gid) .send(); - console.log("Set charge skill " + charge.skill + " on hand"); + // console.log("Set charge skill " + charge.skill + " on hand"); // No need for a delay, since its TCP, the server recv's the next statement always after the send cast skill packet // Cast the skill From 68b4a82c5c4d0e97673511cfe3ff43a2e1a42eb9 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Wed, 26 Apr 2023 13:02:21 -0400 Subject: [PATCH 114/263] Update README.md - Fix reference to old Config.levelCap --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 9f230f1b..cfe98f64 100644 --- a/README.md +++ b/README.md @@ -75,11 +75,11 @@ ### **Q: When will the bot change to the final build I selected?** **A:** In classic, the bot will switch to the final build after it defeats diablo and meets a level requirement. -In expansion, it transitions to the final build when final gear requirements are met ``(Navigate to libs\SoloPlay\``[``BuildFiles``](libs/SoloPlay/BuildFiles/)) and look for the file with the name of the final build you choose to see what items are needed for each build and what level is required for classic). +In expansion, it transitions to the final build when final gear requirements are met ``(Navigate to libs\SoloPlay\``[``BuildFiles\``](libs/SoloPlay/BuildFiles/)) and look for the file with the name of the final build you choose to see what items are needed for each build and what level is required for classic. ### **Q: The bot has beaten diablo (classic) / baal, so why isn't moving on to the next difficulty?** -**A:** The bot will only progress once it has reached a minimum character level (`navigate to libs\SoloPlay\Config\classname.js` and see `Config.levelCap` for level requirments) and will not start the next difficulty with negative resistances. If the bot is more than 5 levels higher than the minimum character level and has not reached the required resistances, it will automatically move to the next difficulty. +**A:** The bot will only progress once it has reached a minimum character level (``navigate to libs\SoloPlay\``[``BuildFiles``](libs/SoloPlay/BuildFiles/)``\classname\classname.js`` and see `CharInfo.levelCap` for level requirments) and will not start the next difficulty with negative resistances. If the bot is more than 5 levels higher than the minimum character level and has not reached the required resistances, it will automatically move to the next difficulty. ### **Q: How can I run more than one of the same class?** From 92fd3d209c40e437452ba36674485f1436185ae7 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Mon, 8 May 2023 13:00:50 -0400 Subject: [PATCH 115/263] add distance pickit flag - grab any amount of gold if it's next to us to reduce the amount of needed town trips to sell items --- libs/SoloPlay/Functions/NTIPOverrides.js | 20 ++++++++++++++++---- libs/SoloPlay/Utils/General.js | 1 + 2 files changed, 17 insertions(+), 4 deletions(-) diff --git a/libs/SoloPlay/Functions/NTIPOverrides.js b/libs/SoloPlay/Functions/NTIPOverrides.js index fc76377f..814e4379 100644 --- a/libs/SoloPlay/Functions/NTIPOverrides.js +++ b/libs/SoloPlay/Functions/NTIPOverrides.js @@ -29,6 +29,10 @@ NTIP.FinalGear = { strArray: [], }; +/** + * @param {string} tierType + * @returns {(item: ItemUnit) => number} + */ NTIP.generateTierFunc = function (tierType) { return function (item) { let tier = -1; @@ -80,25 +84,29 @@ NTIP.generateTierFunc = function (tierType) { /** * @function - * @param item + * @param {ItemUnit} item + * @returns {number} */ NTIP.GetTier = NTIP.generateTierFunc("Tier"); /** * @function - * @param item + * @param {ItemUnit} item + * @returns {number} */ NTIP.GetMercTier = NTIP.generateTierFunc("Merctier"); /** * @function - * @param item + * @param {ItemUnit} item + * @returns {number} */ NTIP.GetCharmTier = NTIP.generateTierFunc("Charmtier"); /** * @function - * @param item + * @param {ItemUnit} item + * @returns {number} */ NTIP.GetSecondaryTier = NTIP.generateTierFunc("Secondarytier"); @@ -636,6 +644,10 @@ NTIP.ParseLineInt = function (input, info) { case "classic": p_result[0] += "(!me.gametype)"; + break; + case "distance": + p_result[0] += "(item.onGroundOrDropping && item.distance || Infinity)"; + break; default: Misc.errorReport("Unknown property: " + property + " File: " + info.file + " Line: " + info.line); diff --git a/libs/SoloPlay/Utils/General.js b/libs/SoloPlay/Utils/General.js index 052527d5..6cee61f4 100644 --- a/libs/SoloPlay/Utils/General.js +++ b/libs/SoloPlay/Utils/General.js @@ -10,6 +10,7 @@ "[name] == tomeoftownportal", "[name] == tomeofidentify", "[name] == gold # [gold] >= me.charlvl * 3 * me.diff", + "[name] == gold && [distance] < 5 # [gold] >= 1", "(me.charlvl < 20 || me.gold < 500) && [name] == minorhealingpotion", "(me.charlvl < 25 || me.gold < 2000) && [name] == lighthealingpotion", "(me.charlvl < 29 || me.gold < 5000) && [name] == healingpotion", From 9f229fefd63872febae5dfa2046c6f18e92ddc0e Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Thu, 18 May 2023 16:44:58 -0400 Subject: [PATCH 116/263] Update SoloIndex.js - Fix preReqs for summoner and tombs --- libs/SoloPlay/Tools/SoloIndex.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libs/SoloPlay/Tools/SoloIndex.js b/libs/SoloPlay/Tools/SoloIndex.js index 078ec352..71b413f0 100644 --- a/libs/SoloPlay/Tools/SoloIndex.js +++ b/libs/SoloPlay/Tools/SoloIndex.js @@ -353,7 +353,7 @@ const SoloIndex = { }, "summoner": { preReq: function () { - return me.accessToAct(2); + return me.accessToAct(2) && me.getQuest(sdk.quest.id.TheTaintedSun, sdk.quest.states.Completed) === 1; }, skipIf: function () { return me.summoner; @@ -378,7 +378,7 @@ const SoloIndex = { }, "tombs": { preReq: function () { - return me.accessToAct(2); + return me.accessToAct(2) && me.summoner; }, skipIf: function () { return (!me.normal || me.charlvl > 22); From a3b374189e990e827dc40d668d981622124ec232 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Sat, 20 May 2023 11:38:24 -0400 Subject: [PATCH 117/263] Move gametime tracking to entry - Move the interval updates to the entry, keeps it consistent and handles the oog update call. Having it done in soloplay main thread meant two updates done back to back and was causing a race condition --- D2BotSoloPlay.dbj | 56 +++++++++++++-------------- libs/SoloPlay/Workers/EventEmitter.js | 13 ------- 2 files changed, 27 insertions(+), 42 deletions(-) diff --git a/D2BotSoloPlay.dbj b/D2BotSoloPlay.dbj index 5c53d109..cfa61fa2 100644 --- a/D2BotSoloPlay.dbj +++ b/D2BotSoloPlay.dbj @@ -1,4 +1,3 @@ -/* eslint-disable no-fallthrough */ /** * @filename D2BotSoloPlay.dbj * @author theBGuy @@ -24,6 +23,7 @@ Starter.Config.GlobalAccountPassword = ""; // Set value for a global password fo // Override default values for StarterConfig under here by following format // Starter.Config.ValueToChange = value; // Example: Starter.Config.MinGameTime = 500; // changes MinGameTime to 500 seconds +// Profile().type === sdk.game.profiletype.SinglePlayer && (Starter.Config.CrashDelay = 2); /** * @todo @@ -46,25 +46,16 @@ include("SoloPlay/Functions/ConfigOverrides.js"); if (typeof Starter.AdvancedConfig[me.profile] === "object") { Object.assign(Starter.Config, Starter.AdvancedConfig[me.profile]); -} else { - // no need to carry around the reference - delete Starter.AdvancedConfig; } +delete Starter.AdvancedConfig; // initialize data files if (!FileTools.exists("data/" + me.profile + ".json") && DataFile.create()) { Starter.firstRun = true; - delay(rand(10, 100)); -} - -if (!FileTools.exists(CharData.filePath) && CharData.create()) { - delay(rand(10, 100)); -} - -if (!FileTools.exists(CharData.login.filePath) && CharData.login.create()) { - delay(rand(10, 100)); } +!FileTools.exists(CharData.filePath) && CharData.create(); +!FileTools.exists(CharData.login.filePath) && CharData.login.create(); Developer.logPerformance && Tracker.initialize(); function main () { @@ -75,22 +66,15 @@ function main () { let oogTick = getTickCount(); while (!Starter.handle) { - delay(100); + delay(3); } DataFile.updateStats("handle", Starter.handle); + D2Bot.handle = Starter.handle; delay(500); - while (!D2Bot.init()) { - delay(250); - } load("threads/heartbeat.js"); - while (!Object.keys(Starter.gameInfo).length) { - D2Bot.requestGameInfo(); - delay(500); - } - if (Profile().type === sdk.game.profiletype.TcpIpJoin) { D2Bot.printToConsole("TcpJoin is unsupported."); D2Bot.stop(); @@ -98,6 +82,12 @@ function main () { Starter.gameCount = (DataFile.getStats().runs + 1 || 1); + while (!Object.keys(Starter.gameInfo).length) { + delay(rand(200, 1500)); + D2Bot.requestGameInfo(); + delay(500); + } + if (Starter.gameInfo.error) { ControlAction.timeoutDelay("Crash Delay", Starter.Config.CrashDelay * 1e3); Starter.BNET && D2Bot.updateRuns(); @@ -106,17 +96,11 @@ function main () { DataFile.updateStats("debugInfo", JSON.stringify({ currScript: "none", area: "out of game" })); while (!Object.keys(Starter.profileInfo).length) { + delay(rand(200, 1500)); D2Bot.getProfile(); - console.log("Getting Profile"); delay(500); } - if (Starter.profileInfo.charName === "") { - console.log("Generating Character Name"); - Starter.profileInfo.charName = require("./libs/SoloPlay/Tools/NameGen")(); - delay(50); - } - while (true) { // returns true before actually in game so we can't only use this check while (me.ingame) { @@ -133,6 +117,20 @@ function main () { Developer.logPerformance && Tracker.update((getTickCount() - oogTick)); oogTick = 0; D2Bot.updateStatus("In-Game :: Initializing threads..."); + } else { + // Tracker + if (Developer.logPerformance) { + if (getTickCount() - Tracker.tick > Time.minutes(3)) { + Tracker.tick = getTickCount(); + + try { + Tracker.update(); + console.log("Gametime Updated"); + } catch (e) { + console.error(e); + } + } + } } } diff --git a/libs/SoloPlay/Workers/EventEmitter.js b/libs/SoloPlay/Workers/EventEmitter.js index a84c2be2..e9a40b01 100644 --- a/libs/SoloPlay/Workers/EventEmitter.js +++ b/libs/SoloPlay/Workers/EventEmitter.js @@ -68,19 +68,6 @@ _AutoBuild.run(); } - // Tracker - if (Developer.logPerformance) { - if (getTickCount() - Tracker.tick > Time.minutes(3)) { - Tracker.tick = getTickCount(); - - try { - Tracker.update(); - } catch (e) { - console.error(e); - } - } - } - return true; }; From 52a1bf0f785576ea7a24244b07deb218760680c1 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Sat, 20 May 2023 23:50:11 -0400 Subject: [PATCH 118/263] Update ToolsThread.js - wrap overlay handling in try-catch so it doesn't shut down toolsthread if there is an error --- libs/SoloPlay/Threads/ToolsThread.js | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/libs/SoloPlay/Threads/ToolsThread.js b/libs/SoloPlay/Threads/ToolsThread.js index e4aafff9..c3c5f2d5 100644 --- a/libs/SoloPlay/Threads/ToolsThread.js +++ b/libs/SoloPlay/Threads/ToolsThread.js @@ -642,14 +642,21 @@ function main () { // handle overlay if (Developer.overlay) { - if (me.ingame && me.gameReady && me.area) { - Overlay.update(quitFlag); - - if (me.act !== myAct) { - Overlay.flush(); - myAct = me.act; + try { + if (me.ingame && me.gameReady && me.area) { Overlay.update(quitFlag); + + if (me.act !== myAct) { + Overlay.flush(); + myAct = me.act; + Overlay.update(quitFlag); + } } + } catch (e) { + console.error(e); + console.log("Overlay disabled"); + D2Bot.printToConsole("Overlay disabled", sdk.colors.D2Bot.Red); + Developer.overlay = false; } } } From 2090be749e10dd34fe09795336b85312df5d78e3 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Sat, 20 May 2023 23:52:05 -0400 Subject: [PATCH 119/263] Update Overlay.js - partial rebuild of overlay, still more to do but this cleans up a significant amount of it - Fixed display bug where gametime was resetting every 24hours --- libs/SoloPlay/Tools/Overlay.js | 868 +++++++++++++-------------------- 1 file changed, 335 insertions(+), 533 deletions(-) diff --git a/libs/SoloPlay/Tools/Overlay.js b/libs/SoloPlay/Tools/Overlay.js index 94f37099..6d4e7943 100644 --- a/libs/SoloPlay/Tools/Overlay.js +++ b/libs/SoloPlay/Tools/Overlay.js @@ -10,6 +10,9 @@ includeIfNotIncluded("SoloPlay/Tools/Developer.js"); includeIfNotIncluded("SoloPlay/Tools/Tracker.js"); includeIfNotIncluded("SoloPlay/Functions/PrototypeOverrides.js"); +/** + * @todo Clean this up, probably needs to be entirely rewritten + */ const Overlay = { resfix: { x: -10, y: me.screensize ? 0 : -120 }, quest: { x: 8, y: 368 }, @@ -17,575 +20,374 @@ const Overlay = { dashboard: { x: 120, y: 470 }, timer: { x: 0, y: 595 }, build: SetUp.currentBuild, + script: "", realm: (me.realm ? me.realm : "SinglePlayer"), - difficulty: () => sdk.difficulty.nameOf(me.diff), + difficulty: sdk.difficulty.nameOf(me.diff), level: () => me.data.level, - text: { - hooks: [], - GameTracker: Tracker.readObj(Tracker.GTPath), - enabled: true, - charlvl: 0, - tick: 0, - - clock: function () { - if (!Developer.logPerformance) return ""; - this.GameTracker === undefined && (this.GameTracker = Tracker.readObj(Tracker.GTPath)); - this.tick = getTickCount(); - let currInGame = getTickCount() - me.gamestarttime; - let totalTime = Time.format(this.GameTracker.Total + currInGame); - let totalInGame = Time.format(this.GameTracker.InGame + currInGame); - - return ("Total: ÿc0" + totalTime + "ÿc4 InGame: ÿc0" + totalInGame + "ÿc4 OOG: ÿc0" + Time.format(this.GameTracker.OOG)); - }, - - timer: function () { + + text: (function () { + const _gameTracker = Tracker.readObj(Tracker.GTPath); + let [_tick, _charlvl] = [0, 0]; + + const _format = function (ms = 0) { + const hours = Math.floor(ms / 3600000); + const minutes = Math.floor((ms % 3600000) / 60000); + const seconds = Math.floor((ms % 60000) / 1000); + + /** @param {number} num */ + const pad = (num) => (num < 10 ? '0' + num : num); + + return pad(hours) + ':' + pad(minutes) + ':' + pad(seconds); + }; + + const _timer = function () { return (new Date(getTickCount() - me.gamestarttime).toISOString().slice(11, -5)); - }, + }; + + return { + /** @type {Array<{ name: string, hook: Hook }>} */ + hooks: [], + enabled: true, + + clock: function () { + if (!Developer.logPerformance) return ""; + _gameTracker === undefined && (Object.assign(_gameTracker, Tracker.readObj(Tracker.GTPath))); + _tick = getTickCount(); + let currInGame = getTickCount() - me.gamestarttime; + let totalTime = _format(_gameTracker.Total + currInGame); + let totalInGame = _format(_gameTracker.InGame + currInGame); + + return ("Total: ÿc0" + totalTime + "ÿc4 InGame: ÿc0" + totalInGame + "ÿc4 OOG: ÿc0" + _format(_gameTracker.OOG)); + }, - check: function () { - if (!this.enabled) { - this.flush(); + check: function () { + if (!this.enabled) { + this.flush(); - return; - } + return; + } - // Double check in case still got here before being ready - if (!me.gameReady && !me.ingame && !me.area) return; + // Double check in case still got here before being ready + if (!me.gameReady && !me.ingame && !me.area) return; - !this.getHook("dashboard") && this.add("dashboard"); - !this.getHook("credits") && this.add("credits"); - - if (!this.getHook("InGameTimer")) { - this.add("InGameTimer"); - } else { - if (getTickCount() - this.tick >= 1000) { - this.getHook("InGameTimer").hook.text = "ÿc0" + this.timer(); + !this.getHook("dashboard") && this.add("dashboard"); + !this.getHook("credits") && this.add("credits"); + + if (!this.getHook("InGameTimer")) { + this.add("InGameTimer"); + } else { + if (getTickCount() - _tick >= 1000) { + this.getHook("InGameTimer").hook.text = "ÿc0" + _timer(); + } } - } - if (Developer.logPerformance) { - this.GameTracker === undefined && (this.GameTracker = Tracker.readObj(Tracker.GTPath)); - if (!this.getHook("times")) { - this.add("times"); - } else { - if (getTickCount() - this.tick >= 1000) { - this.getHook("times").hook.text = this.clock(); + if (Developer.logPerformance) { + if (!this.getHook("times")) { + this.add("times"); + } else { + if (getTickCount() - _tick >= 1000) { + this.getHook("times").hook.text = this.clock(); + } } } - } - if (!this.getHook("level")) { - this.add("level"); - } else if (this.charlvl !== Overlay.level()) { - this.charlvl = Overlay.level(); - this.getHook("level").hook.text = "Name: ÿc0" + me.name + "ÿc4 Diff: ÿc0" + Overlay.difficulty() + "ÿc4 Level: ÿc0" + this.charlvl; - } - }, - - add: function (name) { - switch (name) { - case "dashboard": - this.hooks.push({ - name: "dashboard", - hook: new Box(Overlay.dashboard.x + Overlay.resfix.x, Overlay.dashboard.y + Overlay.resfix.y, 370, 80, 0x0, 4, 0) - }); - - this.hooks.push({ - name: "dashboardframe", - hook: new Frame(Overlay.dashboard.x + Overlay.resfix.x, Overlay.dashboard.y + Overlay.resfix.y, 370, 80, 0) - }); - - this.getHook("dashboard").hook.zorder = 0; - - break; - case "credits": - this.hooks.push({ - name: "credits", - hook: new Text("Kolbot-SoloPlay by: ÿc0 theBGuy" + "ÿc4 Realm: ÿc0" + Overlay.realm, Overlay.dashboard.x, Overlay.dashboard.y + Overlay.resfix.y + 15, 4, 13, 0) - }); - - break; - case "level": - this.charlvl = Overlay.level(); - this.hooks.push({ - name: "level", - hook: new Text("Name: ÿc0" + me.name + "ÿc4 Diff: ÿc0" + Overlay.difficulty() + "ÿc4 Level: ÿc0" + this.charlvl, Overlay.dashboard.x, Overlay.dashboard.y + Overlay.resfix.y + 30, 4, 13, 0) - }); - - break; - case "times": - this.hooks.push({ - name: "times", - hook: new Text(this.clock(), Overlay.dashboard.x, Overlay.dashboard.y + Overlay.resfix.y + 75, 4, 13, 0) - }); - - break; - case "InGameTimer": - this.hooks.push({ - name: "timerBoard", - hook: new Box(Overlay.timer.x, Overlay.timer.y - 15 + Overlay.resfix.y, 68, 18, 0, 4, 0) - }); - - this.hooks.push({ - name: "timerFrame", - hook: new Frame(Overlay.timer.x, Overlay.timer.y - 15 + Overlay.resfix.y, 68, 18, 0) - }); - - this.hooks.push({ - name: "InGameTimer", - hook: new Text("ÿc0" + this.timer(), Overlay.timer.x + 7, Overlay.timer.y + Overlay.resfix.y, 0, 13, 0) - }); - - break; - } - }, + if (!this.getHook("level")) { + this.add("level"); + } else if (_charlvl !== Overlay.level()) { + _charlvl = Overlay.level(); + this.getHook("level").hook.text = "Name: ÿc0" + me.name + "ÿc4 Diff: ÿc0" + Overlay.difficulty + "ÿc4 Level: ÿc0" + _charlvl; + } + }, - getHook: function (name) { - for (let i = 0; i < this.hooks.length; i++) { - if (this.hooks[i].name === name) { - return this.hooks[i]; + add: function (name) { + switch (name) { + case "dashboard": + this.hooks.push({ + name: "dashboard", + hook: new Box(Overlay.dashboard.x + Overlay.resfix.x, Overlay.dashboard.y + Overlay.resfix.y, 370, 80, 0x0, 4, 0) + }); + + this.hooks.push({ + name: "dashboardframe", + hook: new Frame(Overlay.dashboard.x + Overlay.resfix.x, Overlay.dashboard.y + Overlay.resfix.y, 370, 80, 0) + }); + + this.getHook("dashboard").hook.zorder = 0; + + break; + case "credits": + this.hooks.push({ + name: "credits", + hook: new Text("Kolbot-SoloPlay by: ÿc0 theBGuy" + "ÿc4 Realm: ÿc0" + Overlay.realm, Overlay.dashboard.x, Overlay.dashboard.y + Overlay.resfix.y + 15, 4, 13, 0) + }); + + break; + case "level": + _charlvl = Overlay.level(); + this.hooks.push({ + name: "level", + hook: new Text("Name: ÿc0" + me.name + "ÿc4 Diff: ÿc0" + Overlay.difficulty + "ÿc4 Level: ÿc0" + _charlvl, Overlay.dashboard.x, Overlay.dashboard.y + Overlay.resfix.y + 30, 4, 13, 0) + }); + + break; + case "times": + this.hooks.push({ + name: "times", + hook: new Text(this.clock(), Overlay.dashboard.x, Overlay.dashboard.y + Overlay.resfix.y + 75, 4, 13, 0) + }); + + break; + case "InGameTimer": + this.hooks.push({ + name: "timerBoard", + hook: new Box(Overlay.timer.x, Overlay.timer.y - 15 + Overlay.resfix.y, 68, 18, 0, 4, 0) + }); + + this.hooks.push({ + name: "timerFrame", + hook: new Frame(Overlay.timer.x, Overlay.timer.y - 15 + Overlay.resfix.y, 68, 18, 0) + }); + + this.hooks.push({ + name: "InGameTimer", + hook: new Text("ÿc0" + _timer(), Overlay.timer.x + 7, Overlay.timer.y + Overlay.resfix.y, 0, 13, 0) + }); + + break; } - } + }, - return false; - }, + getHook: function (name) { + for (let i = 0; i < this.hooks.length; i++) { + if (this.hooks[i].name === name) { + return this.hooks[i]; + } + } - flush: function () { - while (this.hooks.length) { - this.hooks.shift().hook.remove(); + return false; + }, + + flush: function () { + while (this.hooks.length) { + this.hooks.shift().hook.remove(); + } + return true; } - return true; + }; + })(), + + quests: (function () { + const QuestData = require("../../core/GameData/QuestData"); + + /** + * @constructor + * @param {string} name + * @param {number} id + * @param {boolean} [preReq] + */ + function QuestHook (name, id, preReq = false) { + this.name = name; + this.id = id; + this.preReq = preReq; } - }, - - quests: { - enabled: true, - hooks: [], - font: 12, - qHooks: [], - data: { - Den: { - complete: false, - condition: () => me.getQuest(sdk.quest.id.DenofEvil, sdk.quest.states.Completed) - }, - BloodRaven: { - complete: false, - condition: () => me.getQuest(sdk.quest.id.SistersBurialGrounds, sdk.quest.states.Completed) - }, - Tristram: { - complete: false, - condition: () => me.getQuest(sdk.quest.id.TheSearchForCain, sdk.quest.states.Completed) - }, - Countess: { - complete: false, - condition: () => me.getQuest(sdk.quest.id.ForgottenTower, sdk.quest.states.Completed) - }, - Smith: { - complete: false, - condition: () => (me.getQuest(sdk.quest.id.ToolsoftheTrade, sdk.quest.states.Completed) || me.getQuest(sdk.quest.id.ToolsoftheTrade, sdk.quest.states.ReqComplete)) - }, - Andariel: { - complete: false, - condition: () => me.getQuest(sdk.quest.id.SistersToTheSlaughter, sdk.quest.states.Completed) - }, - Cube: { - complete: false, - condition: () => me.cube - }, - Radament: { - complete: false, - condition: () => me.getQuest(sdk.quest.id.RadamentsLair, sdk.quest.states.Completed) - }, - HoradricStaff: { - complete: false, - condition: () => me.getQuest(sdk.quest.id.TheHoradricStaff, sdk.quest.states.Completed) - }, - Amulet: { - complete: false, - condition: () => me.getQuest(sdk.quest.id.TheTaintedSun, sdk.quest.states.Completed) - }, - Summoner: { - complete: false, - condition: () => me.getQuest(sdk.quest.id.TheSummoner, sdk.quest.states.Completed) - }, - Duriel: { - complete: false, - condition: () => me.getQuest(sdk.quest.id.TheSevenTombs, sdk.quest.states.Completed) - }, - GoldenBird: { - complete: false, - condition: () => me.getQuest(sdk.quest.id.TheGoldenBird, sdk.quest.states.Completed) + /** + * @this {QuestHook} + * @returns {string} + */ + QuestHook.prototype.status = function () { + return QuestData.get(this.id).complete(this.preReq) ? "ÿc2Complete" : "ÿc1Incomplete"; + }; + + /** + * @this {QuestHook} + * @returns {{ name: string, hook: Hook }} + */ + QuestHook.prototype.hook = function () { + return { + name: this.name, + hook: new Text(this.name + ": " + this.status(), Overlay.quest.x, Overlay.quest.y + Overlay.resfix.y + (15 * _qHooks.length), 4, _font, 0) + }; + }; + + const _quests = new Map([ + // Act 1 + ["Den", new QuestHook("Den", sdk.quest.id.DenofEvil)], + ["Blood Raven", new QuestHook("Blood Raven", sdk.quest.id.SistersBurialGrounds)], + ["Tristram", new QuestHook("Tristram", sdk.quest.id.TheSearchForCain)], + ["Countess", new QuestHook("Countess", sdk.quest.id.ForgottenTower)], + ["Smith", new QuestHook("Smith", sdk.quest.id.ToolsoftheTrade, true)], + ["Andariel", new QuestHook("Andariel", sdk.quest.id.SistersToTheSlaughter)], + // Act 2 + ["Cube", new QuestHook("Cube", sdk.quest.id.TheHoradricStaff)], + ["Radament", new QuestHook("Radament", sdk.quest.id.RadamentsLair)], + ["Horadric Staff", new QuestHook("Horadric Staff", sdk.quest.id.TheHoradricStaff)], + ["Amulet", new QuestHook("Amulet", sdk.quest.id.TheTaintedSun)], + ["Summoner", new QuestHook("Summoner", sdk.quest.id.TheSummoner)], + ["Duriel", new QuestHook("Duriel", sdk.quest.id.TheSevenTombs)], + // Act 3 + ["Golden Bird", new QuestHook("Golden Bird", sdk.quest.id.TheGoldenBird)], + ["Khalim's Will", new QuestHook("Khalim's Will", sdk.quest.id.KhalimsWill)], + ["Lam Esen", new QuestHook("Lam Esen", sdk.quest.id.LamEsensTome)], + ["Travincal", new QuestHook("Travincal", sdk.quest.id.TheBlackenedTemple)], + ["Mephisto", new QuestHook("Mephisto", sdk.quest.id.TheGuardian)], + // Act 4 + ["Izual", new QuestHook("Izual", sdk.quest.id.TheFallenAngel)], + ["Hell Forge", new QuestHook("Hell Forge", sdk.quest.id.HellsForge, true)], + ["Diablo", new QuestHook("Diablo", sdk.quest.id.TerrorsEnd)], + // Act 5 + ["Shenk", new QuestHook("Shenk", sdk.quest.id.SiegeOnHarrogath, true)], + ["Barbies", new QuestHook("Barbies", sdk.quest.id.RescueonMountArreat)], + ["Anya", new QuestHook("Anya", sdk.quest.id.PrisonofIce)], + ["Ancients", new QuestHook("Ancients", sdk.quest.id.RiteofPassage)], + ["Baal", new QuestHook("Baal", sdk.quest.id.EyeofDestruction)] + ]); + + const _acts = new Map([ + [1, ["Den", "Blood Raven", "Tristram", "Countess", "Smith", "Andariel"]], + [2, [/* "Cube", */"Radament", "Horadric Staff", "Amulet", "Summoner", "Duriel"]], + [3, ["Golden Bird", "Khalim's Will", "Lam Esen", "Travincal", "Mephisto"]], + [4, ["Izual", "Hell Forge", "Diablo"]], + [5, ["Shenk", "Barbies", "Anya", "Ancients", "Baal"]] + ]); + + const _font = 12; + const _qHooks = []; + + return { + enabled: true, + hooks: [], + + getRes: function () { + // Double check in case still got here before being ready + if (!me.gameReady || !me.ingame || !me.area) return ""; + return ("FR: ÿc1" + me.FR + "ÿc4 CR: ÿc3" + me.CR + "ÿc4 LR: ÿc9" + me.LR + "ÿc4 PR: ÿc2" + me.PR + "ÿc4 CurrentBuild: ÿc0" + Overlay.build); }, - KhalimsWill: { - complete: false, - condition: () => me.getQuest(sdk.quest.id.KhalimsWill, sdk.quest.states.Completed) - }, - LamEsen: { - complete: false, - condition: () => me.getQuest(sdk.quest.id.LamEsensTome, sdk.quest.states.Completed) - }, - Travincal: { - complete: false, - condition: () => me.getQuest(sdk.quest.id.TheBlackenedTemple, sdk.quest.states.Completed) - }, - Mephisto: { - complete: false, - condition: () => me.getQuest(sdk.quest.id.TheGuardian, sdk.quest.states.Completed) - }, - Izual: { - complete: false, - condition: () => me.getQuest(sdk.quest.id.TheFallenAngel, sdk.quest.states.Completed) - }, - HellForge: { - complete: false, - condition: () => (me.getQuest(sdk.quest.id.HellsForge, sdk.quest.states.Completed) || me.getQuest(sdk.quest.id.HellsForge, sdk.quest.states.ReqComplete)) - }, - Diablo: { - complete: false, - condition: () => me.getQuest(sdk.quest.id.TerrorsEnd, sdk.quest.states.Completed) - }, - Shenk: { - complete: false, - condition: () => (me.getQuest(sdk.quest.id.SiegeOnHarrogath, sdk.quest.states.Completed) || me.getQuest(sdk.quest.id.SiegeOnHarrogath, sdk.quest.states.ReqComplete)) - }, - Barbies: { - complete: false, - condition: () => me.getQuest(sdk.quest.id.RescueonMountArreat, sdk.quest.states.Completed) - }, - Anya: { - complete: false, - condition: () => me.getQuest(sdk.quest.id.PrisonofIce, sdk.quest.states.Completed) - }, - Ancients: { - complete: false, - condition: () => me.getQuest(sdk.quest.id.RiteofPassage, sdk.quest.states.Completed) - }, - Baal: { - complete: false, - condition: () => me.getQuest(sdk.quest.id.EyeofDestruction, sdk.quest.states.Completed) + + getStats: function () { + // Double check in case still got here before being ready + if (!me.gameReady || !me.ingame || !me.area) return ""; + + let textLine = ("MF: ÿc8" + me.getStat(sdk.stats.MagicBonus) + "ÿc4 FHR: ÿc8" + (me.FHR) + "ÿc4 FBR: ÿc8" + (me.FBR) + "ÿc4 FCR: ÿc8" + (me.FCR) + + "ÿc4 IAS: ÿc8" + (me.IAS)); + + return textLine; }, - }, - - questStatus: function (q = "") { - if (typeof this.data[q] === "undefined") return "undefined"; - if (this.data[q].complete) return "ÿc2Complete"; - let status = this.data[q].condition(); - if (!!status) { - this.data[q].complete = true; - return "ÿc2Complete"; - } - return "ÿc1Incomplete"; - }, - getRes: function () { - // Double check in case still got here before being ready - if (!me.gameReady || !me.ingame || !me.area) return ""; - return ("FR: ÿc1" + me.FR + "ÿc4 CR: ÿc3" + me.CR + "ÿc4 LR: ÿc9" + me.LR + "ÿc4 PR: ÿc2" + me.PR + "ÿc4 CurrentBuild: ÿc0" + Overlay.build); - }, + /** + * @param {string} name + * @returns {void} + */ + addQuest: function (name) { + const quest = _quests.get(name); + if (!quest) return; - getStats: function () { - // Double check in case still got here before being ready - if (!me.gameReady || !me.ingame || !me.area) return ""; + _qHooks.push(quest.hook()); + }, - let textLine = ("MF: ÿc8" + me.getStat(sdk.stats.MagicBonus) + "ÿc4 FHR: ÿc8" + (me.FHR) + "ÿc4 FBR: ÿc8" + (me.FBR) + "ÿc4 FCR: ÿc8" + (me.FCR) - + "ÿc4 IAS: ÿc8" + (me.IAS)); + /** + * @param {string} name + * @returns {void} + */ + updateQuest: function (name) { + const quest = _quests.get(name); + if (!quest) return; + + const hook = this.getHook(name); + if (!hook) { + this.addQuest(name); + } else { + hook.hook.text = quest.name + ": " + quest.status(); + } + }, - return textLine; - }, + check: function () { + if (!this.enabled || !me.gameReady || !me.ingame || !me.area || me.dead) { + this.flush(); - check: function () { - if (!this.enabled || !me.gameReady || !me.ingame || !me.area || me.dead) { - this.flush(); + return; + } - return; - } + !this.getHook("resistances", this.hooks) ? this.add("resistances") : this.getHook("resistances", this.hooks).hook.text = this.getRes(); + !this.getHook("stats", this.hooks) ? this.add("stats") : this.getHook("stats", this.hooks).hook.text = this.getStats(); + !this.getHook("questheader") && this.add("questheader"); - !this.getHook("resistances", this.hooks) ? this.add("resistances") : this.getHook("resistances", this.hooks).hook.text = this.getRes(); - !this.getHook("stats", this.hooks) ? this.add("stats") : this.getHook("stats", this.hooks).hook.text = this.getStats(); - !this.getHook("questheader") && this.add("questheader"); - - switch (me.act) { - case 1: - !this.getHook("Den") ? this.add("Den") : this.getHook("Den").hook.text = "Den: " + this.questStatus("Den"); - !this.getHook("BloodRaven") ? this.add("BloodRaven") : this.getHook("BloodRaven").hook.text = "Blood Raven: " + this.questStatus("BloodRaven"); - !this.getHook("Tristram") ? this.add("Tristram") : this.getHook("Tristram").hook.text = "Tristram: " + this.questStatus("Tristram"); - !this.getHook("Countess") ? this.add("Countess") : this.getHook("Countess").hook.text = "Countess: " + this.questStatus("Countess"); - !this.getHook("Smith") ? this.add("Smith") : this.getHook("Smith").hook.text = "Smith: " + this.questStatus("Smith"); - !this.getHook("Andariel") ? this.add("Andariel") : this.getHook("Andariel").hook.text = "Andariel: " + this.questStatus("Andariel"); - - break; - case 2: - !this.getHook("Radament") ? this.add("Radament") : this.getHook("Radament").hook.text = "Radament: " + this.questStatus("Radament"); - //!this.getHook("Cube") ? this.add("Cube") : this.getHook("Cube").hook.text = "Horadric Cube: " + this.questStatus("Cube"); - !this.getHook("HoradricStaff") ? this.add("HoradricStaff") : this.getHook("HoradricStaff").hook.text = "Horadric Staff: " + this.questStatus("HoradricStaff"); - !this.getHook("Amulet") ? this.add("Amulet") : this.getHook("Amulet").hook.text = "Amulet: " + this.questStatus("Amulet"); - !this.getHook("Summoner") ? this.add("Summoner") : this.getHook("Summoner").hook.text = "Summoner: " + this.questStatus("Summoner"); - !this.getHook("Duriel") ? this.add("Duriel") : this.getHook("Duriel").hook.text = "Duriel: " + this.questStatus("Duriel"); - - break; - case 3: - !this.getHook("GoldenBird") ? this.add("GoldenBird") : this.getHook("GoldenBird").hook.text = "Golden Bird: " + this.questStatus("GoldenBird"); - !this.getHook("KhalimsWill") ? this.add("KhalimsWill") : this.getHook("KhalimsWill").hook.text = "Khalim's Will: " + this.questStatus("KhalimsWill"); - !this.getHook("LamEsen") ? this.add("LamEsen") : this.getHook("LamEsen").hook.text = "LamEsen: " + this.questStatus("LamEsen"); - !this.getHook("Travincal") ? this.add("Travincal") : this.getHook("Travincal").hook.text = "Travincal: " + this.questStatus("Travincal"); - !this.getHook("Mephisto") ? this.add("Mephisto") : this.getHook("Mephisto").hook.text = "Mephisto: " + this.questStatus("Mephisto"); - - break; - case 4: - !this.getHook("Izual") ? this.add("Izual") : this.getHook("Izual").hook.text = "Izual: " + this.questStatus("Izual"); - !this.getHook("HellForge") ? this.add("HellForge") : this.getHook("HellForge").hook.text = "HellForge: " + this.questStatus("HellForge"); - !this.getHook("Diablo") ? this.add("Diablo") : this.getHook("Diablo").hook.text = "Diablo: " + this.questStatus("Diablo"); - - break; - case 5: - !this.getHook("Shenk") ? this.add("Shenk") : this.getHook("Shenk").hook.text = "Shenk: " + this.questStatus("Shenk"); - !this.getHook("Barbies") ? this.add("Barbies") : this.getHook("Barbies").hook.text = "Barbies: " + this.questStatus("Barbies"); - !this.getHook("Anya") ? this.add("Anya") : this.getHook("Anya").hook.text = "Anya: " + this.questStatus("Anya"); - !this.getHook("Ancients") ? this.add("Ancients") : this.getHook("Ancients").hook.text = "Ancients: " + this.questStatus("Ancients"); - !this.getHook("Baal") ? this.add("Baal") : this.getHook("Baal").hook.text = "Baal: " + this.questStatus("Baal"); - - break; - } + _acts.get(me.act).forEach((quest) => this.updateQuest(quest)); - !this.getHook("questbox") && this.add("questbox"); - }, - - add: function (name) { - switch (name) { - case "resistances": - this.hooks.push({ - name: "resistances", - hook: new Text(this.getRes(), Overlay.dashboard.x, Overlay.dashboard.y + Overlay.resfix.y + 45, 4, 13, 0) - }); - - break; - case "stats": - this.hooks.push({ - name: "stats", - hook: new Text(this.getStats(), Overlay.dashboard.x, Overlay.dashboard.y + Overlay.resfix.y + 60, 4, 13, 0) - }); - - break; - case "questbox": - this.qHooks.push({ - name: "questbox", - hook: new Box(Overlay.quest.x - 8, Overlay.quest.y + Overlay.resfix.y - 17, 145, 10 + [0, 105, 90, 90, 60, 90][me.act], 0x0, 4, 0) - }); - - this.qHooks.push({ - name: "questframe", - hook: new Frame(Overlay.quest.x - 8, Overlay.quest.y + Overlay.resfix.y - 17, 145, 10 + [0, 105, 90, 90, 60, 90][me.act], 0) - }); - - this.getHook("questbox").hook.zorder = 0; - - break; - case "questheader": - Overlay.quest.y = Overlay.qYMod[me.act]; - - this.qHooks.push({ - name: "questheader", - hook: new Text("Quests in Act: ÿc0" + me.act, Overlay.quest.x, Overlay.quest.y + Overlay.resfix.y, 4, 0, 0) - }); - - break; - case "Den": - this.qHooks.push({ - name: "Den", - hook: new Text("Den: " + this.questStatus("Den"), Overlay.quest.x, Overlay.quest.y + Overlay.resfix.y + (15 * this.qHooks.length), 4, this.font, 0) - }); - - break; - case "BloodRaven": - this.qHooks.push({ - name: "BloodRaven", - hook: new Text("Blood Raven: " + this.questStatus("BloodRaven"), Overlay.quest.x, Overlay.quest.y + Overlay.resfix.y + (15 * this.qHooks.length), 4, this.font, 0) - }); - - break; - case "Tristram": - this.qHooks.push({ - name: "Tristram", - hook: new Text("Tristram: " + this.questStatus("Tristram"), Overlay.quest.x, Overlay.quest.y + Overlay.resfix.y + (15 * this.qHooks.length), 4, this.font, 0) - }); - - break; - case "Countess": - this.qHooks.push({ - name: "Countess", - hook: new Text("Countess: " + this.questStatus("Countess"), Overlay.quest.x, Overlay.quest.y + Overlay.resfix.y + (15 * this.qHooks.length), 4, this.font, 0) - }); - - break; - case "Smith": - this.qHooks.push({ - name: "Smith", - hook: new Text("Smith: " + this.questStatus("Smith"), Overlay.quest.x, Overlay.quest.y + Overlay.resfix.y + (15 * this.qHooks.length), 4, this.font, 0) - }); - - break; - case "Andariel": - this.qHooks.push({ - name: "Andariel", - hook: new Text("Andariel: " + this.questStatus("Andariel"), Overlay.quest.x, Overlay.quest.y + Overlay.resfix.y + (15 * this.qHooks.length), 4, this.font, 0) - }); - - break; - case "Radament": - this.qHooks.push({ - name: "Radament", - hook: new Text("Radament: " + this.questStatus("Radament"), Overlay.quest.x, Overlay.quest.y + Overlay.resfix.y + (15 * this.qHooks.length), 4, this.font, 0) - }); - - break; - case "HoradricStaff": - this.qHooks.push({ - name: "HoradricStaff", - hook: new Text("Horadric Staff: " + this.questStatus("HoradricStaff"), Overlay.quest.x, Overlay.quest.y + Overlay.resfix.y + (15 * this.qHooks.length), 4, this.font, 0) - }); - - break; - case "Amulet": - this.qHooks.push({ - name: "Amulet", - hook: new Text("Amulet: " + this.questStatus("Amulet"), Overlay.quest.x, Overlay.quest.y + Overlay.resfix.y + (15 * this.qHooks.length), 4, this.font, 0) - }); - - break; - case "Summoner": - this.qHooks.push({ - name: "Summoner", - hook: new Text("Summoner: " + this.questStatus("Summoner"), Overlay.quest.x, Overlay.quest.y + Overlay.resfix.y + (15 * this.qHooks.length), 4, this.font, 0) - }); - - break; - case "Duriel": - this.qHooks.push({ - name: "Duriel", - hook: new Text("Duriel: " + this.questStatus("Duriel"), Overlay.quest.x, Overlay.quest.y + Overlay.resfix.y + (15 * this.qHooks.length), 4, this.font, 0) - }); - - break; - case "GoldenBird": - this.qHooks.push({ - name: "GoldenBird", - hook: new Text("Golden Bird: " + this.questStatus("GoldenBird"), Overlay.quest.x, Overlay.quest.y + Overlay.resfix.y + (15 * this.qHooks.length), 4, this.font, 0) - }); - - break; - case "KhalimsWill": - this.qHooks.push({ - name: "KhalimsWill", - hook: new Text("Khalim's Will: " + this.questStatus("KhalimsWill"), Overlay.quest.x, Overlay.quest.y + Overlay.resfix.y + (15 * this.qHooks.length), 4, this.font, 0) - }); - - break; - case "LamEsen": - this.qHooks.push({ - name: "LamEsen", - hook: new Text("LamEsen: " + this.questStatus("LamEsen"), Overlay.quest.x, Overlay.quest.y + Overlay.resfix.y + (15 * this.qHooks.length), 4, this.font, 0) - }); - - break; - case "Travincal": - this.qHooks.push({ - name: "Travincal", - hook: new Text("Travincal: " + this.questStatus("Travincal"), Overlay.quest.x, Overlay.quest.y + Overlay.resfix.y + (15 * this.qHooks.length), 4, this.font, 0) - }); - - break; - case "Mephisto": - this.qHooks.push({ - name: "Mephisto", - hook: new Text("Mephisto: " + this.questStatus("Mephisto"), Overlay.quest.x, Overlay.quest.y + Overlay.resfix.y + (15 * this.qHooks.length), 4, this.font, 0) - }); - - break; - case "Izual": - this.qHooks.push({ - name: "Izual", - hook: new Text("Izual: " + this.questStatus("Izual"), Overlay.quest.x, Overlay.quest.y + Overlay.resfix.y + (15 * this.qHooks.length), 4, this.font, 0) - }); - - break; - case "HellForge": - this.qHooks.push({ - name: "HellForge", - hook: new Text("HellForge: " + this.questStatus("HellForge"), Overlay.quest.x, Overlay.quest.y + Overlay.resfix.y + (15 * this.qHooks.length), 4, this.font, 0) - }); - - break; - case "Diablo": - this.qHooks.push({ - name: "Diablo", - hook: new Text("Diablo: " + this.questStatus("Diablo"), Overlay.quest.x, Overlay.quest.y + Overlay.resfix.y + (15 * this.qHooks.length), 4, this.font, 0) - }); - - break; - case "Shenk": - this.qHooks.push({ - name: "Shenk", - hook: new Text("Shenk: " + this.questStatus("Shenk"), Overlay.quest.x, Overlay.quest.y + Overlay.resfix.y + (15 * this.qHooks.length), 4, this.font, 0) - }); - - break; - case "Barbies": - this.qHooks.push({ - name: "Barbies", - hook: new Text("Barbies: " + this.questStatus("Barbies"), Overlay.quest.x, Overlay.quest.y + Overlay.resfix.y + (15 * this.qHooks.length), 4, this.font, 0) - }); - - break; - case "Anya": - this.qHooks.push({ - name: "Anya", - hook: new Text("Anya: " + this.questStatus("Anya"), Overlay.quest.x, Overlay.quest.y + Overlay.resfix.y + (15 * this.qHooks.length), 4, this.font, 0) - }); - - break; - case "Ancients": - this.qHooks.push({ - name: "Ancients", - hook: new Text("Ancients: " + this.questStatus("Ancients"), Overlay.quest.x, Overlay.quest.y + Overlay.resfix.y + (15 * this.qHooks.length), 4, this.font, 0) - }); - - break; - case "Baal": - this.qHooks.push({ - name: "Baal", - hook: new Text("Baal: " + this.questStatus("Baal"), Overlay.quest.x, Overlay.quest.y + Overlay.resfix.y + (15 * this.qHooks.length), 4, this.font, 0) - }); - - break; - } - }, + !this.getHook("questbox") && this.add("questbox"); + }, - getHook: function (name, hooks) { - while (!me.gameReady || !me.ingame || !me.area) { - delay(500); - } + /** + * @param {string} name + */ + add: function (name) { + switch (name) { + case "resistances": + this.hooks.push({ + name: "resistances", + hook: new Text(this.getRes(), Overlay.dashboard.x, Overlay.dashboard.y + Overlay.resfix.y + 45, 4, 13, 0) + }); + + break; + case "stats": + this.hooks.push({ + name: "stats", + hook: new Text(this.getStats(), Overlay.dashboard.x, Overlay.dashboard.y + Overlay.resfix.y + 60, 4, 13, 0) + }); + + break; + case "questbox": + _qHooks.push({ + name: "questbox", + hook: new Box(Overlay.quest.x - 8, Overlay.quest.y + Overlay.resfix.y - 17, 145, 10 + [0, 105, 90, 90, 60, 90][me.act], 0x0, 4, 0) + }); + + _qHooks.push({ + name: "questframe", + hook: new Frame(Overlay.quest.x - 8, Overlay.quest.y + Overlay.resfix.y - 17, 145, 10 + [0, 105, 90, 90, 60, 90][me.act], 0) + }); + + this.getHook("questbox").hook.zorder = 0; + + break; + case "questheader": + Overlay.quest.y = Overlay.qYMod[me.act]; + + _qHooks.push({ + name: "questheader", + hook: new Text("Quests in Act: ÿc0" + me.act, Overlay.quest.x, Overlay.quest.y + Overlay.resfix.y, 4, 0, 0) + }); + + break; + } + }, + + getHook: function (name, hooks) { + while (!me.gameReady || !me.ingame || !me.area) { + delay(500); + } - hooks === undefined && (hooks = this.qHooks); + hooks === undefined && (hooks = _qHooks); - for (let i = 0; i < hooks.length; i += 1) { - if (hooks[i].name === name) return hooks[i]; - } + for (let i = 0; i < hooks.length; i += 1) { + if (hooks[i].name === name) return hooks[i]; + } - return false; - }, + return false; + }, - flush: function () { - while (this.hooks.length) { - this.hooks.shift().hook.remove(); - } + flush: function () { + while (this.hooks.length) { + this.hooks.shift().hook.remove(); + } - while (this.qHooks.length) { - this.qHooks.shift().hook.remove(); + while (_qHooks.length) { + _qHooks.shift().hook.remove(); + } + return true; } - return true; - } - }, + }; + })(), timeOut: 0, From b84db68817d7555acdf5ec6a57ba0ead58020e9a Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Sun, 28 May 2023 01:28:34 -0400 Subject: [PATCH 120/263] Update D2BotSoloPlay.dbj - remove debug statement --- D2BotSoloPlay.dbj | 1 - 1 file changed, 1 deletion(-) diff --git a/D2BotSoloPlay.dbj b/D2BotSoloPlay.dbj index cfa61fa2..08faa142 100644 --- a/D2BotSoloPlay.dbj +++ b/D2BotSoloPlay.dbj @@ -125,7 +125,6 @@ function main () { try { Tracker.update(); - console.log("Gametime Updated"); } catch (e) { console.error(e); } From 0ee7915515088b352e0ba10086757ca30bfbfee4 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Mon, 29 May 2023 12:51:37 -0400 Subject: [PATCH 121/263] Update NecromancerAttacks.js - formatting --- .../NecromancerAttacks.js | 832 +++++++++--------- 1 file changed, 416 insertions(+), 416 deletions(-) diff --git a/libs/SoloPlay/Functions/ClassAttackOverrides/NecromancerAttacks.js b/libs/SoloPlay/Functions/ClassAttackOverrides/NecromancerAttacks.js index eeaa227f..3615cfd2 100644 --- a/libs/SoloPlay/Functions/ClassAttackOverrides/NecromancerAttacks.js +++ b/libs/SoloPlay/Functions/ClassAttackOverrides/NecromancerAttacks.js @@ -8,87 +8,87 @@ includeIfNotIncluded("core/Attacks/Necromancer.js"); ClassAttack.curseIndex = (function () { - /** - * @constructor - * @param {number} skillId - * @param {number} state - * @param {number} priority - * @param {Function} useIf - */ - function Curse (skillId, state, priority, useIf) { - this.skillId = skillId; - this.state = state; - this.priority = priority; - this.useIf = useIf; - } - - Curse.prototype.canUse = function () { - return Skill.canUse(this.skillId); - }; - - let curseIndex = []; - curseIndex.push(new Curse(sdk.skills.AmplifyDamage, sdk.states.AmplifyDamage, 2, - /** @param {Monster} unit */ - function (unit) { - if (!unit || !Skill.canUse(this.skillId) || unit.getState(sdk.states.Decrepify)) return false; - return !Attack.checkResist(unit, "magic") && !Attack.checkResist(unit, "physical"); - } - )); - curseIndex.push(new Curse(sdk.skills.DimVision, sdk.states.DimVision, 1, - /** @param {Monster} unit */ - function (unit) { - if (!unit || !this.canUse() || unit.isSpecial) return false; - if ([sdk.monsters.OblivionKnight1, sdk.monsters.OblivionKnight2, sdk.monsters.OblivionKnight3].includes(unit.classid)) { - return false; - } - return unit.distance > 15; - } - )); - curseIndex.push(new Curse(sdk.skills.Weaken, sdk.states.Weaken, 3, - /** @param {Monster} unit */ - function (unit) { - return this.canUse() && !unit.getState(sdk.states.Decrepify) && !unit.getState(sdk.states.AmplifyDamage); - } - )); - curseIndex.push(new Curse(sdk.skills.IronMaiden, sdk.states.IronMaiden, 1, - function () { - return this.canUse() && me.inArea(sdk.areas.DurielsLair) && me.normal; - } - )); - curseIndex.push(new Curse(sdk.skills.Terror, sdk.states.Terror, 1, - /** @param {Monster} unit */ - function (unit) { - if (!this.canUse() || !unit || !unit.scareable) return false; - return me.getMobCount(6, Coords_1.Collision.BLOCK_MISSILE | Coords_1.BlockBits.BlockWall | Coords_1.BlockBits.Casting, 0, true) >= 3 - && Skill.getManaCost(sdk.skills.Terror) < me.mp && me.hpPercent < 75; - } - )); - curseIndex.push(new Curse(sdk.skills.Confuse, sdk.states.Confuse, 2, - /** @param {Monster} unit */ - function (unit) { - return this.canUse() && unit.scareable && unit.distance > 8; - } - )); - curseIndex.push(new Curse(sdk.skills.Attract, sdk.states.Attract, 1, - /** @param {Monster} unit */ - function (unit) { - return this.canUse() && me.inArea(sdk.areas.ThroneofDestruction) && unit.distance > 8 && unit.scareable; - } - )); - curseIndex.push(new Curse(sdk.skills.Decrepify, sdk.states.Decrepify, 1, - function () { - return this.canUse(); - } - )); - curseIndex.push(new Curse(sdk.skills.LowerResist, sdk.states.LowerResist, 1, - /** @param {Monster} unit */ - function (unit) { - if (SetUp.currentBuild !== "Poison") return false; - return this.canUse() && Attack.checkResist(unit, "poison"); - } - )); - - return curseIndex; + /** + * @constructor + * @param {number} skillId + * @param {number} state + * @param {number} priority + * @param {Function} useIf + */ + function Curse (skillId, state, priority, useIf) { + this.skillId = skillId; + this.state = state; + this.priority = priority; + this.useIf = useIf; + } + + Curse.prototype.canUse = function () { + return Skill.canUse(this.skillId); + }; + + let curseIndex = []; + curseIndex.push(new Curse(sdk.skills.AmplifyDamage, sdk.states.AmplifyDamage, 2, + /** @param {Monster} unit */ + function (unit) { + if (!unit || !Skill.canUse(this.skillId) || unit.getState(sdk.states.Decrepify)) return false; + return !Attack.checkResist(unit, "magic") && !Attack.checkResist(unit, "physical"); + } + )); + curseIndex.push(new Curse(sdk.skills.DimVision, sdk.states.DimVision, 1, + /** @param {Monster} unit */ + function (unit) { + if (!unit || !this.canUse() || unit.isSpecial) return false; + if ([sdk.monsters.OblivionKnight1, sdk.monsters.OblivionKnight2, sdk.monsters.OblivionKnight3].includes(unit.classid)) { + return false; + } + return unit.distance > 15; + } + )); + curseIndex.push(new Curse(sdk.skills.Weaken, sdk.states.Weaken, 3, + /** @param {Monster} unit */ + function (unit) { + return this.canUse() && !unit.getState(sdk.states.Decrepify) && !unit.getState(sdk.states.AmplifyDamage); + } + )); + curseIndex.push(new Curse(sdk.skills.IronMaiden, sdk.states.IronMaiden, 1, + function () { + return this.canUse() && me.inArea(sdk.areas.DurielsLair) && me.normal; + } + )); + curseIndex.push(new Curse(sdk.skills.Terror, sdk.states.Terror, 1, + /** @param {Monster} unit */ + function (unit) { + if (!this.canUse() || !unit || !unit.scareable) return false; + return me.getMobCount(6, Coords_1.Collision.BLOCK_MISSILE | Coords_1.BlockBits.BlockWall | Coords_1.BlockBits.Casting, 0, true) >= 3 + && Skill.getManaCost(sdk.skills.Terror) < me.mp && me.hpPercent < 75; + } + )); + curseIndex.push(new Curse(sdk.skills.Confuse, sdk.states.Confuse, 2, + /** @param {Monster} unit */ + function (unit) { + return this.canUse() && unit.scareable && unit.distance > 8; + } + )); + curseIndex.push(new Curse(sdk.skills.Attract, sdk.states.Attract, 1, + /** @param {Monster} unit */ + function (unit) { + return this.canUse() && me.inArea(sdk.areas.ThroneofDestruction) && unit.distance > 8 && unit.scareable; + } + )); + curseIndex.push(new Curse(sdk.skills.Decrepify, sdk.states.Decrepify, 1, + function () { + return this.canUse(); + } + )); + curseIndex.push(new Curse(sdk.skills.LowerResist, sdk.states.LowerResist, 1, + /** @param {Monster} unit */ + function (unit) { + if (SetUp.currentBuild !== "Poison") return false; + return this.canUse() && Attack.checkResist(unit, "poison"); + } + )); + + return curseIndex; })(); /** @@ -96,24 +96,24 @@ ClassAttack.curseIndex = (function () { * @returns {boolean} */ ClassAttack.smartCurse = function (unit) { - if (unit === undefined || unit.dead || !unit.curseable) return false; - - let choosenCurse = (this.curseIndex - .filter((curse) => curse !== undefined && curse.useIf(unit)) - .sort((a, b) => a.priority - b.priority) - .find((curse) => Skill.getManaCost(curse.skillId) < me.mp) || false); - - if (choosenCurse && !unit.getState(choosenCurse.state)) { - if (!checkCollision(me, unit, sdk.collision.Ranged)) { - me.overhead("Cursing " + unit.name + " with " + getSkillById(choosenCurse.skillId)); - return Skill.cast(choosenCurse.skillId, sdk.skills.hand.Right, unit); - } else { - me.overhead(unit.name + " is blocked, skipping attempt to curse"); - this.doCast(unit, Config.AttackSkill[unit.isSpecial ? 1 : 3], Config.AttackSkill[unit.isSpecial ? 2 : 5]); - } - } - - return false; + if (unit === undefined || unit.dead || !unit.curseable) return false; + + let choosenCurse = (this.curseIndex + .filter((curse) => curse !== undefined && curse.useIf(unit)) + .sort((a, b) => a.priority - b.priority) + .find((curse) => Skill.getManaCost(curse.skillId) < me.mp) || false); + + if (choosenCurse && !unit.getState(choosenCurse.state)) { + if (!checkCollision(me, unit, sdk.collision.Ranged)) { + me.overhead("Cursing " + unit.name + " with " + getSkillById(choosenCurse.skillId)); + return Skill.cast(choosenCurse.skillId, sdk.skills.hand.Right, unit); + } else { + me.overhead(unit.name + " is blocked, skipping attempt to curse"); + this.doCast(unit, Config.AttackSkill[unit.isSpecial ? 1 : 3], Config.AttackSkill[unit.isSpecial ? 2 : 5]); + } + } + + return false; }; ClassAttack.bpTick = 0; @@ -126,366 +126,366 @@ ClassAttack.bpTick = 0; // TODO: clean this up ClassAttack.doAttack = function (unit, preattack, once) { - if (!unit) return Attack.Result.SUCCESS; - let gid = unit.gid; - - if (Config.MercWatch && me.needMerc()) { - console.log("mercwatch"); - - if (Town.visitTown()) { - // lost reference to the mob we were attacking - if (!unit || !copyUnit(unit).x || !Game.getMonster(-1, -1, gid) || unit.dead) { - return Attack.Result.SUCCESS; - } - } - } - - let mercRevive = 0; - let gold = me.gold; - const index = (unit.isSpecial || unit.isPlayer) ? 1 : 3; - const useTerror = Skill.canUse(sdk.skills.Terror); - const useBP = Skill.canUse(sdk.skills.BonePrison); - const bpAllowedAreas = [ - sdk.areas.CatacombsLvl4, sdk.areas.Tristram, sdk.areas.MooMooFarm, sdk.areas.RockyWaste, sdk.areas.DryHills, sdk.areas.FarOasis, sdk.areas.LostCity, sdk.areas.ValleyofSnakes, - sdk.areas.DurielsLair, sdk.areas.SpiderForest, sdk.areas.GreatMarsh, sdk.areas.FlayerJungle, sdk.areas.LowerKurast, sdk.areas.KurastBazaar, sdk.areas.UpperKurast, sdk.areas.KurastCauseway, - sdk.areas.DuranceofHateLvl3, sdk.areas.OuterSteppes, sdk.areas.PlainsofDespair, sdk.areas.CityoftheDamned, sdk.areas.ChaosSanctuary, sdk.areas.BloodyFoothills, sdk.areas.FrigidHighlands, - sdk.areas.ArreatSummit, sdk.areas.NihlathaksTemple, sdk.areas.WorldstoneLvl1, sdk.areas.WorldstoneLvl2, sdk.areas.WorldstoneLvl3, sdk.areas.ThroneofDestruction - ]; - - // Bone prison - if (useBP && unit.distance > ([sdk.areas.DurielsLair, sdk.areas.ArreatSummit].includes(me.area) ? 6 : 10) - && bpAllowedAreas.includes(me.area) && (index === 1 || [sdk.monsters.ListerTheTormenter, sdk.monsters.HellBovine].includes(unit.classid)) - && !checkCollision(me, unit, sdk.collision.Ranged) && Skill.getManaCost(sdk.skills.BonePrison) * 2 < me.mp && getTickCount() - this.bpTick > 2000) { - if (Skill.cast(sdk.skills.BonePrison, sdk.skills.hand.Right, unit)) { - this.bpTick = getTickCount(); - } - } - - // write terrorCheck function, need to take into account if monsters are even scareable - if (useTerror && me.getMobCount(6, Coords_1.Collision.BLOCK_MISSILE | Coords_1.BlockBits.BlockWall | Coords_1.BlockBits.Casting, 0, true) >= 3 - && Skill.getManaCost(sdk.skills.Terror) < me.mp && me.hpPercent < 75) { - Skill.cast(sdk.skills.Terror, sdk.skills.hand.Right); - } - - this.smartCurse(unit); - - if (me.expansion && index === 1 && !unit.dead) { - if (CharData.skillData.haveChargedSkill(sdk.skills.SlowMissiles) - && unit.getEnchant(sdk.enchant.LightningEnchanted) && !unit.getState(sdk.states.SlowMissiles) - && unit.curseable && (gold > 500000 && !unit.isBoss) && !checkCollision(me, unit, sdk.collision.Ranged)) { - // Cast slow missiles - Attack.castCharges(sdk.skills.SlowMissiles, unit); - } - } - - // maybe this should return an object with basic skill info besides the skillId. e.g timed, mana, range, and hand - const skills = Attack.decideSkill(unit); - - const switchBowAttack = (unit) => { - if (Attack.getIntoPosition(unit, 20, sdk.collision.Ranged)) { - try { - const checkForShamans = unit.isFallen && !me.inArea(sdk.areas.BloodMoor); - for (let i = 0; i < 5 && unit.attackable; i++) { - if (checkForShamans && !once) { - // before we waste time let's see if there is a shaman we should kill - const shaman = getUnits(sdk.unittype.Monster) - .filter(mon => mon.distance < 20 && mon.isShaman && mon.attackable) - .sort((a, b) => a.distance - b.distance).first(); - if (shaman) return ClassAttack.doAttack(shaman, null, true); - } - if (!Attack.useBowOnSwitch(unit, sdk.skills.Attack, i === 5)) return Attack.Result.FAILED; - if (unit.distance < 8 || me.inDanger()) { - if (once) return Attack.Result.FAILED; - let closeMob = getUnits(sdk.unittype.Monster) - .filter(mon => mon.distance < 10 && mon.attackable && mon.gid !== gid) - .sort(Attack.walkingSortMonsters).first(); - if (closeMob) return ClassAttack.doAttack(closeMob, null, true); - } - } - } finally { - me.weaponswitch !== sdk.player.slot.Main && me.switchWeapons(sdk.player.slot.Main); - } - } - return unit.dead ? Attack.Result.SUCCESS : Attack.Result.FAILED; - }; - - if (CharData.skillData.bow.onSwitch - && (index !== 1 || !unit.name.includes(getLocaleString(sdk.locale.text.Ghostly))) - && ([-1, sdk.skills.Attack].includes(skills.timed) - || Skill.getManaCost(skills.timed) > me.mp - || (Skill.getManaCost(skills.timed) * 3 > me.mp && [sdk.skills.Teeth].includes(skills.timed)))) { - if (switchBowAttack(unit) === Attack.Result.SUCCESS) return Attack.Result.SUCCESS; - } - - if (me.normal && gold < 5000 && (skills.timed === -1 || Skill.getManaCost(skills.timed) > me.mp)) { - if (skills.timed !== sdk.skills.Teeth && Skill.canUse(sdk.skills.Teeth) && Skill.getManaCost(sdk.skills.Teeth) < me.mp) { - skills.timed = sdk.skills.Teeth; - } else if (Skill.canUse(sdk.skills.PoisonDagger) && Skill.getManaCost(sdk.skills.PoisonDagger) < me.mp) { - skills.timed = sdk.skills.PoisonDagger; - } else if (me.getMobCount(6, Coords_1.Collision.BLOCK_MISSILE | Coords_1.BlockBits.BlockWall) >= 1) { - // I have no mana and there are mobs around me, just attack - skills.timed = sdk.skills.Attack; - } - } - const result = this.doCast(unit, skills.timed, skills.untimed); - - if (result === Attack.Result.SUCCESS) { - Config.ActiveSummon && this.raiseArmy(); - this.explodeCorpses(unit); - } else if (result === Attack.Result.CANTATTACK && Attack.canTeleStomp(unit)) { - let merc = me.getMerc(); - - while (unit.attackable) { - if (!unit) return Attack.Result.SUCCESS; - - if (me.needMerc()) { - if (Config.MercWatch && mercRevive++ < 1) { - Town.visitTown(); - } else { - return Attack.Result.CANTATTACK; - } - - (merc === undefined || !merc) && (merc = me.getMerc()); - } - - if (!!merc && getDistance(merc, unit) > 5) { - Pather.moveToUnit(unit); - - let spot = Attack.findSafeSpot(unit, 10, 5, 9); - !!spot && Pather.walkTo(spot.x, spot.y); - } - - Config.ActiveSummon && this.raiseArmy(); - this.explodeCorpses(unit); - this.smartCurse(unit); - let closeMob = Attack.getNearestMonster({ skipGid: gid }); - if (!!closeMob) { - let findSkill = Attack.decideSkill(closeMob); - (this.doCast(closeMob, findSkill.timed, findSkill.untimed) === Attack.Result.SUCCESS) - || (this.canCurse(unit, sdk.skills.Terror) && Skill.cast(sdk.skills.Terror, sdk.skills.hand.Right, unit)); - } - } - - return Attack.Result.SUCCESS; - } - - return result; + if (!unit) return Attack.Result.SUCCESS; + let gid = unit.gid; + + if (Config.MercWatch && me.needMerc()) { + console.log("mercwatch"); + + if (Town.visitTown()) { + // lost reference to the mob we were attacking + if (!unit || !copyUnit(unit).x || !Game.getMonster(-1, -1, gid) || unit.dead) { + return Attack.Result.SUCCESS; + } + } + } + + let mercRevive = 0; + let gold = me.gold; + const index = (unit.isSpecial || unit.isPlayer) ? 1 : 3; + const useTerror = Skill.canUse(sdk.skills.Terror); + const useBP = Skill.canUse(sdk.skills.BonePrison); + const bpAllowedAreas = [ + sdk.areas.CatacombsLvl4, sdk.areas.Tristram, sdk.areas.MooMooFarm, sdk.areas.RockyWaste, sdk.areas.DryHills, sdk.areas.FarOasis, sdk.areas.LostCity, sdk.areas.ValleyofSnakes, + sdk.areas.DurielsLair, sdk.areas.SpiderForest, sdk.areas.GreatMarsh, sdk.areas.FlayerJungle, sdk.areas.LowerKurast, sdk.areas.KurastBazaar, sdk.areas.UpperKurast, sdk.areas.KurastCauseway, + sdk.areas.DuranceofHateLvl3, sdk.areas.OuterSteppes, sdk.areas.PlainsofDespair, sdk.areas.CityoftheDamned, sdk.areas.ChaosSanctuary, sdk.areas.BloodyFoothills, sdk.areas.FrigidHighlands, + sdk.areas.ArreatSummit, sdk.areas.NihlathaksTemple, sdk.areas.WorldstoneLvl1, sdk.areas.WorldstoneLvl2, sdk.areas.WorldstoneLvl3, sdk.areas.ThroneofDestruction + ]; + + // Bone prison + if (useBP && unit.distance > ([sdk.areas.DurielsLair, sdk.areas.ArreatSummit].includes(me.area) ? 6 : 10) + && bpAllowedAreas.includes(me.area) && (index === 1 || [sdk.monsters.ListerTheTormenter, sdk.monsters.HellBovine].includes(unit.classid)) + && !checkCollision(me, unit, sdk.collision.Ranged) && Skill.getManaCost(sdk.skills.BonePrison) * 2 < me.mp && getTickCount() - this.bpTick > 2000) { + if (Skill.cast(sdk.skills.BonePrison, sdk.skills.hand.Right, unit)) { + this.bpTick = getTickCount(); + } + } + + // write terrorCheck function, need to take into account if monsters are even scareable + if (useTerror && me.getMobCount(6, Coords_1.Collision.BLOCK_MISSILE | Coords_1.BlockBits.BlockWall | Coords_1.BlockBits.Casting, 0, true) >= 3 + && Skill.getManaCost(sdk.skills.Terror) < me.mp && me.hpPercent < 75) { + Skill.cast(sdk.skills.Terror, sdk.skills.hand.Right); + } + + this.smartCurse(unit); + + if (me.expansion && index === 1 && !unit.dead) { + if (CharData.skillData.haveChargedSkill(sdk.skills.SlowMissiles) + && unit.getEnchant(sdk.enchant.LightningEnchanted) && !unit.getState(sdk.states.SlowMissiles) + && unit.curseable && (gold > 500000 && !unit.isBoss) && !checkCollision(me, unit, sdk.collision.Ranged)) { + // Cast slow missiles + Attack.castCharges(sdk.skills.SlowMissiles, unit); + } + } + + // maybe this should return an object with basic skill info besides the skillId. e.g timed, mana, range, and hand + const skills = Attack.decideSkill(unit); + + const switchBowAttack = (unit) => { + if (Attack.getIntoPosition(unit, 20, sdk.collision.Ranged)) { + try { + const checkForShamans = unit.isFallen && !me.inArea(sdk.areas.BloodMoor); + for (let i = 0; i < 5 && unit.attackable; i++) { + if (checkForShamans && !once) { + // before we waste time let's see if there is a shaman we should kill + const shaman = getUnits(sdk.unittype.Monster) + .filter(mon => mon.distance < 20 && mon.isShaman && mon.attackable) + .sort((a, b) => a.distance - b.distance).first(); + if (shaman) return ClassAttack.doAttack(shaman, null, true); + } + if (!Attack.useBowOnSwitch(unit, sdk.skills.Attack, i === 5)) return Attack.Result.FAILED; + if (unit.distance < 8 || me.inDanger()) { + if (once) return Attack.Result.FAILED; + let closeMob = getUnits(sdk.unittype.Monster) + .filter(mon => mon.distance < 10 && mon.attackable && mon.gid !== gid) + .sort(Attack.walkingSortMonsters).first(); + if (closeMob) return ClassAttack.doAttack(closeMob, null, true); + } + } + } finally { + me.weaponswitch !== sdk.player.slot.Main && me.switchWeapons(sdk.player.slot.Main); + } + } + return unit.dead ? Attack.Result.SUCCESS : Attack.Result.FAILED; + }; + + if (CharData.skillData.bow.onSwitch + && (index !== 1 || !unit.name.includes(getLocaleString(sdk.locale.text.Ghostly))) + && ([-1, sdk.skills.Attack].includes(skills.timed) + || Skill.getManaCost(skills.timed) > me.mp + || (Skill.getManaCost(skills.timed) * 3 > me.mp && [sdk.skills.Teeth].includes(skills.timed)))) { + if (switchBowAttack(unit) === Attack.Result.SUCCESS) return Attack.Result.SUCCESS; + } + + if (me.normal && gold < 5000 && (skills.timed === -1 || Skill.getManaCost(skills.timed) > me.mp)) { + if (skills.timed !== sdk.skills.Teeth && Skill.canUse(sdk.skills.Teeth) && Skill.getManaCost(sdk.skills.Teeth) < me.mp) { + skills.timed = sdk.skills.Teeth; + } else if (Skill.canUse(sdk.skills.PoisonDagger) && Skill.getManaCost(sdk.skills.PoisonDagger) < me.mp) { + skills.timed = sdk.skills.PoisonDagger; + } else if (me.getMobCount(6, Coords_1.Collision.BLOCK_MISSILE | Coords_1.BlockBits.BlockWall) >= 1) { + // I have no mana and there are mobs around me, just attack + skills.timed = sdk.skills.Attack; + } + } + const result = this.doCast(unit, skills.timed, skills.untimed); + + if (result === Attack.Result.SUCCESS) { + Config.ActiveSummon && this.raiseArmy(); + this.explodeCorpses(unit); + } else if (result === Attack.Result.CANTATTACK && Attack.canTeleStomp(unit)) { + let merc = me.getMerc(); + + while (unit.attackable) { + if (!unit) return Attack.Result.SUCCESS; + + if (me.needMerc()) { + if (Config.MercWatch && mercRevive++ < 1) { + Town.visitTown(); + } else { + return Attack.Result.CANTATTACK; + } + + (merc === undefined || !merc) && (merc = me.getMerc()); + } + + if (!!merc && getDistance(merc, unit) > 5) { + Pather.moveToUnit(unit); + + let spot = Attack.findSafeSpot(unit, 10, 5, 9); + !!spot && Pather.walkTo(spot.x, spot.y); + } + + Config.ActiveSummon && this.raiseArmy(); + this.explodeCorpses(unit); + this.smartCurse(unit); + let closeMob = Attack.getNearestMonster({ skipGid: gid }); + if (!!closeMob) { + let findSkill = Attack.decideSkill(closeMob); + (this.doCast(closeMob, findSkill.timed, findSkill.untimed) === Attack.Result.SUCCESS) + || (this.canCurse(unit, sdk.skills.Terror) && Skill.cast(sdk.skills.Terror, sdk.skills.hand.Right, unit)); + } + } + + return Attack.Result.SUCCESS; + } + + return result; }; // Returns: 0 - fail, 1 - success, 2 - no valid attack skills ClassAttack.doCast = function (unit, timedSkill, untimedSkill) { - // No valid skills can be found - if (timedSkill < 0 && untimedSkill < 0) return Attack.Result.CANTATTACK; + // No valid skills can be found + if (timedSkill < 0 && untimedSkill < 0) return Attack.Result.CANTATTACK; - // Check for bodies to exploit for CorpseExplosion before committing to an attack for non-summoner type necros - if (Config.Skeletons + Config.SkeletonMages + Config.Revives === 0) { - this.checkCorpseNearMonster(unit) && this.explodeCorpses(unit); - } + // Check for bodies to exploit for CorpseExplosion before committing to an attack for non-summoner type necros + if (Config.Skeletons + Config.SkeletonMages + Config.Revives === 0) { + this.checkCorpseNearMonster(unit) && this.explodeCorpses(unit); + } - let lowMana = true; - let walk, timedSkillRange, untimedSkillRange; + let lowMana = true; + let walk, timedSkillRange, untimedSkillRange; - if (timedSkill > -1 && (!me.getState(sdk.states.SkillDelay) || !Skill.isTimed(timedSkill)) && me.mp > Skill.getManaCost(timedSkill)) { - lowMana = false; - timedSkillRange = Skill.getRange(timedSkill); + if (timedSkill > -1 && (!me.getState(sdk.states.SkillDelay) || !Skill.isTimed(timedSkill)) && me.mp > Skill.getManaCost(timedSkill)) { + lowMana = false; + timedSkillRange = Skill.getRange(timedSkill); - switch (timedSkill) { - case sdk.skills.PoisonNova: - if (!this.novaTick || getTickCount() - this.novaTick > Config.PoisonNovaDelay * 1000) { - if (unit.distance > timedSkillRange || checkCollision(me, unit, sdk.collision.Ranged)) { - if (!Attack.getIntoPosition(unit, timedSkillRange, sdk.collision.Ranged)) { - return Attack.Result.FAILED; - } - } + switch (timedSkill) { + case sdk.skills.PoisonNova: + if (!this.novaTick || getTickCount() - this.novaTick > Config.PoisonNovaDelay * 1000) { + if (unit.distance > timedSkillRange || checkCollision(me, unit, sdk.collision.Ranged)) { + if (!Attack.getIntoPosition(unit, timedSkillRange, sdk.collision.Ranged)) { + return Attack.Result.FAILED; + } + } - if (!unit.dead && Skill.cast(timedSkill, Skill.getHand(timedSkill), unit)) { - this.novaTick = getTickCount(); - } - } + if (!unit.dead && Skill.cast(timedSkill, Skill.getHand(timedSkill), unit)) { + this.novaTick = getTickCount(); + } + } - break; - case sdk.skills.Summoner: // Pure Summoner - if (unit.distance > timedSkillRange || checkCollision(me, unit, sdk.collision.Ranged)) { - if (!Attack.getIntoPosition(unit, timedSkillRange, sdk.collision.Ranged)) { - return Attack.Result.FAILED; - } - } + break; + case sdk.skills.Summoner: // Pure Summoner + if (unit.distance > timedSkillRange || checkCollision(me, unit, sdk.collision.Ranged)) { + if (!Attack.getIntoPosition(unit, timedSkillRange, sdk.collision.Ranged)) { + return Attack.Result.FAILED; + } + } - delay(300); + delay(300); - break; - default: - if (timedSkillRange < 4 && !Attack.validSpot(unit.x, unit.y)) return Attack.Result.FAILED; + break; + default: + if (timedSkillRange < 4 && !Attack.validSpot(unit.x, unit.y)) return Attack.Result.FAILED; - if (timedSkill === sdk.skills.Teeth) { - timedSkillRange = me.getMobCount(6, Coords_1.Collision.BLOCK_MISSILE | Coords_1.BlockBits.BlockWall | Coords_1.BlockBits.Casting) <= 3 ? 6 : timedSkillRange; - } + if (timedSkill === sdk.skills.Teeth) { + timedSkillRange = me.getMobCount(6, Coords_1.Collision.BLOCK_MISSILE | Coords_1.BlockBits.BlockWall | Coords_1.BlockBits.Casting) <= 3 ? 6 : timedSkillRange; + } - if (unit.distance > timedSkillRange || checkCollision(me, unit, sdk.collision.Ranged)) { - // Allow short-distance walking for melee skills - walk = timedSkillRange < 4 && unit.distance < 10 && !checkCollision(me, unit, sdk.collision.BlockWall); + if (unit.distance > timedSkillRange || checkCollision(me, unit, sdk.collision.Ranged)) { + // Allow short-distance walking for melee skills + walk = timedSkillRange < 4 && unit.distance < 10 && !checkCollision(me, unit, sdk.collision.BlockWall); - if (!Attack.getIntoPosition(unit, timedSkillRange, sdk.collision.Ranged, walk)) return Attack.Result.FAILED; - } + if (!Attack.getIntoPosition(unit, timedSkillRange, sdk.collision.Ranged, walk)) return Attack.Result.FAILED; + } - if (!unit.dead) { - // Try to find better spot - if (unit.distance < 4 && timedSkillRange > 6) { - Attack.deploy(unit, 4, 5, 9); - } + if (!unit.dead) { + // Try to find better spot + if (unit.distance < 4 && timedSkillRange > 6) { + Attack.deploy(unit, 4, 5, 9); + } - Skill.cast(timedSkill, Skill.getHand(timedSkill), unit); - } + Skill.cast(timedSkill, Skill.getHand(timedSkill), unit); + } - break; - } - } + break; + } + } - if (untimedSkill > -1 && me.mp > Skill.getManaCost(untimedSkill)) { - lowMana = false; - untimedSkillRange = Skill.getRange(untimedSkill); + if (untimedSkill > -1 && me.mp > Skill.getManaCost(untimedSkill)) { + lowMana = false; + untimedSkillRange = Skill.getRange(untimedSkill); - if (untimedSkillRange < 4 && !Attack.validSpot(unit.x, unit.y)) return Attack.Result.FAILED; + if (untimedSkillRange < 4 && !Attack.validSpot(unit.x, unit.y)) return Attack.Result.FAILED; - if (unit.distance > untimedSkillRange || checkCollision(me, unit, sdk.collision.Ranged)) { - // Allow short-distance walking for melee skills - walk = Skill.getRange(untimedSkill) < 4 && unit.distance < 10 && !checkCollision(me, unit, sdk.collision.BlockWall); + if (unit.distance > untimedSkillRange || checkCollision(me, unit, sdk.collision.Ranged)) { + // Allow short-distance walking for melee skills + walk = Skill.getRange(untimedSkill) < 4 && unit.distance < 10 && !checkCollision(me, unit, sdk.collision.BlockWall); - if (!Attack.getIntoPosition(unit, untimedSkillRange, sdk.collision.Ranged, walk)) { - return Attack.Result.FAILED; - } - } + if (!Attack.getIntoPosition(unit, untimedSkillRange, sdk.collision.Ranged, walk)) { + return Attack.Result.FAILED; + } + } - !unit.dead && Skill.cast(untimedSkill, Skill.getHand(untimedSkill), unit); + !unit.dead && Skill.cast(untimedSkill, Skill.getHand(untimedSkill), unit); - return Attack.Result.SUCCESS; - } + return Attack.Result.SUCCESS; + } - Misc.poll(() => !me.skillDelay, 1000, 40); + Misc.poll(() => !me.skillDelay, 1000, 40); - // Delay for Poison Nova - while (this.novaTick && getTickCount() - this.novaTick < Config.PoisonNovaDelay * 1000) { - delay(40); - } + // Delay for Poison Nova + while (this.novaTick && getTickCount() - this.novaTick < Config.PoisonNovaDelay * 1000) { + delay(40); + } - return lowMana ? Attack.Result.NEEDMANA : Attack.Result.SUCCESS; + return lowMana ? Attack.Result.NEEDMANA : Attack.Result.SUCCESS; }; ClassAttack.farCast = function (unit) { - let timedSkill = Config.AttackSkill[1], untimedSkill = Config.AttackSkill[2]; + let timedSkill = Config.AttackSkill[1], untimedSkill = Config.AttackSkill[2]; - // No valid skills can be found - if (timedSkill < 0 && untimedSkill < 0) return Attack.Result.CANTATTACK; + // No valid skills can be found + if (timedSkill < 0 && untimedSkill < 0) return Attack.Result.CANTATTACK; - // Far to low a range for far casting - if (Skill.getRange(timedSkill) < 4 && Skill.getRange(untimedSkill) < 4) return Attack.Result.CANTATTACK; + // Far to low a range for far casting + if (Skill.getRange(timedSkill) < 4 && Skill.getRange(untimedSkill) < 4) return Attack.Result.CANTATTACK; - // Bone prison - if (unit.distance > 10 && !checkCollision(me, unit, sdk.collision.Ranged) && Skill.getManaCost(sdk.skills.BonePrison) * 2 < me.mp && getTickCount() - this.bpTick > 2000) { - if (Skill.cast(sdk.skills.BonePrison, sdk.skills.hand.Right, unit)) { - this.bpTick = getTickCount(); - } - } + // Bone prison + if (unit.distance > 10 && !checkCollision(me, unit, sdk.collision.Ranged) && Skill.getManaCost(sdk.skills.BonePrison) * 2 < me.mp && getTickCount() - this.bpTick > 2000) { + if (Skill.cast(sdk.skills.BonePrison, sdk.skills.hand.Right, unit)) { + this.bpTick = getTickCount(); + } + } - this.smartCurse(unit); + this.smartCurse(unit); - // Check for bodies to exploit for CorpseExplosion before committing to an attack for non-summoner type necros - if (Config.Skeletons + Config.SkeletonMages + Config.Revives === 0) { - this.checkCorpseNearMonster(unit) && this.explodeCorpses(unit); - } + // Check for bodies to exploit for CorpseExplosion before committing to an attack for non-summoner type necros + if (Config.Skeletons + Config.SkeletonMages + Config.Revives === 0) { + this.checkCorpseNearMonster(unit) && this.explodeCorpses(unit); + } - if (timedSkill > -1 && (!me.getState(sdk.states.SkillDelay) || !Skill.isTimed(timedSkill))) { - switch (timedSkill) { - case sdk.skills.PoisonNova: - case sdk.skills.Summoner: // Pure Summoner - break; - default: - if (!unit.dead && !checkCollision(me, unit, sdk.collision.Ranged)) { - Skill.cast(timedSkill, Skill.getHand(timedSkill), unit); - } + if (timedSkill > -1 && (!me.getState(sdk.states.SkillDelay) || !Skill.isTimed(timedSkill))) { + switch (timedSkill) { + case sdk.skills.PoisonNova: + case sdk.skills.Summoner: // Pure Summoner + break; + default: + if (!unit.dead && !checkCollision(me, unit, sdk.collision.Ranged)) { + Skill.cast(timedSkill, Skill.getHand(timedSkill), unit); + } - break; - } - } + break; + } + } - if (untimedSkill > -1) { - if (Skill.getRange(untimedSkill) < 4 && !Attack.validSpot(unit.x, unit.y)) return Attack.Result.FAILED; + if (untimedSkill > -1) { + if (Skill.getRange(untimedSkill) < 4 && !Attack.validSpot(unit.x, unit.y)) return Attack.Result.FAILED; - if (!unit.dead && !checkCollision(me, unit, sdk.collision.Ranged)) { - Skill.cast(untimedSkill, Skill.getHand(untimedSkill), unit); - } + if (!unit.dead && !checkCollision(me, unit, sdk.collision.Ranged)) { + Skill.cast(untimedSkill, Skill.getHand(untimedSkill), unit); + } - return Attack.Result.SUCCESS; - } + return Attack.Result.SUCCESS; + } - Misc.poll(() => !me.skillDelay, 1000, 40); + Misc.poll(() => !me.skillDelay, 1000, 40); - return Attack.Result.SUCCESS; + return Attack.Result.SUCCESS; }; ClassAttack.explodeCorpses = function (unit) { - if (Config.ExplodeCorpses === 0 || unit.dead) return false; - - let corpseList = []; - let useAmp = Skill.canUse(sdk.skills.AmplifyDamage); - let ampManaCost = Skill.getManaCost(sdk.skills.AmplifyDamage); - let explodeCorpsesManaCost = Skill.getManaCost(Config.ExplodeCorpses); - let range = Math.floor((me.getSkill(Config.ExplodeCorpses, sdk.skills.subindex.SoftPoints) + 7) / 3); - let corpse = Game.getMonster(-1, sdk.monsters.mode.Dead); - - if (corpse) { - do { - if (getDistance(unit, corpse) <= range && this.checkCorpse(corpse)) { - corpseList.push(copyUnit(corpse)); - } - } while (corpse.getNext()); - - // Shuffle the corpseList so if running multiple necrobots they explode separate corpses not the same ones - corpseList.length > 1 && (corpseList = corpseList.shuffle()); - - if (this.isArmyFull()) { - // We don't need corpses as we are not a Summoner Necro, Spam CE till monster dies or we run out of bodies. - do { - corpse = corpseList.shift(); - - if (corpse) { - if (!unit.dead && this.checkCorpse(corpse) && getDistance(corpse, unit) <= range) { - // Added corpse ID so I can see when it blows another monster with the same ClassID and Name - me.overhead("Exploding: " + corpse.classid + " " + corpse.name + " id:" + corpse.gid); - - if (useAmp && !unit.getState(sdk.states.AmplifyDamage) && !unit.getState(sdk.states.Decrepify) && me.mp > (ampManaCost + explodeCorpsesManaCost)) { - Skill.cast(sdk.skills.AmplifyDamage, sdk.skills.hand.Right, unit); - } - - if (Skill.cast(Config.ExplodeCorpses, sdk.skills.hand.Right, corpse)) { - delay(me.ping + 1); - } - } - } - } while (corpseList.length > 0); - } else { - // We are a Summoner Necro, we should conserve corpses, only blow 2 at a time so we can check for needed re-summons. - for (let i = 0; i <= 1; i += 1) { - if (corpseList.length > 0) { - corpse = corpseList.shift(); - - if (corpse) { - me.overhead("Exploding: " + corpse.classid + " " + corpse.name); - - if (useAmp && !unit.getState(sdk.states.AmplifyDamage) && !unit.getState(sdk.states.Decrepify) && me.mp > (ampManaCost + explodeCorpsesManaCost)) { - Skill.cast(sdk.skills.AmplifyDamage, sdk.skills.hand.Right, unit); - } - - if (Skill.cast(Config.ExplodeCorpses, sdk.skills.hand.Right, corpse)) { - delay(200); - } - } - } else { - break; - } - } - } - } - - return true; + if (Config.ExplodeCorpses === 0 || unit.dead) return false; + + let corpseList = []; + let useAmp = Skill.canUse(sdk.skills.AmplifyDamage); + let ampManaCost = Skill.getManaCost(sdk.skills.AmplifyDamage); + let explodeCorpsesManaCost = Skill.getManaCost(Config.ExplodeCorpses); + let range = Math.floor((me.getSkill(Config.ExplodeCorpses, sdk.skills.subindex.SoftPoints) + 7) / 3); + let corpse = Game.getMonster(-1, sdk.monsters.mode.Dead); + + if (corpse) { + do { + if (getDistance(unit, corpse) <= range && this.checkCorpse(corpse)) { + corpseList.push(copyUnit(corpse)); + } + } while (corpse.getNext()); + + // Shuffle the corpseList so if running multiple necrobots they explode separate corpses not the same ones + corpseList.length > 1 && (corpseList = corpseList.shuffle()); + + if (this.isArmyFull()) { + // We don't need corpses as we are not a Summoner Necro, Spam CE till monster dies or we run out of bodies. + do { + corpse = corpseList.shift(); + + if (corpse) { + if (!unit.dead && this.checkCorpse(corpse) && getDistance(corpse, unit) <= range) { + // Added corpse ID so I can see when it blows another monster with the same ClassID and Name + me.overhead("Exploding: " + corpse.classid + " " + corpse.name + " id:" + corpse.gid); + + if (useAmp && !unit.getState(sdk.states.AmplifyDamage) && !unit.getState(sdk.states.Decrepify) && me.mp > (ampManaCost + explodeCorpsesManaCost)) { + Skill.cast(sdk.skills.AmplifyDamage, sdk.skills.hand.Right, unit); + } + + if (Skill.cast(Config.ExplodeCorpses, sdk.skills.hand.Right, corpse)) { + delay(me.ping + 1); + } + } + } + } while (corpseList.length > 0); + } else { + // We are a Summoner Necro, we should conserve corpses, only blow 2 at a time so we can check for needed re-summons. + for (let i = 0; i <= 1; i += 1) { + if (corpseList.length > 0) { + corpse = corpseList.shift(); + + if (corpse) { + me.overhead("Exploding: " + corpse.classid + " " + corpse.name); + + if (useAmp && !unit.getState(sdk.states.AmplifyDamage) && !unit.getState(sdk.states.Decrepify) && me.mp > (ampManaCost + explodeCorpsesManaCost)) { + Skill.cast(sdk.skills.AmplifyDamage, sdk.skills.hand.Right, unit); + } + + if (Skill.cast(Config.ExplodeCorpses, sdk.skills.hand.Right, corpse)) { + delay(200); + } + } + } else { + break; + } + } + } + } + + return true; }; From 00266e98eb18f7ee28bf9fe005524cc65d636685 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Mon, 29 May 2023 12:52:25 -0400 Subject: [PATCH 122/263] Update NecromancerAttacks.js - redo the curse structure again --- .../NecromancerAttacks.js | 828 +++++++++--------- 1 file changed, 436 insertions(+), 392 deletions(-) diff --git a/libs/SoloPlay/Functions/ClassAttackOverrides/NecromancerAttacks.js b/libs/SoloPlay/Functions/ClassAttackOverrides/NecromancerAttacks.js index 3615cfd2..046df2e5 100644 --- a/libs/SoloPlay/Functions/ClassAttackOverrides/NecromancerAttacks.js +++ b/libs/SoloPlay/Functions/ClassAttackOverrides/NecromancerAttacks.js @@ -7,485 +7,529 @@ includeIfNotIncluded("core/Attacks/Necromancer.js"); -ClassAttack.curseIndex = (function () { +(function () { + const curseIndex = (function () { + /** + * @constructor + * @param {number} skillId + * @param {number} state + * @param {number} priority + * @param {function(): boolean} useIf + */ + function Curse (skillId, priority, useIf) { + this.name = getSkillById(skillId); + this.skillId = skillId; + this.state = Skill.getState(skillId); + this.priority = priority; + this.useIf = useIf; + } + + Curse.prototype.have = function () { + return Skill.canUse(this.skillId); + }; + + Curse.prototype.manaCost = function () { + return Skill.getManaCost(this.skillId); + }; + + return [ + new Curse(sdk.skills.AmplifyDamage, 2, + /** @param {Monster} unit */ + function (unit) { + if (!unit || unit.getState(sdk.states.Decrepify)) return false; + return !Attack.checkResist(unit, "magic") && !Attack.checkResist(unit, "physical"); + } + ), + new Curse(sdk.skills.DimVision, 1, + /** @param {Monster} unit */ + function (unit) { + if (!unit || unit.isSpecial) return false; + if ([ + sdk.monsters.OblivionKnight1, + sdk.monsters.OblivionKnight2, + sdk.monsters.OblivionKnight3 + ].includes(unit.classid)) { + return false; + } + return unit.distance > 15; + } + ), + new Curse(sdk.skills.Weaken, 3, + /** @param {Monster} unit */ + function (unit) { + return !unit.getState(sdk.states.Decrepify) + && !unit.getState(sdk.states.AmplifyDamage); + } + ), + new Curse(sdk.skills.IronMaiden, 1, + function () { + return me.inArea(sdk.areas.DurielsLair) && me.normal; + } + ), + new Curse(sdk.skills.Terror, 1, + /** + * @this Curse + * @param {Monster} unit + */ + function (unit) { + if (!unit || !unit.scareable) return false; + let _coll = (sdk.collision.BlockMissile | sdk.collision.BlockWall | sdk.collision.Casting); + if (me.getMobCount(6, _coll, 0, true) < 3) return false; + return this.manaCost() < me.mp && me.hpPercent < 75; + } + ), + new Curse(sdk.skills.Confuse, 2, + /** @param {Monster} unit */ + function (unit) { + return unit.scareable && unit.distance > 8; + } + ), + new Curse(sdk.skills.Attract, 1, + /** @param {Monster} unit */ + function (unit) { + return me.inArea(sdk.areas.ThroneofDestruction) + && unit.distance > 8 && unit.scareable; + } + ), + new Curse(sdk.skills.Decrepify, 1, + function () { + return true; + } + ), + new Curse(sdk.skills.LowerResist, 1, + /** @param {Monster} unit */ + function (unit) { + if (SetUp.currentBuild !== "Poison") return false; + return Attack.checkResist(unit, "poison"); + } + ) + ]; + })(); + /** - * @constructor - * @param {number} skillId - * @param {number} state - * @param {number} priority - * @param {Function} useIf + * @param {Monster} unit + * @returns {boolean} */ - function Curse (skillId, state, priority, useIf) { - this.skillId = skillId; - this.state = state; - this.priority = priority; - this.useIf = useIf; - } - - Curse.prototype.canUse = function () { - return Skill.canUse(this.skillId); - }; - - let curseIndex = []; - curseIndex.push(new Curse(sdk.skills.AmplifyDamage, sdk.states.AmplifyDamage, 2, - /** @param {Monster} unit */ - function (unit) { - if (!unit || !Skill.canUse(this.skillId) || unit.getState(sdk.states.Decrepify)) return false; - return !Attack.checkResist(unit, "magic") && !Attack.checkResist(unit, "physical"); - } - )); - curseIndex.push(new Curse(sdk.skills.DimVision, sdk.states.DimVision, 1, - /** @param {Monster} unit */ - function (unit) { - if (!unit || !this.canUse() || unit.isSpecial) return false; - if ([sdk.monsters.OblivionKnight1, sdk.monsters.OblivionKnight2, sdk.monsters.OblivionKnight3].includes(unit.classid)) { - return false; + const doCurse = function (unit) { + if (unit === undefined || unit.dead || !unit.curseable) return false; + + let curse = (curseIndex + .filter(c => c.have() && c.useIf(unit)) + .sort((a, b) => a.priority - b.priority) + .find(c => c.manaCost() < me.mp) || false); + + if (curse && !unit.getState(curse.state)) { + if (!checkCollision(me, unit, sdk.collision.Ranged)) { + me.overhead("Cursing " + unit.name + " with " + curse.name); + return Skill.cast(curse.skillId, sdk.skills.hand.Right, unit); + } else { + me.overhead(unit.name + " is blocked, skipping attempt to curse"); + let [timed, untimed] = unit.isSpecial ? [1, 2] : [3, 5]; + ClassAttack.doCast(unit, timed, untimed); } - return unit.distance > 15; - } - )); - curseIndex.push(new Curse(sdk.skills.Weaken, sdk.states.Weaken, 3, - /** @param {Monster} unit */ - function (unit) { - return this.canUse() && !unit.getState(sdk.states.Decrepify) && !unit.getState(sdk.states.AmplifyDamage); - } - )); - curseIndex.push(new Curse(sdk.skills.IronMaiden, sdk.states.IronMaiden, 1, - function () { - return this.canUse() && me.inArea(sdk.areas.DurielsLair) && me.normal; - } - )); - curseIndex.push(new Curse(sdk.skills.Terror, sdk.states.Terror, 1, - /** @param {Monster} unit */ - function (unit) { - if (!this.canUse() || !unit || !unit.scareable) return false; - return me.getMobCount(6, Coords_1.Collision.BLOCK_MISSILE | Coords_1.BlockBits.BlockWall | Coords_1.BlockBits.Casting, 0, true) >= 3 - && Skill.getManaCost(sdk.skills.Terror) < me.mp && me.hpPercent < 75; - } - )); - curseIndex.push(new Curse(sdk.skills.Confuse, sdk.states.Confuse, 2, - /** @param {Monster} unit */ - function (unit) { - return this.canUse() && unit.scareable && unit.distance > 8; } - )); - curseIndex.push(new Curse(sdk.skills.Attract, sdk.states.Attract, 1, - /** @param {Monster} unit */ - function (unit) { - return this.canUse() && me.inArea(sdk.areas.ThroneofDestruction) && unit.distance > 8 && unit.scareable; - } - )); - curseIndex.push(new Curse(sdk.skills.Decrepify, sdk.states.Decrepify, 1, - function () { - return this.canUse(); - } - )); - curseIndex.push(new Curse(sdk.skills.LowerResist, sdk.states.LowerResist, 1, - /** @param {Monster} unit */ - function (unit) { - if (SetUp.currentBuild !== "Poison") return false; - return this.canUse() && Attack.checkResist(unit, "poison"); - } - )); - return curseIndex; -})(); + return false; + }; -/** - * @param {Monster} unit - * @returns {boolean} - */ -ClassAttack.smartCurse = function (unit) { - if (unit === undefined || unit.dead || !unit.curseable) return false; - - let choosenCurse = (this.curseIndex - .filter((curse) => curse !== undefined && curse.useIf(unit)) - .sort((a, b) => a.priority - b.priority) - .find((curse) => Skill.getManaCost(curse.skillId) < me.mp) || false); - - if (choosenCurse && !unit.getState(choosenCurse.state)) { - if (!checkCollision(me, unit, sdk.collision.Ranged)) { - me.overhead("Cursing " + unit.name + " with " + getSkillById(choosenCurse.skillId)); - return Skill.cast(choosenCurse.skillId, sdk.skills.hand.Right, unit); - } else { - me.overhead(unit.name + " is blocked, skipping attempt to curse"); - this.doCast(unit, Config.AttackSkill[unit.isSpecial ? 1 : 3], Config.AttackSkill[unit.isSpecial ? 2 : 5]); - } - } + ClassAttack.bpTick = 0; - return false; -}; + /** + * @todo + * - bonemancer specific check for using bonespear vs bone spirit + */ -ClassAttack.bpTick = 0; + // TODO: clean this up + ClassAttack.doAttack = function (unit, preattack, once) { + if (!unit) return Attack.Result.SUCCESS; + let gid = unit.gid; -/** - * - * @todo - * - bonemancer specific check for using bonespear vs bone spirit - */ - -// TODO: clean this up -ClassAttack.doAttack = function (unit, preattack, once) { - if (!unit) return Attack.Result.SUCCESS; - let gid = unit.gid; - - if (Config.MercWatch && me.needMerc()) { - console.log("mercwatch"); - - if (Town.visitTown()) { - // lost reference to the mob we were attacking - if (!unit || !copyUnit(unit).x || !Game.getMonster(-1, -1, gid) || unit.dead) { - return Attack.Result.SUCCESS; + if (Config.MercWatch && me.needMerc()) { + console.log("mercwatch"); + + if (Town.visitTown()) { + // lost reference to the mob we were attacking + if (!unit || !copyUnit(unit).x || !Game.getMonster(-1, -1, gid) || unit.dead) { + return Attack.Result.SUCCESS; + } + } + } + + let mercRevive = 0; + let gold = me.gold; + const index = (unit.isSpecial || unit.isPlayer) ? 1 : 3; + const useTerror = Skill.canUse(sdk.skills.Terror); + const useBP = Skill.canUse(sdk.skills.BonePrison); + const bpAllowedAreas = [ + sdk.areas.CatacombsLvl4, sdk.areas.Tristram, sdk.areas.MooMooFarm, + sdk.areas.RockyWaste, sdk.areas.DryHills, sdk.areas.FarOasis, + sdk.areas.LostCity, sdk.areas.ValleyofSnakes, sdk.areas.DurielsLair, + sdk.areas.SpiderForest, sdk.areas.GreatMarsh, sdk.areas.FlayerJungle, + sdk.areas.LowerKurast, sdk.areas.KurastBazaar, sdk.areas.UpperKurast, + sdk.areas.KurastCauseway, sdk.areas.DuranceofHateLvl3, sdk.areas.OuterSteppes, + sdk.areas.PlainsofDespair, sdk.areas.CityoftheDamned, sdk.areas.ChaosSanctuary, + sdk.areas.BloodyFoothills, sdk.areas.FrigidHighlands, sdk.areas.ArreatSummit, + sdk.areas.NihlathaksTemple, sdk.areas.WorldstoneLvl1, sdk.areas.WorldstoneLvl2, + sdk.areas.WorldstoneLvl3, sdk.areas.ThroneofDestruction + ]; + + // Bone prison + if (useBP && unit.distance > ([sdk.areas.DurielsLair, sdk.areas.ArreatSummit].includes(me.area) ? 6 : 10) + && bpAllowedAreas.includes(me.area) + && (index === 1 || [sdk.monsters.ListerTheTormenter, sdk.monsters.HellBovine].includes(unit.classid)) + && !checkCollision(me, unit, sdk.collision.Ranged) + && Skill.getManaCost(sdk.skills.BonePrison) * 2 < me.mp + && getTickCount() - this.bpTick > 2000) { + if (Skill.cast(sdk.skills.BonePrison, sdk.skills.hand.Right, unit)) { + this.bpTick = getTickCount(); } } - } - - let mercRevive = 0; - let gold = me.gold; - const index = (unit.isSpecial || unit.isPlayer) ? 1 : 3; - const useTerror = Skill.canUse(sdk.skills.Terror); - const useBP = Skill.canUse(sdk.skills.BonePrison); - const bpAllowedAreas = [ - sdk.areas.CatacombsLvl4, sdk.areas.Tristram, sdk.areas.MooMooFarm, sdk.areas.RockyWaste, sdk.areas.DryHills, sdk.areas.FarOasis, sdk.areas.LostCity, sdk.areas.ValleyofSnakes, - sdk.areas.DurielsLair, sdk.areas.SpiderForest, sdk.areas.GreatMarsh, sdk.areas.FlayerJungle, sdk.areas.LowerKurast, sdk.areas.KurastBazaar, sdk.areas.UpperKurast, sdk.areas.KurastCauseway, - sdk.areas.DuranceofHateLvl3, sdk.areas.OuterSteppes, sdk.areas.PlainsofDespair, sdk.areas.CityoftheDamned, sdk.areas.ChaosSanctuary, sdk.areas.BloodyFoothills, sdk.areas.FrigidHighlands, - sdk.areas.ArreatSummit, sdk.areas.NihlathaksTemple, sdk.areas.WorldstoneLvl1, sdk.areas.WorldstoneLvl2, sdk.areas.WorldstoneLvl3, sdk.areas.ThroneofDestruction - ]; - - // Bone prison - if (useBP && unit.distance > ([sdk.areas.DurielsLair, sdk.areas.ArreatSummit].includes(me.area) ? 6 : 10) - && bpAllowedAreas.includes(me.area) && (index === 1 || [sdk.monsters.ListerTheTormenter, sdk.monsters.HellBovine].includes(unit.classid)) - && !checkCollision(me, unit, sdk.collision.Ranged) && Skill.getManaCost(sdk.skills.BonePrison) * 2 < me.mp && getTickCount() - this.bpTick > 2000) { - if (Skill.cast(sdk.skills.BonePrison, sdk.skills.hand.Right, unit)) { - this.bpTick = getTickCount(); + + // write terrorCheck function, need to take into account if monsters are even scareable + let _coll = (sdk.collision.BlockMissile | sdk.collision.BlockWall | sdk.collision.Casting); + if (useTerror && me.getMobCount(6, _coll, 0, true) >= 3 + && Skill.getManaCost(sdk.skills.Terror) < me.mp && me.hpPercent < 75) { + Skill.cast(sdk.skills.Terror, sdk.skills.hand.Right); } - } - - // write terrorCheck function, need to take into account if monsters are even scareable - if (useTerror && me.getMobCount(6, Coords_1.Collision.BLOCK_MISSILE | Coords_1.BlockBits.BlockWall | Coords_1.BlockBits.Casting, 0, true) >= 3 - && Skill.getManaCost(sdk.skills.Terror) < me.mp && me.hpPercent < 75) { - Skill.cast(sdk.skills.Terror, sdk.skills.hand.Right); - } - - this.smartCurse(unit); - - if (me.expansion && index === 1 && !unit.dead) { - if (CharData.skillData.haveChargedSkill(sdk.skills.SlowMissiles) - && unit.getEnchant(sdk.enchant.LightningEnchanted) && !unit.getState(sdk.states.SlowMissiles) - && unit.curseable && (gold > 500000 && !unit.isBoss) && !checkCollision(me, unit, sdk.collision.Ranged)) { - // Cast slow missiles - Attack.castCharges(sdk.skills.SlowMissiles, unit); + + doCurse(unit); + + if (me.expansion && index === 1 && !unit.dead) { + if (CharData.skillData.haveChargedSkill(sdk.skills.SlowMissiles) + && unit.getEnchant(sdk.enchant.LightningEnchanted) && !unit.getState(sdk.states.SlowMissiles) + && unit.curseable && (gold > 500000 && !unit.isBoss) && !checkCollision(me, unit, sdk.collision.Ranged)) { + // Cast slow missiles + Attack.castCharges(sdk.skills.SlowMissiles, unit); + } } - } - - // maybe this should return an object with basic skill info besides the skillId. e.g timed, mana, range, and hand - const skills = Attack.decideSkill(unit); - - const switchBowAttack = (unit) => { - if (Attack.getIntoPosition(unit, 20, sdk.collision.Ranged)) { - try { - const checkForShamans = unit.isFallen && !me.inArea(sdk.areas.BloodMoor); - for (let i = 0; i < 5 && unit.attackable; i++) { - if (checkForShamans && !once) { - // before we waste time let's see if there is a shaman we should kill - const shaman = getUnits(sdk.unittype.Monster) - .filter(mon => mon.distance < 20 && mon.isShaman && mon.attackable) - .sort((a, b) => a.distance - b.distance).first(); - if (shaman) return ClassAttack.doAttack(shaman, null, true); - } - if (!Attack.useBowOnSwitch(unit, sdk.skills.Attack, i === 5)) return Attack.Result.FAILED; - if (unit.distance < 8 || me.inDanger()) { - if (once) return Attack.Result.FAILED; - let closeMob = getUnits(sdk.unittype.Monster) - .filter(mon => mon.distance < 10 && mon.attackable && mon.gid !== gid) - .sort(Attack.walkingSortMonsters).first(); - if (closeMob) return ClassAttack.doAttack(closeMob, null, true); + + // maybe this should return an object with basic skill info besides the skillId. e.g timed, mana, range, and hand + const skills = Attack.decideSkill(unit); + + const switchBowAttack = (unit) => { + if (Attack.getIntoPosition(unit, 20, sdk.collision.Ranged)) { + try { + const checkForShamans = unit.isFallen && !me.inArea(sdk.areas.BloodMoor); + for (let i = 0; i < 5 && unit.attackable; i++) { + if (checkForShamans && !once) { + // before we waste time let's see if there is a shaman we should kill + const shaman = getUnits(sdk.unittype.Monster) + .filter(mon => mon.distance < 20 && mon.isShaman && mon.attackable) + .sort((a, b) => a.distance - b.distance).first(); + if (shaman) return ClassAttack.doAttack(shaman, null, true); + } + if (!Attack.useBowOnSwitch(unit, sdk.skills.Attack, i === 5)) return Attack.Result.FAILED; + if (unit.distance < 8 || me.inDanger()) { + if (once) return Attack.Result.FAILED; + let closeMob = getUnits(sdk.unittype.Monster) + .filter(mon => mon.distance < 10 && mon.attackable && mon.gid !== gid) + .sort(Attack.walkingSortMonsters).first(); + if (closeMob) return ClassAttack.doAttack(closeMob, null, true); + } } + } finally { + me.weaponswitch !== sdk.player.slot.Main && me.switchWeapons(sdk.player.slot.Main); } - } finally { - me.weaponswitch !== sdk.player.slot.Main && me.switchWeapons(sdk.player.slot.Main); } + return unit.dead ? Attack.Result.SUCCESS : Attack.Result.FAILED; + }; + + if (CharData.skillData.bow.onSwitch + && (index !== 1 || !unit.name.includes(getLocaleString(sdk.locale.text.Ghostly))) + && ([-1, sdk.skills.Attack].includes(skills.timed) + || Skill.getManaCost(skills.timed) > me.mp + || (Skill.getManaCost(skills.timed) * 3 > me.mp && [sdk.skills.Teeth].includes(skills.timed)))) { + if (switchBowAttack(unit) === Attack.Result.SUCCESS) return Attack.Result.SUCCESS; } - return unit.dead ? Attack.Result.SUCCESS : Attack.Result.FAILED; - }; - if (CharData.skillData.bow.onSwitch - && (index !== 1 || !unit.name.includes(getLocaleString(sdk.locale.text.Ghostly))) - && ([-1, sdk.skills.Attack].includes(skills.timed) - || Skill.getManaCost(skills.timed) > me.mp - || (Skill.getManaCost(skills.timed) * 3 > me.mp && [sdk.skills.Teeth].includes(skills.timed)))) { - if (switchBowAttack(unit) === Attack.Result.SUCCESS) return Attack.Result.SUCCESS; - } - - if (me.normal && gold < 5000 && (skills.timed === -1 || Skill.getManaCost(skills.timed) > me.mp)) { - if (skills.timed !== sdk.skills.Teeth && Skill.canUse(sdk.skills.Teeth) && Skill.getManaCost(sdk.skills.Teeth) < me.mp) { - skills.timed = sdk.skills.Teeth; - } else if (Skill.canUse(sdk.skills.PoisonDagger) && Skill.getManaCost(sdk.skills.PoisonDagger) < me.mp) { - skills.timed = sdk.skills.PoisonDagger; - } else if (me.getMobCount(6, Coords_1.Collision.BLOCK_MISSILE | Coords_1.BlockBits.BlockWall) >= 1) { - // I have no mana and there are mobs around me, just attack - skills.timed = sdk.skills.Attack; + if (me.normal && gold < 5000 + && (skills.timed === -1 || Skill.getManaCost(skills.timed) > me.mp)) { + if (skills.timed !== sdk.skills.Teeth + && Skill.canUse(sdk.skills.Teeth) + && Skill.getManaCost(sdk.skills.Teeth) < me.mp) { + skills.timed = sdk.skills.Teeth; + } else if (Skill.canUse(sdk.skills.PoisonDagger) && Skill.getManaCost(sdk.skills.PoisonDagger) < me.mp) { + skills.timed = sdk.skills.PoisonDagger; + } else if (me.getMobCount(6, Coords_1.Collision.BLOCK_MISSILE | Coords_1.BlockBits.BlockWall) >= 1) { + // I have no mana and there are mobs around me, just attack + skills.timed = sdk.skills.Attack; + } } - } - const result = this.doCast(unit, skills.timed, skills.untimed); - - if (result === Attack.Result.SUCCESS) { - Config.ActiveSummon && this.raiseArmy(); - this.explodeCorpses(unit); - } else if (result === Attack.Result.CANTATTACK && Attack.canTeleStomp(unit)) { - let merc = me.getMerc(); - - while (unit.attackable) { - if (!unit) return Attack.Result.SUCCESS; - - if (me.needMerc()) { - if (Config.MercWatch && mercRevive++ < 1) { - Town.visitTown(); - } else { - return Attack.Result.CANTATTACK; + const result = this.doCast(unit, skills.timed, skills.untimed); + + if (result === Attack.Result.SUCCESS) { + Config.ActiveSummon && this.raiseArmy(); + this.explodeCorpses(unit); + } else if (result === Attack.Result.CANTATTACK && Attack.canTeleStomp(unit)) { + let merc = me.getMerc(); + + while (unit.attackable) { + if (!unit) return Attack.Result.SUCCESS; + + if (me.needMerc()) { + if (Config.MercWatch && mercRevive++ < 1) { + Town.visitTown(); + } else { + return Attack.Result.CANTATTACK; + } + + (merc === undefined || !merc) && (merc = me.getMerc()); } - (merc === undefined || !merc) && (merc = me.getMerc()); - } + if (!!merc && getDistance(merc, unit) > 5) { + Pather.moveToUnit(unit); - if (!!merc && getDistance(merc, unit) > 5) { - Pather.moveToUnit(unit); + let spot = Attack.findSafeSpot(unit, 10, 5, 9); + !!spot && Pather.walkTo(spot.x, spot.y); + } - let spot = Attack.findSafeSpot(unit, 10, 5, 9); - !!spot && Pather.walkTo(spot.x, spot.y); + Config.ActiveSummon && this.raiseArmy(); + this.explodeCorpses(unit); + this.smartCurse(unit); + let closeMob = Attack.getNearestMonster({ skipGid: gid }); + if (!!closeMob) { + let findSkill = Attack.decideSkill(closeMob); + (this.doCast(closeMob, findSkill.timed, findSkill.untimed) === Attack.Result.SUCCESS) + || (this.canCurse(unit, sdk.skills.Terror) && Skill.cast(sdk.skills.Terror, sdk.skills.hand.Right, unit)); + } } - Config.ActiveSummon && this.raiseArmy(); - this.explodeCorpses(unit); - this.smartCurse(unit); - let closeMob = Attack.getNearestMonster({ skipGid: gid }); - if (!!closeMob) { - let findSkill = Attack.decideSkill(closeMob); - (this.doCast(closeMob, findSkill.timed, findSkill.untimed) === Attack.Result.SUCCESS) - || (this.canCurse(unit, sdk.skills.Terror) && Skill.cast(sdk.skills.Terror, sdk.skills.hand.Right, unit)); - } + return Attack.Result.SUCCESS; } - return Attack.Result.SUCCESS; - } - - return result; -}; + return result; + }; -// Returns: 0 - fail, 1 - success, 2 - no valid attack skills -ClassAttack.doCast = function (unit, timedSkill, untimedSkill) { - // No valid skills can be found - if (timedSkill < 0 && untimedSkill < 0) return Attack.Result.CANTATTACK; + // Returns: 0 - fail, 1 - success, 2 - no valid attack skills + ClassAttack.doCast = function (unit, timedSkill, untimedSkill) { + // No valid skills can be found + if (timedSkill < 0 && untimedSkill < 0) return Attack.Result.CANTATTACK; - // Check for bodies to exploit for CorpseExplosion before committing to an attack for non-summoner type necros - if (Config.Skeletons + Config.SkeletonMages + Config.Revives === 0) { - this.checkCorpseNearMonster(unit) && this.explodeCorpses(unit); - } + // Check for bodies to exploit for CorpseExplosion before committing to an attack for non-summoner type necros + if (Config.Skeletons + Config.SkeletonMages + Config.Revives === 0) { + this.checkCorpseNearMonster(unit) && this.explodeCorpses(unit); + } - let lowMana = true; - let walk, timedSkillRange, untimedSkillRange; + let lowMana = true; + let walk, timedSkillRange, untimedSkillRange; + + if (timedSkill > -1 + && (!me.getState(sdk.states.SkillDelay) || !Skill.isTimed(timedSkill)) + && me.mp > Skill.getManaCost(timedSkill)) { + lowMana = false; + timedSkillRange = Skill.getRange(timedSkill); + + switch (timedSkill) { + case sdk.skills.PoisonNova: + if (!this.novaTick || getTickCount() - this.novaTick > Config.PoisonNovaDelay * 1000) { + if (unit.distance > timedSkillRange || checkCollision(me, unit, sdk.collision.Ranged)) { + if (!Attack.getIntoPosition(unit, timedSkillRange, sdk.collision.Ranged)) { + return Attack.Result.FAILED; + } + } - if (timedSkill > -1 && (!me.getState(sdk.states.SkillDelay) || !Skill.isTimed(timedSkill)) && me.mp > Skill.getManaCost(timedSkill)) { - lowMana = false; - timedSkillRange = Skill.getRange(timedSkill); + if (!unit.dead && Skill.cast(timedSkill, Skill.getHand(timedSkill), unit)) { + this.novaTick = getTickCount(); + } + } - switch (timedSkill) { - case sdk.skills.PoisonNova: - if (!this.novaTick || getTickCount() - this.novaTick > Config.PoisonNovaDelay * 1000) { + break; + case sdk.skills.Summoner: // Pure Summoner if (unit.distance > timedSkillRange || checkCollision(me, unit, sdk.collision.Ranged)) { if (!Attack.getIntoPosition(unit, timedSkillRange, sdk.collision.Ranged)) { return Attack.Result.FAILED; } } - if (!unit.dead && Skill.cast(timedSkill, Skill.getHand(timedSkill), unit)) { - this.novaTick = getTickCount(); - } - } + delay(300); - break; - case sdk.skills.Summoner: // Pure Summoner - if (unit.distance > timedSkillRange || checkCollision(me, unit, sdk.collision.Ranged)) { - if (!Attack.getIntoPosition(unit, timedSkillRange, sdk.collision.Ranged)) { - return Attack.Result.FAILED; - } - } + break; + default: + if (timedSkillRange < 4 && !Attack.validSpot(unit.x, unit.y)) return Attack.Result.FAILED; - delay(300); - - break; - default: - if (timedSkillRange < 4 && !Attack.validSpot(unit.x, unit.y)) return Attack.Result.FAILED; + if (timedSkill === sdk.skills.Teeth) { + let _coll = (sdk.collision.BlockMissile | sdk.collision.BlockWall | sdk.collision.Casting); + timedSkillRange = me.getMobCount(6, _coll) <= 3 ? 6 : timedSkillRange; + } - if (timedSkill === sdk.skills.Teeth) { - timedSkillRange = me.getMobCount(6, Coords_1.Collision.BLOCK_MISSILE | Coords_1.BlockBits.BlockWall | Coords_1.BlockBits.Casting) <= 3 ? 6 : timedSkillRange; - } + if (unit.distance > timedSkillRange || checkCollision(me, unit, sdk.collision.Ranged)) { + // Allow short-distance walking for melee skills + walk = timedSkillRange < 4 && unit.distance < 10 && !checkCollision(me, unit, sdk.collision.BlockWall); - if (unit.distance > timedSkillRange || checkCollision(me, unit, sdk.collision.Ranged)) { - // Allow short-distance walking for melee skills - walk = timedSkillRange < 4 && unit.distance < 10 && !checkCollision(me, unit, sdk.collision.BlockWall); + if (!Attack.getIntoPosition(unit, timedSkillRange, sdk.collision.Ranged, walk)) return Attack.Result.FAILED; + } - if (!Attack.getIntoPosition(unit, timedSkillRange, sdk.collision.Ranged, walk)) return Attack.Result.FAILED; - } + if (!unit.dead) { + // Try to find better spot + if (unit.distance < 4 && timedSkillRange > 6) { + Attack.deploy(unit, 4, 5, 9); + } - if (!unit.dead) { - // Try to find better spot - if (unit.distance < 4 && timedSkillRange > 6) { - Attack.deploy(unit, 4, 5, 9); + Skill.cast(timedSkill, Skill.getHand(timedSkill), unit); } - Skill.cast(timedSkill, Skill.getHand(timedSkill), unit); + break; } - - break; } - } - if (untimedSkill > -1 && me.mp > Skill.getManaCost(untimedSkill)) { - lowMana = false; - untimedSkillRange = Skill.getRange(untimedSkill); + if (untimedSkill > -1 && me.mp > Skill.getManaCost(untimedSkill)) { + lowMana = false; + untimedSkillRange = Skill.getRange(untimedSkill); - if (untimedSkillRange < 4 && !Attack.validSpot(unit.x, unit.y)) return Attack.Result.FAILED; + if (untimedSkillRange < 4 && !Attack.validSpot(unit.x, unit.y)) return Attack.Result.FAILED; - if (unit.distance > untimedSkillRange || checkCollision(me, unit, sdk.collision.Ranged)) { - // Allow short-distance walking for melee skills - walk = Skill.getRange(untimedSkill) < 4 && unit.distance < 10 && !checkCollision(me, unit, sdk.collision.BlockWall); + if (unit.distance > untimedSkillRange || checkCollision(me, unit, sdk.collision.Ranged)) { + // Allow short-distance walking for melee skills + walk = (Skill.getRange(untimedSkill) < 4 + && unit.distance < 10 + && !checkCollision(me, unit, sdk.collision.BlockWall) + ); - if (!Attack.getIntoPosition(unit, untimedSkillRange, sdk.collision.Ranged, walk)) { - return Attack.Result.FAILED; + if (!Attack.getIntoPosition(unit, untimedSkillRange, sdk.collision.Ranged, walk)) { + return Attack.Result.FAILED; + } } - } - !unit.dead && Skill.cast(untimedSkill, Skill.getHand(untimedSkill), unit); + !unit.dead && Skill.cast(untimedSkill, Skill.getHand(untimedSkill), unit); - return Attack.Result.SUCCESS; - } + return Attack.Result.SUCCESS; + } - Misc.poll(() => !me.skillDelay, 1000, 40); + Misc.poll(() => !me.skillDelay, 1000, 40); - // Delay for Poison Nova - while (this.novaTick && getTickCount() - this.novaTick < Config.PoisonNovaDelay * 1000) { - delay(40); - } + // Delay for Poison Nova + while (this.novaTick && getTickCount() - this.novaTick < Config.PoisonNovaDelay * 1000) { + delay(40); + } - return lowMana ? Attack.Result.NEEDMANA : Attack.Result.SUCCESS; -}; + return lowMana ? Attack.Result.NEEDMANA : Attack.Result.SUCCESS; + }; -ClassAttack.farCast = function (unit) { - let timedSkill = Config.AttackSkill[1], untimedSkill = Config.AttackSkill[2]; + ClassAttack.farCast = function (unit) { + let timedSkill = Config.AttackSkill[1]; + let untimedSkill = Config.AttackSkill[2]; - // No valid skills can be found - if (timedSkill < 0 && untimedSkill < 0) return Attack.Result.CANTATTACK; + // No valid skills can be found + if (timedSkill < 0 && untimedSkill < 0) return Attack.Result.CANTATTACK; - // Far to low a range for far casting - if (Skill.getRange(timedSkill) < 4 && Skill.getRange(untimedSkill) < 4) return Attack.Result.CANTATTACK; + // Far to low a range for far casting + if (Skill.getRange(timedSkill) < 4 && Skill.getRange(untimedSkill) < 4) return Attack.Result.CANTATTACK; - // Bone prison - if (unit.distance > 10 && !checkCollision(me, unit, sdk.collision.Ranged) && Skill.getManaCost(sdk.skills.BonePrison) * 2 < me.mp && getTickCount() - this.bpTick > 2000) { - if (Skill.cast(sdk.skills.BonePrison, sdk.skills.hand.Right, unit)) { - this.bpTick = getTickCount(); + // Bone prison + if (unit.distance > 10 + && !checkCollision(me, unit, sdk.collision.Ranged) + && Skill.getManaCost(sdk.skills.BonePrison) * 2 < me.mp + && getTickCount() - this.bpTick > 2000) { + if (Skill.cast(sdk.skills.BonePrison, sdk.skills.hand.Right, unit)) { + this.bpTick = getTickCount(); + } } - } - this.smartCurse(unit); - - // Check for bodies to exploit for CorpseExplosion before committing to an attack for non-summoner type necros - if (Config.Skeletons + Config.SkeletonMages + Config.Revives === 0) { - this.checkCorpseNearMonster(unit) && this.explodeCorpses(unit); - } - - if (timedSkill > -1 && (!me.getState(sdk.states.SkillDelay) || !Skill.isTimed(timedSkill))) { - switch (timedSkill) { - case sdk.skills.PoisonNova: - case sdk.skills.Summoner: // Pure Summoner - break; - default: - if (!unit.dead && !checkCollision(me, unit, sdk.collision.Ranged)) { - Skill.cast(timedSkill, Skill.getHand(timedSkill), unit); - } + doCurse(unit); - break; + // Check for bodies to exploit for CorpseExplosion before committing to an attack for non-summoner type necros + if (Config.Skeletons + Config.SkeletonMages + Config.Revives === 0) { + this.checkCorpseNearMonster(unit) && this.explodeCorpses(unit); } - } - if (untimedSkill > -1) { - if (Skill.getRange(untimedSkill) < 4 && !Attack.validSpot(unit.x, unit.y)) return Attack.Result.FAILED; + if (timedSkill > -1 && (!me.getState(sdk.states.SkillDelay) || !Skill.isTimed(timedSkill))) { + switch (timedSkill) { + case sdk.skills.PoisonNova: + case sdk.skills.Summoner: // Pure Summoner + break; + default: + if (!unit.dead && !checkCollision(me, unit, sdk.collision.Ranged)) { + Skill.cast(timedSkill, Skill.getHand(timedSkill), unit); + } - if (!unit.dead && !checkCollision(me, unit, sdk.collision.Ranged)) { - Skill.cast(untimedSkill, Skill.getHand(untimedSkill), unit); + break; + } } - return Attack.Result.SUCCESS; - } + if (untimedSkill > -1) { + if (Skill.getRange(untimedSkill) < 4 && !Attack.validSpot(unit.x, unit.y)) return Attack.Result.FAILED; - Misc.poll(() => !me.skillDelay, 1000, 40); + if (!unit.dead && !checkCollision(me, unit, sdk.collision.Ranged)) { + Skill.cast(untimedSkill, Skill.getHand(untimedSkill), unit); + } - return Attack.Result.SUCCESS; -}; + return Attack.Result.SUCCESS; + } -ClassAttack.explodeCorpses = function (unit) { - if (Config.ExplodeCorpses === 0 || unit.dead) return false; + Misc.poll(() => !me.skillDelay, 1000, 40); - let corpseList = []; - let useAmp = Skill.canUse(sdk.skills.AmplifyDamage); - let ampManaCost = Skill.getManaCost(sdk.skills.AmplifyDamage); - let explodeCorpsesManaCost = Skill.getManaCost(Config.ExplodeCorpses); - let range = Math.floor((me.getSkill(Config.ExplodeCorpses, sdk.skills.subindex.SoftPoints) + 7) / 3); - let corpse = Game.getMonster(-1, sdk.monsters.mode.Dead); + return Attack.Result.SUCCESS; + }; - if (corpse) { - do { - if (getDistance(unit, corpse) <= range && this.checkCorpse(corpse)) { - corpseList.push(copyUnit(corpse)); - } - } while (corpse.getNext()); + ClassAttack.explodeCorpses = function (unit) { + if (Config.ExplodeCorpses === 0 || unit.dead) return false; - // Shuffle the corpseList so if running multiple necrobots they explode separate corpses not the same ones - corpseList.length > 1 && (corpseList = corpseList.shuffle()); + let corpseList = []; + let useAmp = Skill.canUse(sdk.skills.AmplifyDamage); + let ampManaCost = Skill.getManaCost(sdk.skills.AmplifyDamage); + let explodeCorpsesManaCost = Skill.getManaCost(Config.ExplodeCorpses); + let range = Math.floor((me.getSkill(Config.ExplodeCorpses, sdk.skills.subindex.SoftPoints) + 7) / 3); + let corpse = Game.getMonster(-1, sdk.monsters.mode.Dead); - if (this.isArmyFull()) { - // We don't need corpses as we are not a Summoner Necro, Spam CE till monster dies or we run out of bodies. + if (corpse) { do { - corpse = corpseList.shift(); - - if (corpse) { - if (!unit.dead && this.checkCorpse(corpse) && getDistance(corpse, unit) <= range) { - // Added corpse ID so I can see when it blows another monster with the same ClassID and Name - me.overhead("Exploding: " + corpse.classid + " " + corpse.name + " id:" + corpse.gid); + if (getDistance(unit, corpse) <= range + && this.checkCorpse(corpse)) { + corpseList.push(copyUnit(corpse)); + } + } while (corpse.getNext()); - if (useAmp && !unit.getState(sdk.states.AmplifyDamage) && !unit.getState(sdk.states.Decrepify) && me.mp > (ampManaCost + explodeCorpsesManaCost)) { - Skill.cast(sdk.skills.AmplifyDamage, sdk.skills.hand.Right, unit); - } + // Shuffle the corpseList so if running multiple necrobots they explode separate corpses not the same ones + corpseList.length > 1 && (corpseList = corpseList.shuffle()); - if (Skill.cast(Config.ExplodeCorpses, sdk.skills.hand.Right, corpse)) { - delay(me.ping + 1); - } - } - } - } while (corpseList.length > 0); - } else { - // We are a Summoner Necro, we should conserve corpses, only blow 2 at a time so we can check for needed re-summons. - for (let i = 0; i <= 1; i += 1) { - if (corpseList.length > 0) { + if (this.isArmyFull()) { + // We don't need corpses as we are not a Summoner Necro, Spam CE till monster dies or we run out of bodies. + do { corpse = corpseList.shift(); if (corpse) { - me.overhead("Exploding: " + corpse.classid + " " + corpse.name); - - if (useAmp && !unit.getState(sdk.states.AmplifyDamage) && !unit.getState(sdk.states.Decrepify) && me.mp > (ampManaCost + explodeCorpsesManaCost)) { - Skill.cast(sdk.skills.AmplifyDamage, sdk.skills.hand.Right, unit); + if (!unit.dead && this.checkCorpse(corpse) && getDistance(corpse, unit) <= range) { + // Added corpse ID so I can see when it blows another monster with the same ClassID and Name + me.overhead("Exploding: " + corpse.classid + " " + corpse.name + " id:" + corpse.gid); + + if (useAmp && !unit.getState(sdk.states.AmplifyDamage) + && !unit.getState(sdk.states.Decrepify) + && me.mp > (ampManaCost + explodeCorpsesManaCost)) { + Skill.cast(sdk.skills.AmplifyDamage, sdk.skills.hand.Right, unit); + } + + if (Skill.cast(Config.ExplodeCorpses, sdk.skills.hand.Right, corpse)) { + delay(me.ping + 1); + } } - - if (Skill.cast(Config.ExplodeCorpses, sdk.skills.hand.Right, corpse)) { - delay(200); + } + } while (corpseList.length > 0); + } else { + // We are a Summoner Necro, we should conserve corpses, only blow 2 at a time so we can check for needed re-summons. + for (let i = 0; i <= 1; i += 1) { + if (corpseList.length > 0) { + corpse = corpseList.shift(); + + if (corpse) { + me.overhead("Exploding: " + corpse.classid + " " + corpse.name); + + if (useAmp && !unit.getState(sdk.states.AmplifyDamage) + && !unit.getState(sdk.states.Decrepify) + && me.mp > (ampManaCost + explodeCorpsesManaCost)) { + Skill.cast(sdk.skills.AmplifyDamage, sdk.skills.hand.Right, unit); + } + + if (Skill.cast(Config.ExplodeCorpses, sdk.skills.hand.Right, corpse)) { + delay(200); + } } + } else { + break; } - } else { - break; } } } - } - return true; -}; + return true; + }; +})(); From 44b1b03fee89e3a2844df0f9844f535c6b5f74ed Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Mon, 29 May 2023 13:10:50 -0400 Subject: [PATCH 123/263] Formatting --- D2BotSoloPlay.dbj | 182 +- default.dbj | 493 +- .../BuildFiles/Runewords/AncientsPledge.js | 72 +- libs/SoloPlay/BuildFiles/Runewords/Bone.js | 54 +- .../BuildFiles/Runewords/BreathOfTheDying.js | 34 +- .../BuildFiles/Runewords/CallToArms.js | 70 +- .../BuildFiles/Runewords/ChainsOfHonor.js | 102 +- libs/SoloPlay/BuildFiles/Runewords/Chaos.js | 20 +- .../BuildFiles/Runewords/CrescentMoon.js | 92 +- .../BuildFiles/Runewords/DragonArmor.js | 30 +- .../BuildFiles/Runewords/DreamHelm.js | 18 +- .../BuildFiles/Runewords/DreamShield.js | 26 +- libs/SoloPlay/BuildFiles/Runewords/Duress.js | 56 +- libs/SoloPlay/BuildFiles/Runewords/Enigma.js | 76 +- libs/SoloPlay/BuildFiles/Runewords/Exile.js | 28 +- libs/SoloPlay/BuildFiles/Runewords/Faith.js | 96 +- .../BuildFiles/Runewords/Fortitude.js | 60 +- libs/SoloPlay/BuildFiles/Runewords/Fury.js | 20 +- libs/SoloPlay/BuildFiles/Runewords/Grief.js | 82 +- .../BuildFiles/Runewords/HandOfJustice.js | 50 +- .../BuildFiles/Runewords/HeartOfTheOak.js | 58 +- libs/SoloPlay/BuildFiles/Runewords/Honor.js | 68 +- libs/SoloPlay/BuildFiles/Runewords/Ice.js | 108 +- .../BuildFiles/Runewords/KingsGrace.js | 50 +- .../SoloPlay/BuildFiles/Runewords/LastWish.js | 54 +- .../BuildFiles/Runewords/Lawbringer.js | 56 +- libs/SoloPlay/BuildFiles/Runewords/Lore.js | 236 +- libs/SoloPlay/BuildFiles/Runewords/Malice.js | 34 +- .../SoloPlay/BuildFiles/Runewords/MercDoom.js | 120 +- .../BuildFiles/Runewords/MercFortitude.js | 92 +- .../BuildFiles/Runewords/MercInfinity.js | 58 +- .../BuildFiles/Runewords/MercInsight.js | 90 +- .../BuildFiles/Runewords/MercPride.js | 58 +- .../BuildFiles/Runewords/MercTreachery.js | 84 +- libs/SoloPlay/BuildFiles/Runewords/Myth.js | 56 +- .../BuildFiles/Runewords/PDiamondShield.js | 22 +- .../BuildFiles/Runewords/PhoenixShield.js | 72 +- libs/SoloPlay/BuildFiles/Runewords/Rhyme.js | 74 +- .../BuildFiles/Runewords/Sanctuary.js | 44 +- libs/SoloPlay/BuildFiles/Runewords/Silence.js | 68 +- libs/SoloPlay/BuildFiles/Runewords/Smoke.js | 48 +- .../BuildFiles/Runewords/SpiritShield.js | 76 +- .../BuildFiles/Runewords/SpiritSword.js | 78 +- libs/SoloPlay/BuildFiles/Runewords/Stealth.js | 44 +- libs/SoloPlay/BuildFiles/Runewords/Steel.js | 78 +- .../BuildFiles/Runewords/Treachery.js | 52 +- .../BuildFiles/Runewords/VoiceOfReason.js | 78 +- libs/SoloPlay/BuildFiles/Runewords/White.js | 58 +- .../amazon/amazon.FaithbowzonBuild.js | 264 +- .../amazon/amazon.FrostmaidenBuild.js | 236 +- .../BuildFiles/amazon/amazon.JavazonBuild.js | 318 +- .../BuildFiles/amazon/amazon.LevelingBuild.js | 148 +- .../BuildFiles/amazon/amazon.StartBuild.js | 146 +- .../BuildFiles/amazon/amazon.SteppingBuild.js | 106 +- .../BuildFiles/amazon/amazon.WfzonBuild.js | 258 +- .../amazon/amazon.WitchyzonBuild.js | 248 +- libs/SoloPlay/BuildFiles/amazon/amazon.js | 66 +- .../assassin/assassin.LevelingBuild.js | 148 +- .../assassin/assassin.StartBuild.js | 116 +- .../assassin/assassin.TrapsinBuild.js | 268 +- .../assassin/assassin.WhirlsinBuild.js | 250 +- libs/SoloPlay/BuildFiles/assassin/assassin.js | 60 +- .../barbarian/barbarian.FrenzyBuild.js | 252 +- .../barbarian/barbarian.ImmortalwhirlBuild.js | 238 +- .../barbarian/barbarian.LevelingBuild.js | 100 +- .../barbarian/barbarian.SingerBuild.js | 214 +- .../barbarian/barbarian.StartBuild.js | 150 +- .../barbarian/barbarian.SteppingBuild.js | 134 +- .../barbarian/barbarian.ThrowBuild.js | 238 +- .../barbarian/barbarian.UberconcBuild.js | 210 +- .../barbarian/barbarian.WhirlwindBuild.js | 234 +- .../BuildFiles/barbarian/barbarian.js | 66 +- .../BuildFiles/druid/druid.ElementalBuild.js | 306 +- .../BuildFiles/druid/druid.FirewolfBuild.js | 230 +- .../BuildFiles/druid/druid.LevelingBuild.js | 104 +- .../BuildFiles/druid/druid.PlaguewolfBuild.js | 210 +- .../BuildFiles/druid/druid.StartBuild.js | 124 +- .../BuildFiles/druid/druid.StormbearBuild.js | 228 +- .../BuildFiles/druid/druid.WindBuild.js | 230 +- .../BuildFiles/druid/druid.WolfBuild.js | 214 +- libs/SoloPlay/BuildFiles/druid/druid.js | 60 +- .../necromancer/necromancer.BoneBuild.js | 294 +- .../necromancer/necromancer.LevelingBuild.js | 108 +- .../necromancer/necromancer.PoisonBuild.js | 296 +- .../necromancer/necromancer.StartBuild.js | 138 +- .../necromancer/necromancer.SummonBuild.js | 322 +- .../BuildFiles/necromancer/necromancer.js | 60 +- .../paladin/paladin.AuradinBuild.js | 236 +- .../paladin/paladin.ClassicauradinBuild.js | 280 +- .../paladin/paladin.HammerdinBuild.js | 302 +- .../paladin/paladin.HammershockBuild.js | 278 +- .../paladin/paladin.LevelingBuild.js | 220 +- .../paladin/paladin.SancdreamerBuild.js | 230 +- .../BuildFiles/paladin/paladin.SmiterBuild.js | 212 +- .../BuildFiles/paladin/paladin.StartBuild.js | 172 +- .../paladin/paladin.TorchadinBuild.js | 238 +- .../BuildFiles/paladin/paladin.ZealerBuild.js | 228 +- libs/SoloPlay/BuildFiles/paladin/paladin.js | 62 +- .../sorceress/sorceress.BlizzballerBuild.js | 296 +- .../sorceress/sorceress.BlovaBuild.js | 276 +- .../sorceress/sorceress.ColdBuild.js | 270 +- .../sorceress/sorceress.EsorbBuild.js | 336 +- .../sorceress/sorceress.LevelingBuild.js | 156 +- .../sorceress/sorceress.LightningBuild.js | 272 +- .../sorceress/sorceress.MeteorbBuild.js | 340 +- .../sorceress/sorceress.StartBuild.js | 164 +- .../sorceress/sorceress.SteppingBuild.js | 144 +- .../BuildFiles/sorceress/sorceress.js | 66 +- libs/SoloPlay/Config/Amazon.js | 476 +- libs/SoloPlay/Config/Assassin.js | 406 +- libs/SoloPlay/Config/Barbarian.js | 638 +- libs/SoloPlay/Config/Druid.js | 456 +- libs/SoloPlay/Config/Necromancer.js | 410 +- libs/SoloPlay/Config/Paladin.js | 976 +- libs/SoloPlay/Config/Sorceress.js | 492 +- libs/SoloPlay/Functions/AttackOverrides.js | 2604 +-- libs/SoloPlay/Functions/AutoBuild.js | 130 +- libs/SoloPlay/Functions/AutoMuleOverrides.js | 106 +- libs/SoloPlay/Functions/AutoStatOverrides.js | 56 +- libs/SoloPlay/Functions/CharmEquip.js | 1426 +- .../ClassAttackOverrides/AmazonAttacks-WIP.js | 776 +- .../ClassAttackOverrides/AmazonAttacks.js | 812 +- .../ClassAttackOverrides/AssassinAttacks.js | 484 +- .../ClassAttackOverrides/BarbarianAttacks.js | 870 +- .../ClassAttackOverrides/DruidAttacks.js | 576 +- .../ClassAttackOverrides/PaladinAttacks.js | 760 +- .../ClassAttackOverrides/SorceressAttacks.js | 1284 +- libs/SoloPlay/Functions/ConfigOverrides.js | 96 +- libs/SoloPlay/Functions/CubingOverrides.js | 1704 +- libs/SoloPlay/Functions/DynamicTiers.js | 1264 +- libs/SoloPlay/Functions/Globals.js | 2028 +- libs/SoloPlay/Functions/ItemOverrides.js | 1614 +- libs/SoloPlay/Functions/ItemPrototypes.js | 542 +- libs/SoloPlay/Functions/ItemUtilities.js | 1082 +- libs/SoloPlay/Functions/LoaderOverrides.js | 294 +- libs/SoloPlay/Functions/Me.js | 1418 +- libs/SoloPlay/Functions/Mercenary.js | 614 +- libs/SoloPlay/Functions/MiscOverrides.js | 1266 +- .../SoloPlay/Functions/MuleloggerOverrides.js | 242 +- libs/SoloPlay/Functions/NPCAction.js | 1376 +- libs/SoloPlay/Functions/NTIPOverrides.js | 1400 +- libs/SoloPlay/Functions/PatherOverrides.js | 1550 +- libs/SoloPlay/Functions/PickitOverrides.js | 1492 +- libs/SoloPlay/Functions/Polyfills.js | 314 +- libs/SoloPlay/Functions/PrecastOverrides.js | 254 +- libs/SoloPlay/Functions/PrototypeOverrides.js | 1540 +- libs/SoloPlay/Functions/Quest.js | 1290 +- libs/SoloPlay/Functions/RunewordsOverrides.js | 124 +- libs/SoloPlay/Functions/SkillOverrides.js | 412 +- libs/SoloPlay/Functions/SoloEvents.js | 934 +- libs/SoloPlay/Functions/SoloWants.js | 450 +- libs/SoloPlay/Functions/StorageOverrides.js | 1358 +- libs/SoloPlay/Functions/TownOverrides.js | 1312 +- libs/SoloPlay/Modules/Clear.js | 382 +- libs/SoloPlay/Modules/Coords.js | 372 +- libs/SoloPlay/Modules/Events.js | 172 +- libs/SoloPlay/Modules/GameData/AreaData.js | 394 +- libs/SoloPlay/Modules/GameData/GameData.js | 4038 ++-- .../Modules/GameData/LocaleStringID.js | 15592 ++++++++-------- libs/SoloPlay/Modules/GameData/MissileData.js | 50 +- libs/SoloPlay/Modules/GameData/MonsterData.js | 206 +- libs/SoloPlay/Modules/GameData/PotData.js | 280 +- libs/SoloPlay/Modules/Guard.js | 132 +- libs/SoloPlay/Modules/MercLib.js | 650 +- libs/SoloPlay/Modules/Mock.js | 358 +- libs/SoloPlay/Modules/MockItem.js | 396 +- libs/SoloPlay/Modules/MoveTo.js | 558 +- libs/SoloPlay/Modules/utilities.js | 128 +- libs/SoloPlay/Scripts/a1chests.js | 42 +- libs/SoloPlay/Scripts/a5chests.js | 44 +- libs/SoloPlay/Scripts/amulet.js | 46 +- libs/SoloPlay/Scripts/ancients.js | 102 +- libs/SoloPlay/Scripts/ancienttunnels.js | 48 +- libs/SoloPlay/Scripts/andariel.js | 180 +- libs/SoloPlay/Scripts/anya.js | 232 +- libs/SoloPlay/Scripts/baal.js | 580 +- libs/SoloPlay/Scripts/beetleburst.js | 14 +- libs/SoloPlay/Scripts/bishibosh.js | 18 +- libs/SoloPlay/Scripts/bloodraven.js | 120 +- libs/SoloPlay/Scripts/boneash.js | 16 +- libs/SoloPlay/Scripts/brain.js | 28 +- libs/SoloPlay/Scripts/cave.js | 98 +- libs/SoloPlay/Scripts/corpsefire.js | 16 +- libs/SoloPlay/Scripts/countess.js | 80 +- libs/SoloPlay/Scripts/cows.js | 312 +- libs/SoloPlay/Scripts/cube.js | 22 +- libs/SoloPlay/Scripts/den.js | 332 +- libs/SoloPlay/Scripts/developermode.js | 526 +- libs/SoloPlay/Scripts/diablo.js | 346 +- libs/SoloPlay/Scripts/duriel.js | 112 +- libs/SoloPlay/Scripts/eye.js | 38 +- libs/SoloPlay/Scripts/getkeys.js | 46 +- libs/SoloPlay/Scripts/heart.js | 30 +- libs/SoloPlay/Scripts/hellforge.js | 130 +- libs/SoloPlay/Scripts/hephasto.js | 28 +- libs/SoloPlay/Scripts/izual.js | 24 +- libs/SoloPlay/Scripts/jail.js | 46 +- libs/SoloPlay/Scripts/lamessen.js | 24 +- libs/SoloPlay/Scripts/lowerkurast.js | 12 +- libs/SoloPlay/Scripts/maggotlair.js | 74 +- libs/SoloPlay/Scripts/mausoleum.js | 44 +- libs/SoloPlay/Scripts/mephisto.js | 118 +- libs/SoloPlay/Scripts/nith.js | 36 +- libs/SoloPlay/Scripts/orgtorch.js | 690 +- libs/SoloPlay/Scripts/pindle.js | 20 +- libs/SoloPlay/Scripts/pits.js | 20 +- libs/SoloPlay/Scripts/radament.js | 52 +- libs/SoloPlay/Scripts/river.js | 30 +- libs/SoloPlay/Scripts/savebarby.js | 54 +- libs/SoloPlay/Scripts/shenk.js | 20 +- libs/SoloPlay/Scripts/smith.js | 48 +- libs/SoloPlay/Scripts/staff.js | 28 +- libs/SoloPlay/Scripts/summoner.js | 226 +- libs/SoloPlay/Scripts/templeruns.js | 70 +- libs/SoloPlay/Scripts/tombs.js | 54 +- libs/SoloPlay/Scripts/travincal.js | 186 +- libs/SoloPlay/Scripts/treehead.js | 28 +- libs/SoloPlay/Scripts/tristram.js | 278 +- libs/SoloPlay/Scripts/worldstone.js | 58 +- libs/SoloPlay/SoloPlay.js | 652 +- libs/SoloPlay/Threads/Reload.js | 40 +- libs/SoloPlay/Threads/ToolsThread.js | 1486 +- libs/SoloPlay/Tools/CharData.js | 630 +- libs/SoloPlay/Tools/Developer.js | 154 +- libs/SoloPlay/Tools/NameGen.js | 476 +- libs/SoloPlay/Tools/OOGOverrides.js | 2470 +-- libs/SoloPlay/Tools/Overlay.js | 894 +- libs/SoloPlay/Tools/SoloIndex.js | 1416 +- libs/SoloPlay/Tools/Tracker.js | 408 +- libs/SoloPlay/Utils/General.js | 232 +- libs/SoloPlay/Utils/Init.js | 100 +- libs/SoloPlay/Workers/EventEmitter.js | 106 +- libs/SoloPlay/Workers/EventHandler.js | 220 +- libs/SoloPlay/Workers/TownChicken.js | 786 +- libs/SoloPlay/index.d.ts | 720 +- 235 files changed, 47945 insertions(+), 47932 deletions(-) diff --git a/D2BotSoloPlay.dbj b/D2BotSoloPlay.dbj index 08faa142..c528cc45 100644 --- a/D2BotSoloPlay.dbj +++ b/D2BotSoloPlay.dbj @@ -45,13 +45,13 @@ include("SoloPlay/Tools/OOGOverrides.js"); include("SoloPlay/Functions/ConfigOverrides.js"); if (typeof Starter.AdvancedConfig[me.profile] === "object") { - Object.assign(Starter.Config, Starter.AdvancedConfig[me.profile]); + Object.assign(Starter.Config, Starter.AdvancedConfig[me.profile]); } delete Starter.AdvancedConfig; // initialize data files if (!FileTools.exists("data/" + me.profile + ".json") && DataFile.create()) { - Starter.firstRun = true; + Starter.firstRun = true; } !FileTools.exists(CharData.filePath) && CharData.create(); @@ -59,93 +59,93 @@ if (!FileTools.exists("data/" + me.profile + ".json") && DataFile.create()) { Developer.logPerformance && Tracker.initialize(); function main () { - debugLog(me.profile); - addEventListener("copydata", Starter.receiveCopyData); - addEventListener("scriptmsg", Starter.scriptMsgEvent); - - let oogTick = getTickCount(); - - while (!Starter.handle) { - delay(3); - } - - DataFile.updateStats("handle", Starter.handle); - D2Bot.handle = Starter.handle; - delay(500); - - load("threads/heartbeat.js"); - - if (Profile().type === sdk.game.profiletype.TcpIpJoin) { - D2Bot.printToConsole("TcpJoin is unsupported."); - D2Bot.stop(); - } - - Starter.gameCount = (DataFile.getStats().runs + 1 || 1); - - while (!Object.keys(Starter.gameInfo).length) { - delay(rand(200, 1500)); - D2Bot.requestGameInfo(); - delay(500); - } - - if (Starter.gameInfo.error) { - ControlAction.timeoutDelay("Crash Delay", Starter.Config.CrashDelay * 1e3); - Starter.BNET && D2Bot.updateRuns(); - } - - DataFile.updateStats("debugInfo", JSON.stringify({ currScript: "none", area: "out of game" })); - - while (!Object.keys(Starter.profileInfo).length) { - delay(rand(200, 1500)); - D2Bot.getProfile(); - delay(500); - } - - while (true) { - // returns true before actually in game so we can't only use this check - while (me.ingame) { - // returns false when switching acts so we can't use while - if (me.gameReady) { - Starter.isUp = "yes"; - - if (!Starter.inGame) { - Starter.gameStart = getTickCount(); - Starter.lastGameStatus = "ingame"; - Starter.inGame = true; - DataFile.updateStats("runs", Starter.gameCount); - DataFile.updateStats("ingameTick"); - Developer.logPerformance && Tracker.update((getTickCount() - oogTick)); - oogTick = 0; - D2Bot.updateStatus("In-Game :: Initializing threads..."); - } else { - // Tracker - if (Developer.logPerformance) { - if (getTickCount() - Tracker.tick > Time.minutes(3)) { - Tracker.tick = getTickCount(); - - try { - Tracker.update(); - } catch (e) { - console.error(e); - } - } - } - } - } - - delay(1000); - } - - // was in game so start recording oog time - Starter.inGame && oogTick === 0 && (oogTick = getTickCount()); - Starter.isUp = "no"; - - try { - let loc = getLocation(); - (locations[loc] !== undefined) && locations[loc](loc); - } catch (e) { - console.error(e, "LOCATION: " + getLocation()); - } - delay(1000); - } + debugLog(me.profile); + addEventListener("copydata", Starter.receiveCopyData); + addEventListener("scriptmsg", Starter.scriptMsgEvent); + + let oogTick = getTickCount(); + + while (!Starter.handle) { + delay(3); + } + + DataFile.updateStats("handle", Starter.handle); + D2Bot.handle = Starter.handle; + delay(500); + + load("threads/heartbeat.js"); + + if (Profile().type === sdk.game.profiletype.TcpIpJoin) { + D2Bot.printToConsole("TcpJoin is unsupported."); + D2Bot.stop(); + } + + Starter.gameCount = (DataFile.getStats().runs + 1 || 1); + + while (!Object.keys(Starter.gameInfo).length) { + delay(rand(200, 1500)); + D2Bot.requestGameInfo(); + delay(500); + } + + if (Starter.gameInfo.error) { + ControlAction.timeoutDelay("Crash Delay", Starter.Config.CrashDelay * 1e3); + Starter.BNET && D2Bot.updateRuns(); + } + + DataFile.updateStats("debugInfo", JSON.stringify({ currScript: "none", area: "out of game" })); + + while (!Object.keys(Starter.profileInfo).length) { + delay(rand(200, 1500)); + D2Bot.getProfile(); + delay(500); + } + + while (true) { + // returns true before actually in game so we can't only use this check + while (me.ingame) { + // returns false when switching acts so we can't use while + if (me.gameReady) { + Starter.isUp = "yes"; + + if (!Starter.inGame) { + Starter.gameStart = getTickCount(); + Starter.lastGameStatus = "ingame"; + Starter.inGame = true; + DataFile.updateStats("runs", Starter.gameCount); + DataFile.updateStats("ingameTick"); + Developer.logPerformance && Tracker.update((getTickCount() - oogTick)); + oogTick = 0; + D2Bot.updateStatus("In-Game :: Initializing threads..."); + } else { + // Tracker + if (Developer.logPerformance) { + if (getTickCount() - Tracker.tick > Time.minutes(3)) { + Tracker.tick = getTickCount(); + + try { + Tracker.update(); + } catch (e) { + console.error(e); + } + } + } + } + } + + delay(1000); + } + + // was in game so start recording oog time + Starter.inGame && oogTick === 0 && (oogTick = getTickCount()); + Starter.isUp = "no"; + + try { + let loc = getLocation(); + (locations[loc] !== undefined) && locations[loc](loc); + } catch (e) { + console.error(e, "LOCATION: " + getLocation()); + } + delay(1000); + } } diff --git a/default.dbj b/default.dbj index 3d9d19d6..fe7eb367 100644 --- a/default.dbj +++ b/default.dbj @@ -3,6 +3,7 @@ * @author kolton, theBGuy * @desc gets executed upon gamejoin, main thread for bot * +* @typedef {import("./sdk/globals")} */ js_strict(true); include("critical.js"); // required @@ -19,244 +20,256 @@ include("systems/gameaction/GameAction.js"); const LocalChat = require("./libs/modules/LocalChat"); function main () { - D2Bot.init(); // Get D2Bot# handle - D2Bot.ingame(); - - (function (global, original) { - global.load = function (...args) { - original.apply(this, args); - delay(500); - }; - })([].filter.constructor("return this")(), load); - - // wait until game is ready - while (!me.gameReady) { - delay(50); - } - - clearAllEvents(); // remove any event listeners from game crash - - // load heartbeat if it isn't already running - !getScript("threads/heartbeat.js") && load("threads/heartbeat.js"); - - // SoloPlay runs in it's own thread - check to ensure it exists in the files - if (getScript("D2BotSoloPlay.dbj") && FileTools.exists("libs/SoloPlay/SoloPlay.js")) { - load("libs/SoloPlay/SoloPlay.js"); - return true; - } - - // map mode runs in it's own thread - if (getScript("d2botmap.dbj")) { - load("libs/manualplay/threads/mapthread.js"); - return true; - } - - // MuleLogger handler - if (MuleLogger.inGameCheck()) return true; - - // don't load default for dropper/mules - if (getScript("D2BotDropper.dbj") || getScript("D2BotMule.dbj")) { - FileTools.exists("libs/ItemDB.js") && include("ItemDB.js"); - load("threads/AreaWatcher.js"); - - while (true) { - delay(1000); - } - } - - let sojPause; - let sojCounter = 0; - let startTime = getTickCount(); - - this.scriptEvent = function (msg) { - if (msg === "quit") return; - if (typeof msg === "string" && msg === "soj") { - sojPause = true; - sojCounter = 0; - } - }; - - this.copyDataEvent = function (mode, msg) { - // "Mule Profile" option from D2Bot# - if (mode === 0 && msg === "mule") { - if (AutoMule.getInfo() && AutoMule.getInfo().hasOwnProperty("muleInfo")) { - if (AutoMule.getMuleItems().length > 0) { - D2Bot.printToConsole("Mule triggered"); - scriptBroadcast("mule"); - scriptBroadcast("quit"); - } else { - D2Bot.printToConsole("No items to mule."); - } - } else { - D2Bot.printToConsole("Profile not enabled for muling."); - } - } - - // getProfile - if (mode === 1638) { - msg = JSON.parse(msg); - - if (msg.Tag) { - GameAction.init(msg.Tag); - } - } - }; - - // Initialize libs - load config variables, build pickit list, attacks, containers and cubing and runeword recipes - Config.init(true); - Pickit.init(true); - Attack.init(); - Storage.Init(); - CraftingSystem.buildLists(); - Runewords.init(); - Cubing.init(); - LocalChat.init(); - - // Load event listeners - addEventListener("scriptmsg", this.scriptEvent); - addEventListener("copydata", this.copyDataEvent); - - // GameAction/AutoMule/TorchSystem/Gambling/Crafting handler - if (GameAction.inGameCheck() || AutoMule.inGameCheck() || TorchSystem.inGameCheck() || Gambling.inGameCheck() || CraftingSystem.inGameCheck()) { - return true; - } - - me.maxgametime = Config.MaxGameTime * 1000; - let stats = DataFile.getStats(); - - // Check for experience decrease -> log death. Skip report if life chicken is disabled. - if (stats.name === me.name && me.getStat(sdk.stats.Experience) < stats.experience && Config.LifeChicken > 0) { - D2Bot.printToConsole("You died in last game. | Area :: " + stats.lastArea + " | Script :: " + stats.debugInfo.currScript, sdk.colors.D2Bot.Red); - D2Bot.printToConsole("Experience decreased by " + (stats.experience - me.getStat(sdk.stats.Experience)), sdk.colors.D2Bot.Red); - DataFile.updateStats("deaths"); - D2Bot.updateDeaths(); - } - - DataFile.updateStats(["experience", "name"]); - - // Load threads - load("threads/ToolsThread.js"); - (Config.TownCheck || Config.TownHP > 0 || Config.TownMP > 0) && load("threads/TownChicken.js"); - - if (Config.DebugMode && FileTools.exists("libs/modules/Guard.js")) { - require("libs/modules/Guard"); - } - - if (Config.PublicMode) { - Config.PublicMode === true ? require("libs/modules/SimpleParty") : load("threads/Party.js"); - } - - Config.AntiHostile && load("threads/AntiHostile.js"); - - if (Config.FastPick) { - print("ÿc2Fast pickit active."); - addEventListener("itemaction", Pickit.itemEvent); - } - - // One time maintenance - check cursor, get corpse, clear leftover items, pick items in case anything important was dropped - if (!Scripts.UserAddon && !Scripts.Test) { - // main checks - Cubing.cursorCheck(); - Town.getCorpse(); - Town.clearBelt(); - Pather.init(); // initialize wp data - - let {x, y} = me; - Config.ClearInvOnStart && Town.clearInventory(); - [x, y].distance > 3 && Pather.moveTo(x, y); - Pickit.pickItems(); - me.hpPercent <= 10 && Town.heal() && me.cancelUIFlags(); - - if (Config.DebugMode) { - delay(2000); - getThreads() - .sort((a, b) => b.memory - a.memory) - .forEach(thread => console.debug(thread)); - } - } - - me.automap = Config.AutoMap; - - // Next game = drop keys - TorchSystem.keyCheck() && scriptBroadcast("torch"); - - // Auto skill and stat - if (Config.AutoSkill.Enabled && include("core/Auto/AutoSkill.js")) { - AutoSkill.init(Config.AutoSkill.Build, Config.AutoSkill.Save); - } - - if (Config.AutoStat.Enabled && include("core/Auto/AutoStat.js")) { - AutoStat.init(Config.AutoStat.Build, Config.AutoStat.Save, Config.AutoStat.BlockChance, Config.AutoStat.UseBulk); - } - - // offline - !me.realm && D2Bot.updateRuns(); - - // Go - Loader.init(); - - if (Config.MinGameTime && getTickCount() - startTime < Config.MinGameTime * 1000) { - try { - Town.goToTown(); - - while (getTickCount() - startTime < Config.MinGameTime * 1000) { - me.overhead("Stalling for " + Math.round(((startTime + (Config.MinGameTime * 1000)) - getTickCount()) / 1000) + " Seconds"); - delay(1000); - } - } catch (e1) { - print(e1); - } - } - - DataFile.updateStats("gold"); - - if (sojPause) { - try { - Town.doChores(); - me.maxgametime = 0; - - while (sojCounter < Config.SoJWaitTime) { - me.overhead("Waiting for SoJ sales... " + (Config.SoJWaitTime - sojCounter) + " min"); - delay(6e4); - - sojCounter += 1; - } - } catch (e2) { - print(e2); - } - } - - if (Config.LastMessage) { - switch (typeof Config.LastMessage) { - case "string": - say(Config.LastMessage.replace("$nextgame", DataFile.getStats().nextGame, "i")); - - break; - case "object": - for (let i = 0; i < Config.LastMessage.length; i += 1) { - say(Config.LastMessage[i].replace("$nextgame", DataFile.getStats().nextGame, "i")); - } - - break; - } - } - - removeEventListener("scriptmsg", this.scriptEvent); - - AutoMule.muleCheck() && scriptBroadcast("mule"); - CraftingSystem.checkFullSets() && scriptBroadcast("crafting"); - TorchSystem.keyCheck() && scriptBroadcast("torch"); - - // Anni handler. Mule Anni if it's in unlocked space and profile is set to mule torch/anni. - let anni = me.findItem(sdk.items.SmallCharm, sdk.items.mode.inStorage, -1, sdk.items.quality.Unique); - - if (anni && !Storage.Inventory.IsLocked(anni, Config.Inventory) && AutoMule.getInfo() && AutoMule.getInfo().hasOwnProperty("torchMuleInfo")) { - scriptBroadcast("muleAnni"); - } - - removeEventListener("copydata", this.copyDataEvent); - - scriptBroadcast("quit"); - - return true; + D2Bot.init(); // Get D2Bot# handle + D2Bot.ingame(); + + (function (global, original) { + global.load = function (...args) { + original.apply(this, args); + delay(500); + }; + })([].filter.constructor("return this")(), load); + + // wait until game is ready + while (!me.gameReady) { + delay(50); + } + + clearAllEvents(); // remove any event listeners from game crash + + // load heartbeat if it isn't already running + !getScript("threads/heartbeat.js") && load("threads/heartbeat.js"); + + // SoloPlay runs in it's own thread - check to ensure it exists in the files + if (getScript("D2BotSoloPlay.dbj") && FileTools.exists("libs/SoloPlay/SoloPlay.js")) { + load("libs/SoloPlay/SoloPlay.js"); + return true; + } + + // map mode runs in it's own thread + if (getScript("d2botmap.dbj")) { + load("libs/manualplay/threads/mapthread.js"); + return true; + } + + // MuleLogger handler + if (MuleLogger.inGameCheck()) return true; + + // don't load default for dropper/mules + if (getScript("D2BotDropper.dbj") || getScript("D2BotMule.dbj")) { + FileTools.exists("libs/ItemDB.js") && include("ItemDB.js"); + load("threads/AreaWatcher.js"); + + while (me.ingame) { + delay(10000); + } + return true; + } + + let sojPause; + let sojCounter = 0; + let startTime = getTickCount(); + + this.scriptEvent = function (msg) { + if (msg === "quit") return; + if (typeof msg === "string" && msg === "soj") { + sojPause = true; + sojCounter = 0; + } + }; + + this.copyDataEvent = function (mode, msg) { + // "Mule Profile" option from D2Bot# + if (mode === 0 && msg === "mule") { + if (AutoMule.getInfo() && AutoMule.getInfo().hasOwnProperty("muleInfo")) { + if (AutoMule.getMuleItems().length > 0) { + D2Bot.printToConsole("Mule triggered"); + scriptBroadcast("mule"); + scriptBroadcast("quit"); + } else { + D2Bot.printToConsole("No items to mule."); + } + } else { + D2Bot.printToConsole("Profile not enabled for muling."); + } + } + + // getProfile + if (mode === 1638) { + msg = JSON.parse(msg); + + if (msg.Tag) { + GameAction.init(msg.Tag); + } + } + }; + + // Initialize libs - load config variables, build pickit list, attacks, containers and cubing and runeword recipes + Config.init(true); + Pickit.init(true); + Attack.init(); + Storage.Init(); + CraftingSystem.buildLists(); + Runewords.init(); + Cubing.init(); + LocalChat.init(); + + // Load event listeners + addEventListener("scriptmsg", this.scriptEvent); + addEventListener("copydata", this.copyDataEvent); + + // GameAction/AutoMule/TorchSystem/Gambling/Crafting handler + if (GameAction.inGameCheck() + || AutoMule.inGameCheck() + || TorchSystem.inGameCheck() + || Gambling.inGameCheck() + || CraftingSystem.inGameCheck()) { + return true; + } + + me.maxgametime = Config.MaxGameTime * 1000; + let stats = DataFile.getStats(); + + // Check for experience decrease -> log death. Skip report if life chicken is disabled. + if (stats.name === me.name && me.getStat(sdk.stats.Experience) < stats.experience && Config.LifeChicken > 0) { + D2Bot.printToConsole( + "You died in last game. | Area :: " + stats.lastArea + "\n" + + "Experience decreased by " + (stats.experience - me.getStat(sdk.stats.Experience)), + sdk.colors.D2Bot.Red + ); + DataFile.updateStats("deaths"); + D2Bot.updateDeaths(); + } + + DataFile.updateStats(["experience", "name"]); + + // Load threads + load("threads/ToolsThread.js"); + (Config.TownCheck || Config.TownHP > 0 || Config.TownMP > 0) && load("threads/TownChicken.js"); + + if (Config.DebugMode.Stack && FileTools.exists("libs/modules/Guard.js")) { + require("libs/modules/Guard"); + } + + if (Config.PublicMode) { + Config.PublicMode === true ? require("libs/modules/SimpleParty") : load("threads/Party.js"); + } + + Config.AntiHostile && load("threads/AntiHostile.js"); + + if (Config.FastPick) { + print("ÿc2Fast pickit active."); + addEventListener("itemaction", Pickit.itemEvent); + } + + // One time maintenance - check cursor, get corpse, clear leftover items, pick items in case anything important was dropped + if (!Scripts.UserAddon && !Scripts.Test) { + // main checks + Cubing.cursorCheck(); + Town.getCorpse(); + Town.clearBelt(); + Pather.init(); // initialize wp data + + let { x, y } = me; + Config.ClearInvOnStart && Town.clearInventory(); + [x, y].distance > 3 && Pather.moveTo(x, y); + Pickit.pickItems(); + me.hpPercent <= 10 && Town.heal() && me.cancelUIFlags(); + + if (Config.DebugMode.Memory) { + delay(2000); + getThreads() + .sort((a, b) => b.memory - a.memory) + .forEach(thread => console.debug(thread)); + } + } + + me.automap = Config.AutoMap; + + // Next game = drop keys + TorchSystem.keyCheck() && scriptBroadcast("torch"); + + // Auto skill and stat + if (Config.AutoSkill.Enabled && include("core/Auto/AutoSkill.js")) { + AutoSkill.init(Config.AutoSkill.Build, Config.AutoSkill.Save); + } + + if (Config.AutoStat.Enabled && include("core/Auto/AutoStat.js")) { + AutoStat.init(Config.AutoStat.Build, Config.AutoStat.Save, Config.AutoStat.BlockChance, Config.AutoStat.UseBulk); + } + + // offline + !me.realm && D2Bot.updateRuns(); + + // Go + Loader.init(); + + if (Config.MinGameTime && getTickCount() - startTime < Config.MinGameTime * 1000) { + try { + Town.goToTown(); + + while (getTickCount() - startTime < Config.MinGameTime * 1000) { + me.overhead( + "Stalling for " + + Math.round(((startTime + Time.seconds(Config.MinGameTime)) - getTickCount()) / 1000) + " Seconds" + ); + delay(1000); + } + } catch (e1) { + print(e1); + } + } + + DataFile.updateStats("gold"); + + if (sojPause) { + try { + Town.doChores(); + me.maxgametime = 0; + + while (sojCounter < Config.SoJWaitTime) { + me.overhead("Waiting for SoJ sales... " + (Config.SoJWaitTime - sojCounter) + " min"); + delay(6e4); + + sojCounter += 1; + } + } catch (e2) { + print(e2); + } + } + + if (Config.LastMessage) { + switch (typeof Config.LastMessage) { + case "string": + say(Config.LastMessage.replace("$nextgame", DataFile.getStats().nextGame, "i")); + + break; + case "object": + for (let i = 0; i < Config.LastMessage.length; i += 1) { + say(Config.LastMessage[i].replace("$nextgame", DataFile.getStats().nextGame, "i")); + } + + break; + } + } + + removeEventListener("scriptmsg", this.scriptEvent); + + AutoMule.muleCheck() && scriptBroadcast("mule"); + CraftingSystem.checkFullSets() && scriptBroadcast("crafting"); + TorchSystem.keyCheck() && scriptBroadcast("torch"); + + // Anni handler. Mule Anni if it's in unlocked space and profile is set to mule torch/anni. + let anni = me.findItem(sdk.items.SmallCharm, sdk.items.mode.inStorage, -1, sdk.items.quality.Unique); + + if (anni && !Storage.Inventory.IsLocked(anni, Config.Inventory) + && AutoMule.getInfo() && AutoMule.getInfo().hasOwnProperty("torchMuleInfo")) { + scriptBroadcast("muleAnni"); + } + + removeEventListener("copydata", this.copyDataEvent); + + scriptBroadcast("quit"); + + return true; } diff --git a/libs/SoloPlay/BuildFiles/Runewords/AncientsPledge.js b/libs/SoloPlay/BuildFiles/Runewords/AncientsPledge.js index b1ed2008..c7e80dbf 100644 --- a/libs/SoloPlay/BuildFiles/Runewords/AncientsPledge.js +++ b/libs/SoloPlay/BuildFiles/Runewords/AncientsPledge.js @@ -1,44 +1,44 @@ (function() { - if (!me.checkItem({ name: sdk.locale.items.AncientsPledge }).have && !me.hell) { - // Cube to Ort rune - if (me.normal && !me.getItem(sdk.items.runes.Ort)) { - Config.Recipes.push([Recipe.Rune, "Ral Rune"]); - } + if (!me.checkItem({ name: sdk.locale.items.AncientsPledge }).have && !me.hell) { + // Cube to Ort rune + if (me.normal && !me.getItem(sdk.items.runes.Ort)) { + Config.Recipes.push([Recipe.Rune, "Ral Rune"]); + } - const apRunes = [ - "[name] == RalRune # # [maxquantity] == 1", - "[name] == OrtRune # # [maxquantity] == 1", - "[name] == TalRune # # [maxquantity] == 1", - ]; - NTIP.buildList(apRunes); - } + const apRunes = [ + "[name] == RalRune # # [maxquantity] == 1", + "[name] == OrtRune # # [maxquantity] == 1", + "[name] == TalRune # # [maxquantity] == 1", + ]; + NTIP.buildList(apRunes); + } - const apShields = [ - "me.normal && [name] == largeshield && [flag] != ethereal && [quality] >= normal && [quality] <= superior # [sockets] == 3 # [maxquantity] == 1", - "!me.hell && [name] == kiteshield && [flag] != ethereal && [quality] >= normal && [quality] <= superior # [sockets] == 3 # [maxquantity] == 1", - "([name] == dragonshield || [name] == scutum) && [flag] != ethereal && [quality] >= normal && [quality] <= superior # [sockets] == 3 # [maxquantity] == 1", - ]; - NTIP.buildList(apShields); + const apShields = [ + "me.normal && [name] == largeshield && [flag] != ethereal && [quality] >= normal && [quality] <= superior # [sockets] == 3 # [maxquantity] == 1", + "!me.hell && [name] == kiteshield && [flag] != ethereal && [quality] >= normal && [quality] <= superior # [sockets] == 3 # [maxquantity] == 1", + "([name] == dragonshield || [name] == scutum) && [flag] != ethereal && [quality] >= normal && [quality] <= superior # [sockets] == 3 # [maxquantity] == 1", + ]; + NTIP.buildList(apShields); - if (me.paladin) { - NTIP.addLine("([name] == targe || [name] == rondache || [name] == heraldicshield || [name] == aerinshield || [name] == akarantarge || [name] == akaranrondache || [name] == gildedshield ||[name] == protectorshield || [name] == sacredtarge) && [flag] != ethereal && [quality] >= normal && [quality] <= superior # [fireresist] > 0 && [sockets] == 3"); - Config.Runewords.push([Runeword.AncientsPledge, "targe"]); - Config.Runewords.push([Runeword.AncientsPledge, "rondache"]); - Config.Runewords.push([Runeword.AncientsPledge, "heraldicshield"]); - Config.Runewords.push([Runeword.AncientsPledge, "aerinshield"]); - Config.Runewords.push([Runeword.AncientsPledge, "akarantarge"]); - Config.Runewords.push([Runeword.AncientsPledge, "akaranrondache"]); - Config.Runewords.push([Runeword.AncientsPledge, "protectorshield"]); - Config.Runewords.push([Runeword.AncientsPledge, "gildedshield"]); - Config.Runewords.push([Runeword.AncientsPledge, "sacredtarge"]); + if (me.paladin) { + NTIP.addLine("([name] == targe || [name] == rondache || [name] == heraldicshield || [name] == aerinshield || [name] == akarantarge || [name] == akaranrondache || [name] == gildedshield ||[name] == protectorshield || [name] == sacredtarge) && [flag] != ethereal && [quality] >= normal && [quality] <= superior # [fireresist] > 0 && [sockets] == 3"); + Config.Runewords.push([Runeword.AncientsPledge, "targe"]); + Config.Runewords.push([Runeword.AncientsPledge, "rondache"]); + Config.Runewords.push([Runeword.AncientsPledge, "heraldicshield"]); + Config.Runewords.push([Runeword.AncientsPledge, "aerinshield"]); + Config.Runewords.push([Runeword.AncientsPledge, "akarantarge"]); + Config.Runewords.push([Runeword.AncientsPledge, "akaranrondache"]); + Config.Runewords.push([Runeword.AncientsPledge, "protectorshield"]); + Config.Runewords.push([Runeword.AncientsPledge, "gildedshield"]); + Config.Runewords.push([Runeword.AncientsPledge, "sacredtarge"]); - Config.KeepRunewords.push("[type] == auricshields # [fireresist]+[lightresist]+[coldresist]+[poisonresist] >= 187"); - } + Config.KeepRunewords.push("[type] == auricshields # [fireresist]+[lightresist]+[coldresist]+[poisonresist] >= 187"); + } - Config.Runewords.push([Runeword.AncientsPledge, "dragonshield"]); - Config.Runewords.push([Runeword.AncientsPledge, "scutum"]); - Config.Runewords.push([Runeword.AncientsPledge, "kiteshield"]); - Config.Runewords.push([Runeword.AncientsPledge, "largeshield"]); + Config.Runewords.push([Runeword.AncientsPledge, "dragonshield"]); + Config.Runewords.push([Runeword.AncientsPledge, "scutum"]); + Config.Runewords.push([Runeword.AncientsPledge, "kiteshield"]); + Config.Runewords.push([Runeword.AncientsPledge, "largeshield"]); - Config.KeepRunewords.push("[type] == shield # [fireresist]+[lightresist]+[coldresist]+[poisonresist] >= 187"); + Config.KeepRunewords.push("[type] == shield # [fireresist]+[lightresist]+[coldresist]+[poisonresist] >= 187"); })(); diff --git a/libs/SoloPlay/BuildFiles/Runewords/Bone.js b/libs/SoloPlay/BuildFiles/Runewords/Bone.js index 40f5b626..4a1ea129 100644 --- a/libs/SoloPlay/BuildFiles/Runewords/Bone.js +++ b/libs/SoloPlay/BuildFiles/Runewords/Bone.js @@ -1,33 +1,33 @@ (function() { - const Bone = [ - "[name] == UmRune # # [maxquantity] == 2", - "[name] == SolRune # # [maxquantity] == 1", - ]; - NTIP.buildList(Bone); + const Bone = [ + "[name] == UmRune # # [maxquantity] == 2", + "[name] == SolRune # # [maxquantity] == 1", + ]; + NTIP.buildList(Bone); - // Cube to Um Rune - if (Item.getQuantityOwned(me.getItem(sdk.items.runes.Um)) < 2) { - Config.Recipes.push([Recipe.Rune, "Ko Rune"]); - Config.Recipes.push([Recipe.Rune, "Fal Rune"]); - Config.Recipes.push([Recipe.Rune, "Lem Rune"]); - Config.Recipes.push([Recipe.Rune, "Pul Rune"]); - } + // Cube to Um Rune + if (Item.getQuantityOwned(me.getItem(sdk.items.runes.Um)) < 2) { + Config.Recipes.push([Recipe.Rune, "Ko Rune"]); + Config.Recipes.push([Recipe.Rune, "Fal Rune"]); + Config.Recipes.push([Recipe.Rune, "Lem Rune"]); + Config.Recipes.push([Recipe.Rune, "Pul Rune"]); + } - // Have Um rune before looking for base - if (me.getItem(sdk.items.runes.Um)) { - NTIP.addLine( - "([name] == demonhidearmor || [name] == duskshroud || [name] == ghostarmor || [name] == lightplate || [name] == mageplate || [name] == serpentskinarmor || [name] == trellisedarmor || [name] == wyrmhide) && [flag] != ethereal && [quality] >= normal && [quality] <= superior # [sockets] == 2 # [maxquantity] == 1" - ); - } + // Have Um rune before looking for base + if (me.getItem(sdk.items.runes.Um)) { + NTIP.addLine( + "([name] == demonhidearmor || [name] == duskshroud || [name] == ghostarmor || [name] == lightplate || [name] == mageplate || [name] == serpentskinarmor || [name] == trellisedarmor || [name] == wyrmhide) && [flag] != ethereal && [quality] >= normal && [quality] <= superior # [sockets] == 2 # [maxquantity] == 1" + ); + } - Config.Runewords.push([Runeword.Bone, "demonhidearmor"]); - Config.Runewords.push([Runeword.Bone, "duskshroud"]); - Config.Runewords.push([Runeword.Bone, "ghostarmor"]); - Config.Runewords.push([Runeword.Bone, "lightplate"]); - Config.Runewords.push([Runeword.Bone, "mageplate"]); - Config.Runewords.push([Runeword.Bone, "serpentskinarmor"]); - Config.Runewords.push([Runeword.Bone, "trellisedarmor"]); - Config.Runewords.push([Runeword.Bone, "wyrmhide"]); + Config.Runewords.push([Runeword.Bone, "demonhidearmor"]); + Config.Runewords.push([Runeword.Bone, "duskshroud"]); + Config.Runewords.push([Runeword.Bone, "ghostarmor"]); + Config.Runewords.push([Runeword.Bone, "lightplate"]); + Config.Runewords.push([Runeword.Bone, "mageplate"]); + Config.Runewords.push([Runeword.Bone, "serpentskinarmor"]); + Config.Runewords.push([Runeword.Bone, "trellisedarmor"]); + Config.Runewords.push([Runeword.Bone, "wyrmhide"]); - Config.KeepRunewords.push("[type] == armor # [fireresist] == 30 && [normaldamagereduction] == 7"); + Config.KeepRunewords.push("[type] == armor # [fireresist] == 30 && [normaldamagereduction] == 7"); })(); diff --git a/libs/SoloPlay/BuildFiles/Runewords/BreathOfTheDying.js b/libs/SoloPlay/BuildFiles/Runewords/BreathOfTheDying.js index 4e43cff7..beb42ad3 100644 --- a/libs/SoloPlay/BuildFiles/Runewords/BreathOfTheDying.js +++ b/libs/SoloPlay/BuildFiles/Runewords/BreathOfTheDying.js @@ -1,22 +1,22 @@ (function() { - const BoTD = [ - "[name] == VexRune", - "me.diff == 2 && [name] == HelRune # # [maxquantity] == 1", - "[name] == ElRune # # [maxquantity] == 1", - "[name] == EldRune # # [maxquantity] == 1", - "[name] == ZodRune", - "me.diff == 2 && [name] == EthRune # # [maxquantity] == 1", - "[name] == colossusblade && [flag] == ethereal && [quality] >= normal && [quality] <= superior # [sockets] == 6 # [maxquantity] == 1", - ]; - NTIP.buildList(BoTD); + const BoTD = [ + "[name] == VexRune", + "me.diff == 2 && [name] == HelRune # # [maxquantity] == 1", + "[name] == ElRune # # [maxquantity] == 1", + "[name] == EldRune # # [maxquantity] == 1", + "[name] == ZodRune", + "me.diff == 2 && [name] == EthRune # # [maxquantity] == 1", + "[name] == colossusblade && [flag] == ethereal && [quality] >= normal && [quality] <= superior # [sockets] == 6 # [maxquantity] == 1", + ]; + NTIP.buildList(BoTD); - // Have Zod rune but do not have a base yet - if (!Check.haveBase("colossusblade", 6) && me.getItem(sdk.items.runes.Zod)) { - NTIP.addLine("[name] == colossusblade && [flag] == ethereal && [quality] == normal # [sockets] == 0 # [maxquantity] == 1"); - } + // Have Zod rune but do not have a base yet + if (!Check.haveBase("colossusblade", 6) && me.getItem(sdk.items.runes.Zod)) { + NTIP.addLine("[name] == colossusblade && [flag] == ethereal && [quality] == normal # [sockets] == 0 # [maxquantity] == 1"); + } - Config.Recipes.push([Recipe.Socket.Weapon, "colossusblade", Roll.Eth]); - Config.Runewords.push([Runeword.BreathoftheDying, "colossusblade"]); + Config.Recipes.push([Recipe.Socket.Weapon, "colossusblade", Roll.Eth]); + Config.Runewords.push([Runeword.BreathoftheDying, "colossusblade"]); - Config.KeepRunewords.push("[type] == sword # [ias] >= 60 && [enhanceddamage] >= 350"); + Config.KeepRunewords.push("[type] == sword # [ias] >= 60 && [enhanceddamage] >= 350"); })(); diff --git a/libs/SoloPlay/BuildFiles/Runewords/CallToArms.js b/libs/SoloPlay/BuildFiles/Runewords/CallToArms.js index 8de2ceda..b5e6a5e4 100644 --- a/libs/SoloPlay/BuildFiles/Runewords/CallToArms.js +++ b/libs/SoloPlay/BuildFiles/Runewords/CallToArms.js @@ -1,43 +1,43 @@ (function() { - const CTA = [ - "[name] == AmnRune # # [maxquantity] == 1", - "[name] == RalRune # # [maxquantity] == 1", - "[name] == MalRune", - "[name] == IstRune", - "[name] == OhmRune", - ]; - NTIP.buildList(CTA); + const CTA = [ + "[name] == AmnRune # # [maxquantity] == 1", + "[name] == RalRune # # [maxquantity] == 1", + "[name] == MalRune", + "[name] == IstRune", + "[name] == OhmRune", + ]; + NTIP.buildList(CTA); - // Have Ohm before collecting base - if (me.getItem(sdk.items.runes.Ohm)) { - NTIP.addLine("[name] == crystalsword && [quality] >= normal && [quality] <= superior # [sockets] == 5 # [maxquantity] == 1"); + // Have Ohm before collecting base + if (me.getItem(sdk.items.runes.Ohm)) { + NTIP.addLine("[name] == crystalsword && [quality] >= normal && [quality] <= superior # [sockets] == 5 # [maxquantity] == 1"); - // Have Ohm+Mal+Ist rune but do not have a base yet - if (me.getItem(sdk.items.runes.Ist) && me.getItem(sdk.items.runes.Mal) && !Check.haveBase("crystalsword", 5)) { - NTIP.addLine("[name] == crystalsword && [quality] == normal # [sockets] == 0 # [maxquantity] == 1"); - Config.Recipes.push([Recipe.Socket.Weapon, "crystalsword"]); - } - } + // Have Ohm+Mal+Ist rune but do not have a base yet + if (me.getItem(sdk.items.runes.Ist) && me.getItem(sdk.items.runes.Mal) && !Check.haveBase("crystalsword", 5)) { + NTIP.addLine("[name] == crystalsword && [quality] == normal # [sockets] == 0 # [maxquantity] == 1"); + Config.Recipes.push([Recipe.Socket.Weapon, "crystalsword"]); + } + } - // Cube to Mal rune - if (!me.getItem(sdk.items.runes.Mal)) { - Config.Recipes.push([Recipe.Rune, "Um Rune"]); - } + // Cube to Mal rune + if (!me.getItem(sdk.items.runes.Mal)) { + Config.Recipes.push([Recipe.Rune, "Um Rune"]); + } - // Cube to Ohm Rune - if (!me.getItem(sdk.items.runes.Ohm)) { - Config.Recipes.push([Recipe.Rune, "Lem Rune"]); - Config.Recipes.push([Recipe.Rune, "Pul Rune"]); - Config.Recipes.push([Recipe.Rune, "Um Rune"]); - Config.Recipes.push([Recipe.Rune, "Mal Rune"]); - Config.Recipes.push([Recipe.Rune, "Ist Rune"]); - Config.Recipes.push([Recipe.Rune, "Gul Rune"]); + // Cube to Ohm Rune + if (!me.getItem(sdk.items.runes.Ohm)) { + Config.Recipes.push([Recipe.Rune, "Lem Rune"]); + Config.Recipes.push([Recipe.Rune, "Pul Rune"]); + Config.Recipes.push([Recipe.Rune, "Um Rune"]); + Config.Recipes.push([Recipe.Rune, "Mal Rune"]); + Config.Recipes.push([Recipe.Rune, "Ist Rune"]); + Config.Recipes.push([Recipe.Rune, "Gul Rune"]); - if (me.checkItem({ name: sdk.locale.items.HeartoftheOak }).have || ["Zealer", "Smiter", "Auradin", "Meteorb", "Blizzballer", "Cold"].includes(SetUp.finalBuild)) { - Config.Recipes.push([Recipe.Rune, "Vex Rune"]); - } - } + if (me.checkItem({ name: sdk.locale.items.HeartoftheOak }).have || ["Zealer", "Smiter", "Auradin", "Meteorb", "Blizzballer", "Cold"].includes(SetUp.finalBuild)) { + Config.Recipes.push([Recipe.Rune, "Vex Rune"]); + } + } - Config.Runewords.push([Runeword.CallToArms, "crystalsword"]); - Config.KeepRunewords.push("[type] == sword # [plusskillbattleorders] >= 1"); + Config.Runewords.push([Runeword.CallToArms, "crystalsword"]); + Config.KeepRunewords.push("[type] == sword # [plusskillbattleorders] >= 1"); })(); diff --git a/libs/SoloPlay/BuildFiles/Runewords/ChainsOfHonor.js b/libs/SoloPlay/BuildFiles/Runewords/ChainsOfHonor.js index b91a16c0..bd7d8140 100644 --- a/libs/SoloPlay/BuildFiles/Runewords/ChainsOfHonor.js +++ b/libs/SoloPlay/BuildFiles/Runewords/ChainsOfHonor.js @@ -1,53 +1,53 @@ (function() { - const CoH = [ - "[name] == DolRune # # [maxquantity] == 1", - "[name] == UmRune", - "[name] == BerRune", - "[name] == IstRune", - ]; - NTIP.buildList(CoH); - - // Cube to Ber rune - if (!me.getItem(sdk.items.runes.Ber)) { - if (me.checkItem({ name: sdk.locale.items.CalltoArms }).have || ["Plaguewolf", "Wolf", "Uberconc"].includes(SetUp.finalBuild)) { - Config.Recipes.push([Recipe.Rune, "Mal Rune"]); - Config.Recipes.push([Recipe.Rune, "Ist Rune"]); - Config.Recipes.push([Recipe.Rune, "Gul Rune"]); - Config.Recipes.push([Recipe.Rune, "Vex Rune"]); - Config.Recipes.push([Recipe.Rune, "Ohm Rune"]); - } - - if (me.checkItem({ name: sdk.locale.items.Grief }).have || ["Uberconc"].indexOf(SetUp.finalBuild) === -1) { - Config.Recipes.push([Recipe.Rune, "Lo Rune"]); - } - - Config.Recipes.push([Recipe.Rune, "Sur Rune"]); - } - - // Cube to Um rune - if (!me.getItem(sdk.items.runes.Um)) { - Config.Recipes.push([Recipe.Rune, "Lem Rune"]); - Config.Recipes.push([Recipe.Rune, "Pul Rune"]); - } - - // Have Ber rune before looking for normal base - if (me.getItem(sdk.items.runes.Ber)) { - if (!Check.haveBase("armor", 4)) { - NTIP.addLine("([name] == archonplate || [name] == duskshroud || [name] == wyrmhide) && [flag] != ethereal && [quality] >= normal && [quality] <= superior # [sockets] == 0 # [maxquantity] == 1"); - } - - NTIP.addLine("([name] == archonplate || [name] == duskshroud || [name] == wyrmhide) && [flag] != ethereal && [quality] >= normal && [quality] <= superior # [sockets] == 4 # [maxquantity] == 1"); - } else { - NTIP.addLine("([name] == archonplate || [name] == duskshroud || [name] == wyrmhide) && [flag] != ethereal && [quality] == superior # [enhanceddefense] >= 10 && [sockets] == 4 # [maxquantity] == 1"); - } - - Config.Recipes.push([Recipe.Socket.Armor, "archonplate", Roll.NonEth]); - Config.Recipes.push([Recipe.Socket.Armor, "duskshroud", Roll.NonEth]); - Config.Recipes.push([Recipe.Socket.Armor, "wyrmhide", Roll.NonEth]); - - Config.Runewords.push([Runeword.ChainsofHonor, "archonplate"]); - Config.Runewords.push([Runeword.ChainsofHonor, "duskshroud"]); - Config.Runewords.push([Runeword.ChainsofHonor, "wyrmhide"]); - - Config.KeepRunewords.push("[type] == armor # [fireresist] == 65 && [hpregen] == 7"); + const CoH = [ + "[name] == DolRune # # [maxquantity] == 1", + "[name] == UmRune", + "[name] == BerRune", + "[name] == IstRune", + ]; + NTIP.buildList(CoH); + + // Cube to Ber rune + if (!me.getItem(sdk.items.runes.Ber)) { + if (me.checkItem({ name: sdk.locale.items.CalltoArms }).have || ["Plaguewolf", "Wolf", "Uberconc"].includes(SetUp.finalBuild)) { + Config.Recipes.push([Recipe.Rune, "Mal Rune"]); + Config.Recipes.push([Recipe.Rune, "Ist Rune"]); + Config.Recipes.push([Recipe.Rune, "Gul Rune"]); + Config.Recipes.push([Recipe.Rune, "Vex Rune"]); + Config.Recipes.push([Recipe.Rune, "Ohm Rune"]); + } + + if (me.checkItem({ name: sdk.locale.items.Grief }).have || ["Uberconc"].indexOf(SetUp.finalBuild) === -1) { + Config.Recipes.push([Recipe.Rune, "Lo Rune"]); + } + + Config.Recipes.push([Recipe.Rune, "Sur Rune"]); + } + + // Cube to Um rune + if (!me.getItem(sdk.items.runes.Um)) { + Config.Recipes.push([Recipe.Rune, "Lem Rune"]); + Config.Recipes.push([Recipe.Rune, "Pul Rune"]); + } + + // Have Ber rune before looking for normal base + if (me.getItem(sdk.items.runes.Ber)) { + if (!Check.haveBase("armor", 4)) { + NTIP.addLine("([name] == archonplate || [name] == duskshroud || [name] == wyrmhide) && [flag] != ethereal && [quality] >= normal && [quality] <= superior # [sockets] == 0 # [maxquantity] == 1"); + } + + NTIP.addLine("([name] == archonplate || [name] == duskshroud || [name] == wyrmhide) && [flag] != ethereal && [quality] >= normal && [quality] <= superior # [sockets] == 4 # [maxquantity] == 1"); + } else { + NTIP.addLine("([name] == archonplate || [name] == duskshroud || [name] == wyrmhide) && [flag] != ethereal && [quality] == superior # [enhanceddefense] >= 10 && [sockets] == 4 # [maxquantity] == 1"); + } + + Config.Recipes.push([Recipe.Socket.Armor, "archonplate", Roll.NonEth]); + Config.Recipes.push([Recipe.Socket.Armor, "duskshroud", Roll.NonEth]); + Config.Recipes.push([Recipe.Socket.Armor, "wyrmhide", Roll.NonEth]); + + Config.Runewords.push([Runeword.ChainsofHonor, "archonplate"]); + Config.Runewords.push([Runeword.ChainsofHonor, "duskshroud"]); + Config.Runewords.push([Runeword.ChainsofHonor, "wyrmhide"]); + + Config.KeepRunewords.push("[type] == armor # [fireresist] == 65 && [hpregen] == 7"); })(); diff --git a/libs/SoloPlay/BuildFiles/Runewords/Chaos.js b/libs/SoloPlay/BuildFiles/Runewords/Chaos.js index b5fce4f9..e55695f5 100644 --- a/libs/SoloPlay/BuildFiles/Runewords/Chaos.js +++ b/libs/SoloPlay/BuildFiles/Runewords/Chaos.js @@ -1,14 +1,14 @@ (function() { - const Chaos = [ - "[name] == FalRune # # [maxquantity] == 1", - "[name] == OhmRune", - "[name] == UmRune", - "[name] == suwayyah && [quality] >= normal && [quality] <= superior # ([assassinskills]+[shadowdisciplinesskilltab]+[skillvenom]+[skilldeathsentry]+[skillfade]+[skillshadowmaster]) >= 1 && [sockets] == 3 # [maxquantity] == 1", - "[name] == suwayyah && [quality] == normal # ([assassinskills]+[shadowdisciplinesskilltab]+[skillvenom]+[skilldeathsentry]+[skillfade]+[skillshadowmaster]) >= 1 && [sockets] == 0 # [maxquantity] == 1", - ]; - NTIP.buildList(Chaos); + const Chaos = [ + "[name] == FalRune # # [maxquantity] == 1", + "[name] == OhmRune", + "[name] == UmRune", + "[name] == suwayyah && [quality] >= normal && [quality] <= superior # ([assassinskills]+[shadowdisciplinesskilltab]+[skillvenom]+[skilldeathsentry]+[skillfade]+[skillshadowmaster]) >= 1 && [sockets] == 3 # [maxquantity] == 1", + "[name] == suwayyah && [quality] == normal # ([assassinskills]+[shadowdisciplinesskilltab]+[skillvenom]+[skilldeathsentry]+[skillfade]+[skillshadowmaster]) >= 1 && [sockets] == 0 # [maxquantity] == 1", + ]; + NTIP.buildList(Chaos); - Config.Runewords.push([Runeword.Chaos, "suwayyah"]); + Config.Runewords.push([Runeword.Chaos, "suwayyah"]); - Config.KeepRunewords.push("[type] == assassinclaw # [plusskillwhirlwind] == 1"); + Config.KeepRunewords.push("[type] == assassinclaw # [plusskillwhirlwind] == 1"); })(); diff --git a/libs/SoloPlay/BuildFiles/Runewords/CrescentMoon.js b/libs/SoloPlay/BuildFiles/Runewords/CrescentMoon.js index 671d74c5..fe39f039 100644 --- a/libs/SoloPlay/BuildFiles/Runewords/CrescentMoon.js +++ b/libs/SoloPlay/BuildFiles/Runewords/CrescentMoon.js @@ -1,55 +1,55 @@ (function() { - const Crescent = [ - "[name] == ShaelRune # # [maxquantity] == 2", - "[name] == UmRune", - "[name] == TirRune # # [maxquantity] == 1", - ]; - NTIP.buildList(Crescent); + const Crescent = [ + "[name] == ShaelRune # # [maxquantity] == 2", + "[name] == UmRune", + "[name] == TirRune # # [maxquantity] == 1", + ]; + NTIP.buildList(Crescent); - if (me.barbarian) { - // Cube to Um Rune - if (!me.getItem(sdk.items.runes.Um)) { - Config.Recipes.push([Recipe.Rune, "Pul Rune"]); - } + if (me.barbarian) { + // Cube to Um Rune + if (!me.getItem(sdk.items.runes.Um)) { + Config.Recipes.push([Recipe.Rune, "Pul Rune"]); + } - // Have Shael and Um runes before looking for base - if (me.getItem(sdk.items.runes.Shael) && me.getItem(sdk.items.runes.Um)) { - NTIP.addLine("[type] == sword && [flag] != ethereal && [quality] >= normal && [quality] <= superior && [wsm] <= 10 && [strreq] <= 150 # [sockets] == 3"); - NTIP.addLine("([name] == legendsword || [name] == highlandblade || [name] == balrogblade || [name] == championsword || [name] == colossussword) && [flag] != ethereal && [quality] >= normal && [quality] <= superior # [sockets] == 3"); - } else { - NTIP.addLine("([name] == legendsword || [name] == highlandblade || [name] == balrogblade || [name] == championsword || [name] == colossussword) && [flag] != ethereal && [quality] == superior # [enhanceddamage] >= 5 && [sockets] == 3"); - } + // Have Shael and Um runes before looking for base + if (me.getItem(sdk.items.runes.Shael) && me.getItem(sdk.items.runes.Um)) { + NTIP.addLine("[type] == sword && [flag] != ethereal && [quality] >= normal && [quality] <= superior && [wsm] <= 10 && [strreq] <= 150 # [sockets] == 3"); + NTIP.addLine("([name] == legendsword || [name] == highlandblade || [name] == balrogblade || [name] == championsword || [name] == colossussword) && [flag] != ethereal && [quality] >= normal && [quality] <= superior # [sockets] == 3"); + } else { + NTIP.addLine("([name] == legendsword || [name] == highlandblade || [name] == balrogblade || [name] == championsword || [name] == colossussword) && [flag] != ethereal && [quality] == superior # [enhanceddamage] >= 5 && [sockets] == 3"); + } - Config.Runewords.push([Runeword.CrescentMoon, "dimensionalblade"]); - Config.Runewords.push([Runeword.CrescentMoon, "battlesword"]); - Config.Runewords.push([Runeword.CrescentMoon, "runesword"]); - Config.Runewords.push([Runeword.CrescentMoon, "conquestsword"]); - Config.Runewords.push([Runeword.CrescentMoon, "crypticsword"]); - Config.Runewords.push([Runeword.CrescentMoon, "phaseblade"]); - Config.Runewords.push([Runeword.CrescentMoon, "espandon"]); - Config.Runewords.push([Runeword.CrescentMoon, "tusksword"]); - Config.Runewords.push([Runeword.CrescentMoon, "zweihander"]); - Config.Runewords.push([Runeword.CrescentMoon, "legendsword"]); - Config.Runewords.push([Runeword.CrescentMoon, "highlandblade"]); - Config.Runewords.push([Runeword.CrescentMoon, "balrogblade"]); - Config.Runewords.push([Runeword.CrescentMoon, "championsword"]); - Config.Runewords.push([Runeword.CrescentMoon, "colossussword"]); - } + Config.Runewords.push([Runeword.CrescentMoon, "dimensionalblade"]); + Config.Runewords.push([Runeword.CrescentMoon, "battlesword"]); + Config.Runewords.push([Runeword.CrescentMoon, "runesword"]); + Config.Runewords.push([Runeword.CrescentMoon, "conquestsword"]); + Config.Runewords.push([Runeword.CrescentMoon, "crypticsword"]); + Config.Runewords.push([Runeword.CrescentMoon, "phaseblade"]); + Config.Runewords.push([Runeword.CrescentMoon, "espandon"]); + Config.Runewords.push([Runeword.CrescentMoon, "tusksword"]); + Config.Runewords.push([Runeword.CrescentMoon, "zweihander"]); + Config.Runewords.push([Runeword.CrescentMoon, "legendsword"]); + Config.Runewords.push([Runeword.CrescentMoon, "highlandblade"]); + Config.Runewords.push([Runeword.CrescentMoon, "balrogblade"]); + Config.Runewords.push([Runeword.CrescentMoon, "championsword"]); + Config.Runewords.push([Runeword.CrescentMoon, "colossussword"]); + } - if (me.paladin) { - NTIP.addLine("[name] == phaseblade && [quality] == normal # ([sockets] == 0 || [sockets] == 3) # [maxquantity] == 1"); + if (me.paladin) { + NTIP.addLine("[name] == phaseblade && [quality] == normal # ([sockets] == 0 || [sockets] == 3) # [maxquantity] == 1"); - // Cube to Um rune - if (!me.getItem(sdk.items.runes.Um)) { - Config.Recipes.push([Recipe.Rune, "Ko Rune"]); - Config.Recipes.push([Recipe.Rune, "Fal Rune"]); - Config.Recipes.push([Recipe.Rune, "Lem Rune"]); - Config.Recipes.push([Recipe.Rune, "Pul Rune"]); - } + // Cube to Um rune + if (!me.getItem(sdk.items.runes.Um)) { + Config.Recipes.push([Recipe.Rune, "Ko Rune"]); + Config.Recipes.push([Recipe.Rune, "Fal Rune"]); + Config.Recipes.push([Recipe.Rune, "Lem Rune"]); + Config.Recipes.push([Recipe.Rune, "Pul Rune"]); + } - Config.Recipes.push([Recipe.Socket.Weapon, "phaseblade", Roll.NonEth]); - Config.Runewords.push([Runeword.CrescentMoon, "phaseblade"]); - } + Config.Recipes.push([Recipe.Socket.Weapon, "phaseblade", Roll.NonEth]); + Config.Runewords.push([Runeword.CrescentMoon, "phaseblade"]); + } - Config.KeepRunewords.push("[type] == sword # [ias] >= 20 && [passiveltngpierce] >= 35"); + Config.KeepRunewords.push("[type] == sword # [ias] >= 20 && [passiveltngpierce] >= 35"); })(); diff --git a/libs/SoloPlay/BuildFiles/Runewords/DragonArmor.js b/libs/SoloPlay/BuildFiles/Runewords/DragonArmor.js index 4f298332..03c01a30 100644 --- a/libs/SoloPlay/BuildFiles/Runewords/DragonArmor.js +++ b/libs/SoloPlay/BuildFiles/Runewords/DragonArmor.js @@ -1,20 +1,20 @@ (function() { - const DragonArmor = [ - "[name] == SurRune", - "[name] == LoRune", - "[name] == SolRune # # [maxquantity] == 1", - ]; - NTIP.buildList(DragonArmor); + const DragonArmor = [ + "[name] == SurRune", + "[name] == LoRune", + "[name] == SolRune # # [maxquantity] == 1", + ]; + NTIP.buildList(DragonArmor); - // Cube to Sur rune - if (!me.getItem(sdk.items.runes.Sur)) { - Config.Recipes.push([Recipe.Rune, "Lo Rune"]); - } + // Cube to Sur rune + if (!me.getItem(sdk.items.runes.Sur)) { + Config.Recipes.push([Recipe.Rune, "Lo Rune"]); + } - // Have Sur and Lo rune before attempting to make runeword - if (me.getItem(sdk.items.runes.Lo) && me.getItem(sdk.items.runes.Sur)) { - Config.Runewords.push([Runeword.Dragon, "archonplate", Roll.NonEth]); - } + // Have Sur and Lo rune before attempting to make runeword + if (me.getItem(sdk.items.runes.Lo) && me.getItem(sdk.items.runes.Sur)) { + Config.Runewords.push([Runeword.Dragon, "archonplate", Roll.NonEth]); + } - Config.KeepRunewords.push("[type] == armor # [holyfireaura] >= 14"); + Config.KeepRunewords.push("[type] == armor # [holyfireaura] >= 14"); })(); diff --git a/libs/SoloPlay/BuildFiles/Runewords/DreamHelm.js b/libs/SoloPlay/BuildFiles/Runewords/DreamHelm.js index 9adee6c3..fc3bb762 100644 --- a/libs/SoloPlay/BuildFiles/Runewords/DreamHelm.js +++ b/libs/SoloPlay/BuildFiles/Runewords/DreamHelm.js @@ -1,12 +1,12 @@ (function() { - const DreamHelm = [ - "[name] == IoRune # # [maxquantity] == 1", - "[name] == JahRune", - "[name] == PulRune", - "[name] == bonevisage && [flag] != ethereal && [quality] == superior # [enhanceddefense] >= 15 && [sockets] == 3 # [maxquantity] == 1", - ]; - NTIP.buildList(DreamHelm); + const DreamHelm = [ + "[name] == IoRune # # [maxquantity] == 1", + "[name] == JahRune", + "[name] == PulRune", + "[name] == bonevisage && [flag] != ethereal && [quality] == superior # [enhanceddefense] >= 15 && [sockets] == 3 # [maxquantity] == 1", + ]; + NTIP.buildList(DreamHelm); - Config.Runewords.push([Runeword.Dream, "bonevisage"]); - Config.KeepRunewords.push("[type] == helm # [holyshockaura] >= 15"); + Config.Runewords.push([Runeword.Dream, "bonevisage"]); + Config.KeepRunewords.push("[type] == helm # [holyshockaura] >= 15"); })(); diff --git a/libs/SoloPlay/BuildFiles/Runewords/DreamShield.js b/libs/SoloPlay/BuildFiles/Runewords/DreamShield.js index a95ff9bc..4347c445 100644 --- a/libs/SoloPlay/BuildFiles/Runewords/DreamShield.js +++ b/libs/SoloPlay/BuildFiles/Runewords/DreamShield.js @@ -1,17 +1,17 @@ (function() { - const DreamShield = [ - "[name] == IoRune # # [maxquantity] == 1", - "[name] == JahRune", - "[name] == PulRune", - "[name] == sacredtarge && [flag] != ethereal && [quality] >= normal && [quality] <= superior # [fireresist] >= 45 && [sockets] == 3 # [maxquantity] == 1", - ]; - NTIP.buildList(DreamShield); + const DreamShield = [ + "[name] == IoRune # # [maxquantity] == 1", + "[name] == JahRune", + "[name] == PulRune", + "[name] == sacredtarge && [flag] != ethereal && [quality] >= normal && [quality] <= superior # [fireresist] >= 45 && [sockets] == 3 # [maxquantity] == 1", + ]; + NTIP.buildList(DreamShield); - if (!Check.haveBase("sacredtarge", 3)) { - NTIP.addLine("[name] == sacredtarge && [flag] != ethereal && [quality] == normal # [fireresist] >= 45 && [sockets] == 0 # [maxquantity] == 1"); - } + if (!Check.haveBase("sacredtarge", 3)) { + NTIP.addLine("[name] == sacredtarge && [flag] != ethereal && [quality] == normal # [fireresist] >= 45 && [sockets] == 0 # [maxquantity] == 1"); + } - Config.Recipes.push([Recipe.Socket.Shield, "sacredtarge", Roll.NonEth]); - Config.Runewords.push([Runeword.Dream, "sacredtarge"]); - Config.KeepRunewords.push("[type] == auricshields # [holyshockaura] >= 15"); + Config.Recipes.push([Recipe.Socket.Shield, "sacredtarge", Roll.NonEth]); + Config.Runewords.push([Runeword.Dream, "sacredtarge"]); + Config.KeepRunewords.push("[type] == auricshields # [holyshockaura] >= 15"); })(); diff --git a/libs/SoloPlay/BuildFiles/Runewords/Duress.js b/libs/SoloPlay/BuildFiles/Runewords/Duress.js index c39f4252..ceb95b8a 100644 --- a/libs/SoloPlay/BuildFiles/Runewords/Duress.js +++ b/libs/SoloPlay/BuildFiles/Runewords/Duress.js @@ -1,34 +1,34 @@ (function() { - const Duress = [ - "[name] == ShaelRune # # [maxquantity] == 1", - "[name] == UmRune # # [maxquantity] == 1", - "[name] == ThulRune # # [maxquantity] == 1", - ]; - NTIP.buildList(Duress); + const Duress = [ + "[name] == ShaelRune # # [maxquantity] == 1", + "[name] == UmRune # # [maxquantity] == 1", + "[name] == ThulRune # # [maxquantity] == 1", + ]; + NTIP.buildList(Duress); - // Cube to Um rune - if (!me.getItem(sdk.items.runes.Um)) { - Config.Recipes.push([Recipe.Rune, "Io Rune"]); - Config.Recipes.push([Recipe.Rune, "Lum Rune"]); - Config.Recipes.push([Recipe.Rune, "Ko Rune"]); - Config.Recipes.push([Recipe.Rune, "Fal Rune"]); - Config.Recipes.push([Recipe.Rune, "Lem Rune"]); - Config.Recipes.push([Recipe.Rune, "Pul Rune"]); - } + // Cube to Um rune + if (!me.getItem(sdk.items.runes.Um)) { + Config.Recipes.push([Recipe.Rune, "Io Rune"]); + Config.Recipes.push([Recipe.Rune, "Lum Rune"]); + Config.Recipes.push([Recipe.Rune, "Ko Rune"]); + Config.Recipes.push([Recipe.Rune, "Fal Rune"]); + Config.Recipes.push([Recipe.Rune, "Lem Rune"]); + Config.Recipes.push([Recipe.Rune, "Pul Rune"]); + } - // Have Um and Shael runes before looking for base - if (me.getItem(sdk.items.runes.Um) && me.getItem(sdk.items.runes.Shael)) { - NTIP.addLine("([name] == archonplate || [name] == demonhidearmor || [name] == duskshroud || [name] == ghostarmor || [name] == boneweave || [name] == serpentskinarmor || [name] == trellisedarmor || [name] == wyrmhide) && [flag] != ethereal && [quality] >= normal && [quality] <= superior # [sockets] == 3 # [maxquantity] == 1"); - } + // Have Um and Shael runes before looking for base + if (me.getItem(sdk.items.runes.Um) && me.getItem(sdk.items.runes.Shael)) { + NTIP.addLine("([name] == archonplate || [name] == demonhidearmor || [name] == duskshroud || [name] == ghostarmor || [name] == boneweave || [name] == serpentskinarmor || [name] == trellisedarmor || [name] == wyrmhide) && [flag] != ethereal && [quality] >= normal && [quality] <= superior # [sockets] == 3 # [maxquantity] == 1"); + } - Config.Runewords.push([Runeword.Duress, "archonplate"]); - Config.Runewords.push([Runeword.Duress, "demonhidearmor"]); - Config.Runewords.push([Runeword.Duress, "duskshroud"]); - Config.Runewords.push([Runeword.Duress, "ghostarmor"]); - Config.Runewords.push([Runeword.Duress, "boneweave"]); - Config.Runewords.push([Runeword.Duress, "serpentskinarmor"]); - Config.Runewords.push([Runeword.Duress, "trellisedarmor"]); - Config.Runewords.push([Runeword.Duress, "wyrmhide"]); + Config.Runewords.push([Runeword.Duress, "archonplate"]); + Config.Runewords.push([Runeword.Duress, "demonhidearmor"]); + Config.Runewords.push([Runeword.Duress, "duskshroud"]); + Config.Runewords.push([Runeword.Duress, "ghostarmor"]); + Config.Runewords.push([Runeword.Duress, "boneweave"]); + Config.Runewords.push([Runeword.Duress, "serpentskinarmor"]); + Config.Runewords.push([Runeword.Duress, "trellisedarmor"]); + Config.Runewords.push([Runeword.Duress, "wyrmhide"]); - Config.KeepRunewords.push("[type] == armor # [coldresist] == 45"); + Config.KeepRunewords.push("[type] == armor # [coldresist] == 45"); })(); diff --git a/libs/SoloPlay/BuildFiles/Runewords/Enigma.js b/libs/SoloPlay/BuildFiles/Runewords/Enigma.js index e253ee4a..a54c2ed2 100644 --- a/libs/SoloPlay/BuildFiles/Runewords/Enigma.js +++ b/libs/SoloPlay/BuildFiles/Runewords/Enigma.js @@ -1,45 +1,45 @@ (function() { - const Enigma = [ - "[name] == JahRune", - "[name] == IthRune # # [maxquantity] == 1", - "[name] == BerRune", - ]; - NTIP.buildList(Enigma); + const Enigma = [ + "[name] == JahRune", + "[name] == IthRune # # [maxquantity] == 1", + "[name] == BerRune", + ]; + NTIP.buildList(Enigma); - // Cube to Jah rune - if (!me.getItem(sdk.items.runes.Jah)) { - Config.Recipes.push([Recipe.Rune, "Mal Rune"]); - Config.Recipes.push([Recipe.Rune, "Ist Rune"]); - Config.Recipes.push([Recipe.Rune, "Gul Rune"]); - Config.Recipes.push([Recipe.Rune, "Vex Rune"]); - Config.Recipes.push([Recipe.Rune, "Ohm Rune"]); - Config.Recipes.push([Recipe.Rune, "Lo Rune"]); - Config.Recipes.push([Recipe.Rune, "Sur Rune"]); - Config.Recipes.push([Recipe.Rune, "Ber Rune"]); - } + // Cube to Jah rune + if (!me.getItem(sdk.items.runes.Jah)) { + Config.Recipes.push([Recipe.Rune, "Mal Rune"]); + Config.Recipes.push([Recipe.Rune, "Ist Rune"]); + Config.Recipes.push([Recipe.Rune, "Gul Rune"]); + Config.Recipes.push([Recipe.Rune, "Vex Rune"]); + Config.Recipes.push([Recipe.Rune, "Ohm Rune"]); + Config.Recipes.push([Recipe.Rune, "Lo Rune"]); + Config.Recipes.push([Recipe.Rune, "Sur Rune"]); + Config.Recipes.push([Recipe.Rune, "Ber Rune"]); + } - // Cube to Ber rune - if (!me.getItem(sdk.items.runes.Ber)) { - Config.Recipes.push([Recipe.Rune, "Mal Rune"]); - Config.Recipes.push([Recipe.Rune, "Ist Rune"]); - Config.Recipes.push([Recipe.Rune, "Gul Rune"]); - Config.Recipes.push([Recipe.Rune, "Vex Rune"]); - Config.Recipes.push([Recipe.Rune, "Ohm Rune"]); - Config.Recipes.push([Recipe.Rune, "Lo Rune"]); - Config.Recipes.push([Recipe.Rune, "Sur Rune"]); - } + // Cube to Ber rune + if (!me.getItem(sdk.items.runes.Ber)) { + Config.Recipes.push([Recipe.Rune, "Mal Rune"]); + Config.Recipes.push([Recipe.Rune, "Ist Rune"]); + Config.Recipes.push([Recipe.Rune, "Gul Rune"]); + Config.Recipes.push([Recipe.Rune, "Vex Rune"]); + Config.Recipes.push([Recipe.Rune, "Ohm Rune"]); + Config.Recipes.push([Recipe.Rune, "Lo Rune"]); + Config.Recipes.push([Recipe.Rune, "Sur Rune"]); + } - // Have Ber and Jah runes before looking for normal base - if (me.getItem(sdk.items.runes.Ber) && me.getItem(sdk.items.runes.Jah)) { - Config.Runewords.push([Runeword.Enigma, "mageplate", Roll.NonEth]); - Config.Runewords.push([Runeword.Enigma, "duskshroud", Roll.NonEth]); - Config.Runewords.push([Runeword.Enigma, "wyrmhide", Roll.NonEth]); - Config.Runewords.push([Runeword.Enigma, "scarabhusk", Roll.NonEth]); + // Have Ber and Jah runes before looking for normal base + if (me.getItem(sdk.items.runes.Ber) && me.getItem(sdk.items.runes.Jah)) { + Config.Runewords.push([Runeword.Enigma, "mageplate", Roll.NonEth]); + Config.Runewords.push([Runeword.Enigma, "duskshroud", Roll.NonEth]); + Config.Runewords.push([Runeword.Enigma, "wyrmhide", Roll.NonEth]); + Config.Runewords.push([Runeword.Enigma, "scarabhusk", Roll.NonEth]); - NTIP.addLine("([name] == mageplate || [name] == scarabhusk || [name] == wyrmhide || [name] == duskshroud) && [flag] != ethereal && [quality] >= normal && [quality] <= superior # [sockets] == 3 # [maxquantity] == 1"); - } else { - NTIP.addLine("([name] == mageplate || [name] == scarabhusk || [name] == wyrmhide || [name] == duskshroud) && [flag] != ethereal && [quality] == superior # [enhanceddefense] >= 10 && [sockets] == 3 # [maxquantity] == 1"); - } + NTIP.addLine("([name] == mageplate || [name] == scarabhusk || [name] == wyrmhide || [name] == duskshroud) && [flag] != ethereal && [quality] >= normal && [quality] <= superior # [sockets] == 3 # [maxquantity] == 1"); + } else { + NTIP.addLine("([name] == mageplate || [name] == scarabhusk || [name] == wyrmhide || [name] == duskshroud) && [flag] != ethereal && [quality] == superior # [enhanceddefense] >= 10 && [sockets] == 3 # [maxquantity] == 1"); + } - Config.KeepRunewords.push("[type] == armor # [itemallskills] == 2"); + Config.KeepRunewords.push("[type] == armor # [itemallskills] == 2"); })(); diff --git a/libs/SoloPlay/BuildFiles/Runewords/Exile.js b/libs/SoloPlay/BuildFiles/Runewords/Exile.js index 0293942d..3764c2a3 100644 --- a/libs/SoloPlay/BuildFiles/Runewords/Exile.js +++ b/libs/SoloPlay/BuildFiles/Runewords/Exile.js @@ -1,18 +1,18 @@ (function() { - const Exile = [ - "[name] == VexRune", - "[name] == OhmRune", - "[name] == IstRune", - "[name] == DolRune # # [maxquantity] == 1", - "[name] == sacredtarge && [quality] >= normal && [quality] <= superior # [fireresist] >= 30 && [sockets] == 4 # [maxquantity] == 1", - ]; - NTIP.buildList(Exile); + const Exile = [ + "[name] == VexRune", + "[name] == OhmRune", + "[name] == IstRune", + "[name] == DolRune # # [maxquantity] == 1", + "[name] == sacredtarge && [quality] >= normal && [quality] <= superior # [fireresist] >= 30 && [sockets] == 4 # [maxquantity] == 1", + ]; + NTIP.buildList(Exile); - if (!Check.haveBase("sacredtarge", 4)) { - NTIP.addLine("[name] == sacredtarge && [quality] == normal && [flag] == ethereal # [fireresist] >= 30 && [sockets] == 0 # [maxquantity] == 1"); - } + if (!Check.haveBase("sacredtarge", 4)) { + NTIP.addLine("[name] == sacredtarge && [quality] == normal && [flag] == ethereal # [fireresist] >= 30 && [sockets] == 0 # [maxquantity] == 1"); + } - Config.Recipes.push([Recipe.Socket.Shield, "sacredtarge"]); - Config.Runewords.push([Runeword.Exile, "sacredtarge"]); - Config.KeepRunewords.push("[type] == auricshields # [defianceaura] >= 13"); + Config.Recipes.push([Recipe.Socket.Shield, "sacredtarge"]); + Config.Runewords.push([Runeword.Exile, "sacredtarge"]); + Config.KeepRunewords.push("[type] == auricshields # [defianceaura] >= 13"); })(); diff --git a/libs/SoloPlay/BuildFiles/Runewords/Faith.js b/libs/SoloPlay/BuildFiles/Runewords/Faith.js index 2366d182..f36b8241 100644 --- a/libs/SoloPlay/BuildFiles/Runewords/Faith.js +++ b/libs/SoloPlay/BuildFiles/Runewords/Faith.js @@ -1,57 +1,57 @@ (function() { - const FaithRunes = [ - "[name] == OhmRune", - "[name] == JahRune", - "[name] == EldRune # # [maxquantity] == 1", - "[name] == LemRune # # [maxquantity] == 1", - ]; - NTIP.buildList(FaithRunes); - // Cube to Ohm and Keep cubing to Jah rune - if (Item.getQuantityOwned(me.getItem(sdk.items.runes.Ohm) > 1) && me.checkItem({ name: sdk.locale.items.CalltoArms }).have) { - if (!me.getItem(sdk.items.runes.Jah)) { - Config.Recipes.push([Recipe.Rune, "Mal Rune"]); - Config.Recipes.push([Recipe.Rune, "Ist Rune"]); - Config.Recipes.push([Recipe.Rune, "Gul Rune"]); - Config.Recipes.push([Recipe.Rune, "Vex Rune"]); - } - !me.getItem(sdk.items.runes.Jah) && Config.Recipes.push([Recipe.Rune, "Ohm Rune"]); - } - // Cube to Jah rune - if (!me.getItem(sdk.items.runes.Jah) && me.checkItem({ name: sdk.locale.items.ChainofHonor }).have) { - Config.Recipes.push([Recipe.Rune, "Ohm Rune"]); - Config.Recipes.push([Recipe.Rune, "Lo Rune"]); - Config.Recipes.push([Recipe.Rune, "Sur Rune"]); - Config.Recipes.push([Recipe.Rune, "Ber Rune"]); - } + const FaithRunes = [ + "[name] == OhmRune", + "[name] == JahRune", + "[name] == EldRune # # [maxquantity] == 1", + "[name] == LemRune # # [maxquantity] == 1", + ]; + NTIP.buildList(FaithRunes); + // Cube to Ohm and Keep cubing to Jah rune + if (Item.getQuantityOwned(me.getItem(sdk.items.runes.Ohm) > 1) && me.checkItem({ name: sdk.locale.items.CalltoArms }).have) { + if (!me.getItem(sdk.items.runes.Jah)) { + Config.Recipes.push([Recipe.Rune, "Mal Rune"]); + Config.Recipes.push([Recipe.Rune, "Ist Rune"]); + Config.Recipes.push([Recipe.Rune, "Gul Rune"]); + Config.Recipes.push([Recipe.Rune, "Vex Rune"]); + } + !me.getItem(sdk.items.runes.Jah) && Config.Recipes.push([Recipe.Rune, "Ohm Rune"]); + } + // Cube to Jah rune + if (!me.getItem(sdk.items.runes.Jah) && me.checkItem({ name: sdk.locale.items.ChainofHonor }).have) { + Config.Recipes.push([Recipe.Rune, "Ohm Rune"]); + Config.Recipes.push([Recipe.Rune, "Lo Rune"]); + Config.Recipes.push([Recipe.Rune, "Sur Rune"]); + Config.Recipes.push([Recipe.Rune, "Ber Rune"]); + } - if (me.amazon) { - if (me.getItem(sdk.items.runes.Lo) && me.getItem(sdk.items.runes.Jah)) { - if (!Check.haveBase("amazonbow", 4)) { - NTIP.addLine("[name] == grandmatronbow && [quality] == normal # [bowandcrossbowskilltab] == 3 && [sockets] == 0 # [maxquantity] == 1"); - } + if (me.amazon) { + if (me.getItem(sdk.items.runes.Lo) && me.getItem(sdk.items.runes.Jah)) { + if (!Check.haveBase("amazonbow", 4)) { + NTIP.addLine("[name] == grandmatronbow && [quality] == normal # [bowandcrossbowskilltab] == 3 && [sockets] == 0 # [maxquantity] == 1"); + } - NTIP.addLine("[name] == grandmatronbow && [quality] >= normal && [quality] <= superior # [bowandcrossbowskilltab] >= 1 && [sockets] == 4 # [maxquantity] == 1"); - } else { - NTIP.addLine("[name] == grandmatronbow && [quality] == superior # [bowandcrossbowskilltab] == 3 && [enhanceddamage] >= 5 && [sockets] == 4 # [maxquantity] == 1"); - } + NTIP.addLine("[name] == grandmatronbow && [quality] >= normal && [quality] <= superior # [bowandcrossbowskilltab] >= 1 && [sockets] == 4 # [maxquantity] == 1"); + } else { + NTIP.addLine("[name] == grandmatronbow && [quality] == superior # [bowandcrossbowskilltab] == 3 && [enhanceddamage] >= 5 && [sockets] == 4 # [maxquantity] == 1"); + } - Config.Runewords.push([Runeword.Faith, "grandmatronbow"]); + Config.Runewords.push([Runeword.Faith, "grandmatronbow"]); - Config.Recipes.push([Recipe.Socket.Bow, "grandmatronbow"]); + Config.Recipes.push([Recipe.Socket.Bow, "grandmatronbow"]); - } else { - if (me.getItem(sdk.items.runes.Lo) && me.getItem(sdk.items.runes.Jah)) { - if (!Check.haveBase("amazonbow", 4)) { - NTIP.addLine("([name] == wardbow || [name] == bladebow || [name] == diamonbow) && [quality] == superior # [enhanceddamage] >= 5 && [sockets] == 4 # [maxquantity] == 1"); - } else { - NTIP.addLine("([name] == wardbow || [name] == bladebow || [name] == diamonbow) && [quality] == superior # [enhanceddamage] == 15 && [sockets] == 4 # [maxquantity] == 1"); - } + } else { + if (me.getItem(sdk.items.runes.Lo) && me.getItem(sdk.items.runes.Jah)) { + if (!Check.haveBase("amazonbow", 4)) { + NTIP.addLine("([name] == wardbow || [name] == bladebow || [name] == diamonbow) && [quality] == superior # [enhanceddamage] >= 5 && [sockets] == 4 # [maxquantity] == 1"); + } else { + NTIP.addLine("([name] == wardbow || [name] == bladebow || [name] == diamonbow) && [quality] == superior # [enhanceddamage] == 15 && [sockets] == 4 # [maxquantity] == 1"); + } - Config.Runewords.push([Runeword.Faith, "wardbow"]); - Config.Runewords.push([Runeword.Faith, "bladebow"]); - Config.Runewords.push([Runeword.Faith, "diamonbow"]); - } - } + Config.Runewords.push([Runeword.Faith, "wardbow"]); + Config.Runewords.push([Runeword.Faith, "bladebow"]); + Config.Runewords.push([Runeword.Faith, "diamonbow"]); + } + } - Config.KeepRunewords.push("([type] == bow || [type] == amazonbow) && [flag] == runeword # [fanaticismaura] >= 12 && [itemallskills] >= 1"); + Config.KeepRunewords.push("([type] == bow || [type] == amazonbow) && [flag] == runeword # [fanaticismaura] >= 12 && [itemallskills] >= 1"); })(); diff --git a/libs/SoloPlay/BuildFiles/Runewords/Fortitude.js b/libs/SoloPlay/BuildFiles/Runewords/Fortitude.js index 7c77c4c1..1cda7e87 100644 --- a/libs/SoloPlay/BuildFiles/Runewords/Fortitude.js +++ b/libs/SoloPlay/BuildFiles/Runewords/Fortitude.js @@ -1,38 +1,38 @@ (function() { - const Fortitude = [ - "[name] == ElRune # # [maxquantity] == 1", - "[name] == SolRune # # [maxquantity] == 1", - "[name] == DolRune # # [maxquantity] == 1", - "[name] == LoRune", - ]; - NTIP.buildList(Fortitude); + const Fortitude = [ + "[name] == ElRune # # [maxquantity] == 1", + "[name] == SolRune # # [maxquantity] == 1", + "[name] == DolRune # # [maxquantity] == 1", + "[name] == LoRune", + ]; + NTIP.buildList(Fortitude); - // Have Lo rune before looking for normal base - if (me.getItem(sdk.items.runes.Lo)) { - if (!Check.haveBase("armor", 4)) { - NTIP.addLine("([name] == archonplate || [name] == duskshroud || [name] == wyrmhide) && [flag] != ethereal && [quality] >= normal && [quality] <= superior # [sockets] == 0 # [maxquantity] == 1"); - } + // Have Lo rune before looking for normal base + if (me.getItem(sdk.items.runes.Lo)) { + if (!Check.haveBase("armor", 4)) { + NTIP.addLine("([name] == archonplate || [name] == duskshroud || [name] == wyrmhide) && [flag] != ethereal && [quality] >= normal && [quality] <= superior # [sockets] == 0 # [maxquantity] == 1"); + } - NTIP.addLine("([name] == archonplate || [name] == duskshroud || [name] == wyrmhide) && [flag] != ethereal && [quality] >= normal && [quality] <= superior # [sockets] == 4 # [maxquantity] == 1"); - } else { - NTIP.addLine("([name] == archonplate || [name] == duskshroud || [name] == wyrmhide) && [flag] != ethereal && [quality] == superior # [enhanceddefense] >= 10 && [sockets] == 4 # [maxquantity] == 1"); - } + NTIP.addLine("([name] == archonplate || [name] == duskshroud || [name] == wyrmhide) && [flag] != ethereal && [quality] >= normal && [quality] <= superior # [sockets] == 4 # [maxquantity] == 1"); + } else { + NTIP.addLine("([name] == archonplate || [name] == duskshroud || [name] == wyrmhide) && [flag] != ethereal && [quality] == superior # [enhanceddefense] >= 10 && [sockets] == 4 # [maxquantity] == 1"); + } - // Cube to Lo rune - if (!me.getItem(sdk.items.runes.Lo)) { - Config.Recipes.push([Recipe.Rune, "Ist Rune"]); - Config.Recipes.push([Recipe.Rune, "Gul Rune"]); - Config.Recipes.push([Recipe.Rune, "Vex Rune"]); - Config.Recipes.push([Recipe.Rune, "Ohm Rune"]); - } + // Cube to Lo rune + if (!me.getItem(sdk.items.runes.Lo)) { + Config.Recipes.push([Recipe.Rune, "Ist Rune"]); + Config.Recipes.push([Recipe.Rune, "Gul Rune"]); + Config.Recipes.push([Recipe.Rune, "Vex Rune"]); + Config.Recipes.push([Recipe.Rune, "Ohm Rune"]); + } - Config.Recipes.push([Recipe.Socket.Armor, "archonplate", Roll.NonEth]); - Config.Recipes.push([Recipe.Socket.Armor, "duskshroud", Roll.NonEth]); - Config.Recipes.push([Recipe.Socket.Armor, "wyrmhide", Roll.NonEth]); + Config.Recipes.push([Recipe.Socket.Armor, "archonplate", Roll.NonEth]); + Config.Recipes.push([Recipe.Socket.Armor, "duskshroud", Roll.NonEth]); + Config.Recipes.push([Recipe.Socket.Armor, "wyrmhide", Roll.NonEth]); - Config.Runewords.push([Runeword.Fortitude, "archonplate"]); - Config.Runewords.push([Runeword.Fortitude, "duskshroud"]); - Config.Runewords.push([Runeword.Fortitude, "wyrmhide"]); + Config.Runewords.push([Runeword.Fortitude, "archonplate"]); + Config.Runewords.push([Runeword.Fortitude, "duskshroud"]); + Config.Runewords.push([Runeword.Fortitude, "wyrmhide"]); - Config.KeepRunewords.push("[type] == armor # [enhanceddefense] >= 200 && [enhanceddamage] >= 300"); + Config.KeepRunewords.push("[type] == armor # [enhanceddefense] >= 200 && [enhanceddamage] >= 300"); })(); diff --git a/libs/SoloPlay/BuildFiles/Runewords/Fury.js b/libs/SoloPlay/BuildFiles/Runewords/Fury.js index 8468c59a..700deed6 100644 --- a/libs/SoloPlay/BuildFiles/Runewords/Fury.js +++ b/libs/SoloPlay/BuildFiles/Runewords/Fury.js @@ -1,14 +1,14 @@ (function() { - const Fury = [ - "[name] == JahRune", - "[name] == GulRune", - "[name] == EthRune ## [maxquantity] == 1", - "[name] == suwayyah && [quality] >= normal && [quality] <= superior # ([assassinskills]+[shadowdisciplinesskilltab]+[skillvenom]+[skilldeathsentry]+[skillfade]+[skillshadowmaster]) >= 1 && [sockets] == 3 # [maxquantity] == 1", - "[name] == suwayyah && [quality] == normal # ([assassinskills]+[shadowdisciplinesskilltab]+[skillvenom]+[skilldeathsentry]+[skillfade]+[skillshadowmaster]) >= 1 && [sockets] == 0 # [maxquantity] == 1", - ]; - NTIP.buildList(Fury); + const Fury = [ + "[name] == JahRune", + "[name] == GulRune", + "[name] == EthRune ## [maxquantity] == 1", + "[name] == suwayyah && [quality] >= normal && [quality] <= superior # ([assassinskills]+[shadowdisciplinesskilltab]+[skillvenom]+[skilldeathsentry]+[skillfade]+[skillshadowmaster]) >= 1 && [sockets] == 3 # [maxquantity] == 1", + "[name] == suwayyah && [quality] == normal # ([assassinskills]+[shadowdisciplinesskilltab]+[skillvenom]+[skilldeathsentry]+[skillfade]+[skillshadowmaster]) >= 1 && [sockets] == 0 # [maxquantity] == 1", + ]; + NTIP.buildList(Fury); - Config.Runewords.push([Runeword.Fury, "suwayyah"]); + Config.Runewords.push([Runeword.Fury, "suwayyah"]); - Config.KeepRunewords.push("[type] == assassinclaw # [itemallskills] == 2 && [ias] == 40 && [itemdeadlystrike] == 33"); + Config.KeepRunewords.push("[type] == assassinclaw # [itemallskills] == 2 && [ias] == 40 && [itemdeadlystrike] == 33"); })(); diff --git a/libs/SoloPlay/BuildFiles/Runewords/Grief.js b/libs/SoloPlay/BuildFiles/Runewords/Grief.js index aaccf200..20b7e36a 100644 --- a/libs/SoloPlay/BuildFiles/Runewords/Grief.js +++ b/libs/SoloPlay/BuildFiles/Runewords/Grief.js @@ -1,49 +1,49 @@ (function() { - const Grief = [ - "[name] == EthRune # # [maxquantity] == 1", - "[name] == TirRune # # [maxquantity] == 1", - "[name] == LoRune # # [maxquantity] == 1", - "[name] == MalRune # # [maxquantity] == 1", - "[name] == RalRune # # [maxquantity] == 1", - ]; - NTIP.buildList(Grief); + const Grief = [ + "[name] == EthRune # # [maxquantity] == 1", + "[name] == TirRune # # [maxquantity] == 1", + "[name] == LoRune # # [maxquantity] == 1", + "[name] == MalRune # # [maxquantity] == 1", + "[name] == RalRune # # [maxquantity] == 1", + ]; + NTIP.buildList(Grief); - if (me.getItem(sdk.items.runes.Lo)) { - NTIP.addLine("[name] == phaseblade && [quality] >= normal && [quality] <= superior # [sockets] == 5 # [maxquantity] == 1"); + if (me.getItem(sdk.items.runes.Lo)) { + NTIP.addLine("[name] == phaseblade && [quality] >= normal && [quality] <= superior # [sockets] == 5 # [maxquantity] == 1"); - if (!Check.haveBase("phaseblade", 5)) { - NTIP.addLine("[name] == phaseblade && [quality] == normal # [sockets] == 0 # [maxquantity] == 1"); - Config.Recipes.push([Recipe.Socket.Weapon, "phaseblade"]); - } - } else { - NTIP.addLine("[name] == phaseblade && [quality] == superior # [enhanceddamage] >= 10 && [sockets] == 5 # [maxquantity] == 1"); - } + if (!Check.haveBase("phaseblade", 5)) { + NTIP.addLine("[name] == phaseblade && [quality] == normal # [sockets] == 0 # [maxquantity] == 1"); + Config.Recipes.push([Recipe.Socket.Weapon, "phaseblade"]); + } + } else { + NTIP.addLine("[name] == phaseblade && [quality] == superior # [enhanceddamage] >= 10 && [sockets] == 5 # [maxquantity] == 1"); + } - // Cube to Lo Rune - if (!me.getItem(sdk.items.runes.Lo)) { - Config.Recipes.push([Recipe.Rune, "Ist Rune"]); - Config.Recipes.push([Recipe.Rune, "Gul Rune"]); - Config.Recipes.push([Recipe.Rune, "Vex Rune"]); + // Cube to Lo Rune + if (!me.getItem(sdk.items.runes.Lo)) { + Config.Recipes.push([Recipe.Rune, "Ist Rune"]); + Config.Recipes.push([Recipe.Rune, "Gul Rune"]); + Config.Recipes.push([Recipe.Rune, "Vex Rune"]); - if (me.checkItem({ name: sdk.locale.items.CalltoArms }).have || ["Smiter", "Zealer"].indexOf(SetUp.finalBuild) === -1) { - Config.Recipes.push([Recipe.Rune, "Ohm Rune"]); - } - } + if (me.checkItem({ name: sdk.locale.items.CalltoArms }).have || ["Smiter", "Zealer"].indexOf(SetUp.finalBuild) === -1) { + Config.Recipes.push([Recipe.Rune, "Ohm Rune"]); + } + } - // Cube to Mal Rune - if (!me.getItem(sdk.items.runes.Mal)) { - Config.Recipes.push([Recipe.Rune, "Pul Rune"]); - Config.Recipes.push([Recipe.Rune, "Um Rune"]); - } + // Cube to Mal Rune + if (!me.getItem(sdk.items.runes.Mal)) { + Config.Recipes.push([Recipe.Rune, "Pul Rune"]); + Config.Recipes.push([Recipe.Rune, "Um Rune"]); + } - if (SetUp.finalBuild === "Plaguewolf") { - // Only start making Grief after Chains of Honor is made - if (Check.haveItem("armor", "runeword", "Chains of Honor")) { - Config.Runewords.push([Runeword.Grief, "phaseblade"]); - Config.KeepRunewords.push("[type] == sword # [ias] >= 30 && [itemdeadlystrike] == 20 && [passivepoispierce] >= 20"); - } - } else { - Config.Runewords.push([Runeword.Grief, "phaseblade"]); - Config.KeepRunewords.push("[type] == sword # [ias] >= 30 && [itemdeadlystrike] == 20 && [passivepoispierce] >= 20"); - } + if (SetUp.finalBuild === "Plaguewolf") { + // Only start making Grief after Chains of Honor is made + if (Check.haveItem("armor", "runeword", "Chains of Honor")) { + Config.Runewords.push([Runeword.Grief, "phaseblade"]); + Config.KeepRunewords.push("[type] == sword # [ias] >= 30 && [itemdeadlystrike] == 20 && [passivepoispierce] >= 20"); + } + } else { + Config.Runewords.push([Runeword.Grief, "phaseblade"]); + Config.KeepRunewords.push("[type] == sword # [ias] >= 30 && [itemdeadlystrike] == 20 && [passivepoispierce] >= 20"); + } })(); diff --git a/libs/SoloPlay/BuildFiles/Runewords/HandOfJustice.js b/libs/SoloPlay/BuildFiles/Runewords/HandOfJustice.js index 3e5751d9..d3a8ee98 100644 --- a/libs/SoloPlay/BuildFiles/Runewords/HandOfJustice.js +++ b/libs/SoloPlay/BuildFiles/Runewords/HandOfJustice.js @@ -1,31 +1,31 @@ (function() { - const HoJ = [ - "[name] == SurRune", - "[name] == ChamRune", - "[name] == AmnRune # # [maxquantity] == 1", - "[name] == LoRune", - "[name] == phaseblade && [quality] >= normal && [quality] <= superior # [sockets] == 4 # [maxquantity] == 1", - ]; - NTIP.buildList(HoJ); + const HoJ = [ + "[name] == SurRune", + "[name] == ChamRune", + "[name] == AmnRune # # [maxquantity] == 1", + "[name] == LoRune", + "[name] == phaseblade && [quality] >= normal && [quality] <= superior # [sockets] == 4 # [maxquantity] == 1", + ]; + NTIP.buildList(HoJ); - // Cube to Lo rune - if (!me.getItem(sdk.items.runes.Lo)) { - Config.Recipes.push([Recipe.Rune, "Mal Rune"]); - Config.Recipes.push([Recipe.Rune, "Ist Rune"]); - Config.Recipes.push([Recipe.Rune, "Gul Rune"]); - Config.Recipes.push([Recipe.Rune, "Vex Rune"]); + // Cube to Lo rune + if (!me.getItem(sdk.items.runes.Lo)) { + Config.Recipes.push([Recipe.Rune, "Mal Rune"]); + Config.Recipes.push([Recipe.Rune, "Ist Rune"]); + Config.Recipes.push([Recipe.Rune, "Gul Rune"]); + Config.Recipes.push([Recipe.Rune, "Vex Rune"]); - if (me.checkItem({ name: sdk.locale.items.CalltoArms }).have) { - Config.Recipes.push([Recipe.Rune, "Ohm Rune"]); - } - } + if (me.checkItem({ name: sdk.locale.items.CalltoArms }).have) { + Config.Recipes.push([Recipe.Rune, "Ohm Rune"]); + } + } - // Cube to Cham rune - if (!me.getItem(sdk.items.runes.Cham)) { - Config.Recipes.push([Recipe.Rune, "Ber Rune"]); - Config.Recipes.push([Recipe.Rune, "Jah Rune"]); - } + // Cube to Cham rune + if (!me.getItem(sdk.items.runes.Cham)) { + Config.Recipes.push([Recipe.Rune, "Ber Rune"]); + Config.Recipes.push([Recipe.Rune, "Jah Rune"]); + } - Config.Runewords.push([Runeword.HandofJustice, "phaseblade"]); - Config.KeepRunewords.push("[type] == sword # [holyfireaura] >= 16"); + Config.Runewords.push([Runeword.HandofJustice, "phaseblade"]); + Config.KeepRunewords.push("[type] == sword # [holyfireaura] >= 16"); })(); diff --git a/libs/SoloPlay/BuildFiles/Runewords/HeartOfTheOak.js b/libs/SoloPlay/BuildFiles/Runewords/HeartOfTheOak.js index 6baabec9..d4250292 100644 --- a/libs/SoloPlay/BuildFiles/Runewords/HeartOfTheOak.js +++ b/libs/SoloPlay/BuildFiles/Runewords/HeartOfTheOak.js @@ -1,35 +1,35 @@ (function() { - const HotO = [ - "[name] == ThulRune # # [maxquantity] == 1", - "[name] == PulRune", - "[name] == KoRune # # [maxquantity] == 1", - "[name] == VexRune", - ]; - NTIP.buildList(HotO); + const HotO = [ + "[name] == ThulRune # # [maxquantity] == 1", + "[name] == PulRune", + "[name] == KoRune # # [maxquantity] == 1", + "[name] == VexRune", + ]; + NTIP.buildList(HotO); - // Have Vex rune before looking for base - if (me.getItem(sdk.items.runes.Vex)) { - NTIP.addLine("([name] == flail || [name] == knout) && [flag] != ethereal && [quality] >= normal && [quality] <= superior # [sockets] == 4 # [maxquantity] == 1"); + // Have Vex rune before looking for base + if (me.getItem(sdk.items.runes.Vex)) { + NTIP.addLine("([name] == flail || [name] == knout) && [flag] != ethereal && [quality] >= normal && [quality] <= superior # [sockets] == 4 # [maxquantity] == 1"); - // Have Vex rune but do not have a base yet - if (!Check.haveBase("mace", 4)) { - NTIP.addLine("([name] == flail || [name] == knout) && [flag] != ethereal && [quality] == normal # [sockets] == 0 # [maxquantity] == 1"); - Config.Recipes.push([Recipe.Socket.Weapon, "flail"]); - Config.Recipes.push([Recipe.Socket.Weapon, "knout"]); - } - } + // Have Vex rune but do not have a base yet + if (!Check.haveBase("mace", 4)) { + NTIP.addLine("([name] == flail || [name] == knout) && [flag] != ethereal && [quality] == normal # [sockets] == 0 # [maxquantity] == 1"); + Config.Recipes.push([Recipe.Socket.Weapon, "flail"]); + Config.Recipes.push([Recipe.Socket.Weapon, "knout"]); + } + } - // Cube to Vex rune - if (!me.getItem(sdk.items.runes.Vex)) { - Config.Recipes.push([Recipe.Rune, "Lem Rune"]); - Config.Recipes.push([Recipe.Rune, "Pul Rune"]); - Config.Recipes.push([Recipe.Rune, "Um Rune"]); - Config.Recipes.push([Recipe.Rune, "Mal Rune"]); - Config.Recipes.push([Recipe.Rune, "Ist Rune"]); - Config.Recipes.push([Recipe.Rune, "Gul Rune"]); - } + // Cube to Vex rune + if (!me.getItem(sdk.items.runes.Vex)) { + Config.Recipes.push([Recipe.Rune, "Lem Rune"]); + Config.Recipes.push([Recipe.Rune, "Pul Rune"]); + Config.Recipes.push([Recipe.Rune, "Um Rune"]); + Config.Recipes.push([Recipe.Rune, "Mal Rune"]); + Config.Recipes.push([Recipe.Rune, "Ist Rune"]); + Config.Recipes.push([Recipe.Rune, "Gul Rune"]); + } - Config.Runewords.push([Runeword.HeartoftheOak, "knout"]); - Config.Runewords.push([Runeword.HeartoftheOak, "flail"]); - Config.KeepRunewords.push("[type] == mace # [itemallskills] == 3"); + Config.Runewords.push([Runeword.HeartoftheOak, "knout"]); + Config.Runewords.push([Runeword.HeartoftheOak, "flail"]); + Config.KeepRunewords.push("[type] == mace # [itemallskills] == 3"); })(); diff --git a/libs/SoloPlay/BuildFiles/Runewords/Honor.js b/libs/SoloPlay/BuildFiles/Runewords/Honor.js index 3f8dce5a..df3f2847 100644 --- a/libs/SoloPlay/BuildFiles/Runewords/Honor.js +++ b/libs/SoloPlay/BuildFiles/Runewords/Honor.js @@ -1,42 +1,42 @@ (function() { - const Honor = [ - "[name] == AmnRune # # [maxquantity] == 1", - "[name] == ElRune # # [maxquantity] == 1", - "[name] == IthRune # # [maxquantity] == 1", - "[name] == TirRune # # [maxquantity] == 1", - "[name] == SolRune # # [maxquantity] == 1", - ]; - NTIP.buildList(Honor); + const Honor = [ + "[name] == AmnRune # # [maxquantity] == 1", + "[name] == ElRune # # [maxquantity] == 1", + "[name] == IthRune # # [maxquantity] == 1", + "[name] == TirRune # # [maxquantity] == 1", + "[name] == SolRune # # [maxquantity] == 1", + ]; + NTIP.buildList(Honor); - // Cube to Amn rune - if (!me.getItem(sdk.items.runes.Amn)) { - Config.Recipes.push([Recipe.Rune, "Thul Rune"]); - } + // Cube to Amn rune + if (!me.getItem(sdk.items.runes.Amn)) { + Config.Recipes.push([Recipe.Rune, "Thul Rune"]); + } - // Have Sol rune before looking for base - if (me.getItem(sdk.items.runes.Sol)) { - if (!Check.haveBase("sword", 5)) { - if (me.accessToAct(5) && !me.getQuest(sdk.quest.id.SiegeOnHarrogath, sdk.quest.states.Completed)) { - NTIP.addLine("((me.diff == 0 && [name] == flamberge) || (me.diff > 0 && [name] == zweihander) || (me.diff == 2 && [name] == colossussword)) && [flag] != ethereal && [quality] >= normal && [quality] <= superior && [level] >= 41 # [sockets] == 0 # [maxquantity] == 1"); - } else { - NTIP.addLine("([name] == flamberge || [name] == zweihander || [name] == dimensionalblade || [name] == phaseblade || [name] == colossussword) && [flag] != ethereal && [quality] == normal && [level] >= 41 # [sockets] == 0 # [maxquantity] == 1"); - } - } + // Have Sol rune before looking for base + if (me.getItem(sdk.items.runes.Sol)) { + if (!Check.haveBase("sword", 5)) { + if (me.accessToAct(5) && !me.getQuest(sdk.quest.id.SiegeOnHarrogath, sdk.quest.states.Completed)) { + NTIP.addLine("((me.diff == 0 && [name] == flamberge) || (me.diff > 0 && [name] == zweihander) || (me.diff == 2 && [name] == colossussword)) && [flag] != ethereal && [quality] >= normal && [quality] <= superior && [level] >= 41 # [sockets] == 0 # [maxquantity] == 1"); + } else { + NTIP.addLine("([name] == flamberge || [name] == zweihander || [name] == dimensionalblade || [name] == phaseblade || [name] == colossussword) && [flag] != ethereal && [quality] == normal && [level] >= 41 # [sockets] == 0 # [maxquantity] == 1"); + } + } - NTIP.addLine("[type] == Sword && [flag] != ethereal && [quality] >= normal && [quality] <= superior && [wsm] <= 10 && [strreq] <= 187 # [sockets] == 5 # [maxquantity] == 1"); - } + NTIP.addLine("[type] == Sword && [flag] != ethereal && [quality] >= normal && [quality] <= superior && [wsm] <= 10 && [strreq] <= 187 # [sockets] == 5 # [maxquantity] == 1"); + } - Config.Runewords.push([Runeword.Honor, "dimensionalblade"]); - Config.Runewords.push([Runeword.Honor, "flamberge"]); - Config.Runewords.push([Runeword.Honor, "zweihander"]); - Config.Runewords.push([Runeword.Honor, "phaseblade"]); - Config.Runewords.push([Runeword.Honor, "colossussword"]); + Config.Runewords.push([Runeword.Honor, "dimensionalblade"]); + Config.Runewords.push([Runeword.Honor, "flamberge"]); + Config.Runewords.push([Runeword.Honor, "zweihander"]); + Config.Runewords.push([Runeword.Honor, "phaseblade"]); + Config.Runewords.push([Runeword.Honor, "colossussword"]); - Config.Recipes.push([Recipe.Socket.Weapon, "flamberge"]); - Config.Recipes.push([Recipe.Socket.Weapon, "zweihander"]); - Config.Recipes.push([Recipe.Socket.Weapon, "dimensionalblade"]); - Config.Recipes.push([Recipe.Socket.Weapon, "phaseblade"]); - Config.Recipes.push([Recipe.Socket.Weapon, "colossussword"]); + Config.Recipes.push([Recipe.Socket.Weapon, "flamberge"]); + Config.Recipes.push([Recipe.Socket.Weapon, "zweihander"]); + Config.Recipes.push([Recipe.Socket.Weapon, "dimensionalblade"]); + Config.Recipes.push([Recipe.Socket.Weapon, "phaseblade"]); + Config.Recipes.push([Recipe.Socket.Weapon, "colossussword"]); - Config.KeepRunewords.push("[type] == sword # [enhanceddamage] >= 160 && [tohit] >= 250 && [itemallskills] >= 1"); + Config.KeepRunewords.push("[type] == sword # [enhanceddamage] >= 160 && [tohit] >= 250 && [itemallskills] >= 1"); })(); diff --git a/libs/SoloPlay/BuildFiles/Runewords/Ice.js b/libs/SoloPlay/BuildFiles/Runewords/Ice.js index 2f1a52bd..6342acd8 100644 --- a/libs/SoloPlay/BuildFiles/Runewords/Ice.js +++ b/libs/SoloPlay/BuildFiles/Runewords/Ice.js @@ -1,56 +1,56 @@ (function() { - const IceRunes = [ - "[name] == AmnRune # # [maxquantity] == 1", - "[name] == ShaelRune # # [maxquantity] == 1", - "[name] == JahRune", - "[name] == LoRune", - ]; - NTIP.buildList(IceRunes); - - // Cube to Lo and Keep cubing to Jah rune - if (Item.getQuantityOwned(me.getItem(sdk.items.runes.Lo)) > 1 && me.checkItem({ name: sdk.locale.items.ChainofHonor }).have) { - if (!me.getItem(sdk.items.runes.Jah)) { - Config.Recipes.push([Recipe.Rune, "Mal Rune"]); - Config.Recipes.push([Recipe.Rune, "Ist Rune"]); - Config.Recipes.push([Recipe.Rune, "Gul Rune"]); - Config.Recipes.push([Recipe.Rune, "Vex Rune"]); - Config.Recipes.push([Recipe.Rune, "Ohm Rune"]); - } - !me.getItem(sdk.items.runes.Jah) && Config.Recipes.push([Recipe.Rune, "Lo Rune"]); - } - // Cube to Jah rune - if (!me.getItem(sdk.items.runes.Jah) && me.checkItem({ name: sdk.locale.items.ChainofHonor }).have) { - Config.Recipes.push([Recipe.Rune, "Lo Rune"]); - Config.Recipes.push([Recipe.Rune, "Sur Rune"]); - Config.Recipes.push([Recipe.Rune, "Ber Rune"]); - } - - if (me.amazon) { - NTIP.addLine("([name] == matriarchalbow || [name] == grandmatronbow) && [quality] == superior # [bowandcrossbowskilltab] == 3 && [enhanceddamage] >= 10 && [sockets] == 4 # [maxquantity] == 1"); - - Config.Runewords.push([Runeword.Ice, "matriarchalbow"]); - Config.Runewords.push([Runeword.Ice, "grandmatronbow"]); - - Config.Recipes.push([Recipe.Socket.Bow, "matriarchalbow"]); - Config.Recipes.push([Recipe.Socket.Bow, "grandmatronbow"]); - - if (!Check.haveBase("amazonbow", 4) && me.getItem(sdk.items.runes.Lo) && me.getItem(sdk.items.runes.Jah)) { - NTIP.addLine("([name] == matriarchalbow || [name] == grandmatronbow) && [quality] == normal # [sockets] == 0 # [maxquantity] == 1"); - } - - Config.KeepRunewords.push("[type] == amazonbow # [enhanceddamage] >= 140 && [passivecoldpierce] >= 25"); - - } else { - NTIP.addLine("[type] == demoncrossbow && [quality] == superior # [enhanceddamage] >= 10 && [sockets] == 4 # [maxquantity] == 1"); - - Config.Runewords.push([Runeword.Ice, "demoncrossbow"]); - - Config.Recipes.push([Recipe.Socket.Crossbow, "demoncrossbow"]); - - if (!Check.haveBase("crossbow", 4) && me.getItem(sdk.items.runes.Lo) && me.getItem(sdk.items.runes.Jah)) { - NTIP.addLine("[name] == demoncrossbow && [quality] == normal # [sockets] == 0 # [maxquantity] == 1"); - } - - Config.KeepRunewords.push("[type] == demoncrossbow # [enhanceddamage] >= 140 && [passivecoldpierce] >= 25"); - } + const IceRunes = [ + "[name] == AmnRune # # [maxquantity] == 1", + "[name] == ShaelRune # # [maxquantity] == 1", + "[name] == JahRune", + "[name] == LoRune", + ]; + NTIP.buildList(IceRunes); + + // Cube to Lo and Keep cubing to Jah rune + if (Item.getQuantityOwned(me.getItem(sdk.items.runes.Lo)) > 1 && me.checkItem({ name: sdk.locale.items.ChainofHonor }).have) { + if (!me.getItem(sdk.items.runes.Jah)) { + Config.Recipes.push([Recipe.Rune, "Mal Rune"]); + Config.Recipes.push([Recipe.Rune, "Ist Rune"]); + Config.Recipes.push([Recipe.Rune, "Gul Rune"]); + Config.Recipes.push([Recipe.Rune, "Vex Rune"]); + Config.Recipes.push([Recipe.Rune, "Ohm Rune"]); + } + !me.getItem(sdk.items.runes.Jah) && Config.Recipes.push([Recipe.Rune, "Lo Rune"]); + } + // Cube to Jah rune + if (!me.getItem(sdk.items.runes.Jah) && me.checkItem({ name: sdk.locale.items.ChainofHonor }).have) { + Config.Recipes.push([Recipe.Rune, "Lo Rune"]); + Config.Recipes.push([Recipe.Rune, "Sur Rune"]); + Config.Recipes.push([Recipe.Rune, "Ber Rune"]); + } + + if (me.amazon) { + NTIP.addLine("([name] == matriarchalbow || [name] == grandmatronbow) && [quality] == superior # [bowandcrossbowskilltab] == 3 && [enhanceddamage] >= 10 && [sockets] == 4 # [maxquantity] == 1"); + + Config.Runewords.push([Runeword.Ice, "matriarchalbow"]); + Config.Runewords.push([Runeword.Ice, "grandmatronbow"]); + + Config.Recipes.push([Recipe.Socket.Bow, "matriarchalbow"]); + Config.Recipes.push([Recipe.Socket.Bow, "grandmatronbow"]); + + if (!Check.haveBase("amazonbow", 4) && me.getItem(sdk.items.runes.Lo) && me.getItem(sdk.items.runes.Jah)) { + NTIP.addLine("([name] == matriarchalbow || [name] == grandmatronbow) && [quality] == normal # [sockets] == 0 # [maxquantity] == 1"); + } + + Config.KeepRunewords.push("[type] == amazonbow # [enhanceddamage] >= 140 && [passivecoldpierce] >= 25"); + + } else { + NTIP.addLine("[type] == demoncrossbow && [quality] == superior # [enhanceddamage] >= 10 && [sockets] == 4 # [maxquantity] == 1"); + + Config.Runewords.push([Runeword.Ice, "demoncrossbow"]); + + Config.Recipes.push([Recipe.Socket.Crossbow, "demoncrossbow"]); + + if (!Check.haveBase("crossbow", 4) && me.getItem(sdk.items.runes.Lo) && me.getItem(sdk.items.runes.Jah)) { + NTIP.addLine("[name] == demoncrossbow && [quality] == normal # [sockets] == 0 # [maxquantity] == 1"); + } + + Config.KeepRunewords.push("[type] == demoncrossbow # [enhanceddamage] >= 140 && [passivecoldpierce] >= 25"); + } })(); diff --git a/libs/SoloPlay/BuildFiles/Runewords/KingsGrace.js b/libs/SoloPlay/BuildFiles/Runewords/KingsGrace.js index 16489788..20f91a31 100644 --- a/libs/SoloPlay/BuildFiles/Runewords/KingsGrace.js +++ b/libs/SoloPlay/BuildFiles/Runewords/KingsGrace.js @@ -1,29 +1,29 @@ (function() { - const KingsGrace = [ - "[name] == AmnRune # # [maxquantity] == 1", - "[name] == RalRune # # [maxquantity] == 1", - "[name] == ThulRune # # [maxquantity] == 1", - "[type] == sword && [flag] != ethereal && [quality] >= normal && [quality] <= superior && [wsm] <= 10 && [strreq] <= 150 # [sockets] == 3 # [maxquantity] == 1", - "([name] == legendsword || [name] == highlandblade || [name] == balrogblade || [name] == colossussword) && [flag] != ethereal && [quality] >= normal && [quality] <= superior # [sockets] == 3 # [maxquantity] == 1", - ]; - NTIP.buildList(KingsGrace); + const KingsGrace = [ + "[name] == AmnRune # # [maxquantity] == 1", + "[name] == RalRune # # [maxquantity] == 1", + "[name] == ThulRune # # [maxquantity] == 1", + "[type] == sword && [flag] != ethereal && [quality] >= normal && [quality] <= superior && [wsm] <= 10 && [strreq] <= 150 # [sockets] == 3 # [maxquantity] == 1", + "([name] == legendsword || [name] == highlandblade || [name] == balrogblade || [name] == colossussword) && [flag] != ethereal && [quality] >= normal && [quality] <= superior # [sockets] == 3 # [maxquantity] == 1", + ]; + NTIP.buildList(KingsGrace); - Config.Runewords.push([Runeword.KingsGrace, "broadsword"]); - Config.Runewords.push([Runeword.KingsGrace, "longsword"]); - Config.Runewords.push([Runeword.KingsGrace, "warsword"]); - Config.Runewords.push([Runeword.KingsGrace, "giantsword"]); - Config.Runewords.push([Runeword.KingsGrace, "flamberge"]); - Config.Runewords.push([Runeword.KingsGrace, "dimensionalblade"]); - Config.Runewords.push([Runeword.KingsGrace, "battlesword"]); - Config.Runewords.push([Runeword.KingsGrace, "runesword"]); - Config.Runewords.push([Runeword.KingsGrace, "ancientsword"]); - Config.Runewords.push([Runeword.KingsGrace, "espandon"]); - Config.Runewords.push([Runeword.KingsGrace, "tusksword"]); - Config.Runewords.push([Runeword.KingsGrace, "zweihander"]); - Config.Runewords.push([Runeword.KingsGrace, "legendsword"]); - Config.Runewords.push([Runeword.KingsGrace, "highlandblade"]); - Config.Runewords.push([Runeword.KingsGrace, "balrogblade"]); - Config.Runewords.push([Runeword.KingsGrace, "colossussword"]); + Config.Runewords.push([Runeword.KingsGrace, "broadsword"]); + Config.Runewords.push([Runeword.KingsGrace, "longsword"]); + Config.Runewords.push([Runeword.KingsGrace, "warsword"]); + Config.Runewords.push([Runeword.KingsGrace, "giantsword"]); + Config.Runewords.push([Runeword.KingsGrace, "flamberge"]); + Config.Runewords.push([Runeword.KingsGrace, "dimensionalblade"]); + Config.Runewords.push([Runeword.KingsGrace, "battlesword"]); + Config.Runewords.push([Runeword.KingsGrace, "runesword"]); + Config.Runewords.push([Runeword.KingsGrace, "ancientsword"]); + Config.Runewords.push([Runeword.KingsGrace, "espandon"]); + Config.Runewords.push([Runeword.KingsGrace, "tusksword"]); + Config.Runewords.push([Runeword.KingsGrace, "zweihander"]); + Config.Runewords.push([Runeword.KingsGrace, "legendsword"]); + Config.Runewords.push([Runeword.KingsGrace, "highlandblade"]); + Config.Runewords.push([Runeword.KingsGrace, "balrogblade"]); + Config.Runewords.push([Runeword.KingsGrace, "colossussword"]); - Config.KeepRunewords.push("[type] == sword # [enhanceddamage] >= 100 && [lifeleech] >= 7"); + Config.KeepRunewords.push("[type] == sword # [enhanceddamage] >= 100 && [lifeleech] >= 7"); })(); diff --git a/libs/SoloPlay/BuildFiles/Runewords/LastWish.js b/libs/SoloPlay/BuildFiles/Runewords/LastWish.js index f2265e2c..f464d19c 100644 --- a/libs/SoloPlay/BuildFiles/Runewords/LastWish.js +++ b/libs/SoloPlay/BuildFiles/Runewords/LastWish.js @@ -1,31 +1,31 @@ (function() { - // Jah/Mal/Jah/Sur/Jah/Ber - const LW = [ - "[name] == JahRune", - "[name] == MalRune", - "[name] == SurRune", - "[name] == BerRune", - "[name] == phaseblade && [quality] >= normal && [quality] <= superior # [sockets] == 6 # [maxquantity] == 1", - ]; - NTIP.buildList(LW); - // Cube to Jah/Sur rune - if (!me.getItem(sdk.items.runes.Jah) || !me.getItem(sdk.items.runes.Sur)) { - if (me.checkItem({ name: sdk.locale.items.CalltoArms }).have) { - Config.Recipes.push([Recipe.Rune, "Mal Rune"]); - Config.Recipes.push([Recipe.Rune, "Ist Rune"]); - Config.Recipes.push([Recipe.Rune, "Gul Rune"]); - Config.Recipes.push([Recipe.Rune, "Vex Rune"]); - Config.Recipes.push([Recipe.Rune, "Ohm Rune"]); - } + // Jah/Mal/Jah/Sur/Jah/Ber + const LW = [ + "[name] == JahRune", + "[name] == MalRune", + "[name] == SurRune", + "[name] == BerRune", + "[name] == phaseblade && [quality] >= normal && [quality] <= superior # [sockets] == 6 # [maxquantity] == 1", + ]; + NTIP.buildList(LW); + // Cube to Jah/Sur rune + if (!me.getItem(sdk.items.runes.Jah) || !me.getItem(sdk.items.runes.Sur)) { + if (me.checkItem({ name: sdk.locale.items.CalltoArms }).have) { + Config.Recipes.push([Recipe.Rune, "Mal Rune"]); + Config.Recipes.push([Recipe.Rune, "Ist Rune"]); + Config.Recipes.push([Recipe.Rune, "Gul Rune"]); + Config.Recipes.push([Recipe.Rune, "Vex Rune"]); + Config.Recipes.push([Recipe.Rune, "Ohm Rune"]); + } - Config.Recipes.push([Recipe.Rune, "Lo Rune"]); + Config.Recipes.push([Recipe.Rune, "Lo Rune"]); - if (!me.getItem(sdk.items.runes.Jah)) { - Config.Recipes.push([Recipe.Rune, "Sur Rune"]); - Config.Recipes.push([Recipe.Rune, "Ber Rune"]); - } - - Config.Runewords.push([Runeword.LastWish, "phaseblade"]); - Config.KeepRunewords.push("[type] == sword # [mightaura] >= 17"); - } + if (!me.getItem(sdk.items.runes.Jah)) { + Config.Recipes.push([Recipe.Rune, "Sur Rune"]); + Config.Recipes.push([Recipe.Rune, "Ber Rune"]); + } + + Config.Runewords.push([Runeword.LastWish, "phaseblade"]); + Config.KeepRunewords.push("[type] == sword # [mightaura] >= 17"); + } })(); diff --git a/libs/SoloPlay/BuildFiles/Runewords/Lawbringer.js b/libs/SoloPlay/BuildFiles/Runewords/Lawbringer.js index 428cf7fb..421bfd7d 100644 --- a/libs/SoloPlay/BuildFiles/Runewords/Lawbringer.js +++ b/libs/SoloPlay/BuildFiles/Runewords/Lawbringer.js @@ -1,33 +1,33 @@ (function() { - const Lawbringer = [ - "[name] == AmnRune # # [maxquantity] == 1", - "[name] == LemRune", - "[name] == KoRune", - ]; - NTIP.buildList(Lawbringer); + const Lawbringer = [ + "[name] == AmnRune # # [maxquantity] == 1", + "[name] == LemRune", + "[name] == KoRune", + ]; + NTIP.buildList(Lawbringer); - // Have Lem and Ko runes before looking for normal base - if (me.getItem(sdk.items.runes.Lem) && me.getItem(sdk.items.runes.Ko)) { - NTIP.addLine("[type] == sword && [flag] != ethereal && [quality] >= normal && [quality] <= superior && [wsm] <= 10 && [strreq] <= 150 # [sockets] == 3"); - NTIP.addLine("([name] == legendsword || [name] == highlandblade || [name] == balrogblade || [name] == championsword || [name] == colossussword) && [flag] != ethereal && [quality] >= normal && [quality] <= superior # [sockets] == 3"); - } else { - NTIP.addLine("([name] == legendsword || [name] == highlandblade || [name] == balrogblade || [name] == championsword || [name] == colossussword) && [flag] != ethereal && [quality] == superior # [enhanceddamage] >= 5 && [sockets] == 3"); - } + // Have Lem and Ko runes before looking for normal base + if (me.getItem(sdk.items.runes.Lem) && me.getItem(sdk.items.runes.Ko)) { + NTIP.addLine("[type] == sword && [flag] != ethereal && [quality] >= normal && [quality] <= superior && [wsm] <= 10 && [strreq] <= 150 # [sockets] == 3"); + NTIP.addLine("([name] == legendsword || [name] == highlandblade || [name] == balrogblade || [name] == championsword || [name] == colossussword) && [flag] != ethereal && [quality] >= normal && [quality] <= superior # [sockets] == 3"); + } else { + NTIP.addLine("([name] == legendsword || [name] == highlandblade || [name] == balrogblade || [name] == championsword || [name] == colossussword) && [flag] != ethereal && [quality] == superior # [enhanceddamage] >= 5 && [sockets] == 3"); + } - Config.Runewords.push([Runeword.Lawbringer, "dimensionalblade"]); - Config.Runewords.push([Runeword.Lawbringer, "battlesword"]); - Config.Runewords.push([Runeword.Lawbringer, "runesword"]); - Config.Runewords.push([Runeword.Lawbringer, "conquestsword"]); - Config.Runewords.push([Runeword.Lawbringer, "crypticsword"]); - Config.Runewords.push([Runeword.Lawbringer, "phaseblade"]); - Config.Runewords.push([Runeword.Lawbringer, "espandon"]); - Config.Runewords.push([Runeword.Lawbringer, "tusksword"]); - Config.Runewords.push([Runeword.Lawbringer, "zweihander"]); - Config.Runewords.push([Runeword.Lawbringer, "legendsword"]); - Config.Runewords.push([Runeword.Lawbringer, "highlandblade"]); - Config.Runewords.push([Runeword.Lawbringer, "balrogblade"]); - Config.Runewords.push([Runeword.Lawbringer, "championsword"]); - Config.Runewords.push([Runeword.Lawbringer, "colossussword"]); + Config.Runewords.push([Runeword.Lawbringer, "dimensionalblade"]); + Config.Runewords.push([Runeword.Lawbringer, "battlesword"]); + Config.Runewords.push([Runeword.Lawbringer, "runesword"]); + Config.Runewords.push([Runeword.Lawbringer, "conquestsword"]); + Config.Runewords.push([Runeword.Lawbringer, "crypticsword"]); + Config.Runewords.push([Runeword.Lawbringer, "phaseblade"]); + Config.Runewords.push([Runeword.Lawbringer, "espandon"]); + Config.Runewords.push([Runeword.Lawbringer, "tusksword"]); + Config.Runewords.push([Runeword.Lawbringer, "zweihander"]); + Config.Runewords.push([Runeword.Lawbringer, "legendsword"]); + Config.Runewords.push([Runeword.Lawbringer, "highlandblade"]); + Config.Runewords.push([Runeword.Lawbringer, "balrogblade"]); + Config.Runewords.push([Runeword.Lawbringer, "championsword"]); + Config.Runewords.push([Runeword.Lawbringer, "colossussword"]); - Config.KeepRunewords.push("[type] == sword # [sanctuaryaura] >= 16"); + Config.KeepRunewords.push("[type] == sword # [sanctuaryaura] >= 16"); })(); diff --git a/libs/SoloPlay/BuildFiles/Runewords/Lore.js b/libs/SoloPlay/BuildFiles/Runewords/Lore.js index a1aec71d..9ca241c4 100644 --- a/libs/SoloPlay/BuildFiles/Runewords/Lore.js +++ b/libs/SoloPlay/BuildFiles/Runewords/Lore.js @@ -1,135 +1,135 @@ (function() { - if (!Check.haveItem("helm", "runeword", "Lore")) { - const loreRunes = [ - "[name] == OrtRune # # [maxquantity] == 1", - "[name] == SolRune # # [maxquantity] == 1", - ]; - NTIP.buildList(loreRunes); + if (!Check.haveItem("helm", "runeword", "Lore")) { + const loreRunes = [ + "[name] == OrtRune # # [maxquantity] == 1", + "[name] == SolRune # # [maxquantity] == 1", + ]; + NTIP.buildList(loreRunes); - // Cube to Sol rune - if (!me.getItem(sdk.items.runes.Sol)) { - Config.Recipes.push([Recipe.Rune, "Ort Rune"]); - Config.Recipes.push([Recipe.Rune, "Thul Rune"]); - Config.Recipes.push([Recipe.Rune, "Amn Rune"]); - } - } else { - // Cube to Sol rune - if (!me.getItem(sdk.items.runes.Sol)) { - Config.Recipes.push([Recipe.Rune, "Amn Rune"]); - } - } + // Cube to Sol rune + if (!me.getItem(sdk.items.runes.Sol)) { + Config.Recipes.push([Recipe.Rune, "Ort Rune"]); + Config.Recipes.push([Recipe.Rune, "Thul Rune"]); + Config.Recipes.push([Recipe.Rune, "Amn Rune"]); + } + } else { + // Cube to Sol rune + if (!me.getItem(sdk.items.runes.Sol)) { + Config.Recipes.push([Recipe.Rune, "Amn Rune"]); + } + } - let classLoreHelm = []; - const loreHelm = [ - "!me.hell && ([name] == crown || [name] == bonehelm || [name] == fullhelm) && [flag] != ethereal && [quality] >= normal && [quality] <= superior # [sockets] == 2 # [maxquantity] == 1", - "([name] == casque || [name] == sallet || [name] == deathmask || [name] == grimhelm) && [flag] != ethereal && [quality] >= normal && [quality] <= superior # [sockets] == 2 # [maxquantity] == 1", - ]; + let classLoreHelm = []; + const loreHelm = [ + "!me.hell && ([name] == crown || [name] == bonehelm || [name] == fullhelm) && [flag] != ethereal && [quality] >= normal && [quality] <= superior # [sockets] == 2 # [maxquantity] == 1", + "([name] == casque || [name] == sallet || [name] == deathmask || [name] == grimhelm) && [flag] != ethereal && [quality] >= normal && [quality] <= superior # [sockets] == 2 # [maxquantity] == 1", + ]; - if (me.druid) { - classLoreHelm = [ - "[name] == OrtRune # # [maxquantity] == 1", - "[name] == SolRune # # [maxquantity] == 1", - "[type] == pelt && [flag] != ethereal && [quality] >= normal && [quality] <= superior # [sockets] == 2", - "[type] == pelt && [quality] == normal # ([druidskills]+[elementalskilltab]+[skillcyclonearmor]+[skilltwister]+[skilltornado]+[skillhurricane]) >= 1 && [sockets] == 0", - ]; - NTIP.buildList(classLoreHelm); + if (me.druid) { + classLoreHelm = [ + "[name] == OrtRune # # [maxquantity] == 1", + "[name] == SolRune # # [maxquantity] == 1", + "[type] == pelt && [flag] != ethereal && [quality] >= normal && [quality] <= superior # [sockets] == 2", + "[type] == pelt && [quality] == normal # ([druidskills]+[elementalskilltab]+[skillcyclonearmor]+[skilltwister]+[skilltornado]+[skillhurricane]) >= 1 && [sockets] == 0", + ]; + NTIP.buildList(classLoreHelm); - // Normal Helms - if (me.equipped.get(sdk.body.Head).tier < 150) { - NTIP.buildList(loreHelm); - } + // Normal Helms + if (me.equipped.get(sdk.body.Head).tier < 150) { + NTIP.buildList(loreHelm); + } - // Pelts - Config.Runewords.push([Runeword.Lore, "wolfhead"]); - Config.Runewords.push([Runeword.Lore, "hawkhelm"]); - Config.Runewords.push([Runeword.Lore, "antlers"]); - Config.Runewords.push([Runeword.Lore, "falconmask"]); - Config.Runewords.push([Runeword.Lore, "spiritmask"]); - Config.Runewords.push([Runeword.Lore, "alphahelm"]); - Config.Runewords.push([Runeword.Lore, "griffonheaddress"]); - Config.Runewords.push([Runeword.Lore, "hunter'sguise"]); - Config.Runewords.push([Runeword.Lore, "sacredfeathers"]); - Config.Runewords.push([Runeword.Lore, "totemicmask"]); - Config.Runewords.push([Runeword.Lore, "bloodspirit"]); - Config.Runewords.push([Runeword.Lore, "sunspirit"]); - Config.Runewords.push([Runeword.Lore, "earthspirit"]); - Config.Runewords.push([Runeword.Lore, "skyspirit"]); - Config.Runewords.push([Runeword.Lore, "dreamspirit"]); + // Pelts + Config.Runewords.push([Runeword.Lore, "wolfhead"]); + Config.Runewords.push([Runeword.Lore, "hawkhelm"]); + Config.Runewords.push([Runeword.Lore, "antlers"]); + Config.Runewords.push([Runeword.Lore, "falconmask"]); + Config.Runewords.push([Runeword.Lore, "spiritmask"]); + Config.Runewords.push([Runeword.Lore, "alphahelm"]); + Config.Runewords.push([Runeword.Lore, "griffonheaddress"]); + Config.Runewords.push([Runeword.Lore, "hunter'sguise"]); + Config.Runewords.push([Runeword.Lore, "sacredfeathers"]); + Config.Runewords.push([Runeword.Lore, "totemicmask"]); + Config.Runewords.push([Runeword.Lore, "bloodspirit"]); + Config.Runewords.push([Runeword.Lore, "sunspirit"]); + Config.Runewords.push([Runeword.Lore, "earthspirit"]); + Config.Runewords.push([Runeword.Lore, "skyspirit"]); + Config.Runewords.push([Runeword.Lore, "dreamspirit"]); - Config.Recipes.push([Recipe.Socket.Helm, "wolfhead"]); - Config.Recipes.push([Recipe.Socket.Helm, "hawkhelm"]); - Config.Recipes.push([Recipe.Socket.Helm, "antlers"]); - Config.Recipes.push([Recipe.Socket.Helm, "falconmask"]); - Config.Recipes.push([Recipe.Socket.Helm, "alphahelm"]); - Config.Recipes.push([Recipe.Socket.Helm, "griffonheaddress"]); - Config.Recipes.push([Recipe.Socket.Helm, "hunter'sguise"]); - Config.Recipes.push([Recipe.Socket.Helm, "sacredfeathers"]); - Config.Recipes.push([Recipe.Socket.Helm, "totemicmask"]); - Config.Recipes.push([Recipe.Socket.Helm, "bloodspirit"]); - Config.Recipes.push([Recipe.Socket.Helm, "sunspirit"]); - Config.Recipes.push([Recipe.Socket.Helm, "earthspirit"]); - Config.Recipes.push([Recipe.Socket.Helm, "skyspirit"]); - Config.Recipes.push([Recipe.Socket.Helm, "dreamspirit"]); + Config.Recipes.push([Recipe.Socket.Helm, "wolfhead"]); + Config.Recipes.push([Recipe.Socket.Helm, "hawkhelm"]); + Config.Recipes.push([Recipe.Socket.Helm, "antlers"]); + Config.Recipes.push([Recipe.Socket.Helm, "falconmask"]); + Config.Recipes.push([Recipe.Socket.Helm, "alphahelm"]); + Config.Recipes.push([Recipe.Socket.Helm, "griffonheaddress"]); + Config.Recipes.push([Recipe.Socket.Helm, "hunter'sguise"]); + Config.Recipes.push([Recipe.Socket.Helm, "sacredfeathers"]); + Config.Recipes.push([Recipe.Socket.Helm, "totemicmask"]); + Config.Recipes.push([Recipe.Socket.Helm, "bloodspirit"]); + Config.Recipes.push([Recipe.Socket.Helm, "sunspirit"]); + Config.Recipes.push([Recipe.Socket.Helm, "earthspirit"]); + Config.Recipes.push([Recipe.Socket.Helm, "skyspirit"]); + Config.Recipes.push([Recipe.Socket.Helm, "dreamspirit"]); - Config.KeepRunewords.push("[type] == pelt # [lightresist] >= 25"); - } + Config.KeepRunewords.push("[type] == pelt # [lightresist] >= 25"); + } - if (me.barbarian) { - classLoreHelm = [ - "[name] == OrtRune # # [maxquantity] == 1", - "[name] == SolRune # # [maxquantity] == 1", - "[type] == primalhelm && [flag] != ethereal && [quality] >= normal && [quality] <= superior && [strreq] <= 150 # [sockets] == 2", - "[type] == primalhelm && [flag] != ethereal && [quality] == normal && [strreq] <= 150 # ([barbarianskills]+[barbcombatskilltab]+[skillbattleorders]+[skillfrenzy]+[skilldoubleswing]+[skillnaturalresistance]) >= 1 && [sockets] == 0", - ]; - NTIP.buildList(classLoreHelm); + if (me.barbarian) { + classLoreHelm = [ + "[name] == OrtRune # # [maxquantity] == 1", + "[name] == SolRune # # [maxquantity] == 1", + "[type] == primalhelm && [flag] != ethereal && [quality] >= normal && [quality] <= superior && [strreq] <= 150 # [sockets] == 2", + "[type] == primalhelm && [flag] != ethereal && [quality] == normal && [strreq] <= 150 # ([barbarianskills]+[barbcombatskilltab]+[skillbattleorders]+[skillfrenzy]+[skilldoubleswing]+[skillnaturalresistance]) >= 1 && [sockets] == 0", + ]; + NTIP.buildList(classLoreHelm); - // Normal Helms - if (me.equipped.get(sdk.body.Head).tier < 150) { - NTIP.buildList(loreHelm); - } + // Normal Helms + if (me.equipped.get(sdk.body.Head).tier < 150) { + NTIP.buildList(loreHelm); + } - // Primal Helms - Config.Runewords.push([Runeword.Lore, "jawbonecap"]); - Config.Runewords.push([Runeword.Lore, "fangedhelm"]); - Config.Runewords.push([Runeword.Lore, "hornedhelm"]); - Config.Runewords.push([Runeword.Lore, "assaulthelmet"]); - Config.Runewords.push([Runeword.Lore, "avengerguard"]); - Config.Runewords.push([Runeword.Lore, "jawbonevisor"]); - Config.Runewords.push([Runeword.Lore, "lionhelm"]); - Config.Runewords.push([Runeword.Lore, "ragemask"]); - Config.Runewords.push([Runeword.Lore, "savagehelmet"]); - Config.Runewords.push([Runeword.Lore, "slayerguard"]); - Config.Runewords.push([Runeword.Lore, "carnagehelm"]); - Config.Runewords.push([Runeword.Lore, "furyvisor"]); + // Primal Helms + Config.Runewords.push([Runeword.Lore, "jawbonecap"]); + Config.Runewords.push([Runeword.Lore, "fangedhelm"]); + Config.Runewords.push([Runeword.Lore, "hornedhelm"]); + Config.Runewords.push([Runeword.Lore, "assaulthelmet"]); + Config.Runewords.push([Runeword.Lore, "avengerguard"]); + Config.Runewords.push([Runeword.Lore, "jawbonevisor"]); + Config.Runewords.push([Runeword.Lore, "lionhelm"]); + Config.Runewords.push([Runeword.Lore, "ragemask"]); + Config.Runewords.push([Runeword.Lore, "savagehelmet"]); + Config.Runewords.push([Runeword.Lore, "slayerguard"]); + Config.Runewords.push([Runeword.Lore, "carnagehelm"]); + Config.Runewords.push([Runeword.Lore, "furyvisor"]); - Config.Recipes.push([Recipe.Socket.Helm, "jawbonecap"]); - Config.Recipes.push([Recipe.Socket.Helm, "fangedhelm"]); - Config.Recipes.push([Recipe.Socket.Helm, "hornedhelm"]); - Config.Recipes.push([Recipe.Socket.Helm, "assaulthelmet"]); - Config.Recipes.push([Recipe.Socket.Helm, "avengerguard"]); - Config.Recipes.push([Recipe.Socket.Helm, "jawbonevisor"]); - Config.Recipes.push([Recipe.Socket.Helm, "lionhelm"]); - Config.Recipes.push([Recipe.Socket.Helm, "ragemask"]); - Config.Recipes.push([Recipe.Socket.Helm, "savagehelmet"]); - Config.Recipes.push([Recipe.Socket.Helm, "slayerguard"]); - Config.Recipes.push([Recipe.Socket.Helm, "carnagehelm"]); - Config.Recipes.push([Recipe.Socket.Helm, "furyvisor"]); + Config.Recipes.push([Recipe.Socket.Helm, "jawbonecap"]); + Config.Recipes.push([Recipe.Socket.Helm, "fangedhelm"]); + Config.Recipes.push([Recipe.Socket.Helm, "hornedhelm"]); + Config.Recipes.push([Recipe.Socket.Helm, "assaulthelmet"]); + Config.Recipes.push([Recipe.Socket.Helm, "avengerguard"]); + Config.Recipes.push([Recipe.Socket.Helm, "jawbonevisor"]); + Config.Recipes.push([Recipe.Socket.Helm, "lionhelm"]); + Config.Recipes.push([Recipe.Socket.Helm, "ragemask"]); + Config.Recipes.push([Recipe.Socket.Helm, "savagehelmet"]); + Config.Recipes.push([Recipe.Socket.Helm, "slayerguard"]); + Config.Recipes.push([Recipe.Socket.Helm, "carnagehelm"]); + Config.Recipes.push([Recipe.Socket.Helm, "furyvisor"]); - Config.KeepRunewords.push("[type] == primalhelm # [LightResist] >= 25"); - } + Config.KeepRunewords.push("[type] == primalhelm # [LightResist] >= 25"); + } - if (!me.druid && !me.barbarian) { - NTIP.buildList(loreHelm); - } + if (!me.druid && !me.barbarian) { + NTIP.buildList(loreHelm); + } - // Normal helms - Config.Runewords.push([Runeword.Lore, "crown"]); - Config.Runewords.push([Runeword.Lore, "grimhelm"]); - Config.Runewords.push([Runeword.Lore, "bonehelm"]); - Config.Runewords.push([Runeword.Lore, "sallet"]); - Config.Runewords.push([Runeword.Lore, "casque"]); - Config.Runewords.push([Runeword.Lore, "deathmask"]); - Config.Runewords.push([Runeword.Lore, "fullhelm"]); + // Normal helms + Config.Runewords.push([Runeword.Lore, "crown"]); + Config.Runewords.push([Runeword.Lore, "grimhelm"]); + Config.Runewords.push([Runeword.Lore, "bonehelm"]); + Config.Runewords.push([Runeword.Lore, "sallet"]); + Config.Runewords.push([Runeword.Lore, "casque"]); + Config.Runewords.push([Runeword.Lore, "deathmask"]); + Config.Runewords.push([Runeword.Lore, "fullhelm"]); - Config.KeepRunewords.push("([type] == circlet || [type] == helm) # [lightresist] >= 25"); + Config.KeepRunewords.push("([type] == circlet || [type] == helm) # [lightresist] >= 25"); })(); diff --git a/libs/SoloPlay/BuildFiles/Runewords/Malice.js b/libs/SoloPlay/BuildFiles/Runewords/Malice.js index 8289fbb9..7b6783e2 100644 --- a/libs/SoloPlay/BuildFiles/Runewords/Malice.js +++ b/libs/SoloPlay/BuildFiles/Runewords/Malice.js @@ -1,21 +1,21 @@ (function() { - const Malice = [ - "[name] == IthRune # # [maxquantity] == 1", - "[name] == ElRune # # [maxquantity] == 1", - "[name] == EthRune # # [maxquantity] == 1", - "[type] == sword && [flag] != ethereal && [quality] >= normal && [quality] <= superior && [wsm] <= 10 && [strreq] <= 150 # [sockets] == 3 # [maxquantity] == 1", - ]; - NTIP.buildList(Malice); + const Malice = [ + "[name] == IthRune # # [maxquantity] == 1", + "[name] == ElRune # # [maxquantity] == 1", + "[name] == EthRune # # [maxquantity] == 1", + "[type] == sword && [flag] != ethereal && [quality] >= normal && [quality] <= superior && [wsm] <= 10 && [strreq] <= 150 # [sockets] == 3 # [maxquantity] == 1", + ]; + NTIP.buildList(Malice); - Config.Runewords.push([Runeword.Malice, "crystalsword"]); - Config.Runewords.push([Runeword.Malice, "broadsword"]); - Config.Runewords.push([Runeword.Malice, "longsword"]); - Config.Runewords.push([Runeword.Malice, "warsword"]); - Config.Runewords.push([Runeword.Malice, "giantsword"]); - Config.Runewords.push([Runeword.Malice, "flamberge"]); - Config.Runewords.push([Runeword.Malice, "espandon"]); - Config.Runewords.push([Runeword.Malice, "tusksword"]); - Config.Runewords.push([Runeword.Malice, "zweihander"]); + Config.Runewords.push([Runeword.Malice, "crystalsword"]); + Config.Runewords.push([Runeword.Malice, "broadsword"]); + Config.Runewords.push([Runeword.Malice, "longsword"]); + Config.Runewords.push([Runeword.Malice, "warsword"]); + Config.Runewords.push([Runeword.Malice, "giantsword"]); + Config.Runewords.push([Runeword.Malice, "flamberge"]); + Config.Runewords.push([Runeword.Malice, "espandon"]); + Config.Runewords.push([Runeword.Malice, "tusksword"]); + Config.Runewords.push([Runeword.Malice, "zweihander"]); - Config.KeepRunewords.push("[type] == sword # [enhanceddamage] >= 33 && [tohit] >= 50"); + Config.KeepRunewords.push("[type] == sword # [enhanceddamage] >= 33 && [tohit] >= 50"); })(); diff --git a/libs/SoloPlay/BuildFiles/Runewords/MercDoom.js b/libs/SoloPlay/BuildFiles/Runewords/MercDoom.js index 3da876fd..cf5b95aa 100644 --- a/libs/SoloPlay/BuildFiles/Runewords/MercDoom.js +++ b/libs/SoloPlay/BuildFiles/Runewords/MercDoom.js @@ -1,63 +1,63 @@ (function() { - const Doom = [ - "[name] == HelRune # # [maxquantity] == 1", - "[name] == OhmRune", - "[name] == LoRune", - "[name] == UmRune", - "[name] == ChamRune", - ]; - NTIP.buildList(Doom); + const Doom = [ + "[name] == HelRune # # [maxquantity] == 1", + "[name] == OhmRune", + "[name] == LoRune", + "[name] == UmRune", + "[name] == ChamRune", + ]; + NTIP.buildList(Doom); - // Have Cham, Lo, and Ohm Rune before looking for normal base - if (me.getItem(sdk.items.runes.Cham) && me.getItem(sdk.items.runes.Lo) && me.getItem(sdk.items.runes.Ohm)) { - if (!Check.haveBase("polearm", 5)) { - NTIP.addLine("([name] == thresher || [name] == crypticaxe || [name] == greatpoleaxe || [name] == giantthresher) && [flag] == ethereal && [quality] == normal # [sockets] == 0 # [maxquantity] == 1"); - } - NTIP.addLine("([name] == thresher || [name] == crypticaxe || [name] == greatpoleaxe || [name] == giantthresher) && [flag] == ethereal && [quality] >= normal && [quality] <= superior # [sockets] == 5 # [maxquantity] == 1"); - } else { - NTIP.addLine("([name] == thresher || [name] == crypticaxe || [name] == greatpoleaxe || [name] == giantthresher) && [flag] == ethereal && [quality] == superior # [enhanceddamage] >= 10 && [sockets] == 5 # [maxquantity] == 1"); - } - // Cube to Cham - if (!me.getItem(sdk.items.runes.Cham)) { - Config.Recipes.push([Recipe.Rune, "Mal Rune"]); - Config.Recipes.push([Recipe.Rune, "Ist Rune"]); - Config.Recipes.push([Recipe.Rune, "Gul Rune"]); - Config.Recipes.push([Recipe.Rune, "Vex Rune"]); - Config.Recipes.push([Recipe.Rune, "Ohm Rune"]); - if ((me.barbarian && me.haveAll([{ name: sdk.locale.items.Grief }, { name: sdk.locale.items.Fortitude }])) - || (["Witchyzon", "Wfzon"].includes(SetUp.finalBuild) && me.checkItem({ name: sdk.locale.items.ChainsofHonor }).have) - || (SetUp.currentBuild === "Faithbowzon")) { - Config.Recipes.push([Recipe.Rune, "Lo Rune"]); - } - Config.Recipes.push([Recipe.Rune, "Sur Rune"]); - Config.Recipes.push([Recipe.Rune, "Ber Rune"]); - Config.Recipes.push([Recipe.Rune, "Jah Rune"]); - } - // Cube to Lo - if (!me.getItem(sdk.items.runes.Lo)) { - if ((me.barbarian) || (SetUp.currentBuild === "Faithbowzon" && me.checkItem({ name: sdk.locale.items.CalltoArms }).have) - || (["Witchyzon", "Wfzon"].includes(SetUp.finalBuild) && me.haveAll([{ name: sdk.locale.items.ChainsofHonor }, { name: sdk.locale.items.CalltoArms }]))) { - Config.Recipes.push([Recipe.Rune, "Mal Rune"]); - Config.Recipes.push([Recipe.Rune, "Ist Rune"]); - Config.Recipes.push([Recipe.Rune, "Gul Rune"]); - Config.Recipes.push([Recipe.Rune, "Vex Rune"]); - Config.Recipes.push([Recipe.Rune, "Ohm Rune"]); - } - } - // Cube to Ohm - if (!me.getItem(sdk.items.runes.Ohm)) { - Config.Recipes.push([Recipe.Rune, "Mal Rune"]); - Config.Recipes.push([Recipe.Rune, "Ist Rune"]); - Config.Recipes.push([Recipe.Rune, "Gul Rune"]); - Config.Recipes.push([Recipe.Rune, "Vex Rune"]); - } - Config.Recipes.push([Recipe.Socket.Weapon, "giantthresher"]); - Config.Recipes.push([Recipe.Socket.Weapon, "greatpoleaxe"]); - Config.Recipes.push([Recipe.Socket.Weapon, "crypticaxe"]); - Config.Recipes.push([Recipe.Socket.Weapon, "thresher"]); - Config.Runewords.push([Runeword.Doom, "giantthresher"]); - Config.Runewords.push([Runeword.Doom, "greatpoleaxe"]); - Config.Runewords.push([Runeword.Doom, "crypticaxe"]); - Config.Runewords.push([Runeword.Doom, "thresher"]); - Config.KeepRunewords.push("[type] == polearm # [holyfreezeaura] == 12"); + // Have Cham, Lo, and Ohm Rune before looking for normal base + if (me.getItem(sdk.items.runes.Cham) && me.getItem(sdk.items.runes.Lo) && me.getItem(sdk.items.runes.Ohm)) { + if (!Check.haveBase("polearm", 5)) { + NTIP.addLine("([name] == thresher || [name] == crypticaxe || [name] == greatpoleaxe || [name] == giantthresher) && [flag] == ethereal && [quality] == normal # [sockets] == 0 # [maxquantity] == 1"); + } + NTIP.addLine("([name] == thresher || [name] == crypticaxe || [name] == greatpoleaxe || [name] == giantthresher) && [flag] == ethereal && [quality] >= normal && [quality] <= superior # [sockets] == 5 # [maxquantity] == 1"); + } else { + NTIP.addLine("([name] == thresher || [name] == crypticaxe || [name] == greatpoleaxe || [name] == giantthresher) && [flag] == ethereal && [quality] == superior # [enhanceddamage] >= 10 && [sockets] == 5 # [maxquantity] == 1"); + } + // Cube to Cham + if (!me.getItem(sdk.items.runes.Cham)) { + Config.Recipes.push([Recipe.Rune, "Mal Rune"]); + Config.Recipes.push([Recipe.Rune, "Ist Rune"]); + Config.Recipes.push([Recipe.Rune, "Gul Rune"]); + Config.Recipes.push([Recipe.Rune, "Vex Rune"]); + Config.Recipes.push([Recipe.Rune, "Ohm Rune"]); + if ((me.barbarian && me.haveAll([{ name: sdk.locale.items.Grief }, { name: sdk.locale.items.Fortitude }])) + || (["Witchyzon", "Wfzon"].includes(SetUp.finalBuild) && me.checkItem({ name: sdk.locale.items.ChainsofHonor }).have) + || (SetUp.currentBuild === "Faithbowzon")) { + Config.Recipes.push([Recipe.Rune, "Lo Rune"]); + } + Config.Recipes.push([Recipe.Rune, "Sur Rune"]); + Config.Recipes.push([Recipe.Rune, "Ber Rune"]); + Config.Recipes.push([Recipe.Rune, "Jah Rune"]); + } + // Cube to Lo + if (!me.getItem(sdk.items.runes.Lo)) { + if ((me.barbarian) || (SetUp.currentBuild === "Faithbowzon" && me.checkItem({ name: sdk.locale.items.CalltoArms }).have) + || (["Witchyzon", "Wfzon"].includes(SetUp.finalBuild) && me.haveAll([{ name: sdk.locale.items.ChainsofHonor }, { name: sdk.locale.items.CalltoArms }]))) { + Config.Recipes.push([Recipe.Rune, "Mal Rune"]); + Config.Recipes.push([Recipe.Rune, "Ist Rune"]); + Config.Recipes.push([Recipe.Rune, "Gul Rune"]); + Config.Recipes.push([Recipe.Rune, "Vex Rune"]); + Config.Recipes.push([Recipe.Rune, "Ohm Rune"]); + } + } + // Cube to Ohm + if (!me.getItem(sdk.items.runes.Ohm)) { + Config.Recipes.push([Recipe.Rune, "Mal Rune"]); + Config.Recipes.push([Recipe.Rune, "Ist Rune"]); + Config.Recipes.push([Recipe.Rune, "Gul Rune"]); + Config.Recipes.push([Recipe.Rune, "Vex Rune"]); + } + Config.Recipes.push([Recipe.Socket.Weapon, "giantthresher"]); + Config.Recipes.push([Recipe.Socket.Weapon, "greatpoleaxe"]); + Config.Recipes.push([Recipe.Socket.Weapon, "crypticaxe"]); + Config.Recipes.push([Recipe.Socket.Weapon, "thresher"]); + Config.Runewords.push([Runeword.Doom, "giantthresher"]); + Config.Runewords.push([Runeword.Doom, "greatpoleaxe"]); + Config.Runewords.push([Runeword.Doom, "crypticaxe"]); + Config.Runewords.push([Runeword.Doom, "thresher"]); + Config.KeepRunewords.push("[type] == polearm # [holyfreezeaura] == 12"); })(); diff --git a/libs/SoloPlay/BuildFiles/Runewords/MercFortitude.js b/libs/SoloPlay/BuildFiles/Runewords/MercFortitude.js index 70af0a5d..a01b6a08 100644 --- a/libs/SoloPlay/BuildFiles/Runewords/MercFortitude.js +++ b/libs/SoloPlay/BuildFiles/Runewords/MercFortitude.js @@ -1,52 +1,52 @@ (function() { - const fort = [ - "[name] == ElRune # # [maxquantity] == 1", - "[name] == SolRune # # [maxquantity] == 1", - "[name] == DolRune # # [maxquantity] == 1", - "[name] == LoRune", - "([name] == hellforgeplate || [name] == krakenshell || [name] == archonplate || [name] == balrogskin || [name] == boneweave || [name] == greathauberk || [name] == loricatedmail || [name] == diamondmail || [name] == wirefleece || [name] == scarabhusk || [name] == wyrmhide || [name] == duskshroud) && [quality] == normal && [flag] == ethereal # [Defense] >= 1000 && [sockets] == 4 # [maxquantity] == 1", - "([name] == hellforgeplate || [name] == krakenshell || [name] == archonplate || [name] == balrogskin || [name] == boneweave || [name] == greathauberk || [name] == loricatedmail || [name] == diamondmail || [name] == wirefleece || [name] == scarabhusk || [name] == wyrmhide || [name] == duskshroud) && [quality] == normal && [flag] == ethereal # [Defense] >= 700 && [sockets] == 0 # [maxquantity] == 1", - ]; + const fort = [ + "[name] == ElRune # # [maxquantity] == 1", + "[name] == SolRune # # [maxquantity] == 1", + "[name] == DolRune # # [maxquantity] == 1", + "[name] == LoRune", + "([name] == hellforgeplate || [name] == krakenshell || [name] == archonplate || [name] == balrogskin || [name] == boneweave || [name] == greathauberk || [name] == loricatedmail || [name] == diamondmail || [name] == wirefleece || [name] == scarabhusk || [name] == wyrmhide || [name] == duskshroud) && [quality] == normal && [flag] == ethereal # [Defense] >= 1000 && [sockets] == 4 # [maxquantity] == 1", + "([name] == hellforgeplate || [name] == krakenshell || [name] == archonplate || [name] == balrogskin || [name] == boneweave || [name] == greathauberk || [name] == loricatedmail || [name] == diamondmail || [name] == wirefleece || [name] == scarabhusk || [name] == wyrmhide || [name] == duskshroud) && [quality] == normal && [flag] == ethereal # [Defense] >= 700 && [sockets] == 0 # [maxquantity] == 1", + ]; - if (["Zealer", "Smiter", "Frenzy", "Whirlwind", "Uberconc", "Wolf"].includes(SetUp.finalBuild)) { - // Make Grief first, if using it for final build - if (me.checkItem({ name: sdk.locale.items.Grief, itemtype: sdk.items.type.Sword }).have) { - NTIP.buildList(fort); - } - } else if (["Blova", "Lightning"].includes(SetUp.currentBuild)) { - // Make Chains of Honor first for Blova/Lightning, or already have ber so Lo isn't needed for cubing - if (me.checkItem({ name: sdk.locale.items.ChainsofHonor }).have || me.getItem(sdk.items.runes.Ber)) { - NTIP.buildList(fort); - } - } else { - NTIP.buildList(fort); - } + if (["Zealer", "Smiter", "Frenzy", "Whirlwind", "Uberconc", "Wolf"].includes(SetUp.finalBuild)) { + // Make Grief first, if using it for final build + if (me.checkItem({ name: sdk.locale.items.Grief, itemtype: sdk.items.type.Sword }).have) { + NTIP.buildList(fort); + } + } else if (["Blova", "Lightning"].includes(SetUp.currentBuild)) { + // Make Chains of Honor first for Blova/Lightning, or already have ber so Lo isn't needed for cubing + if (me.checkItem({ name: sdk.locale.items.ChainsofHonor }).have || me.getItem(sdk.items.runes.Ber)) { + NTIP.buildList(fort); + } + } else { + NTIP.buildList(fort); + } - Config.Recipes.push([Recipe.Socket.Armor, "hellforgeplate"]); - Config.Recipes.push([Recipe.Socket.Armor, "krakenshell"]); - Config.Recipes.push([Recipe.Socket.Armor, "archonplate"]); - Config.Recipes.push([Recipe.Socket.Armor, "balrogskin"]); - Config.Recipes.push([Recipe.Socket.Armor, "boneweave"]); - Config.Recipes.push([Recipe.Socket.Armor, "greathauberk"]); - Config.Recipes.push([Recipe.Socket.Armor, "loricatedmail"]); - Config.Recipes.push([Recipe.Socket.Armor, "diamondmail"]); - Config.Recipes.push([Recipe.Socket.Armor, "wirefleece"]); - Config.Recipes.push([Recipe.Socket.Armor, "scarabhusk"]); - Config.Recipes.push([Recipe.Socket.Armor, "wyrmhide"]); - Config.Recipes.push([Recipe.Socket.Armor, "duskshroud"]); + Config.Recipes.push([Recipe.Socket.Armor, "hellforgeplate"]); + Config.Recipes.push([Recipe.Socket.Armor, "krakenshell"]); + Config.Recipes.push([Recipe.Socket.Armor, "archonplate"]); + Config.Recipes.push([Recipe.Socket.Armor, "balrogskin"]); + Config.Recipes.push([Recipe.Socket.Armor, "boneweave"]); + Config.Recipes.push([Recipe.Socket.Armor, "greathauberk"]); + Config.Recipes.push([Recipe.Socket.Armor, "loricatedmail"]); + Config.Recipes.push([Recipe.Socket.Armor, "diamondmail"]); + Config.Recipes.push([Recipe.Socket.Armor, "wirefleece"]); + Config.Recipes.push([Recipe.Socket.Armor, "scarabhusk"]); + Config.Recipes.push([Recipe.Socket.Armor, "wyrmhide"]); + Config.Recipes.push([Recipe.Socket.Armor, "duskshroud"]); - Config.Runewords.push([Runeword.Fortitude, "hellforgeplate"]); - Config.Runewords.push([Runeword.Fortitude, "krakenshell"]); - Config.Runewords.push([Runeword.Fortitude, "archonplate"]); - Config.Runewords.push([Runeword.Fortitude, "balrogskin"]); - Config.Runewords.push([Runeword.Fortitude, "boneweave"]); - Config.Runewords.push([Runeword.Fortitude, "greathauberk"]); - Config.Runewords.push([Runeword.Fortitude, "loricatedmail"]); - Config.Runewords.push([Runeword.Fortitude, "diamondmail"]); - Config.Runewords.push([Runeword.Fortitude, "wirefleece"]); - Config.Runewords.push([Runeword.Fortitude, "scarabhusk"]); - Config.Runewords.push([Runeword.Fortitude, "wyrmhide"]); - Config.Runewords.push([Runeword.Fortitude, "duskshroud"]); + Config.Runewords.push([Runeword.Fortitude, "hellforgeplate"]); + Config.Runewords.push([Runeword.Fortitude, "krakenshell"]); + Config.Runewords.push([Runeword.Fortitude, "archonplate"]); + Config.Runewords.push([Runeword.Fortitude, "balrogskin"]); + Config.Runewords.push([Runeword.Fortitude, "boneweave"]); + Config.Runewords.push([Runeword.Fortitude, "greathauberk"]); + Config.Runewords.push([Runeword.Fortitude, "loricatedmail"]); + Config.Runewords.push([Runeword.Fortitude, "diamondmail"]); + Config.Runewords.push([Runeword.Fortitude, "wirefleece"]); + Config.Runewords.push([Runeword.Fortitude, "scarabhusk"]); + Config.Runewords.push([Runeword.Fortitude, "wyrmhide"]); + Config.Runewords.push([Runeword.Fortitude, "duskshroud"]); - Config.KeepRunewords.push("[type] == armor # [enhanceddefense] >= 200 && [enhanceddamage] >= 300"); + Config.KeepRunewords.push("[type] == armor # [enhanceddefense] >= 200 && [enhanceddamage] >= 300"); })(); diff --git a/libs/SoloPlay/BuildFiles/Runewords/MercInfinity.js b/libs/SoloPlay/BuildFiles/Runewords/MercInfinity.js index 1e183a15..4b05d890 100644 --- a/libs/SoloPlay/BuildFiles/Runewords/MercInfinity.js +++ b/libs/SoloPlay/BuildFiles/Runewords/MercInfinity.js @@ -1,36 +1,36 @@ (function() { - const Inf = [ - "[name] == BerRune", - "[name] == MalRune", - "[name] == IstRune", - "([name] == thresher || [name] == crypticaxe || [name] == greatpoleaxe || [name] == giantthresher) && [flag] == ethereal && [quality] == normal # [Sockets] == 0 # [maxquantity] == 1", - "([name] == thresher || [name] == crypticaxe || [name] == greatpoleaxe || [name] == giantthresher) && [quality] >= normal && [quality] <= Superior # [Sockets] == 4 # [maxquantity] == 1", - ]; - NTIP.buildList(Inf); + const Inf = [ + "[name] == BerRune", + "[name] == MalRune", + "[name] == IstRune", + "([name] == thresher || [name] == crypticaxe || [name] == greatpoleaxe || [name] == giantthresher) && [flag] == ethereal && [quality] == normal # [Sockets] == 0 # [maxquantity] == 1", + "([name] == thresher || [name] == crypticaxe || [name] == greatpoleaxe || [name] == giantthresher) && [quality] >= normal && [quality] <= Superior # [Sockets] == 4 # [maxquantity] == 1", + ]; + NTIP.buildList(Inf); - // Cube to Ber rune - if (me.findItems(sdk.items.runes.Ber).length < 2) { - if (me.checkItem({ name: sdk.locale.items.CalltoArms }).have || me.barbarian) { - Config.Recipes.push([Recipe.Rune, "Mal Rune"]); - Config.Recipes.push([Recipe.Rune, "Ist Rune"]); - Config.Recipes.push([Recipe.Rune, "Gul Rune"]); - Config.Recipes.push([Recipe.Rune, "Vex Rune"]); - Config.Recipes.push([Recipe.Rune, "Ohm Rune"]); - } + // Cube to Ber rune + if (me.findItems(sdk.items.runes.Ber).length < 2) { + if (me.checkItem({ name: sdk.locale.items.CalltoArms }).have || me.barbarian) { + Config.Recipes.push([Recipe.Rune, "Mal Rune"]); + Config.Recipes.push([Recipe.Rune, "Ist Rune"]); + Config.Recipes.push([Recipe.Rune, "Gul Rune"]); + Config.Recipes.push([Recipe.Rune, "Vex Rune"]); + Config.Recipes.push([Recipe.Rune, "Ohm Rune"]); + } - Config.Recipes.push([Recipe.Rune, "Lo Rune"]); - Config.Recipes.push([Recipe.Rune, "Sur Rune"]); - } + Config.Recipes.push([Recipe.Rune, "Lo Rune"]); + Config.Recipes.push([Recipe.Rune, "Sur Rune"]); + } - Config.Recipes.push([Recipe.Socket.Weapon, "giantthresher"]); - Config.Recipes.push([Recipe.Socket.Weapon, "greatpoleaxe"]); - Config.Recipes.push([Recipe.Socket.Weapon, "crypticaxe"]); - Config.Recipes.push([Recipe.Socket.Weapon, "thresher"]); + Config.Recipes.push([Recipe.Socket.Weapon, "giantthresher"]); + Config.Recipes.push([Recipe.Socket.Weapon, "greatpoleaxe"]); + Config.Recipes.push([Recipe.Socket.Weapon, "crypticaxe"]); + Config.Recipes.push([Recipe.Socket.Weapon, "thresher"]); - Config.Runewords.push([Runeword.Infinity, "giantthresher"]); - Config.Runewords.push([Runeword.Infinity, "greatpoleaxe"]); - Config.Runewords.push([Runeword.Infinity, "crypticaxe"]); - Config.Runewords.push([Runeword.Infinity, "thresher"]); + Config.Runewords.push([Runeword.Infinity, "giantthresher"]); + Config.Runewords.push([Runeword.Infinity, "greatpoleaxe"]); + Config.Runewords.push([Runeword.Infinity, "crypticaxe"]); + Config.Runewords.push([Runeword.Infinity, "thresher"]); - Config.KeepRunewords.push("[type] == polearm # [convictionaura] >= 12"); + Config.KeepRunewords.push("[type] == polearm # [convictionaura] >= 12"); })(); diff --git a/libs/SoloPlay/BuildFiles/Runewords/MercInsight.js b/libs/SoloPlay/BuildFiles/Runewords/MercInsight.js index 9b0d4f12..a39dcc4f 100644 --- a/libs/SoloPlay/BuildFiles/Runewords/MercInsight.js +++ b/libs/SoloPlay/BuildFiles/Runewords/MercInsight.js @@ -1,53 +1,53 @@ (function () { - let low = [ - sdk.items.Voulge, sdk.items.Scythe, sdk.items.Poleaxe, - sdk.items.Halberd, sdk.items.WarScythe - ].map(el => "[name] == " + el).join(" || "); + let low = [ + sdk.items.Voulge, sdk.items.Scythe, sdk.items.Poleaxe, + sdk.items.Halberd, sdk.items.WarScythe + ].map(el => "[name] == " + el).join(" || "); - let mid = [ - sdk.items.Bill, sdk.items.BattleScythe, - sdk.items.Partizan, sdk.items.GrimScythe - ].map(el => "[name] == " + el).join(" || "); + let mid = [ + sdk.items.Bill, sdk.items.BattleScythe, + sdk.items.Partizan, sdk.items.GrimScythe + ].map(el => "[name] == " + el).join(" || "); - let high = [ - sdk.items.Thresher, sdk.items.CrypticAxe, - sdk.items.GreatPoleaxe, sdk.items.GiantThresher - ].map(el => "[name] == " + el).join(" || "); - - const Insight = [ - ("(" + high + ") && [flag] == ethereal && [quality] == normal # [sockets] == 0 # [maxquantity] == 1"), - ("(" + high + ") && [quality] >= normal && [quality] <= superior && [flag] != runeword # [sockets] == 4 # [maxquantity] == 1"), - ("(" + mid + ") && [quality] >= normal && [quality] <= superior && [flag] != runeword # [sockets] == 4 # [maxquantity] == 1"), - ]; + let high = [ + sdk.items.Thresher, sdk.items.CrypticAxe, + sdk.items.GreatPoleaxe, sdk.items.GiantThresher + ].map(el => "[name] == " + el).join(" || "); + + const Insight = [ + ("(" + high + ") && [flag] == ethereal && [quality] == normal # [sockets] == 0 # [maxquantity] == 1"), + ("(" + high + ") && [quality] >= normal && [quality] <= superior && [flag] != runeword # [sockets] == 4 # [maxquantity] == 1"), + ("(" + mid + ") && [quality] >= normal && [quality] <= superior && [flag] != runeword # [sockets] == 4 # [maxquantity] == 1"), + ]; - Config.Recipes.push([Recipe.Socket.Weapon, "giantthresher"]); - Config.Recipes.push([Recipe.Socket.Weapon, "greatpoleaxe"]); - Config.Recipes.push([Recipe.Socket.Weapon, "crypticaxe"]); - Config.Recipes.push([Recipe.Socket.Weapon, "thresher"]); + Config.Recipes.push([Recipe.Socket.Weapon, "giantthresher"]); + Config.Recipes.push([Recipe.Socket.Weapon, "greatpoleaxe"]); + Config.Recipes.push([Recipe.Socket.Weapon, "crypticaxe"]); + Config.Recipes.push([Recipe.Socket.Weapon, "thresher"]); - Config.Runewords.push([Runeword.Insight, "giantthresher"]); - Config.Runewords.push([Runeword.Insight, "greatpoleaxe"]); - Config.Runewords.push([Runeword.Insight, "crypticaxe"]); - Config.Runewords.push([Runeword.Insight, "thresher"]); - Config.Runewords.push([Runeword.Insight, "grimscythe"]); - Config.Runewords.push([Runeword.Insight, "partizan"]); - Config.Runewords.push([Runeword.Insight, "battlescythe"]); - Config.Runewords.push([Runeword.Insight, "bill"]); - - if (!me.hell) { - Insight.push("(" + low + ") && [quality] >= normal && [quality] <= superior && [flag] != runeword # [sockets] == 4 # [maxquantity] == 1"); - Config.Runewords.push([Runeword.Insight, "Warscythe"]); - Config.Runewords.push([Runeword.Insight, "halberd"]); - Config.Runewords.push([Runeword.Insight, "poleaxe"]); - Config.Runewords.push([Runeword.Insight, "scythe"]); - Config.Runewords.push([Runeword.Insight, "voulge"]); - } - - NTIP.buildList(Insight); + Config.Runewords.push([Runeword.Insight, "giantthresher"]); + Config.Runewords.push([Runeword.Insight, "greatpoleaxe"]); + Config.Runewords.push([Runeword.Insight, "crypticaxe"]); + Config.Runewords.push([Runeword.Insight, "thresher"]); + Config.Runewords.push([Runeword.Insight, "grimscythe"]); + Config.Runewords.push([Runeword.Insight, "partizan"]); + Config.Runewords.push([Runeword.Insight, "battlescythe"]); + Config.Runewords.push([Runeword.Insight, "bill"]); + + if (!me.hell) { + Insight.push("(" + low + ") && [quality] >= normal && [quality] <= superior && [flag] != runeword # [sockets] == 4 # [maxquantity] == 1"); + Config.Runewords.push([Runeword.Insight, "Warscythe"]); + Config.Runewords.push([Runeword.Insight, "halberd"]); + Config.Runewords.push([Runeword.Insight, "poleaxe"]); + Config.Runewords.push([Runeword.Insight, "scythe"]); + Config.Runewords.push([Runeword.Insight, "voulge"]); + } + + NTIP.buildList(Insight); - if (!me.hell && Item.getMercEquipped(sdk.body.RightArm).prefixnum !== sdk.locale.items.Insight && !Check.haveBase("polearm", 4)) { - NTIP.addLine("[name] == voulge && [flag] != ethereal && [quality] == normal && [level] >= 26 && [level] <= 40 # [sockets] == 0 # [maxquantity] == 1"); - } + if (!me.hell && Item.getMercEquipped(sdk.body.RightArm).prefixnum !== sdk.locale.items.Insight && !Check.haveBase("polearm", 4)) { + NTIP.addLine("[name] == voulge && [flag] != ethereal && [quality] == normal && [level] >= 26 && [level] <= 40 # [sockets] == 0 # [maxquantity] == 1"); + } - Config.KeepRunewords.push("[type] == polearm # [meditationaura] >= 12"); + Config.KeepRunewords.push("[type] == polearm # [meditationaura] >= 12"); })(); diff --git a/libs/SoloPlay/BuildFiles/Runewords/MercPride.js b/libs/SoloPlay/BuildFiles/Runewords/MercPride.js index b9525352..39f21f31 100644 --- a/libs/SoloPlay/BuildFiles/Runewords/MercPride.js +++ b/libs/SoloPlay/BuildFiles/Runewords/MercPride.js @@ -1,36 +1,36 @@ (function() { - const Pride = [ - "[name] == ChamRune", - "[name] == SurRune", - "[name] == IoRune ## [maxquantity] == 1", - "[name] == LoRune", - "([name] == thresher || [name] == crypticaxe || [name] == greatpoleaxe || [name] == giantthresher) && [flag] == ethereal && [quality] == normal # [Sockets] == 0 # [maxquantity] == 1", - "([name] == thresher || [name] == crypticaxe || [name] == greatpoleaxe || [name] == giantthresher) && [quality] >= normal && [quality] <= Superior # [Sockets] == 4 # [maxquantity] == 1", - ]; - NTIP.buildList(Pride); + const Pride = [ + "[name] == ChamRune", + "[name] == SurRune", + "[name] == IoRune ## [maxquantity] == 1", + "[name] == LoRune", + "([name] == thresher || [name] == crypticaxe || [name] == greatpoleaxe || [name] == giantthresher) && [flag] == ethereal && [quality] == normal # [Sockets] == 0 # [maxquantity] == 1", + "([name] == thresher || [name] == crypticaxe || [name] == greatpoleaxe || [name] == giantthresher) && [quality] >= normal && [quality] <= Superior # [Sockets] == 4 # [maxquantity] == 1", + ]; + NTIP.buildList(Pride); - // Cube to Sur/Lo rune - if (!me.getItem(sdk.items.runes.Sur) || !me.getItem(sdk.items.runes.Lo)) { - if (me.checkItem({ name: sdk.locale.items.CalltoArms }).have || me.barbarian) { - Config.Recipes.push([Recipe.Rune, "Mal Rune"]); - Config.Recipes.push([Recipe.Rune, "Ist Rune"]); - Config.Recipes.push([Recipe.Rune, "Gul Rune"]); - Config.Recipes.push([Recipe.Rune, "Vex Rune"]); - Config.Recipes.push([Recipe.Rune, "Ohm Rune"]); - } + // Cube to Sur/Lo rune + if (!me.getItem(sdk.items.runes.Sur) || !me.getItem(sdk.items.runes.Lo)) { + if (me.checkItem({ name: sdk.locale.items.CalltoArms }).have || me.barbarian) { + Config.Recipes.push([Recipe.Rune, "Mal Rune"]); + Config.Recipes.push([Recipe.Rune, "Ist Rune"]); + Config.Recipes.push([Recipe.Rune, "Gul Rune"]); + Config.Recipes.push([Recipe.Rune, "Vex Rune"]); + Config.Recipes.push([Recipe.Rune, "Ohm Rune"]); + } - !me.getItem(sdk.items.runes.Sur) && Config.Recipes.push([Recipe.Rune, "Lo Rune"]); - } + !me.getItem(sdk.items.runes.Sur) && Config.Recipes.push([Recipe.Rune, "Lo Rune"]); + } - Config.Recipes.push([Recipe.Socket.Weapon, "giantthresher"]); - Config.Recipes.push([Recipe.Socket.Weapon, "greatpoleaxe"]); - Config.Recipes.push([Recipe.Socket.Weapon, "crypticaxe"]); - Config.Recipes.push([Recipe.Socket.Weapon, "thresher"]); + Config.Recipes.push([Recipe.Socket.Weapon, "giantthresher"]); + Config.Recipes.push([Recipe.Socket.Weapon, "greatpoleaxe"]); + Config.Recipes.push([Recipe.Socket.Weapon, "crypticaxe"]); + Config.Recipes.push([Recipe.Socket.Weapon, "thresher"]); - Config.Runewords.push([Runeword.Pride, "giantthresher"]); - Config.Runewords.push([Runeword.Pride, "greatpoleaxe"]); - Config.Runewords.push([Runeword.Pride, "crypticaxe"]); - Config.Runewords.push([Runeword.Pride, "thresher"]); + Config.Runewords.push([Runeword.Pride, "giantthresher"]); + Config.Runewords.push([Runeword.Pride, "greatpoleaxe"]); + Config.Runewords.push([Runeword.Pride, "crypticaxe"]); + Config.Runewords.push([Runeword.Pride, "thresher"]); - Config.KeepRunewords.push("[type] == polearm # [concentrationaura] >= 16"); + Config.KeepRunewords.push("[type] == polearm # [concentrationaura] >= 16"); })(); diff --git a/libs/SoloPlay/BuildFiles/Runewords/MercTreachery.js b/libs/SoloPlay/BuildFiles/Runewords/MercTreachery.js index 9e223c7a..11c72d3d 100644 --- a/libs/SoloPlay/BuildFiles/Runewords/MercTreachery.js +++ b/libs/SoloPlay/BuildFiles/Runewords/MercTreachery.js @@ -1,48 +1,48 @@ (function() { - const treach = [ - "[name] == ShaelRune # # [maxquantity] == 1", - "[name] == ThulRune # # [maxquantity] == 1", - "[name] == LemRune # # [maxquantity] == 1", - ]; - NTIP.buildList(treach); - - const MercTreachery = [ - "([name] == breastplate || [name] == mageplate || [name] == hellforgeplate || [name] == krakenshell || [name] == archonplate || [name] == balrogskin || [name] == boneweave || [name] == greathauberk || [name] == loricatedmail || [name] == diamondmail || [name] == wirefleece || [name] == scarabhusk || [name] == wyrmhide || [name] == duskshroud) && [quality] >= normal && [quality] <= superior # [sockets] == 3 # [maxquantity] == 1", - "!me.normal && ([name] == hellforgeplate || [name] == krakenshell || [name] == archonplate || [name] == balrogskin || [name] == boneweave || [name] == greathauberk || [name] == loricatedmail || [name] == diamondmail || [name] == wirefleece || [name] == scarabhusk || [name] == wyrmhide || [name] == duskshroud) && [quality] == normal && [flag] == ethereal # [sockets] == 0 # [maxquantity] == 1", - ]; + const treach = [ + "[name] == ShaelRune # # [maxquantity] == 1", + "[name] == ThulRune # # [maxquantity] == 1", + "[name] == LemRune # # [maxquantity] == 1", + ]; + NTIP.buildList(treach); + + const MercTreachery = [ + "([name] == breastplate || [name] == mageplate || [name] == hellforgeplate || [name] == krakenshell || [name] == archonplate || [name] == balrogskin || [name] == boneweave || [name] == greathauberk || [name] == loricatedmail || [name] == diamondmail || [name] == wirefleece || [name] == scarabhusk || [name] == wyrmhide || [name] == duskshroud) && [quality] >= normal && [quality] <= superior # [sockets] == 3 # [maxquantity] == 1", + "!me.normal && ([name] == hellforgeplate || [name] == krakenshell || [name] == archonplate || [name] == balrogskin || [name] == boneweave || [name] == greathauberk || [name] == loricatedmail || [name] == diamondmail || [name] == wirefleece || [name] == scarabhusk || [name] == wyrmhide || [name] == duskshroud) && [quality] == normal && [flag] == ethereal # [sockets] == 0 # [maxquantity] == 1", + ]; - // Have Shael and Lem before looking for base - if (me.getItem(sdk.items.runes.Shael) && me.getItem(sdk.items.runes.Lem)) { - NTIP.buildList(MercTreachery); - } + // Have Shael and Lem before looking for base + if (me.getItem(sdk.items.runes.Shael) && me.getItem(sdk.items.runes.Lem)) { + NTIP.buildList(MercTreachery); + } - Config.Recipes.push([Recipe.Socket.Armor, "hellforgeplate"]); - Config.Recipes.push([Recipe.Socket.Armor, "krakenshell"]); - Config.Recipes.push([Recipe.Socket.Armor, "archonplate"]); - Config.Recipes.push([Recipe.Socket.Armor, "balrogskin"]); - Config.Recipes.push([Recipe.Socket.Armor, "boneweave"]); - Config.Recipes.push([Recipe.Socket.Armor, "greathauberk"]); - Config.Recipes.push([Recipe.Socket.Armor, "loricatedmail"]); - Config.Recipes.push([Recipe.Socket.Armor, "diamondmail"]); - Config.Recipes.push([Recipe.Socket.Armor, "wirefleece"]); - Config.Recipes.push([Recipe.Socket.Armor, "scarabhusk"]); - Config.Recipes.push([Recipe.Socket.Armor, "wyrmhide"]); - Config.Recipes.push([Recipe.Socket.Armor, "duskshroud"]); + Config.Recipes.push([Recipe.Socket.Armor, "hellforgeplate"]); + Config.Recipes.push([Recipe.Socket.Armor, "krakenshell"]); + Config.Recipes.push([Recipe.Socket.Armor, "archonplate"]); + Config.Recipes.push([Recipe.Socket.Armor, "balrogskin"]); + Config.Recipes.push([Recipe.Socket.Armor, "boneweave"]); + Config.Recipes.push([Recipe.Socket.Armor, "greathauberk"]); + Config.Recipes.push([Recipe.Socket.Armor, "loricatedmail"]); + Config.Recipes.push([Recipe.Socket.Armor, "diamondmail"]); + Config.Recipes.push([Recipe.Socket.Armor, "wirefleece"]); + Config.Recipes.push([Recipe.Socket.Armor, "scarabhusk"]); + Config.Recipes.push([Recipe.Socket.Armor, "wyrmhide"]); + Config.Recipes.push([Recipe.Socket.Armor, "duskshroud"]); - Config.Runewords.push([Runeword.Treachery, "breastplate"]); - Config.Runewords.push([Runeword.Treachery, "mageplate"]); - Config.Runewords.push([Runeword.Treachery, "hellforgeplate"]); - Config.Runewords.push([Runeword.Treachery, "krakenshell"]); - Config.Runewords.push([Runeword.Treachery, "archonplate"]); - Config.Runewords.push([Runeword.Treachery, "balrogskin"]); - Config.Runewords.push([Runeword.Treachery, "boneweave"]); - Config.Runewords.push([Runeword.Treachery, "greathauberk"]); - Config.Runewords.push([Runeword.Treachery, "loricatedmail"]); - Config.Runewords.push([Runeword.Treachery, "diamondmail"]); - Config.Runewords.push([Runeword.Treachery, "wirefleece"]); - Config.Runewords.push([Runeword.Treachery, "scarabhusk"]); - Config.Runewords.push([Runeword.Treachery, "wyrmhide"]); - Config.Runewords.push([Runeword.Treachery, "duskshroud"]); + Config.Runewords.push([Runeword.Treachery, "breastplate"]); + Config.Runewords.push([Runeword.Treachery, "mageplate"]); + Config.Runewords.push([Runeword.Treachery, "hellforgeplate"]); + Config.Runewords.push([Runeword.Treachery, "krakenshell"]); + Config.Runewords.push([Runeword.Treachery, "archonplate"]); + Config.Runewords.push([Runeword.Treachery, "balrogskin"]); + Config.Runewords.push([Runeword.Treachery, "boneweave"]); + Config.Runewords.push([Runeword.Treachery, "greathauberk"]); + Config.Runewords.push([Runeword.Treachery, "loricatedmail"]); + Config.Runewords.push([Runeword.Treachery, "diamondmail"]); + Config.Runewords.push([Runeword.Treachery, "wirefleece"]); + Config.Runewords.push([Runeword.Treachery, "scarabhusk"]); + Config.Runewords.push([Runeword.Treachery, "wyrmhide"]); + Config.Runewords.push([Runeword.Treachery, "duskshroud"]); - Config.KeepRunewords.push("[type] == armor # [ias] == 45 && [coldresist] == 30"); + Config.KeepRunewords.push("[type] == armor # [ias] == 45 && [coldresist] == 30"); })(); diff --git a/libs/SoloPlay/BuildFiles/Runewords/Myth.js b/libs/SoloPlay/BuildFiles/Runewords/Myth.js index dde3feb9..e4a839ce 100644 --- a/libs/SoloPlay/BuildFiles/Runewords/Myth.js +++ b/libs/SoloPlay/BuildFiles/Runewords/Myth.js @@ -1,35 +1,35 @@ (function() { - const Myth = [ - "[name] == HelRune # # [maxquantity] == 1", - "[name] == AmnRune # # [maxquantity] == 1", - "[name] == NefRune # # [maxquantity] == 1", - ]; - NTIP.buildList(Myth); + const Myth = [ + "[name] == HelRune # # [maxquantity] == 1", + "[name] == AmnRune # # [maxquantity] == 1", + "[name] == NefRune # # [maxquantity] == 1", + ]; + NTIP.buildList(Myth); - // Cube to Hel rune - if (!me.getItem(sdk.items.runes.Hel)) { - Config.Recipes.push([Recipe.Rune, "Dol Rune"]); - } + // Cube to Hel rune + if (!me.getItem(sdk.items.runes.Hel)) { + Config.Recipes.push([Recipe.Rune, "Dol Rune"]); + } - // Have Hel rune before looking for base - if (me.getItem(sdk.items.runes.Hel)) { - NTIP.addLine("([name] == demonhidearmor || [name] == duskshroud || [name] == ghostarmor || [name] == lightplate || [name] == mageplate || [name] == serpentskinarmor || [name] == trellisedarmor || [name] == wyrmhide) && [flag] != ethereal && [quality] >= normal && [quality] <= superior # [sockets] == 3 # [maxquantity] == 1"); - } + // Have Hel rune before looking for base + if (me.getItem(sdk.items.runes.Hel)) { + NTIP.addLine("([name] == demonhidearmor || [name] == duskshroud || [name] == ghostarmor || [name] == lightplate || [name] == mageplate || [name] == serpentskinarmor || [name] == trellisedarmor || [name] == wyrmhide) && [flag] != ethereal && [quality] >= normal && [quality] <= superior # [sockets] == 3 # [maxquantity] == 1"); + } - // Have Hel rune and currently equipped armor is low tier - if (me.getItem(sdk.items.runes.Hel) && me.equipped.get(sdk.body.Armor).tier < 200) { - NTIP.addLine("[name] == breastplate && [flag] != ethereal && [quality] >= normal && [quality] <= superior # [sockets] == 3 # [maxquantity] == 1"); - } + // Have Hel rune and currently equipped armor is low tier + if (me.getItem(sdk.items.runes.Hel) && me.equipped.get(sdk.body.Armor).tier < 200) { + NTIP.addLine("[name] == breastplate && [flag] != ethereal && [quality] >= normal && [quality] <= superior # [sockets] == 3 # [maxquantity] == 1"); + } - Config.Runewords.push([Runeword.Myth, "breastplate"]); - Config.Runewords.push([Runeword.Myth, "demonhidearmor"]); - Config.Runewords.push([Runeword.Myth, "duskshroud"]); - Config.Runewords.push([Runeword.Myth, "ghostarmor"]); - Config.Runewords.push([Runeword.Myth, "lightplate"]); - Config.Runewords.push([Runeword.Myth, "mageplate"]); - Config.Runewords.push([Runeword.Myth, "serpentskinarmor"]); - Config.Runewords.push([Runeword.Myth, "trellisedarmor"]); - Config.Runewords.push([Runeword.Myth, "wyrmhide"]); + Config.Runewords.push([Runeword.Myth, "breastplate"]); + Config.Runewords.push([Runeword.Myth, "demonhidearmor"]); + Config.Runewords.push([Runeword.Myth, "duskshroud"]); + Config.Runewords.push([Runeword.Myth, "ghostarmor"]); + Config.Runewords.push([Runeword.Myth, "lightplate"]); + Config.Runewords.push([Runeword.Myth, "mageplate"]); + Config.Runewords.push([Runeword.Myth, "serpentskinarmor"]); + Config.Runewords.push([Runeword.Myth, "trellisedarmor"]); + Config.Runewords.push([Runeword.Myth, "wyrmhide"]); - Config.KeepRunewords.push("[type] == armor # [barbarianskills] == 2"); + Config.KeepRunewords.push("[type] == armor # [barbarianskills] == 2"); })(); diff --git a/libs/SoloPlay/BuildFiles/Runewords/PDiamondShield.js b/libs/SoloPlay/BuildFiles/Runewords/PDiamondShield.js index ad43c331..fdf5617f 100644 --- a/libs/SoloPlay/BuildFiles/Runewords/PDiamondShield.js +++ b/libs/SoloPlay/BuildFiles/Runewords/PDiamondShield.js @@ -1,16 +1,16 @@ (function() { - const PDiamondShield = [ - "[name] == perfectdiamond # # [maxquantity] == 3", - "[name] == towershield && [quality] >= normal && [quality] <= superior # [sockets] == 3 # [maxquantity] == 1", - ]; - NTIP.buildList(PDiamondShield); + const PDiamondShield = [ + "[name] == perfectdiamond # # [maxquantity] == 3", + "[name] == towershield && [quality] >= normal && [quality] <= superior # [sockets] == 3 # [maxquantity] == 1", + ]; + NTIP.buildList(PDiamondShield); - // cube to Pdiamonds - if (Item.getQuantityOwned(me.getItem(sdk.items.gems.Perfect.Diamond)) < 3) { - Config.Recipes.push([Recipe.Gem, "flawlessdiamond"]); - } + // cube to Pdiamonds + if (Item.getQuantityOwned(me.getItem(sdk.items.gems.Perfect.Diamond)) < 3) { + Config.Recipes.push([Recipe.Gem, "flawlessdiamond"]); + } - Config.Runewords.push([Runeword.PDiamondShield, "towershield"]); + Config.Runewords.push([Runeword.PDiamondShield, "towershield"]); - Config.KeepRunewords.push("[type] == shield # [allres] == 57"); + Config.KeepRunewords.push("[type] == shield # [allres] == 57"); })(); diff --git a/libs/SoloPlay/BuildFiles/Runewords/PhoenixShield.js b/libs/SoloPlay/BuildFiles/Runewords/PhoenixShield.js index b939ddd1..cb2b6667 100644 --- a/libs/SoloPlay/BuildFiles/Runewords/PhoenixShield.js +++ b/libs/SoloPlay/BuildFiles/Runewords/PhoenixShield.js @@ -1,41 +1,41 @@ (function() { - const PhoenixRunes = [ - "[name] == VexRune", - "[name] == LoRune", - "[name] == JahRune", - "[name] == monarch && [flag] != ethereal && [quality] >= normal && [quality] <= superior # [sockets] == 4 # [maxquantity] == 1" - ]; - NTIP.buildList(PhoenixRunes); - // Cube to vex and Keep cubing to Jah rune - if (!me.getItem(sdk.items.runes.Jah)) { - if (Item.getQuantityOwned(me.getItem(sdk.items.runes.Vex)) < 2) { - Config.Recipes.push([Recipe.Rune, "Mal Rune"]); - Config.Recipes.push([Recipe.Rune, "Ist Rune"]); - Config.Recipes.push([Recipe.Rune, "Gul Rune"]); - } - (Item.getQuantityOwned(me.getItem(sdk.items.runes.Vex)) > 1 && !me.getItem(sdk.items.runes.Jah) && Config.Recipes.push([Recipe.Rune, "Vex Rune"])); - } - // Cube to Lo and Keep cubing to Jah rune - if (Item.getQuantityOwned(me.getItem(sdk.items.runes.Lo)) > 1 && me.checkItem({ name: sdk.locale.items.HeartoftheOak }).have) { - if (!me.getItem(sdk.items.runes.Jah)) { - Config.Recipes.push([Recipe.Rune, "Vex Rune"]); - Config.Recipes.push([Recipe.Rune, "Ohm Rune"]); - } - !me.getItem(sdk.items.runes.Jah) && Config.Recipes.push([Recipe.Rune, "Lo Rune"]); - } - // Cube to Jah rune - if (me.checkItem({ name: sdk.locale.items.Enigma }).have && !me.getItem(sdk.items.runes.Jah)) { - Config.Recipes.push([Recipe.Rune, "Lo Rune"]); - Config.Recipes.push([Recipe.Rune, "Sur Rune"]); - Config.Recipes.push([Recipe.Rune, "Ber Rune"]); - } + const PhoenixRunes = [ + "[name] == VexRune", + "[name] == LoRune", + "[name] == JahRune", + "[name] == monarch && [flag] != ethereal && [quality] >= normal && [quality] <= superior # [sockets] == 4 # [maxquantity] == 1" + ]; + NTIP.buildList(PhoenixRunes); + // Cube to vex and Keep cubing to Jah rune + if (!me.getItem(sdk.items.runes.Jah)) { + if (Item.getQuantityOwned(me.getItem(sdk.items.runes.Vex)) < 2) { + Config.Recipes.push([Recipe.Rune, "Mal Rune"]); + Config.Recipes.push([Recipe.Rune, "Ist Rune"]); + Config.Recipes.push([Recipe.Rune, "Gul Rune"]); + } + (Item.getQuantityOwned(me.getItem(sdk.items.runes.Vex)) > 1 && !me.getItem(sdk.items.runes.Jah) && Config.Recipes.push([Recipe.Rune, "Vex Rune"])); + } + // Cube to Lo and Keep cubing to Jah rune + if (Item.getQuantityOwned(me.getItem(sdk.items.runes.Lo)) > 1 && me.checkItem({ name: sdk.locale.items.HeartoftheOak }).have) { + if (!me.getItem(sdk.items.runes.Jah)) { + Config.Recipes.push([Recipe.Rune, "Vex Rune"]); + Config.Recipes.push([Recipe.Rune, "Ohm Rune"]); + } + !me.getItem(sdk.items.runes.Jah) && Config.Recipes.push([Recipe.Rune, "Lo Rune"]); + } + // Cube to Jah rune + if (me.checkItem({ name: sdk.locale.items.Enigma }).have && !me.getItem(sdk.items.runes.Jah)) { + Config.Recipes.push([Recipe.Rune, "Lo Rune"]); + Config.Recipes.push([Recipe.Rune, "Sur Rune"]); + Config.Recipes.push([Recipe.Rune, "Ber Rune"]); + } - if (!Check.haveBase("shield", 4)) { - NTIP.addLine("[name] == monarch && [flag] != ethereal && [quality] == normal # [sockets] == 0 # [maxquantity] == 1"); - } + if (!Check.haveBase("shield", 4)) { + NTIP.addLine("[name] == monarch && [flag] != ethereal && [quality] == normal # [sockets] == 0 # [maxquantity] == 1"); + } - Config.Recipes.push([Recipe.Socket.Shield, "monarch", Roll.NonEth]); - Config.Runewords.push([Runeword.Phoenix, "monarch"]); + Config.Recipes.push([Recipe.Socket.Shield, "monarch", Roll.NonEth]); + Config.Runewords.push([Runeword.Phoenix, "monarch"]); - Config.KeepRunewords.push("[type] == shield # [passivefirepierce] >= 28"); + Config.KeepRunewords.push("[type] == shield # [passivefirepierce] >= 28"); })(); diff --git a/libs/SoloPlay/BuildFiles/Runewords/Rhyme.js b/libs/SoloPlay/BuildFiles/Runewords/Rhyme.js index 8a1088f9..1d25468a 100644 --- a/libs/SoloPlay/BuildFiles/Runewords/Rhyme.js +++ b/libs/SoloPlay/BuildFiles/Runewords/Rhyme.js @@ -1,42 +1,42 @@ (function() { - const rhyme = [ - "[name] == ShaelRune # # [maxquantity] == 1", - "[name] == EthRune # # [maxquantity] == 1", - "[type] == voodooheads && [quality] >= normal && [quality] <= superior # [sockets] == 2 # [maxquantity] == 1", - "[type] == voodooheads && [quality] == normal # ([necromancerskills]+[poisonandboneskilltab]+[skillbonespear]+[skillbonespirit]+[skillteeth]+[skillbonewall]+[skillboneprison]+[skillamplifydamage]) >= 1 && [sockets] == 0 # [maxquantity] == 1", - ]; - NTIP.buildList(rhyme); + const rhyme = [ + "[name] == ShaelRune # # [maxquantity] == 1", + "[name] == EthRune # # [maxquantity] == 1", + "[type] == voodooheads && [quality] >= normal && [quality] <= superior # [sockets] == 2 # [maxquantity] == 1", + "[type] == voodooheads && [quality] == normal # ([necromancerskills]+[poisonandboneskilltab]+[skillbonespear]+[skillbonespirit]+[skillteeth]+[skillbonewall]+[skillboneprison]+[skillamplifydamage]) >= 1 && [sockets] == 0 # [maxquantity] == 1", + ]; + NTIP.buildList(rhyme); - Config.Runewords.push([Runeword.Rhyme, "preservedhead"]); - Config.Runewords.push([Runeword.Rhyme, "zombiehead"]); - Config.Runewords.push([Runeword.Rhyme, "unravellerhead"]); - Config.Runewords.push([Runeword.Rhyme, "gargoylehead"]); - Config.Runewords.push([Runeword.Rhyme, "demonhead"]); - Config.Runewords.push([Runeword.Rhyme, "mummifiedtrophy"]); - Config.Runewords.push([Runeword.Rhyme, "fetishtrophy"]); - Config.Runewords.push([Runeword.Rhyme, "sextontrophy"]); - Config.Runewords.push([Runeword.Rhyme, "cantortrophy"]); - Config.Runewords.push([Runeword.Rhyme, "hierophanttrophy"]); - Config.Runewords.push([Runeword.Rhyme, "minionskull"]); - Config.Runewords.push([Runeword.Rhyme, "hellspawnskull"]); - Config.Runewords.push([Runeword.Rhyme, "overseerskull"]); - Config.Runewords.push([Runeword.Rhyme, "succubusskull"]); - Config.Runewords.push([Runeword.Rhyme, "bloodlordskull"]); + Config.Runewords.push([Runeword.Rhyme, "preservedhead"]); + Config.Runewords.push([Runeword.Rhyme, "zombiehead"]); + Config.Runewords.push([Runeword.Rhyme, "unravellerhead"]); + Config.Runewords.push([Runeword.Rhyme, "gargoylehead"]); + Config.Runewords.push([Runeword.Rhyme, "demonhead"]); + Config.Runewords.push([Runeword.Rhyme, "mummifiedtrophy"]); + Config.Runewords.push([Runeword.Rhyme, "fetishtrophy"]); + Config.Runewords.push([Runeword.Rhyme, "sextontrophy"]); + Config.Runewords.push([Runeword.Rhyme, "cantortrophy"]); + Config.Runewords.push([Runeword.Rhyme, "hierophanttrophy"]); + Config.Runewords.push([Runeword.Rhyme, "minionskull"]); + Config.Runewords.push([Runeword.Rhyme, "hellspawnskull"]); + Config.Runewords.push([Runeword.Rhyme, "overseerskull"]); + Config.Runewords.push([Runeword.Rhyme, "succubusskull"]); + Config.Runewords.push([Runeword.Rhyme, "bloodlordskull"]); - Config.Recipes.push([Recipe.Socket.Shield, "preservedhead"]); - Config.Recipes.push([Recipe.Socket.Shield, "zombiehead"]); - Config.Recipes.push([Recipe.Socket.Shield, "unravellerhead"]); - Config.Recipes.push([Recipe.Socket.Shield, "gargoylehead"]); - Config.Recipes.push([Recipe.Socket.Shield, "demonhead"]); - Config.Recipes.push([Recipe.Socket.Shield, "mummifiedtrophy"]); - Config.Recipes.push([Recipe.Socket.Shield, "fetishtrophy"]); - Config.Recipes.push([Recipe.Socket.Shield, "cantortrophy"]); - Config.Recipes.push([Recipe.Socket.Shield, "hierophanttrophy"]); - Config.Recipes.push([Recipe.Socket.Shield, "minionskull"]); - Config.Recipes.push([Recipe.Socket.Shield, "hellspawnskull"]); - Config.Recipes.push([Recipe.Socket.Shield, "overseerskull"]); - Config.Recipes.push([Recipe.Socket.Shield, "succubusskull"]); - Config.Recipes.push([Recipe.Socket.Shield, "bloodlordskull"]); + Config.Recipes.push([Recipe.Socket.Shield, "preservedhead"]); + Config.Recipes.push([Recipe.Socket.Shield, "zombiehead"]); + Config.Recipes.push([Recipe.Socket.Shield, "unravellerhead"]); + Config.Recipes.push([Recipe.Socket.Shield, "gargoylehead"]); + Config.Recipes.push([Recipe.Socket.Shield, "demonhead"]); + Config.Recipes.push([Recipe.Socket.Shield, "mummifiedtrophy"]); + Config.Recipes.push([Recipe.Socket.Shield, "fetishtrophy"]); + Config.Recipes.push([Recipe.Socket.Shield, "cantortrophy"]); + Config.Recipes.push([Recipe.Socket.Shield, "hierophanttrophy"]); + Config.Recipes.push([Recipe.Socket.Shield, "minionskull"]); + Config.Recipes.push([Recipe.Socket.Shield, "hellspawnskull"]); + Config.Recipes.push([Recipe.Socket.Shield, "overseerskull"]); + Config.Recipes.push([Recipe.Socket.Shield, "succubusskull"]); + Config.Recipes.push([Recipe.Socket.Shield, "bloodlordskull"]); - Config.KeepRunewords.push("[type] == voodooheads # [fireresist] >= 25 && [itemmagicbonus] >= 25"); + Config.KeepRunewords.push("[type] == voodooheads # [fireresist] >= 25 && [itemmagicbonus] >= 25"); })(); diff --git a/libs/SoloPlay/BuildFiles/Runewords/Sanctuary.js b/libs/SoloPlay/BuildFiles/Runewords/Sanctuary.js index ba825ec7..09443544 100644 --- a/libs/SoloPlay/BuildFiles/Runewords/Sanctuary.js +++ b/libs/SoloPlay/BuildFiles/Runewords/Sanctuary.js @@ -1,29 +1,29 @@ (function() { - const Sanctuary = [ - "[name] == KoRune # # [maxquantity] == 2", - "[name] == MalRune", - "[name] == hyperion && [flag] != ethereal && [quality] >= normal && [quality] <= superior # [sockets] == 3 # [maxquantity] == 1", - ]; - NTIP.buildList(Sanctuary); + const Sanctuary = [ + "[name] == KoRune # # [maxquantity] == 2", + "[name] == MalRune", + "[name] == hyperion && [flag] != ethereal && [quality] >= normal && [quality] <= superior # [sockets] == 3 # [maxquantity] == 1", + ]; + NTIP.buildList(Sanctuary); - // Cube to Mal rune - if (!me.getItem(sdk.items.runes.Mal)) { - Config.Recipes.push([Recipe.Rune, "Um Rune"]); - } + // Cube to Mal rune + if (!me.getItem(sdk.items.runes.Mal)) { + Config.Recipes.push([Recipe.Rune, "Um Rune"]); + } - // Cube to Ko rune - if (!me.getItem(sdk.items.runes.Ko)) { - Config.Recipes.push([Recipe.Rune, "Hel Rune"]); - Config.Recipes.push([Recipe.Rune, "Io Rune"]); - Config.Recipes.push([Recipe.Rune, "Lum Rune"]); - } + // Cube to Ko rune + if (!me.getItem(sdk.items.runes.Ko)) { + Config.Recipes.push([Recipe.Rune, "Hel Rune"]); + Config.Recipes.push([Recipe.Rune, "Io Rune"]); + Config.Recipes.push([Recipe.Rune, "Lum Rune"]); + } - if (!Check.haveBase("shield", 3)) { - NTIP.addLine("[name] == hyperion && [flag] != ethereal && [quality] == normal # [sockets] == 0 # [maxquantity] == 1"); - } + if (!Check.haveBase("shield", 3)) { + NTIP.addLine("[name] == hyperion && [flag] != ethereal && [quality] == normal # [sockets] == 0 # [maxquantity] == 1"); + } - Config.Recipes.push([Recipe.Socket.Shield, "hyperion", Roll.NonEth]); - Config.Runewords.push([Runeword.Sanctuary, "hyperion"]); + Config.Recipes.push([Recipe.Socket.Shield, "hyperion", Roll.NonEth]); + Config.Runewords.push([Runeword.Sanctuary, "hyperion"]); - Config.KeepRunewords.push("[type] == shield # [fhr] >= 20 && [enhanceddefense] >= 130 && [fireresist] >= 50"); + Config.KeepRunewords.push("[type] == shield # [fhr] >= 20 && [enhanceddefense] >= 130 && [fireresist] >= 50"); })(); diff --git a/libs/SoloPlay/BuildFiles/Runewords/Silence.js b/libs/SoloPlay/BuildFiles/Runewords/Silence.js index cf8ead05..faabccd1 100644 --- a/libs/SoloPlay/BuildFiles/Runewords/Silence.js +++ b/libs/SoloPlay/BuildFiles/Runewords/Silence.js @@ -1,41 +1,41 @@ (function() { - const Silence = [ - "[name] == EldRune # # [maxquantity] == 1", - "[name] == TirRune # # [maxquantity] == 1", - "[name] == DolRune # # [maxquantity] == 1", - "[name] == HelRune # # [maxquantity] == 1", - "[name] == IstRune", - "[name] == VexRune", - ]; - NTIP.buildList(Silence); + const Silence = [ + "[name] == EldRune # # [maxquantity] == 1", + "[name] == TirRune # # [maxquantity] == 1", + "[name] == DolRune # # [maxquantity] == 1", + "[name] == HelRune # # [maxquantity] == 1", + "[name] == IstRune", + "[name] == VexRune", + ]; + NTIP.buildList(Silence); - // Have Vex before collecting base - if (me.getItem(sdk.items.runes.Vex)) { - NTIP.addLine("[name] == phaseblade && [quality] <= superior && [flag] != ethereal # [sockets] == 6 # [maxquantity] == 1"); + // Have Vex before collecting base + if (me.getItem(sdk.items.runes.Vex)) { + NTIP.addLine("[name] == phaseblade && [quality] <= superior && [flag] != ethereal # [sockets] == 6 # [maxquantity] == 1"); - // Have Ist+Vex rune but do not have a base yet - if (me.getItem(sdk.items.runes.Ist) && !Check.haveBase("phaseblade", 6)) { - NTIP.addLine("[name] == phaseblade && [quality] == normal && [flag] != ethereal # [sockets] == 0 # [maxquantity] == 1"); - Config.Recipes.push([Recipe.Socket.Weapon, "phaseblade"]); - } - } + // Have Ist+Vex rune but do not have a base yet + if (me.getItem(sdk.items.runes.Ist) && !Check.haveBase("phaseblade", 6)) { + NTIP.addLine("[name] == phaseblade && [quality] == normal && [flag] != ethereal # [sockets] == 0 # [maxquantity] == 1"); + Config.Recipes.push([Recipe.Socket.Weapon, "phaseblade"]); + } + } - // Cube to Ist rune - if (!me.getItem(sdk.items.runes.Ist)) { - Config.Recipes.push([Recipe.Rune, "Um Rune"]); - Config.Recipes.push([Recipe.Rune, "Mal Rune"]); - } + // Cube to Ist rune + if (!me.getItem(sdk.items.runes.Ist)) { + Config.Recipes.push([Recipe.Rune, "Um Rune"]); + Config.Recipes.push([Recipe.Rune, "Mal Rune"]); + } - // Cube to Vex rune - if (!me.getItem(sdk.items.runes.Vex)) { - Config.Recipes.push([Recipe.Rune, "Lem Rune"]); - Config.Recipes.push([Recipe.Rune, "Pul Rune"]); - Config.Recipes.push([Recipe.Rune, "Um Rune"]); - Config.Recipes.push([Recipe.Rune, "Mal Rune"]); - Config.Recipes.push([Recipe.Rune, "Ist Rune"]); - Config.Recipes.push([Recipe.Rune, "Gul Rune"]); - } + // Cube to Vex rune + if (!me.getItem(sdk.items.runes.Vex)) { + Config.Recipes.push([Recipe.Rune, "Lem Rune"]); + Config.Recipes.push([Recipe.Rune, "Pul Rune"]); + Config.Recipes.push([Recipe.Rune, "Um Rune"]); + Config.Recipes.push([Recipe.Rune, "Mal Rune"]); + Config.Recipes.push([Recipe.Rune, "Ist Rune"]); + Config.Recipes.push([Recipe.Rune, "Gul Rune"]); + } - Config.Runewords.push([Runeword.Silence, "phaseblade"]); - Config.KeepRunewords.push("[type] == sword # [itemallskills] == 2 && [ias] == 20 && [fireresist] == 75"); + Config.Runewords.push([Runeword.Silence, "phaseblade"]); + Config.KeepRunewords.push("[type] == sword # [itemallskills] == 2 && [ias] == 20 && [fireresist] == 75"); })(); diff --git a/libs/SoloPlay/BuildFiles/Runewords/Smoke.js b/libs/SoloPlay/BuildFiles/Runewords/Smoke.js index 00353992..07f74e16 100644 --- a/libs/SoloPlay/BuildFiles/Runewords/Smoke.js +++ b/libs/SoloPlay/BuildFiles/Runewords/Smoke.js @@ -1,30 +1,30 @@ (function() { - if (!Check.haveItem("armor", "runeword", "Smoke") && !me.hell) { - // Cube to Lum Rune - if (!me.getItem(sdk.items.runes.Lum)) { - Config.Recipes.push([Recipe.Rune, "Io Rune"]); - } + if (!Check.haveItem("armor", "runeword", "Smoke") && !me.hell) { + // Cube to Lum Rune + if (!me.getItem(sdk.items.runes.Lum)) { + Config.Recipes.push([Recipe.Rune, "Io Rune"]); + } - const smokeRunes = [ - "[name] == NefRune # # [maxquantity] == 1", - "[name] == LumRune # # [maxquantity] == 1", - ]; - NTIP.buildList(smokeRunes); - } + const smokeRunes = [ + "[name] == NefRune # # [maxquantity] == 1", + "[name] == LumRune # # [maxquantity] == 1", + ]; + NTIP.buildList(smokeRunes); + } - // Have Lum rune before looking for base - if (me.getItem(sdk.items.runes.Lum)) { - NTIP.addLine("([name] == demonhidearmor || [name] == duskshroud || [name] == ghostarmor || [name] == lightplate || [name] == mageplate || [name] == serpentskinarmor || [name] == trellisedarmor || [name] == wyrmhide) && [flag] != ethereal && [quality] >= normal && [quality] <= superior # [sockets] == 2 # [maxquantity] == 1"); - } + // Have Lum rune before looking for base + if (me.getItem(sdk.items.runes.Lum)) { + NTIP.addLine("([name] == demonhidearmor || [name] == duskshroud || [name] == ghostarmor || [name] == lightplate || [name] == mageplate || [name] == serpentskinarmor || [name] == trellisedarmor || [name] == wyrmhide) && [flag] != ethereal && [quality] >= normal && [quality] <= superior # [sockets] == 2 # [maxquantity] == 1"); + } - Config.Runewords.push([Runeword.Smoke, "demonhidearmor"]); - Config.Runewords.push([Runeword.Smoke, "duskshroud"]); - Config.Runewords.push([Runeword.Smoke, "ghostarmor"]); - Config.Runewords.push([Runeword.Smoke, "lightplate"]); - Config.Runewords.push([Runeword.Smoke, "mageplate"]); - Config.Runewords.push([Runeword.Smoke, "serpentskinarmor"]); - Config.Runewords.push([Runeword.Smoke, "trellisedarmor"]); - Config.Runewords.push([Runeword.Smoke, "wyrmhide"]); + Config.Runewords.push([Runeword.Smoke, "demonhidearmor"]); + Config.Runewords.push([Runeword.Smoke, "duskshroud"]); + Config.Runewords.push([Runeword.Smoke, "ghostarmor"]); + Config.Runewords.push([Runeword.Smoke, "lightplate"]); + Config.Runewords.push([Runeword.Smoke, "mageplate"]); + Config.Runewords.push([Runeword.Smoke, "serpentskinarmor"]); + Config.Runewords.push([Runeword.Smoke, "trellisedarmor"]); + Config.Runewords.push([Runeword.Smoke, "wyrmhide"]); - Config.KeepRunewords.push("[type] == armor # [fireresist] == 50"); + Config.KeepRunewords.push("[type] == armor # [fireresist] == 50"); })(); diff --git a/libs/SoloPlay/BuildFiles/Runewords/SpiritShield.js b/libs/SoloPlay/BuildFiles/Runewords/SpiritShield.js index b0af22d8..d992bf80 100644 --- a/libs/SoloPlay/BuildFiles/Runewords/SpiritShield.js +++ b/libs/SoloPlay/BuildFiles/Runewords/SpiritShield.js @@ -1,47 +1,47 @@ (function() { - const SpiritRunes = [ - "[name] == TalRune # # [maxquantity] == 1", - "[name] == ThulRune # # [maxquantity] == 1", - "[name] == OrtRune # # [maxquantity] == 1", - "[name] == AmnRune # # [maxquantity] == 1", - ]; - NTIP.buildList(SpiritRunes); + const SpiritRunes = [ + "[name] == TalRune # # [maxquantity] == 1", + "[name] == ThulRune # # [maxquantity] == 1", + "[name] == OrtRune # # [maxquantity] == 1", + "[name] == AmnRune # # [maxquantity] == 1", + ]; + NTIP.buildList(SpiritRunes); - if (me.paladin) { - NTIP.addLine("([name] == targe || [name] == rondache || [name] == heraldicshield || [name] == aerinshield || [name] == akarantarge || [name] == akaranrondache || [name] == gildedshield ||[name] == protectorshield || [name] == sacredtarge) && [flag] != ethereal && [quality] >= normal && [quality] <= superior # [fireresist] > 0 && [sockets] == 4"); - NTIP.addLine("([name] == targe || [name] == rondache || [name] == heraldicshield || [name] == aerinshield || [name] == akarantarge || [name] == akaranrondache || [name] == gildedshield ||[name] == protectorshield || [name] == sacredtarge) && [flag] != ethereal && [quality] == normal # [fireresist] > 0 && [sockets] == 0"); + if (me.paladin) { + NTIP.addLine("([name] == targe || [name] == rondache || [name] == heraldicshield || [name] == aerinshield || [name] == akarantarge || [name] == akaranrondache || [name] == gildedshield ||[name] == protectorshield || [name] == sacredtarge) && [flag] != ethereal && [quality] >= normal && [quality] <= superior # [fireresist] > 0 && [sockets] == 4"); + NTIP.addLine("([name] == targe || [name] == rondache || [name] == heraldicshield || [name] == aerinshield || [name] == akarantarge || [name] == akaranrondache || [name] == gildedshield ||[name] == protectorshield || [name] == sacredtarge) && [flag] != ethereal && [quality] == normal # [fireresist] > 0 && [sockets] == 0"); - Config.Runewords.push([Runeword.Spirit, "targe"]); - Config.Runewords.push([Runeword.Spirit, "rondache"]); - Config.Runewords.push([Runeword.Spirit, "heraldicshield"]); - Config.Runewords.push([Runeword.Spirit, "aerinshield"]); - Config.Runewords.push([Runeword.Spirit, "akarantarge"]); - Config.Runewords.push([Runeword.Spirit, "akaranrondache"]); - Config.Runewords.push([Runeword.Spirit, "protectorshield"]); - Config.Runewords.push([Runeword.Spirit, "gildedshield"]); - Config.Runewords.push([Runeword.Spirit, "sacredtarge"]); + Config.Runewords.push([Runeword.Spirit, "targe"]); + Config.Runewords.push([Runeword.Spirit, "rondache"]); + Config.Runewords.push([Runeword.Spirit, "heraldicshield"]); + Config.Runewords.push([Runeword.Spirit, "aerinshield"]); + Config.Runewords.push([Runeword.Spirit, "akarantarge"]); + Config.Runewords.push([Runeword.Spirit, "akaranrondache"]); + Config.Runewords.push([Runeword.Spirit, "protectorshield"]); + Config.Runewords.push([Runeword.Spirit, "gildedshield"]); + Config.Runewords.push([Runeword.Spirit, "sacredtarge"]); - Config.Recipes.push([Recipe.Socket.Shield, "targe"]); - Config.Recipes.push([Recipe.Socket.Shield, "rondache"]); - Config.Recipes.push([Recipe.Socket.Shield, "heraldicshield"]); - Config.Recipes.push([Recipe.Socket.Shield, "aerinshield"]); - Config.Recipes.push([Recipe.Socket.Shield, "akarantarge"]); - Config.Recipes.push([Recipe.Socket.Shield, "akaranrondache"]); - Config.Recipes.push([Recipe.Socket.Shield, "protectorshield"]); - Config.Recipes.push([Recipe.Socket.Shield, "gildedshield"]); - Config.Recipes.push([Recipe.Socket.Shield, "sacredtarge"]); + Config.Recipes.push([Recipe.Socket.Shield, "targe"]); + Config.Recipes.push([Recipe.Socket.Shield, "rondache"]); + Config.Recipes.push([Recipe.Socket.Shield, "heraldicshield"]); + Config.Recipes.push([Recipe.Socket.Shield, "aerinshield"]); + Config.Recipes.push([Recipe.Socket.Shield, "akarantarge"]); + Config.Recipes.push([Recipe.Socket.Shield, "akaranrondache"]); + Config.Recipes.push([Recipe.Socket.Shield, "protectorshield"]); + Config.Recipes.push([Recipe.Socket.Shield, "gildedshield"]); + Config.Recipes.push([Recipe.Socket.Shield, "sacredtarge"]); - Config.KeepRunewords.push("[type] == auricshields # [fcr] >= 25 && [maxmana] >= 89"); - } else { - NTIP.addLine("[name] == monarch && [flag] != ethereal && [quality] >= normal && [quality] <= superior # [sockets] == 4 # [maxquantity] == 1"); + Config.KeepRunewords.push("[type] == auricshields # [fcr] >= 25 && [maxmana] >= 89"); + } else { + NTIP.addLine("[name] == monarch && [flag] != ethereal && [quality] >= normal && [quality] <= superior # [sockets] == 4 # [maxquantity] == 1"); - if (!Check.haveBase("shield", 4)) { - NTIP.addLine("[name] == monarch && [flag] != ethereal && [quality] == normal # [sockets] == 0 # [maxquantity] == 1"); - } + if (!Check.haveBase("shield", 4)) { + NTIP.addLine("[name] == monarch && [flag] != ethereal && [quality] == normal # [sockets] == 0 # [maxquantity] == 1"); + } - Config.Recipes.push([Recipe.Socket.Shield, "monarch", Roll.NonEth]); - Config.Runewords.push([Runeword.Spirit, "monarch"]); - } + Config.Recipes.push([Recipe.Socket.Shield, "monarch", Roll.NonEth]); + Config.Runewords.push([Runeword.Spirit, "monarch"]); + } - Config.KeepRunewords.push("[type] == shield # [fcr] >= 25 && [maxmana] >= 89"); + Config.KeepRunewords.push("[type] == shield # [fcr] >= 25 && [maxmana] >= 89"); })(); diff --git a/libs/SoloPlay/BuildFiles/Runewords/SpiritSword.js b/libs/SoloPlay/BuildFiles/Runewords/SpiritSword.js index 3dee1dc0..3eb18496 100644 --- a/libs/SoloPlay/BuildFiles/Runewords/SpiritSword.js +++ b/libs/SoloPlay/BuildFiles/Runewords/SpiritSword.js @@ -1,45 +1,45 @@ (function() { - if (!Check.haveItem("sword", "runeword", "Spirit") && !me.hell) { - const SpiritSword = [ - "[name] == TalRune # # [maxquantity] == 1", - "[name] == ThulRune # # [maxquantity] == 1", - "[name] == OrtRune # # [maxquantity] == 1", - "[name] == AmnRune # # [maxquantity] == 1", - ]; - NTIP.buildList(SpiritSword); + if (!Check.haveItem("sword", "runeword", "Spirit") && !me.hell) { + const SpiritSword = [ + "[name] == TalRune # # [maxquantity] == 1", + "[name] == ThulRune # # [maxquantity] == 1", + "[name] == OrtRune # # [maxquantity] == 1", + "[name] == AmnRune # # [maxquantity] == 1", + ]; + NTIP.buildList(SpiritSword); - // Cube to Amn Rune - if (!me.getItem(sdk.items.runes.Amn)) { - Config.Recipes.push([Recipe.Rune, "Ral Rune"]); - Config.Recipes.push([Recipe.Rune, "Ort Rune"]); - Config.Recipes.push([Recipe.Rune, "Thul Rune"]); - } + // Cube to Amn Rune + if (!me.getItem(sdk.items.runes.Amn)) { + Config.Recipes.push([Recipe.Rune, "Ral Rune"]); + Config.Recipes.push([Recipe.Rune, "Ort Rune"]); + Config.Recipes.push([Recipe.Rune, "Thul Rune"]); + } - if (!me.barbarian) { - NTIP.addLine("([name] == broadsword || [name] == crystalsword) && [flag] != ethereal && [quality] == normal && [level] >= 26 && [level] <= 40 # ([sockets] == 0 || [sockets] == 4) # [maxquantity] == 1"); - } else { - // Have Thul and Amn before looking for base - if (me.getItem(sdk.items.runes.Thul) && me.getItem(sdk.items.runes.Amn)) { - NTIP.addLine("([name] == broadsword || [name] == crystalsword) && [flag] != ethereal && [quality] >= normal && [quality] <= superior # [sockets] == 4 # [maxquantity] == 1"); - } - } - - Config.Recipes.push([Recipe.Socket.Weapon, "crystalsword", Roll.NonEth]); - Config.Recipes.push([Recipe.Socket.Weapon, "broadsword", Roll.NonEth]); - } else { - if (!me.barbarian) { - NTIP.addLine("([name] == broadsword || [name] == crystalsword) && [flag] != ethereal && [quality] >= normal && [quality] <= superior # [sockets] == 4 # [maxquantity] == 1"); - } else { - !me.getItem(sdk.items.runes.Amn) && Config.Recipes.push([Recipe.Rune, "Thul Rune"]); - // Have Thul and Amn before looking for base - if (me.getItem(sdk.items.runes.Thul) && me.getItem(sdk.items.runes.Amn)) { - NTIP.addLine("([name] == broadsword || [name] == crystalsword) && [flag] != ethereal && [quality] >= normal && [quality] <= superior # [sockets] == 4 # [maxquantity] == 1"); - } - } - } + if (!me.barbarian) { + NTIP.addLine("([name] == broadsword || [name] == crystalsword) && [flag] != ethereal && [quality] == normal && [level] >= 26 && [level] <= 40 # ([sockets] == 0 || [sockets] == 4) # [maxquantity] == 1"); + } else { + // Have Thul and Amn before looking for base + if (me.getItem(sdk.items.runes.Thul) && me.getItem(sdk.items.runes.Amn)) { + NTIP.addLine("([name] == broadsword || [name] == crystalsword) && [flag] != ethereal && [quality] >= normal && [quality] <= superior # [sockets] == 4 # [maxquantity] == 1"); + } + } + + Config.Recipes.push([Recipe.Socket.Weapon, "crystalsword", Roll.NonEth]); + Config.Recipes.push([Recipe.Socket.Weapon, "broadsword", Roll.NonEth]); + } else { + if (!me.barbarian) { + NTIP.addLine("([name] == broadsword || [name] == crystalsword) && [flag] != ethereal && [quality] >= normal && [quality] <= superior # [sockets] == 4 # [maxquantity] == 1"); + } else { + !me.getItem(sdk.items.runes.Amn) && Config.Recipes.push([Recipe.Rune, "Thul Rune"]); + // Have Thul and Amn before looking for base + if (me.getItem(sdk.items.runes.Thul) && me.getItem(sdk.items.runes.Amn)) { + NTIP.addLine("([name] == broadsword || [name] == crystalsword) && [flag] != ethereal && [quality] >= normal && [quality] <= superior # [sockets] == 4 # [maxquantity] == 1"); + } + } + } - Config.Runewords.push([Runeword.Spirit, "crystalsword"]); - Config.Runewords.push([Runeword.Spirit, "broadsword"]); + Config.Runewords.push([Runeword.Spirit, "crystalsword"]); + Config.Runewords.push([Runeword.Spirit, "broadsword"]); - Config.KeepRunewords.push("[type] == sword # [fcr] >= 25 && [maxmana] >= 89"); + Config.KeepRunewords.push("[type] == sword # [fcr] >= 25 && [maxmana] >= 89"); })(); diff --git a/libs/SoloPlay/BuildFiles/Runewords/Stealth.js b/libs/SoloPlay/BuildFiles/Runewords/Stealth.js index a49ec5f5..cca9178d 100644 --- a/libs/SoloPlay/BuildFiles/Runewords/Stealth.js +++ b/libs/SoloPlay/BuildFiles/Runewords/Stealth.js @@ -1,28 +1,28 @@ (function() { - if (!Check.haveItem("armor", "runeword", "Stealth") && me.normal) { - const stealthRunes = [ - "[name] == TalRune # # [maxquantity] == 1", - "[name] == EthRune # # [maxquantity] == 1", - ]; - NTIP.buildList(stealthRunes); - } + if (!Check.haveItem("armor", "runeword", "Stealth") && me.normal) { + const stealthRunes = [ + "[name] == TalRune # # [maxquantity] == 1", + "[name] == EthRune # # [maxquantity] == 1", + ]; + NTIP.buildList(stealthRunes); + } - const stealthArmor = [ - "!me.hell && ([name] == studdedleather || [name] == lightplate) && [flag] != ethereal && [quality] >= normal && [quality] <= superior # [sockets] == 2 # [maxquantity] == 1", - "([name] == ghostarmor || [name] == serpentskinarmor || [name] == mageplate) && [flag] != ethereal && [quality] >= normal && [quality] <= superior # [sockets] == 2 # [maxquantity] == 1", - ]; - NTIP.buildList(stealthArmor); + const stealthArmor = [ + "!me.hell && ([name] == studdedleather || [name] == lightplate) && [flag] != ethereal && [quality] >= normal && [quality] <= superior # [sockets] == 2 # [maxquantity] == 1", + "([name] == ghostarmor || [name] == serpentskinarmor || [name] == mageplate) && [flag] != ethereal && [quality] >= normal && [quality] <= superior # [sockets] == 2 # [maxquantity] == 1", + ]; + NTIP.buildList(stealthArmor); - if (me.equipped.get(sdk.body.Armor).tier < 200) { - NTIP.addLine("[name] == breastplate && [flag] != ethereal && [quality] >= normal && [quality] <= superior # [sockets] == 2 # [maxquantity] == 1"); - } + if (me.equipped.get(sdk.body.Armor).tier < 200) { + NTIP.addLine("[name] == breastplate && [flag] != ethereal && [quality] >= normal && [quality] <= superior # [sockets] == 2 # [maxquantity] == 1"); + } - Config.Runewords.push([Runeword.Stealth, "mageplate"]); - Config.Runewords.push([Runeword.Stealth, "serpentskinarmor"]); - Config.Runewords.push([Runeword.Stealth, "ghostarmor"]); - Config.Runewords.push([Runeword.Stealth, "lightplate"]); - Config.Runewords.push([Runeword.Stealth, "breastplate"]); - Config.Runewords.push([Runeword.Stealth, "studdedleather"]); + Config.Runewords.push([Runeword.Stealth, "mageplate"]); + Config.Runewords.push([Runeword.Stealth, "serpentskinarmor"]); + Config.Runewords.push([Runeword.Stealth, "ghostarmor"]); + Config.Runewords.push([Runeword.Stealth, "lightplate"]); + Config.Runewords.push([Runeword.Stealth, "breastplate"]); + Config.Runewords.push([Runeword.Stealth, "studdedleather"]); - Config.KeepRunewords.push("[type] == armor # [frw] == 25"); + Config.KeepRunewords.push("[type] == armor # [frw] == 25"); })(); diff --git a/libs/SoloPlay/BuildFiles/Runewords/Steel.js b/libs/SoloPlay/BuildFiles/Runewords/Steel.js index 3e554211..ddb1da6f 100644 --- a/libs/SoloPlay/BuildFiles/Runewords/Steel.js +++ b/libs/SoloPlay/BuildFiles/Runewords/Steel.js @@ -1,44 +1,44 @@ (function() { - const Steel = [ - "[name] == TirRune # # [maxquantity] == 2", - "[name] == ElRune # # [maxquantity] == 2", - ]; - NTIP.buildList(Steel); + const Steel = [ + "[name] == TirRune # # [maxquantity] == 2", + "[name] == ElRune # # [maxquantity] == 2", + ]; + NTIP.buildList(Steel); - if (me.equipped.get(sdk.body.LeftArm).tier < 500 && me.equipped.get(sdk.body.LeftArm).tier > 395) { - NTIP.addLine("[type] == sword && [flag] != ethereal && [quality] >= normal && [quality] <= superior && [wsm] <= 10 && [strreq] <= 150 && [class] == elite # [sockets] == 2 # [maxquantity] == 1"); - } else if (me.equipped.get(sdk.body.LeftArm).tier < 500 && me.equipped.get(sdk.body.LeftArm).tier > 278) { - NTIP.addLine("[type] == sword && [flag] != ethereal && [quality] >= normal && [quality] <= superior && [wsm] <= 10 && [strreq] <= 150 && [class] > normal # [sockets] == 2 # [maxquantity] == 1"); - } else { - NTIP.addLine("[type] == sword && [flag] != ethereal && [quality] == superior && [wsm] <= 10 && [strreq] <= 150 # [enhanceddamage] >= 10 && [sockets] == 2 # [maxquantity] == 1"); - } + if (me.equipped.get(sdk.body.LeftArm).tier < 500 && me.equipped.get(sdk.body.LeftArm).tier > 395) { + NTIP.addLine("[type] == sword && [flag] != ethereal && [quality] >= normal && [quality] <= superior && [wsm] <= 10 && [strreq] <= 150 && [class] == elite # [sockets] == 2 # [maxquantity] == 1"); + } else if (me.equipped.get(sdk.body.LeftArm).tier < 500 && me.equipped.get(sdk.body.LeftArm).tier > 278) { + NTIP.addLine("[type] == sword && [flag] != ethereal && [quality] >= normal && [quality] <= superior && [wsm] <= 10 && [strreq] <= 150 && [class] > normal # [sockets] == 2 # [maxquantity] == 1"); + } else { + NTIP.addLine("[type] == sword && [flag] != ethereal && [quality] == superior && [wsm] <= 10 && [strreq] <= 150 # [enhanceddamage] >= 10 && [sockets] == 2 # [maxquantity] == 1"); + } - Config.Runewords.push([Runeword.Steel, "shortsword"]); - Config.Runewords.push([Runeword.Steel, "scimitar"]); - Config.Runewords.push([Runeword.Steel, "sabre"]); - Config.Runewords.push([Runeword.Steel, "crystalsword"]); - Config.Runewords.push([Runeword.Steel, "broadsword"]); - Config.Runewords.push([Runeword.Steel, "longsword"]); - Config.Runewords.push([Runeword.Steel, "warsword"]); - Config.Runewords.push([Runeword.Steel, "giantsword"]); - Config.Runewords.push([Runeword.Steel, "flamberge"]); - Config.Runewords.push([Runeword.Steel, "gladius"]); - Config.Runewords.push([Runeword.Steel, "cutlass"]); - Config.Runewords.push([Runeword.Steel, "shamshir"]); - Config.Runewords.push([Runeword.Steel, "dimensionalblade"]); - Config.Runewords.push([Runeword.Steel, "battlesword"]); - Config.Runewords.push([Runeword.Steel, "runesword"]); - Config.Runewords.push([Runeword.Steel, "ancientsword"]); - Config.Runewords.push([Runeword.Steel, "espandon"]); - Config.Runewords.push([Runeword.Steel, "tusksword"]); - Config.Runewords.push([Runeword.Steel, "zweihander"]); - Config.Runewords.push([Runeword.Steel, "falcata"]); - Config.Runewords.push([Runeword.Steel, "ataghan"]); - Config.Runewords.push([Runeword.Steel, "elegantblade"]); - Config.Runewords.push([Runeword.Steel, "phaseblade"]); - Config.Runewords.push([Runeword.Steel, "conquestsword"]); - Config.Runewords.push([Runeword.Steel, "crypticsword"]); - Config.Runewords.push([Runeword.Steel, "mythicalsword"]); + Config.Runewords.push([Runeword.Steel, "shortsword"]); + Config.Runewords.push([Runeword.Steel, "scimitar"]); + Config.Runewords.push([Runeword.Steel, "sabre"]); + Config.Runewords.push([Runeword.Steel, "crystalsword"]); + Config.Runewords.push([Runeword.Steel, "broadsword"]); + Config.Runewords.push([Runeword.Steel, "longsword"]); + Config.Runewords.push([Runeword.Steel, "warsword"]); + Config.Runewords.push([Runeword.Steel, "giantsword"]); + Config.Runewords.push([Runeword.Steel, "flamberge"]); + Config.Runewords.push([Runeword.Steel, "gladius"]); + Config.Runewords.push([Runeword.Steel, "cutlass"]); + Config.Runewords.push([Runeword.Steel, "shamshir"]); + Config.Runewords.push([Runeword.Steel, "dimensionalblade"]); + Config.Runewords.push([Runeword.Steel, "battlesword"]); + Config.Runewords.push([Runeword.Steel, "runesword"]); + Config.Runewords.push([Runeword.Steel, "ancientsword"]); + Config.Runewords.push([Runeword.Steel, "espandon"]); + Config.Runewords.push([Runeword.Steel, "tusksword"]); + Config.Runewords.push([Runeword.Steel, "zweihander"]); + Config.Runewords.push([Runeword.Steel, "falcata"]); + Config.Runewords.push([Runeword.Steel, "ataghan"]); + Config.Runewords.push([Runeword.Steel, "elegantblade"]); + Config.Runewords.push([Runeword.Steel, "phaseblade"]); + Config.Runewords.push([Runeword.Steel, "conquestsword"]); + Config.Runewords.push([Runeword.Steel, "crypticsword"]); + Config.Runewords.push([Runeword.Steel, "mythicalsword"]); - Config.KeepRunewords.push("[type] == sword # [enhanceddamage] >= 20 && [ias] >= 25"); + Config.KeepRunewords.push("[type] == sword # [enhanceddamage] >= 20 && [ias] >= 25"); })(); diff --git a/libs/SoloPlay/BuildFiles/Runewords/Treachery.js b/libs/SoloPlay/BuildFiles/Runewords/Treachery.js index 805550d1..beab3398 100644 --- a/libs/SoloPlay/BuildFiles/Runewords/Treachery.js +++ b/libs/SoloPlay/BuildFiles/Runewords/Treachery.js @@ -1,32 +1,32 @@ (function() { - const treach = [ - "[name] == ShaelRune # # [maxquantity] == 1", - "[name] == ThulRune # # [maxquantity] == 1", - "[name] == LemRune # # [maxquantity] == 1", - ]; - NTIP.buildList(treach); + const treach = [ + "[name] == ShaelRune # # [maxquantity] == 1", + "[name] == ThulRune # # [maxquantity] == 1", + "[name] == LemRune # # [maxquantity] == 1", + ]; + NTIP.buildList(treach); - // Cube to Lem rune - if (!me.getItem(sdk.items.runes.Lem)) { - Config.Recipes.push([Recipe.Rune, "Io Rune"]); - Config.Recipes.push([Recipe.Rune, "Lum Rune"]); - Config.Recipes.push([Recipe.Rune, "Ko Rune"]); - Config.Recipes.push([Recipe.Rune, "Fal Rune"]); - } + // Cube to Lem rune + if (!me.getItem(sdk.items.runes.Lem)) { + Config.Recipes.push([Recipe.Rune, "Io Rune"]); + Config.Recipes.push([Recipe.Rune, "Lum Rune"]); + Config.Recipes.push([Recipe.Rune, "Ko Rune"]); + Config.Recipes.push([Recipe.Rune, "Fal Rune"]); + } - // Have Shael and Lem before looking for base - if (me.getItem(sdk.items.runes.Lem)) { - NTIP.addLine("([name] == demonhidearmor || [name] == duskshroud || [name] == ghostarmor || [name] == lightplate || [name] == mageplate || [name] == serpentskinarmor || [name] == trellisedarmor || [name] == wyrmhide) && [flag] != ethereal && [quality] >= normal && [quality] <= superior # [sockets] == 3 # [maxquantity] == 1"); - } + // Have Shael and Lem before looking for base + if (me.getItem(sdk.items.runes.Lem)) { + NTIP.addLine("([name] == demonhidearmor || [name] == duskshroud || [name] == ghostarmor || [name] == lightplate || [name] == mageplate || [name] == serpentskinarmor || [name] == trellisedarmor || [name] == wyrmhide) && [flag] != ethereal && [quality] >= normal && [quality] <= superior # [sockets] == 3 # [maxquantity] == 1"); + } - Config.Runewords.push([Runeword.Treachery, "demonhidearmor"]); - Config.Runewords.push([Runeword.Treachery, "duskshroud"]); - Config.Runewords.push([Runeword.Treachery, "ghostarmor"]); - Config.Runewords.push([Runeword.Treachery, "lightplate"]); - Config.Runewords.push([Runeword.Treachery, "mageplate"]); - Config.Runewords.push([Runeword.Treachery, "serpentskinarmor"]); - Config.Runewords.push([Runeword.Treachery, "trellisedarmor"]); - Config.Runewords.push([Runeword.Treachery, "wyrmhide"]); + Config.Runewords.push([Runeword.Treachery, "demonhidearmor"]); + Config.Runewords.push([Runeword.Treachery, "duskshroud"]); + Config.Runewords.push([Runeword.Treachery, "ghostarmor"]); + Config.Runewords.push([Runeword.Treachery, "lightplate"]); + Config.Runewords.push([Runeword.Treachery, "mageplate"]); + Config.Runewords.push([Runeword.Treachery, "serpentskinarmor"]); + Config.Runewords.push([Runeword.Treachery, "trellisedarmor"]); + Config.Runewords.push([Runeword.Treachery, "wyrmhide"]); - Config.KeepRunewords.push("[type] == armor # [ias] == 45 && [coldresist] == 30"); + Config.KeepRunewords.push("[type] == armor # [ias] == 45 && [coldresist] == 30"); })(); diff --git a/libs/SoloPlay/BuildFiles/Runewords/VoiceOfReason.js b/libs/SoloPlay/BuildFiles/Runewords/VoiceOfReason.js index 54669c49..6f5d8d07 100644 --- a/libs/SoloPlay/BuildFiles/Runewords/VoiceOfReason.js +++ b/libs/SoloPlay/BuildFiles/Runewords/VoiceOfReason.js @@ -1,47 +1,47 @@ (function() { - const VoiceofReason = [ - "[name] == LemRune", - "[name] == KoRune", - "[name] == ElRune # # [maxquantity] == 1", - "[name] == EldRune # # [maxquantity] == 1", - ]; - NTIP.buildList(VoiceofReason); + const VoiceofReason = [ + "[name] == LemRune", + "[name] == KoRune", + "[name] == ElRune # # [maxquantity] == 1", + "[name] == EldRune # # [maxquantity] == 1", + ]; + NTIP.buildList(VoiceofReason); - if (me.barbarian) { - // Have Lem and Ko runes before looking for normal base - if (me.getItem(sdk.items.runes.Lem) && me.getItem(sdk.items.runes.Ko)) { - NTIP.addLine("[type] == sword && [flag] != ethereal && [quality] >= normal && [quality] <= superior && [wsm] <= 10 && [strreq] <= 150 # [sockets] == 4"); - NTIP.addLine("([name] == legendsword || [name] == highlandblade || [name] == balrogblade || [name] == championsword || [name] == colossussword) && [flag] != ethereal && [quality] >= normal && [quality] <= superior # [sockets] == 4"); - } else { - NTIP.addLine("([name] == legendsword || [name] == highlandblade || [name] == balrogblade || [name] == championsword || [name] == colossussword) && [flag] != ethereal && [quality] == superior # [enhanceddamage] >= 5 && [sockets] == 4"); - } + if (me.barbarian) { + // Have Lem and Ko runes before looking for normal base + if (me.getItem(sdk.items.runes.Lem) && me.getItem(sdk.items.runes.Ko)) { + NTIP.addLine("[type] == sword && [flag] != ethereal && [quality] >= normal && [quality] <= superior && [wsm] <= 10 && [strreq] <= 150 # [sockets] == 4"); + NTIP.addLine("([name] == legendsword || [name] == highlandblade || [name] == balrogblade || [name] == championsword || [name] == colossussword) && [flag] != ethereal && [quality] >= normal && [quality] <= superior # [sockets] == 4"); + } else { + NTIP.addLine("([name] == legendsword || [name] == highlandblade || [name] == balrogblade || [name] == championsword || [name] == colossussword) && [flag] != ethereal && [quality] == superior # [enhanceddamage] >= 5 && [sockets] == 4"); + } - Config.Runewords.push([Runeword.VoiceofReason, "dimensionalblade"]); - Config.Runewords.push([Runeword.VoiceofReason, "battlesword"]); - Config.Runewords.push([Runeword.VoiceofReason, "runesword"]); - Config.Runewords.push([Runeword.VoiceofReason, "conquestsword"]); - Config.Runewords.push([Runeword.VoiceofReason, "crypticsword"]); - Config.Runewords.push([Runeword.VoiceofReason, "phaseblade"]); - Config.Runewords.push([Runeword.VoiceofReason, "tusksword"]); - Config.Runewords.push([Runeword.VoiceofReason, "zweihander"]); - Config.Runewords.push([Runeword.VoiceofReason, "legendsword"]); - Config.Runewords.push([Runeword.VoiceofReason, "highlandblade"]); - Config.Runewords.push([Runeword.VoiceofReason, "balrogblade"]); - Config.Runewords.push([Runeword.VoiceofReason, "championsword"]); - Config.Runewords.push([Runeword.VoiceofReason, "colossussword"]); - } + Config.Runewords.push([Runeword.VoiceofReason, "dimensionalblade"]); + Config.Runewords.push([Runeword.VoiceofReason, "battlesword"]); + Config.Runewords.push([Runeword.VoiceofReason, "runesword"]); + Config.Runewords.push([Runeword.VoiceofReason, "conquestsword"]); + Config.Runewords.push([Runeword.VoiceofReason, "crypticsword"]); + Config.Runewords.push([Runeword.VoiceofReason, "phaseblade"]); + Config.Runewords.push([Runeword.VoiceofReason, "tusksword"]); + Config.Runewords.push([Runeword.VoiceofReason, "zweihander"]); + Config.Runewords.push([Runeword.VoiceofReason, "legendsword"]); + Config.Runewords.push([Runeword.VoiceofReason, "highlandblade"]); + Config.Runewords.push([Runeword.VoiceofReason, "balrogblade"]); + Config.Runewords.push([Runeword.VoiceofReason, "championsword"]); + Config.Runewords.push([Runeword.VoiceofReason, "colossussword"]); + } - if (me.paladin) { - NTIP.addLine("[name] == phaseblade && [quality] == normal # ([sockets] == 0 || [sockets] == 4) # [maxquantity] == 1"); + if (me.paladin) { + NTIP.addLine("[name] == phaseblade && [quality] == normal # ([sockets] == 0 || [sockets] == 4) # [maxquantity] == 1"); - // Cube to Lem rune - if (!me.getItem(sdk.items.runes.Lem)) { - Config.Recipes.push([Recipe.Rune, "Fal Rune"]); - } + // Cube to Lem rune + if (!me.getItem(sdk.items.runes.Lem)) { + Config.Recipes.push([Recipe.Rune, "Fal Rune"]); + } - Config.Recipes.push([Recipe.Socket.Weapon, "phaseblade", Roll.NonEth]); - Config.Runewords.push([Runeword.VoiceofReason, "phaseblade"]); - } + Config.Recipes.push([Recipe.Socket.Weapon, "phaseblade", Roll.NonEth]); + Config.Runewords.push([Runeword.VoiceofReason, "phaseblade"]); + } - Config.KeepRunewords.push("[type] == sword # [passivecoldpierce] >= 24"); + Config.KeepRunewords.push("[type] == sword # [passivecoldpierce] >= 24"); })(); diff --git a/libs/SoloPlay/BuildFiles/Runewords/White.js b/libs/SoloPlay/BuildFiles/Runewords/White.js index 04c6dd89..a4bfa314 100644 --- a/libs/SoloPlay/BuildFiles/Runewords/White.js +++ b/libs/SoloPlay/BuildFiles/Runewords/White.js @@ -1,35 +1,35 @@ (function() { - const white = [ - "[name] == DolRune # # [maxquantity] == 1", - "[name] == IoRune # # [maxquantity] == 1", - "[type] == wand && ([name] != wand && [name] != yewwand && [name] != burntwand) && [quality] >= normal && [quality] <= superior # [sockets] == 2 # [maxquantity] == 1", - "[type] == wand && ([name] != wand && [name] != yewwand && [name] != burntwand) && [quality] == normal # ([necromancerskills]+[poisonandboneskilltab]+[skillbonespear]+[skillbonespirit]+[skillteeth]+[skillbonewall]+[skillboneprison]+[skillamplifydamage]) >= 1 && [sockets] == 0 # [maxquantity] == 1", - ]; - NTIP.buildList(white); + const white = [ + "[name] == DolRune # # [maxquantity] == 1", + "[name] == IoRune # # [maxquantity] == 1", + "[type] == wand && ([name] != wand && [name] != yewwand && [name] != burntwand) && [quality] >= normal && [quality] <= superior # [sockets] == 2 # [maxquantity] == 1", + "[type] == wand && ([name] != wand && [name] != yewwand && [name] != burntwand) && [quality] == normal # ([necromancerskills]+[poisonandboneskilltab]+[skillbonespear]+[skillbonespirit]+[skillteeth]+[skillbonewall]+[skillboneprison]+[skillamplifydamage]) >= 1 && [sockets] == 0 # [maxquantity] == 1", + ]; + NTIP.buildList(white); - // Cube to Io rune - if (!me.getItem(sdk.items.runes.Io)) { - Config.Recipes.push([Recipe.Rune, "Hel Rune"]); - } + // Cube to Io rune + if (!me.getItem(sdk.items.runes.Io)) { + Config.Recipes.push([Recipe.Rune, "Hel Rune"]); + } - Config.Runewords.push([Runeword.White, "bonewand"]); - Config.Runewords.push([Runeword.White, "grimwand"]); - Config.Runewords.push([Runeword.White, "petrifiedwand"]); - Config.Runewords.push([Runeword.White, "tombwand"]); - Config.Runewords.push([Runeword.White, "gravewand"]); - Config.Runewords.push([Runeword.White, "polishedwand"]); - Config.Runewords.push([Runeword.White, "ghostwand"]); - Config.Runewords.push([Runeword.White, "lichwand"]); - Config.Runewords.push([Runeword.White, "unearthedwand"]); + Config.Runewords.push([Runeword.White, "bonewand"]); + Config.Runewords.push([Runeword.White, "grimwand"]); + Config.Runewords.push([Runeword.White, "petrifiedwand"]); + Config.Runewords.push([Runeword.White, "tombwand"]); + Config.Runewords.push([Runeword.White, "gravewand"]); + Config.Runewords.push([Runeword.White, "polishedwand"]); + Config.Runewords.push([Runeword.White, "ghostwand"]); + Config.Runewords.push([Runeword.White, "lichwand"]); + Config.Runewords.push([Runeword.White, "unearthedwand"]); - Config.Recipes.push([Recipe.Socket.Weapon, "bonewand"]); - Config.Recipes.push([Recipe.Socket.Weapon, "petrifiedwand"]); - Config.Recipes.push([Recipe.Socket.Weapon, "tombwand"]); - Config.Recipes.push([Recipe.Socket.Weapon, "Grave wand"]); - Config.Recipes.push([Recipe.Socket.Weapon, "polishedwand"]); - Config.Recipes.push([Recipe.Socket.Weapon, "ghostwand"]); - Config.Recipes.push([Recipe.Socket.Weapon, "lichwand"]); - Config.Recipes.push([Recipe.Socket.Weapon, "unearthedwand"]); + Config.Recipes.push([Recipe.Socket.Weapon, "bonewand"]); + Config.Recipes.push([Recipe.Socket.Weapon, "petrifiedwand"]); + Config.Recipes.push([Recipe.Socket.Weapon, "tombwand"]); + Config.Recipes.push([Recipe.Socket.Weapon, "Grave wand"]); + Config.Recipes.push([Recipe.Socket.Weapon, "polishedwand"]); + Config.Recipes.push([Recipe.Socket.Weapon, "ghostwand"]); + Config.Recipes.push([Recipe.Socket.Weapon, "lichwand"]); + Config.Recipes.push([Recipe.Socket.Weapon, "unearthedwand"]); - Config.KeepRunewords.push("[type] == wand # [fcr] >= 20"); + Config.KeepRunewords.push("[type] == wand # [fcr] >= 20"); })(); diff --git a/libs/SoloPlay/BuildFiles/amazon/amazon.FaithbowzonBuild.js b/libs/SoloPlay/BuildFiles/amazon/amazon.FaithbowzonBuild.js index 1602e584..de5e7807 100644 --- a/libs/SoloPlay/BuildFiles/amazon/amazon.FaithbowzonBuild.js +++ b/libs/SoloPlay/BuildFiles/amazon/amazon.FaithbowzonBuild.js @@ -8,147 +8,147 @@ (function (module) { - module.exports = (function () { - const build = { - caster: false, - skillstab: sdk.skills.tabs.BowandCrossbow, - wantedskills: [sdk.skills.Strafe], - usefulskills: [sdk.skills.Penetrate, sdk.skills.Valkyrie, sdk.skills.Pierce], - precastSkills: [sdk.skills.Valkyrie], - wantedMerc: MercData[sdk.skills.Might], - stats: [ - ["strength", 103], ["dexterity", 152], ["vitality", 150], ["dexterity", "all"] - ], - skills: [ - [sdk.skills.Valkyrie, 1], - [sdk.skills.Strafe, 20], - [sdk.skills.Pierce, 19], - [sdk.skills.Penetrate, 3], - [sdk.skills.CriticalStrike, 19], - [sdk.skills.Valkyrie, 20], - [sdk.skills.Dodge, 4], - [sdk.skills.Avoid, 4], - [sdk.skills.Evade, 4], - [sdk.skills.Dodge, 9], - [sdk.skills.Evade, 9], + module.exports = (function () { + const build = { + caster: false, + skillstab: sdk.skills.tabs.BowandCrossbow, + wantedskills: [sdk.skills.Strafe], + usefulskills: [sdk.skills.Penetrate, sdk.skills.Valkyrie, sdk.skills.Pierce], + precastSkills: [sdk.skills.Valkyrie], + wantedMerc: MercData[sdk.skills.Might], + stats: [ + ["strength", 103], ["dexterity", 152], ["vitality", 150], ["dexterity", "all"] + ], + skills: [ + [sdk.skills.Valkyrie, 1], + [sdk.skills.Strafe, 20], + [sdk.skills.Pierce, 19], + [sdk.skills.Penetrate, 3], + [sdk.skills.CriticalStrike, 19], + [sdk.skills.Valkyrie, 20], + [sdk.skills.Dodge, 4], + [sdk.skills.Avoid, 4], + [sdk.skills.Evade, 4], + [sdk.skills.Dodge, 9], + [sdk.skills.Evade, 9], - ], + ], - charms: { - ResLife: { - max: 3, - have: [], - classid: sdk.items.SmallCharm, - stats: function (check) { - return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MaxHp) === 20); - } - }, + charms: { + ResLife: { + max: 3, + have: [], + classid: sdk.items.SmallCharm, + stats: function (check) { + return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MaxHp) === 20); + } + }, - ResMf: { - max: 2, - have: [], - classid: sdk.items.SmallCharm, - stats: function (check) { - return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MagicBonus) === 7); - } - }, + ResMf: { + max: 2, + have: [], + classid: sdk.items.SmallCharm, + stats: function (check) { + return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MagicBonus) === 7); + } + }, - ResFHR: { - max: 1, - have: [], - classid: sdk.items.SmallCharm, - stats: function (check) { - return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.FHR) === 5); - } - }, + ResFHR: { + max: 1, + have: [], + classid: sdk.items.SmallCharm, + stats: function (check) { + return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.FHR) === 5); + } + }, - SkillerCrossbow: { - max: 1, - have: [], - classid: sdk.items.GrandCharm, - stats: function (check) { - return (!check.unique && check.classid === this.classid && check.getStat(sdk.stats.AddSkillTab, sdk.skills.tabs.BowandCrossbow) === 1 - && check.getStat(sdk.stats.MaxHp) >= 40); - } - }, + SkillerCrossbow: { + max: 1, + have: [], + classid: sdk.items.GrandCharm, + stats: function (check) { + return (!check.unique && check.classid === this.classid && check.getStat(sdk.stats.AddSkillTab, sdk.skills.tabs.BowandCrossbow) === 1 + && check.getStat(sdk.stats.MaxHp) >= 40); + } + }, - SkillerPassive: { - max: 1, - have: [], - classid: sdk.items.GrandCharm, - stats: function (check) { - return (!check.unique && check.classid === this.classid && check.getStat(sdk.stats.AddSkillTab, sdk.skills.tabs.PassiveandMagic) === 1 - && check.getStat(sdk.stats.MaxHp) >= 40); - } - }, - }, + SkillerPassive: { + max: 1, + have: [], + classid: sdk.items.GrandCharm, + stats: function (check) { + return (!check.unique && check.classid === this.classid && check.getStat(sdk.stats.AddSkillTab, sdk.skills.tabs.PassiveandMagic) === 1 + && check.getStat(sdk.stats.MaxHp) >= 40); + } + }, + }, - AutoBuildTemplate: { - 1: { - Update: function () { - Config.AttackSkill = [-1, sdk.skills.Strafe, -1, sdk.skills.Strafe, -1, sdk.skills.MagicArrow, -1]; - Config.LowManaSkill = [0, -1]; - Config.HPBuffer = 1; - Config.MPBuffer = 1; - } - }, - }, + AutoBuildTemplate: { + 1: { + Update: function () { + Config.AttackSkill = [-1, sdk.skills.Strafe, -1, sdk.skills.Strafe, -1, sdk.skills.MagicArrow, -1]; + Config.LowManaSkill = [0, -1]; + Config.HPBuffer = 1; + Config.MPBuffer = 1; + } + }, + }, - respec: function () { - if (me.classic) { - return false; - } else { - return (me.checkItem({ name: sdk.locale.items.Faith }).have || Check.haveItem("diamondbow", "unique", "Witchwild String")); - } - }, + respec: function () { + if (me.classic) { + return false; + } else { + return (me.checkItem({ name: sdk.locale.items.Faith }).have || Check.haveItem("diamondbow", "unique", "Witchwild String")); + } + }, - active: function () { - return this.respec() && me.checkSkill(sdk.skills.Strafe, sdk.skills.subindex.HardPoints); - }, - }; - - let finalGear = [ // autoequip final gear - // Final Weapon - Faith - "([type] == amazonbow || [type] == bow) && [flag] == runeword # [fanaticismaura] >= 12 && [itemallskills] >= 1 # [tier] == tierscore(item, 100000)", - // Weapon - WitchWild String up'd - "[name] == diamondbow && [quality] == unique # [fireresist] == 40 # [tier] == tierscore(item, 100000)", - // Helmet - Vampz Gaze - "[name] == grimhelm && [quality] == unique && [flag] != ethereal # [manaleech] >= 6 && [lifeleech] >= 6 && [damageresist] >= 15 # [tier] == tierscore(item, 100000)", - // Boots - War Traveler - "[name] == battleboots && [quality] == unique && [flag] != ethereal # [itemmagicbonus] >= 35 # [tier] == tierscore(item, 100000)", - // Belt - Nosferatu's Coil - "[name] == vampirefangbelt && [quality] == unique && [flag] != ethereal # [lifeleech] >= 5 # [tier] == tierscore(item, 100000)", - // Armor - CoH - "[type] == armor && [flag] == runeword && [flag] != ethereal # [fireresist] == 65 && [hpregen] == 7 # [tier] == 100000", - // Gloves - Lava Gout - "[name] == battlegauntlets && [quality] == unique && [flag] != ethereal # [ias] == 20 && [enhanceddefense] >= 150 # [tier] == tierscore(item, 3000)", - // Final Amulet - Atma's Scarab - "[type] == amulet && [quality] == unique # [poisonresist] == 75 # [tier] == 110000", - // Amulet - Highlords - "[type] == amulet && [quality] == unique # [lightresist] == 35 # [tier] == 100000", - // Final Rings - Perfect Raven Frost & Bul-Kathos' Wedding Band - "[type] == ring && [quality] == unique # [dexterity] == 20 && [tohit] == 250 # [tier] == 110000", - "[type] == ring && [quality] == unique # [maxstamina] == 50 && [lifeleech] == 5 # [tier] == 110000", - // Rings - Raven Frost && Bul-Kathos' Wedding Band - "[type] == ring && [quality] == unique # [dexterity] >= 15 && [tohit] >= 150 # [tier] == 100000", - "[type] == ring && [quality] == unique # [maxstamina] == 50 && [lifeleech] >= 3 # [tier] == 100000", - // Switch Final Weapon - CTA - "[minimumsockets] >= 5 && [flag] == runeword # [plusskillbattleorders] >= 1 # [secondarytier] == 100000", - // Switch Temporary Weapon - Life Tap charged wand - "[type] == wand && [quality] >= normal # [itemchargedskill] == 82 # [secondarytier] == 75000 + chargeditemscore(item, 82)", - // Switch Shield - Any 1+ all skill - "[type] == shield # [itemallskills] >= 1 # [secondarytier] == tierscore(item, 100000)", - // Merc Armor - Fortitude - "[type] == armor && [flag] == runeword # [enhanceddefense] >= 200 && [enhanceddamage] >= 300 # [merctier] == 100000", - // Merc Final Helmet - Eth Andy's - "[name] == demonhead && [quality] == unique && [flag] == ethereal # [strength] >= 25 && [enhanceddefense] >= 100 # [merctier] == 40000 + mercscore(item)", - // Merc Helmet - Andy's - "[name] == demonhead && [quality] == unique && [flag] != ethereal # [strength] >= 25 && [enhanceddefense] >= 100 # [merctier] == 50000 + mercscore(item)", - ]; + active: function () { + return this.respec() && me.checkSkill(sdk.skills.Strafe, sdk.skills.subindex.HardPoints); + }, + }; + + let finalGear = [ // autoequip final gear + // Final Weapon - Faith + "([type] == amazonbow || [type] == bow) && [flag] == runeword # [fanaticismaura] >= 12 && [itemallskills] >= 1 # [tier] == tierscore(item, 100000)", + // Weapon - WitchWild String up'd + "[name] == diamondbow && [quality] == unique # [fireresist] == 40 # [tier] == tierscore(item, 100000)", + // Helmet - Vampz Gaze + "[name] == grimhelm && [quality] == unique && [flag] != ethereal # [manaleech] >= 6 && [lifeleech] >= 6 && [damageresist] >= 15 # [tier] == tierscore(item, 100000)", + // Boots - War Traveler + "[name] == battleboots && [quality] == unique && [flag] != ethereal # [itemmagicbonus] >= 35 # [tier] == tierscore(item, 100000)", + // Belt - Nosferatu's Coil + "[name] == vampirefangbelt && [quality] == unique && [flag] != ethereal # [lifeleech] >= 5 # [tier] == tierscore(item, 100000)", + // Armor - CoH + "[type] == armor && [flag] == runeword && [flag] != ethereal # [fireresist] == 65 && [hpregen] == 7 # [tier] == 100000", + // Gloves - Lava Gout + "[name] == battlegauntlets && [quality] == unique && [flag] != ethereal # [ias] == 20 && [enhanceddefense] >= 150 # [tier] == tierscore(item, 3000)", + // Final Amulet - Atma's Scarab + "[type] == amulet && [quality] == unique # [poisonresist] == 75 # [tier] == 110000", + // Amulet - Highlords + "[type] == amulet && [quality] == unique # [lightresist] == 35 # [tier] == 100000", + // Final Rings - Perfect Raven Frost & Bul-Kathos' Wedding Band + "[type] == ring && [quality] == unique # [dexterity] == 20 && [tohit] == 250 # [tier] == 110000", + "[type] == ring && [quality] == unique # [maxstamina] == 50 && [lifeleech] == 5 # [tier] == 110000", + // Rings - Raven Frost && Bul-Kathos' Wedding Band + "[type] == ring && [quality] == unique # [dexterity] >= 15 && [tohit] >= 150 # [tier] == 100000", + "[type] == ring && [quality] == unique # [maxstamina] == 50 && [lifeleech] >= 3 # [tier] == 100000", + // Switch Final Weapon - CTA + "[minimumsockets] >= 5 && [flag] == runeword # [plusskillbattleorders] >= 1 # [secondarytier] == 100000", + // Switch Temporary Weapon - Life Tap charged wand + "[type] == wand && [quality] >= normal # [itemchargedskill] == 82 # [secondarytier] == 75000 + chargeditemscore(item, 82)", + // Switch Shield - Any 1+ all skill + "[type] == shield # [itemallskills] >= 1 # [secondarytier] == tierscore(item, 100000)", + // Merc Armor - Fortitude + "[type] == armor && [flag] == runeword # [enhanceddefense] >= 200 && [enhanceddamage] >= 300 # [merctier] == 100000", + // Merc Final Helmet - Eth Andy's + "[name] == demonhead && [quality] == unique && [flag] == ethereal # [strength] >= 25 && [enhanceddefense] >= 100 # [merctier] == 40000 + mercscore(item)", + // Merc Helmet - Andy's + "[name] == demonhead && [quality] == unique && [flag] != ethereal # [strength] >= 25 && [enhanceddefense] >= 100 # [merctier] == 50000 + mercscore(item)", + ]; - NTIP.buildList(finalGear); - NTIP.buildFinalGear(finalGear); + NTIP.buildList(finalGear); + NTIP.buildFinalGear(finalGear); - return build; - })(); + return build; + })(); })(module); diff --git a/libs/SoloPlay/BuildFiles/amazon/amazon.FrostmaidenBuild.js b/libs/SoloPlay/BuildFiles/amazon/amazon.FrostmaidenBuild.js index c44ac72b..6b6cbcb5 100644 --- a/libs/SoloPlay/BuildFiles/amazon/amazon.FrostmaidenBuild.js +++ b/libs/SoloPlay/BuildFiles/amazon/amazon.FrostmaidenBuild.js @@ -7,132 +7,132 @@ (function (module) { - module.exports = (function () { - const build = { - caster: false, - skillstab: sdk.skills.tabs.BowandCrossbow, - wantedskills: [sdk.skills.Strafe, sdk.skills.FreezingArrow, sdk.skills.ExplodingArrow], - usefulskills: [sdk.skills.Penetrate, sdk.skills.Valkyrie, sdk.skills.Pierce, sdk.skills.IceArrow, sdk.skills.ColdArrow], - precastSkills: [sdk.skills.Valkyrie], - wantedMerc: MercData[sdk.skills.Might], - stats: [ - ["strength", 156], ["dexterity", 152], ["vitality", 150], ["dexterity", "all"] - ], - skills: [ - [sdk.skills.Valkyrie, 1], - [sdk.skills.Strafe, 1], - [sdk.skills.Pierce, 1], - [sdk.skills.FreezingArrow, 20], - [sdk.skills.IceArrow, 20], - [sdk.skills.ColdArrow, 20], - [sdk.skills.Strafe, 20], - [sdk.skills.Valkyrie, 20], - ], + module.exports = (function () { + const build = { + caster: false, + skillstab: sdk.skills.tabs.BowandCrossbow, + wantedskills: [sdk.skills.Strafe, sdk.skills.FreezingArrow, sdk.skills.ExplodingArrow], + usefulskills: [sdk.skills.Penetrate, sdk.skills.Valkyrie, sdk.skills.Pierce, sdk.skills.IceArrow, sdk.skills.ColdArrow], + precastSkills: [sdk.skills.Valkyrie], + wantedMerc: MercData[sdk.skills.Might], + stats: [ + ["strength", 156], ["dexterity", 152], ["vitality", 150], ["dexterity", "all"] + ], + skills: [ + [sdk.skills.Valkyrie, 1], + [sdk.skills.Strafe, 1], + [sdk.skills.Pierce, 1], + [sdk.skills.FreezingArrow, 20], + [sdk.skills.IceArrow, 20], + [sdk.skills.ColdArrow, 20], + [sdk.skills.Strafe, 20], + [sdk.skills.Valkyrie, 20], + ], - charms: { - ResLife: { - max: 3, - have: [], - classid: sdk.items.SmallCharm, - stats: function (check) { - return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MaxHp) === 20); - } - }, + charms: { + ResLife: { + max: 3, + have: [], + classid: sdk.items.SmallCharm, + stats: function (check) { + return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MaxHp) === 20); + } + }, - ResMf: { - max: 2, - have: [], - classid: sdk.items.SmallCharm, - stats: function (check) { - return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MagicBonus) === 7); - } - }, + ResMf: { + max: 2, + have: [], + classid: sdk.items.SmallCharm, + stats: function (check) { + return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MagicBonus) === 7); + } + }, - ResFHR: { - max: 1, - have: [], - classid: sdk.items.SmallCharm, - stats: function (check) { - return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.FHR) === 5); - } - }, + ResFHR: { + max: 1, + have: [], + classid: sdk.items.SmallCharm, + stats: function (check) { + return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.FHR) === 5); + } + }, - SkillerCrossbow: { - max: 1, - have: [], - classid: sdk.items.GrandCharm, - stats: function (check) { - return (!check.unique && check.classid === this.classid && check.getStat(sdk.stats.AddSkillTab, sdk.skills.tabs.BowandCrossbow) === 1 - && check.getStat(sdk.stats.MaxHp) >= 40); - } - }, + SkillerCrossbow: { + max: 1, + have: [], + classid: sdk.items.GrandCharm, + stats: function (check) { + return (!check.unique && check.classid === this.classid && check.getStat(sdk.stats.AddSkillTab, sdk.skills.tabs.BowandCrossbow) === 1 + && check.getStat(sdk.stats.MaxHp) >= 40); + } + }, - SkillerPassive: { - max: 1, - have: [], - classid: sdk.items.GrandCharm, - stats: function (check) { - return (!check.unique && check.classid === this.classid && check.getStat(sdk.stats.AddSkillTab, sdk.skills.tabs.PassiveandMagic) === 1 - && check.getStat(sdk.stats.MaxHp) >= 40); - } - }, - }, + SkillerPassive: { + max: 1, + have: [], + classid: sdk.items.GrandCharm, + stats: function (check) { + return (!check.unique && check.classid === this.classid && check.getStat(sdk.stats.AddSkillTab, sdk.skills.tabs.PassiveandMagic) === 1 + && check.getStat(sdk.stats.MaxHp) >= 40); + } + }, + }, - AutoBuildTemplate: { - 1: { - Update: function () { - Config.AttackSkill = [-1, sdk.skills.FreezingArrow, -1, sdk.skills.FreezingArrow, -1, sdk.skills.Strafe, -1]; - Config.LowManaSkill = [0, -1]; - } - }, - }, + AutoBuildTemplate: { + 1: { + Update: function () { + Config.AttackSkill = [-1, sdk.skills.FreezingArrow, -1, sdk.skills.FreezingArrow, -1, sdk.skills.Strafe, -1]; + Config.LowManaSkill = [0, -1]; + } + }, + }, - respec: function () { - if (me.classic) { - return false; - } else { - return me.haveAll([{ name: sdk.locale.items.Dream, itemtype: sdk.items.type.Helm }, { name: sdk.locale.items.Brand }, { name: sdk.locale.items.ChainsofHonor }]); - } - }, + respec: function () { + if (me.classic) { + return false; + } else { + return me.haveAll([{ name: sdk.locale.items.Dream, itemtype: sdk.items.type.Helm }, { name: sdk.locale.items.Brand }, { name: sdk.locale.items.ChainsofHonor }]); + } + }, - active: function () { - return this.respec() && me.checkSkill(sdk.skills.FreezingArrow, sdk.skills.subindex.HardPoints); - }, - }; - - let finalGear = [ // autoequip final gear - // Weapon - Brand - "[name] == grandmatronbow && [flag] == runeword # [enhanceddamage] >= 260 && [itemknockback] == 1 && [itemdeadlystrike] == 20 # [tier] == tierscore(item, 100000)", - // Helmet - Dream - "[type] == helm && [flag] == runeword # [holyshockaura] >= 15 # [tier] == 110000", - // Boots - War Traveler - "[name] == battleboots && [quality] == unique && [flag] != ethereal # [itemmagicbonus] >= 30 # [tier] == 100000", - // Belt - Nosferatu's Coil - "[name] == sharkskinbelt && [quality] == unique && [flag] != ethereal # [dexterity] == 15 # [tier] == tierscore(item, 100000)", - // Armor - Chains of Honor - "[type] == armor && [flag] == runeword && [flag] != ethereal # [fireresist] == 65 # [tier] == 100000", - // Amulet - Highlords - "[type] == amulet && [quality] == unique # [lightresist] == 35 # [tier] == 110000", - // Rings - Ravenfrost & Carrion Wind - "[type] == ring && [quality] == unique # [dexterity] == 20 && [tohit] == 250 # [tier] == 110000", - "[name] == ring && [quality] == unique # [poisonresist] == 55 && [lifeleech] >= 6 # [tier] == 110000", - // Switch - CTA - "[minimumsockets] >= 5 && [flag] == runeword # [plusskillbattleorders] >= 1 # [secondarytier] == 100000", - // Switch Temporary Weapon - Life Tap charged wand - "[type] == wand && [quality] >= normal # [itemchargedskill] == 82 # [secondarytier] == 75000 + chargeditemscore(item, 82)", - // Switch Shield - Any 1+ all skill - "[type] == shield # [itemallskills] >= 1 # [secondarytier] == tierscore(item, 100000)", - // Merc Armor - Fortitude - "[type] == armor && [flag] == runeword # [enhanceddefense] >= 200 && [enhanceddamage] >= 300 # [merctier] == 100000", - // Merc Final Helmet - Eth Andy's - "[name] == demonhead && [quality] == unique && [flag] == ethereal # [strength] >= 25 && [enhanceddefense] >= 100 # [merctier] == 50000 + mercscore(item)", - // Merc Helmet - Andy's - "[name] == demonhead && [quality] == unique && [flag] != ethereal # [strength] >= 25 && [enhanceddefense] >= 100 # [merctier] == 40000 + mercscore(item)", - ]; + active: function () { + return this.respec() && me.checkSkill(sdk.skills.FreezingArrow, sdk.skills.subindex.HardPoints); + }, + }; + + let finalGear = [ // autoequip final gear + // Weapon - Brand + "[name] == grandmatronbow && [flag] == runeword # [enhanceddamage] >= 260 && [itemknockback] == 1 && [itemdeadlystrike] == 20 # [tier] == tierscore(item, 100000)", + // Helmet - Dream + "[type] == helm && [flag] == runeword # [holyshockaura] >= 15 # [tier] == 110000", + // Boots - War Traveler + "[name] == battleboots && [quality] == unique && [flag] != ethereal # [itemmagicbonus] >= 30 # [tier] == 100000", + // Belt - Nosferatu's Coil + "[name] == sharkskinbelt && [quality] == unique && [flag] != ethereal # [dexterity] == 15 # [tier] == tierscore(item, 100000)", + // Armor - Chains of Honor + "[type] == armor && [flag] == runeword && [flag] != ethereal # [fireresist] == 65 # [tier] == 100000", + // Amulet - Highlords + "[type] == amulet && [quality] == unique # [lightresist] == 35 # [tier] == 110000", + // Rings - Ravenfrost & Carrion Wind + "[type] == ring && [quality] == unique # [dexterity] == 20 && [tohit] == 250 # [tier] == 110000", + "[name] == ring && [quality] == unique # [poisonresist] == 55 && [lifeleech] >= 6 # [tier] == 110000", + // Switch - CTA + "[minimumsockets] >= 5 && [flag] == runeword # [plusskillbattleorders] >= 1 # [secondarytier] == 100000", + // Switch Temporary Weapon - Life Tap charged wand + "[type] == wand && [quality] >= normal # [itemchargedskill] == 82 # [secondarytier] == 75000 + chargeditemscore(item, 82)", + // Switch Shield - Any 1+ all skill + "[type] == shield # [itemallskills] >= 1 # [secondarytier] == tierscore(item, 100000)", + // Merc Armor - Fortitude + "[type] == armor && [flag] == runeword # [enhanceddefense] >= 200 && [enhanceddamage] >= 300 # [merctier] == 100000", + // Merc Final Helmet - Eth Andy's + "[name] == demonhead && [quality] == unique && [flag] == ethereal # [strength] >= 25 && [enhanceddefense] >= 100 # [merctier] == 50000 + mercscore(item)", + // Merc Helmet - Andy's + "[name] == demonhead && [quality] == unique && [flag] != ethereal # [strength] >= 25 && [enhanceddefense] >= 100 # [merctier] == 40000 + mercscore(item)", + ]; - NTIP.buildList(finalGear); - NTIP.buildFinalGear(finalGear); + NTIP.buildList(finalGear); + NTIP.buildFinalGear(finalGear); - return build; - })(); + return build; + })(); })(module); diff --git a/libs/SoloPlay/BuildFiles/amazon/amazon.JavazonBuild.js b/libs/SoloPlay/BuildFiles/amazon/amazon.JavazonBuild.js index 7c86d94e..740ad5a9 100644 --- a/libs/SoloPlay/BuildFiles/amazon/amazon.JavazonBuild.js +++ b/libs/SoloPlay/BuildFiles/amazon/amazon.JavazonBuild.js @@ -7,173 +7,173 @@ (function (module) { - module.exports = (function () { - const build = { - caster: false, - skillstab: sdk.skills.tabs.JavelinandSpear, - wantedskills: [sdk.skills.ChargedStrike, sdk.skills.LightningStrike], - usefulskills: [sdk.skills.CriticalStrike, sdk.skills.Penetrate, sdk.skills.Valkyrie, sdk.skills.Pierce], - precastSkills: [sdk.skills.Valkyrie], - usefulStats: [sdk.stats.PierceLtng, sdk.stats.PassiveLightningMastery, sdk.stats.PassiveLightningPierce], - wantedMerc: MercData[sdk.skills.HolyFreeze], - stats: [], - skills: [], + module.exports = (function () { + const build = { + caster: false, + skillstab: sdk.skills.tabs.JavelinandSpear, + wantedskills: [sdk.skills.ChargedStrike, sdk.skills.LightningStrike], + usefulskills: [sdk.skills.CriticalStrike, sdk.skills.Penetrate, sdk.skills.Valkyrie, sdk.skills.Pierce], + precastSkills: [sdk.skills.Valkyrie], + usefulStats: [sdk.stats.PierceLtng, sdk.stats.PassiveLightningMastery, sdk.stats.PassiveLightningPierce], + wantedMerc: MercData[sdk.skills.HolyFreeze], + stats: [], + skills: [], - charms: { - ResLife: { - max: 5, - have: [], - classid: sdk.items.SmallCharm, - stats: function (check) { - return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MaxHp) === 20); - } - }, + charms: { + ResLife: { + max: 5, + have: [], + classid: sdk.items.SmallCharm, + stats: function (check) { + return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MaxHp) === 20); + } + }, - ResMf: { - max: 2, - have: [], - classid: sdk.items.SmallCharm, - stats: function (check) { - return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MagicBonus) === 7); - } - }, + ResMf: { + max: 2, + have: [], + classid: sdk.items.SmallCharm, + stats: function (check) { + return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MagicBonus) === 7); + } + }, - ResFHR: { - max: 1, - have: [], - classid: sdk.items.SmallCharm, - stats: function (check) { - return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.FHR) === 5); - } - }, + ResFHR: { + max: 1, + have: [], + classid: sdk.items.SmallCharm, + stats: function (check) { + return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.FHR) === 5); + } + }, - Skiller: { - max: 2, - have: [], - classid: sdk.items.GrandCharm, - stats: function (check) { - return (!check.unique && check.classid === this.classid && check.getStat(sdk.stats.AddSkillTab, sdk.skills.tabs.JavelinandSpear) === 1 - && check.getStat(sdk.stats.MaxHp) >= 40); - } - }, - }, - - AutoBuildTemplate: { - 1: { - Update: function () { - Config.AttackSkill = [-1, sdk.skills.ChargedStrike, -1, sdk.skills.LightningStrike, -1, -1, -1]; - Config.BeltColumn = ["hp", "hp", "mp", "rv"]; - Config.HPBuffer = me.expansion ? 2 : 4; - Config.MPBuffer = me.expansion ? 4 : 6; - } - }, - }, + Skiller: { + max: 2, + have: [], + classid: sdk.items.GrandCharm, + stats: function (check) { + return (!check.unique && check.classid === this.classid && check.getStat(sdk.stats.AddSkillTab, sdk.skills.tabs.JavelinandSpear) === 1 + && check.getStat(sdk.stats.MaxHp) >= 40); + } + }, + }, + + AutoBuildTemplate: { + 1: { + Update: function () { + Config.AttackSkill = [-1, sdk.skills.ChargedStrike, -1, sdk.skills.LightningStrike, -1, -1, -1]; + Config.BeltColumn = ["hp", "hp", "mp", "rv"]; + Config.HPBuffer = me.expansion ? 2 : 4; + Config.MPBuffer = me.expansion ? 4 : 6; + } + }, + }, - respec: function () { - if (me.classic) { - return me.charlvl >= 75 && me.diablo; - } else { - return (Attack.checkInfinity() || (me.data.merc.gear.includes(sdk.locale.items.Infinity) && !Misc.poll(() => me.getMerc(), 200, 50))); - } - }, + respec: function () { + if (me.classic) { + return me.charlvl >= 75 && me.diablo; + } else { + return (Attack.checkInfinity() || (me.data.merc.gear.includes(sdk.locale.items.Infinity) && !Misc.poll(() => me.getMerc(), 200, 50))); + } + }, - active: function () { - return this.respec() && (me.expansion ? me.getSkill(sdk.skills.PlagueJavelin, sdk.skills.subindex.HardPoints) > 1 && me.getSkill(sdk.skills.PlagueJavelin, sdk.skills.subindex.HardPoints) < 5 : me.getSkill(sdk.skills.PlagueJavelin, sdk.skills.subindex.HardPoints) === 20); - }, - }; + active: function () { + return this.respec() && (me.expansion ? me.getSkill(sdk.skills.PlagueJavelin, sdk.skills.subindex.HardPoints) > 1 && me.getSkill(sdk.skills.PlagueJavelin, sdk.skills.subindex.HardPoints) < 5 : me.getSkill(sdk.skills.PlagueJavelin, sdk.skills.subindex.HardPoints) === 20); + }, + }; - build.stats = me.classic - ? [ - ["dexterity", 65], ["strength", 75], ["vitality", "all"] - ] - : [ - ["strength", 34], ["vitality", 30], ["dexterity", 47], - ["vitality", 45], ["strength", 47], ["dexterity", 65], - ["vitality", 65], ["strength", 53], ["dexterity", 118], - ["vitality", 100], ["strength", 118], ["dexterity", 151], - ["strength", 156], ["vitality", "all"], - ]; - - build.skills = me.classic - ? [ - [sdk.skills.Valkyrie, 1], - [sdk.skills.LightningFury, 1], - [sdk.skills.LightningStrike, 1], - [sdk.skills.Pierce, 1], - [sdk.skills.PlagueJavelin, 20], - [sdk.skills.ChargedStrike, 10], - [sdk.skills.LightningStrike, 10], - [sdk.skills.Decoy, 5], - [sdk.skills.LightningStrike, 17], - [sdk.skills.ChargedStrike, 15], - [sdk.skills.LightningStrike, 20, false], - [sdk.skills.ChargedStrike, 20, false], - [sdk.skills.PoisonJavelin, 20, false], - [sdk.skills.Valkyrie, 12, false], - [sdk.skills.LightningFury, 20, false], - ] - : [ - [sdk.skills.Valkyrie, 1], - [sdk.skills.Pierce, 1], - [sdk.skills.LightningStrike, 20], - [sdk.skills.ChargedStrike, 20], - [sdk.skills.LightningFury, 20], - [sdk.skills.Decoy, 5, false], - [sdk.skills.Valkyrie, 17, false], - [sdk.skills.PowerStrike, 20, false], - [sdk.skills.Pierce, 5, false], - ]; + build.stats = me.classic + ? [ + ["dexterity", 65], ["strength", 75], ["vitality", "all"] + ] + : [ + ["strength", 34], ["vitality", 30], ["dexterity", 47], + ["vitality", 45], ["strength", 47], ["dexterity", 65], + ["vitality", 65], ["strength", 53], ["dexterity", 118], + ["vitality", 100], ["strength", 118], ["dexterity", 151], + ["strength", 156], ["vitality", "all"], + ]; + + build.skills = me.classic + ? [ + [sdk.skills.Valkyrie, 1], + [sdk.skills.LightningFury, 1], + [sdk.skills.LightningStrike, 1], + [sdk.skills.Pierce, 1], + [sdk.skills.PlagueJavelin, 20], + [sdk.skills.ChargedStrike, 10], + [sdk.skills.LightningStrike, 10], + [sdk.skills.Decoy, 5], + [sdk.skills.LightningStrike, 17], + [sdk.skills.ChargedStrike, 15], + [sdk.skills.LightningStrike, 20, false], + [sdk.skills.ChargedStrike, 20, false], + [sdk.skills.PoisonJavelin, 20, false], + [sdk.skills.Valkyrie, 12, false], + [sdk.skills.LightningFury, 20, false], + ] + : [ + [sdk.skills.Valkyrie, 1], + [sdk.skills.Pierce, 1], + [sdk.skills.LightningStrike, 20], + [sdk.skills.ChargedStrike, 20], + [sdk.skills.LightningFury, 20], + [sdk.skills.Decoy, 5, false], + [sdk.skills.Valkyrie, 17, false], + [sdk.skills.PowerStrike, 20, false], + [sdk.skills.Pierce, 5, false], + ]; - me.classic && build.usefulStats.push(sdk.stats.PassivePoisonMastery, sdk.stats.PassivePoisonPierce, sdk.stats.PiercePois); - - let finalGear = me.classic - ? [ - // Helm - Tarnhelm - "[name] == skullcap && [quality] == unique # [itemallskills] == 1 && [itemmagicbonus] >= 25 # [tier] == tierscore(item, 100000)", - // Armor - Twitchthroe - "[name] == studdedleather && [quality] == unique # [ias] == 20 && [fhr] == 20 # [tier] == 100000", - // Belt - Death's Guard Sash - "[name] == sash && [quality] == set # [itemcannotbefrozen] == 1 # [tier] == 100000", - // Gloves - Death's Hand Leather Gloves - "[name] == leathergloves && [quality] == set # [poisonresist] >= 50 # [tier] == 100000", - // Rings - SoJ - "[type] == ring && [quality] == unique # [itemmaxmanapercent] == 25 # [tier] == 100000", - ] : [ - // Weapon - Titan's Revenge - "[name] == ceremonialjavelin && [quality] == unique # [itemchargedskill] >= 0 # [tier] == tierscore(item, 100000)", - // Helmet - Harlequin's Crest - "[name] == shako && [quality] == unique && [flag] != ethereal # [itemallskills] == 2 # [tier] == tierscore(item, 100000)", - // Boots - Sandstorm Treks - "[name] == scarabshellboots && [quality] == unique # [strength]+[vitality] >= 20 # [tier] == tierscore(item, 100000)", - // Belt - Thundergod's Vigor - "[name] == warbelt && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 160 # [tier] == tierscore(item, 110000)", - // Armor - CoH - "[type] == armor && [flag] == runeword && [flag] != ethereal # [fireresist] == 65 && [hpregen] == 7 # [tier] == 110000", - // Shield - Spirit - "[type] == shield # [fcr] >= 25 && [maxmana] >= 89 # [tier] == tierscore(item, 110000)", - // Amulet - Highlords - "[type] == amulet && [quality] == unique # [lightresist] == 35 # [tier] == 110000", - // Final Rings - Perfect Raven Frost & Wisp - "[type] == ring && [quality] == unique # [dexterity] == 20 && [tohit] == 250 # [tier] == 110000", - "[type] == ring && [quality] == unique # [itemabsorblightpercent] == 20 # [tier] == 110000", - // Rings - Raven Frost & Wisp - "[type] == ring && [quality] == unique # [dexterity] >= 15 && [tohit] >= 150 # [tier] == 100000", - "[type] == ring && [quality] == unique # [itemabsorblightpercent] >= 10 # [tier] == 100000", - // Switch - CTA - "[minimumsockets] >= 5 && [flag] == runeword # [plusskillbattleorders] >= 1 # [secondarytier] == 100000", - // Switch - Spirit - "[name] == monarch && [flag] == runeword # [fcr] >= 25 && [maxmana] >= 89 # [secondarytier] == 110000", - // Merc Armor - Fortitude - "[type] == armor && [flag] == runeword # [enhanceddefense] >= 200 && [enhanceddamage] >= 300 # [merctier] == 100000", - // Merc Final Helmet - Eth Andy's - "[name] == demonhead && [quality] == unique && [flag] == ethereal # [strength] >= 25 && [enhanceddefense] >= 100 # [merctier] == 50000 + mercscore(item)", - // Merc Helmet - Andy's - "[name] == demonhead && [quality] == unique && [flag] != ethereal # [strength] >= 25 && [enhanceddefense] >= 100 # [merctier] == 40000 + mercscore(item)", - ]; + me.classic && build.usefulStats.push(sdk.stats.PassivePoisonMastery, sdk.stats.PassivePoisonPierce, sdk.stats.PiercePois); + + let finalGear = me.classic + ? [ + // Helm - Tarnhelm + "[name] == skullcap && [quality] == unique # [itemallskills] == 1 && [itemmagicbonus] >= 25 # [tier] == tierscore(item, 100000)", + // Armor - Twitchthroe + "[name] == studdedleather && [quality] == unique # [ias] == 20 && [fhr] == 20 # [tier] == 100000", + // Belt - Death's Guard Sash + "[name] == sash && [quality] == set # [itemcannotbefrozen] == 1 # [tier] == 100000", + // Gloves - Death's Hand Leather Gloves + "[name] == leathergloves && [quality] == set # [poisonresist] >= 50 # [tier] == 100000", + // Rings - SoJ + "[type] == ring && [quality] == unique # [itemmaxmanapercent] == 25 # [tier] == 100000", + ] : [ + // Weapon - Titan's Revenge + "[name] == ceremonialjavelin && [quality] == unique # [itemchargedskill] >= 0 # [tier] == tierscore(item, 100000)", + // Helmet - Harlequin's Crest + "[name] == shako && [quality] == unique && [flag] != ethereal # [itemallskills] == 2 # [tier] == tierscore(item, 100000)", + // Boots - Sandstorm Treks + "[name] == scarabshellboots && [quality] == unique # [strength]+[vitality] >= 20 # [tier] == tierscore(item, 100000)", + // Belt - Thundergod's Vigor + "[name] == warbelt && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 160 # [tier] == tierscore(item, 110000)", + // Armor - CoH + "[type] == armor && [flag] == runeword && [flag] != ethereal # [fireresist] == 65 && [hpregen] == 7 # [tier] == 110000", + // Shield - Spirit + "[type] == shield # [fcr] >= 25 && [maxmana] >= 89 # [tier] == tierscore(item, 110000)", + // Amulet - Highlords + "[type] == amulet && [quality] == unique # [lightresist] == 35 # [tier] == 110000", + // Final Rings - Perfect Raven Frost & Wisp + "[type] == ring && [quality] == unique # [dexterity] == 20 && [tohit] == 250 # [tier] == 110000", + "[type] == ring && [quality] == unique # [itemabsorblightpercent] == 20 # [tier] == 110000", + // Rings - Raven Frost & Wisp + "[type] == ring && [quality] == unique # [dexterity] >= 15 && [tohit] >= 150 # [tier] == 100000", + "[type] == ring && [quality] == unique # [itemabsorblightpercent] >= 10 # [tier] == 100000", + // Switch - CTA + "[minimumsockets] >= 5 && [flag] == runeword # [plusskillbattleorders] >= 1 # [secondarytier] == 100000", + // Switch - Spirit + "[name] == monarch && [flag] == runeword # [fcr] >= 25 && [maxmana] >= 89 # [secondarytier] == 110000", + // Merc Armor - Fortitude + "[type] == armor && [flag] == runeword # [enhanceddefense] >= 200 && [enhanceddamage] >= 300 # [merctier] == 100000", + // Merc Final Helmet - Eth Andy's + "[name] == demonhead && [quality] == unique && [flag] == ethereal # [strength] >= 25 && [enhanceddefense] >= 100 # [merctier] == 50000 + mercscore(item)", + // Merc Helmet - Andy's + "[name] == demonhead && [quality] == unique && [flag] != ethereal # [strength] >= 25 && [enhanceddefense] >= 100 # [merctier] == 40000 + mercscore(item)", + ]; - NTIP.buildList(finalGear); - NTIP.buildFinalGear(finalGear); + NTIP.buildList(finalGear); + NTIP.buildFinalGear(finalGear); - return build; - })(); + return build; + })(); })(module); diff --git a/libs/SoloPlay/BuildFiles/amazon/amazon.LevelingBuild.js b/libs/SoloPlay/BuildFiles/amazon/amazon.LevelingBuild.js index 5f316198..5b5f65d3 100644 --- a/libs/SoloPlay/BuildFiles/amazon/amazon.LevelingBuild.js +++ b/libs/SoloPlay/BuildFiles/amazon/amazon.LevelingBuild.js @@ -7,80 +7,80 @@ (function (module) { - module.exports = (function () { - const build = { - caster: false, - skillstab: sdk.skills.tabs.JavelinandSpear, - wantedskills: [sdk.skills.ChargedStrike, sdk.skills.LightningStrike], - usefulskills: [sdk.skills.CriticalStrike, sdk.skills.Penetrate, sdk.skills.Valkyrie, sdk.skills.Pierce], - usefulStats: [sdk.stats.PierceLtng, sdk.stats.PassiveLightningMastery, sdk.stats.PassiveLightningPierce], - wantedMerc: MercData[sdk.skills.HolyFreeze], - stats: [], - skills: [], + module.exports = (function () { + const build = { + caster: false, + skillstab: sdk.skills.tabs.JavelinandSpear, + wantedskills: [sdk.skills.ChargedStrike, sdk.skills.LightningStrike], + usefulskills: [sdk.skills.CriticalStrike, sdk.skills.Penetrate, sdk.skills.Valkyrie, sdk.skills.Pierce], + usefulStats: [sdk.stats.PierceLtng, sdk.stats.PassiveLightningMastery, sdk.stats.PassiveLightningPierce], + wantedMerc: MercData[sdk.skills.HolyFreeze], + stats: [], + skills: [], - active: function () { - return (me.charlvl > CharInfo.respecOne && me.charlvl > CharInfo.respecTwo && me.getSkill(sdk.skills.PlagueJavelin, sdk.skills.subindex.HardPoints) === 20 && !Check.finalBuild().active()); - }, + active: function () { + return (me.charlvl > CharInfo.respecOne && me.charlvl > CharInfo.respecTwo && me.getSkill(sdk.skills.PlagueJavelin, sdk.skills.subindex.HardPoints) === 20 && !Check.finalBuild().active()); + }, - AutoBuildTemplate: { - 1: { - Update: function () { - let mainSkill = Skill.canUse(sdk.skills.LightningStrike) ? sdk.skills.LightningStrike : sdk.skills.ChargedStrike; - Config.AttackSkill = [-1, sdk.skills.ChargedStrike, 0, mainSkill, 0, -1, -1]; - Config.TownHP = me.hardcore ? 0 : 35; - Config.BeltColumn = ["hp", "hp", "mp", "mp"]; - Config.HPBuffer = me.expansion ? 2 : 4; - Config.MPBuffer = me.expansion && me.charlvl < 80 ? 6 : me.classic ? 5 : 2; - SetUp.belt(); - } - } - }, - }; - - // Has to be set after its loaded - me.classic && build.usefulStats.push(sdk.stats.PassivePoisonMastery, sdk.stats.PassivePoisonPierce, sdk.stats.PiercePois); - build.stats = me.classic - ? [ - ["dexterity", 65], ["strength", 75], ["vitality", "all"] - ] : [ - ["strength", 34], ["vitality", 30], ["dexterity", 47], - ["vitality", 45], ["strength", 47], ["dexterity", 65], - ["vitality", 65], ["strength", 53], ["dexterity", 118], - ["vitality", 100], ["strength", 118], ["dexterity", 151], - ["strength", 156], ["vitality", "all"], - ]; - - build.skills = me.classic - ? [ - // Points at respec 71 - [sdk.skills.Valkyrie, 1], // points left 64 - [sdk.skills.Pierce, 1], // points left 61 - [sdk.skills.LightningFury, 1], // points left 57 - [sdk.skills.LightningStrike, 17], // points left 37 - [sdk.skills.PlagueJavelin, 20], // points left 18 - [sdk.skills.ChargedStrike, 15], // points left 4 - [sdk.skills.Decoy, 5], // points left 0 - [sdk.skills.LightningStrike, 20, false], - [sdk.skills.ChargedStrike, 20, false], - [sdk.skills.PoisonJavelin, 20, false], // synergy for PlagueJavelin - [sdk.skills.Valkyrie, 12, false], - [sdk.skills.LightningFury, 20, false], - ] : [ - // Points at respec 71 - [sdk.skills.Valkyrie, 1], // points left 64 - [sdk.skills.Pierce, 1], // points left 61 - [sdk.skills.LightningFury, 1], // points left 57 - [sdk.skills.LightningStrike, 15], // points left 39 - [sdk.skills.ChargedStrike, 17], // points left 23 - [sdk.skills.PlagueJavelin, 20], // points left 4 - [sdk.skills.Decoy, 5], // points left 0 - [sdk.skills.LightningStrike, 20, false], - [sdk.skills.ChargedStrike, 20, false], - [sdk.skills.LightningFury, 20, false], - [sdk.skills.Valkyrie, 17, false], - [sdk.skills.PowerStrike, 20, false], - ]; - - return build; - })(); + AutoBuildTemplate: { + 1: { + Update: function () { + let mainSkill = Skill.canUse(sdk.skills.LightningStrike) ? sdk.skills.LightningStrike : sdk.skills.ChargedStrike; + Config.AttackSkill = [-1, sdk.skills.ChargedStrike, 0, mainSkill, 0, -1, -1]; + Config.TownHP = me.hardcore ? 0 : 35; + Config.BeltColumn = ["hp", "hp", "mp", "mp"]; + Config.HPBuffer = me.expansion ? 2 : 4; + Config.MPBuffer = me.expansion && me.charlvl < 80 ? 6 : me.classic ? 5 : 2; + SetUp.belt(); + } + } + }, + }; + + // Has to be set after its loaded + me.classic && build.usefulStats.push(sdk.stats.PassivePoisonMastery, sdk.stats.PassivePoisonPierce, sdk.stats.PiercePois); + build.stats = me.classic + ? [ + ["dexterity", 65], ["strength", 75], ["vitality", "all"] + ] : [ + ["strength", 34], ["vitality", 30], ["dexterity", 47], + ["vitality", 45], ["strength", 47], ["dexterity", 65], + ["vitality", 65], ["strength", 53], ["dexterity", 118], + ["vitality", 100], ["strength", 118], ["dexterity", 151], + ["strength", 156], ["vitality", "all"], + ]; + + build.skills = me.classic + ? [ + // Points at respec 71 + [sdk.skills.Valkyrie, 1], // points left 64 + [sdk.skills.Pierce, 1], // points left 61 + [sdk.skills.LightningFury, 1], // points left 57 + [sdk.skills.LightningStrike, 17], // points left 37 + [sdk.skills.PlagueJavelin, 20], // points left 18 + [sdk.skills.ChargedStrike, 15], // points left 4 + [sdk.skills.Decoy, 5], // points left 0 + [sdk.skills.LightningStrike, 20, false], + [sdk.skills.ChargedStrike, 20, false], + [sdk.skills.PoisonJavelin, 20, false], // synergy for PlagueJavelin + [sdk.skills.Valkyrie, 12, false], + [sdk.skills.LightningFury, 20, false], + ] : [ + // Points at respec 71 + [sdk.skills.Valkyrie, 1], // points left 64 + [sdk.skills.Pierce, 1], // points left 61 + [sdk.skills.LightningFury, 1], // points left 57 + [sdk.skills.LightningStrike, 15], // points left 39 + [sdk.skills.ChargedStrike, 17], // points left 23 + [sdk.skills.PlagueJavelin, 20], // points left 4 + [sdk.skills.Decoy, 5], // points left 0 + [sdk.skills.LightningStrike, 20, false], + [sdk.skills.ChargedStrike, 20, false], + [sdk.skills.LightningFury, 20, false], + [sdk.skills.Valkyrie, 17, false], + [sdk.skills.PowerStrike, 20, false], + ]; + + return build; + })(); })(module); diff --git a/libs/SoloPlay/BuildFiles/amazon/amazon.StartBuild.js b/libs/SoloPlay/BuildFiles/amazon/amazon.StartBuild.js index 5ed45a2c..bcb8ea2d 100644 --- a/libs/SoloPlay/BuildFiles/amazon/amazon.StartBuild.js +++ b/libs/SoloPlay/BuildFiles/amazon/amazon.StartBuild.js @@ -8,81 +8,81 @@ (function (module, require) { - module.exports = (function () { - const build = { - AutoBuildTemplate: {}, - caster: false, - skillstab: sdk.skills.tabs.JavelinandSpear, - wantedskills: [sdk.skills.ChargedStrike, sdk.skills.LightningStrike], - usefulskills: [sdk.skills.CriticalStrike, sdk.skills.Penetrate, sdk.skills.Valkyrie, sdk.skills.Pierce], - usefulStats: [sdk.stats.PierceLtng, sdk.stats.PassiveLightningMastery, sdk.stats.PassiveLightningPierce], - wantedMerc: MercData[sdk.skills.HolyFreeze], - stats: [ - ["strength", 34], ["vitality", 30], ["dexterity", 47], - ["vitality", 45], ["strength", 47], ["dexterity", 65], - ["vitality", "all"], - ], - skills: [ - [sdk.skills.Jab, 1, false], // charlvl 2 - [sdk.skills.InnerSight, 1, false], // charlvl 3 - [sdk.skills.CriticalStrike, 2, false], // charlvl 4 - [sdk.skills.PowerStrike, 1, false], // charlvl 6 - [sdk.skills.Dodge, 1, false], // charlvl 6 - [sdk.skills.PowerStrike, 5, false], // charlvl 10 - [sdk.skills.SlowMissiles, 1, false], // charlvl 12 - [sdk.skills.Avoid, 1, false], // charlvl 12 - [sdk.skills.PowerStrike, 8, false], // charlvl 15 - [sdk.skills.ChargedStrike, 1, false], // charlvl 18 - [sdk.skills.Penetrate, 1, false], // charlvl 18 - [sdk.skills.ChargedStrike, 5, false], // charlvl 22 - [sdk.skills.Evade, 1, false], // charLvl 24 - [sdk.skills.Decoy, 1, false], // charlvl 24 - [sdk.skills.ChargedStrike, 20, false], // respec at 30 - ], + module.exports = (function () { + const build = { + AutoBuildTemplate: {}, + caster: false, + skillstab: sdk.skills.tabs.JavelinandSpear, + wantedskills: [sdk.skills.ChargedStrike, sdk.skills.LightningStrike], + usefulskills: [sdk.skills.CriticalStrike, sdk.skills.Penetrate, sdk.skills.Valkyrie, sdk.skills.Pierce], + usefulStats: [sdk.stats.PierceLtng, sdk.stats.PassiveLightningMastery, sdk.stats.PassiveLightningPierce], + wantedMerc: MercData[sdk.skills.HolyFreeze], + stats: [ + ["strength", 34], ["vitality", 30], ["dexterity", 47], + ["vitality", 45], ["strength", 47], ["dexterity", 65], + ["vitality", "all"], + ], + skills: [ + [sdk.skills.Jab, 1, false], // charlvl 2 + [sdk.skills.InnerSight, 1, false], // charlvl 3 + [sdk.skills.CriticalStrike, 2, false], // charlvl 4 + [sdk.skills.PowerStrike, 1, false], // charlvl 6 + [sdk.skills.Dodge, 1, false], // charlvl 6 + [sdk.skills.PowerStrike, 5, false], // charlvl 10 + [sdk.skills.SlowMissiles, 1, false], // charlvl 12 + [sdk.skills.Avoid, 1, false], // charlvl 12 + [sdk.skills.PowerStrike, 8, false], // charlvl 15 + [sdk.skills.ChargedStrike, 1, false], // charlvl 18 + [sdk.skills.Penetrate, 1, false], // charlvl 18 + [sdk.skills.ChargedStrike, 5, false], // charlvl 22 + [sdk.skills.Evade, 1, false], // charLvl 24 + [sdk.skills.Decoy, 1, false], // charlvl 24 + [sdk.skills.ChargedStrike, 20, false], // respec at 30 + ], - active: function () { - return me.charlvl < CharInfo.respecOne && !me.checkSkill(sdk.skills.LightningStrike, sdk.skills.subindex.HardPoints); - }, - }; + active: function () { + return me.charlvl < CharInfo.respecOne && !me.checkSkill(sdk.skills.LightningStrike, sdk.skills.subindex.HardPoints); + }, + }; - const { buildAutoBuildTempObj } = require("../../Utils/General"); + const { buildAutoBuildTempObj } = require("../../Utils/General"); - build.AutoBuildTemplate[1] = buildAutoBuildTempObj(() => { - Config.TownHP = me.hardcore ? 0 : 35; - Config.BeltColumn = ["hp", "hp", "hp", "hp"]; - Config.HPBuffer = 8; - Config.MPBuffer = 4; - Config.AttackSkill = [-1, 0, 0, 0, 0, 0, 0]; - Config.LowManaSkill = [0, 0]; - SetUp.belt(); - }); - build.AutoBuildTemplate[2] = buildAutoBuildTempObj(() => { - Config.TownHP = me.hardcore ? 0 : 35; - Config.HPBuffer = 8; - Config.MPBuffer = 4; - Config.AttackSkill = [-1, sdk.skills.Jab, 0, sdk.skills.Jab, 0, 0, 0]; - }); - build.AutoBuildTemplate[6] = buildAutoBuildTempObj(() => { - Config.TownHP = me.hardcore ? 0 : 35; - Config.BeltColumn = ["hp", "hp", "mp", "mp"]; - Config.HPBuffer = me.expansion ? 4 : 6; - Config.MPBuffer = 6; - Config.AttackSkill = [-1, sdk.skills.PowerStrike, 0, sdk.skills.PowerStrike, 0, -1, -1]; - SetUp.belt(); - }); - build.AutoBuildTemplate[7] = buildAutoBuildTempObj(() => { - Config.HPBuffer = me.expansion ? 4 : 6; - Config.MPBuffer = 6; - Config.AttackSkill = [-1, sdk.skills.PowerStrike, 0, sdk.skills.PowerStrike, 0, -1, -1]; - }); - build.AutoBuildTemplate[18] = buildAutoBuildTempObj(() => { - Config.AttackSkill = [-1, sdk.skills.ChargedStrike, 0, sdk.skills.ChargedStrike, 0, -1, -1]; - }); - build.AutoBuildTemplate[50] = buildAutoBuildTempObj(() => { - let mainSkill = Skill.canUse(sdk.skills.LightningStrike) ? sdk.skills.LightningStrike : sdk.skills.ChargedStrike; - Config.AttackSkill = [-1, sdk.skills.ChargedStrike, 0, mainSkill, 0, -1, -1]; - }); + build.AutoBuildTemplate[1] = buildAutoBuildTempObj(() => { + Config.TownHP = me.hardcore ? 0 : 35; + Config.BeltColumn = ["hp", "hp", "hp", "hp"]; + Config.HPBuffer = 8; + Config.MPBuffer = 4; + Config.AttackSkill = [-1, 0, 0, 0, 0, 0, 0]; + Config.LowManaSkill = [0, 0]; + SetUp.belt(); + }); + build.AutoBuildTemplate[2] = buildAutoBuildTempObj(() => { + Config.TownHP = me.hardcore ? 0 : 35; + Config.HPBuffer = 8; + Config.MPBuffer = 4; + Config.AttackSkill = [-1, sdk.skills.Jab, 0, sdk.skills.Jab, 0, 0, 0]; + }); + build.AutoBuildTemplate[6] = buildAutoBuildTempObj(() => { + Config.TownHP = me.hardcore ? 0 : 35; + Config.BeltColumn = ["hp", "hp", "mp", "mp"]; + Config.HPBuffer = me.expansion ? 4 : 6; + Config.MPBuffer = 6; + Config.AttackSkill = [-1, sdk.skills.PowerStrike, 0, sdk.skills.PowerStrike, 0, -1, -1]; + SetUp.belt(); + }); + build.AutoBuildTemplate[7] = buildAutoBuildTempObj(() => { + Config.HPBuffer = me.expansion ? 4 : 6; + Config.MPBuffer = 6; + Config.AttackSkill = [-1, sdk.skills.PowerStrike, 0, sdk.skills.PowerStrike, 0, -1, -1]; + }); + build.AutoBuildTemplate[18] = buildAutoBuildTempObj(() => { + Config.AttackSkill = [-1, sdk.skills.ChargedStrike, 0, sdk.skills.ChargedStrike, 0, -1, -1]; + }); + build.AutoBuildTemplate[50] = buildAutoBuildTempObj(() => { + let mainSkill = Skill.canUse(sdk.skills.LightningStrike) ? sdk.skills.LightningStrike : sdk.skills.ChargedStrike; + Config.AttackSkill = [-1, sdk.skills.ChargedStrike, 0, mainSkill, 0, -1, -1]; + }); - return build; - })(); + return build; + })(); })(module, require); diff --git a/libs/SoloPlay/BuildFiles/amazon/amazon.SteppingBuild.js b/libs/SoloPlay/BuildFiles/amazon/amazon.SteppingBuild.js index 6a2a6fbb..60e6f1f4 100644 --- a/libs/SoloPlay/BuildFiles/amazon/amazon.SteppingBuild.js +++ b/libs/SoloPlay/BuildFiles/amazon/amazon.SteppingBuild.js @@ -7,59 +7,59 @@ (function (module) { - module.exports = (function () { - const build = { - caster: false, - skillstab: sdk.skills.tabs.JavelinandSpear, - wantedskills: [sdk.skills.ChargedStrike, sdk.skills.LightningStrike], - usefulskills: [sdk.skills.CriticalStrike, sdk.skills.Penetrate, sdk.skills.Valkyrie, sdk.skills.Pierce], - usefulStats: [sdk.stats.PierceLtng, sdk.stats.PassiveLightningMastery, sdk.stats.PassiveLightningPierce], - wantedMerc: MercData[sdk.skills.HolyFreeze], - stats: [], - skills: [ - // points at respec 33 - [sdk.skills.LightningStrike, 1], // points left 27 - [sdk.skills.Valkyrie, 1], // points left 20 - [sdk.skills.Penetrate, 1], // points left 18 - [sdk.skills.ChargedStrike, 13], // points left 6 - [sdk.skills.PowerStrike, 5], // points left 2 - [sdk.skills.LightningFury, 1], // points left 0 - [sdk.skills.LightningStrike, 20, false], // charlvl ? - [sdk.skills.ChargedStrike, 20, false], // charlvl 52 - [sdk.skills.LightningFury, 20, false], // respec at 64 - ], + module.exports = (function () { + const build = { + caster: false, + skillstab: sdk.skills.tabs.JavelinandSpear, + wantedskills: [sdk.skills.ChargedStrike, sdk.skills.LightningStrike], + usefulskills: [sdk.skills.CriticalStrike, sdk.skills.Penetrate, sdk.skills.Valkyrie, sdk.skills.Pierce], + usefulStats: [sdk.stats.PierceLtng, sdk.stats.PassiveLightningMastery, sdk.stats.PassiveLightningPierce], + wantedMerc: MercData[sdk.skills.HolyFreeze], + stats: [], + skills: [ + // points at respec 33 + [sdk.skills.LightningStrike, 1], // points left 27 + [sdk.skills.Valkyrie, 1], // points left 20 + [sdk.skills.Penetrate, 1], // points left 18 + [sdk.skills.ChargedStrike, 13], // points left 6 + [sdk.skills.PowerStrike, 5], // points left 2 + [sdk.skills.LightningFury, 1], // points left 0 + [sdk.skills.LightningStrike, 20, false], // charlvl ? + [sdk.skills.ChargedStrike, 20, false], // charlvl 52 + [sdk.skills.LightningFury, 20, false], // respec at 64 + ], - AutoBuildTemplate: { - 1: { - Update: function () { - let mainSkill = Skill.canUse(sdk.skills.LightningStrike) ? sdk.skills.LightningStrike : sdk.skills.ChargedStrike; - Config.AttackSkill = [-1, sdk.skills.ChargedStrike, 0, mainSkill, 0, -1, -1]; - Config.BeltColumn = ["hp", "hp", "mp", "mp"]; - Config.HPBuffer = me.expansion ? 2 : 4; - Config.MPBuffer = 6; - SetUp.belt(); - } - } - }, + AutoBuildTemplate: { + 1: { + Update: function () { + let mainSkill = Skill.canUse(sdk.skills.LightningStrike) ? sdk.skills.LightningStrike : sdk.skills.ChargedStrike; + Config.AttackSkill = [-1, sdk.skills.ChargedStrike, 0, mainSkill, 0, -1, -1]; + Config.BeltColumn = ["hp", "hp", "mp", "mp"]; + Config.HPBuffer = me.expansion ? 2 : 4; + Config.MPBuffer = 6; + SetUp.belt(); + } + } + }, - active: function () { - return me.charlvl > CharInfo.respecOne && me.charlvl < CharInfo.respecTwo && me.checkSkill(sdk.skills.LightningStrike, sdk.skills.subindex.HardPoints) && me.getSkill(sdk.skills.PlagueJavelin, sdk.skills.subindex.HardPoints) <= 5; - }, - }; - - // Has to be set after its loaded - me.classic && build.usefulStats.push(sdk.stats.PassivePoisonMastery, sdk.stats.PassivePoisonPierce, sdk.stats.PiercePois); - build.stats = me.classic - ? [ - ["dexterity", 65], ["strength", 75], ["vitality", "all"] - ] : [ - ["strength", 34], ["vitality", 30], ["dexterity", 47], - ["vitality", 45], ["strength", 47], ["dexterity", 65], - ["vitality", 65], ["strength", 53], ["dexterity", 118], - ["vitality", 100], ["strength", 118], ["dexterity", 151], - ["strength", 156], ["vitality", "all"], - ]; - - return build; - })(); + active: function () { + return me.charlvl > CharInfo.respecOne && me.charlvl < CharInfo.respecTwo && me.checkSkill(sdk.skills.LightningStrike, sdk.skills.subindex.HardPoints) && me.getSkill(sdk.skills.PlagueJavelin, sdk.skills.subindex.HardPoints) <= 5; + }, + }; + + // Has to be set after its loaded + me.classic && build.usefulStats.push(sdk.stats.PassivePoisonMastery, sdk.stats.PassivePoisonPierce, sdk.stats.PiercePois); + build.stats = me.classic + ? [ + ["dexterity", 65], ["strength", 75], ["vitality", "all"] + ] : [ + ["strength", 34], ["vitality", 30], ["dexterity", 47], + ["vitality", 45], ["strength", 47], ["dexterity", 65], + ["vitality", 65], ["strength", 53], ["dexterity", 118], + ["vitality", 100], ["strength", 118], ["dexterity", 151], + ["strength", 156], ["vitality", "all"], + ]; + + return build; + })(); })(module); diff --git a/libs/SoloPlay/BuildFiles/amazon/amazon.WfzonBuild.js b/libs/SoloPlay/BuildFiles/amazon/amazon.WfzonBuild.js index de17d215..b5af9faa 100644 --- a/libs/SoloPlay/BuildFiles/amazon/amazon.WfzonBuild.js +++ b/libs/SoloPlay/BuildFiles/amazon/amazon.WfzonBuild.js @@ -8,143 +8,143 @@ (function (module) { - module.exports = (function () { - const build = { - caster: false, - skillstab: sdk.skills.tabs.BowandCrossbow, - wantedskills: [sdk.skills.Strafe], - usefulskills: [sdk.skills.Penetrate, sdk.skills.Valkyrie, sdk.skills.Pierce], - precastSkills: [sdk.skills.Valkyrie], - wantedMerc: MercData[sdk.skills.Might], - stats: [ - ["strength", 134], ["dexterity", 167], ["vitality", 150], ["dexterity", "all"] - ], - skills: [ - [sdk.skills.Valkyrie, 1], - [sdk.skills.Strafe, 20], - [sdk.skills.Pierce, 13], - [sdk.skills.Penetrate, 20], - [sdk.skills.CriticalStrike, 13], - [sdk.skills.Valkyrie, 16], - [sdk.skills.Dodge, 9], - [sdk.skills.Avoid, 4], - [sdk.skills.Evade, 8], - ], + module.exports = (function () { + const build = { + caster: false, + skillstab: sdk.skills.tabs.BowandCrossbow, + wantedskills: [sdk.skills.Strafe], + usefulskills: [sdk.skills.Penetrate, sdk.skills.Valkyrie, sdk.skills.Pierce], + precastSkills: [sdk.skills.Valkyrie], + wantedMerc: MercData[sdk.skills.Might], + stats: [ + ["strength", 134], ["dexterity", 167], ["vitality", 150], ["dexterity", "all"] + ], + skills: [ + [sdk.skills.Valkyrie, 1], + [sdk.skills.Strafe, 20], + [sdk.skills.Pierce, 13], + [sdk.skills.Penetrate, 20], + [sdk.skills.CriticalStrike, 13], + [sdk.skills.Valkyrie, 16], + [sdk.skills.Dodge, 9], + [sdk.skills.Avoid, 4], + [sdk.skills.Evade, 8], + ], - charms: { - ResLife: { - max: 3, - have: [], - classid: sdk.items.SmallCharm, - stats: function (check) { - return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MaxHp) === 20); - } - }, + charms: { + ResLife: { + max: 3, + have: [], + classid: sdk.items.SmallCharm, + stats: function (check) { + return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MaxHp) === 20); + } + }, - ResMf: { - max: 2, - have: [], - classid: sdk.items.SmallCharm, - stats: function (check) { - return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MagicBonus) === 7); - } - }, + ResMf: { + max: 2, + have: [], + classid: sdk.items.SmallCharm, + stats: function (check) { + return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MagicBonus) === 7); + } + }, - ResFHR: { - max: 1, - have: [], - classid: sdk.items.SmallCharm, - stats: function (check) { - return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.FHR) === 5); - } - }, + ResFHR: { + max: 1, + have: [], + classid: sdk.items.SmallCharm, + stats: function (check) { + return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.FHR) === 5); + } + }, - SkillerCrossbow: { - max: 1, - have: [], - classid: sdk.items.GrandCharm, - stats: function (check) { - return (!check.unique && check.classid === this.classid && check.getStat(sdk.stats.AddSkillTab, sdk.skills.tabs.BowandCrossbow) === 1 - && check.getStat(sdk.stats.MaxHp) >= 40); - } - }, + SkillerCrossbow: { + max: 1, + have: [], + classid: sdk.items.GrandCharm, + stats: function (check) { + return (!check.unique && check.classid === this.classid && check.getStat(sdk.stats.AddSkillTab, sdk.skills.tabs.BowandCrossbow) === 1 + && check.getStat(sdk.stats.MaxHp) >= 40); + } + }, - SkillerPassive: { - max: 1, - have: [], - classid: sdk.items.GrandCharm, - stats: function (check) { - return (!check.unique && check.classid === this.classid && check.getStat(sdk.stats.AddSkillTab, sdk.skills.tabs.PassiveandMagic) === 1 - && check.getStat(sdk.stats.MaxHp) >= 40); - } - }, - }, + SkillerPassive: { + max: 1, + have: [], + classid: sdk.items.GrandCharm, + stats: function (check) { + return (!check.unique && check.classid === this.classid && check.getStat(sdk.stats.AddSkillTab, sdk.skills.tabs.PassiveandMagic) === 1 + && check.getStat(sdk.stats.MaxHp) >= 40); + } + }, + }, - AutoBuildTemplate: { - 1: { - Update: function () { - Config.AttackSkill = [-1, sdk.skills.Strafe, -1, sdk.skills.Strafe, -1, sdk.skills.MagicArrow, -1]; - Config.LowManaSkill = [0, -1]; - } - }, - }, + AutoBuildTemplate: { + 1: { + Update: function () { + Config.AttackSkill = [-1, sdk.skills.Strafe, -1, sdk.skills.Strafe, -1, sdk.skills.MagicArrow, -1]; + Config.LowManaSkill = [0, -1]; + } + }, + }, - respec: function () { - if (me.classic) { - return false; - } else { - return (Check.haveItem("hydrabow", "unique", "Windforce") || Check.haveItem("diamondbow", "unique", "Witchwild String")); - } - }, + respec: function () { + if (me.classic) { + return false; + } else { + return (Check.haveItem("hydrabow", "unique", "Windforce") || Check.haveItem("diamondbow", "unique", "Witchwild String")); + } + }, - active: function () { - return this.respec() && me.checkSkill(sdk.skills.Strafe, sdk.skills.subindex.HardPoints); - }, - }; - - let finalGear = [ // autoequip final gear - // Final Weapon - Windforce - "[name] == hydrabow && [quality] == unique # [manaleech] >= 6 # [tier] == tierscore(item, 100000)", - // Weapon - WitchWild String up'd - "[name] == diamondbow && [quality] == unique # [fireresist] == 40 # [tier] == tierscore(item, 50000)", - // Helmet - Vampz Gaze - "[name] == grimhelm && [quality] == unique && [flag] != ethereal # [manaleech] >= 6 && [lifeleech] >= 6 && [damageresist] >= 15 # [tier] == tierscore(item, 100000)", - // Boots - War Traveler - "[name] == battleboots && [quality] == unique && [flag] != ethereal # [itemmagicbonus] >= 30 # [tier] == 100000", - // Belt - Nosferatu's Coil - "[name] == vampirefangbelt && [quality] == unique && [flag] != ethereal # [lifeleech] >= 5 # [tier] == tierscore(item, 100000)", - // Armor - CoH - "[type] == armor && [flag] == runeword && [flag] != ethereal # [fireresist] == 65 && [hpregen] == 7 # [tier] == 100000", - // Gloves - Lava Gout - "[name] == battlegauntlets && [quality] == unique && [flag] != ethereal # [ias] == 20 && [enhanceddefense] >= 150 # [tier] == tierscore(item, 3000)", - // Final Amulet - Atma's Scarab - "[type] == amulet && [quality] == unique # [poisonresist] == 75 # [tier] == 110000", - // Amulet - Highlords - "[type] == amulet && [quality] == unique # [lightresist] == 35 # [tier] == 100000", - // Final Rings - Perfect Raven Frost & Perfect Bul-Kathos' Wedding Band - "[type] == ring && [quality] == unique # [dexterity] == 20 && [tohit] == 250 # [tier] == 110000", - "[name] == ring && [quality] == unique # [maxstamina] == 50 && [lifeleech] == 5 # [tier] == 110000", - // Rings - Raven Frost && Bul-Kathos' Wedding Band - "[type] == ring && [quality] == unique # [dexterity] >= 15 && [tohit] >= 150 # [tier] == 100000", - "[name] == ring && [quality] == unique # [maxstamina] == 50 && [lifeleech] >= 3 # [tier] == 100000", // Switch Final Weapon - CTA - "[minimumsockets] >= 5 && [flag] == runeword # [plusskillbattleorders] >= 1 # [secondarytier] == 100000", - // Switch Temporary Weapon - Life Tap charged wand - "[type] == wand && [quality] >= normal # [itemchargedskill] == 82 # [secondarytier] == 75000 + chargeditemscore(item, 82)", - // Switch Final Shield - Spirit - "[name] == monarch && [flag] == runeword # [fcr] >= 25 && [maxmana] >= 89 # [secondarytier] == 110000", - // Switch Temporary Shield - Any 1+ all skill - "[type] == shield # [itemallskills] >= 1 # [secondarytier] == tierscore(item, 100000)", - // Merc Armor - Fortitude - "[type] == armor && [flag] == runeword # [enhanceddefense] >= 200 && [enhanceddamage] >= 300 # [merctier] == 100000", - // Merc Helmet - Eth Andy's - "[name] == demonhead && [quality] == unique && [flag] == ethereal # [strength] >= 25 && [enhanceddefense] >= 100 # [merctier] == 40000 + mercscore(item)", - // Merc Helmet - Andy's - "[name] == demonhead && [quality] == unique && [flag] != ethereal # [strength] >= 25 && [enhanceddefense] >= 100 # [merctier] == 50000 + mercscore(item)", - ]; + active: function () { + return this.respec() && me.checkSkill(sdk.skills.Strafe, sdk.skills.subindex.HardPoints); + }, + }; + + let finalGear = [ // autoequip final gear + // Final Weapon - Windforce + "[name] == hydrabow && [quality] == unique # [manaleech] >= 6 # [tier] == tierscore(item, 100000)", + // Weapon - WitchWild String up'd + "[name] == diamondbow && [quality] == unique # [fireresist] == 40 # [tier] == tierscore(item, 50000)", + // Helmet - Vampz Gaze + "[name] == grimhelm && [quality] == unique && [flag] != ethereal # [manaleech] >= 6 && [lifeleech] >= 6 && [damageresist] >= 15 # [tier] == tierscore(item, 100000)", + // Boots - War Traveler + "[name] == battleboots && [quality] == unique && [flag] != ethereal # [itemmagicbonus] >= 30 # [tier] == 100000", + // Belt - Nosferatu's Coil + "[name] == vampirefangbelt && [quality] == unique && [flag] != ethereal # [lifeleech] >= 5 # [tier] == tierscore(item, 100000)", + // Armor - CoH + "[type] == armor && [flag] == runeword && [flag] != ethereal # [fireresist] == 65 && [hpregen] == 7 # [tier] == 100000", + // Gloves - Lava Gout + "[name] == battlegauntlets && [quality] == unique && [flag] != ethereal # [ias] == 20 && [enhanceddefense] >= 150 # [tier] == tierscore(item, 3000)", + // Final Amulet - Atma's Scarab + "[type] == amulet && [quality] == unique # [poisonresist] == 75 # [tier] == 110000", + // Amulet - Highlords + "[type] == amulet && [quality] == unique # [lightresist] == 35 # [tier] == 100000", + // Final Rings - Perfect Raven Frost & Perfect Bul-Kathos' Wedding Band + "[type] == ring && [quality] == unique # [dexterity] == 20 && [tohit] == 250 # [tier] == 110000", + "[name] == ring && [quality] == unique # [maxstamina] == 50 && [lifeleech] == 5 # [tier] == 110000", + // Rings - Raven Frost && Bul-Kathos' Wedding Band + "[type] == ring && [quality] == unique # [dexterity] >= 15 && [tohit] >= 150 # [tier] == 100000", + "[name] == ring && [quality] == unique # [maxstamina] == 50 && [lifeleech] >= 3 # [tier] == 100000", // Switch Final Weapon - CTA + "[minimumsockets] >= 5 && [flag] == runeword # [plusskillbattleorders] >= 1 # [secondarytier] == 100000", + // Switch Temporary Weapon - Life Tap charged wand + "[type] == wand && [quality] >= normal # [itemchargedskill] == 82 # [secondarytier] == 75000 + chargeditemscore(item, 82)", + // Switch Final Shield - Spirit + "[name] == monarch && [flag] == runeword # [fcr] >= 25 && [maxmana] >= 89 # [secondarytier] == 110000", + // Switch Temporary Shield - Any 1+ all skill + "[type] == shield # [itemallskills] >= 1 # [secondarytier] == tierscore(item, 100000)", + // Merc Armor - Fortitude + "[type] == armor && [flag] == runeword # [enhanceddefense] >= 200 && [enhanceddamage] >= 300 # [merctier] == 100000", + // Merc Helmet - Eth Andy's + "[name] == demonhead && [quality] == unique && [flag] == ethereal # [strength] >= 25 && [enhanceddefense] >= 100 # [merctier] == 40000 + mercscore(item)", + // Merc Helmet - Andy's + "[name] == demonhead && [quality] == unique && [flag] != ethereal # [strength] >= 25 && [enhanceddefense] >= 100 # [merctier] == 50000 + mercscore(item)", + ]; - NTIP.buildList(finalGear); - NTIP.buildFinalGear(finalGear); + NTIP.buildList(finalGear); + NTIP.buildFinalGear(finalGear); - return build; - })(); + return build; + })(); })(module); diff --git a/libs/SoloPlay/BuildFiles/amazon/amazon.WitchyzonBuild.js b/libs/SoloPlay/BuildFiles/amazon/amazon.WitchyzonBuild.js index c039b571..f21721df 100644 --- a/libs/SoloPlay/BuildFiles/amazon/amazon.WitchyzonBuild.js +++ b/libs/SoloPlay/BuildFiles/amazon/amazon.WitchyzonBuild.js @@ -8,138 +8,138 @@ (function (module) { - module.exports = (function () { - const build = { - caster: false, - skillstab: sdk.skills.tabs.BowandCrossbow, - wantedskills: [sdk.skills.Strafe], - usefulskills: [sdk.skills.Penetrate, sdk.skills.Valkyrie, sdk.skills.Pierce], - precastSkills: [sdk.skills.Valkyrie], - wantedMerc: MercData[sdk.skills.Might], - stats: [ - ["strength", 90], ["dexterity", 132], ["vitality", 150], ["dexterity", "all"] - ], - skills: [ - [sdk.skills.Strafe, 1], - [sdk.skills.Valkyrie, 1], - [sdk.skills.Pierce, 1], - [sdk.skills.Strafe, 20], - [sdk.skills.Pierce, 10], - [sdk.skills.Penetrate, 20], - [sdk.skills.Valkyrie, 20], - [sdk.skills.Dodge, 12], - [sdk.skills.Avoid, 7], - [sdk.skills.Evade, 12], - [sdk.skills.Decoy, 2], - ], + module.exports = (function () { + const build = { + caster: false, + skillstab: sdk.skills.tabs.BowandCrossbow, + wantedskills: [sdk.skills.Strafe], + usefulskills: [sdk.skills.Penetrate, sdk.skills.Valkyrie, sdk.skills.Pierce], + precastSkills: [sdk.skills.Valkyrie], + wantedMerc: MercData[sdk.skills.Might], + stats: [ + ["strength", 90], ["dexterity", 132], ["vitality", 150], ["dexterity", "all"] + ], + skills: [ + [sdk.skills.Strafe, 1], + [sdk.skills.Valkyrie, 1], + [sdk.skills.Pierce, 1], + [sdk.skills.Strafe, 20], + [sdk.skills.Pierce, 10], + [sdk.skills.Penetrate, 20], + [sdk.skills.Valkyrie, 20], + [sdk.skills.Dodge, 12], + [sdk.skills.Avoid, 7], + [sdk.skills.Evade, 12], + [sdk.skills.Decoy, 2], + ], - charms: { - ResLife: { - max: 3, - have: [], - classid: sdk.items.SmallCharm, - stats: function (check) { - return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MaxHp) === 20); - } - }, + charms: { + ResLife: { + max: 3, + have: [], + classid: sdk.items.SmallCharm, + stats: function (check) { + return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MaxHp) === 20); + } + }, - ResMf: { - max: 2, - have: [], - classid: sdk.items.SmallCharm, - stats: function (check) { - return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MagicBonus) === 7); - } - }, + ResMf: { + max: 2, + have: [], + classid: sdk.items.SmallCharm, + stats: function (check) { + return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MagicBonus) === 7); + } + }, - ResFHR: { - max: 1, - have: [], - classid: sdk.items.SmallCharm, - stats: function (check) { - return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.FHR) === 5); - } - }, + ResFHR: { + max: 1, + have: [], + classid: sdk.items.SmallCharm, + stats: function (check) { + return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.FHR) === 5); + } + }, - SkillerCrossbow: { - max: 1, - have: [], - classid: sdk.items.GrandCharm, - stats: function (check) { - return (!check.unique && check.classid === this.classid && check.getStat(sdk.stats.AddSkillTab, sdk.skills.tabs.BowandCrossbow) === 1 - && check.getStat(sdk.stats.MaxHp) >= 40); - } - }, + SkillerCrossbow: { + max: 1, + have: [], + classid: sdk.items.GrandCharm, + stats: function (check) { + return (!check.unique && check.classid === this.classid && check.getStat(sdk.stats.AddSkillTab, sdk.skills.tabs.BowandCrossbow) === 1 + && check.getStat(sdk.stats.MaxHp) >= 40); + } + }, - SkillerPassive: { - max: 1, - have: [], - classid: sdk.items.GrandCharm, - stats: function (check) { - return (!check.unique && check.classid === this.classid && check.getStat(sdk.stats.AddSkillTab, sdk.skills.tabs.PassiveandMagic) === 1 - && check.getStat(sdk.stats.MaxHp) >= 40); - } - }, - }, + SkillerPassive: { + max: 1, + have: [], + classid: sdk.items.GrandCharm, + stats: function (check) { + return (!check.unique && check.classid === this.classid && check.getStat(sdk.stats.AddSkillTab, sdk.skills.tabs.PassiveandMagic) === 1 + && check.getStat(sdk.stats.MaxHp) >= 40); + } + }, + }, - AutoBuildTemplate: { - 1: { - Update: function () { - Config.AttackSkill = [-1, sdk.skills.Strafe, -1, sdk.skills.Strafe, -1, sdk.skills.MagicArrow, -1]; - Config.LowManaSkill = [0, -1]; - } - }, - }, + AutoBuildTemplate: { + 1: { + Update: function () { + Config.AttackSkill = [-1, sdk.skills.Strafe, -1, sdk.skills.Strafe, -1, sdk.skills.MagicArrow, -1]; + Config.LowManaSkill = [0, -1]; + } + }, + }, - respec: function () { - if (me.classic) { - return false; - } else { - return Check.haveItem("diamondbow", "unique", "Witchwild String") && Check.haveItem("vampirefangbelt", "unique", "Nosferatu's Coil"); - } - }, + respec: function () { + if (me.classic) { + return false; + } else { + return Check.haveItem("diamondbow", "unique", "Witchwild String") && Check.haveItem("vampirefangbelt", "unique", "Nosferatu's Coil"); + } + }, - active: function () { - return this.respec() && me.checkSkill(sdk.skills.Strafe, sdk.skills.subindex.HardPoints); - }, - }; - - let finalGear = [ // autoequip final gear - // Weapon - WitchWild String up'd - "[name] == diamondbow && [quality] == unique # [fireresist] == 40 # [tier] == tierscore(item, 100000)", - // Helmet - Vampz Gaze - "[name] == grimhelm && [quality] == unique && [flag] != ethereal # [manaleech] >= 6 && [lifeleech] >= 6 && [damageresist] >= 15 # [tier] == tierscore(item, 100000)", - // Boots - War Traveler - "[name] == battleboots && [quality] == unique && [flag] != ethereal # [itemmagicbonus] >= 35 # [tier] == tierscore(item, 100000)", - // Belt - Nosferatu's Coil - "[name] == vampirefangbelt && [quality] == unique && [flag] != ethereal # [lifeleech] >= 5 # [tier] == tierscore(item, 100000)", - // Armor - CoH - "[type] == armor && [flag] == runeword && [flag] != ethereal # [fireresist] == 65 && [hpregen] == 7 # [tier] == 100000", - // Amulet - Cat's Eye - "[type] == amulet && [quality] == unique # [dexterity] == 25 # [tier] == 110000", - // Final Rings - Perfect Raven Frost & Bul-Kathos' Wedding Band - "[type] == ring && [quality] == unique # [dexterity] == 20 && [tohit] == 250 # [tier] == 110000", - "[type] == ring && [quality] == unique # [maxstamina] == 50 && [lifeleech] == 5 # [tier] == 110000", - // Rings - Raven Frost && Bul-Kathos' Wedding Band - "[type] == ring && [quality] == unique # [dexterity] >= 15 && [tohit] >= 150 # [tier] == 100000", - "[type] == ring && [quality] == unique # [maxstamina] == 50 && [lifeleech] >= 3 # [tier] == 100000", - // Switch Final Weapon - CTA - "[minimumsockets] >= 5 && [flag] == runeword # [plusskillbattleorders] >= 1 # [secondarytier] == 100000", - // Switch Temporary Weapon - Life Tap charged wand - "[type] == wand && [quality] >= normal # [itemchargedskill] == 82 # [secondarytier] == 75000 + chargeditemscore(item, 82)", - // Switch Shield - Any 1+ all skill - "[type] == shield # [itemallskills] >= 1 # [secondarytier] == tierscore(item, 100000)", - // Merc Armor - Fortitude - "[type] == armor && [flag] == runeword # [enhanceddefense] >= 200 && [enhanceddamage] >= 300 # [merctier] == 100000", - // Merc Final Helmet - Eth Andy's - "[name] == demonhead && [quality] == unique && [flag] == ethereal # [strength] >= 25 && [enhanceddefense] >= 100 # [merctier] == 40000 + mercscore(item)", - // Merc Helmet - Andy's - "[name] == demonhead && [quality] == unique && [flag] != ethereal # [strength] >= 25 && [enhanceddefense] >= 100 # [merctier] == 50000 + mercscore(item)", - ]; + active: function () { + return this.respec() && me.checkSkill(sdk.skills.Strafe, sdk.skills.subindex.HardPoints); + }, + }; + + let finalGear = [ // autoequip final gear + // Weapon - WitchWild String up'd + "[name] == diamondbow && [quality] == unique # [fireresist] == 40 # [tier] == tierscore(item, 100000)", + // Helmet - Vampz Gaze + "[name] == grimhelm && [quality] == unique && [flag] != ethereal # [manaleech] >= 6 && [lifeleech] >= 6 && [damageresist] >= 15 # [tier] == tierscore(item, 100000)", + // Boots - War Traveler + "[name] == battleboots && [quality] == unique && [flag] != ethereal # [itemmagicbonus] >= 35 # [tier] == tierscore(item, 100000)", + // Belt - Nosferatu's Coil + "[name] == vampirefangbelt && [quality] == unique && [flag] != ethereal # [lifeleech] >= 5 # [tier] == tierscore(item, 100000)", + // Armor - CoH + "[type] == armor && [flag] == runeword && [flag] != ethereal # [fireresist] == 65 && [hpregen] == 7 # [tier] == 100000", + // Amulet - Cat's Eye + "[type] == amulet && [quality] == unique # [dexterity] == 25 # [tier] == 110000", + // Final Rings - Perfect Raven Frost & Bul-Kathos' Wedding Band + "[type] == ring && [quality] == unique # [dexterity] == 20 && [tohit] == 250 # [tier] == 110000", + "[type] == ring && [quality] == unique # [maxstamina] == 50 && [lifeleech] == 5 # [tier] == 110000", + // Rings - Raven Frost && Bul-Kathos' Wedding Band + "[type] == ring && [quality] == unique # [dexterity] >= 15 && [tohit] >= 150 # [tier] == 100000", + "[type] == ring && [quality] == unique # [maxstamina] == 50 && [lifeleech] >= 3 # [tier] == 100000", + // Switch Final Weapon - CTA + "[minimumsockets] >= 5 && [flag] == runeword # [plusskillbattleorders] >= 1 # [secondarytier] == 100000", + // Switch Temporary Weapon - Life Tap charged wand + "[type] == wand && [quality] >= normal # [itemchargedskill] == 82 # [secondarytier] == 75000 + chargeditemscore(item, 82)", + // Switch Shield - Any 1+ all skill + "[type] == shield # [itemallskills] >= 1 # [secondarytier] == tierscore(item, 100000)", + // Merc Armor - Fortitude + "[type] == armor && [flag] == runeword # [enhanceddefense] >= 200 && [enhanceddamage] >= 300 # [merctier] == 100000", + // Merc Final Helmet - Eth Andy's + "[name] == demonhead && [quality] == unique && [flag] == ethereal # [strength] >= 25 && [enhanceddefense] >= 100 # [merctier] == 40000 + mercscore(item)", + // Merc Helmet - Andy's + "[name] == demonhead && [quality] == unique && [flag] != ethereal # [strength] >= 25 && [enhanceddefense] >= 100 # [merctier] == 50000 + mercscore(item)", + ]; - NTIP.buildList(finalGear); - NTIP.buildFinalGear(finalGear); + NTIP.buildList(finalGear); + NTIP.buildFinalGear(finalGear); - return build; - })(); + return build; + })(); })(module); diff --git a/libs/SoloPlay/BuildFiles/amazon/amazon.js b/libs/SoloPlay/BuildFiles/amazon/amazon.js index c73c36ea..267fc591 100644 --- a/libs/SoloPlay/BuildFiles/amazon/amazon.js +++ b/libs/SoloPlay/BuildFiles/amazon/amazon.js @@ -6,40 +6,40 @@ */ const CharInfo = { - respecOne: me.expansion ? 30 : 30, - respecTwo: me.expansion ? 64 : 64, - levelCap: (function() { - const currentDiff = sdk.difficulty.nameOf(me.diff); - const softcoreMode = { - "Normal": me.expansion ? 33 : 33, - "Nightmare": me.expansion ? 70 : 70, - "Hell": 100, - }; - const hardcoreMode = { - "Normal": me.expansion ? 36 : 33, - "Nightmare": me.expansion ? 71 : 65, - "Hell": 100, - }; + respecOne: me.expansion ? 30 : 30, + respecTwo: me.expansion ? 64 : 64, + levelCap: (function() { + const currentDiff = sdk.difficulty.nameOf(me.diff); + const softcoreMode = { + "Normal": me.expansion ? 33 : 33, + "Nightmare": me.expansion ? 70 : 70, + "Hell": 100, + }; + const hardcoreMode = { + "Normal": me.expansion ? 36 : 33, + "Nightmare": me.expansion ? 71 : 65, + "Hell": 100, + }; - return me.softcore ? softcoreMode[currentDiff] : hardcoreMode[currentDiff]; - })(), + return me.softcore ? softcoreMode[currentDiff] : hardcoreMode[currentDiff]; + })(), - getActiveBuild: function () { - const nSkills = me.getStat(sdk.stats.NewSkills); - const currLevel = me.charlvl; - const justRepeced = (nSkills >= currLevel); + getActiveBuild: function () { + const nSkills = me.getStat(sdk.stats.NewSkills); + const currLevel = me.charlvl; + const justRepeced = (nSkills >= currLevel); - switch (true) { - case currLevel < this.respecOne && !me.checkSkill(sdk.skills.LightningStrike, sdk.skills.subindex.HardPoints): - return "Start"; - case currLevel >= this.respecOne && currLevel < this.respecTwo && justRepeced: - case (currLevel >= this.respecOne && currLevel < this.respecTwo && me.checkSkill(sdk.skills.LightningStrike, sdk.skills.subindex.HardPoints)): - return "Stepping"; - case Check.finalBuild().respec() && justRepeced: - case Check.finalBuild().active(): - return SetUp.finalBuild; - default: - return "Leveling"; - } - }, + switch (true) { + case currLevel < this.respecOne && !me.checkSkill(sdk.skills.LightningStrike, sdk.skills.subindex.HardPoints): + return "Start"; + case currLevel >= this.respecOne && currLevel < this.respecTwo && justRepeced: + case (currLevel >= this.respecOne && currLevel < this.respecTwo && me.checkSkill(sdk.skills.LightningStrike, sdk.skills.subindex.HardPoints)): + return "Stepping"; + case Check.finalBuild().respec() && justRepeced: + case Check.finalBuild().active(): + return SetUp.finalBuild; + default: + return "Leveling"; + } + }, }; diff --git a/libs/SoloPlay/BuildFiles/assassin/assassin.LevelingBuild.js b/libs/SoloPlay/BuildFiles/assassin/assassin.LevelingBuild.js index 155675d0..a9a52d0c 100644 --- a/libs/SoloPlay/BuildFiles/assassin/assassin.LevelingBuild.js +++ b/libs/SoloPlay/BuildFiles/assassin/assassin.LevelingBuild.js @@ -7,80 +7,80 @@ (function (module) { - module.exports = (function () { - const build = { - caster: true, - skillstab: sdk.skills.tabs.Traps, - wantedskills: [sdk.skills.FireBlast, sdk.skills.LightningSentry, sdk.skills.DeathSentry, sdk.skills.ShadowMaster], - usefulskills: [sdk.skills.ChargedBoltSentry, sdk.skills.BladeShield, sdk.skills.Fade], - wantedMerc: MercData[sdk.skills.HolyFreeze], - stats: [ - ["strength", 47], ["dexterity", 46], ["vitality", 166], - ["strength", 61], ["vitality", 241], ["strength", 79], - ["dexterity", 79], ["strength", 156], ["vitality", "all"] - ], - skills: [ - // Skills points at respec 33 - [sdk.skills.Fade, 1], // points left 30 - [sdk.skills.ShadowMaster, 1], // points left 25 - [sdk.skills.MindBlast, 1], // points left 20 - [sdk.skills.DeathSentry, 1], // points left 19 - [sdk.skills.LightningSentry, 7], // points left 13 - [sdk.skills.FireBlast, 6], // points left 8 - [sdk.skills.ShockWeb, 8], // points left 1 - [sdk.skills.WakeofFire, 1], // points left 0 - [sdk.skills.LightningSentry, 20, false], - [sdk.skills.DeathSentry, 10], - [sdk.skills.ShockWeb, 9], - [sdk.skills.FireBlast, 8], - [sdk.skills.DeathSentry, 12], - [sdk.skills.ShockWeb, 11], - [sdk.skills.FireBlast, 11], - [sdk.skills.DeathSentry, 13], - [sdk.skills.ShockWeb, 13], - [sdk.skills.FireBlast, 12], - [sdk.skills.DeathSentry, 14], - [sdk.skills.ShockWeb, 15], - [sdk.skills.FireBlast, 14], - [sdk.skills.DeathSentry, 15], - [sdk.skills.ShockWeb, 16], - [sdk.skills.FireBlast, 15], - [sdk.skills.DeathSentry, 16], - [sdk.skills.ShockWeb, 18], - [sdk.skills.FireBlast, 16], - [sdk.skills.DeathSentry, 17], - [sdk.skills.ShockWeb, 20], - [sdk.skills.FireBlast, 18], - [sdk.skills.DeathSentry, 20], - [sdk.skills.ShockWeb, 20], - [sdk.skills.FireBlast, 20], - [sdk.skills.ChargedBoltSentry, 20], - ], + module.exports = (function () { + const build = { + caster: true, + skillstab: sdk.skills.tabs.Traps, + wantedskills: [sdk.skills.FireBlast, sdk.skills.LightningSentry, sdk.skills.DeathSentry, sdk.skills.ShadowMaster], + usefulskills: [sdk.skills.ChargedBoltSentry, sdk.skills.BladeShield, sdk.skills.Fade], + wantedMerc: MercData[sdk.skills.HolyFreeze], + stats: [ + ["strength", 47], ["dexterity", 46], ["vitality", 166], + ["strength", 61], ["vitality", 241], ["strength", 79], + ["dexterity", 79], ["strength", 156], ["vitality", "all"] + ], + skills: [ + // Skills points at respec 33 + [sdk.skills.Fade, 1], // points left 30 + [sdk.skills.ShadowMaster, 1], // points left 25 + [sdk.skills.MindBlast, 1], // points left 20 + [sdk.skills.DeathSentry, 1], // points left 19 + [sdk.skills.LightningSentry, 7], // points left 13 + [sdk.skills.FireBlast, 6], // points left 8 + [sdk.skills.ShockWeb, 8], // points left 1 + [sdk.skills.WakeofFire, 1], // points left 0 + [sdk.skills.LightningSentry, 20, false], + [sdk.skills.DeathSentry, 10], + [sdk.skills.ShockWeb, 9], + [sdk.skills.FireBlast, 8], + [sdk.skills.DeathSentry, 12], + [sdk.skills.ShockWeb, 11], + [sdk.skills.FireBlast, 11], + [sdk.skills.DeathSentry, 13], + [sdk.skills.ShockWeb, 13], + [sdk.skills.FireBlast, 12], + [sdk.skills.DeathSentry, 14], + [sdk.skills.ShockWeb, 15], + [sdk.skills.FireBlast, 14], + [sdk.skills.DeathSentry, 15], + [sdk.skills.ShockWeb, 16], + [sdk.skills.FireBlast, 15], + [sdk.skills.DeathSentry, 16], + [sdk.skills.ShockWeb, 18], + [sdk.skills.FireBlast, 16], + [sdk.skills.DeathSentry, 17], + [sdk.skills.ShockWeb, 20], + [sdk.skills.FireBlast, 18], + [sdk.skills.DeathSentry, 20], + [sdk.skills.ShockWeb, 20], + [sdk.skills.FireBlast, 20], + [sdk.skills.ChargedBoltSentry, 20], + ], - active: function () { - return (me.charlvl > CharInfo.respecOne && me.charlvl > CharInfo.respecTwo && me.getSkill(sdk.skills.LightningSentry, sdk.skills.subindex.HardPoints) === 20 && !Check.finalBuild().active()); - }, + active: function () { + return (me.charlvl > CharInfo.respecOne && me.charlvl > CharInfo.respecTwo && me.getSkill(sdk.skills.LightningSentry, sdk.skills.subindex.HardPoints) === 20 && !Check.finalBuild().active()); + }, - AutoBuildTemplate: { - 1: { - Update: function () { - Config.AttackSkill = [-1, sdk.skills.ShockWeb, sdk.skills.FireBlast, sdk.skills.ShockWeb, sdk.skills.FireBlast, -1, -1]; - Config.LowManaSkill = [-1, -1]; - Config.UseTraps = true; - Config.UseFade = true; - Config.Traps = [sdk.skills.LightningSentry, sdk.skills.LightningSentry, sdk.skills.LightningSentry, sdk.skills.DeathSentry, sdk.skills.DeathSentry]; - Config.BossTraps = [sdk.skills.LightningSentry, sdk.skills.LightningSentry, sdk.skills.LightningSentry, sdk.skills.LightningSentry, sdk.skills.LightningSentry]; - Config.TownHP = me.hardcore ? 0 : 35; - Config.BeltColumn = ["hp", "hp", "mp", "mp"]; - Config.HPBuffer = 2; - Config.MPBuffer = me.charlvl < 80 ? 6 : 2; - Config.DodgeHP = 75; - SetUp.belt(); - } - } - }, - }; - - return build; - })(); + AutoBuildTemplate: { + 1: { + Update: function () { + Config.AttackSkill = [-1, sdk.skills.ShockWeb, sdk.skills.FireBlast, sdk.skills.ShockWeb, sdk.skills.FireBlast, -1, -1]; + Config.LowManaSkill = [-1, -1]; + Config.UseTraps = true; + Config.UseFade = true; + Config.Traps = [sdk.skills.LightningSentry, sdk.skills.LightningSentry, sdk.skills.LightningSentry, sdk.skills.DeathSentry, sdk.skills.DeathSentry]; + Config.BossTraps = [sdk.skills.LightningSentry, sdk.skills.LightningSentry, sdk.skills.LightningSentry, sdk.skills.LightningSentry, sdk.skills.LightningSentry]; + Config.TownHP = me.hardcore ? 0 : 35; + Config.BeltColumn = ["hp", "hp", "mp", "mp"]; + Config.HPBuffer = 2; + Config.MPBuffer = me.charlvl < 80 ? 6 : 2; + Config.DodgeHP = 75; + SetUp.belt(); + } + } + }, + }; + + return build; + })(); })(module); diff --git a/libs/SoloPlay/BuildFiles/assassin/assassin.StartBuild.js b/libs/SoloPlay/BuildFiles/assassin/assassin.StartBuild.js index b024d254..6566cb0b 100644 --- a/libs/SoloPlay/BuildFiles/assassin/assassin.StartBuild.js +++ b/libs/SoloPlay/BuildFiles/assassin/assassin.StartBuild.js @@ -7,65 +7,65 @@ (function (module, require) { - module.exports = (function () { - const build = { - AutoBuildTemplate: {}, - caster: true, - skillstab: sdk.skills.tabs.Traps, - wantedskills: [sdk.skills.FireBlast, sdk.skills.WakeofFire], - usefulskills: [sdk.skills.CloakofShadows, sdk.skills.ShadowWarrior], - wantedMerc: MercData[sdk.skills.HolyFreeze], - stats: [ - ["vitality", 35], ["energy", 35], ["strength", 33], ["dexterity", 33], - ["vitality", 50], ["strength", 46], ["dexterity", 46], - ["vitality", 70], ["strength", 50], ["dexterity", 50], - ["energy", 50], ["vitality", "all"] - ], - skills: [ - [sdk.skills.FireBlast, 4, false], // level 4 - [sdk.skills.ClawMastery, 1], // level 5 (den) - [sdk.skills.PsychicHammer, 1], // level 6 - [sdk.skills.BurstofSpeed, 5], // level 11 - [sdk.skills.WakeofFire, 1, false], // level 12 - [sdk.skills.CloakofShadows, 1, true], // level 13 - [sdk.skills.WakeofFire, 10, false], // level 24 - [sdk.skills.FireBlast, 6, false], // level 26 - [sdk.skills.WakeofFire, 20, false], // level 36 - [sdk.skills.FireBlast, 10], // level 42 - ], + module.exports = (function () { + const build = { + AutoBuildTemplate: {}, + caster: true, + skillstab: sdk.skills.tabs.Traps, + wantedskills: [sdk.skills.FireBlast, sdk.skills.WakeofFire], + usefulskills: [sdk.skills.CloakofShadows, sdk.skills.ShadowWarrior], + wantedMerc: MercData[sdk.skills.HolyFreeze], + stats: [ + ["vitality", 35], ["energy", 35], ["strength", 33], ["dexterity", 33], + ["vitality", 50], ["strength", 46], ["dexterity", 46], + ["vitality", 70], ["strength", 50], ["dexterity", 50], + ["energy", 50], ["vitality", "all"] + ], + skills: [ + [sdk.skills.FireBlast, 4, false], // level 4 + [sdk.skills.ClawMastery, 1], // level 5 (den) + [sdk.skills.PsychicHammer, 1], // level 6 + [sdk.skills.BurstofSpeed, 5], // level 11 + [sdk.skills.WakeofFire, 1, false], // level 12 + [sdk.skills.CloakofShadows, 1, true], // level 13 + [sdk.skills.WakeofFire, 10, false], // level 24 + [sdk.skills.FireBlast, 6, false], // level 26 + [sdk.skills.WakeofFire, 20, false], // level 36 + [sdk.skills.FireBlast, 10], // level 42 + ], - active: function () { - return me.charlvl < CharInfo.respecOne && !me.checkSkill(sdk.skills.LightningSentry, sdk.skills.subindex.HardPoints); - }, - }; + active: function () { + return me.charlvl < CharInfo.respecOne && !me.checkSkill(sdk.skills.LightningSentry, sdk.skills.subindex.HardPoints); + }, + }; - const { buildAutoBuildTempObj } = require("../../Utils/General"); - - build.AutoBuildTemplate[1] = buildAutoBuildTempObj(() => { - Config.TownHP = me.hardcore ? 0 : 35; - Config.BeltColumn = ["hp", "hp", "hp", "hp"]; - Config.HPBuffer = 4; - Config.MPBuffer = 2; - Config.AttackSkill = [-1, 0, 0, 0, 0, 0, 0]; - Config.LowManaSkill = [0, 0]; - SetUp.belt(); - }); - build.AutoBuildTemplate[2] = buildAutoBuildTempObj(() => { - Config.BeltColumn = ["hp", "hp", "mp", "mp"]; - Config.HPBuffer = 2; - Config.MPBuffer = 6; - Config.AttackSkill = [-1, sdk.skills.FireBlast, -1, sdk.skills.FireBlast, -1, (me.checkSkill(sdk.skills.PsychicHammer, sdk.skills.subindex.SoftPoints) ? sdk.skills.PsychicHammer : 0), 0]; - Config.UseBoS = true; - SetUp.belt(); - }); - build.AutoBuildTemplate[12] = buildAutoBuildTempObj(() => { - Config.AttackSkill = [-1, sdk.skills.FireBlast, -1, sdk.skills.FireBlast, -1, (me.checkSkill(sdk.skills.PsychicHammer, sdk.skills.subindex.SoftPoints) ? sdk.skills.PsychicHammer : 0), 0]; - Config.UseTraps = true; - Config.Traps = [sdk.skills.WakeofFire, sdk.skills.WakeofFire, sdk.skills.WakeofFire, -1, -1]; // Skill IDs for traps to be cast on all mosters except act bosses. - Config.BossTraps = [sdk.skills.WakeofFire, sdk.skills.WakeofFire, sdk.skills.WakeofFire, sdk.skills.WakeofFire, sdk.skills.WakeofFire]; // Skill IDs for traps to be cast on act bosses. - SetUp.belt(); - }); + const { buildAutoBuildTempObj } = require("../../Utils/General"); + + build.AutoBuildTemplate[1] = buildAutoBuildTempObj(() => { + Config.TownHP = me.hardcore ? 0 : 35; + Config.BeltColumn = ["hp", "hp", "hp", "hp"]; + Config.HPBuffer = 4; + Config.MPBuffer = 2; + Config.AttackSkill = [-1, 0, 0, 0, 0, 0, 0]; + Config.LowManaSkill = [0, 0]; + SetUp.belt(); + }); + build.AutoBuildTemplate[2] = buildAutoBuildTempObj(() => { + Config.BeltColumn = ["hp", "hp", "mp", "mp"]; + Config.HPBuffer = 2; + Config.MPBuffer = 6; + Config.AttackSkill = [-1, sdk.skills.FireBlast, -1, sdk.skills.FireBlast, -1, (me.checkSkill(sdk.skills.PsychicHammer, sdk.skills.subindex.SoftPoints) ? sdk.skills.PsychicHammer : 0), 0]; + Config.UseBoS = true; + SetUp.belt(); + }); + build.AutoBuildTemplate[12] = buildAutoBuildTempObj(() => { + Config.AttackSkill = [-1, sdk.skills.FireBlast, -1, sdk.skills.FireBlast, -1, (me.checkSkill(sdk.skills.PsychicHammer, sdk.skills.subindex.SoftPoints) ? sdk.skills.PsychicHammer : 0), 0]; + Config.UseTraps = true; + Config.Traps = [sdk.skills.WakeofFire, sdk.skills.WakeofFire, sdk.skills.WakeofFire, -1, -1]; // Skill IDs for traps to be cast on all mosters except act bosses. + Config.BossTraps = [sdk.skills.WakeofFire, sdk.skills.WakeofFire, sdk.skills.WakeofFire, sdk.skills.WakeofFire, sdk.skills.WakeofFire]; // Skill IDs for traps to be cast on act bosses. + SetUp.belt(); + }); - return build; - })(); + return build; + })(); })(module, require); diff --git a/libs/SoloPlay/BuildFiles/assassin/assassin.TrapsinBuild.js b/libs/SoloPlay/BuildFiles/assassin/assassin.TrapsinBuild.js index 9a200a79..1bed4844 100644 --- a/libs/SoloPlay/BuildFiles/assassin/assassin.TrapsinBuild.js +++ b/libs/SoloPlay/BuildFiles/assassin/assassin.TrapsinBuild.js @@ -7,148 +7,148 @@ (function (module) { - module.exports = (function () { - const build = { - caster: true, - skillstab: sdk.skills.tabs.Traps, - wantedskills: [sdk.skills.FireBlast, sdk.skills.LightningSentry, sdk.skills.DeathSentry, sdk.skills.ShadowMaster], - usefulskills: [sdk.skills.ChargedBoltSentry, sdk.skills.BladeShield, sdk.skills.Fade], - precastSkills: [sdk.skills.Fade, sdk.skills.ShadowMaster], - wantedMerc: MercData[sdk.skills.HolyFreeze], - stats: [ - ["strength", 156], ["dexterity", 79], ["vitality", "all"] - ], - skills: [ - [sdk.skills.MindBlast, 1], - [sdk.skills.ShadowMaster, 1], - [sdk.skills.Fade, 1], - [sdk.skills.LightningSentry, 20], - [sdk.skills.ShockWeb, 15], - [sdk.skills.FireBlast, 14], - [sdk.skills.DeathSentry, 15], // lvl 74 w/o quest skills pts - [sdk.skills.ShockWeb, 16], - [sdk.skills.FireBlast, 15], - [sdk.skills.DeathSentry, 16], - [sdk.skills.ShockWeb, 18], - [sdk.skills.FireBlast, 16], - [sdk.skills.DeathSentry, 17], - [sdk.skills.ShockWeb, 20], - [sdk.skills.FireBlast, 18], - [sdk.skills.DeathSentry, 20], - [sdk.skills.ShockWeb, 20], - [sdk.skills.FireBlast, 20], - [sdk.skills.ChargedBoltSentry, 20], - ], + module.exports = (function () { + const build = { + caster: true, + skillstab: sdk.skills.tabs.Traps, + wantedskills: [sdk.skills.FireBlast, sdk.skills.LightningSentry, sdk.skills.DeathSentry, sdk.skills.ShadowMaster], + usefulskills: [sdk.skills.ChargedBoltSentry, sdk.skills.BladeShield, sdk.skills.Fade], + precastSkills: [sdk.skills.Fade, sdk.skills.ShadowMaster], + wantedMerc: MercData[sdk.skills.HolyFreeze], + stats: [ + ["strength", 156], ["dexterity", 79], ["vitality", "all"] + ], + skills: [ + [sdk.skills.MindBlast, 1], + [sdk.skills.ShadowMaster, 1], + [sdk.skills.Fade, 1], + [sdk.skills.LightningSentry, 20], + [sdk.skills.ShockWeb, 15], + [sdk.skills.FireBlast, 14], + [sdk.skills.DeathSentry, 15], // lvl 74 w/o quest skills pts + [sdk.skills.ShockWeb, 16], + [sdk.skills.FireBlast, 15], + [sdk.skills.DeathSentry, 16], + [sdk.skills.ShockWeb, 18], + [sdk.skills.FireBlast, 16], + [sdk.skills.DeathSentry, 17], + [sdk.skills.ShockWeb, 20], + [sdk.skills.FireBlast, 18], + [sdk.skills.DeathSentry, 20], + [sdk.skills.ShockWeb, 20], + [sdk.skills.FireBlast, 20], + [sdk.skills.ChargedBoltSentry, 20], + ], - charms: { - ResLife: { - max: 3, - have: [], - classid: sdk.items.SmallCharm, - stats: function (check) { - return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MaxHp) === 20); - } - }, + charms: { + ResLife: { + max: 3, + have: [], + classid: sdk.items.SmallCharm, + stats: function (check) { + return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MaxHp) === 20); + } + }, - ResMf: { - max: 2, - have: [], - classid: sdk.items.SmallCharm, - stats: function (check) { - return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MagicBonus) === 7); - } - }, + ResMf: { + max: 2, + have: [], + classid: sdk.items.SmallCharm, + stats: function (check) { + return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MagicBonus) === 7); + } + }, - ResFHR: { - max: 1, - have: [], - classid: sdk.items.SmallCharm, - stats: function (check) { - return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.FHR) === 5); - } - }, + ResFHR: { + max: 1, + have: [], + classid: sdk.items.SmallCharm, + stats: function (check) { + return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.FHR) === 5); + } + }, - LifeMana: { - max: 2, - have: [], - classid: sdk.items.SmallCharm, - stats: function (check) { - return (!check.unique && check.classid === this.classid && check.getStat(sdk.stats.MaxHp) === 20 && check.getStat(sdk.stats.MaxMana) === 17); - } - }, + LifeMana: { + max: 2, + have: [], + classid: sdk.items.SmallCharm, + stats: function (check) { + return (!check.unique && check.classid === this.classid && check.getStat(sdk.stats.MaxHp) === 20 && check.getStat(sdk.stats.MaxMana) === 17); + } + }, - Skiller: { - max: 2, - have: [], - classid: sdk.items.GrandCharm, - stats: function (check) { - return (!check.unique && check.classid === this.classid && check.getStat(sdk.stats.AddSkillTab, sdk.skills.tabs.Traps) === 1 - && check.getStat(sdk.stats.MaxHp) >= 40); - } - }, - }, + Skiller: { + max: 2, + have: [], + classid: sdk.items.GrandCharm, + stats: function (check) { + return (!check.unique && check.classid === this.classid && check.getStat(sdk.stats.AddSkillTab, sdk.skills.tabs.Traps) === 1 + && check.getStat(sdk.stats.MaxHp) >= 40); + } + }, + }, - AutoBuildTemplate: { - 1: { - Update: function () { - Config.UseTraps = true; - Config.AttackSkill = [-1, sdk.skills.ShockWeb, sdk.skills.FireBlast, sdk.skills.ShockWeb, sdk.skills.FireBlast, -1, -1]; - Config.Traps = [sdk.skills.LightningSentry, sdk.skills.LightningSentry, sdk.skills.LightningSentry, sdk.skills.DeathSentry, sdk.skills.DeathSentry]; - Config.BossTraps = [sdk.skills.LightningSentry, sdk.skills.LightningSentry, sdk.skills.LightningSentry, sdk.skills.LightningSentry, sdk.skills.LightningSentry]; - } - }, - }, + AutoBuildTemplate: { + 1: { + Update: function () { + Config.UseTraps = true; + Config.AttackSkill = [-1, sdk.skills.ShockWeb, sdk.skills.FireBlast, sdk.skills.ShockWeb, sdk.skills.FireBlast, -1, -1]; + Config.Traps = [sdk.skills.LightningSentry, sdk.skills.LightningSentry, sdk.skills.LightningSentry, sdk.skills.DeathSentry, sdk.skills.DeathSentry]; + Config.BossTraps = [sdk.skills.LightningSentry, sdk.skills.LightningSentry, sdk.skills.LightningSentry, sdk.skills.LightningSentry, sdk.skills.LightningSentry]; + } + }, + }, - respec: function () { - return (Attack.checkInfinity() || (me.data.merc.gear.includes(sdk.locale.items.Infinity) && !Misc.poll(() => me.getMerc(), 200, 50))) && Check.haveItem("armor", "runeword", "Enigma"); - }, + respec: function () { + return (Attack.checkInfinity() || (me.data.merc.gear.includes(sdk.locale.items.Infinity) && !Misc.poll(() => me.getMerc(), 200, 50))) && Check.haveItem("armor", "runeword", "Enigma"); + }, - active: function () { - return this.respec() && me.getSkill(sdk.skills.LightningSentry, sdk.skills.subindex.HardPoints) === 20; - }, - }; - - let finalGear = [ // autoequip final gear - // Final Weapon - Silence - "[type] == sword && [flag] == runeword # [itemallskills] == 2 && [ias] == 20 && [fireresist] == 75 # [tier] == 200000", - // Temporary Weapon - HotO - "[type] == mace && [flag] == runeword # [itemallskills] == 3 # [tier] == 100000", - // Helmet - Andy's - "[name] == demonhead && [quality] == unique && [flag] != ethereal # [strength] >= 25 && [enhanceddefense] >= 100 # [tier] == tierscore(item, 100000)", - // Belt - Arach's - "[name] == spiderwebsash && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 90 # [tier] == tierscore(item, 100000)", - // Boots - Waterwalks - "[name] == sharkskinboots && [quality] == unique && [flag] != ethereal # [maxhp] >= 65 # [tier] == tierscore(item, 100000)", - // Armor - Enigma - "[type] == armor && [flag] != ethereal && [flag] == runeword # [itemallskills] == 2 # [tier] == 100000", - // Shield - Spirit - "[name] == monarch && [flag] == runeword # [fcr] >= 25 && [maxmana] >= 89 # [tier] == tierscore(item, 110000)", - // Gloves - Lava Gout - "[name] == battlegauntlets && [quality] == unique && [flag] != ethereal # [ias] == 20 # [tier] == tierscore(item, 100000)", - // Amulet - Maras - "[type] == amulet && [quality] == unique # [strength] == 5 && [coldresist] >= 30 # [tier] == tierscore(item, 100000)", - // Final Rings - SoJ & Perfect Raven Frost - "[type] == ring && [quality] == unique # [itemmaxmanapercent] == 25 # [tier] == 100000", - "[type] == ring && [quality] == unique # [dexterity] == 20 # [tier] == 110000", - // Rings - Raven Frost - "[type] == ring && [quality] == unique # [dexterity] >= 15 # [tier] == 100000", - // Switch Final Weapon - CTA - "[minimumsockets] >= 5 && [flag] == runeword # [plusskillbattleorders] >= 1 # [secondarytier] == 100000", - // Switch Final Shield - Spirit - "[name] == monarch && [flag] == runeword # [fcr] >= 25 && [maxmana] >= 89 # [secondarytier] == 110000", - // Switch Temporary Shield - Any 1+ all skill - "[type] == shield # [itemallskills] >= 1 # [secondarytier] == tierscore(item, 50000)", - // Merc Armor - Fortitude - "[type] == armor && [flag] == runeword # [enhanceddefense] >= 200 && [enhanceddamage] >= 300 # [merctier] == 100000", - // Merc Final Helmet - Eth Andy's - "[name] == demonhead && [quality] == unique && [flag] == ethereal # [strength] >= 25 && [enhanceddefense] >= 100 # [merctier] == 50000 + mercscore(item)", - // Merc Helmet - Andy's - "[name] == demonhead && [quality] == unique && [flag] != ethereal # [strength] >= 25 && [enhanceddefense] >= 100 # [merctier] == 40000 + mercscore(item)", - ]; + active: function () { + return this.respec() && me.getSkill(sdk.skills.LightningSentry, sdk.skills.subindex.HardPoints) === 20; + }, + }; + + let finalGear = [ // autoequip final gear + // Final Weapon - Silence + "[type] == sword && [flag] == runeword # [itemallskills] == 2 && [ias] == 20 && [fireresist] == 75 # [tier] == 200000", + // Temporary Weapon - HotO + "[type] == mace && [flag] == runeword # [itemallskills] == 3 # [tier] == 100000", + // Helmet - Andy's + "[name] == demonhead && [quality] == unique && [flag] != ethereal # [strength] >= 25 && [enhanceddefense] >= 100 # [tier] == tierscore(item, 100000)", + // Belt - Arach's + "[name] == spiderwebsash && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 90 # [tier] == tierscore(item, 100000)", + // Boots - Waterwalks + "[name] == sharkskinboots && [quality] == unique && [flag] != ethereal # [maxhp] >= 65 # [tier] == tierscore(item, 100000)", + // Armor - Enigma + "[type] == armor && [flag] != ethereal && [flag] == runeword # [itemallskills] == 2 # [tier] == 100000", + // Shield - Spirit + "[name] == monarch && [flag] == runeword # [fcr] >= 25 && [maxmana] >= 89 # [tier] == tierscore(item, 110000)", + // Gloves - Lava Gout + "[name] == battlegauntlets && [quality] == unique && [flag] != ethereal # [ias] == 20 # [tier] == tierscore(item, 100000)", + // Amulet - Maras + "[type] == amulet && [quality] == unique # [strength] == 5 && [coldresist] >= 30 # [tier] == tierscore(item, 100000)", + // Final Rings - SoJ & Perfect Raven Frost + "[type] == ring && [quality] == unique # [itemmaxmanapercent] == 25 # [tier] == 100000", + "[type] == ring && [quality] == unique # [dexterity] == 20 # [tier] == 110000", + // Rings - Raven Frost + "[type] == ring && [quality] == unique # [dexterity] >= 15 # [tier] == 100000", + // Switch Final Weapon - CTA + "[minimumsockets] >= 5 && [flag] == runeword # [plusskillbattleorders] >= 1 # [secondarytier] == 100000", + // Switch Final Shield - Spirit + "[name] == monarch && [flag] == runeword # [fcr] >= 25 && [maxmana] >= 89 # [secondarytier] == 110000", + // Switch Temporary Shield - Any 1+ all skill + "[type] == shield # [itemallskills] >= 1 # [secondarytier] == tierscore(item, 50000)", + // Merc Armor - Fortitude + "[type] == armor && [flag] == runeword # [enhanceddefense] >= 200 && [enhanceddamage] >= 300 # [merctier] == 100000", + // Merc Final Helmet - Eth Andy's + "[name] == demonhead && [quality] == unique && [flag] == ethereal # [strength] >= 25 && [enhanceddefense] >= 100 # [merctier] == 50000 + mercscore(item)", + // Merc Helmet - Andy's + "[name] == demonhead && [quality] == unique && [flag] != ethereal # [strength] >= 25 && [enhanceddefense] >= 100 # [merctier] == 40000 + mercscore(item)", + ]; - NTIP.buildList(finalGear); - NTIP.buildFinalGear(finalGear); + NTIP.buildList(finalGear); + NTIP.buildFinalGear(finalGear); - return build; - })(); + return build; + })(); })(module); diff --git a/libs/SoloPlay/BuildFiles/assassin/assassin.WhirlsinBuild.js b/libs/SoloPlay/BuildFiles/assassin/assassin.WhirlsinBuild.js index 7d93a148..ed2fe915 100644 --- a/libs/SoloPlay/BuildFiles/assassin/assassin.WhirlsinBuild.js +++ b/libs/SoloPlay/BuildFiles/assassin/assassin.WhirlsinBuild.js @@ -7,139 +7,139 @@ (function (module) { - module.exports = (function () { - const build = { - caster: false, - skillstab: sdk.skills.tabs.ShadowDisciplines, - wantedskills: [sdk.skills.FireBlast, sdk.skills.LightningSentry, sdk.skills.DeathSentry, sdk.skills.ShadowMaster], - usefulskills: [sdk.skills.ChargedBoltSentry, sdk.skills.BladeShield, sdk.skills.Fade], - precastSkills: [sdk.skills.Fade, sdk.skills.ShadowMaster], - wantedMerc: MercData[sdk.skills.HolyFreeze], - stats: [ - ["strength", 130], ["dexterity", 99], ["vitality", "all"] - ], - skills: [ - [sdk.skills.Fade, 1], - [sdk.skills.Venom, 1], - [sdk.skills.MindBlast, 1], - [sdk.skills.BladeShield, 1], - [sdk.skills.ClawMastery, 20], - [sdk.skills.DeathSentry, 1], - [sdk.skills.ShadowMaster, 20, false], - [sdk.skills.Venom, 20, false], // lvl 77 w/o quest skill pts - [sdk.skills.Fade, 20, false], - [sdk.skills.BladeShield, 20], - ], + module.exports = (function () { + const build = { + caster: false, + skillstab: sdk.skills.tabs.ShadowDisciplines, + wantedskills: [sdk.skills.FireBlast, sdk.skills.LightningSentry, sdk.skills.DeathSentry, sdk.skills.ShadowMaster], + usefulskills: [sdk.skills.ChargedBoltSentry, sdk.skills.BladeShield, sdk.skills.Fade], + precastSkills: [sdk.skills.Fade, sdk.skills.ShadowMaster], + wantedMerc: MercData[sdk.skills.HolyFreeze], + stats: [ + ["strength", 130], ["dexterity", 99], ["vitality", "all"] + ], + skills: [ + [sdk.skills.Fade, 1], + [sdk.skills.Venom, 1], + [sdk.skills.MindBlast, 1], + [sdk.skills.BladeShield, 1], + [sdk.skills.ClawMastery, 20], + [sdk.skills.DeathSentry, 1], + [sdk.skills.ShadowMaster, 20, false], + [sdk.skills.Venom, 20, false], // lvl 77 w/o quest skill pts + [sdk.skills.Fade, 20, false], + [sdk.skills.BladeShield, 20], + ], - charms: { - ResLife: { - max: 3, - have: [], - classid: sdk.items.SmallCharm, - stats: function (check) { - return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MaxHp) === 20); - } - }, + charms: { + ResLife: { + max: 3, + have: [], + classid: sdk.items.SmallCharm, + stats: function (check) { + return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MaxHp) === 20); + } + }, - ResMf: { - max: 2, - have: [], - classid: sdk.items.SmallCharm, - stats: function (check) { - return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MagicBonus) === 7); - } - }, + ResMf: { + max: 2, + have: [], + classid: sdk.items.SmallCharm, + stats: function (check) { + return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MagicBonus) === 7); + } + }, - ResFHR: { - max: 1, - have: [], - classid: sdk.items.SmallCharm, - stats: function (check) { - return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.FHR) === 5); - } - }, + ResFHR: { + max: 1, + have: [], + classid: sdk.items.SmallCharm, + stats: function (check) { + return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.FHR) === 5); + } + }, - LifeMana: { - max: 2, - have: [], - classid: sdk.items.SmallCharm, - stats: function (check) { - return (!check.unique && check.classid === this.classid && check.getStat(sdk.stats.MaxHp) === 20 && check.getStat(sdk.stats.MaxMana) === 17); - } - }, + LifeMana: { + max: 2, + have: [], + classid: sdk.items.SmallCharm, + stats: function (check) { + return (!check.unique && check.classid === this.classid && check.getStat(sdk.stats.MaxHp) === 20 && check.getStat(sdk.stats.MaxMana) === 17); + } + }, - Skiller: { - max: 1, - have: [], - classid: sdk.items.GrandCharm, - stats: function (check) { - return (!check.unique && check.classid === this.classid && check.getStat(sdk.stats.AddSkillTab, sdk.skills.tabs.ShadowDisciplines) === 1 - && check.getStat(sdk.stats.MaxHp) >= 40); - } - }, - }, + Skiller: { + max: 1, + have: [], + classid: sdk.items.GrandCharm, + stats: function (check) { + return (!check.unique && check.classid === this.classid && check.getStat(sdk.stats.AddSkillTab, sdk.skills.tabs.ShadowDisciplines) === 1 + && check.getStat(sdk.stats.MaxHp) >= 40); + } + }, + }, - AutoBuildTemplate: { - 1: { - Update: function () { - Config.Dodge = false; - Config.UseVenom = true; - Config.UseTraps = true; - Config.AttackSkill = [-1, sdk.skills.Whirlwind, -1, sdk.skills.Whirlwind, -1, -1, -1]; - Config.Traps = [sdk.skills.DeathSentry, sdk.skills.DeathSentry, sdk.skills.DeathSentry, sdk.skills.DeathSentry, sdk.skills.DeathSentry]; - Config.BossTraps = [-1, -1, -1, -1, -1]; - } - }, - }, + AutoBuildTemplate: { + 1: { + Update: function () { + Config.Dodge = false; + Config.UseVenom = true; + Config.UseTraps = true; + Config.AttackSkill = [-1, sdk.skills.Whirlwind, -1, sdk.skills.Whirlwind, -1, -1, -1]; + Config.Traps = [sdk.skills.DeathSentry, sdk.skills.DeathSentry, sdk.skills.DeathSentry, sdk.skills.DeathSentry, sdk.skills.DeathSentry]; + Config.BossTraps = [-1, -1, -1, -1, -1]; + } + }, + }, - respec: function () { - return me.haveAll([{ name: sdk.locale.items.Chaos }, { name: sdk.locale.items.Fury }]); - }, + respec: function () { + return me.haveAll([{ name: sdk.locale.items.Chaos }, { name: sdk.locale.items.Fury }]); + }, - active: function () { - return this.respec() && me.getSkill(sdk.skills.ClawMastery, sdk.skills.subindex.HardPoints) === 20; - }, - }; - - let finalGear = [ // autoequip final gear - // Final Weapon - Chaos Claw & Fury - "[type] == assassinclaw && [flag] == runeword # [plusskillwhirlwind] == 1 # [tier] == 100000", - "[type] == assassinclaw && [flag] == runeword # [itemallskills] == 2 && [ias] == 40 && [itemdeadlystrike] == 33 # [tier] == 200000", - // Helmet - GFace - "[name] == wingedhelm && [quality] == set && [flag] != ethereal # [fhr] >= 30 # [tier] == tierscore(item, 100000)", - // Belt - Verdungos - "[name] == mithrilcoil && [quality] == unique && [flag] != ethereal # [damageresist] == 15 # [tier] == tierscore(item, 110000)", - // Boots - Gore Rider - "[name] == warboots && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 160 # [tier] == tierscore(item, 110000)", - // Armor - Fortitude - "[type] == armor && [flag] == runeword # [enhanceddefense] >= 200 && [enhanceddamage] >= 300 # [tier] == 100000", - // Gloves - Trang-Ouls - "[name] == heavybracers && [quality] == set && [flag] != ethereal # [fcr] == 20 # [tier] == 100000", // - // Amulet - Highlords - "[type] == amulet && [quality] == unique # [lightresist] == 35 # [tier] == 100000", - // Final Rings - Perfect Raven Frost & Bul-Kathos' Wedding Band - "[type] == ring && [quality] == unique # [dexterity] == 20 && [tohit] == 250 # [tier] == 110000", - "[type] == ring && [quality] == unique # [maxstamina] == 50 && [lifeleech] == 5 # [tier] == 110000", - // Rings - Raven Frost && Bul-Kathos' Wedding Band - "[type] == ring && [quality] == unique # [dexterity] >= 15 && [tohit] >= 150 # [tier] == 100000", - "[type] == ring && [quality] == unique # [maxstamina] == 50 && [lifeleech] >= 3 # [tier] == 100000", - // Switch Final Weapon - CTA - "[minimumsockets] >= 5 && [flag] == runeword # [plusskillbattleorders] >= 1 # [secondarytier] == 100000", - // Switch Final Shield - Spirit - "[name] == monarch && [flag] == runeword # [fcr] >= 25 && [maxmana] >= 89 # [secondarytier] == 110000", - // Switch Temporary Shield - Any 1+ all skill - "[type] == shield # [itemallskills] >= 1 # [secondarytier] == tierscore(item, 50000)", - // Merc Armor - Fortitude - "[type] == armor && [flag] == runeword # [enhanceddefense] >= 200 && [enhanceddamage] >= 300 # [merctier] == 100000", - // Merc Final Helmet - Eth Andy's - "[name] == demonhead && [quality] == unique && [flag] == ethereal # [strength] >= 25 && [enhanceddefense] >= 100 # [merctier] == 50000 + mercscore(item)", - // Merc Helmet - Andy's - "[name] == demonhead && [quality] == unique && [flag] != ethereal # [strength] >= 25 && [enhanceddefense] >= 100 # [merctier] == 40000 + mercscore(item)", - ]; + active: function () { + return this.respec() && me.getSkill(sdk.skills.ClawMastery, sdk.skills.subindex.HardPoints) === 20; + }, + }; + + let finalGear = [ // autoequip final gear + // Final Weapon - Chaos Claw & Fury + "[type] == assassinclaw && [flag] == runeword # [plusskillwhirlwind] == 1 # [tier] == 100000", + "[type] == assassinclaw && [flag] == runeword # [itemallskills] == 2 && [ias] == 40 && [itemdeadlystrike] == 33 # [tier] == 200000", + // Helmet - GFace + "[name] == wingedhelm && [quality] == set && [flag] != ethereal # [fhr] >= 30 # [tier] == tierscore(item, 100000)", + // Belt - Verdungos + "[name] == mithrilcoil && [quality] == unique && [flag] != ethereal # [damageresist] == 15 # [tier] == tierscore(item, 110000)", + // Boots - Gore Rider + "[name] == warboots && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 160 # [tier] == tierscore(item, 110000)", + // Armor - Fortitude + "[type] == armor && [flag] == runeword # [enhanceddefense] >= 200 && [enhanceddamage] >= 300 # [tier] == 100000", + // Gloves - Trang-Ouls + "[name] == heavybracers && [quality] == set && [flag] != ethereal # [fcr] == 20 # [tier] == 100000", // + // Amulet - Highlords + "[type] == amulet && [quality] == unique # [lightresist] == 35 # [tier] == 100000", + // Final Rings - Perfect Raven Frost & Bul-Kathos' Wedding Band + "[type] == ring && [quality] == unique # [dexterity] == 20 && [tohit] == 250 # [tier] == 110000", + "[type] == ring && [quality] == unique # [maxstamina] == 50 && [lifeleech] == 5 # [tier] == 110000", + // Rings - Raven Frost && Bul-Kathos' Wedding Band + "[type] == ring && [quality] == unique # [dexterity] >= 15 && [tohit] >= 150 # [tier] == 100000", + "[type] == ring && [quality] == unique # [maxstamina] == 50 && [lifeleech] >= 3 # [tier] == 100000", + // Switch Final Weapon - CTA + "[minimumsockets] >= 5 && [flag] == runeword # [plusskillbattleorders] >= 1 # [secondarytier] == 100000", + // Switch Final Shield - Spirit + "[name] == monarch && [flag] == runeword # [fcr] >= 25 && [maxmana] >= 89 # [secondarytier] == 110000", + // Switch Temporary Shield - Any 1+ all skill + "[type] == shield # [itemallskills] >= 1 # [secondarytier] == tierscore(item, 50000)", + // Merc Armor - Fortitude + "[type] == armor && [flag] == runeword # [enhanceddefense] >= 200 && [enhanceddamage] >= 300 # [merctier] == 100000", + // Merc Final Helmet - Eth Andy's + "[name] == demonhead && [quality] == unique && [flag] == ethereal # [strength] >= 25 && [enhanceddefense] >= 100 # [merctier] == 50000 + mercscore(item)", + // Merc Helmet - Andy's + "[name] == demonhead && [quality] == unique && [flag] != ethereal # [strength] >= 25 && [enhanceddefense] >= 100 # [merctier] == 40000 + mercscore(item)", + ]; - NTIP.buildList(finalGear); - NTIP.buildFinalGear(finalGear); + NTIP.buildList(finalGear); + NTIP.buildFinalGear(finalGear); - return build; - })(); + return build; + })(); })(module); diff --git a/libs/SoloPlay/BuildFiles/assassin/assassin.js b/libs/SoloPlay/BuildFiles/assassin/assassin.js index 739491d4..c0e23d84 100644 --- a/libs/SoloPlay/BuildFiles/assassin/assassin.js +++ b/libs/SoloPlay/BuildFiles/assassin/assassin.js @@ -6,37 +6,37 @@ */ const CharInfo = { - respecOne: 32, - respecTwo: 0, - levelCap: (function() { - const currentDiff = sdk.difficulty.nameOf(me.diff); - const softcoreMode = { - "Normal": 33, - "Nightmare": 65, - "Hell": 100, - }; - const hardcoreMode = { - "Normal": 36, - "Nightmare": 65, - "Hell": 100, - }; + respecOne: 32, + respecTwo: 0, + levelCap: (function() { + const currentDiff = sdk.difficulty.nameOf(me.diff); + const softcoreMode = { + "Normal": 33, + "Nightmare": 65, + "Hell": 100, + }; + const hardcoreMode = { + "Normal": 36, + "Nightmare": 65, + "Hell": 100, + }; - return me.softcore ? softcoreMode[currentDiff] : hardcoreMode[currentDiff]; - })(), + return me.softcore ? softcoreMode[currentDiff] : hardcoreMode[currentDiff]; + })(), - getActiveBuild: function () { - const nSkills = me.getStat(sdk.stats.NewSkills); - const currLevel = me.charlvl; - const justRepeced = (nSkills >= currLevel); + getActiveBuild: function () { + const nSkills = me.getStat(sdk.stats.NewSkills); + const currLevel = me.charlvl; + const justRepeced = (nSkills >= currLevel); - switch (true) { - case currLevel < this.respecOne && !me.checkSkill(sdk.skills.LightningSentry, sdk.skills.subindex.HardPoints): - return "Start"; - case Check.finalBuild().respec() && justRepeced: - case Check.finalBuild().active(): - return SetUp.finalBuild; - default: - return "Leveling"; - } - }, + switch (true) { + case currLevel < this.respecOne && !me.checkSkill(sdk.skills.LightningSentry, sdk.skills.subindex.HardPoints): + return "Start"; + case Check.finalBuild().respec() && justRepeced: + case Check.finalBuild().active(): + return SetUp.finalBuild; + default: + return "Leveling"; + } + }, }; diff --git a/libs/SoloPlay/BuildFiles/barbarian/barbarian.FrenzyBuild.js b/libs/SoloPlay/BuildFiles/barbarian/barbarian.FrenzyBuild.js index ee631d17..f8bc20ab 100644 --- a/libs/SoloPlay/BuildFiles/barbarian/barbarian.FrenzyBuild.js +++ b/libs/SoloPlay/BuildFiles/barbarian/barbarian.FrenzyBuild.js @@ -7,140 +7,140 @@ (function (module) { - module.exports = (function () { - const build = { - caster: false, - skillstab: sdk.skills.tabs.BarbCombat, - wantedskills: [sdk.skills.BattleOrders, sdk.skills.Frenzy, sdk.skills.DoubleSwing], - usefulskills: [sdk.skills.NaturalResistance, sdk.skills.IronSkin, sdk.skills.IncreasedSpeed], - precastSkills: [sdk.skills.BattleOrders, sdk.skills.WarCry], - wantedMerc: MercData[sdk.skills.Might], - stats: [ - ["strength", 103], ["dexterity", 79], ["vitality", 90], - ["dexterity", 136], ["strength", 150], ["vitality", "all"], - ], - skills: [ - [sdk.skills.DoubleSwing, 9, true], - [sdk.skills.SwordMastery, 6, false], - [sdk.skills.BattleCommand, 1, true], - [sdk.skills.WarCry, 1, true], - [sdk.skills.NaturalResistance, 5, true], - [sdk.skills.Berserk, 5, true], - [sdk.skills.Frenzy, 20, false], - [sdk.skills.BattleOrders, 20, false], // lvl 77 w/o quest skill pts - [sdk.skills.SwordMastery, 20, false], - [sdk.skills.DoubleSwing, 20, false], - ], + module.exports = (function () { + const build = { + caster: false, + skillstab: sdk.skills.tabs.BarbCombat, + wantedskills: [sdk.skills.BattleOrders, sdk.skills.Frenzy, sdk.skills.DoubleSwing], + usefulskills: [sdk.skills.NaturalResistance, sdk.skills.IronSkin, sdk.skills.IncreasedSpeed], + precastSkills: [sdk.skills.BattleOrders, sdk.skills.WarCry], + wantedMerc: MercData[sdk.skills.Might], + stats: [ + ["strength", 103], ["dexterity", 79], ["vitality", 90], + ["dexterity", 136], ["strength", 150], ["vitality", "all"], + ], + skills: [ + [sdk.skills.DoubleSwing, 9, true], + [sdk.skills.SwordMastery, 6, false], + [sdk.skills.BattleCommand, 1, true], + [sdk.skills.WarCry, 1, true], + [sdk.skills.NaturalResistance, 5, true], + [sdk.skills.Berserk, 5, true], + [sdk.skills.Frenzy, 20, false], + [sdk.skills.BattleOrders, 20, false], // lvl 77 w/o quest skill pts + [sdk.skills.SwordMastery, 20, false], + [sdk.skills.DoubleSwing, 20, false], + ], - charms: { - ResLife: { - max: 4, - have: [], - classid: sdk.items.SmallCharm, - stats: function (check) { - return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MaxHp) === 20); - } - }, + charms: { + ResLife: { + max: 4, + have: [], + classid: sdk.items.SmallCharm, + stats: function (check) { + return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MaxHp) === 20); + } + }, - ResMf: { - max: 2, - have: [], - classid: sdk.items.SmallCharm, - stats: function (check) { - return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MagicBonus) === 7); - } - }, + ResMf: { + max: 2, + have: [], + classid: sdk.items.SmallCharm, + stats: function (check) { + return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MagicBonus) === 7); + } + }, - ResFHR: { - max: 2, - have: [], - classid: sdk.items.SmallCharm, - stats: function (check) { - return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.FHR) === 5); - } - }, + ResFHR: { + max: 2, + have: [], + classid: sdk.items.SmallCharm, + stats: function (check) { + return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.FHR) === 5); + } + }, - SkillerCombat: { - max: 1, - have: [], - classid: sdk.items.GrandCharm, - stats: function (check) { - return (!check.unique && check.classid === this.classid && check.getStat(sdk.stats.AddSkillTab, sdk.skills.tabs.BarbCombat) === 1 - && check.getStat(sdk.stats.MaxHp) >= 40); - } - }, + SkillerCombat: { + max: 1, + have: [], + classid: sdk.items.GrandCharm, + stats: function (check) { + return (!check.unique && check.classid === this.classid && check.getStat(sdk.stats.AddSkillTab, sdk.skills.tabs.BarbCombat) === 1 + && check.getStat(sdk.stats.MaxHp) >= 40); + } + }, - SkillerMasteries: { - max: 1, - have: [], - classid: sdk.items.GrandCharm, - stats: function (check) { - return (!check.unique && check.classid === this.classid && check.getStat(sdk.stats.AddSkillTab, sdk.skills.tabs.Masteries) === 1 - && check.getStat(sdk.stats.MaxHp) >= 40); - } - }, - }, + SkillerMasteries: { + max: 1, + have: [], + classid: sdk.items.GrandCharm, + stats: function (check) { + return (!check.unique && check.classid === this.classid && check.getStat(sdk.stats.AddSkillTab, sdk.skills.tabs.Masteries) === 1 + && check.getStat(sdk.stats.MaxHp) >= 40); + } + }, + }, - AutoBuildTemplate: { - 1: { - Update: function () { - Config.AttackSkill = [sdk.skills.WarCry, sdk.skills.Frenzy, -1, sdk.skills.Frenzy, -1]; - Config.LowManaSkill = me.getSkill(sdk.skills.DoubleSwing, sdk.skills.subindex.SoftPoints) >= 9 ? [sdk.skills.DoubleSwing, 0] : [0, -1]; - Config.BeltColumn = ["hp", "hp", "mp", "rv"]; - Config.MPBuffer = 2; - Config.HPBuffer = 2; - } - }, - }, + AutoBuildTemplate: { + 1: { + Update: function () { + Config.AttackSkill = [sdk.skills.WarCry, sdk.skills.Frenzy, -1, sdk.skills.Frenzy, -1]; + Config.LowManaSkill = me.getSkill(sdk.skills.DoubleSwing, sdk.skills.subindex.SoftPoints) >= 9 ? [sdk.skills.DoubleSwing, 0] : [0, -1]; + Config.BeltColumn = ["hp", "hp", "mp", "rv"]; + Config.MPBuffer = 2; + Config.HPBuffer = 2; + } + }, + }, - respec: function () { - if (me.classic) { - return me.charlvl >= 75 && me.diablo; - } else { - return me.haveAll([{ name: sdk.locale.items.Grief }, { name: sdk.locale.items.BreathoftheDying }]); - } - }, + respec: function () { + if (me.classic) { + return me.charlvl >= 75 && me.diablo; + } else { + return me.haveAll([{ name: sdk.locale.items.Grief }, { name: sdk.locale.items.BreathoftheDying }]); + } + }, - active: function () { - return this.respec() && me.getSkill(sdk.skills.Frenzy, sdk.skills.subindex.HardPoints) === 20; - }, - }; - - let finalGear = [ // autoequip final gear - // Final Weapon - Grief & BoTD - "[name] == phaseblade && [flag] == runeword # [ias] >= 30 # [tier] == 100000", - "[name] == colossusblade && [flag] == runeword # [ias] >= 60 && [enhanceddamage] >= 350 # [tier] == 100000", - // Helmet - Arreat's Face - "[name] == slayerguard && [quality] == unique && [flag] != ethereal # [barbarianskills] == 2 # [tier] == tierscore(item, 100000)", - // Belt - Dungo's - "[name] == mithrilcoil && [quality] == unique && [flag] != ethereal # [damageresist] >= 10 && [vitality] >= 30 # [tier] == tierscore(item, 100000)", - // Boots - Gore Rider - "[name] == warboots && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 160 # [tier] == tierscore(item, 100000)", - // Armor - Fortitude - "[type] == armor && [flag] != ethereal && [flag] == runeword # [enhanceddefense] >= 200 && [enhanceddamage] >= 30 # [tier] == 100000", - // Gloves - Laying of Hands - "[name] == bramblemitts && [quality] == set && [flag] != ethereal # [ias] == 20 # [tier] == 100000", - // Amulet - Atma's - "[type] == amulet && [quality] == unique # [poisonresist] == 75 # [tier] == tierscore(item, 100000)", - // Final Rings - Perfect Raven Frost & Bul-Kathos' Wedding Band - "[type] == ring && [quality] == unique # [dexterity] == 20 && [tohit] == 250 # [tier] == 110000", - "[type] == ring && [quality] == unique # [maxstamina] == 50 && [lifeleech] == 5 # [tier] == 110000", - // Rings - Raven Frost && Bul-Kathos' Wedding Band - "[type] == ring && [quality] == unique # [dexterity] >= 15 && [tohit] >= 150 # [tier] == 100000", - "[type] == ring && [quality] == unique # [maxstamina] == 50 && [lifeleech] >= 3 # [tier] == 100000", - // Switch - BO Sticks - "([type] == club || [type] == sword || [type] == knife || [type] == throwingknife || [type] == mace) && ([quality] == magic || [flag] == runeword) && [2handed] == 0 # [itemallskills]+[warcriesskilltab]+[barbarianskills] >= 1 # [secondarytier] == 100000 + secondaryscore(item)", - // Merc Armor - Fortitude - "[type] == armor && [flag] == runeword # [enhanceddefense] >= 200 && [enhanceddamage] >= 300 # [merctier] == 100000", - // Merc Final Helmet - Eth Andy's - "[name] == demonhead && [quality] == unique && [flag] == ethereal # [strength] >= 25 && [enhanceddefense] >= 100 # [merctier] == 50000 + mercscore(item)", - // Merc Helmet - Andy's - "[name] == demonhead && [quality] == unique && [flag] != ethereal # [strength] >= 25 && [enhanceddefense] >= 100 # [merctier] == 40000 + mercscore(item)", - ]; + active: function () { + return this.respec() && me.getSkill(sdk.skills.Frenzy, sdk.skills.subindex.HardPoints) === 20; + }, + }; + + let finalGear = [ // autoequip final gear + // Final Weapon - Grief & BoTD + "[name] == phaseblade && [flag] == runeword # [ias] >= 30 # [tier] == 100000", + "[name] == colossusblade && [flag] == runeword # [ias] >= 60 && [enhanceddamage] >= 350 # [tier] == 100000", + // Helmet - Arreat's Face + "[name] == slayerguard && [quality] == unique && [flag] != ethereal # [barbarianskills] == 2 # [tier] == tierscore(item, 100000)", + // Belt - Dungo's + "[name] == mithrilcoil && [quality] == unique && [flag] != ethereal # [damageresist] >= 10 && [vitality] >= 30 # [tier] == tierscore(item, 100000)", + // Boots - Gore Rider + "[name] == warboots && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 160 # [tier] == tierscore(item, 100000)", + // Armor - Fortitude + "[type] == armor && [flag] != ethereal && [flag] == runeword # [enhanceddefense] >= 200 && [enhanceddamage] >= 30 # [tier] == 100000", + // Gloves - Laying of Hands + "[name] == bramblemitts && [quality] == set && [flag] != ethereal # [ias] == 20 # [tier] == 100000", + // Amulet - Atma's + "[type] == amulet && [quality] == unique # [poisonresist] == 75 # [tier] == tierscore(item, 100000)", + // Final Rings - Perfect Raven Frost & Bul-Kathos' Wedding Band + "[type] == ring && [quality] == unique # [dexterity] == 20 && [tohit] == 250 # [tier] == 110000", + "[type] == ring && [quality] == unique # [maxstamina] == 50 && [lifeleech] == 5 # [tier] == 110000", + // Rings - Raven Frost && Bul-Kathos' Wedding Band + "[type] == ring && [quality] == unique # [dexterity] >= 15 && [tohit] >= 150 # [tier] == 100000", + "[type] == ring && [quality] == unique # [maxstamina] == 50 && [lifeleech] >= 3 # [tier] == 100000", + // Switch - BO Sticks + "([type] == club || [type] == sword || [type] == knife || [type] == throwingknife || [type] == mace) && ([quality] == magic || [flag] == runeword) && [2handed] == 0 # [itemallskills]+[warcriesskilltab]+[barbarianskills] >= 1 # [secondarytier] == 100000 + secondaryscore(item)", + // Merc Armor - Fortitude + "[type] == armor && [flag] == runeword # [enhanceddefense] >= 200 && [enhanceddamage] >= 300 # [merctier] == 100000", + // Merc Final Helmet - Eth Andy's + "[name] == demonhead && [quality] == unique && [flag] == ethereal # [strength] >= 25 && [enhanceddefense] >= 100 # [merctier] == 50000 + mercscore(item)", + // Merc Helmet - Andy's + "[name] == demonhead && [quality] == unique && [flag] != ethereal # [strength] >= 25 && [enhanceddefense] >= 100 # [merctier] == 40000 + mercscore(item)", + ]; - NTIP.buildList(finalGear); - NTIP.buildFinalGear(finalGear); + NTIP.buildList(finalGear); + NTIP.buildFinalGear(finalGear); - return build; - })(); + return build; + })(); })(module); diff --git a/libs/SoloPlay/BuildFiles/barbarian/barbarian.ImmortalwhirlBuild.js b/libs/SoloPlay/BuildFiles/barbarian/barbarian.ImmortalwhirlBuild.js index 98f7ab1a..de76d8f6 100644 --- a/libs/SoloPlay/BuildFiles/barbarian/barbarian.ImmortalwhirlBuild.js +++ b/libs/SoloPlay/BuildFiles/barbarian/barbarian.ImmortalwhirlBuild.js @@ -7,133 +7,133 @@ (function (module) { - module.exports = (function () { - const build = { - caster: false, - skillstab: sdk.skills.tabs.BarbCombat, - wantedskills: [sdk.skills.Bash, sdk.skills.Whirlwind], - usefulskills: [sdk.skills.Howl, sdk.skills.Shout], - precastSkills: [sdk.skills.BattleOrders, sdk.skills.WarCry], // Battle orders, War Cry - wantedMerc: MercData[sdk.skills.Might], - stats: [ - ["strength", 232], ["vitality", "all"] - ], - skills: [ - [sdk.skills.MaceMastery, 20], - [sdk.skills.Whirlwind, 20], - [sdk.skills.Shout, 20], - [sdk.skills.BattleCry, 1], - [sdk.skills.BattleCommand, 1], - [sdk.skills.NaturalResistance, 1], - [sdk.skills.IncreasedSpeed, 1], - [sdk.skills.BattleOrders, 20], - ], + module.exports = (function () { + const build = { + caster: false, + skillstab: sdk.skills.tabs.BarbCombat, + wantedskills: [sdk.skills.Bash, sdk.skills.Whirlwind], + usefulskills: [sdk.skills.Howl, sdk.skills.Shout], + precastSkills: [sdk.skills.BattleOrders, sdk.skills.WarCry], // Battle orders, War Cry + wantedMerc: MercData[sdk.skills.Might], + stats: [ + ["strength", 232], ["vitality", "all"] + ], + skills: [ + [sdk.skills.MaceMastery, 20], + [sdk.skills.Whirlwind, 20], + [sdk.skills.Shout, 20], + [sdk.skills.BattleCry, 1], + [sdk.skills.BattleCommand, 1], + [sdk.skills.NaturalResistance, 1], + [sdk.skills.IncreasedSpeed, 1], + [sdk.skills.BattleOrders, 20], + ], - charms: { - ResLife: { - max: 3, - have: [], - classid: sdk.items.SmallCharm, - stats: function (check) { - return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MaxHp) === 20); - } - }, + charms: { + ResLife: { + max: 3, + have: [], + classid: sdk.items.SmallCharm, + stats: function (check) { + return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MaxHp) === 20); + } + }, - ResMf: { - max: 2, - have: [], - classid: sdk.items.SmallCharm, - stats: function (check) { - return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MagicBonus) === 7); - } - }, + ResMf: { + max: 2, + have: [], + classid: sdk.items.SmallCharm, + stats: function (check) { + return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MagicBonus) === 7); + } + }, - ResFHR: { - max: 1, - have: [], - classid: sdk.items.SmallCharm, - stats: function (check) { - return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.FHR) === 5); - } - }, + ResFHR: { + max: 1, + have: [], + classid: sdk.items.SmallCharm, + stats: function (check) { + return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.FHR) === 5); + } + }, - Skiller: { - max: 2, - have: [], - classid: sdk.items.GrandCharm, - stats: function (check) { - return (!check.unique && check.classid === this.classid && check.getStat(sdk.stats.AddSkillTab, sdk.skills.tabs.Masteries) === 1 - && check.getStat(sdk.stats.MaxHp) >= 40); - } - }, - }, + Skiller: { + max: 2, + have: [], + classid: sdk.items.GrandCharm, + stats: function (check) { + return (!check.unique && check.classid === this.classid && check.getStat(sdk.stats.AddSkillTab, sdk.skills.tabs.Masteries) === 1 + && check.getStat(sdk.stats.MaxHp) >= 40); + } + }, + }, - AutoBuildTemplate: { - 1: { - Update: function () { - Config.AttackSkill = [sdk.skills.BattleCry, sdk.skills.Whirlwind, -1, sdk.skills.Whirlwind, -1]; - Config.LowManaSkill = [0, -1]; - Config.BeltColumn = ["hp", "hp", "mp", "rv"]; - Config.MPBuffer = 2; - Config.HPBuffer = 2; - } - }, - }, + AutoBuildTemplate: { + 1: { + Update: function () { + Config.AttackSkill = [sdk.skills.BattleCry, sdk.skills.Whirlwind, -1, sdk.skills.Whirlwind, -1]; + Config.LowManaSkill = [0, -1]; + Config.BeltColumn = ["hp", "hp", "mp", "rv"]; + Config.MPBuffer = 2; + Config.HPBuffer = 2; + } + }, + }, - respec: function () { - if (me.classic) { - return false; - } else { - return me.haveAll([ - { name: sdk.locale.items.ImmortalKingsMaul, quality: sdk.items.quality.Set }, - { name: sdk.locale.items.ImmortalKingsBoots, quality: sdk.items.quality.Set }, - { name: sdk.locale.items.ImmortalKingsGloves, quality: sdk.items.quality.Set }, - { name: sdk.locale.items.ImmortalKingsBelt, quality: sdk.items.quality.Set }, - { name: sdk.locale.items.ImmortalKingsArmor, quality: sdk.items.quality.Set }, - { name: sdk.locale.items.ImmortalKingsHelmet, quality: sdk.items.quality.Set }, - ]); - } - }, + respec: function () { + if (me.classic) { + return false; + } else { + return me.haveAll([ + { name: sdk.locale.items.ImmortalKingsMaul, quality: sdk.items.quality.Set }, + { name: sdk.locale.items.ImmortalKingsBoots, quality: sdk.items.quality.Set }, + { name: sdk.locale.items.ImmortalKingsGloves, quality: sdk.items.quality.Set }, + { name: sdk.locale.items.ImmortalKingsBelt, quality: sdk.items.quality.Set }, + { name: sdk.locale.items.ImmortalKingsArmor, quality: sdk.items.quality.Set }, + { name: sdk.locale.items.ImmortalKingsHelmet, quality: sdk.items.quality.Set }, + ]); + } + }, - active: function () { - return this.respec() && me.getSkill(sdk.skills.MaceMastery, sdk.skills.subindex.HardPoints) === 20; - }, - }; + active: function () { + return this.respec() && me.getSkill(sdk.skills.MaceMastery, sdk.skills.subindex.HardPoints) === 20; + }, + }; - let finalGear = [ // autoequip final gear - // Weapon - IK Maul - "[name] == ogremaul && [quality] == set # [enhanceddamage] >= 200 && [ias] >= 40 # [tier] == 110000", - // Helmet - IK Helm - "[name] == avengerguard && [quality] == set && [flag] != ethereal # [warcriesskilltab] == 2 # [tier] == 110000", - // Belt - IK Belt - "[name] == warbelt && [quality] == set && [flag] != ethereal # [strength] >= 25 && [fireresist] >= 28 # [tier] == 110000", - // Boots - IK Boots - "[name] == warboots && [quality] == set && [flag] != ethereal # [frw] >= 40 && [tohit] >= 110 # [tier] == 110000", - // Armor - IK Armor - "[name] == sacredarmor && [quality] == set && [flag] != ethereal # [barbcombatskilltab] == 2 # [tier] == 110000", - // Gloves - IK Gauntlets - "[name] == wargauntlets && [quality] == set && [flag] != ethereal # [strength] >= 20 && [dexterity] >= 20 # [tier] == 110000", - // Amulet - Metalgrid - "[type] == amulet && [quality] == unique # [defense] >= 300 # [tier] == tierscore(item, 110000)", - // Final Rings - Perfect Raven Frost & Bul-Kathos' Wedding Band - "[type] == ring && [quality] == unique # [dexterity] == 20 && [tohit] == 250 # [tier] == 110000", - "[type] == ring && [quality] == unique # [maxstamina] == 50 && [lifeleech] == 5 # [tier] == 110000", - // Rings - Raven Frost && Bul-Kathos' Wedding Band - "[type] == ring && [quality] == unique # [dexterity] >= 15 && [tohit] >= 150 # [tier] == 100000", - "[type] == ring && [quality] == unique # [maxstamina] == 50 && [lifeleech] >= 3 # [tier] == 100000", - // Switch - BO sticks - "([type] == club || [type] == sword || [type] == knife || [type] == throwingknife || [type] == mace) && ([quality] == magic || [flag] == runeword) && [2handed] == 0 # [itemallskills]+[warcriesskilltab]+[barbarianskills] >= 1 # [secondarytier] == 100000 + secondaryscore(item)", - // Merc Armor - Fortitude - "[type] == armor && [flag] == runeword # [enhanceddefense] >= 200 && [enhanceddamage] >= 300 # [merctier] == 100000", - // Merc Final Helmet - Eth Andy's - "[name] == demonhead && [quality] == unique && [flag] == ethereal # [strength] >= 25 && [enhanceddefense] >= 100 # [merctier] == 50000 + mercscore(item)", - // Merc Helmet - Andy's - "[name] == demonhead && [quality] == unique && [flag] != ethereal # [strength] >= 25 && [enhanceddefense] >= 100 # [merctier] == 40000 + mercscore(item)", - ]; + let finalGear = [ // autoequip final gear + // Weapon - IK Maul + "[name] == ogremaul && [quality] == set # [enhanceddamage] >= 200 && [ias] >= 40 # [tier] == 110000", + // Helmet - IK Helm + "[name] == avengerguard && [quality] == set && [flag] != ethereal # [warcriesskilltab] == 2 # [tier] == 110000", + // Belt - IK Belt + "[name] == warbelt && [quality] == set && [flag] != ethereal # [strength] >= 25 && [fireresist] >= 28 # [tier] == 110000", + // Boots - IK Boots + "[name] == warboots && [quality] == set && [flag] != ethereal # [frw] >= 40 && [tohit] >= 110 # [tier] == 110000", + // Armor - IK Armor + "[name] == sacredarmor && [quality] == set && [flag] != ethereal # [barbcombatskilltab] == 2 # [tier] == 110000", + // Gloves - IK Gauntlets + "[name] == wargauntlets && [quality] == set && [flag] != ethereal # [strength] >= 20 && [dexterity] >= 20 # [tier] == 110000", + // Amulet - Metalgrid + "[type] == amulet && [quality] == unique # [defense] >= 300 # [tier] == tierscore(item, 110000)", + // Final Rings - Perfect Raven Frost & Bul-Kathos' Wedding Band + "[type] == ring && [quality] == unique # [dexterity] == 20 && [tohit] == 250 # [tier] == 110000", + "[type] == ring && [quality] == unique # [maxstamina] == 50 && [lifeleech] == 5 # [tier] == 110000", + // Rings - Raven Frost && Bul-Kathos' Wedding Band + "[type] == ring && [quality] == unique # [dexterity] >= 15 && [tohit] >= 150 # [tier] == 100000", + "[type] == ring && [quality] == unique # [maxstamina] == 50 && [lifeleech] >= 3 # [tier] == 100000", + // Switch - BO sticks + "([type] == club || [type] == sword || [type] == knife || [type] == throwingknife || [type] == mace) && ([quality] == magic || [flag] == runeword) && [2handed] == 0 # [itemallskills]+[warcriesskilltab]+[barbarianskills] >= 1 # [secondarytier] == 100000 + secondaryscore(item)", + // Merc Armor - Fortitude + "[type] == armor && [flag] == runeword # [enhanceddefense] >= 200 && [enhanceddamage] >= 300 # [merctier] == 100000", + // Merc Final Helmet - Eth Andy's + "[name] == demonhead && [quality] == unique && [flag] == ethereal # [strength] >= 25 && [enhanceddefense] >= 100 # [merctier] == 50000 + mercscore(item)", + // Merc Helmet - Andy's + "[name] == demonhead && [quality] == unique && [flag] != ethereal # [strength] >= 25 && [enhanceddefense] >= 100 # [merctier] == 40000 + mercscore(item)", + ]; - NTIP.buildList(finalGear); - NTIP.buildFinalGear(finalGear); + NTIP.buildList(finalGear); + NTIP.buildFinalGear(finalGear); - return build; - })(); + return build; + })(); })(module); diff --git a/libs/SoloPlay/BuildFiles/barbarian/barbarian.LevelingBuild.js b/libs/SoloPlay/BuildFiles/barbarian/barbarian.LevelingBuild.js index ad490f4c..3c891df9 100644 --- a/libs/SoloPlay/BuildFiles/barbarian/barbarian.LevelingBuild.js +++ b/libs/SoloPlay/BuildFiles/barbarian/barbarian.LevelingBuild.js @@ -8,56 +8,56 @@ (function (module) { - module.exports = (function () { - const build = { - caster: false, - skillstab: sdk.skills.tabs.BarbCombat, - wantedskills: [sdk.skills.BattleOrders, sdk.skills.Frenzy, sdk.skills.DoubleSwing, sdk.skills.SwordMastery], - usefulskills: [sdk.skills.NaturalResistance, sdk.skills.IronSkin, sdk.skills.IncreasedSpeed, sdk.skills.Shout, sdk.skills.FindItem], - wantedMerc: MercData[sdk.skills.Might], - stats: [ - ["dexterity", 136], ["strength", 150], ["vitality", 125], - ["strength", 185], ["vitality", "all"], - ], - skills: [ - // Total points at time of respec 79 - [sdk.skills.SwordMastery, 11, true], // total left 68 - [sdk.skills.FindItem, 1, true], // total left 66 - [sdk.skills.DoubleSwing, 9, true], // total left 56 - [sdk.skills.NaturalResistance, 5, true], // total left 51 - [sdk.skills.Frenzy, 9, true], // total left 42 - [sdk.skills.Berserk, 5, true], // total left 35 - [sdk.skills.WarCry, 5, true], // total left 25 - [sdk.skills.BattleCommand, 1, true], // total left 24 - [sdk.skills.BattleOrders, 8, true], // total left 16 - [sdk.skills.Taunt, 16, true], // total left 0 - // End of respec points, Start of Leveling build - total points left to use 31 - [sdk.skills.Taunt, 20, false], // charlvl 75 -> total left 27 - [sdk.skills.BattleOrders, 10, false], // charlvl 77 -> total left 25 - [sdk.skills.SwordMastery, 20, false], // charlvl 84 -> total left 18 - [sdk.skills.Frenzy, 20, false], // total left 7 - [sdk.skills.BattleOrders, 15, false], // total left 0 - ], + module.exports = (function () { + const build = { + caster: false, + skillstab: sdk.skills.tabs.BarbCombat, + wantedskills: [sdk.skills.BattleOrders, sdk.skills.Frenzy, sdk.skills.DoubleSwing, sdk.skills.SwordMastery], + usefulskills: [sdk.skills.NaturalResistance, sdk.skills.IronSkin, sdk.skills.IncreasedSpeed, sdk.skills.Shout, sdk.skills.FindItem], + wantedMerc: MercData[sdk.skills.Might], + stats: [ + ["dexterity", 136], ["strength", 150], ["vitality", 125], + ["strength", 185], ["vitality", "all"], + ], + skills: [ + // Total points at time of respec 79 + [sdk.skills.SwordMastery, 11, true], // total left 68 + [sdk.skills.FindItem, 1, true], // total left 66 + [sdk.skills.DoubleSwing, 9, true], // total left 56 + [sdk.skills.NaturalResistance, 5, true], // total left 51 + [sdk.skills.Frenzy, 9, true], // total left 42 + [sdk.skills.Berserk, 5, true], // total left 35 + [sdk.skills.WarCry, 5, true], // total left 25 + [sdk.skills.BattleCommand, 1, true], // total left 24 + [sdk.skills.BattleOrders, 8, true], // total left 16 + [sdk.skills.Taunt, 16, true], // total left 0 + // End of respec points, Start of Leveling build - total points left to use 31 + [sdk.skills.Taunt, 20, false], // charlvl 75 -> total left 27 + [sdk.skills.BattleOrders, 10, false], // charlvl 77 -> total left 25 + [sdk.skills.SwordMastery, 20, false], // charlvl 84 -> total left 18 + [sdk.skills.Frenzy, 20, false], // total left 7 + [sdk.skills.BattleOrders, 15, false], // total left 0 + ], - active: function () { - return (me.charlvl > CharInfo.respecOne && me.charlvl > CharInfo.respecTwo && me.getSkill(sdk.skills.WarCry, sdk.skills.subindex.HardPoints) >= 5 && !Check.finalBuild().active()); - }, + active: function () { + return (me.charlvl > CharInfo.respecOne && me.charlvl > CharInfo.respecTwo && me.getSkill(sdk.skills.WarCry, sdk.skills.subindex.HardPoints) >= 5 && !Check.finalBuild().active()); + }, - AutoBuildTemplate: { - 1: { - Update: function () { - Config.AttackSkill = [-1, sdk.skills.Frenzy, sdk.skills.Berserk, sdk.skills.Frenzy, sdk.skills.Berserk]; - Config.LowManaSkill = [sdk.skills.DoubleSwing, 0]; - Config.BeltColumn = ["hp", "hp", "hp", "mp"]; - Config.TownHP = me.hardcore ? 0 : 35; - Config.MPBuffer = me.expansion ? 2 : 4; - Config.HPBuffer = me.expansion && me.charlvl < 80 ? 6 : me.classic ? 5 : 2; - SetUp.belt(); - } - } - }, - }; - - return build; - })(); + AutoBuildTemplate: { + 1: { + Update: function () { + Config.AttackSkill = [-1, sdk.skills.Frenzy, sdk.skills.Berserk, sdk.skills.Frenzy, sdk.skills.Berserk]; + Config.LowManaSkill = [sdk.skills.DoubleSwing, 0]; + Config.BeltColumn = ["hp", "hp", "hp", "mp"]; + Config.TownHP = me.hardcore ? 0 : 35; + Config.MPBuffer = me.expansion ? 2 : 4; + Config.HPBuffer = me.expansion && me.charlvl < 80 ? 6 : me.classic ? 5 : 2; + SetUp.belt(); + } + } + }, + }; + + return build; + })(); })(module); diff --git a/libs/SoloPlay/BuildFiles/barbarian/barbarian.SingerBuild.js b/libs/SoloPlay/BuildFiles/barbarian/barbarian.SingerBuild.js index e074573c..100d012d 100644 --- a/libs/SoloPlay/BuildFiles/barbarian/barbarian.SingerBuild.js +++ b/libs/SoloPlay/BuildFiles/barbarian/barbarian.SingerBuild.js @@ -8,121 +8,121 @@ (function (module) { - module.exports = (function () { - const build = { - caster: true, - skillstab: sdk.skills.tabs.Warcries, - wantedskills: [sdk.skills.WarCry, sdk.skills.Shout], - usefulskills: [sdk.skills.IncreasedSpeed, sdk.skills.NaturalResistance], - precastSkills: [sdk.skills.BattleOrders, sdk.skills.BattleCommand], - wantedMerc: MercData[sdk.skills.Might], - stats: [ - ["dexterity", 35], ["strength", 103], ["vitality", "all"] - ], - skills: [ - [sdk.skills.WarCry, 20, true], - [sdk.skills.NaturalResistance, 4, true], - [sdk.skills.BattleCry, 20, true], - [sdk.skills.BattleCommand, 1, true], - [sdk.skills.BattleOrders, 20, true], - [sdk.skills.Taunt, 20, true], - [sdk.skills.Shout, 11, false], - [sdk.skills.Howl, 15, false], - ], + module.exports = (function () { + const build = { + caster: true, + skillstab: sdk.skills.tabs.Warcries, + wantedskills: [sdk.skills.WarCry, sdk.skills.Shout], + usefulskills: [sdk.skills.IncreasedSpeed, sdk.skills.NaturalResistance], + precastSkills: [sdk.skills.BattleOrders, sdk.skills.BattleCommand], + wantedMerc: MercData[sdk.skills.Might], + stats: [ + ["dexterity", 35], ["strength", 103], ["vitality", "all"] + ], + skills: [ + [sdk.skills.WarCry, 20, true], + [sdk.skills.NaturalResistance, 4, true], + [sdk.skills.BattleCry, 20, true], + [sdk.skills.BattleCommand, 1, true], + [sdk.skills.BattleOrders, 20, true], + [sdk.skills.Taunt, 20, true], + [sdk.skills.Shout, 11, false], + [sdk.skills.Howl, 15, false], + ], - charms: { - ResLife: { - max: 3, - have: [], - classid: sdk.items.SmallCharm, - stats: function (check) { - return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MaxHp) === 20); - } - }, + charms: { + ResLife: { + max: 3, + have: [], + classid: sdk.items.SmallCharm, + stats: function (check) { + return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MaxHp) === 20); + } + }, - ResMf: { - max: 2, - have: [], - classid: sdk.items.SmallCharm, - stats: function (check) { - return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MagicBonus) === 7); - } - }, + ResMf: { + max: 2, + have: [], + classid: sdk.items.SmallCharm, + stats: function (check) { + return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MagicBonus) === 7); + } + }, - ResFHR: { - max: 1, - have: [], - classid: sdk.items.SmallCharm, - stats: function (check) { - return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.FHR) === 5); - } - }, + ResFHR: { + max: 1, + have: [], + classid: sdk.items.SmallCharm, + stats: function (check) { + return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.FHR) === 5); + } + }, - Skiller: { - max: 2, - have: [], - classid: sdk.items.GrandCharm, - stats: function (check) { - return (!check.unique && check.classid === this.classid && check.getStat(sdk.stats.AddSkillTab, sdk.skills.tabs.Warcries) === 1 - && check.getStat(sdk.stats.MaxHp) >= 40); - } - }, - }, + Skiller: { + max: 2, + have: [], + classid: sdk.items.GrandCharm, + stats: function (check) { + return (!check.unique && check.classid === this.classid && check.getStat(sdk.stats.AddSkillTab, sdk.skills.tabs.Warcries) === 1 + && check.getStat(sdk.stats.MaxHp) >= 40); + } + }, + }, - AutoBuildTemplate: { - 1: { - Update: function () { - Config.AttackSkill = [sdk.skills.BattleCry, sdk.skills.WarCry, -1, sdk.skills.WarCry, -1]; - Config.BeltColumn = ["hp", "hp", "mp", "rv"]; - Config.MPBuffer = 4; - Config.HPBuffer = 2; - } - }, - }, + AutoBuildTemplate: { + 1: { + Update: function () { + Config.AttackSkill = [sdk.skills.BattleCry, sdk.skills.WarCry, -1, sdk.skills.WarCry, -1]; + Config.BeltColumn = ["hp", "hp", "mp", "rv"]; + Config.MPBuffer = 4; + Config.HPBuffer = 2; + } + }, + }, - respec: function () { - if (me.classic) { - return me.charlvl >= 75 && me.diablo; - } else { - return me.haveAll([{ name: sdk.locale.items.Enigma }, { name: sdk.locale.items.HeartoftheOak }]); - } - }, + respec: function () { + if (me.classic) { + return me.charlvl >= 75 && me.diablo; + } else { + return me.haveAll([{ name: sdk.locale.items.Enigma }, { name: sdk.locale.items.HeartoftheOak }]); + } + }, - active: function () { - return this.respec() && me.getSkill(sdk.skills.WarCry, sdk.skills.subindex.HardPoints) === 20; - }, - }; + active: function () { + return this.respec() && me.getSkill(sdk.skills.WarCry, sdk.skills.subindex.HardPoints) === 20; + }, + }; - let finalGear = [ // autoequip final gear - // Weapon - HotO x2 dual wield - "[type] == mace && [flag] == runeword # [itemallskills] == 3 # [tier] == 100000", - // Helmet - Harlequin's Crest - "[name] == shako && [quality] == unique && [flag] != ethereal # [damageresist] == 10 # [tier] == tierscore(item, 100000)", - // Belt - Arach's - "[name] == spiderwebsash && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 90 # [tier] == tierscore(item, 100000)", - // Boots - Silkweave - "[name] == meshboots && [quality] == unique && [flag] != ethereal # [frw] >= 30 # [tier] == tierscore(item, 100000)", - // Armor - Enigma - "[type] == armor && [flag] != ethereal && [flag] == runeword # [itemallskills] == 2 # [tier] == 100000", - // Gloves - Frostburns - "[name] == gauntlets && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 10 # [tier] == 100000", - // Amulet - Maras - "[type] == amulet && [quality] == unique # [strength] == 5 && [coldresist] >= 30 # [tier] == tierscore(item, 100000)", - // Rings - SoJ - "[type] == ring && [quality] == unique # [itemmaxmanapercent] == 25 # [tier] == 100000", - // Switch - BO sticks - "([type] == club || [type] == sword || [type] == knife || [type] == throwingknife || [type] == mace) && ([quality] == magic || [flag] == runeword) && [2handed] == 0 # [itemallskills]+[warcriesskilltab]+[barbarianskills] >= 1 # [secondarytier] == 100000 + secondaryscore(item)", - // Merc Armor - Fortitude - "[type] == armor && [flag] == runeword # [enhanceddefense] >= 200 && [enhanceddamage] >= 300 # [merctier] == 100000", - // Merc Final Helmet - Eth Andy's - "[name] == demonhead && [quality] == unique && [flag] == ethereal # [strength] >= 25 && [enhanceddefense] >= 100 # [merctier] == 50000 + mercscore(item)", - // Merc Helmet - Andy's - "[name] == demonhead && [quality] == unique && [flag] != ethereal # [strength] >= 25 && [enhanceddefense] >= 100 # [merctier] == 40000 + mercscore(item)", - ]; + let finalGear = [ // autoequip final gear + // Weapon - HotO x2 dual wield + "[type] == mace && [flag] == runeword # [itemallskills] == 3 # [tier] == 100000", + // Helmet - Harlequin's Crest + "[name] == shako && [quality] == unique && [flag] != ethereal # [damageresist] == 10 # [tier] == tierscore(item, 100000)", + // Belt - Arach's + "[name] == spiderwebsash && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 90 # [tier] == tierscore(item, 100000)", + // Boots - Silkweave + "[name] == meshboots && [quality] == unique && [flag] != ethereal # [frw] >= 30 # [tier] == tierscore(item, 100000)", + // Armor - Enigma + "[type] == armor && [flag] != ethereal && [flag] == runeword # [itemallskills] == 2 # [tier] == 100000", + // Gloves - Frostburns + "[name] == gauntlets && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 10 # [tier] == 100000", + // Amulet - Maras + "[type] == amulet && [quality] == unique # [strength] == 5 && [coldresist] >= 30 # [tier] == tierscore(item, 100000)", + // Rings - SoJ + "[type] == ring && [quality] == unique # [itemmaxmanapercent] == 25 # [tier] == 100000", + // Switch - BO sticks + "([type] == club || [type] == sword || [type] == knife || [type] == throwingknife || [type] == mace) && ([quality] == magic || [flag] == runeword) && [2handed] == 0 # [itemallskills]+[warcriesskilltab]+[barbarianskills] >= 1 # [secondarytier] == 100000 + secondaryscore(item)", + // Merc Armor - Fortitude + "[type] == armor && [flag] == runeword # [enhanceddefense] >= 200 && [enhanceddamage] >= 300 # [merctier] == 100000", + // Merc Final Helmet - Eth Andy's + "[name] == demonhead && [quality] == unique && [flag] == ethereal # [strength] >= 25 && [enhanceddefense] >= 100 # [merctier] == 50000 + mercscore(item)", + // Merc Helmet - Andy's + "[name] == demonhead && [quality] == unique && [flag] != ethereal # [strength] >= 25 && [enhanceddefense] >= 100 # [merctier] == 40000 + mercscore(item)", + ]; - NTIP.buildList(finalGear); - NTIP.buildFinalGear(finalGear); + NTIP.buildList(finalGear); + NTIP.buildFinalGear(finalGear); - return build; - })(); + return build; + })(); })(module); diff --git a/libs/SoloPlay/BuildFiles/barbarian/barbarian.StartBuild.js b/libs/SoloPlay/BuildFiles/barbarian/barbarian.StartBuild.js index 3f711f22..33b59044 100644 --- a/libs/SoloPlay/BuildFiles/barbarian/barbarian.StartBuild.js +++ b/libs/SoloPlay/BuildFiles/barbarian/barbarian.StartBuild.js @@ -8,84 +8,84 @@ (function (module, require) { - module.exports = (function () { - const build = { - AutoBuildTemplate: {}, - caster: false, - skillstab: sdk.skills.tabs.BarbCombat, - wantedskills: [sdk.skills.BattleOrders, sdk.skills.Frenzy, sdk.skills.DoubleSwing, sdk.skills.SwordMastery], - usefulskills: [sdk.skills.NaturalResistance, sdk.skills.IronSkin, sdk.skills.IncreasedSpeed, sdk.skills.Shout], - wantedMerc: MercData[sdk.skills.Might], - stats: [ - ["strength", 35], ["dexterity", 27], ["vitality", 45], - ["strength", 48], ["dexterity", 30], ["vitality", 55], - ["strength", 55], ["dexterity", 39], ["vitality", 65], - ["strength", 60], ["dexterity", 40], ["vitality", 75], - ["strength", 71], ["dexterity", 49], ["vitality", "all"], - ], - skills: [ - [sdk.skills.Bash, 1], // charlevel 2 - [sdk.skills.Howl, 1], // charlevel 3 - [sdk.skills.DoubleSwing, 6, false], // charlevel 9 - [sdk.skills.SwordMastery, 5], // charlevel 13 - [sdk.skills.Taunt, 1], // charlevel 14 - [sdk.skills.SwordMastery, 6], // charlevel 15 - [sdk.skills.IronSkin, 1], // charlevel 18 - [sdk.skills.BattleCry, 1], // charlevel 18 - [sdk.skills.SwordMastery, 9], - [sdk.skills.DoubleThrow, 1], - [sdk.skills.Shout, 1], - [sdk.skills.Taunt, 3, false], - [sdk.skills.Frenzy, 1], - [sdk.skills.BattleOrders, 4, false], - [sdk.skills.Taunt, 20], - ], + module.exports = (function () { + const build = { + AutoBuildTemplate: {}, + caster: false, + skillstab: sdk.skills.tabs.BarbCombat, + wantedskills: [sdk.skills.BattleOrders, sdk.skills.Frenzy, sdk.skills.DoubleSwing, sdk.skills.SwordMastery], + usefulskills: [sdk.skills.NaturalResistance, sdk.skills.IronSkin, sdk.skills.IncreasedSpeed, sdk.skills.Shout], + wantedMerc: MercData[sdk.skills.Might], + stats: [ + ["strength", 35], ["dexterity", 27], ["vitality", 45], + ["strength", 48], ["dexterity", 30], ["vitality", 55], + ["strength", 55], ["dexterity", 39], ["vitality", 65], + ["strength", 60], ["dexterity", 40], ["vitality", 75], + ["strength", 71], ["dexterity", 49], ["vitality", "all"], + ], + skills: [ + [sdk.skills.Bash, 1], // charlevel 2 + [sdk.skills.Howl, 1], // charlevel 3 + [sdk.skills.DoubleSwing, 6, false], // charlevel 9 + [sdk.skills.SwordMastery, 5], // charlevel 13 + [sdk.skills.Taunt, 1], // charlevel 14 + [sdk.skills.SwordMastery, 6], // charlevel 15 + [sdk.skills.IronSkin, 1], // charlevel 18 + [sdk.skills.BattleCry, 1], // charlevel 18 + [sdk.skills.SwordMastery, 9], + [sdk.skills.DoubleThrow, 1], + [sdk.skills.Shout, 1], + [sdk.skills.Taunt, 3, false], + [sdk.skills.Frenzy, 1], + [sdk.skills.BattleOrders, 4, false], + [sdk.skills.Taunt, 20], + ], - active: function () { - return me.charlvl < CharInfo.respecOne && !me.checkSkill(sdk.skills.WarCry, sdk.skills.subindex.HardPoints); - }, - }; + active: function () { + return me.charlvl < CharInfo.respecOne && !me.checkSkill(sdk.skills.WarCry, sdk.skills.subindex.HardPoints); + }, + }; - const { buildAutoBuildTempObj } = require("../../Utils/General"); - - build.AutoBuildTemplate[1] = buildAutoBuildTempObj(() => { - Config.ScanShrines.indexOf(sdk.shrines.Combat) === -1 && Config.ScanShrines.push(sdk.shrines.Combat); - Config.FieldID.Enabled = !Misc.checkQuest(sdk.quest.id.TheSearchForCain, sdk.quest.states.Completed); - Config.FieldID.UsedSpace = 0; + const { buildAutoBuildTempObj } = require("../../Utils/General"); + + build.AutoBuildTemplate[1] = buildAutoBuildTempObj(() => { + Config.ScanShrines.indexOf(sdk.shrines.Combat) === -1 && Config.ScanShrines.push(sdk.shrines.Combat); + Config.FieldID.Enabled = !Misc.checkQuest(sdk.quest.id.TheSearchForCain, sdk.quest.states.Completed); + Config.FieldID.UsedSpace = 0; - Config.TownHP = me.hardcore ? 0 : 35; - Config.BeltColumn = ["hp", "hp", "hp", "hp"]; - Config.MPBuffer = 4; - Config.HPBuffer = 6; - Config.AttackSkill = [-1, 0, 0, 0, 0]; - Config.LowManaSkill = [0, 0]; - SetUp.belt(); - }); - build.AutoBuildTemplate[2] = buildAutoBuildTempObj(() => { - if (me.checkSkill(sdk.skills.Bash, sdk.skills.subindex.HardPoints)) { - Config.AttackSkill = [-1, sdk.skills.Bash, -1, sdk.skills.Attack, -1]; - } - }); - build.AutoBuildTemplate[6] = buildAutoBuildTempObj(() => { - Config.AttackSkill = [-1, sdk.skills.Bash, 0, 0, 0]; - }); - build.AutoBuildTemplate[12] = buildAutoBuildTempObj(() => { - Config.BeltColumn = me.charlvl < 13 ? ["hp", "hp", "hp", "mp"] : ["hp", "hp", "mp", "mp"]; - Config.HPBuffer = me.expansion ? 2 : 4; - Config.MPBuffer = 6; - Config.AttackSkill = [-1, sdk.skills.DoubleSwing, -1, sdk.skills.DoubleSwing, -1]; + Config.TownHP = me.hardcore ? 0 : 35; + Config.BeltColumn = ["hp", "hp", "hp", "hp"]; + Config.MPBuffer = 4; + Config.HPBuffer = 6; + Config.AttackSkill = [-1, 0, 0, 0, 0]; + Config.LowManaSkill = [0, 0]; + SetUp.belt(); + }); + build.AutoBuildTemplate[2] = buildAutoBuildTempObj(() => { + if (me.checkSkill(sdk.skills.Bash, sdk.skills.subindex.HardPoints)) { + Config.AttackSkill = [-1, sdk.skills.Bash, -1, sdk.skills.Attack, -1]; + } + }); + build.AutoBuildTemplate[6] = buildAutoBuildTempObj(() => { + Config.AttackSkill = [-1, sdk.skills.Bash, 0, 0, 0]; + }); + build.AutoBuildTemplate[12] = buildAutoBuildTempObj(() => { + Config.BeltColumn = me.charlvl < 13 ? ["hp", "hp", "hp", "mp"] : ["hp", "hp", "mp", "mp"]; + Config.HPBuffer = me.expansion ? 2 : 4; + Config.MPBuffer = 6; + Config.AttackSkill = [-1, sdk.skills.DoubleSwing, -1, sdk.skills.DoubleSwing, -1]; - if (me.getSkill(sdk.skills.DoubleSwing, sdk.skills.subindex.SoftPoints) >= 9) { - Config.LowManaSkill = [sdk.skills.DoubleSwing, 0]; - } - SetUp.belt(); - }); - build.AutoBuildTemplate[24] = buildAutoBuildTempObj(() => { - if (me.checkSkill(sdk.skills.Frenzy, sdk.skills.subindex.HardPoints)) { - Config.AttackSkill = [-1, sdk.skills.Frenzy, -1, sdk.skills.Frenzy, -1]; - } - }); + if (me.getSkill(sdk.skills.DoubleSwing, sdk.skills.subindex.SoftPoints) >= 9) { + Config.LowManaSkill = [sdk.skills.DoubleSwing, 0]; + } + SetUp.belt(); + }); + build.AutoBuildTemplate[24] = buildAutoBuildTempObj(() => { + if (me.checkSkill(sdk.skills.Frenzy, sdk.skills.subindex.HardPoints)) { + Config.AttackSkill = [-1, sdk.skills.Frenzy, -1, sdk.skills.Frenzy, -1]; + } + }); - return build; - })(); + return build; + })(); })(module, require); diff --git a/libs/SoloPlay/BuildFiles/barbarian/barbarian.SteppingBuild.js b/libs/SoloPlay/BuildFiles/barbarian/barbarian.SteppingBuild.js index 2be47518..c2cbf39a 100644 --- a/libs/SoloPlay/BuildFiles/barbarian/barbarian.SteppingBuild.js +++ b/libs/SoloPlay/BuildFiles/barbarian/barbarian.SteppingBuild.js @@ -8,74 +8,74 @@ (function (module, require) { - module.exports = (function () { - const build = { - AutoBuildTemplate: {}, - caster: false, - skillstab: sdk.skills.tabs.BarbCombat, - wantedskills: [sdk.skills.BattleOrders, sdk.skills.Frenzy, sdk.skills.DoubleSwing, sdk.skills.SwordMastery], - usefulskills: [sdk.skills.NaturalResistance, sdk.skills.IronSkin, sdk.skills.IncreasedSpeed, sdk.skills.Shout, sdk.skills.FindItem], - wantedMerc: MercData[sdk.skills.Might], - stats: [ - ["strength", 71], ["dexterity", 50], ["vitality", 100], - ["strength", 85], ["dexterity", 60], ["vitality", 110], - ["strength", 103], ["dexterity", 79], ["vitality", 125], - ["dexterity", 94], ["strength", 125], ["vitality", 130], - ["strength", 140], ["vitality", 135], ["strength", 150], - ["vitality", "all"], - ], - skills: [ - // Total points at time of respec 33 - [sdk.skills.SwordMastery, 9, true], // total left 24 - [sdk.skills.FindItem, 1, true], // total left 22 - [sdk.skills.BattleOrders, 4, true], // total left 16 - [sdk.skills.BattleCommand, 1, true], // total left 15 - [sdk.skills.NaturalResistance, 1, true], // total left 13 - [sdk.skills.Frenzy, 2, true], // total left 8 - [sdk.skills.WarCry, 1, true], // total left 5 - [sdk.skills.DoubleSwing, 5, true], // total left 1 - // End of respec points, Start of Stepping build - [sdk.skills.NaturalResistance, 2, false], // charlvl 31 - [sdk.skills.WarCry, 2, false], // charlvl 32 - [sdk.skills.NaturalResistance, 3, false], // charlvl 33 - [sdk.skills.WarCry, 3, false], // charlvl 34 - [sdk.skills.Taunt, 11, false], // charlvl 45 - [sdk.skills.NaturalResistance, 4, false], // charlvl 46 - [sdk.skills.Frenzy, 6, false], // charlvl 50 - [sdk.skills.WarCry, 5, false], // charlvl 52 - [sdk.skills.Frenzy, 9, false], // charlvl 53 - [sdk.skills.BattleOrders, 6, false], // charlvl 54 - [sdk.skills.NaturalResistance, 5, false], // charlvl 56 - [sdk.skills.WarCry, 6, false], // charlvl 59 - [sdk.skills.SwordMastery, 20, false], // charlvl 67 - [sdk.skills.Taunt, 20, false], // charlvl 76 - ], + module.exports = (function () { + const build = { + AutoBuildTemplate: {}, + caster: false, + skillstab: sdk.skills.tabs.BarbCombat, + wantedskills: [sdk.skills.BattleOrders, sdk.skills.Frenzy, sdk.skills.DoubleSwing, sdk.skills.SwordMastery], + usefulskills: [sdk.skills.NaturalResistance, sdk.skills.IronSkin, sdk.skills.IncreasedSpeed, sdk.skills.Shout, sdk.skills.FindItem], + wantedMerc: MercData[sdk.skills.Might], + stats: [ + ["strength", 71], ["dexterity", 50], ["vitality", 100], + ["strength", 85], ["dexterity", 60], ["vitality", 110], + ["strength", 103], ["dexterity", 79], ["vitality", 125], + ["dexterity", 94], ["strength", 125], ["vitality", 130], + ["strength", 140], ["vitality", 135], ["strength", 150], + ["vitality", "all"], + ], + skills: [ + // Total points at time of respec 33 + [sdk.skills.SwordMastery, 9, true], // total left 24 + [sdk.skills.FindItem, 1, true], // total left 22 + [sdk.skills.BattleOrders, 4, true], // total left 16 + [sdk.skills.BattleCommand, 1, true], // total left 15 + [sdk.skills.NaturalResistance, 1, true], // total left 13 + [sdk.skills.Frenzy, 2, true], // total left 8 + [sdk.skills.WarCry, 1, true], // total left 5 + [sdk.skills.DoubleSwing, 5, true], // total left 1 + // End of respec points, Start of Stepping build + [sdk.skills.NaturalResistance, 2, false], // charlvl 31 + [sdk.skills.WarCry, 2, false], // charlvl 32 + [sdk.skills.NaturalResistance, 3, false], // charlvl 33 + [sdk.skills.WarCry, 3, false], // charlvl 34 + [sdk.skills.Taunt, 11, false], // charlvl 45 + [sdk.skills.NaturalResistance, 4, false], // charlvl 46 + [sdk.skills.Frenzy, 6, false], // charlvl 50 + [sdk.skills.WarCry, 5, false], // charlvl 52 + [sdk.skills.Frenzy, 9, false], // charlvl 53 + [sdk.skills.BattleOrders, 6, false], // charlvl 54 + [sdk.skills.NaturalResistance, 5, false], // charlvl 56 + [sdk.skills.WarCry, 6, false], // charlvl 59 + [sdk.skills.SwordMastery, 20, false], // charlvl 67 + [sdk.skills.Taunt, 20, false], // charlvl 76 + ], - active: function () { - return me.charlvl > CharInfo.respecOne && me.charlvl < CharInfo.respecTwo && me.checkSkill(sdk.skills.NaturalResistance, sdk.skills.subindex.HardPoints) && !me.checkSkill(sdk.skills.Berserk, sdk.skills.subindex.HardPoints); - }, - }; + active: function () { + return me.charlvl > CharInfo.respecOne && me.charlvl < CharInfo.respecTwo && me.checkSkill(sdk.skills.NaturalResistance, sdk.skills.subindex.HardPoints) && !me.checkSkill(sdk.skills.Berserk, sdk.skills.subindex.HardPoints); + }, + }; - const { buildAutoBuildTempObj } = require("../../Utils/General"); - - build.AutoBuildTemplate[1] = buildAutoBuildTempObj(() => { - Config.AttackSkill = [-1, sdk.skills.DoubleSwing, -1, sdk.skills.DoubleSwing, -1]; - if (me.getSkill(sdk.skills.DoubleSwing, sdk.skills.subindex.SoftPoints) >= 9) { - Config.LowManaSkill = [sdk.skills.DoubleSwing, 0]; - } - SetUp.belt(); - }); - build.AutoBuildTemplate[24] = buildAutoBuildTempObj(() => { - if (me.checkSkill(sdk.skills.Frenzy, sdk.skills.subindex.HardPoints)) { - Config.AttackSkill = [-1, sdk.skills.Frenzy, -1, sdk.skills.Frenzy, -1]; - } - Config.BeltColumn = ["hp", "hp", "hp", "mp"]; - Config.TownHP = me.hardcore ? 0 : 35; - Config.MPBuffer = me.expansion ? 2 : 4; - Config.HPBuffer = me.expansion && me.charlvl < 80 ? 6 : me.classic ? 5 : 2; - SetUp.belt(); - }); + const { buildAutoBuildTempObj } = require("../../Utils/General"); + + build.AutoBuildTemplate[1] = buildAutoBuildTempObj(() => { + Config.AttackSkill = [-1, sdk.skills.DoubleSwing, -1, sdk.skills.DoubleSwing, -1]; + if (me.getSkill(sdk.skills.DoubleSwing, sdk.skills.subindex.SoftPoints) >= 9) { + Config.LowManaSkill = [sdk.skills.DoubleSwing, 0]; + } + SetUp.belt(); + }); + build.AutoBuildTemplate[24] = buildAutoBuildTempObj(() => { + if (me.checkSkill(sdk.skills.Frenzy, sdk.skills.subindex.HardPoints)) { + Config.AttackSkill = [-1, sdk.skills.Frenzy, -1, sdk.skills.Frenzy, -1]; + } + Config.BeltColumn = ["hp", "hp", "hp", "mp"]; + Config.TownHP = me.hardcore ? 0 : 35; + Config.MPBuffer = me.expansion ? 2 : 4; + Config.HPBuffer = me.expansion && me.charlvl < 80 ? 6 : me.classic ? 5 : 2; + SetUp.belt(); + }); - return build; - })(); + return build; + })(); })(module, require); diff --git a/libs/SoloPlay/BuildFiles/barbarian/barbarian.ThrowBuild.js b/libs/SoloPlay/BuildFiles/barbarian/barbarian.ThrowBuild.js index 7663170f..46fe1abd 100644 --- a/libs/SoloPlay/BuildFiles/barbarian/barbarian.ThrowBuild.js +++ b/libs/SoloPlay/BuildFiles/barbarian/barbarian.ThrowBuild.js @@ -7,132 +7,132 @@ (function (module) { - module.exports = (function () { - const build = { - caster: false, - skillstab: sdk.skills.tabs.BarbCombat, - wantedskills: [sdk.skills.BattleOrders, sdk.skills.DoubleThrow, sdk.skills.DoubleSwing], - usefulskills: [sdk.skills.NaturalResistance, sdk.skills.IronSkin, sdk.skills.IncreasedSpeed], - precastSkills: [sdk.skills.BattleOrders, sdk.skills.BattleCommand], - wantedMerc: MercData[sdk.skills.HolyFreeze], - stats: [ - ["strength", 103], ["dexterity", 79], ["vitality", 90], - ["dexterity", 136], ["strength", 150], ["vitality", "all"], - ], - skills: [ - [sdk.skills.DoubleThrow, 20], - [sdk.skills.Howl, 9], - [sdk.skills.BattleOrders, 20], - [sdk.skills.BattleCommand, 1], - [sdk.skills.ThrowMastery, 20], - [sdk.skills.NaturalResistance, 5], - [sdk.skills.DoubleSwing, 20], - [sdk.skills.Frenzy, 1], - [sdk.skills.Berserk, 1], - [sdk.skills.Howl, 20], - ], + module.exports = (function () { + const build = { + caster: false, + skillstab: sdk.skills.tabs.BarbCombat, + wantedskills: [sdk.skills.BattleOrders, sdk.skills.DoubleThrow, sdk.skills.DoubleSwing], + usefulskills: [sdk.skills.NaturalResistance, sdk.skills.IronSkin, sdk.skills.IncreasedSpeed], + precastSkills: [sdk.skills.BattleOrders, sdk.skills.BattleCommand], + wantedMerc: MercData[sdk.skills.HolyFreeze], + stats: [ + ["strength", 103], ["dexterity", 79], ["vitality", 90], + ["dexterity", 136], ["strength", 150], ["vitality", "all"], + ], + skills: [ + [sdk.skills.DoubleThrow, 20], + [sdk.skills.Howl, 9], + [sdk.skills.BattleOrders, 20], + [sdk.skills.BattleCommand, 1], + [sdk.skills.ThrowMastery, 20], + [sdk.skills.NaturalResistance, 5], + [sdk.skills.DoubleSwing, 20], + [sdk.skills.Frenzy, 1], + [sdk.skills.Berserk, 1], + [sdk.skills.Howl, 20], + ], - charms: { - ResLife: { - max: 4, - have: [], - classid: sdk.items.SmallCharm, - stats: function (check) { - return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MaxHp) === 20); - } - }, + charms: { + ResLife: { + max: 4, + have: [], + classid: sdk.items.SmallCharm, + stats: function (check) { + return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MaxHp) === 20); + } + }, - ResFHR: { - max: 4, - have: [], - classid: sdk.items.SmallCharm, - stats: function (check) { - return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.FHR) === 5); - } - }, + ResFHR: { + max: 4, + have: [], + classid: sdk.items.SmallCharm, + stats: function (check) { + return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.FHR) === 5); + } + }, - SkillerCombat: { - max: 1, - have: [], - classid: sdk.items.GrandCharm, - stats: function (check) { - return (!check.unique && check.classid === this.classid && check.getStat(sdk.stats.AddSkillTab, sdk.skills.tabs.BarbCombat) === 1 - && check.getStat(sdk.stats.MaxHp) >= 40); - } - }, + SkillerCombat: { + max: 1, + have: [], + classid: sdk.items.GrandCharm, + stats: function (check) { + return (!check.unique && check.classid === this.classid && check.getStat(sdk.stats.AddSkillTab, sdk.skills.tabs.BarbCombat) === 1 + && check.getStat(sdk.stats.MaxHp) >= 40); + } + }, - SkillerMasteries: { - max: 1, - have: [], - classid: sdk.items.GrandCharm, - stats: function (check) { - return (!check.unique && check.classid === this.classid && check.getStat(sdk.stats.AddSkillTab, sdk.skills.tabs.Masteries) === 1 - && check.getStat(sdk.stats.MaxHp) >= 40); - } - }, - }, + SkillerMasteries: { + max: 1, + have: [], + classid: sdk.items.GrandCharm, + stats: function (check) { + return (!check.unique && check.classid === this.classid && check.getStat(sdk.stats.AddSkillTab, sdk.skills.tabs.Masteries) === 1 + && check.getStat(sdk.stats.MaxHp) >= 40); + } + }, + }, - AutoBuildTemplate: { - 1: { - Update: function () { - Config.AttackSkill = [-1, sdk.skills.DoubleThrow, sdk.skills.Frenzy, sdk.skills.DoubleThrow, sdk.skills.Berserk]; - Config.LowManaSkill = [sdk.skills.DoubleSwing, sdk.skills.DoubleSwing]; - Config.BeltColumn = ["hp", "hp", "mp", "rv"]; - } - }, - }, + AutoBuildTemplate: { + 1: { + Update: function () { + Config.AttackSkill = [-1, sdk.skills.DoubleThrow, sdk.skills.Frenzy, sdk.skills.DoubleThrow, sdk.skills.Berserk]; + Config.LowManaSkill = [sdk.skills.DoubleSwing, sdk.skills.DoubleSwing]; + Config.BeltColumn = ["hp", "hp", "mp", "rv"]; + } + }, + }, - respec: function () { - if (me.classic) { - return false; - } else { - return Check.haveItem("throwingknife", "unique", "Warshrike") && Check.haveItem("throwingaxe", "unique", "Lacerator"); - } - }, + respec: function () { + if (me.classic) { + return false; + } else { + return Check.haveItem("throwingknife", "unique", "Warshrike") && Check.haveItem("throwingaxe", "unique", "Lacerator"); + } + }, - active: function () { - return this.respec() && me.getSkill(sdk.skills.DoubleThrow, sdk.skills.subindex.HardPoints) === 20; - }, - }; - - let finalGear = [ // autoequip final gear - // Final Weapon - Perfect Eth Lacerator & Warshrike - "[name] == wingedknife && [quality] == unique && [flag] == ethereal # [ias] == 30 && [enhanceddamage] == 250 # [tier] == 110000", - "[name] == wingedaxe && [quality] == unique && [flag] == ethereal # [ias] == 30 && [enhanceddamage] == 210 # [tier] == 110000", - // Weapon - Eth Lacerator & Warshrike - "[name] == wingedknife && [quality] == unique && [flag] == ethereal # [ias] == 30 && [enhanceddamage] >= 200 # [tier] == 100000", - "[name] == wingedaxe && [quality] == unique && [flag] == ethereal # [ias] == 30 && [enhanceddamage] >= 150 # [tier] == 100000", - // Helmet - Arreat's Face - "[name] == slayerguard && [quality] == unique && [flag] != ethereal # [barbarianskills] == 2 # [tier] == 100000", - // Belt- Arach's - "[name] == spiderwebsash && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 90 # [tier] == 100000", - // Boots - IK Boots - "[name] == warboots && [quality] == set && [flag] != ethereal # [frw] >= 40 && [tohit] >= 110 # [tier] == 110000", - // Armor - Enigma - "[type] == armor && [flag] != ethereal && [flag] == runeword # [itemallskills] == 2 # [tier] == 100000", - // Gloves - Laying of Hands - "[name] == bramblemitts && [quality] == set && [flag] != ethereal # [ias] == 20 # [tier] == 100000", - // Amulet - Highlords - "[type] == amulet && [quality] == unique # [lightresist] == 35 # [tier] == 100000", - // Final Rings - Perfect Raven Frost & Bul-Kathos' Wedding Band - "[type] == ring && [quality] == unique # [dexterity] == 20 && [tohit] == 250 # [tier] == 110000", - "[type] == ring && [quality] == unique # [maxstamina] == 50 && [lifeleech] == 5 # [tier] == 110000", - // Rings - Raven Frost && Bul-Kathos' Wedding Band - "[type] == ring && [quality] == unique # [dexterity] >= 15 && [tohit] >= 150 # [tier] == 100000", - "[type] == ring && [quality] == unique # [maxstamina] == 50 && [lifeleech] >= 3 # [tier] == 100000", - // Switch - BO sticks - "([type] == club || [type] == sword || [type] == knife || [type] == throwingknife || [type] == mace) && ([quality] == magic || [flag] == runeword) && [2handed] == 0 # [itemallskills]+[warcriesskilltab]+[barbarianskills] >= 1 # [secondarytier] == 100000 + secondaryscore(item)", - // Merc Armor - Fortitude - "[type] == armor && [flag] == runeword # [enhanceddefense] >= 200 && [enhanceddamage] >= 300 # [merctier] == 100000", - // Merc Final Helmet - Eth Andy's - "[name] == demonhead && [quality] == unique && [flag] == ethereal # [strength] >= 25 && [enhanceddefense] >= 100 # [merctier] == 50000 + mercscore(item)", - // Merc Helmet - Andy's - "[name] == demonhead && [quality] == unique && [flag] != ethereal # [strength] >= 25 && [enhanceddefense] >= 100 # [merctier] == 40000 + mercscore(item)", - ]; + active: function () { + return this.respec() && me.getSkill(sdk.skills.DoubleThrow, sdk.skills.subindex.HardPoints) === 20; + }, + }; + + let finalGear = [ // autoequip final gear + // Final Weapon - Perfect Eth Lacerator & Warshrike + "[name] == wingedknife && [quality] == unique && [flag] == ethereal # [ias] == 30 && [enhanceddamage] == 250 # [tier] == 110000", + "[name] == wingedaxe && [quality] == unique && [flag] == ethereal # [ias] == 30 && [enhanceddamage] == 210 # [tier] == 110000", + // Weapon - Eth Lacerator & Warshrike + "[name] == wingedknife && [quality] == unique && [flag] == ethereal # [ias] == 30 && [enhanceddamage] >= 200 # [tier] == 100000", + "[name] == wingedaxe && [quality] == unique && [flag] == ethereal # [ias] == 30 && [enhanceddamage] >= 150 # [tier] == 100000", + // Helmet - Arreat's Face + "[name] == slayerguard && [quality] == unique && [flag] != ethereal # [barbarianskills] == 2 # [tier] == 100000", + // Belt- Arach's + "[name] == spiderwebsash && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 90 # [tier] == 100000", + // Boots - IK Boots + "[name] == warboots && [quality] == set && [flag] != ethereal # [frw] >= 40 && [tohit] >= 110 # [tier] == 110000", + // Armor - Enigma + "[type] == armor && [flag] != ethereal && [flag] == runeword # [itemallskills] == 2 # [tier] == 100000", + // Gloves - Laying of Hands + "[name] == bramblemitts && [quality] == set && [flag] != ethereal # [ias] == 20 # [tier] == 100000", + // Amulet - Highlords + "[type] == amulet && [quality] == unique # [lightresist] == 35 # [tier] == 100000", + // Final Rings - Perfect Raven Frost & Bul-Kathos' Wedding Band + "[type] == ring && [quality] == unique # [dexterity] == 20 && [tohit] == 250 # [tier] == 110000", + "[type] == ring && [quality] == unique # [maxstamina] == 50 && [lifeleech] == 5 # [tier] == 110000", + // Rings - Raven Frost && Bul-Kathos' Wedding Band + "[type] == ring && [quality] == unique # [dexterity] >= 15 && [tohit] >= 150 # [tier] == 100000", + "[type] == ring && [quality] == unique # [maxstamina] == 50 && [lifeleech] >= 3 # [tier] == 100000", + // Switch - BO sticks + "([type] == club || [type] == sword || [type] == knife || [type] == throwingknife || [type] == mace) && ([quality] == magic || [flag] == runeword) && [2handed] == 0 # [itemallskills]+[warcriesskilltab]+[barbarianskills] >= 1 # [secondarytier] == 100000 + secondaryscore(item)", + // Merc Armor - Fortitude + "[type] == armor && [flag] == runeword # [enhanceddefense] >= 200 && [enhanceddamage] >= 300 # [merctier] == 100000", + // Merc Final Helmet - Eth Andy's + "[name] == demonhead && [quality] == unique && [flag] == ethereal # [strength] >= 25 && [enhanceddefense] >= 100 # [merctier] == 50000 + mercscore(item)", + // Merc Helmet - Andy's + "[name] == demonhead && [quality] == unique && [flag] != ethereal # [strength] >= 25 && [enhanceddefense] >= 100 # [merctier] == 40000 + mercscore(item)", + ]; - NTIP.buildList(finalGear); - NTIP.buildFinalGear(finalGear); + NTIP.buildList(finalGear); + NTIP.buildFinalGear(finalGear); - return build; - })(); + return build; + })(); })(module); diff --git a/libs/SoloPlay/BuildFiles/barbarian/barbarian.UberconcBuild.js b/libs/SoloPlay/BuildFiles/barbarian/barbarian.UberconcBuild.js index 5f430c6c..22876901 100644 --- a/libs/SoloPlay/BuildFiles/barbarian/barbarian.UberconcBuild.js +++ b/libs/SoloPlay/BuildFiles/barbarian/barbarian.UberconcBuild.js @@ -7,118 +7,118 @@ (function (module, require) { - module.exports = (function () { - const build = { - caster: false, - skillstab: sdk.skills.tabs.BarbCombat, - wantedskills: [sdk.skills.BattleOrders, sdk.skills.Concentrate], - usefulskills: [sdk.skills.SwordMastery, sdk.skills.Bash], - precastSkills: [sdk.skills.BattleOrders, sdk.skills.BattleCommand], - wantedMerc: MercData[sdk.skills.Might], - stats: [ - ["strength", 196], ["dexterity", "block"], ["vitality", "all"], - ], - skills: [ - [sdk.skills.BattleCommand, 1], - [sdk.skills.NaturalResistance, 1], - [sdk.skills.WarCry, 1], - [sdk.skills.Berserk, 1], - [sdk.skills.BattleOrders, 20, false], - [sdk.skills.SwordMastery, 10, false], - [sdk.skills.Concentrate, 20, false], - [sdk.skills.Shout, 20, false], - [sdk.skills.LeapAttack, 1, false], - [sdk.skills.Bash, 20, false], - [sdk.skills.SwordMastery, 20], - ], + module.exports = (function () { + const build = { + caster: false, + skillstab: sdk.skills.tabs.BarbCombat, + wantedskills: [sdk.skills.BattleOrders, sdk.skills.Concentrate], + usefulskills: [sdk.skills.SwordMastery, sdk.skills.Bash], + precastSkills: [sdk.skills.BattleOrders, sdk.skills.BattleCommand], + wantedMerc: MercData[sdk.skills.Might], + stats: [ + ["strength", 196], ["dexterity", "block"], ["vitality", "all"], + ], + skills: [ + [sdk.skills.BattleCommand, 1], + [sdk.skills.NaturalResistance, 1], + [sdk.skills.WarCry, 1], + [sdk.skills.Berserk, 1], + [sdk.skills.BattleOrders, 20, false], + [sdk.skills.SwordMastery, 10, false], + [sdk.skills.Concentrate, 20, false], + [sdk.skills.Shout, 20, false], + [sdk.skills.LeapAttack, 1, false], + [sdk.skills.Bash, 20, false], + [sdk.skills.SwordMastery, 20], + ], - charms: { - ResLife: { - max: 4, - have: [], - classid: sdk.items.SmallCharm, - stats: function (check) { - return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MaxHp) === 20); - } - }, + charms: { + ResLife: { + max: 4, + have: [], + classid: sdk.items.SmallCharm, + stats: function (check) { + return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MaxHp) === 20); + } + }, - ResFHR: { - max: 4, - have: [], - classid: sdk.items.SmallCharm, - stats: function (check) { - return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.FHR) === 5); - } - }, + ResFHR: { + max: 4, + have: [], + classid: sdk.items.SmallCharm, + stats: function (check) { + return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.FHR) === 5); + } + }, - Skiller: { - max: 2, - have: [], - classid: sdk.items.GrandCharm, - stats: function (check) { - return (!check.unique && check.classid === this.classid && check.getStat(sdk.stats.AddSkillTab, sdk.skills.tabs.BarbCombat) === 1 - && check.getStat(sdk.stats.MaxHp) >= 40); - } - }, - }, + Skiller: { + max: 2, + have: [], + classid: sdk.items.GrandCharm, + stats: function (check) { + return (!check.unique && check.classid === this.classid && check.getStat(sdk.stats.AddSkillTab, sdk.skills.tabs.BarbCombat) === 1 + && check.getStat(sdk.stats.MaxHp) >= 40); + } + }, + }, - AutoBuildTemplate: { - 1: { - Update: function () { - Config.AttackSkill = [-1, sdk.skills.Concentrate, sdk.skills.Berserk, sdk.skills.Concentrate, sdk.skills.Berserk]; - Config.LowManaSkill = [0, 0]; - Config.BeltColumn = ["hp", "hp", "mp", "rv"]; - } - }, - }, + AutoBuildTemplate: { + 1: { + Update: function () { + Config.AttackSkill = [-1, sdk.skills.Concentrate, sdk.skills.Berserk, sdk.skills.Concentrate, sdk.skills.Berserk]; + Config.LowManaSkill = [0, 0]; + Config.BeltColumn = ["hp", "hp", "mp", "rv"]; + } + }, + }, - respec: function () { - if (me.classic) { - return me.charlvl >= 75 && me.diablo; - } else { - return me.checkItem({ name: sdk.locale.items.Grief, itemtype: sdk.items.type.Sword }).have && Check.haveItem("monarch", "unique", "Stormshield"); - } - }, + respec: function () { + if (me.classic) { + return me.charlvl >= 75 && me.diablo; + } else { + return me.checkItem({ name: sdk.locale.items.Grief, itemtype: sdk.items.type.Sword }).have && Check.haveItem("monarch", "unique", "Stormshield"); + } + }, - active: function () { - return this.respec() && me.getSkill(sdk.skills.Concentrate, sdk.skills.subindex.HardPoints) >= 5; - }, - }; + active: function () { + return this.respec() && me.getSkill(sdk.skills.Concentrate, sdk.skills.subindex.HardPoints) >= 5; + }, + }; - let finalGear = [ // autoequip final gear - // Weapon - Grief - "[type] == sword && [flag] == runeword # [ias] >= 30 && [itemdeadlystrike] == 20 && [passivepoispierce] >= 20 # [tier] == 100000", - // Helmet - Arreat's Face - "[name] == slayerguard && [quality] == unique && [flag] != ethereal # [barbarianskills] == 2 # [tier] == tierscore(item, 100000)", - // Belt - Dungo's - "[name] == mithrilcoil && [quality] == unique && [flag] != ethereal # [damageresist] >= 10 && [vitality] >= 30 # [tier] == tierscore(item, 100000)", - // Boots - Gore Rider's - "[name] == warboots && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 160 # [tier] == tierscore(item, 100000)", - // Armor - CoH - "[type] == armor && [flag] == runeword && [flag] != ethereal # [fireresist] == 65 && [hpregen] == 7 # [tier] == 100000", - // Gloves - Drac's - "[name] == vampirebonegloves && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 100 && [strength] >= 12 && [lifeleech] >= 9 # [tier] == tierscore(item, 100000)", - // Amulet - Highlords - "[type] == amulet && [quality] == unique # [lightresist] == 35 # [tier] == 100000", - // Final Rings - Perfect Raven Frost & Bul-Kathos' Wedding Band - "[type] == ring && [quality] == unique # [dexterity] == 20 && [tohit] == 250 # [tier] == 110000", - "[name] == ring && [quality] == unique # [maxstamina] == 50 && [lifeleech] == 5 # [tier] == 110000", - // Rings - Raven Frost && Bul-Kathos' Wedding Band - "[type] == ring && [quality] == unique # [dexterity] >= 15 && [tohit] >= 150 # [tier] == 100000", - "[name] == ring && [quality] == unique # [maxstamina] == 50 && [lifeleech] >= 3 # [tier] == 100000", - // Switch - BO sticks - "([type] == club || [type] == sword || [type] == knife || [type] == throwingknife || [type] == mace) && ([quality] == magic || [flag] == runeword) && [2handed] == 0 # [itemallskills]+[warcriesskilltab]+[barbarianskills] >= 1 # [secondarytier] == 100000 + secondaryscore(item)", - // Merc Armor - Fortitude - "[type] == armor && [flag] == runeword # [enhanceddefense] >= 200 && [enhanceddamage] >= 300 # [merctier] == 100000", - // Merc Final Helmet - Eth Andy's - "[name] == demonhead && [quality] == unique && [flag] == ethereal # [strength] >= 25 && [enhanceddefense] >= 100 # [merctier] == 50000 + mercscore(item)", - // Merc Helmet - Andy's - "[name] == demonhead && [quality] == unique && [flag] != ethereal # [strength] >= 25 && [enhanceddefense] >= 100 # [merctier] == 40000 + mercscore(item)", - ]; + let finalGear = [ // autoequip final gear + // Weapon - Grief + "[type] == sword && [flag] == runeword # [ias] >= 30 && [itemdeadlystrike] == 20 && [passivepoispierce] >= 20 # [tier] == 100000", + // Helmet - Arreat's Face + "[name] == slayerguard && [quality] == unique && [flag] != ethereal # [barbarianskills] == 2 # [tier] == tierscore(item, 100000)", + // Belt - Dungo's + "[name] == mithrilcoil && [quality] == unique && [flag] != ethereal # [damageresist] >= 10 && [vitality] >= 30 # [tier] == tierscore(item, 100000)", + // Boots - Gore Rider's + "[name] == warboots && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 160 # [tier] == tierscore(item, 100000)", + // Armor - CoH + "[type] == armor && [flag] == runeword && [flag] != ethereal # [fireresist] == 65 && [hpregen] == 7 # [tier] == 100000", + // Gloves - Drac's + "[name] == vampirebonegloves && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 100 && [strength] >= 12 && [lifeleech] >= 9 # [tier] == tierscore(item, 100000)", + // Amulet - Highlords + "[type] == amulet && [quality] == unique # [lightresist] == 35 # [tier] == 100000", + // Final Rings - Perfect Raven Frost & Bul-Kathos' Wedding Band + "[type] == ring && [quality] == unique # [dexterity] == 20 && [tohit] == 250 # [tier] == 110000", + "[name] == ring && [quality] == unique # [maxstamina] == 50 && [lifeleech] == 5 # [tier] == 110000", + // Rings - Raven Frost && Bul-Kathos' Wedding Band + "[type] == ring && [quality] == unique # [dexterity] >= 15 && [tohit] >= 150 # [tier] == 100000", + "[name] == ring && [quality] == unique # [maxstamina] == 50 && [lifeleech] >= 3 # [tier] == 100000", + // Switch - BO sticks + "([type] == club || [type] == sword || [type] == knife || [type] == throwingknife || [type] == mace) && ([quality] == magic || [flag] == runeword) && [2handed] == 0 # [itemallskills]+[warcriesskilltab]+[barbarianskills] >= 1 # [secondarytier] == 100000 + secondaryscore(item)", + // Merc Armor - Fortitude + "[type] == armor && [flag] == runeword # [enhanceddefense] >= 200 && [enhanceddamage] >= 300 # [merctier] == 100000", + // Merc Final Helmet - Eth Andy's + "[name] == demonhead && [quality] == unique && [flag] == ethereal # [strength] >= 25 && [enhanceddefense] >= 100 # [merctier] == 50000 + mercscore(item)", + // Merc Helmet - Andy's + "[name] == demonhead && [quality] == unique && [flag] != ethereal # [strength] >= 25 && [enhanceddefense] >= 100 # [merctier] == 40000 + mercscore(item)", + ]; - NTIP.buildList(finalGear); - NTIP.buildFinalGear(finalGear); + NTIP.buildList(finalGear); + NTIP.buildFinalGear(finalGear); - return build; - })(); + return build; + })(); })(module, require); diff --git a/libs/SoloPlay/BuildFiles/barbarian/barbarian.WhirlwindBuild.js b/libs/SoloPlay/BuildFiles/barbarian/barbarian.WhirlwindBuild.js index 46d52db6..9b4cd6e8 100644 --- a/libs/SoloPlay/BuildFiles/barbarian/barbarian.WhirlwindBuild.js +++ b/libs/SoloPlay/BuildFiles/barbarian/barbarian.WhirlwindBuild.js @@ -7,131 +7,131 @@ (function (module) { - module.exports = (function () { - const build = { - caster: false, - skillstab: sdk.skills.tabs.BarbCombat, - wantedskills: [sdk.skills.Bash, sdk.skills.Whirlwind], - usefulskills: [sdk.skills.Howl, sdk.skills.Shout], - precastSkills: [sdk.skills.BattleOrders, sdk.skills.BattleCommand], - wantedMerc: MercData[sdk.skills.Might], - stats: [ - ["strength", 118], ["dexterity", 136], ["vitality", "all"] - ], - skills: [ - [sdk.skills.Whirlwind, 20, true], - [sdk.skills.SwordMastery, 20, true], - [sdk.skills.NaturalResistance, 5, true], - [sdk.skills.BattleCommand, 1, true], - [sdk.skills.Berserk, 5, true], - [sdk.skills.IncreasedSpeed, 1, true], - [sdk.skills.WarCry, 5, true], - [sdk.skills.BattleOrders, 20, true], - [sdk.skills.Shout, 20, true], - [sdk.skills.IronSkin, 3, true], - ], + module.exports = (function () { + const build = { + caster: false, + skillstab: sdk.skills.tabs.BarbCombat, + wantedskills: [sdk.skills.Bash, sdk.skills.Whirlwind], + usefulskills: [sdk.skills.Howl, sdk.skills.Shout], + precastSkills: [sdk.skills.BattleOrders, sdk.skills.BattleCommand], + wantedMerc: MercData[sdk.skills.Might], + stats: [ + ["strength", 118], ["dexterity", 136], ["vitality", "all"] + ], + skills: [ + [sdk.skills.Whirlwind, 20, true], + [sdk.skills.SwordMastery, 20, true], + [sdk.skills.NaturalResistance, 5, true], + [sdk.skills.BattleCommand, 1, true], + [sdk.skills.Berserk, 5, true], + [sdk.skills.IncreasedSpeed, 1, true], + [sdk.skills.WarCry, 5, true], + [sdk.skills.BattleOrders, 20, true], + [sdk.skills.Shout, 20, true], + [sdk.skills.IronSkin, 3, true], + ], - charms: { - ResLife: { - max: 4, - have: [], - classid: sdk.items.SmallCharm, - stats: function (check) { - return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MaxHp) === 20); - } - }, + charms: { + ResLife: { + max: 4, + have: [], + classid: sdk.items.SmallCharm, + stats: function (check) { + return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MaxHp) === 20); + } + }, - ResMf: { - max: 2, - have: [], - classid: sdk.items.SmallCharm, - stats: function (check) { - return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MagicBonus) === 7); - } - }, + ResMf: { + max: 2, + have: [], + classid: sdk.items.SmallCharm, + stats: function (check) { + return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MagicBonus) === 7); + } + }, - ResFHR: { - max: 2, - have: [], - classid: sdk.items.SmallCharm, - stats: function (check) { - return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.FHR) === 5); - } - }, + ResFHR: { + max: 2, + have: [], + classid: sdk.items.SmallCharm, + stats: function (check) { + return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.FHR) === 5); + } + }, - Skiller: { - max: 2, - have: [], - classid: sdk.items.GrandCharm, - stats: function (check) { - return (!check.unique && check.classid === this.classid && check.getStat(sdk.stats.AddSkillTab, sdk.skills.tabs.Masteries) === 1 - && check.getStat(sdk.stats.MaxHp) >= 40); - } - }, - }, + Skiller: { + max: 2, + have: [], + classid: sdk.items.GrandCharm, + stats: function (check) { + return (!check.unique && check.classid === this.classid && check.getStat(sdk.stats.AddSkillTab, sdk.skills.tabs.Masteries) === 1 + && check.getStat(sdk.stats.MaxHp) >= 40); + } + }, + }, - AutoBuildTemplate: { - 1: { - Update: function () { - Config.AttackSkill = [sdk.skills.BattleCry, sdk.skills.Whirlwind, -1, sdk.skills.Whirlwind, -1]; - Config.LowManaSkill = [0, -1]; - Config.BeltColumn = ["hp", "hp", "mp", "rv"]; - Config.MPBuffer = 2; - Config.HPBuffer = 2; - } - }, - }, + AutoBuildTemplate: { + 1: { + Update: function () { + Config.AttackSkill = [sdk.skills.BattleCry, sdk.skills.Whirlwind, -1, sdk.skills.Whirlwind, -1]; + Config.LowManaSkill = [0, -1]; + Config.BeltColumn = ["hp", "hp", "mp", "rv"]; + Config.MPBuffer = 2; + Config.HPBuffer = 2; + } + }, + }, - respec: function () { - if (me.classic) { - return me.charlvl >= 75 && me.diablo; - } else { - // TODO: figure out how to make sure we have two, or determine if that even matters - return me.checkItem({ name: sdk.locale.items.Grief, itemtype: sdk.items.type.Sword }).have; - } - }, + respec: function () { + if (me.classic) { + return me.charlvl >= 75 && me.diablo; + } else { + // TODO: figure out how to make sure we have two, or determine if that even matters + return me.checkItem({ name: sdk.locale.items.Grief, itemtype: sdk.items.type.Sword }).have; + } + }, - active: function () { - return this.respec() && me.getSkill(sdk.skills.Whirlwind, sdk.skills.subindex.HardPoints) === 20; - }, - }; + active: function () { + return this.respec() && me.getSkill(sdk.skills.Whirlwind, sdk.skills.subindex.HardPoints) === 20; + }, + }; - let finalGear = [ // autoequip final gear - // Weapon - Grief x2 dual wield - "[type] == sword && [flag] == runeword # [ias] >= 30 && [itemdeadlystrike] == 20 && [passivepoispierce] >= 20 # [tier] == 100000", - // Final Helmet - Upp'ed Arreat's Face - "[name] == guardiancrown && [quality] == unique && [flag] != ethereal # [barbarianskills] == 2 && [fhr] >= 30 # [tier] == tierscore(item, 150000)", - // Helmet - Arreat's Face - "[name] == slayerguard && [quality] == unique && [flag] != ethereal # [barbarianskills] == 2 && [fhr] >= 30 # [tier] == tierscore(item, 100000)", - // Belt - Dungo's - "[name] == mithrilcoil && [quality] == unique && [flag] != ethereal # [damageresist] >= 10 && [vitality] >= 30 # [tier] == tierscore(item, 100000)", - // Boots - Gore Rider - "[name] == warboots && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 160 # [tier] == tierscore(item, 100000)", - // Armor - Fortitude - "[type] == armor && [flag] != ethereal && [flag] == runeword # [enhanceddefense] >= 200 && [enhanceddamage] >= 30 # [tier] == 100000", - // Gloves - Laying of Hands - "[name] == bramblemitts && [quality] == set && [flag] != ethereal # [ias] == 20 # [tier] == 100000", - // Amulet - Atma's - "[type] == amulet && [quality] == unique # [poisonresist] == 75 # [tier] == tierscore(item, 100000)", - // Final Rings - Perfect Raven Frost & Bul-Kathos' Wedding Band - "[type] == ring && [quality] == unique # [dexterity] == 20 && [tohit] == 250 # [tier] == 110000", - "[type] == ring && [quality] == unique # [maxstamina] == 50 && [lifeleech] == 5 # [tier] == 110000", - // Rings - Raven Frost && Bul-Kathos' Wedding Band - "[type] == ring && [quality] == unique # [dexterity] >= 15 && [tohit] >= 150 # [tier] == 100000", - "[type] == ring && [quality] == unique # [maxstamina] == 50 && [lifeleech] >= 3 # [tier] == 100000", - // Switch - BO sticks - "([type] == club || [type] == sword || [type] == knife || [type] == throwingknife || [type] == mace) && ([quality] == magic || [flag] == runeword) && [2handed] == 0 # [itemallskills]+[warcriesskilltab]+[barbarianskills] >= 1 # [secondarytier] == 100000 + secondaryscore(item)", - // Merc Armor - Fortitude - "[type] == armor && [flag] == runeword # [enhanceddefense] >= 200 && [enhanceddamage] >= 300 # [merctier] == 100000", - // Merc Final Helmet - Eth Andy's - "[name] == demonhead && [quality] == unique && [flag] == ethereal # [strength] >= 25 && [enhanceddefense] >= 100 # [merctier] == 50000 + mercscore(item)", - // Merc Helmet - Andy's - "[name] == demonhead && [quality] == unique && [flag] != ethereal # [strength] >= 25 && [enhanceddefense] >= 100 # [merctier] == 40000 + mercscore(item)", - ]; + let finalGear = [ // autoequip final gear + // Weapon - Grief x2 dual wield + "[type] == sword && [flag] == runeword # [ias] >= 30 && [itemdeadlystrike] == 20 && [passivepoispierce] >= 20 # [tier] == 100000", + // Final Helmet - Upp'ed Arreat's Face + "[name] == guardiancrown && [quality] == unique && [flag] != ethereal # [barbarianskills] == 2 && [fhr] >= 30 # [tier] == tierscore(item, 150000)", + // Helmet - Arreat's Face + "[name] == slayerguard && [quality] == unique && [flag] != ethereal # [barbarianskills] == 2 && [fhr] >= 30 # [tier] == tierscore(item, 100000)", + // Belt - Dungo's + "[name] == mithrilcoil && [quality] == unique && [flag] != ethereal # [damageresist] >= 10 && [vitality] >= 30 # [tier] == tierscore(item, 100000)", + // Boots - Gore Rider + "[name] == warboots && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 160 # [tier] == tierscore(item, 100000)", + // Armor - Fortitude + "[type] == armor && [flag] != ethereal && [flag] == runeword # [enhanceddefense] >= 200 && [enhanceddamage] >= 30 # [tier] == 100000", + // Gloves - Laying of Hands + "[name] == bramblemitts && [quality] == set && [flag] != ethereal # [ias] == 20 # [tier] == 100000", + // Amulet - Atma's + "[type] == amulet && [quality] == unique # [poisonresist] == 75 # [tier] == tierscore(item, 100000)", + // Final Rings - Perfect Raven Frost & Bul-Kathos' Wedding Band + "[type] == ring && [quality] == unique # [dexterity] == 20 && [tohit] == 250 # [tier] == 110000", + "[type] == ring && [quality] == unique # [maxstamina] == 50 && [lifeleech] == 5 # [tier] == 110000", + // Rings - Raven Frost && Bul-Kathos' Wedding Band + "[type] == ring && [quality] == unique # [dexterity] >= 15 && [tohit] >= 150 # [tier] == 100000", + "[type] == ring && [quality] == unique # [maxstamina] == 50 && [lifeleech] >= 3 # [tier] == 100000", + // Switch - BO sticks + "([type] == club || [type] == sword || [type] == knife || [type] == throwingknife || [type] == mace) && ([quality] == magic || [flag] == runeword) && [2handed] == 0 # [itemallskills]+[warcriesskilltab]+[barbarianskills] >= 1 # [secondarytier] == 100000 + secondaryscore(item)", + // Merc Armor - Fortitude + "[type] == armor && [flag] == runeword # [enhanceddefense] >= 200 && [enhanceddamage] >= 300 # [merctier] == 100000", + // Merc Final Helmet - Eth Andy's + "[name] == demonhead && [quality] == unique && [flag] == ethereal # [strength] >= 25 && [enhanceddefense] >= 100 # [merctier] == 50000 + mercscore(item)", + // Merc Helmet - Andy's + "[name] == demonhead && [quality] == unique && [flag] != ethereal # [strength] >= 25 && [enhanceddefense] >= 100 # [merctier] == 40000 + mercscore(item)", + ]; - NTIP.buildList(finalGear); - NTIP.buildFinalGear(finalGear); + NTIP.buildList(finalGear); + NTIP.buildFinalGear(finalGear); - return build; - })(); + return build; + })(); })(module); diff --git a/libs/SoloPlay/BuildFiles/barbarian/barbarian.js b/libs/SoloPlay/BuildFiles/barbarian/barbarian.js index e127ea90..50011bf2 100644 --- a/libs/SoloPlay/BuildFiles/barbarian/barbarian.js +++ b/libs/SoloPlay/BuildFiles/barbarian/barbarian.js @@ -6,40 +6,40 @@ */ const CharInfo = { - respecOne: me.expansion ? 30 : 30, - respecTwo: me.expansion ? 74 : 74, - levelCap: (function() { - const currentDiff = sdk.difficulty.nameOf(me.diff); - const softcoreMode = { - "Normal": me.expansion ? 33 : 33, - "Nightmare": me.expansion ? 75 : 75, - "Hell": 100, - }; - const hardcoreMode = { - "Normal": me.expansion ? 36 : 33, - "Nightmare": me.expansion ? 75 : 75, - "Hell": 100, - }; + respecOne: me.expansion ? 30 : 30, + respecTwo: me.expansion ? 74 : 74, + levelCap: (function() { + const currentDiff = sdk.difficulty.nameOf(me.diff); + const softcoreMode = { + "Normal": me.expansion ? 33 : 33, + "Nightmare": me.expansion ? 75 : 75, + "Hell": 100, + }; + const hardcoreMode = { + "Normal": me.expansion ? 36 : 33, + "Nightmare": me.expansion ? 75 : 75, + "Hell": 100, + }; - return me.softcore ? softcoreMode[currentDiff] : hardcoreMode[currentDiff]; - })(), + return me.softcore ? softcoreMode[currentDiff] : hardcoreMode[currentDiff]; + })(), - getActiveBuild: function () { - const nSkills = me.getStat(sdk.stats.NewSkills); - const currLevel = me.charlvl; - const justRepeced = (nSkills >= currLevel); + getActiveBuild: function () { + const nSkills = me.getStat(sdk.stats.NewSkills); + const currLevel = me.charlvl; + const justRepeced = (nSkills >= currLevel); - switch (true) { - case currLevel < this.respecOne && !me.checkSkill(sdk.skills.WarCry, sdk.skills.subindex.HardPoints): - return "Start"; - case currLevel >= this.respecOne && currLevel < this.respecTwo && justRepeced: - case currLevel >= this.respecOne && currLevel < this.respecTwo && me.checkSkill(sdk.skills.NaturalResistance, sdk.skills.subindex.HardPoints) && !me.checkSkill(sdk.skills.Berserk, sdk.skills.subindex.HardPoints): - return "Stepping"; - case Check.finalBuild().respec() && justRepeced: - case Check.finalBuild().active(): - return SetUp.finalBuild; - default: - return "Leveling"; - } - }, + switch (true) { + case currLevel < this.respecOne && !me.checkSkill(sdk.skills.WarCry, sdk.skills.subindex.HardPoints): + return "Start"; + case currLevel >= this.respecOne && currLevel < this.respecTwo && justRepeced: + case currLevel >= this.respecOne && currLevel < this.respecTwo && me.checkSkill(sdk.skills.NaturalResistance, sdk.skills.subindex.HardPoints) && !me.checkSkill(sdk.skills.Berserk, sdk.skills.subindex.HardPoints): + return "Stepping"; + case Check.finalBuild().respec() && justRepeced: + case Check.finalBuild().active(): + return SetUp.finalBuild; + default: + return "Leveling"; + } + }, }; diff --git a/libs/SoloPlay/BuildFiles/druid/druid.ElementalBuild.js b/libs/SoloPlay/BuildFiles/druid/druid.ElementalBuild.js index 2f6bdb58..b0e43958 100644 --- a/libs/SoloPlay/BuildFiles/druid/druid.ElementalBuild.js +++ b/libs/SoloPlay/BuildFiles/druid/druid.ElementalBuild.js @@ -7,167 +7,167 @@ (function (module) { - module.exports = (function () { - const build = { - caster: true, - skillstab: sdk.skills.tabs.Elemental, - wantedskills: [sdk.skills.Firestorm, sdk.skills.Fissure], - usefulskills: [sdk.skills.CycloneArmor], - precastSkills: [sdk.skills.CycloneArmor], - usefulStats: [sdk.stats.PassiveFireMastery, sdk.stats.PassiveFirePierce], - wantedMerc: MercData[sdk.skills.HolyFreeze], - stats: [ - ["dexterity", 35], ["strength", 48], ["vitality", 165], - ["strength", 61], ["vitality", 252], ["strength", 156], ["vitality", "all"] - ], - skills: [ - [sdk.skills.OakSage, 6, false], - [sdk.skills.Fissure, 11, false], - [sdk.skills.Grizzly, 1, false], - [sdk.skills.Volcano, 1, false], - [sdk.skills.Fissure, 20, false], - [sdk.skills.CycloneArmor, 1, false], - [sdk.skills.Firestorm, 20, false], - [sdk.skills.Volcano, 20, false], - [sdk.skills.OakSage, 20, false], - [sdk.skills.CycloneArmor, 20, false], - [sdk.skills.Grizzly, 5, false], - ], - autoEquipTiers: [ // autoequip final gear - // Weapon - HotO - "[type] == mace && [flag] == runeword # [itemallskills] == 3 # [tier] == 100000", - // Helmet - Ravenlore - "[name] == skyspirit && [quality] == unique # [passivefirepierce] >= 10 # [tier] == tierscore(item, 100000)", - // Belt - Arach's - "[name] == spiderwebsash && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 90 # [tier] == tierscore(item, 100000)", - // Final Boots - Sandstorm Treks - "[name] == scarabshellboots && [quality] == unique # [strength]+[vitality] >= 20 # [tier] == tierscore(item, 100000)", - // Boots - War Traveler - "[name] == battleboots && [quality] == unique && [flag] != ethereal # [itemmagicbonus] >= 50 # [tier] == tierscore(item, 5000)", - // Armor - Enigma - "[type] == armor && [flag] != ethereal && [flag] == runeword # [itemallskills] == 2 # [tier] == 100000", - // Shield - Phoenix - "[name] == monarch && [flag] != ethereal && [flag] == runeword # [passivefirepierce] >= 28 # [tier] == tierscore(item, 100000)", - // Final Gloves - Perfect 2x Upp'ed Magefist - "[name] == crusadergauntlets && [quality] == unique && [flag] != ethereal # [enhanceddefense] == 30 && [addfireskills] == 1 # [tier] == 110000", - // Gloves - 2x Upp'ed Magefist - "[name] == crusadergauntlets && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 20 && [addfireskills] == 1 # [tier] == tierscore(item, 100000)", - // Gloves - Upp'ed Magefist - "[name] == battlegauntlets && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 20 && [addfireskills] == 1 # [tier] == tierscore(item, 100000)", - // Gloves - Magefist - "[name] == lightgauntlets && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 20 && [addfireskills] == 1 # [tier] == tierscore(item, 100000)", - // Amulet - Maras - "[type] == amulet && [quality] == unique # [strength] == 5 && [coldresist] >= 30 # [tier] == tierscore(item, 100000)", - // Rings - SoJ - "[type] == ring && [quality] == unique # [itemmaxmanapercent] == 25 # [tier] == 100000", - // Switch - CTA - "[minimumsockets] >= 5 && [flag] == runeword # [plusskillbattleorders] >= 1 # [secondarytier] == 100000", - // Merc Armor - Fortitude - "[type] == armor && [flag] == runeword # [enhanceddefense] >= 200 && [enhanceddamage] >= 300 # [merctier] == 100000", - // Merc Final Helmet - Eth Andy's - "[name] == demonhead && [quality] == unique && [flag] == ethereal # [strength] >= 25 && [enhanceddefense] >= 100 # [merctier] == 50000 + mercscore(item)", - // Merc Helmet - Andy's - "[name] == demonhead && [quality] == unique && [flag] != ethereal # [strength] >= 25 && [enhanceddefense] >= 100 # [merctier] == 40000 + mercscore(item)", - ], + module.exports = (function () { + const build = { + caster: true, + skillstab: sdk.skills.tabs.Elemental, + wantedskills: [sdk.skills.Firestorm, sdk.skills.Fissure], + usefulskills: [sdk.skills.CycloneArmor], + precastSkills: [sdk.skills.CycloneArmor], + usefulStats: [sdk.stats.PassiveFireMastery, sdk.stats.PassiveFirePierce], + wantedMerc: MercData[sdk.skills.HolyFreeze], + stats: [ + ["dexterity", 35], ["strength", 48], ["vitality", 165], + ["strength", 61], ["vitality", 252], ["strength", 156], ["vitality", "all"] + ], + skills: [ + [sdk.skills.OakSage, 6, false], + [sdk.skills.Fissure, 11, false], + [sdk.skills.Grizzly, 1, false], + [sdk.skills.Volcano, 1, false], + [sdk.skills.Fissure, 20, false], + [sdk.skills.CycloneArmor, 1, false], + [sdk.skills.Firestorm, 20, false], + [sdk.skills.Volcano, 20, false], + [sdk.skills.OakSage, 20, false], + [sdk.skills.CycloneArmor, 20, false], + [sdk.skills.Grizzly, 5, false], + ], + autoEquipTiers: [ // autoequip final gear + // Weapon - HotO + "[type] == mace && [flag] == runeword # [itemallskills] == 3 # [tier] == 100000", + // Helmet - Ravenlore + "[name] == skyspirit && [quality] == unique # [passivefirepierce] >= 10 # [tier] == tierscore(item, 100000)", + // Belt - Arach's + "[name] == spiderwebsash && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 90 # [tier] == tierscore(item, 100000)", + // Final Boots - Sandstorm Treks + "[name] == scarabshellboots && [quality] == unique # [strength]+[vitality] >= 20 # [tier] == tierscore(item, 100000)", + // Boots - War Traveler + "[name] == battleboots && [quality] == unique && [flag] != ethereal # [itemmagicbonus] >= 50 # [tier] == tierscore(item, 5000)", + // Armor - Enigma + "[type] == armor && [flag] != ethereal && [flag] == runeword # [itemallskills] == 2 # [tier] == 100000", + // Shield - Phoenix + "[name] == monarch && [flag] != ethereal && [flag] == runeword # [passivefirepierce] >= 28 # [tier] == tierscore(item, 100000)", + // Final Gloves - Perfect 2x Upp'ed Magefist + "[name] == crusadergauntlets && [quality] == unique && [flag] != ethereal # [enhanceddefense] == 30 && [addfireskills] == 1 # [tier] == 110000", + // Gloves - 2x Upp'ed Magefist + "[name] == crusadergauntlets && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 20 && [addfireskills] == 1 # [tier] == tierscore(item, 100000)", + // Gloves - Upp'ed Magefist + "[name] == battlegauntlets && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 20 && [addfireskills] == 1 # [tier] == tierscore(item, 100000)", + // Gloves - Magefist + "[name] == lightgauntlets && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 20 && [addfireskills] == 1 # [tier] == tierscore(item, 100000)", + // Amulet - Maras + "[type] == amulet && [quality] == unique # [strength] == 5 && [coldresist] >= 30 # [tier] == tierscore(item, 100000)", + // Rings - SoJ + "[type] == ring && [quality] == unique # [itemmaxmanapercent] == 25 # [tier] == 100000", + // Switch - CTA + "[minimumsockets] >= 5 && [flag] == runeword # [plusskillbattleorders] >= 1 # [secondarytier] == 100000", + // Merc Armor - Fortitude + "[type] == armor && [flag] == runeword # [enhanceddefense] >= 200 && [enhanceddamage] >= 300 # [merctier] == 100000", + // Merc Final Helmet - Eth Andy's + "[name] == demonhead && [quality] == unique && [flag] == ethereal # [strength] >= 25 && [enhanceddefense] >= 100 # [merctier] == 50000 + mercscore(item)", + // Merc Helmet - Andy's + "[name] == demonhead && [quality] == unique && [flag] != ethereal # [strength] >= 25 && [enhanceddefense] >= 100 # [merctier] == 40000 + mercscore(item)", + ], - charms: { - ResLife: { - max: 3, - have: [], - classid: sdk.items.SmallCharm, - stats: function (check) { - return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MaxHp) === 20); - } - }, + charms: { + ResLife: { + max: 3, + have: [], + classid: sdk.items.SmallCharm, + stats: function (check) { + return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MaxHp) === 20); + } + }, - ResMf: { - max: 2, - have: [], - classid: sdk.items.SmallCharm, - stats: function (check) { - return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MagicBonus) === 7); - } - }, + ResMf: { + max: 2, + have: [], + classid: sdk.items.SmallCharm, + stats: function (check) { + return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MagicBonus) === 7); + } + }, - ResFHR: { - max: 1, - have: [], - classid: sdk.items.SmallCharm, - stats: function (check) { - return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.FHR) === 5); - } - }, + ResFHR: { + max: 1, + have: [], + classid: sdk.items.SmallCharm, + stats: function (check) { + return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.FHR) === 5); + } + }, - Skiller: { - max: 2, - have: [], - classid: sdk.items.GrandCharm, - stats: function (check) { - return (!check.unique && check.classid === this.classid && check.getStat(sdk.stats.AddSkillTab, sdk.skills.tabs.Elemental) === 1 - && check.getStat(sdk.stats.MaxHp) >= 40); - } - }, - }, + Skiller: { + max: 2, + have: [], + classid: sdk.items.GrandCharm, + stats: function (check) { + return (!check.unique && check.classid === this.classid && check.getStat(sdk.stats.AddSkillTab, sdk.skills.tabs.Elemental) === 1 + && check.getStat(sdk.stats.MaxHp) >= 40); + } + }, + }, - AutoBuildTemplate: { - 1: { - Update: function () { - Config.AttackSkill = [-1, sdk.skills.Fissure, sdk.skills.Firestorm, sdk.skills.Fissure, sdk.skills.Firestorm, sdk.skills.ArticBlast, -1]; - Config.SummonAnimal = "Grizzly"; - Config.SummonSpirit = "Oak Sage"; - } - }, - }, + AutoBuildTemplate: { + 1: { + Update: function () { + Config.AttackSkill = [-1, sdk.skills.Fissure, sdk.skills.Firestorm, sdk.skills.Fissure, sdk.skills.Firestorm, sdk.skills.ArticBlast, -1]; + Config.SummonAnimal = "Grizzly"; + Config.SummonSpirit = "Oak Sage"; + } + }, + }, - respec: function () { - return Check.haveItem("armor", "runeword", "Enigma"); - }, + respec: function () { + return Check.haveItem("armor", "runeword", "Enigma"); + }, - active: function () { - return this.respec() && me.checkSkill(sdk.skills.Volcano, sdk.skills.subindex.HardPoints); - }, - }; + active: function () { + return this.respec() && me.checkSkill(sdk.skills.Volcano, sdk.skills.subindex.HardPoints); + }, + }; - let finalGear = [ // autoequip final gear - // Weapon - HotO - "[type] == mace && [flag] == runeword # [itemallskills] == 3 # [tier] == 100000", - // Helmet - Ravenlore - "[name] == skyspirit && [quality] == unique # [passivefirepierce] >= 10 # [tier] == tierscore(item, 100000)", - // Belt - Arach's - "[name] == spiderwebsash && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 90 # [tier] == tierscore(item, 100000)", - // Final Boots - Sandstorm Treks - "[name] == scarabshellboots && [quality] == unique # [strength]+[vitality] >= 20 # [tier] == tierscore(item, 100000)", - // Boots - War Traveler - "[name] == battleboots && [quality] == unique && [flag] != ethereal # [itemmagicbonus] >= 50 # [tier] == tierscore(item, 5000)", - // Armor - Enigma - "[type] == armor && [flag] != ethereal && [flag] == runeword # [itemallskills] == 2 # [tier] == 100000", - // Shield - Phoenix - "[name] == monarch && [flag] != ethereal && [flag] == runeword # [passivefirepierce] >= 28 # [tier] == tierscore(item, 100000)", - // Final Gloves - Perfect 2x Upp'ed Magefist - "[name] == crusadergauntlets && [quality] == unique && [flag] != ethereal # [enhanceddefense] == 30 && [addfireskills] == 1 # [tier] == 110000", - // Gloves - 2x Upp'ed Magefist - "[name] == crusadergauntlets && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 20 && [addfireskills] == 1 # [tier] == tierscore(item, 100000)", - // Gloves - Upp'ed Magefist - "[name] == battlegauntlets && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 20 && [addfireskills] == 1 # [tier] == tierscore(item, 100000)", - // Gloves - Magefist - "[name] == lightgauntlets && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 20 && [addfireskills] == 1 # [tier] == tierscore(item, 100000)", - // Amulet - Maras - "[type] == amulet && [quality] == unique # [strength] == 5 && [coldresist] >= 30 # [tier] == tierscore(item, 100000)", - // Rings - SoJ - "[type] == ring && [quality] == unique # [itemmaxmanapercent] == 25 # [tier] == 100000", - // Switch - CTA - "[minimumsockets] >= 5 && [flag] == runeword # [plusskillbattleorders] >= 1 # [secondarytier] == 100000", - // Merc Armor - Fortitude - "[type] == armor && [flag] == runeword # [enhanceddefense] >= 200 && [enhanceddamage] >= 300 # [merctier] == 100000", - // Merc Final Helmet - Eth Andy's - "[name] == demonhead && [quality] == unique && [flag] == ethereal # [strength] >= 25 && [enhanceddefense] >= 100 # [merctier] == 50000 + mercscore(item)", - // Merc Helmet - Andy's - "[name] == demonhead && [quality] == unique && [flag] != ethereal # [strength] >= 25 && [enhanceddefense] >= 100 # [merctier] == 40000 + mercscore(item)", - ]; + let finalGear = [ // autoequip final gear + // Weapon - HotO + "[type] == mace && [flag] == runeword # [itemallskills] == 3 # [tier] == 100000", + // Helmet - Ravenlore + "[name] == skyspirit && [quality] == unique # [passivefirepierce] >= 10 # [tier] == tierscore(item, 100000)", + // Belt - Arach's + "[name] == spiderwebsash && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 90 # [tier] == tierscore(item, 100000)", + // Final Boots - Sandstorm Treks + "[name] == scarabshellboots && [quality] == unique # [strength]+[vitality] >= 20 # [tier] == tierscore(item, 100000)", + // Boots - War Traveler + "[name] == battleboots && [quality] == unique && [flag] != ethereal # [itemmagicbonus] >= 50 # [tier] == tierscore(item, 5000)", + // Armor - Enigma + "[type] == armor && [flag] != ethereal && [flag] == runeword # [itemallskills] == 2 # [tier] == 100000", + // Shield - Phoenix + "[name] == monarch && [flag] != ethereal && [flag] == runeword # [passivefirepierce] >= 28 # [tier] == tierscore(item, 100000)", + // Final Gloves - Perfect 2x Upp'ed Magefist + "[name] == crusadergauntlets && [quality] == unique && [flag] != ethereal # [enhanceddefense] == 30 && [addfireskills] == 1 # [tier] == 110000", + // Gloves - 2x Upp'ed Magefist + "[name] == crusadergauntlets && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 20 && [addfireskills] == 1 # [tier] == tierscore(item, 100000)", + // Gloves - Upp'ed Magefist + "[name] == battlegauntlets && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 20 && [addfireskills] == 1 # [tier] == tierscore(item, 100000)", + // Gloves - Magefist + "[name] == lightgauntlets && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 20 && [addfireskills] == 1 # [tier] == tierscore(item, 100000)", + // Amulet - Maras + "[type] == amulet && [quality] == unique # [strength] == 5 && [coldresist] >= 30 # [tier] == tierscore(item, 100000)", + // Rings - SoJ + "[type] == ring && [quality] == unique # [itemmaxmanapercent] == 25 # [tier] == 100000", + // Switch - CTA + "[minimumsockets] >= 5 && [flag] == runeword # [plusskillbattleorders] >= 1 # [secondarytier] == 100000", + // Merc Armor - Fortitude + "[type] == armor && [flag] == runeword # [enhanceddefense] >= 200 && [enhanceddamage] >= 300 # [merctier] == 100000", + // Merc Final Helmet - Eth Andy's + "[name] == demonhead && [quality] == unique && [flag] == ethereal # [strength] >= 25 && [enhanceddefense] >= 100 # [merctier] == 50000 + mercscore(item)", + // Merc Helmet - Andy's + "[name] == demonhead && [quality] == unique && [flag] != ethereal # [strength] >= 25 && [enhanceddefense] >= 100 # [merctier] == 40000 + mercscore(item)", + ]; - NTIP.buildList(finalGear); - NTIP.buildFinalGear(finalGear); + NTIP.buildList(finalGear); + NTIP.buildFinalGear(finalGear); - return build; - })(); + return build; + })(); })(module); diff --git a/libs/SoloPlay/BuildFiles/druid/druid.FirewolfBuild.js b/libs/SoloPlay/BuildFiles/druid/druid.FirewolfBuild.js index 6df25083..fee8715c 100644 --- a/libs/SoloPlay/BuildFiles/druid/druid.FirewolfBuild.js +++ b/libs/SoloPlay/BuildFiles/druid/druid.FirewolfBuild.js @@ -7,127 +7,127 @@ (function (module) { - module.exports = (function () { - const build = { - caster: false, - skillstab: sdk.skills.tabs.ShapeShifting, - wantedskills: [sdk.skills.Werewolf, sdk.skills.Lycanthropy, sdk.skills.FireClaws], - usefulskills: [sdk.skills.HeartofWolverine, sdk.skills.Grizzly, sdk.skills.Rabies, sdk.skills.Volcano, sdk.skills.Fissure, sdk.skills.Firestorm], - precastSkills: [sdk.skills.Werewolf, sdk.skills.HeartofWolverine, sdk.skills.Grizzly], - usefulStats: [sdk.stats.PassiveFireMastery, sdk.stats.PassiveFirePierce, sdk.stats.PierceFire], - wantedMerc: MercData[sdk.skills.Might], - stats: [ - ["strength", 156], ["dexterity", 136], ["vitality", "all"] - ], - skills: [ - [sdk.skills.Werewolf, 1, false], - [sdk.skills.Lycanthropy, 20, false], - [sdk.skills.PoisonCreeper, 1, false], - [sdk.skills.Grizzly, 1, false], - [sdk.skills.HeartofWolverine, 1, false], - [sdk.skills.Volcano, 1, false], - [sdk.skills.Fury, 1, false], - [sdk.skills.FireClaws, 20, false], - [sdk.skills.Rabies, 20, false], - [sdk.skills.Volcano, 20, false], - [sdk.skills.HeartofWolverine, 20, false], - ], + module.exports = (function () { + const build = { + caster: false, + skillstab: sdk.skills.tabs.ShapeShifting, + wantedskills: [sdk.skills.Werewolf, sdk.skills.Lycanthropy, sdk.skills.FireClaws], + usefulskills: [sdk.skills.HeartofWolverine, sdk.skills.Grizzly, sdk.skills.Rabies, sdk.skills.Volcano, sdk.skills.Fissure, sdk.skills.Firestorm], + precastSkills: [sdk.skills.Werewolf, sdk.skills.HeartofWolverine, sdk.skills.Grizzly], + usefulStats: [sdk.stats.PassiveFireMastery, sdk.stats.PassiveFirePierce, sdk.stats.PierceFire], + wantedMerc: MercData[sdk.skills.Might], + stats: [ + ["strength", 156], ["dexterity", 136], ["vitality", "all"] + ], + skills: [ + [sdk.skills.Werewolf, 1, false], + [sdk.skills.Lycanthropy, 20, false], + [sdk.skills.PoisonCreeper, 1, false], + [sdk.skills.Grizzly, 1, false], + [sdk.skills.HeartofWolverine, 1, false], + [sdk.skills.Volcano, 1, false], + [sdk.skills.Fury, 1, false], + [sdk.skills.FireClaws, 20, false], + [sdk.skills.Rabies, 20, false], + [sdk.skills.Volcano, 20, false], + [sdk.skills.HeartofWolverine, 20, false], + ], - charms: { - ResMf: { - max: 2, - have: [], - classid: sdk.items.SmallCharm, - stats: function (check) { - return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MagicBonus) === 7); - } - }, + charms: { + ResMf: { + max: 2, + have: [], + classid: sdk.items.SmallCharm, + stats: function (check) { + return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MagicBonus) === 7); + } + }, - Poison: { - max: 6, - have: [], - classid: sdk.items.SmallCharm, - stats: function (check) { - return (!check.unique && check.classid === this.classid - && ((check.getStat(sdk.stats.PoisonLength) * check.getStat(sdk.stats.PoisonMaxDamage)) / 256) >= 141); - } - }, + Poison: { + max: 6, + have: [], + classid: sdk.items.SmallCharm, + stats: function (check) { + return (!check.unique && check.classid === this.classid + && ((check.getStat(sdk.stats.PoisonLength) * check.getStat(sdk.stats.PoisonMaxDamage)) / 256) >= 141); + } + }, - Skiller: { - max: 2, - have: [], - classid: sdk.items.GrandCharm, - stats: function (check) { - return (!check.unique && check.classid === this.classid && check.getStat(sdk.stats.AddSkillTab, sdk.skills.tabs.ShapeShifting) === 1 - && check.getStat(sdk.stats.MaxHp) >= 40); - } - }, - }, + Skiller: { + max: 2, + have: [], + classid: sdk.items.GrandCharm, + stats: function (check) { + return (!check.unique && check.classid === this.classid && check.getStat(sdk.stats.AddSkillTab, sdk.skills.tabs.ShapeShifting) === 1 + && check.getStat(sdk.stats.MaxHp) >= 40); + } + }, + }, - AutoBuildTemplate: { - 1: { - Update: function () { - Config.BeltColumn = ["hp", "hp", "mp", "rv"]; - Config.AttackSkill = [ - sdk.skills.FeralRage, - sdk.skills.FireClaws, sdk.skills.Rabies, - sdk.skills.FireClaws, sdk.skills.Rabies, - sdk.skills.Fury, sdk.skills.Rabies - ]; - Config.LowManaSkill = [0, 0]; - Config.Wereform = "Werewolf"; - Config.SummonAnimal = "Grizzly"; - Config.SummonSpirit = "Heart of Wolverine"; - } - }, - }, + AutoBuildTemplate: { + 1: { + Update: function () { + Config.BeltColumn = ["hp", "hp", "mp", "rv"]; + Config.AttackSkill = [ + sdk.skills.FeralRage, + sdk.skills.FireClaws, sdk.skills.Rabies, + sdk.skills.FireClaws, sdk.skills.Rabies, + sdk.skills.Fury, sdk.skills.Rabies + ]; + Config.LowManaSkill = [0, 0]; + Config.Wereform = "Werewolf"; + Config.SummonAnimal = "Grizzly"; + Config.SummonSpirit = "Heart of Wolverine"; + } + }, + }, - respec: function () { - return me.haveAll([{ name: sdk.locale.items.Ice }, { name: sdk.locale.items.ChainsofHonor }]); - }, + respec: function () { + return me.haveAll([{ name: sdk.locale.items.Ice }, { name: sdk.locale.items.ChainsofHonor }]); + }, - active: function () { - return this.respec() && me.getSkill(sdk.skills.FireClaws, sdk.skills.subindex.HardPoints) === 20; - }, - }; - - let finalGear = [ - // Weapon - Ice - "[name] == demoncrossbow && [flag] == runeword # [holyfreezeaura] == 18 # [tier] == 110000", - // Helmet - Jalal's Mane - "[name] == totemicmask && [quality] == unique # [druidskills] == 2 && [shapeshiftingskilltab] == 2 # [tier] == tierscore(item, 110000)", - // Belt - Verdungo's - "[name] == mithrilcoil && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 90 # [tier] == tierscore(item, 110000)", - // Armor - Chains of Honor - "[type] == armor && [flag] == runeword && [flag] != ethereal # [fireresist] == 65 && [hpregen] == 7 # [tier] == 110000", - // Gloves - Dracul's Grasp - "[name] == vampirebonegloves && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 90 # [tier] == tierscore(item, 110000)", - // Boots - Goblin Toes - "[name] == lightplatedboots && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 50 # [tier] == tierscore(item, 100000)", - // Amulet - Metalgrid - "[type] == amulet && [quality] == unique # [tohit] >= 400 && [coldresist] >= 25 # [tier] == tierscore(item, 110000)", - // Ring 1 - Ravenfrost - "[type] == ring && [quality] == unique # [tohit] >= 180 && [dexterity] >= 15 # [tier] == 100000", - // Ring 2 - Carrion Wind - "[name] == ring && [quality] == unique # [poisonresist] == 55 && [lifeleech] >= 6 # [tier] == tierscore(item, 110000)", - // Merc - // Final Armor - Fortitude - "[type] == armor && [flag] == runeword # [enhanceddefense] >= 200 && [enhanceddamage] >= 300 # [merctier] == 100000", - // Temporary Armor - Treachery - "[type] == armor && [flag] == runeword # [ias] == 45 && [coldresist] == 30 # [merctier] == 50000 + mercscore(item)", - // Final Helm - Eth Andy's - "[name] == demonhead && [quality] == unique && [flag] == ethereal # [strength] >= 25 && [enhanceddefense] >= 100 # [merctier] == 50000 + mercscore(item)", - // Temporary Helm - Andy's - "[name] == demonhead && [quality] == unique && [flag] != ethereal # [strength] >= 25 && [enhanceddefense] >= 100 # [merctier] == 40000 + mercscore(item)", - // Final Weapon - Infinity - "[type] == polearm && [flag] == runeword # [convictionaura] >= 13 # [merctier] == 100000 + mercscore(item)", - // Temporary Weapon - Reaper's Toll - "[name] == thresher && [quality] == unique # [enhanceddamage] >= 190 && [lifeleech] >= 11 # [merctier] == 50000 + mercscore(item)", - ]; + active: function () { + return this.respec() && me.getSkill(sdk.skills.FireClaws, sdk.skills.subindex.HardPoints) === 20; + }, + }; + + let finalGear = [ + // Weapon - Ice + "[name] == demoncrossbow && [flag] == runeword # [holyfreezeaura] == 18 # [tier] == 110000", + // Helmet - Jalal's Mane + "[name] == totemicmask && [quality] == unique # [druidskills] == 2 && [shapeshiftingskilltab] == 2 # [tier] == tierscore(item, 110000)", + // Belt - Verdungo's + "[name] == mithrilcoil && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 90 # [tier] == tierscore(item, 110000)", + // Armor - Chains of Honor + "[type] == armor && [flag] == runeword && [flag] != ethereal # [fireresist] == 65 && [hpregen] == 7 # [tier] == 110000", + // Gloves - Dracul's Grasp + "[name] == vampirebonegloves && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 90 # [tier] == tierscore(item, 110000)", + // Boots - Goblin Toes + "[name] == lightplatedboots && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 50 # [tier] == tierscore(item, 100000)", + // Amulet - Metalgrid + "[type] == amulet && [quality] == unique # [tohit] >= 400 && [coldresist] >= 25 # [tier] == tierscore(item, 110000)", + // Ring 1 - Ravenfrost + "[type] == ring && [quality] == unique # [tohit] >= 180 && [dexterity] >= 15 # [tier] == 100000", + // Ring 2 - Carrion Wind + "[name] == ring && [quality] == unique # [poisonresist] == 55 && [lifeleech] >= 6 # [tier] == tierscore(item, 110000)", + // Merc + // Final Armor - Fortitude + "[type] == armor && [flag] == runeword # [enhanceddefense] >= 200 && [enhanceddamage] >= 300 # [merctier] == 100000", + // Temporary Armor - Treachery + "[type] == armor && [flag] == runeword # [ias] == 45 && [coldresist] == 30 # [merctier] == 50000 + mercscore(item)", + // Final Helm - Eth Andy's + "[name] == demonhead && [quality] == unique && [flag] == ethereal # [strength] >= 25 && [enhanceddefense] >= 100 # [merctier] == 50000 + mercscore(item)", + // Temporary Helm - Andy's + "[name] == demonhead && [quality] == unique && [flag] != ethereal # [strength] >= 25 && [enhanceddefense] >= 100 # [merctier] == 40000 + mercscore(item)", + // Final Weapon - Infinity + "[type] == polearm && [flag] == runeword # [convictionaura] >= 13 # [merctier] == 100000 + mercscore(item)", + // Temporary Weapon - Reaper's Toll + "[name] == thresher && [quality] == unique # [enhanceddamage] >= 190 && [lifeleech] >= 11 # [merctier] == 50000 + mercscore(item)", + ]; - NTIP.buildList(finalGear); - NTIP.buildFinalGear(finalGear); + NTIP.buildList(finalGear); + NTIP.buildFinalGear(finalGear); - return build; - })(); + return build; + })(); })(module); diff --git a/libs/SoloPlay/BuildFiles/druid/druid.LevelingBuild.js b/libs/SoloPlay/BuildFiles/druid/druid.LevelingBuild.js index 0567ce48..20ab4a9c 100644 --- a/libs/SoloPlay/BuildFiles/druid/druid.LevelingBuild.js +++ b/libs/SoloPlay/BuildFiles/druid/druid.LevelingBuild.js @@ -7,58 +7,58 @@ (function (module) { - module.exports = (function () { - const build = { - caster: true, - skillstab: 42, // elemental - wantedskills: [sdk.skills.Tornado, sdk.skills.Hurricane, sdk.skills.Twister], - usefulskills: [sdk.skills.CycloneArmor], - wantedMerc: MercData[sdk.skills.BlessedAim], - stats: [ - ["strength", 48], ["dexterity", 35], ["vitality", 165], - ["strength", 61], ["vitality", 252], ["strength", 156], ["vitality", "all"] - ], - skills: [ - // Total skills at respec = 25 (assume hasn't killed izual yet) - [sdk.skills.Tornado, 1], // points left 21 - [sdk.skills.OakSage, 6], // points left 15 - [sdk.skills.SummonDireWolf, 1], // points left 12 - [sdk.skills.CycloneArmor, 13], // points left 0 - // Start - [sdk.skills.Tornado, 13, false], - [sdk.skills.Hurricane, 6, false], - [sdk.skills.Tornado, 14, false], - [sdk.skills.Hurricane, 7, false], - [sdk.skills.Grizzly, 1, false], - [sdk.skills.Tornado, 15, false], - [sdk.skills.Hurricane, 8, false], - [sdk.skills.Tornado, 20, false], - [sdk.skills.Hurricane, 20, false], - [sdk.skills.CycloneArmor, 20, false], - [sdk.skills.OakSage, 20, false], - [sdk.skills.Twister, 20], - ], + module.exports = (function () { + const build = { + caster: true, + skillstab: 42, // elemental + wantedskills: [sdk.skills.Tornado, sdk.skills.Hurricane, sdk.skills.Twister], + usefulskills: [sdk.skills.CycloneArmor], + wantedMerc: MercData[sdk.skills.BlessedAim], + stats: [ + ["strength", 48], ["dexterity", 35], ["vitality", 165], + ["strength", 61], ["vitality", 252], ["strength", 156], ["vitality", "all"] + ], + skills: [ + // Total skills at respec = 25 (assume hasn't killed izual yet) + [sdk.skills.Tornado, 1], // points left 21 + [sdk.skills.OakSage, 6], // points left 15 + [sdk.skills.SummonDireWolf, 1], // points left 12 + [sdk.skills.CycloneArmor, 13], // points left 0 + // Start + [sdk.skills.Tornado, 13, false], + [sdk.skills.Hurricane, 6, false], + [sdk.skills.Tornado, 14, false], + [sdk.skills.Hurricane, 7, false], + [sdk.skills.Grizzly, 1, false], + [sdk.skills.Tornado, 15, false], + [sdk.skills.Hurricane, 8, false], + [sdk.skills.Tornado, 20, false], + [sdk.skills.Hurricane, 20, false], + [sdk.skills.CycloneArmor, 20, false], + [sdk.skills.OakSage, 20, false], + [sdk.skills.Twister, 20], + ], - active: function () { - return (me.charlvl > CharInfo.respecOne && me.charlvl > CharInfo.respecTwo && me.checkSkill(sdk.skills.Tornado, sdk.skills.subindex.HardPoints) && !Check.finalBuild().active()); - }, + active: function () { + return (me.charlvl > CharInfo.respecOne && me.charlvl > CharInfo.respecTwo && me.checkSkill(sdk.skills.Tornado, sdk.skills.subindex.HardPoints) && !Check.finalBuild().active()); + }, - AutoBuildTemplate: { - 1: { - Update: function () { - Config.AttackSkill = [-1, sdk.skills.Tornado, -1, sdk.skills.Tornado, -1, sdk.skills.ArticBlast, -1]; - Config.LowManaSkill = [-1, -1]; - Config.SummonAnimal = "Grizzly"; - Config.SummonSpirit = "Oak Sage"; - Config.BeltColumn = ["hp", "hp", "mp", "mp"]; - Config.HPBuffer = 2; - Config.MPBuffer = me.charlvl < 80 ? 6 : 2; - SetUp.belt(); - } - } - }, - }; - - return build; - })(); + AutoBuildTemplate: { + 1: { + Update: function () { + Config.AttackSkill = [-1, sdk.skills.Tornado, -1, sdk.skills.Tornado, -1, sdk.skills.ArticBlast, -1]; + Config.LowManaSkill = [-1, -1]; + Config.SummonAnimal = "Grizzly"; + Config.SummonSpirit = "Oak Sage"; + Config.BeltColumn = ["hp", "hp", "mp", "mp"]; + Config.HPBuffer = 2; + Config.MPBuffer = me.charlvl < 80 ? 6 : 2; + SetUp.belt(); + } + } + }, + }; + + return build; + })(); })(module); diff --git a/libs/SoloPlay/BuildFiles/druid/druid.PlaguewolfBuild.js b/libs/SoloPlay/BuildFiles/druid/druid.PlaguewolfBuild.js index 057cbcf1..3013084e 100644 --- a/libs/SoloPlay/BuildFiles/druid/druid.PlaguewolfBuild.js +++ b/libs/SoloPlay/BuildFiles/druid/druid.PlaguewolfBuild.js @@ -7,118 +7,118 @@ (function (module) { - module.exports = (function () { - const build = { - caster: false, - skillstab: sdk.skills.tabs.ShapeShifting, - wantedskills: [sdk.skills.Werewolf, sdk.skills.Lycanthropy, sdk.skills.Fury], - usefulskills: [sdk.skills.HeartofWolverine, sdk.skills.Grizzly], - precastSkills: [sdk.skills.Werewolf, sdk.skills.HeartofWolverine, sdk.skills.Grizzly], - usefulStats: [sdk.stats.PassivePoisonMastery, sdk.stats.PassivePoisonPierce, sdk.stats.PiercePois], - wantedMerc: MercData[sdk.skills.Might], - stats: [ - ["strength", 156], ["dexterity", 136], ["vitality", "all"] - ], - skills: [ - [sdk.skills.Werewolf, 20, false], - [sdk.skills.Lycanthropy, 20, false], - [sdk.skills.PoisonCreeper, 1, false], - [sdk.skills.Grizzly, 1, false], - [sdk.skills.Rabies, 20, false], - [sdk.skills.Fury, 20, false], - [sdk.skills.HeartofWolverine, 20, false], - [sdk.skills.PoisonCreeper, 20, false], - ], + module.exports = (function () { + const build = { + caster: false, + skillstab: sdk.skills.tabs.ShapeShifting, + wantedskills: [sdk.skills.Werewolf, sdk.skills.Lycanthropy, sdk.skills.Fury], + usefulskills: [sdk.skills.HeartofWolverine, sdk.skills.Grizzly], + precastSkills: [sdk.skills.Werewolf, sdk.skills.HeartofWolverine, sdk.skills.Grizzly], + usefulStats: [sdk.stats.PassivePoisonMastery, sdk.stats.PassivePoisonPierce, sdk.stats.PiercePois], + wantedMerc: MercData[sdk.skills.Might], + stats: [ + ["strength", 156], ["dexterity", 136], ["vitality", "all"] + ], + skills: [ + [sdk.skills.Werewolf, 20, false], + [sdk.skills.Lycanthropy, 20, false], + [sdk.skills.PoisonCreeper, 1, false], + [sdk.skills.Grizzly, 1, false], + [sdk.skills.Rabies, 20, false], + [sdk.skills.Fury, 20, false], + [sdk.skills.HeartofWolverine, 20, false], + [sdk.skills.PoisonCreeper, 20, false], + ], - charms: { - ResMf: { - max: 2, - have: [], - classid: sdk.items.SmallCharm, - stats: function (check) { - return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MagicBonus) === 7); - } - }, + charms: { + ResMf: { + max: 2, + have: [], + classid: sdk.items.SmallCharm, + stats: function (check) { + return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MagicBonus) === 7); + } + }, - Poison: { - max: 6, - have: [], - classid: sdk.items.SmallCharm, - stats: function (check) { - return (!check.unique && check.classid === this.classid - && ((check.getStat(sdk.stats.PoisonLength) * check.getStat(sdk.stats.PoisonMaxDamage)) / 256) >= 141); - } - }, + Poison: { + max: 6, + have: [], + classid: sdk.items.SmallCharm, + stats: function (check) { + return (!check.unique && check.classid === this.classid + && ((check.getStat(sdk.stats.PoisonLength) * check.getStat(sdk.stats.PoisonMaxDamage)) / 256) >= 141); + } + }, - Skiller: { - max: 2, - have: [], - classid: sdk.items.GrandCharm, - stats: function (check) { - return (!check.unique && check.classid === this.classid && check.getStat(sdk.stats.AddSkillTab, sdk.skills.tabs.ShapeShifting) === 1 - && check.getStat(sdk.stats.MaxHp) >= 40); - } - }, - }, + Skiller: { + max: 2, + have: [], + classid: sdk.items.GrandCharm, + stats: function (check) { + return (!check.unique && check.classid === this.classid && check.getStat(sdk.stats.AddSkillTab, sdk.skills.tabs.ShapeShifting) === 1 + && check.getStat(sdk.stats.MaxHp) >= 40); + } + }, + }, - AutoBuildTemplate: { - 1: { - Update: function () { - Config.BeltColumn = ["hp", "hp", "mp", "rv"]; - Config.AttackSkill = [sdk.skills.FeralRage, sdk.skills.Fury, sdk.skills.Rabies, sdk.skills.Fury, sdk.skills.Rabies, sdk.skills.Rabies, -1]; - Config.LowManaSkill = [0, 0]; - Config.Wereform = "Werewolf"; - Config.SummonAnimal = "Grizzly"; - Config.SummonSpirit = "Heart of Wolverine"; - } - }, - }, + AutoBuildTemplate: { + 1: { + Update: function () { + Config.BeltColumn = ["hp", "hp", "mp", "rv"]; + Config.AttackSkill = [sdk.skills.FeralRage, sdk.skills.Fury, sdk.skills.Rabies, sdk.skills.Fury, sdk.skills.Rabies, sdk.skills.Rabies, -1]; + Config.LowManaSkill = [0, 0]; + Config.Wereform = "Werewolf"; + Config.SummonAnimal = "Grizzly"; + Config.SummonSpirit = "Heart of Wolverine"; + } + }, + }, - respec: function () { - return me.haveAll([{ name: sdk.locale.items.Grief }, { name: sdk.locale.items.ChainsofHonor }]); - }, + respec: function () { + return me.haveAll([{ name: sdk.locale.items.Grief }, { name: sdk.locale.items.ChainsofHonor }]); + }, - active: function () { - return this.respec() && me.getSkill(sdk.skills.Rabies, sdk.skills.subindex.HardPoints) === 20; - }, - }; + active: function () { + return this.respec() && me.getSkill(sdk.skills.Rabies, sdk.skills.subindex.HardPoints) === 20; + }, + }; - let finalGear = [ // autoequip final gear - // Weapon - Grief - "[type] == sword && [flag] == runeword # [ias] >= 30 && [itemdeadlystrike] == 20 && [passivepoispierce] >= 20 # [tier] == 110000", - // Shield - Stormshield - "[name] == monarch && [quality] == unique # [damageresist] >= 35 # [tier] == 110000", - // Helmet - Jalal's Mane - "[name] == totemicmask && [quality] == unique # [druidskills] == 2 && [shapeshiftingskilltab] == 2 # [tier] == tierscore(item, 110000)", - // Belt - Verdungo's - "[name] == mithrilcoil && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 90 # [tier] == tierscore(item, 110000)", - // Armor - CoH - "[type] == armor && [flag] == runeword && [flag] != ethereal # [fireresist] == 65 && [hpregen] == 7 # [tier] == 110000", - // Gloves - Dracul's - "[name] == vampirebonegloves && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 90 # [tier] == tierscore(item, 110000)", - // Amulet - Maras - "[type] == amulet && [quality] == unique # [strength] == 5 && [coldresist] >= 30 # [tier] == tierscore(item, 110000)", - // Final Rings - Perfect Wisp & Bul-Kathos' Wedding Band - "[type] == ring && [quality] == unique # [itemabsorblightpercent] == 20 # [tier] == 110000", - "[type] == ring && [quality] == unique # [maxstamina] == 50 && [lifeleech] == 5 # [tier] == 110000", - // Rings - Wisp & Bul-Kathos' Wedding Band - "[type] == ring && [quality] == unique # [itemabsorblightpercent] >= 10 # [tier] == 100000", - "[type] == ring && [quality] == unique # [maxstamina] == 50 && [lifeleech] >= 3 # [tier] == 100000", - // Merc Final Armor - Fortitude - "[type] == armor && [flag] == runeword # [enhanceddefense] >= 200 && [enhanceddamage] >= 300 # [merctier] == 100000", - // Merc Armor - Treachery - "[type] == armor && [flag] == runeword # [ias] == 45 && [coldresist] == 30 # [merctier] == 50000 + mercscore(item)", - // Merc Final Helmet - Eth Andy's - "[name] == demonhead && [quality] == unique && [flag] == ethereal # [strength] >= 25 && [enhanceddefense] >= 100 # [merctier] == 50000 + mercscore(item)", - // Merc Helmet - Andy's - "[name] == demonhead && [quality] == unique && [flag] != ethereal # [strength] >= 25 && [enhanceddefense] >= 100 # [merctier] == 40000 + mercscore(item)", - // Merc Weapon - Reaper's Toll - "[name] == thresher && [quality] == unique # [enhanceddamage] >= 190 && [lifeleech] >= 11 # [merctier] == 100000 + mercscore(item)", - ]; + let finalGear = [ // autoequip final gear + // Weapon - Grief + "[type] == sword && [flag] == runeword # [ias] >= 30 && [itemdeadlystrike] == 20 && [passivepoispierce] >= 20 # [tier] == 110000", + // Shield - Stormshield + "[name] == monarch && [quality] == unique # [damageresist] >= 35 # [tier] == 110000", + // Helmet - Jalal's Mane + "[name] == totemicmask && [quality] == unique # [druidskills] == 2 && [shapeshiftingskilltab] == 2 # [tier] == tierscore(item, 110000)", + // Belt - Verdungo's + "[name] == mithrilcoil && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 90 # [tier] == tierscore(item, 110000)", + // Armor - CoH + "[type] == armor && [flag] == runeword && [flag] != ethereal # [fireresist] == 65 && [hpregen] == 7 # [tier] == 110000", + // Gloves - Dracul's + "[name] == vampirebonegloves && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 90 # [tier] == tierscore(item, 110000)", + // Amulet - Maras + "[type] == amulet && [quality] == unique # [strength] == 5 && [coldresist] >= 30 # [tier] == tierscore(item, 110000)", + // Final Rings - Perfect Wisp & Bul-Kathos' Wedding Band + "[type] == ring && [quality] == unique # [itemabsorblightpercent] == 20 # [tier] == 110000", + "[type] == ring && [quality] == unique # [maxstamina] == 50 && [lifeleech] == 5 # [tier] == 110000", + // Rings - Wisp & Bul-Kathos' Wedding Band + "[type] == ring && [quality] == unique # [itemabsorblightpercent] >= 10 # [tier] == 100000", + "[type] == ring && [quality] == unique # [maxstamina] == 50 && [lifeleech] >= 3 # [tier] == 100000", + // Merc Final Armor - Fortitude + "[type] == armor && [flag] == runeword # [enhanceddefense] >= 200 && [enhanceddamage] >= 300 # [merctier] == 100000", + // Merc Armor - Treachery + "[type] == armor && [flag] == runeword # [ias] == 45 && [coldresist] == 30 # [merctier] == 50000 + mercscore(item)", + // Merc Final Helmet - Eth Andy's + "[name] == demonhead && [quality] == unique && [flag] == ethereal # [strength] >= 25 && [enhanceddefense] >= 100 # [merctier] == 50000 + mercscore(item)", + // Merc Helmet - Andy's + "[name] == demonhead && [quality] == unique && [flag] != ethereal # [strength] >= 25 && [enhanceddefense] >= 100 # [merctier] == 40000 + mercscore(item)", + // Merc Weapon - Reaper's Toll + "[name] == thresher && [quality] == unique # [enhanceddamage] >= 190 && [lifeleech] >= 11 # [merctier] == 100000 + mercscore(item)", + ]; - NTIP.buildList(finalGear); - NTIP.buildFinalGear(finalGear); + NTIP.buildList(finalGear); + NTIP.buildFinalGear(finalGear); - return build; - })(); + return build; + })(); })(module); diff --git a/libs/SoloPlay/BuildFiles/druid/druid.StartBuild.js b/libs/SoloPlay/BuildFiles/druid/druid.StartBuild.js index e4fa3e28..d4768ae2 100644 --- a/libs/SoloPlay/BuildFiles/druid/druid.StartBuild.js +++ b/libs/SoloPlay/BuildFiles/druid/druid.StartBuild.js @@ -7,72 +7,72 @@ (function (module, require) { - module.exports = (function () { - /** - * @todo Test summoner/elemental build - */ - const build = { - AutoBuildTemplate: {}, - caster: true, - skillstab: sdk.skills.tabs.Elemental, - wantedskills: [sdk.skills.Firestorm, sdk.skills.Fissure], - usefulskills: [sdk.skills.MoltenBoulder], - wantedMerc: MercData[sdk.skills.BlessedAim], - stats: [ - ["vitality", 70], - ["strength", 35], - ["energy", 85], - ["vitality", "all"] - ], - skills: [ - [sdk.skills.PoisonCreeper, 2, false], - [sdk.skills.Firestorm, 4, false], - [sdk.skills.MoltenBoulder, 1], - [sdk.skills.Firestorm, 7, false], - [sdk.skills.Fissure, 1], - [sdk.skills.CarrionVine, 1], - [sdk.skills.Fissure, 8, false], - [sdk.skills.Firestorm, 11, false], - [sdk.skills.Fissure, 20, false], - [sdk.skills.Firestorm, 18, false], - ], + module.exports = (function () { + /** + * @todo Test summoner/elemental build + */ + const build = { + AutoBuildTemplate: {}, + caster: true, + skillstab: sdk.skills.tabs.Elemental, + wantedskills: [sdk.skills.Firestorm, sdk.skills.Fissure], + usefulskills: [sdk.skills.MoltenBoulder], + wantedMerc: MercData[sdk.skills.BlessedAim], + stats: [ + ["vitality", 70], + ["strength", 35], + ["energy", 85], + ["vitality", "all"] + ], + skills: [ + [sdk.skills.PoisonCreeper, 2, false], + [sdk.skills.Firestorm, 4, false], + [sdk.skills.MoltenBoulder, 1], + [sdk.skills.Firestorm, 7, false], + [sdk.skills.Fissure, 1], + [sdk.skills.CarrionVine, 1], + [sdk.skills.Fissure, 8, false], + [sdk.skills.Firestorm, 11, false], + [sdk.skills.Fissure, 20, false], + [sdk.skills.Firestorm, 18, false], + ], - active: function () { - return me.charlvl < CharInfo.respecOne && !me.checkSkill(sdk.skills.Tornado, sdk.skills.subindex.HardPoints); - }, - }; + active: function () { + return me.charlvl < CharInfo.respecOne && !me.checkSkill(sdk.skills.Tornado, sdk.skills.subindex.HardPoints); + }, + }; - const { buildAutoBuildTempObj } = require("../../Utils/General"); + const { buildAutoBuildTempObj } = require("../../Utils/General"); - build.AutoBuildTemplate[1] = buildAutoBuildTempObj(() => { - Config.TownHP = me.hardcore ? 0 : 35; - Config.SkipImmune = ["fire"]; - Config.BeltColumn = me.charlvl < 7 ? ["hp", "hp", "hp", "mp"] : ["hp", "hp", "mp", "mp"]; - Config.HPBuffer = 4; - Config.MPBuffer = 4; - Config.AttackSkill = [-1, 0, 0, 0, 0, 0, 0]; - Config.LowManaSkill = [0, 0]; - Config.SummonVine = 1; // "Poison Creeper" - if (me.checkSkill(sdk.skills.Firestorm, sdk.skills.subindex.HardPoints)) { - Config.AttackSkill = [-1, sdk.skills.Firestorm, -1, sdk.skills.Firestorm, -1, 0, 0]; - } else { - Config.AttackSkill = [-1, 0, 0, 0, 0, 0, 0]; - } - SetUp.belt(); - }); + build.AutoBuildTemplate[1] = buildAutoBuildTempObj(() => { + Config.TownHP = me.hardcore ? 0 : 35; + Config.SkipImmune = ["fire"]; + Config.BeltColumn = me.charlvl < 7 ? ["hp", "hp", "hp", "mp"] : ["hp", "hp", "mp", "mp"]; + Config.HPBuffer = 4; + Config.MPBuffer = 4; + Config.AttackSkill = [-1, 0, 0, 0, 0, 0, 0]; + Config.LowManaSkill = [0, 0]; + Config.SummonVine = 1; // "Poison Creeper" + if (me.checkSkill(sdk.skills.Firestorm, sdk.skills.subindex.HardPoints)) { + Config.AttackSkill = [-1, sdk.skills.Firestorm, -1, sdk.skills.Firestorm, -1, 0, 0]; + } else { + Config.AttackSkill = [-1, 0, 0, 0, 0, 0, 0]; + } + SetUp.belt(); + }); - build.AutoBuildTemplate[12] = buildAutoBuildTempObj(() => { - if (me.checkSkill(sdk.skills.Fissure, sdk.skills.subindex.HardPoints)) { - Config.AttackSkill = [-1, sdk.skills.Fissure, sdk.skills.Firestorm, sdk.skills.Fissure, sdk.skills.Firestorm, 0, 0]; - } - }); + build.AutoBuildTemplate[12] = buildAutoBuildTempObj(() => { + if (me.checkSkill(sdk.skills.Fissure, sdk.skills.subindex.HardPoints)) { + Config.AttackSkill = [-1, sdk.skills.Fissure, sdk.skills.Firestorm, sdk.skills.Fissure, sdk.skills.Firestorm, 0, 0]; + } + }); - build.AutoBuildTemplate[13] = buildAutoBuildTempObj(() => { - if (Skill.canUse(sdk.skills.Fissure)) { - Config.AttackSkill = [-1, sdk.skills.Fissure, sdk.skills.Firestorm, sdk.skills.Fissure, sdk.skills.Firestorm, 0, 0]; - } - }); + build.AutoBuildTemplate[13] = buildAutoBuildTempObj(() => { + if (Skill.canUse(sdk.skills.Fissure)) { + Config.AttackSkill = [-1, sdk.skills.Fissure, sdk.skills.Firestorm, sdk.skills.Fissure, sdk.skills.Firestorm, 0, 0]; + } + }); - return build; - })(); + return build; + })(); })(module, require); diff --git a/libs/SoloPlay/BuildFiles/druid/druid.StormbearBuild.js b/libs/SoloPlay/BuildFiles/druid/druid.StormbearBuild.js index cc71910b..43b0a3fc 100644 --- a/libs/SoloPlay/BuildFiles/druid/druid.StormbearBuild.js +++ b/libs/SoloPlay/BuildFiles/druid/druid.StormbearBuild.js @@ -7,127 +7,127 @@ (function (module) { - module.exports = (function () { - const build = { - caster: false, - skillstab: sdk.skills.tabs.ShapeShifting, - wantedskills: [sdk.skills.Werebear, sdk.skills.Lycanthropy, sdk.skills.Maul], - usefulskills: [sdk.skills.HeartofWolverine, sdk.skills.Grizzly, sdk.skills.Shockwave, sdk.skills.PoisonCreeper], - precastSkills: [sdk.skills.Werebear, sdk.skills.HeartofWolverine, sdk.skills.Grizzly], - usefulStats: [], - wantedMerc: MercData[sdk.skills.Might], - stats: [ - ["strength", 127], ["dexterity", 136], ["vitality", "all"] - ], - skills: [ - [sdk.skills.Werebear, 20, false], - [sdk.skills.Lycanthropy, 20, false], - [sdk.skills.ShockWave, 1, false], - [sdk.skills.Maul, 20, false], - [sdk.skills.Grizzly, 20, false], - [sdk.skills.HeartofWolverine, 20, false], - [sdk.skills.PoisonCreeper, 20, false], - [sdk.skills.Shockwave, 20, false], - ], + module.exports = (function () { + const build = { + caster: false, + skillstab: sdk.skills.tabs.ShapeShifting, + wantedskills: [sdk.skills.Werebear, sdk.skills.Lycanthropy, sdk.skills.Maul], + usefulskills: [sdk.skills.HeartofWolverine, sdk.skills.Grizzly, sdk.skills.Shockwave, sdk.skills.PoisonCreeper], + precastSkills: [sdk.skills.Werebear, sdk.skills.HeartofWolverine, sdk.skills.Grizzly], + usefulStats: [], + wantedMerc: MercData[sdk.skills.Might], + stats: [ + ["strength", 127], ["dexterity", 136], ["vitality", "all"] + ], + skills: [ + [sdk.skills.Werebear, 20, false], + [sdk.skills.Lycanthropy, 20, false], + [sdk.skills.ShockWave, 1, false], + [sdk.skills.Maul, 20, false], + [sdk.skills.Grizzly, 20, false], + [sdk.skills.HeartofWolverine, 20, false], + [sdk.skills.PoisonCreeper, 20, false], + [sdk.skills.Shockwave, 20, false], + ], - charms: { - ResMf: { - max: 2, - have: [], - classid: sdk.items.SmallCharm, - stats: function (check) { - return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MagicBonus) === 7); - } - }, + charms: { + ResMf: { + max: 2, + have: [], + classid: sdk.items.SmallCharm, + stats: function (check) { + return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MagicBonus) === 7); + } + }, - Poison: { - max: 6, - have: [], - classid: sdk.items.SmallCharm, - stats: function (check) { - return (!check.unique && check.classid === this.classid - && ((check.getStat(sdk.stats.PoisonLength) * check.getStat(sdk.stats.PoisonMaxDamage)) / 256) >= 141); - } - }, + Poison: { + max: 6, + have: [], + classid: sdk.items.SmallCharm, + stats: function (check) { + return (!check.unique && check.classid === this.classid + && ((check.getStat(sdk.stats.PoisonLength) * check.getStat(sdk.stats.PoisonMaxDamage)) / 256) >= 141); + } + }, - Skiller: { - max: 2, - have: [], - classid: sdk.items.GrandCharm, - stats: function (check) { - return (!check.unique && check.classid === this.classid && check.getStat(sdk.stats.AddSkillTab, sdk.skills.tabs.ShapeShifting) === 1 - && check.getStat(sdk.stats.MaxHp) >= 40); - } - }, - }, + Skiller: { + max: 2, + have: [], + classid: sdk.items.GrandCharm, + stats: function (check) { + return (!check.unique && check.classid === this.classid && check.getStat(sdk.stats.AddSkillTab, sdk.skills.tabs.ShapeShifting) === 1 + && check.getStat(sdk.stats.MaxHp) >= 40); + } + }, + }, - AutoBuildTemplate: { - 1: { - Update: function () { - Config.BeltColumn = ["hp", "hp", "mp", "rv"]; - Config.AttackSkill = [ - sdk.skills.Maul, - sdk.skills.Maul, sdk.skills.Shockwave, - sdk.skills.Maul, sdk.skills.Shockwave, - -1, -1 - ]; - Config.LowManaSkill = [0, 0]; - Config.Wereform = "Werebear"; - Config.SummonAnimal = "Grizzly"; - Config.SummonSpirit = "Heart of Wolverine"; - } - }, - }, + AutoBuildTemplate: { + 1: { + Update: function () { + Config.BeltColumn = ["hp", "hp", "mp", "rv"]; + Config.AttackSkill = [ + sdk.skills.Maul, + sdk.skills.Maul, sdk.skills.Shockwave, + sdk.skills.Maul, sdk.skills.Shockwave, + -1, -1 + ]; + Config.LowManaSkill = [0, 0]; + Config.Wereform = "Werebear"; + Config.SummonAnimal = "Grizzly"; + Config.SummonSpirit = "Heart of Wolverine"; + } + }, + }, - respec: function () { - return me.haveAll([{ name: sdk.locale.items.Destruction }, { name: sdk.locale.items.Dragon, itemtype: sdk.items.type.Armor }]); - }, + respec: function () { + return me.haveAll([{ name: sdk.locale.items.Destruction }, { name: sdk.locale.items.Dragon, itemtype: sdk.items.type.Armor }]); + }, - active: function () { - return this.respec() && me.getSkill(sdk.skills.Maul, sdk.skills.subindex.HardPoints) === 20; - }, - }; + active: function () { + return this.respec() && me.getSkill(sdk.skills.Maul, sdk.skills.subindex.HardPoints) === 20; + }, + }; - let finalGear = [ - // Weapon - Destruction - "[name] == phaseblade && [flag] == runeword # [enhanceddamage] >= 350 && [itemdeadlystrike] == 20 && [itemcrushingblow] == 20 # [tier] == 110000", - // Shield - Sanctuary - "[name] == hyperion # [fhr] >= 20 && [enhanceddefense] >= 130 && [fireresist] >= 70 # [tier] == 110000", - "[name] == hyperion # [fhr] >= 20 && [enhanceddefense] >= 130 && [fireresist] >= 50 # [tier] == tierscore(item, 100000)", - // Helmet - Jalal's Mane - "[name] == totemicmask && [quality] == unique # [druidskills] == 2 && [shapeshiftingskilltab] == 2 # [tier] == tierscore(item, 110000)", - // Belt - Verdungo's - "[name] == mithrilcoil && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 90 # [tier] == tierscore(item, 110000)", - // Armor - Chains of Honor - "[type] == armor && [flag] == runeword && [flag] != ethereal # [fireresist] == 65 && [hpregen] == 7 # [tier] == 110000", - // Gloves - Dracul's Grasp - "[name] == vampirebonegloves && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 90 # [tier] == tierscore(item, 110000)", - // Boots - Gore Rider - "[name] == warboots && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 160 # [tier] == tierscore(item, 110000)", - // Amulet - Metalgrid - "[type] == amulet && [quality] == unique # [tohit] >= 400 && [coldresist] >= 25 # [tier] == tierscore(item, 110000)", - // Ring 1 - Ravenfrost - "[type] == ring && [quality] == unique # [tohit] >= 180 && [dexterity] >= 15 # [tier] == 100000", - // Ring 2 - Carrion Wind - "[name] == ring && [quality] == unique # [poisonresist] == 55 && [lifeleech] >= 6 # [tier] == tierscore(item, 110000)", - // Merc - // Final Armor - Fortitude - "[type] == armor && [flag] == runeword # [enhanceddefense] >= 200 && [enhanceddamage] >= 300 # [merctier] == 100000", - // Temporary Armor - Treachery - "[type] == armor && [flag] == runeword # [ias] == 45 && [coldresist] == 30 # [merctier] == 50000 + mercscore(item)", - // Final Helm - Eth Andy's - "[name] == demonhead && [quality] == unique && [flag] == ethereal # [strength] >= 25 && [enhanceddefense] >= 100 # [merctier] == 50000 + mercscore(item)", - // Temporary Helm - Andy's - "[name] == demonhead && [quality] == unique && [flag] != ethereal # [strength] >= 25 && [enhanceddefense] >= 100 # [merctier] == 40000 + mercscore(item)", - // Final Weapon - Infinity - "[type] == polearm && [flag] == runeword # [convictionaura] >= 13 # [merctier] == 100000 + mercscore(item)", - // Temporary Weapon - Reaper's Toll - "[name] == thresher && [quality] == unique # [enhanceddamage] >= 190 && [lifeleech] >= 11 # [merctier] == 50000 + mercscore(item)", - ]; + let finalGear = [ + // Weapon - Destruction + "[name] == phaseblade && [flag] == runeword # [enhanceddamage] >= 350 && [itemdeadlystrike] == 20 && [itemcrushingblow] == 20 # [tier] == 110000", + // Shield - Sanctuary + "[name] == hyperion # [fhr] >= 20 && [enhanceddefense] >= 130 && [fireresist] >= 70 # [tier] == 110000", + "[name] == hyperion # [fhr] >= 20 && [enhanceddefense] >= 130 && [fireresist] >= 50 # [tier] == tierscore(item, 100000)", + // Helmet - Jalal's Mane + "[name] == totemicmask && [quality] == unique # [druidskills] == 2 && [shapeshiftingskilltab] == 2 # [tier] == tierscore(item, 110000)", + // Belt - Verdungo's + "[name] == mithrilcoil && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 90 # [tier] == tierscore(item, 110000)", + // Armor - Chains of Honor + "[type] == armor && [flag] == runeword && [flag] != ethereal # [fireresist] == 65 && [hpregen] == 7 # [tier] == 110000", + // Gloves - Dracul's Grasp + "[name] == vampirebonegloves && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 90 # [tier] == tierscore(item, 110000)", + // Boots - Gore Rider + "[name] == warboots && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 160 # [tier] == tierscore(item, 110000)", + // Amulet - Metalgrid + "[type] == amulet && [quality] == unique # [tohit] >= 400 && [coldresist] >= 25 # [tier] == tierscore(item, 110000)", + // Ring 1 - Ravenfrost + "[type] == ring && [quality] == unique # [tohit] >= 180 && [dexterity] >= 15 # [tier] == 100000", + // Ring 2 - Carrion Wind + "[name] == ring && [quality] == unique # [poisonresist] == 55 && [lifeleech] >= 6 # [tier] == tierscore(item, 110000)", + // Merc + // Final Armor - Fortitude + "[type] == armor && [flag] == runeword # [enhanceddefense] >= 200 && [enhanceddamage] >= 300 # [merctier] == 100000", + // Temporary Armor - Treachery + "[type] == armor && [flag] == runeword # [ias] == 45 && [coldresist] == 30 # [merctier] == 50000 + mercscore(item)", + // Final Helm - Eth Andy's + "[name] == demonhead && [quality] == unique && [flag] == ethereal # [strength] >= 25 && [enhanceddefense] >= 100 # [merctier] == 50000 + mercscore(item)", + // Temporary Helm - Andy's + "[name] == demonhead && [quality] == unique && [flag] != ethereal # [strength] >= 25 && [enhanceddefense] >= 100 # [merctier] == 40000 + mercscore(item)", + // Final Weapon - Infinity + "[type] == polearm && [flag] == runeword # [convictionaura] >= 13 # [merctier] == 100000 + mercscore(item)", + // Temporary Weapon - Reaper's Toll + "[name] == thresher && [quality] == unique # [enhanceddamage] >= 190 && [lifeleech] >= 11 # [merctier] == 50000 + mercscore(item)", + ]; - NTIP.buildList(finalGear); - NTIP.buildFinalGear(finalGear); + NTIP.buildList(finalGear); + NTIP.buildFinalGear(finalGear); - return build; - })(); + return build; + })(); })(module); diff --git a/libs/SoloPlay/BuildFiles/druid/druid.WindBuild.js b/libs/SoloPlay/BuildFiles/druid/druid.WindBuild.js index ec0c6036..fc7c945b 100644 --- a/libs/SoloPlay/BuildFiles/druid/druid.WindBuild.js +++ b/libs/SoloPlay/BuildFiles/druid/druid.WindBuild.js @@ -7,129 +7,129 @@ (function (module) { - module.exports = (function () { - const build = { - caster: true, - skillstab: sdk.skills.tabs.Elemental, - wantedskills: [sdk.skills.Tornado, sdk.skills.Hurricane, sdk.skills.Twister], - usefulskills: [sdk.skills.CycloneArmor], - precastSkills: [sdk.skills.CycloneArmor], - usefulStats: [sdk.stats.PassiveColdPierce, sdk.stats.PassiveColdMastery], - wantedMerc: MercData[sdk.skills.Might], - stats: [ - ["dexterity", 35], ["strength", 156], ["vitality", "all"] - ], - skills: [ - [sdk.skills.Grizzly, 1], - [sdk.skills.Tornado, 20, false], - [sdk.skills.Hurricane, 20, false], - [sdk.skills.CycloneArmor, 20, false], - [sdk.skills.OakSage, 20, false], - [sdk.skills.Twister, 20], - [sdk.skills.Grizzly, 5], - ], + module.exports = (function () { + const build = { + caster: true, + skillstab: sdk.skills.tabs.Elemental, + wantedskills: [sdk.skills.Tornado, sdk.skills.Hurricane, sdk.skills.Twister], + usefulskills: [sdk.skills.CycloneArmor], + precastSkills: [sdk.skills.CycloneArmor], + usefulStats: [sdk.stats.PassiveColdPierce, sdk.stats.PassiveColdMastery], + wantedMerc: MercData[sdk.skills.Might], + stats: [ + ["dexterity", 35], ["strength", 156], ["vitality", "all"] + ], + skills: [ + [sdk.skills.Grizzly, 1], + [sdk.skills.Tornado, 20, false], + [sdk.skills.Hurricane, 20, false], + [sdk.skills.CycloneArmor, 20, false], + [sdk.skills.OakSage, 20, false], + [sdk.skills.Twister, 20], + [sdk.skills.Grizzly, 5], + ], - charms: { - ResLife: { - max: 3, - have: [], - classid: sdk.items.SmallCharm, - stats: function (check) { - return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MaxHp) === 20); - } - }, + charms: { + ResLife: { + max: 3, + have: [], + classid: sdk.items.SmallCharm, + stats: function (check) { + return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MaxHp) === 20); + } + }, - ResMf: { - max: 2, - have: [], - classid: sdk.items.SmallCharm, - stats: function (check) { - return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MagicBonus) === 7); - } - }, + ResMf: { + max: 2, + have: [], + classid: sdk.items.SmallCharm, + stats: function (check) { + return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MagicBonus) === 7); + } + }, - ResFHR: { - max: 1, - have: [], - classid: sdk.items.SmallCharm, - stats: function (check) { - return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.FHR) === 5); - } - }, + ResFHR: { + max: 1, + have: [], + classid: sdk.items.SmallCharm, + stats: function (check) { + return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.FHR) === 5); + } + }, - Skiller: { - max: 2, - have: [], - classid: sdk.items.GrandCharm, - stats: function (check) { - return (!check.unique && check.classid === this.classid && check.getStat(sdk.stats.AddSkillTab, sdk.skills.tabs.Elemental) === 1 - && check.getStat(sdk.stats.MaxHp) >= 40); - } - }, - }, + Skiller: { + max: 2, + have: [], + classid: sdk.items.GrandCharm, + stats: function (check) { + return (!check.unique && check.classid === this.classid && check.getStat(sdk.stats.AddSkillTab, sdk.skills.tabs.Elemental) === 1 + && check.getStat(sdk.stats.MaxHp) >= 40); + } + }, + }, - AutoBuildTemplate: { - 1: { - Update: function () { - Config.AttackSkill = [-1, sdk.skills.Tornado, -1, sdk.skills.Tornado, -1, sdk.skills.ArticBlast, -1]; - Config.LowManaSkill = [-1, -1]; - Config.SummonAnimal = "Grizzly"; - Config.SummonSpirit = "Oak Sage"; - } - }, - }, + AutoBuildTemplate: { + 1: { + Update: function () { + Config.AttackSkill = [-1, sdk.skills.Tornado, -1, sdk.skills.Tornado, -1, sdk.skills.ArticBlast, -1]; + Config.LowManaSkill = [-1, -1]; + Config.SummonAnimal = "Grizzly"; + Config.SummonSpirit = "Oak Sage"; + } + }, + }, - respec: function () { - return Check.haveItem("armor", "runeword", "Enigma"); - }, + respec: function () { + return Check.haveItem("armor", "runeword", "Enigma"); + }, - active: function () { - return this.respec() && me.getSkill(sdk.skills.Tornado, sdk.skills.subindex.HardPoints) === 20; - }, - }; + active: function () { + return this.respec() && me.getSkill(sdk.skills.Tornado, sdk.skills.subindex.HardPoints) === 20; + }, + }; - let finalGear = [ // autoequip final gear - // Weapon - HotO - "[type] == mace && [flag] == runeword # [itemallskills] == 3 # [tier] == 100000", - // Helmet - Jalal's mane - "[name] == totemicmask && [quality] == unique # [druidskills] == 2 && [shapeshiftingskilltab] == 2 # [tier] == tierscore(item, 110000)", - // Belt - Arach's - "[name] == spiderwebsash && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 90 # [tier] == tierscore(item, 100000)", - // Final Boots - Sandstorm Treks - "[name] == scarabshellboots && [quality] == unique # [strength]+[vitality] >= 20 # [tier] == tierscore(item, 100000)", - // Boots - War Traveler - "[name] == battleboots && [quality] == unique && [flag] != ethereal # [itemmagicbonus] >= 50 # [tier] == tierscore(item, 5000)", - // Armor - Enigma - "[type] == armor && [flag] != ethereal && [flag] == runeword # [itemallskills] == 2 # [tier] == 100000", - // Shield - Spirit - "[name] == monarch && [flag] != ethereal && [flag] == runeword # [fcr] >= 35 # [tier] == 100000", - // Final Gloves - Perfect 2x Upp'ed Magefist - "[name] == crusadergauntlets && [quality] == unique && [flag] != ethereal # [enhanceddefense] == 30 && [addfireskills] == 1 # [tier] == 110000", - // Gloves - 2x Upp'ed Magefist - "[name] == crusadergauntlets && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 20 && [addfireskills] == 1 # [tier] == tierscore(item, 100000)", - // Gloves - Upp'ed Magefist - "[name] == battlegauntlets && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 20 && [addfireskills] == 1 # [tier] == tierscore(item, 100000)", - // Gloves - Magefist - "[name] == lightgauntlets && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 20 && [addfireskills] == 1 # [tier] == tierscore(item, 100000)", - // Amulet - Maras - "[type] == amulet && [quality] == unique # [strength] == 5 && [coldresist] >= 30 # [tier] == tierscore(item, 100000)", - // Rings - SoJ - "[type] == ring && [quality] == unique # [itemmaxmanapercent] == 25 # [tier] == 100000", - // Switch - CTA - "[minimumsockets] >= 5 && [flag] == runeword # [plusskillbattleorders] >= 1 # [secondarytier] == 100000", - // Merc Armor - Fortitude - "[type] == armor && [flag] == runeword # [enhanceddefense] >= 200 && [enhanceddamage] >= 300 # [merctier] == 100000", - // Merc Final Helmet - Eth Andy's - "[name] == demonhead && [quality] == unique && [flag] == ethereal # [strength] >= 25 && [enhanceddefense] >= 100 # [merctier] == 50000 + mercscore(item)", - // Merc Helmet - Andy's - "[name] == demonhead && [quality] == unique && [flag] != ethereal # [strength] >= 25 && [enhanceddefense] >= 100 # [merctier] == 40000 + mercscore(item)", - // Merc Weapon - Reaper's Toll - "[name] == thresher && [quality] == unique # [enhanceddamage] >= 190 && [lifeleech] >= 11 # [merctier] == 100000 + mercscore(item)", - ]; + let finalGear = [ // autoequip final gear + // Weapon - HotO + "[type] == mace && [flag] == runeword # [itemallskills] == 3 # [tier] == 100000", + // Helmet - Jalal's mane + "[name] == totemicmask && [quality] == unique # [druidskills] == 2 && [shapeshiftingskilltab] == 2 # [tier] == tierscore(item, 110000)", + // Belt - Arach's + "[name] == spiderwebsash && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 90 # [tier] == tierscore(item, 100000)", + // Final Boots - Sandstorm Treks + "[name] == scarabshellboots && [quality] == unique # [strength]+[vitality] >= 20 # [tier] == tierscore(item, 100000)", + // Boots - War Traveler + "[name] == battleboots && [quality] == unique && [flag] != ethereal # [itemmagicbonus] >= 50 # [tier] == tierscore(item, 5000)", + // Armor - Enigma + "[type] == armor && [flag] != ethereal && [flag] == runeword # [itemallskills] == 2 # [tier] == 100000", + // Shield - Spirit + "[name] == monarch && [flag] != ethereal && [flag] == runeword # [fcr] >= 35 # [tier] == 100000", + // Final Gloves - Perfect 2x Upp'ed Magefist + "[name] == crusadergauntlets && [quality] == unique && [flag] != ethereal # [enhanceddefense] == 30 && [addfireskills] == 1 # [tier] == 110000", + // Gloves - 2x Upp'ed Magefist + "[name] == crusadergauntlets && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 20 && [addfireskills] == 1 # [tier] == tierscore(item, 100000)", + // Gloves - Upp'ed Magefist + "[name] == battlegauntlets && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 20 && [addfireskills] == 1 # [tier] == tierscore(item, 100000)", + // Gloves - Magefist + "[name] == lightgauntlets && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 20 && [addfireskills] == 1 # [tier] == tierscore(item, 100000)", + // Amulet - Maras + "[type] == amulet && [quality] == unique # [strength] == 5 && [coldresist] >= 30 # [tier] == tierscore(item, 100000)", + // Rings - SoJ + "[type] == ring && [quality] == unique # [itemmaxmanapercent] == 25 # [tier] == 100000", + // Switch - CTA + "[minimumsockets] >= 5 && [flag] == runeword # [plusskillbattleorders] >= 1 # [secondarytier] == 100000", + // Merc Armor - Fortitude + "[type] == armor && [flag] == runeword # [enhanceddefense] >= 200 && [enhanceddamage] >= 300 # [merctier] == 100000", + // Merc Final Helmet - Eth Andy's + "[name] == demonhead && [quality] == unique && [flag] == ethereal # [strength] >= 25 && [enhanceddefense] >= 100 # [merctier] == 50000 + mercscore(item)", + // Merc Helmet - Andy's + "[name] == demonhead && [quality] == unique && [flag] != ethereal # [strength] >= 25 && [enhanceddefense] >= 100 # [merctier] == 40000 + mercscore(item)", + // Merc Weapon - Reaper's Toll + "[name] == thresher && [quality] == unique # [enhanceddamage] >= 190 && [lifeleech] >= 11 # [merctier] == 100000 + mercscore(item)", + ]; - NTIP.buildList(finalGear); - NTIP.buildFinalGear(finalGear); + NTIP.buildList(finalGear); + NTIP.buildFinalGear(finalGear); - return build; - })(); + return build; + })(); })(module); diff --git a/libs/SoloPlay/BuildFiles/druid/druid.WolfBuild.js b/libs/SoloPlay/BuildFiles/druid/druid.WolfBuild.js index 15035f67..095ceee7 100644 --- a/libs/SoloPlay/BuildFiles/druid/druid.WolfBuild.js +++ b/libs/SoloPlay/BuildFiles/druid/druid.WolfBuild.js @@ -7,121 +7,121 @@ (function (module) { - module.exports = (function () { - const build = { - caster: false, - skillstab: sdk.skills.tabs.ShapeShifting, - wantedskills: [sdk.skills.Werewolf, sdk.skills.Lycanthropy, sdk.skills.Fury], - usefulskills: [sdk.skills.HeartofWolverine, sdk.skills.Grizzly], - precastSkills: [sdk.skills.Werewolf, sdk.skills.HeartofWolverine, sdk.skills.Grizzly], - wantedMerc: MercData[sdk.skills.Might], - stats: [ - ["strength", 103], ["dexterity", 35], ["vitality", "all"] - ], - skills: [ - [sdk.skills.Werewolf, 20, true], - [sdk.skills.Lycanthropy, 20, true], - [sdk.skills.Fury, 20, true], - [sdk.skills.Grizzly, 1, false], - [sdk.skills.HeartofWolverine, 20, true], - [sdk.skills.FeralRage, 10, false], - [sdk.skills.Grizzly, 15], - ], + module.exports = (function () { + const build = { + caster: false, + skillstab: sdk.skills.tabs.ShapeShifting, + wantedskills: [sdk.skills.Werewolf, sdk.skills.Lycanthropy, sdk.skills.Fury], + usefulskills: [sdk.skills.HeartofWolverine, sdk.skills.Grizzly], + precastSkills: [sdk.skills.Werewolf, sdk.skills.HeartofWolverine, sdk.skills.Grizzly], + wantedMerc: MercData[sdk.skills.Might], + stats: [ + ["strength", 103], ["dexterity", 35], ["vitality", "all"] + ], + skills: [ + [sdk.skills.Werewolf, 20, true], + [sdk.skills.Lycanthropy, 20, true], + [sdk.skills.Fury, 20, true], + [sdk.skills.Grizzly, 1, false], + [sdk.skills.HeartofWolverine, 20, true], + [sdk.skills.FeralRage, 10, false], + [sdk.skills.Grizzly, 15], + ], - charms: { - ResLife: { - max: 3, - have: [], - classid: sdk.items.SmallCharm, - stats: function (check) { - return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MaxHp) === 20); - } - }, + charms: { + ResLife: { + max: 3, + have: [], + classid: sdk.items.SmallCharm, + stats: function (check) { + return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MaxHp) === 20); + } + }, - ResMf: { - max: 2, - have: [], - classid: sdk.items.SmallCharm, - stats: function (check) { - return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MagicBonus) === 7); - } - }, + ResMf: { + max: 2, + have: [], + classid: sdk.items.SmallCharm, + stats: function (check) { + return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MagicBonus) === 7); + } + }, - ResFHR: { - max: 1, - have: [], - classid: sdk.items.SmallCharm, - stats: function (check) { - return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.FHR) === 5); - } - }, + ResFHR: { + max: 1, + have: [], + classid: sdk.items.SmallCharm, + stats: function (check) { + return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.FHR) === 5); + } + }, - Skiller: { - max: 2, - have: [], - classid: sdk.items.GrandCharm, - stats: function (check) { - return (!check.unique && check.classid === this.classid && check.getStat(sdk.stats.AddSkillTab, sdk.skills.tabs.ShapeShifting) === 1 - && check.getStat(sdk.stats.MaxHp) >= 40); - } - }, - }, + Skiller: { + max: 2, + have: [], + classid: sdk.items.GrandCharm, + stats: function (check) { + return (!check.unique && check.classid === this.classid && check.getStat(sdk.stats.AddSkillTab, sdk.skills.tabs.ShapeShifting) === 1 + && check.getStat(sdk.stats.MaxHp) >= 40); + } + }, + }, - AutoBuildTemplate: { - 1: { - Update: function () { - Config.AttackSkill = [-1, sdk.skills.Fury, sdk.skills.FeralRage, sdk.skills.Fury, sdk.skills.FeralRage, sdk.skills.Rabies, -1]; - Config.LowManaSkill = [0, 0]; - Config.Wereform = "Werewolf"; - Config.SummonAnimal = "Grizzly"; - Config.SummonSpirit = "Heart of Wolverine"; - } - }, - }, + AutoBuildTemplate: { + 1: { + Update: function () { + Config.AttackSkill = [-1, sdk.skills.Fury, sdk.skills.FeralRage, sdk.skills.Fury, sdk.skills.FeralRage, sdk.skills.Rabies, -1]; + Config.LowManaSkill = [0, 0]; + Config.Wereform = "Werewolf"; + Config.SummonAnimal = "Grizzly"; + Config.SummonSpirit = "Heart of Wolverine"; + } + }, + }, - respec: function () { - return Check.haveItem("stalagmite", "unique", "Ribcracker") && Check.haveItem("armor", "runeword", "Chains of Honor"); - }, + respec: function () { + return Check.haveItem("stalagmite", "unique", "Ribcracker") && Check.haveItem("armor", "runeword", "Chains of Honor"); + }, - active: function () { - return this.respec() && me.getSkill(sdk.skills.Werewolf, sdk.skills.subindex.HardPoints) === 20; - }, - }; + active: function () { + return this.respec() && me.getSkill(sdk.skills.Werewolf, sdk.skills.subindex.HardPoints) === 20; + }, + }; - let finalGear = [ // autoequip final gear - // Weapon - Upp'ed Ribcracker - "[name] == stalagmite && [quality] == unique # [enhanceddamage] >= 300 && [ias] >= 50 # [tier] == 110000", - // Helmet - Jalal's Mane - "[name] == totemicmask && [quality] == unique # [druidskills] == 2 && [shapeshiftingskilltab] == 2 # [tier] == tierscore(item, 110000)", - // Belt - Verdungo's - "[name] == mithrilcoil && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 90 # [tier] == tierscore(item, 110000)", - // Armor - CoH - "[type] == armor && [flag] == runeword && [flag] != ethereal # [fireresist] == 65 && [hpregen] == 7 # [tier] == 110000", - // Gloves - Dracul's - "[name] == vampirebonegloves && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 90 # [tier] == tierscore(item, 110000)", - // Amulet - Maras - "[type] == amulet && [quality] == unique # [strength] == 5 && [coldresist] >= 30 # [tier] == tierscore(item, 110000)", - // Final Rings - Perfect Wisp & Bul-Kathos' Wedding Band - "[type] == ring && [quality] == unique # [itemabsorblightpercent] == 20 # [tier] == tierscore(item, 110000)", - "[type] == ring && [quality] == unique # [maxstamina] == 50 && [lifeleech] == 5 # [tier] == tierscore(item, 110000)", - // Rings - Wisp & Bul-Kathos' Wedding Band - "[type] == ring && [quality] == unique # [itemabsorblightpercent] >= 10 # [tier] == tierscore(item, 110000)", - "[type] == ring && [quality] == unique # [maxstamina] == 50 && [lifeleech] >= 3 # [tier] == tierscore(item, 110000)", - // Merc Final Armor - Fortitude - "[type] == armor && [flag] == runeword # [enhanceddefense] >= 200 && [enhanceddamage] >= 300 # [merctier] == 100000", - // Merc Armor - Treachery - "[type] == armor && [flag] == runeword # [ias] == 45 && [coldresist] == 30 # [merctier] == 50000 + mercscore(item)", - // Merc Final Helmet - Eth Andy's - "[name] == demonhead && [quality] == unique && [flag] == ethereal # [strength] >= 25 && [enhanceddefense] >= 100 # [merctier] == 50000 + mercscore(item)", - // Merc Helmet - Andy's - "[name] == demonhead && [quality] == unique && [flag] != ethereal # [strength] >= 25 && [enhanceddefense] >= 100 # [merctier] == 40000 + mercscore(item)", - // Merc Weapon - Reaper's Toll - "[name] == thresher && [quality] == unique # [enhanceddamage] >= 190 && [lifeleech] >= 11 # [merctier] == 100000 + mercscore(item)", - ]; + let finalGear = [ // autoequip final gear + // Weapon - Upp'ed Ribcracker + "[name] == stalagmite && [quality] == unique # [enhanceddamage] >= 300 && [ias] >= 50 # [tier] == 110000", + // Helmet - Jalal's Mane + "[name] == totemicmask && [quality] == unique # [druidskills] == 2 && [shapeshiftingskilltab] == 2 # [tier] == tierscore(item, 110000)", + // Belt - Verdungo's + "[name] == mithrilcoil && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 90 # [tier] == tierscore(item, 110000)", + // Armor - CoH + "[type] == armor && [flag] == runeword && [flag] != ethereal # [fireresist] == 65 && [hpregen] == 7 # [tier] == 110000", + // Gloves - Dracul's + "[name] == vampirebonegloves && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 90 # [tier] == tierscore(item, 110000)", + // Amulet - Maras + "[type] == amulet && [quality] == unique # [strength] == 5 && [coldresist] >= 30 # [tier] == tierscore(item, 110000)", + // Final Rings - Perfect Wisp & Bul-Kathos' Wedding Band + "[type] == ring && [quality] == unique # [itemabsorblightpercent] == 20 # [tier] == tierscore(item, 110000)", + "[type] == ring && [quality] == unique # [maxstamina] == 50 && [lifeleech] == 5 # [tier] == tierscore(item, 110000)", + // Rings - Wisp & Bul-Kathos' Wedding Band + "[type] == ring && [quality] == unique # [itemabsorblightpercent] >= 10 # [tier] == tierscore(item, 110000)", + "[type] == ring && [quality] == unique # [maxstamina] == 50 && [lifeleech] >= 3 # [tier] == tierscore(item, 110000)", + // Merc Final Armor - Fortitude + "[type] == armor && [flag] == runeword # [enhanceddefense] >= 200 && [enhanceddamage] >= 300 # [merctier] == 100000", + // Merc Armor - Treachery + "[type] == armor && [flag] == runeword # [ias] == 45 && [coldresist] == 30 # [merctier] == 50000 + mercscore(item)", + // Merc Final Helmet - Eth Andy's + "[name] == demonhead && [quality] == unique && [flag] == ethereal # [strength] >= 25 && [enhanceddefense] >= 100 # [merctier] == 50000 + mercscore(item)", + // Merc Helmet - Andy's + "[name] == demonhead && [quality] == unique && [flag] != ethereal # [strength] >= 25 && [enhanceddefense] >= 100 # [merctier] == 40000 + mercscore(item)", + // Merc Weapon - Reaper's Toll + "[name] == thresher && [quality] == unique # [enhanceddamage] >= 190 && [lifeleech] >= 11 # [merctier] == 100000 + mercscore(item)", + ]; - NTIP.buildList(finalGear); - NTIP.buildFinalGear(finalGear); + NTIP.buildList(finalGear); + NTIP.buildFinalGear(finalGear); - return build; - })(); + return build; + })(); })(module); diff --git a/libs/SoloPlay/BuildFiles/druid/druid.js b/libs/SoloPlay/BuildFiles/druid/druid.js index 487c4d41..dca84ec8 100644 --- a/libs/SoloPlay/BuildFiles/druid/druid.js +++ b/libs/SoloPlay/BuildFiles/druid/druid.js @@ -6,37 +6,37 @@ */ const CharInfo = { - respecOne: 26, - respecTwo: 0, - levelCap: (function() { - const currentDiff = sdk.difficulty.nameOf(me.diff); - const softcoreMode = { - "Normal": 33, - "Nightmare": 73, - "Hell": 100, - }; - const hardcoreMode = { - "Normal": 36, - "Nightmare": 73, - "Hell": 100, - }; + respecOne: 26, + respecTwo: 0, + levelCap: (function() { + const currentDiff = sdk.difficulty.nameOf(me.diff); + const softcoreMode = { + "Normal": 33, + "Nightmare": 73, + "Hell": 100, + }; + const hardcoreMode = { + "Normal": 36, + "Nightmare": 73, + "Hell": 100, + }; - return me.softcore ? softcoreMode[currentDiff] : hardcoreMode[currentDiff]; - })(), + return me.softcore ? softcoreMode[currentDiff] : hardcoreMode[currentDiff]; + })(), - getActiveBuild: function () { - const nSkills = me.getStat(sdk.stats.NewSkills); - const currLevel = me.charlvl; - const justRepeced = (nSkills >= currLevel); + getActiveBuild: function () { + const nSkills = me.getStat(sdk.stats.NewSkills); + const currLevel = me.charlvl; + const justRepeced = (nSkills >= currLevel); - switch (true) { - case currLevel < this.respecOne && !me.checkSkill(sdk.skills.Tornado, sdk.skills.subindex.HardPoints): - return "Start"; - case Check.finalBuild().respec() && justRepeced: - case Check.finalBuild().active(): - return SetUp.finalBuild; - default: - return "Leveling"; - } - }, + switch (true) { + case currLevel < this.respecOne && !me.checkSkill(sdk.skills.Tornado, sdk.skills.subindex.HardPoints): + return "Start"; + case Check.finalBuild().respec() && justRepeced: + case Check.finalBuild().active(): + return SetUp.finalBuild; + default: + return "Leveling"; + } + }, }; diff --git a/libs/SoloPlay/BuildFiles/necromancer/necromancer.BoneBuild.js b/libs/SoloPlay/BuildFiles/necromancer/necromancer.BoneBuild.js index 5a1c484a..edda36ac 100644 --- a/libs/SoloPlay/BuildFiles/necromancer/necromancer.BoneBuild.js +++ b/libs/SoloPlay/BuildFiles/necromancer/necromancer.BoneBuild.js @@ -7,160 +7,160 @@ (function (module) { - module.exports = (function () { - const build = { - caster: true, - skillstab: sdk.skills.tabs.PoisonandBone, - wantedskills: [sdk.skills.BoneSpirit, sdk.skills.BoneSpear, sdk.skills.Teeth], - usefulskills: [sdk.skills.AmplifyDamage, sdk.skills.BoneArmor, sdk.skills.Decrepify, sdk.skills.BoneWall, sdk.skills.BonePrison], - precastSkills: [sdk.skills.BoneArmor], - wantedMerc: MercData[sdk.skills.Might], - skills: [ - [sdk.skills.BoneSpirit, 1], - [sdk.skills.BonePrison, 1], - [sdk.skills.SummonResist, 1], - [sdk.skills.Decrepify, 1], - [sdk.skills.Attract, 1], - [sdk.skills.BoneSpear, 20, false], - [sdk.skills.BonePrison, 20, false], - [sdk.skills.BoneWall, 20, false], - [sdk.skills.BoneSpirit, 20, false], - [sdk.skills.Teeth, 20, false], - ], - stats: [], + module.exports = (function () { + const build = { + caster: true, + skillstab: sdk.skills.tabs.PoisonandBone, + wantedskills: [sdk.skills.BoneSpirit, sdk.skills.BoneSpear, sdk.skills.Teeth], + usefulskills: [sdk.skills.AmplifyDamage, sdk.skills.BoneArmor, sdk.skills.Decrepify, sdk.skills.BoneWall, sdk.skills.BonePrison], + precastSkills: [sdk.skills.BoneArmor], + wantedMerc: MercData[sdk.skills.Might], + skills: [ + [sdk.skills.BoneSpirit, 1], + [sdk.skills.BonePrison, 1], + [sdk.skills.SummonResist, 1], + [sdk.skills.Decrepify, 1], + [sdk.skills.Attract, 1], + [sdk.skills.BoneSpear, 20, false], + [sdk.skills.BonePrison, 20, false], + [sdk.skills.BoneWall, 20, false], + [sdk.skills.BoneSpirit, 20, false], + [sdk.skills.Teeth, 20, false], + ], + stats: [], - charms: { - ResLife: { - max: 4, - have: [], - classid: sdk.items.SmallCharm, - stats: function (check) { - return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MaxHp) === 20); - } - }, + charms: { + ResLife: { + max: 4, + have: [], + classid: sdk.items.SmallCharm, + stats: function (check) { + return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MaxHp) === 20); + } + }, - ResMf: { - max: 3, - have: [], - classid: sdk.items.SmallCharm, - stats: function (check) { - return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MagicBonus) === 7); - } - }, + ResMf: { + max: 3, + have: [], + classid: sdk.items.SmallCharm, + stats: function (check) { + return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MagicBonus) === 7); + } + }, - ResFHR: { - max: 1, - have: [], - classid: sdk.items.SmallCharm, - stats: function (check) { - return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.FHR) === 5); - } - }, + ResFHR: { + max: 1, + have: [], + classid: sdk.items.SmallCharm, + stats: function (check) { + return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.FHR) === 5); + } + }, - Skiller: { - max: 2, - have: [], - classid: sdk.items.GrandCharm, - stats: function (check) { - return (!check.unique && check.classid === this.classid && check.getStat(sdk.stats.AddSkillTab, sdk.skills.tabs.PoisonandBone) === 1 - && check.getStat(sdk.stats.MaxHp) >= 40); - } - }, - }, + Skiller: { + max: 2, + have: [], + classid: sdk.items.GrandCharm, + stats: function (check) { + return (!check.unique && check.classid === this.classid && check.getStat(sdk.stats.AddSkillTab, sdk.skills.tabs.PoisonandBone) === 1 + && check.getStat(sdk.stats.MaxHp) >= 40); + } + }, + }, - AutoBuildTemplate: { - 1: { - Update: function () { - Config.AttackSkill = [-1, sdk.skills.BoneSpear, -1, sdk.skills.BoneSpear, -1, -1, -1]; - Config.ExplodeCorpses = sdk.skills.CorpseExplosion; - Config.Golem = "Clay"; - } - }, - }, + AutoBuildTemplate: { + 1: { + Update: function () { + Config.AttackSkill = [-1, sdk.skills.BoneSpear, -1, sdk.skills.BoneSpear, -1, -1, -1]; + Config.ExplodeCorpses = sdk.skills.CorpseExplosion; + Config.Golem = "Clay"; + } + }, + }, - respec: function () { - if (me.classic) { - return me.charlvl >= 75 && me.diablo; - } else { - return Check.haveItem("armor", "runeword", "Enigma"); - } - }, + respec: function () { + if (me.classic) { + return me.charlvl >= 75 && me.diablo; + } else { + return Check.haveItem("armor", "runeword", "Enigma"); + } + }, - active: function () { - return this.respec() && me.getSkill(sdk.skills.BoneSpear, sdk.skills.subindex.HardPoints) === 20; - }, - }; - - build.stats = me.classic - ? [ - ["dexterity", 51], ["strength", 80], ["energy", 100], ["vitality", "all"] - ] - : [ - ["strength", 48], ["dexterity", 35], ["vitality", 165], - ["strength", 61], ["vitality", 252], ["strength", 156], ["vitality", "all"] - ]; - - let finalGear = me.classic - ? [ - // Weapon - Spectral Shard - "[name] == blade && [quality] == unique # [fcr] == 20 && [allres] == 10 # [tier] == 100000", - // Helm - Tarnhelm - "[name] == skullcap && [quality] == unique # [itemallskills] == 1 && [itemmagicbonus] >= 25 # [tier] == tierscore(item, 100000)", - // Shield - "[type] == shield && [quality] >= magic # [necromancerskills] == 2 && [allres] >= 16 # [tier] == tierscore(item, 100000)", - // Rings - SoJ - "[type] == ring && [quality] == unique # [itemmaxmanapercent] == 25 # [tier] == 100000", - // Amulet - "[type] == amulet && [quality] >= magic # [necromancerskills] == 2 && [fcr] == 10 # [tier] == tierscore(item, 100000)", - // Boots - "[type] == boots && [quality] >= magic # [frw] >= 20 && [fhr] == 10 && [coldresist]+[lightresist] >= 10 # [tier] == tierscore(item, 100000)", - // Belt - "[type] == belt && [quality] >= magic # [fhr] >= 20 && [maxhp] >= 40 && [fireresist]+[lightresist] >= 20 # [tier] == tierscore(item, 100000)", - // Gloves - Magefist - "[name] == lightgauntlets && [quality] == unique # [fcr] >= 20 && [addfireskills] == 1 # [tier] == tierscore(item, 100000)", - ] : [ - // Weapon - "([type] == wand || [type] == sword && ([quality] >= magic || [flag] == runeword) || [type] == knife && [quality] >= magic) && [flag] != ethereal # [secondarymindamage] == 0 && [itemchargedskill] >= 0 # [tier] == tierscore(item)", - // Helmet - Harlequin's Crest - "[name] == shako && [quality] == unique && [flag] != ethereal # [damageresist] == 10 # [tier] == tierscore(item, 100000)", - // Belt - Arach's - "[name] == spiderwebsash && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 90 # [tier] == tierscore(item, 100000)", - // Final Boots - Sandstorm Treks - "[name] == scarabshellboots && [quality] == unique # [strength]+[vitality] >= 20 # [tier] == tierscore(item, 100000)", - // Boots - War Traveler - "[name] == battleboots && [quality] == unique && [flag] != ethereal # [itemmagicbonus] >= 50 # [tier] == tierscore(item, 5000)", - // Armor - Enigma - "[type] == armor && [flag] != ethereal && [flag] == runeword # [itemallskills] == 2 # [tier] == 100000", - // Shield - "([type] == shield && ([quality] >= magic || [flag] == runeword) || [type] == voodooheads) && [flag] != ethereal # [itemchargedskill] >= 0 # [tier] == tierscore(item)", - // Final Gloves - Perfect 2x Upp'ed Magefist - "[name] == crusadergauntlets && [quality] == unique && [flag] != ethereal # [enhanceddefense] == 30 && [addfireskills] == 1 # [tier] == 110000", - // Gloves - 2x Upp'ed Magefist - "[name] == crusadergauntlets && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 20 && [addfireskills] == 1 # [tier] == tierscore(item, 100000)", - // Gloves - Upp'ed Magefist - "[name] == battlegauntlets && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 20 && [addfireskills] == 1 # [tier] == tierscore(item, 100000)", - // Gloves - Magefist - "[name] == lightgauntlets && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 20 && [addfireskills] == 1 # [tier] == tierscore(item, 100000)", - // Amulet - Maras - "[type] == amulet && [quality] == unique # [strength] == 5 && [coldresist] >= 30 # [tier] == tierscore(item, 100000)", - // Rings - Dwarf Star & SoJ - "[type] == ring && [quality] == unique # [maxhp] >= 40 && [magicdamagereduction] >= 12 # [tier] == 99000", - "[type] == ring && [quality] == unique # [itemmaxmanapercent] == 25 # [tier] == 100000", - // Switch - CTA - "[minimumsockets] >= 5 && [flag] == runeword # [plusskillbattleorders] >= 1 # [secondarytier] == 100000", - // Switch - Spirit - "[name] == monarch && [flag] == runeword # [fcr] >= 25 && [maxmana] >= 89 # [secondarytier] == 110000", - // Merc Armor - Fortitude - "[type] == armor && [flag] == runeword # [enhanceddefense] >= 200 && [enhanceddamage] >= 300 # [merctier] == 100000", - // Merc Final Helmet - Eth Andy's - "[name] == demonhead && [quality] == unique && [flag] == ethereal # [strength] >= 25 && [enhanceddefense] >= 100 # [merctier] == 50000 + mercscore(item)", - // Merc Helmet - Andy's - "[name] == demonhead && [quality] == unique && [flag] != ethereal # [strength] >= 25 && [enhanceddefense] >= 100 # [merctier] == 40000 + mercscore(item)", - ]; + active: function () { + return this.respec() && me.getSkill(sdk.skills.BoneSpear, sdk.skills.subindex.HardPoints) === 20; + }, + }; + + build.stats = me.classic + ? [ + ["dexterity", 51], ["strength", 80], ["energy", 100], ["vitality", "all"] + ] + : [ + ["strength", 48], ["dexterity", 35], ["vitality", 165], + ["strength", 61], ["vitality", 252], ["strength", 156], ["vitality", "all"] + ]; + + let finalGear = me.classic + ? [ + // Weapon - Spectral Shard + "[name] == blade && [quality] == unique # [fcr] == 20 && [allres] == 10 # [tier] == 100000", + // Helm - Tarnhelm + "[name] == skullcap && [quality] == unique # [itemallskills] == 1 && [itemmagicbonus] >= 25 # [tier] == tierscore(item, 100000)", + // Shield + "[type] == shield && [quality] >= magic # [necromancerskills] == 2 && [allres] >= 16 # [tier] == tierscore(item, 100000)", + // Rings - SoJ + "[type] == ring && [quality] == unique # [itemmaxmanapercent] == 25 # [tier] == 100000", + // Amulet + "[type] == amulet && [quality] >= magic # [necromancerskills] == 2 && [fcr] == 10 # [tier] == tierscore(item, 100000)", + // Boots + "[type] == boots && [quality] >= magic # [frw] >= 20 && [fhr] == 10 && [coldresist]+[lightresist] >= 10 # [tier] == tierscore(item, 100000)", + // Belt + "[type] == belt && [quality] >= magic # [fhr] >= 20 && [maxhp] >= 40 && [fireresist]+[lightresist] >= 20 # [tier] == tierscore(item, 100000)", + // Gloves - Magefist + "[name] == lightgauntlets && [quality] == unique # [fcr] >= 20 && [addfireskills] == 1 # [tier] == tierscore(item, 100000)", + ] : [ + // Weapon + "([type] == wand || [type] == sword && ([quality] >= magic || [flag] == runeword) || [type] == knife && [quality] >= magic) && [flag] != ethereal # [secondarymindamage] == 0 && [itemchargedskill] >= 0 # [tier] == tierscore(item)", + // Helmet - Harlequin's Crest + "[name] == shako && [quality] == unique && [flag] != ethereal # [damageresist] == 10 # [tier] == tierscore(item, 100000)", + // Belt - Arach's + "[name] == spiderwebsash && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 90 # [tier] == tierscore(item, 100000)", + // Final Boots - Sandstorm Treks + "[name] == scarabshellboots && [quality] == unique # [strength]+[vitality] >= 20 # [tier] == tierscore(item, 100000)", + // Boots - War Traveler + "[name] == battleboots && [quality] == unique && [flag] != ethereal # [itemmagicbonus] >= 50 # [tier] == tierscore(item, 5000)", + // Armor - Enigma + "[type] == armor && [flag] != ethereal && [flag] == runeword # [itemallskills] == 2 # [tier] == 100000", + // Shield + "([type] == shield && ([quality] >= magic || [flag] == runeword) || [type] == voodooheads) && [flag] != ethereal # [itemchargedskill] >= 0 # [tier] == tierscore(item)", + // Final Gloves - Perfect 2x Upp'ed Magefist + "[name] == crusadergauntlets && [quality] == unique && [flag] != ethereal # [enhanceddefense] == 30 && [addfireskills] == 1 # [tier] == 110000", + // Gloves - 2x Upp'ed Magefist + "[name] == crusadergauntlets && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 20 && [addfireskills] == 1 # [tier] == tierscore(item, 100000)", + // Gloves - Upp'ed Magefist + "[name] == battlegauntlets && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 20 && [addfireskills] == 1 # [tier] == tierscore(item, 100000)", + // Gloves - Magefist + "[name] == lightgauntlets && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 20 && [addfireskills] == 1 # [tier] == tierscore(item, 100000)", + // Amulet - Maras + "[type] == amulet && [quality] == unique # [strength] == 5 && [coldresist] >= 30 # [tier] == tierscore(item, 100000)", + // Rings - Dwarf Star & SoJ + "[type] == ring && [quality] == unique # [maxhp] >= 40 && [magicdamagereduction] >= 12 # [tier] == 99000", + "[type] == ring && [quality] == unique # [itemmaxmanapercent] == 25 # [tier] == 100000", + // Switch - CTA + "[minimumsockets] >= 5 && [flag] == runeword # [plusskillbattleorders] >= 1 # [secondarytier] == 100000", + // Switch - Spirit + "[name] == monarch && [flag] == runeword # [fcr] >= 25 && [maxmana] >= 89 # [secondarytier] == 110000", + // Merc Armor - Fortitude + "[type] == armor && [flag] == runeword # [enhanceddefense] >= 200 && [enhanceddamage] >= 300 # [merctier] == 100000", + // Merc Final Helmet - Eth Andy's + "[name] == demonhead && [quality] == unique && [flag] == ethereal # [strength] >= 25 && [enhanceddefense] >= 100 # [merctier] == 50000 + mercscore(item)", + // Merc Helmet - Andy's + "[name] == demonhead && [quality] == unique && [flag] != ethereal # [strength] >= 25 && [enhanceddefense] >= 100 # [merctier] == 40000 + mercscore(item)", + ]; - NTIP.buildList(finalGear); - NTIP.buildFinalGear(finalGear); + NTIP.buildList(finalGear); + NTIP.buildFinalGear(finalGear); - return build; - })(); + return build; + })(); })(module); diff --git a/libs/SoloPlay/BuildFiles/necromancer/necromancer.LevelingBuild.js b/libs/SoloPlay/BuildFiles/necromancer/necromancer.LevelingBuild.js index 693b4efc..79488587 100644 --- a/libs/SoloPlay/BuildFiles/necromancer/necromancer.LevelingBuild.js +++ b/libs/SoloPlay/BuildFiles/necromancer/necromancer.LevelingBuild.js @@ -8,60 +8,60 @@ // TODO: test summonnovamancer for classic (wouldn't be able to farcast diablo though :( ) (function (module) { - module.exports = (function () { - const build = { - caster: true, - skillstab: sdk.skills.tabs.PoisonandBone, - wantedskills: [sdk.skills.CorpseExplosion, sdk.skills.BoneSpear], - usefulskills: [sdk.skills.AmplifyDamage, sdk.skills.BoneArmor, sdk.skills.Decrepify, sdk.skills.BoneWall, sdk.skills.BonePrison, sdk.skills.BoneSpirit, sdk.skills.Teeth], - wantedMerc: MercData[sdk.skills.Might], - skills: [ - // Total skills at respec = 29 - [sdk.skills.Decrepify, 1], // points left 25 - [sdk.skills.SummonResist, 1], // points left 22 - [sdk.skills.BonePrison, 1], // points left 16 - [sdk.skills.Attract, 1], // points left 13 - [sdk.skills.BoneSpear, 9], // points left 5 - [sdk.skills.BonePrison, 3], // points left 3 - [sdk.skills.BoneSpear, 20, false], - [sdk.skills.BoneSpirit, 1, false], - [sdk.skills.BonePrison, 20, false], - [sdk.skills.CorpseExplosion, 20, false], - [sdk.skills.BoneWall, 20, false], - [sdk.skills.Teeth, 20, false], - ], - stats: [], + module.exports = (function () { + const build = { + caster: true, + skillstab: sdk.skills.tabs.PoisonandBone, + wantedskills: [sdk.skills.CorpseExplosion, sdk.skills.BoneSpear], + usefulskills: [sdk.skills.AmplifyDamage, sdk.skills.BoneArmor, sdk.skills.Decrepify, sdk.skills.BoneWall, sdk.skills.BonePrison, sdk.skills.BoneSpirit, sdk.skills.Teeth], + wantedMerc: MercData[sdk.skills.Might], + skills: [ + // Total skills at respec = 29 + [sdk.skills.Decrepify, 1], // points left 25 + [sdk.skills.SummonResist, 1], // points left 22 + [sdk.skills.BonePrison, 1], // points left 16 + [sdk.skills.Attract, 1], // points left 13 + [sdk.skills.BoneSpear, 9], // points left 5 + [sdk.skills.BonePrison, 3], // points left 3 + [sdk.skills.BoneSpear, 20, false], + [sdk.skills.BoneSpirit, 1, false], + [sdk.skills.BonePrison, 20, false], + [sdk.skills.CorpseExplosion, 20, false], + [sdk.skills.BoneWall, 20, false], + [sdk.skills.Teeth, 20, false], + ], + stats: [], - active: function () { - return (me.charlvl > CharInfo.respecOne && me.charlvl > CharInfo.respecTwo && me.checkSkill(sdk.skills.BonePrison, sdk.skills.subindex.HardPoints) && !Check.finalBuild().active()); - }, + active: function () { + return (me.charlvl > CharInfo.respecOne && me.charlvl > CharInfo.respecTwo && me.checkSkill(sdk.skills.BonePrison, sdk.skills.subindex.HardPoints) && !Check.finalBuild().active()); + }, - AutoBuildTemplate: { - 1: { - Update: function () { - Config.TownHP = me.hardcore ? 0 : 35; - Config.AttackSkill = [-1, sdk.skills.BoneSpear, -1, sdk.skills.BoneSpear, -1, -1, -1]; - Config.LowManaSkill = [sdk.skills.Teeth, -1]; - Config.ExplodeCorpses = sdk.skills.CorpseExplosion; - Config.Golem = "Clay"; - Config.BeltColumn = ["hp", "hp", "mp", "mp"]; - Config.HPBuffer = me.expansion ? 2 : 4; - Config.MPBuffer = me.expansion && me.charlvl < 80 ? 6 : me.classic ? 5 : 2; - SetUp.belt(); - } - } - }, - }; - - // Has to be set after its loaded - build.stats = me.classic - ? [ - ["dexterity", 51], ["strength", 80], ["energy", 100], ["vitality", "all"] - ] : [ - ["strength", 48], ["vitality", 165], ["strength", 61], - ["vitality", 252], ["strength", 156], ["vitality", "all"] - ]; - - return build; - })(); + AutoBuildTemplate: { + 1: { + Update: function () { + Config.TownHP = me.hardcore ? 0 : 35; + Config.AttackSkill = [-1, sdk.skills.BoneSpear, -1, sdk.skills.BoneSpear, -1, -1, -1]; + Config.LowManaSkill = [sdk.skills.Teeth, -1]; + Config.ExplodeCorpses = sdk.skills.CorpseExplosion; + Config.Golem = "Clay"; + Config.BeltColumn = ["hp", "hp", "mp", "mp"]; + Config.HPBuffer = me.expansion ? 2 : 4; + Config.MPBuffer = me.expansion && me.charlvl < 80 ? 6 : me.classic ? 5 : 2; + SetUp.belt(); + } + } + }, + }; + + // Has to be set after its loaded + build.stats = me.classic + ? [ + ["dexterity", 51], ["strength", 80], ["energy", 100], ["vitality", "all"] + ] : [ + ["strength", 48], ["vitality", 165], ["strength", 61], + ["vitality", 252], ["strength", 156], ["vitality", "all"] + ]; + + return build; + })(); })(module); diff --git a/libs/SoloPlay/BuildFiles/necromancer/necromancer.PoisonBuild.js b/libs/SoloPlay/BuildFiles/necromancer/necromancer.PoisonBuild.js index 902a7990..241f40f1 100644 --- a/libs/SoloPlay/BuildFiles/necromancer/necromancer.PoisonBuild.js +++ b/libs/SoloPlay/BuildFiles/necromancer/necromancer.PoisonBuild.js @@ -7,161 +7,161 @@ (function (module) { - module.exports = (function () { - const build = { - caster: true, - skillstab: sdk.skills.tabs.PoisonandBone, - wantedskills: [sdk.skills.PoisonNova, sdk.skills.CorpseExplosion], - usefulskills: [sdk.skills.AmplifyDamage, sdk.skills.BoneArmor, sdk.skills.LowerResist], - precastSkills: [sdk.skills.BoneArmor], - usefulStats: [sdk.stats.PassivePoisonMastery, sdk.stats.PassivePoisonPierce], - wantedMerc: MercData[sdk.skills.HolyFreeze], - skills: [ - [sdk.skills.LowerResist, 5], - [sdk.skills.SummonResist, 1], - [sdk.skills.BonePrison, 1], - [sdk.skills.PoisonNova, 20], - [sdk.skills.PoisonExplosion, 20], - [sdk.skills.PoisonDagger, 20], - [sdk.skills.BonePrison, 10], - [sdk.skills.BoneSpear, 10], - [sdk.skills.BonePrison, 20], - [sdk.skills.BoneSpear, 20], - ], - stats: [], + module.exports = (function () { + const build = { + caster: true, + skillstab: sdk.skills.tabs.PoisonandBone, + wantedskills: [sdk.skills.PoisonNova, sdk.skills.CorpseExplosion], + usefulskills: [sdk.skills.AmplifyDamage, sdk.skills.BoneArmor, sdk.skills.LowerResist], + precastSkills: [sdk.skills.BoneArmor], + usefulStats: [sdk.stats.PassivePoisonMastery, sdk.stats.PassivePoisonPierce], + wantedMerc: MercData[sdk.skills.HolyFreeze], + skills: [ + [sdk.skills.LowerResist, 5], + [sdk.skills.SummonResist, 1], + [sdk.skills.BonePrison, 1], + [sdk.skills.PoisonNova, 20], + [sdk.skills.PoisonExplosion, 20], + [sdk.skills.PoisonDagger, 20], + [sdk.skills.BonePrison, 10], + [sdk.skills.BoneSpear, 10], + [sdk.skills.BonePrison, 20], + [sdk.skills.BoneSpear, 20], + ], + stats: [], - charms: { - ResLife: { - max: 4, - have: [], - classid: sdk.items.SmallCharm, - stats: function (check) { - return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MaxHp) === 20); - } - }, + charms: { + ResLife: { + max: 4, + have: [], + classid: sdk.items.SmallCharm, + stats: function (check) { + return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MaxHp) === 20); + } + }, - ResMf: { - max: 2, - have: [], - classid: sdk.items.SmallCharm, - stats: function (check) { - return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MagicBonus) === 7); - } - }, + ResMf: { + max: 2, + have: [], + classid: sdk.items.SmallCharm, + stats: function (check) { + return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MagicBonus) === 7); + } + }, - ResFHR: { - max: 2, - have: [], - classid: sdk.items.SmallCharm, - stats: function (check) { - return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.FHR) === 5); - } - }, + ResFHR: { + max: 2, + have: [], + classid: sdk.items.SmallCharm, + stats: function (check) { + return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.FHR) === 5); + } + }, - Skiller: { - max: 2, - have: [], - classid: sdk.items.GrandCharm, - stats: function (check) { - return (!check.unique && check.classid === this.classid && check.getStat(sdk.stats.AddSkillTab, sdk.skills.tabs.PoisonandBone) === 1 - && check.getStat(sdk.stats.MaxHp) >= 40); - } - }, - }, - - AutoBuildTemplate: { - 1: { - Update: function () { - Config.AttackSkill = [-1, sdk.skills.PoisonNova, -1, sdk.skills.PoisonNova, -1, sdk.skills.BoneSpear, -1]; - Config.ExplodeCorpses = sdk.skills.CorpseExplosion; - Config.Golem = "Clay"; - } - }, - }, + Skiller: { + max: 2, + have: [], + classid: sdk.items.GrandCharm, + stats: function (check) { + return (!check.unique && check.classid === this.classid && check.getStat(sdk.stats.AddSkillTab, sdk.skills.tabs.PoisonandBone) === 1 + && check.getStat(sdk.stats.MaxHp) >= 40); + } + }, + }, + + AutoBuildTemplate: { + 1: { + Update: function () { + Config.AttackSkill = [-1, sdk.skills.PoisonNova, -1, sdk.skills.PoisonNova, -1, sdk.skills.BoneSpear, -1]; + Config.ExplodeCorpses = sdk.skills.CorpseExplosion; + Config.Golem = "Clay"; + } + }, + }, - respec: function () { - if (me.classic) { - return me.charlvl >= 75 && me.diablo; - } else { - return Check.haveItem("armor", "runeword", "Enigma"); - } - }, + respec: function () { + if (me.classic) { + return me.charlvl >= 75 && me.diablo; + } else { + return Check.haveItem("armor", "runeword", "Enigma"); + } + }, - active: function () { - return this.respec() && me.getSkill(sdk.skills.PoisonNova, sdk.skills.subindex.HardPoints) === 20; - }, - }; - - build.stats = me.classic - ? [ - ["dexterity", 51], ["strength", 80], ["energy", 100], ["vitality", "all"] - ] - : [ - ["strength", 48], ["vitality", 165], ["strength", 61], - ["vitality", 252], ["strength", 156], ["vitality", "all"] - ]; + active: function () { + return this.respec() && me.getSkill(sdk.skills.PoisonNova, sdk.skills.subindex.HardPoints) === 20; + }, + }; + + build.stats = me.classic + ? [ + ["dexterity", 51], ["strength", 80], ["energy", 100], ["vitality", "all"] + ] + : [ + ["strength", 48], ["vitality", 165], ["strength", 61], + ["vitality", 252], ["strength", 156], ["vitality", "all"] + ]; - let finalGear = me.classic - ? [ - // Weapon - Blackbog's Sharp - "[name] == cinquedeas && [quality] == unique # [ias] == 30 && [skillpoisonnova] == 4 # [tier] == 100000", - // Helm - Tarnhelm - "[name] == skullcap && [quality] == unique # [itemallskills] == 1 && [itemmagicbonus] >= 25 # [tier] == tierscore(item, 100000)", - // Shield - "[type] == shield && [quality] >= magic # [necromancerskills] == 2 && [allres] >= 16 # [tier] == tierscore(item, 100000)", - // Rings - SoJ - "[type] == ring && [quality] == unique # [itemmaxmanapercent] == 25 # [tier] == 100000", - // Amulet - "[type] == amulet && [quality] >= magic # [necromancerskills] == 2 && [fcr] == 10 # [tier] == tierscore(item, 100000)", - // Boots - "[type] == boots && [quality] >= magic # [frw] >= 20 && [fhr] == 10 && [coldresist]+[lightresist] >= 10 # [tier] == tierscore(item, 100000)", - // Belt - "[type] == belt && [quality] >= magic # [fhr] >= 20 && [maxhp] >= 40 && [fireresist]+[lightresist] >= 20 # [tier] == tierscore(item, 100000)", - // Gloves - Magefist - "[name] == lightgauntlets && [quality] == unique # [fcr] >= 20 && [addfireskills] == 1 # [tier] == tierscore(item, 100000)", - ] : [ - // Weapon - "([type] == wand || [type] == sword && ([quality] >= magic || [flag] == runeword) || [type] == knife && [quality] >= magic) && [flag] != ethereal # [secondarymindamage] == 0 && [itemchargedskill] >= 0 # [tier] == tierscore(item)", - // Helmet - Harlequin's Crest - "[name] == shako && [quality] == unique && [flag] != ethereal # [damageresist] == 10 # [tier] == tierscore(item, 100000)", - // Belt - Arach's - "[name] == spiderwebsash && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 90 # [tier] == tierscore(item, 100000)", - // Final Boots - Sandstorm Treks - "[name] == scarabshellboots && [quality] == unique # [strength]+[vitality] >= 20 # [tier] == tierscore(item, 100000)", - // Boots - War Traveler - "[name] == battleboots && [quality] == unique && [flag] != ethereal # [itemmagicbonus] >= 50 # [tier] == tierscore(item, 5000)", - // Armor - Enigma - "[type] == armor && [flag] != ethereal && [flag] == runeword # [itemallskills] == 2 # [tier] == 100000", - // Shield - "([type] == shield && ([quality] >= magic || [flag] == runeword) || [type] == voodooheads) && [flag] != ethereal # [itemchargedskill] >= 0 # [tier] == tierscore(item)", - // Final Gloves - Perfect 2x Upp'ed Magefist - "[name] == crusadergauntlets && [quality] == unique && [flag] != ethereal # [enhanceddefense] == 30 && [addfireskills] == 1 # [tier] == 110000", - // Gloves - 2x Upp'ed Magefist - "[name] == crusadergauntlets && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 20 && [addfireskills] == 1 # [tier] == tierscore(item, 100000)", - // Gloves - Upp'ed Magefist - "[name] == battlegauntlets && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 20 && [addfireskills] == 1 # [tier] == tierscore(item, 100000)", - // Gloves - Magefist - "[name] == lightgauntlets && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 20 && [addfireskills] == 1 # [tier] == tierscore(item, 100000)", - // Amulet - "[type] == amulet && [quality] == unique # [strength] == 5 && [coldresist] >= 30 # [tier] == tierscore(item, 100000)", // Maras - // Rings - Dwarf Star & SoJ - "[name] == ring && [quality] == unique # [maxhp] >= 40 && [magicdamagereduction] >= 12 # [tier] == 99000", - "[type] == ring && [quality] == unique # [itemmaxmanapercent] == 25 # [tier] == 100000", - // Switch - CTA - "[minimumsockets] >= 5 && [flag] == runeword # [plusskillbattleorders] >= 1 # [secondarytier] == 100000", - // Switch - Spirit - "[name] == monarch && [flag] == runeword # [fcr] >= 25 && [maxmana] >= 89 # [secondarytier] == 110000", - // Merc Armor - Fortitude - "[type] == armor && [flag] == runeword # [enhanceddefense] >= 200 && [enhanceddamage] >= 300 # [merctier] == 100000", - // Merc Final Helmet - Eth Andy's - "[name] == demonhead && [quality] == unique && [flag] == ethereal # [strength] >= 25 && [enhanceddefense] >= 100 # [merctier] == 50000 + mercscore(item)", - // Merc Helmet - Andy's - "[name] == demonhead && [quality] == unique && [flag] != ethereal # [strength] >= 25 && [enhanceddefense] >= 100 # [merctier] == 40000 + mercscore(item)", - ]; + let finalGear = me.classic + ? [ + // Weapon - Blackbog's Sharp + "[name] == cinquedeas && [quality] == unique # [ias] == 30 && [skillpoisonnova] == 4 # [tier] == 100000", + // Helm - Tarnhelm + "[name] == skullcap && [quality] == unique # [itemallskills] == 1 && [itemmagicbonus] >= 25 # [tier] == tierscore(item, 100000)", + // Shield + "[type] == shield && [quality] >= magic # [necromancerskills] == 2 && [allres] >= 16 # [tier] == tierscore(item, 100000)", + // Rings - SoJ + "[type] == ring && [quality] == unique # [itemmaxmanapercent] == 25 # [tier] == 100000", + // Amulet + "[type] == amulet && [quality] >= magic # [necromancerskills] == 2 && [fcr] == 10 # [tier] == tierscore(item, 100000)", + // Boots + "[type] == boots && [quality] >= magic # [frw] >= 20 && [fhr] == 10 && [coldresist]+[lightresist] >= 10 # [tier] == tierscore(item, 100000)", + // Belt + "[type] == belt && [quality] >= magic # [fhr] >= 20 && [maxhp] >= 40 && [fireresist]+[lightresist] >= 20 # [tier] == tierscore(item, 100000)", + // Gloves - Magefist + "[name] == lightgauntlets && [quality] == unique # [fcr] >= 20 && [addfireskills] == 1 # [tier] == tierscore(item, 100000)", + ] : [ + // Weapon + "([type] == wand || [type] == sword && ([quality] >= magic || [flag] == runeword) || [type] == knife && [quality] >= magic) && [flag] != ethereal # [secondarymindamage] == 0 && [itemchargedskill] >= 0 # [tier] == tierscore(item)", + // Helmet - Harlequin's Crest + "[name] == shako && [quality] == unique && [flag] != ethereal # [damageresist] == 10 # [tier] == tierscore(item, 100000)", + // Belt - Arach's + "[name] == spiderwebsash && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 90 # [tier] == tierscore(item, 100000)", + // Final Boots - Sandstorm Treks + "[name] == scarabshellboots && [quality] == unique # [strength]+[vitality] >= 20 # [tier] == tierscore(item, 100000)", + // Boots - War Traveler + "[name] == battleboots && [quality] == unique && [flag] != ethereal # [itemmagicbonus] >= 50 # [tier] == tierscore(item, 5000)", + // Armor - Enigma + "[type] == armor && [flag] != ethereal && [flag] == runeword # [itemallskills] == 2 # [tier] == 100000", + // Shield + "([type] == shield && ([quality] >= magic || [flag] == runeword) || [type] == voodooheads) && [flag] != ethereal # [itemchargedskill] >= 0 # [tier] == tierscore(item)", + // Final Gloves - Perfect 2x Upp'ed Magefist + "[name] == crusadergauntlets && [quality] == unique && [flag] != ethereal # [enhanceddefense] == 30 && [addfireskills] == 1 # [tier] == 110000", + // Gloves - 2x Upp'ed Magefist + "[name] == crusadergauntlets && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 20 && [addfireskills] == 1 # [tier] == tierscore(item, 100000)", + // Gloves - Upp'ed Magefist + "[name] == battlegauntlets && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 20 && [addfireskills] == 1 # [tier] == tierscore(item, 100000)", + // Gloves - Magefist + "[name] == lightgauntlets && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 20 && [addfireskills] == 1 # [tier] == tierscore(item, 100000)", + // Amulet + "[type] == amulet && [quality] == unique # [strength] == 5 && [coldresist] >= 30 # [tier] == tierscore(item, 100000)", // Maras + // Rings - Dwarf Star & SoJ + "[name] == ring && [quality] == unique # [maxhp] >= 40 && [magicdamagereduction] >= 12 # [tier] == 99000", + "[type] == ring && [quality] == unique # [itemmaxmanapercent] == 25 # [tier] == 100000", + // Switch - CTA + "[minimumsockets] >= 5 && [flag] == runeword # [plusskillbattleorders] >= 1 # [secondarytier] == 100000", + // Switch - Spirit + "[name] == monarch && [flag] == runeword # [fcr] >= 25 && [maxmana] >= 89 # [secondarytier] == 110000", + // Merc Armor - Fortitude + "[type] == armor && [flag] == runeword # [enhanceddefense] >= 200 && [enhanceddamage] >= 300 # [merctier] == 100000", + // Merc Final Helmet - Eth Andy's + "[name] == demonhead && [quality] == unique && [flag] == ethereal # [strength] >= 25 && [enhanceddefense] >= 100 # [merctier] == 50000 + mercscore(item)", + // Merc Helmet - Andy's + "[name] == demonhead && [quality] == unique && [flag] != ethereal # [strength] >= 25 && [enhanceddefense] >= 100 # [merctier] == 40000 + mercscore(item)", + ]; - NTIP.buildList(finalGear); - NTIP.buildFinalGear(finalGear); + NTIP.buildList(finalGear); + NTIP.buildFinalGear(finalGear); - return build; - })(); + return build; + })(); })(module); diff --git a/libs/SoloPlay/BuildFiles/necromancer/necromancer.StartBuild.js b/libs/SoloPlay/BuildFiles/necromancer/necromancer.StartBuild.js index 7455afb2..59cd0e37 100644 --- a/libs/SoloPlay/BuildFiles/necromancer/necromancer.StartBuild.js +++ b/libs/SoloPlay/BuildFiles/necromancer/necromancer.StartBuild.js @@ -7,77 +7,77 @@ (function (module, require) { - module.exports = (function () { - const build = { - AutoBuildTemplate: {}, - caster: true, - skillstab: sdk.skills.tabs.PoisonandBone, - wantedskills: [sdk.skills.Teeth, sdk.skills.BoneSpear], - usefulskills: [sdk.skills.AmplifyDamage, sdk.skills.BoneArmor, sdk.skills.Decrepify, sdk.skills.BoneWall], - wantedMerc: MercData[sdk.skills.Might], - stats: [ - ["strength", 20], ["vitality", 70], ["strength", 35], - ["energy", 85], ["vitality", "all"] - ], - skills: [ - [sdk.skills.Teeth, 4], // charlvl 4 - [sdk.skills.AmplifyDamage, 1], // charlvl 5 - [sdk.skills.ClayGolem, 1], // charlvl 6 - [sdk.skills.BoneArmor, 1], // charlvl 7 - [sdk.skills.Weaken, 1], // charlvl 8 - [sdk.skills.DimVision, 1], // charlvl 9 - [sdk.skills.Teeth, 7], // charlvl 11 - [sdk.skills.GolemMastery, 1], // charlvl 12 - [sdk.skills.IronMaiden, 1], // charlvl 13 - [sdk.skills.CorpseExplosion, 1], // charlvl 14 - [sdk.skills.BoneWall, 3], // charlvl 17 - [sdk.skills.BoneSpear, 6], // charlvl 23 - [sdk.skills.Decrepify, 1], // charlvl 24 - [sdk.skills.BoneSpear, 20], // charlvl -> Until respec at 26 - ], + module.exports = (function () { + const build = { + AutoBuildTemplate: {}, + caster: true, + skillstab: sdk.skills.tabs.PoisonandBone, + wantedskills: [sdk.skills.Teeth, sdk.skills.BoneSpear], + usefulskills: [sdk.skills.AmplifyDamage, sdk.skills.BoneArmor, sdk.skills.Decrepify, sdk.skills.BoneWall], + wantedMerc: MercData[sdk.skills.Might], + stats: [ + ["strength", 20], ["vitality", 70], ["strength", 35], + ["energy", 85], ["vitality", "all"] + ], + skills: [ + [sdk.skills.Teeth, 4], // charlvl 4 + [sdk.skills.AmplifyDamage, 1], // charlvl 5 + [sdk.skills.ClayGolem, 1], // charlvl 6 + [sdk.skills.BoneArmor, 1], // charlvl 7 + [sdk.skills.Weaken, 1], // charlvl 8 + [sdk.skills.DimVision, 1], // charlvl 9 + [sdk.skills.Teeth, 7], // charlvl 11 + [sdk.skills.GolemMastery, 1], // charlvl 12 + [sdk.skills.IronMaiden, 1], // charlvl 13 + [sdk.skills.CorpseExplosion, 1], // charlvl 14 + [sdk.skills.BoneWall, 3], // charlvl 17 + [sdk.skills.BoneSpear, 6], // charlvl 23 + [sdk.skills.Decrepify, 1], // charlvl 24 + [sdk.skills.BoneSpear, 20], // charlvl -> Until respec at 26 + ], - active: function () { - return me.charlvl < CharInfo.respecOne && !me.checkSkill(sdk.skills.BonePrison, sdk.skills.subindex.HardPoints); - }, - }; + active: function () { + return me.charlvl < CharInfo.respecOne && !me.checkSkill(sdk.skills.BonePrison, sdk.skills.subindex.HardPoints); + }, + }; - const { buildAutoBuildTempObj } = require("../../Utils/General"); - - build.AutoBuildTemplate[1] = buildAutoBuildTempObj(() => { - Config.ScanShrines.indexOf(sdk.shrines.Combat) === -1 && Config.ScanShrines.push(sdk.shrines.Combat); - Config.FieldID.Enabled = !Misc.checkQuest(sdk.quest.id.TheSearchForCain, sdk.quest.states.Completed); - Config.FieldID.UsedSpace = 0; - - Config.TownHP = me.hardcore ? 0 : 35; - Config.BeltColumn = ["hp", "hp", "hp", "hp"]; - Config.HPBuffer = 6; - Config.MPBuffer = 6; - Config.AttackSkill = [-1, 0, 0, 0, 0, -1, -1]; - Config.LowManaSkill = [0, 0]; - Config.Golem = "Clay"; - SetUp.belt(); - }); - build.AutoBuildTemplate[2] = buildAutoBuildTempObj(() => { - Config.ScanShrines.indexOf(sdk.shrines.Combat) === -1 && Config.ScanShrines.push(sdk.shrines.Combat); - Config.FieldID.Enabled = !Misc.checkQuest(sdk.quest.id.TheSearchForCain, sdk.quest.states.Completed); - Config.FieldID.UsedSpace = 0; - - Config.AttackSkill = [-1, sdk.skills.Teeth, -1, sdk.skills.Teeth, -1, -1, -1]; - Config.BeltColumn = ["hp", "hp", "mp", "mp"]; - Config.HPBuffer = 2; - Config.MPBuffer = 6; - SetUp.belt(); - }); - build.AutoBuildTemplate[18] = buildAutoBuildTempObj(() => { - Config.AttackSkill = [-1, sdk.skills.Teeth, -1, sdk.skills.Teeth, -1, -1, -1]; - Config.ExplodeCorpses = sdk.skills.CorpseExplosion; + const { buildAutoBuildTempObj } = require("../../Utils/General"); + + build.AutoBuildTemplate[1] = buildAutoBuildTempObj(() => { + Config.ScanShrines.indexOf(sdk.shrines.Combat) === -1 && Config.ScanShrines.push(sdk.shrines.Combat); + Config.FieldID.Enabled = !Misc.checkQuest(sdk.quest.id.TheSearchForCain, sdk.quest.states.Completed); + Config.FieldID.UsedSpace = 0; + + Config.TownHP = me.hardcore ? 0 : 35; + Config.BeltColumn = ["hp", "hp", "hp", "hp"]; + Config.HPBuffer = 6; + Config.MPBuffer = 6; + Config.AttackSkill = [-1, 0, 0, 0, 0, -1, -1]; + Config.LowManaSkill = [0, 0]; + Config.Golem = "Clay"; + SetUp.belt(); + }); + build.AutoBuildTemplate[2] = buildAutoBuildTempObj(() => { + Config.ScanShrines.indexOf(sdk.shrines.Combat) === -1 && Config.ScanShrines.push(sdk.shrines.Combat); + Config.FieldID.Enabled = !Misc.checkQuest(sdk.quest.id.TheSearchForCain, sdk.quest.states.Completed); + Config.FieldID.UsedSpace = 0; + + Config.AttackSkill = [-1, sdk.skills.Teeth, -1, sdk.skills.Teeth, -1, -1, -1]; + Config.BeltColumn = ["hp", "hp", "mp", "mp"]; + Config.HPBuffer = 2; + Config.MPBuffer = 6; + SetUp.belt(); + }); + build.AutoBuildTemplate[18] = buildAutoBuildTempObj(() => { + Config.AttackSkill = [-1, sdk.skills.Teeth, -1, sdk.skills.Teeth, -1, -1, -1]; + Config.ExplodeCorpses = sdk.skills.CorpseExplosion; - if (me.checkSkill(sdk.skills.BoneSpear, sdk.skills.subindex.HardPoints)) { - Config.AttackSkill = [-1, sdk.skills.BoneSpear, -1, sdk.skills.BoneSpear, -1, -1, -1]; - Config.LowManaSkill = [sdk.skills.Teeth, -1]; - } - }); + if (me.checkSkill(sdk.skills.BoneSpear, sdk.skills.subindex.HardPoints)) { + Config.AttackSkill = [-1, sdk.skills.BoneSpear, -1, sdk.skills.BoneSpear, -1, -1, -1]; + Config.LowManaSkill = [sdk.skills.Teeth, -1]; + } + }); - return build; - })(); + return build; + })(); })(module, require); diff --git a/libs/SoloPlay/BuildFiles/necromancer/necromancer.SummonBuild.js b/libs/SoloPlay/BuildFiles/necromancer/necromancer.SummonBuild.js index a0f22cf9..76435580 100644 --- a/libs/SoloPlay/BuildFiles/necromancer/necromancer.SummonBuild.js +++ b/libs/SoloPlay/BuildFiles/necromancer/necromancer.SummonBuild.js @@ -7,175 +7,175 @@ (function (module) { - module.exports = (function () { - const build = { - caster: true, - skillstab: sdk.skills.tabs.NecroSummoning, - wantedskills: [sdk.skills.RaiseSkeleton, sdk.skills.CorpseExplosion], - usefulskills: [sdk.skills.AmplifyDamage, sdk.skills.SkeletonMastery, sdk.skills.BoneArmor, sdk.skills.Decrepify], - precastSkills: [sdk.skills.BoneArmor], - wantedMerc: MercData[sdk.skills.Might], - stats: [], - skills: [], + module.exports = (function () { + const build = { + caster: true, + skillstab: sdk.skills.tabs.NecroSummoning, + wantedskills: [sdk.skills.RaiseSkeleton, sdk.skills.CorpseExplosion], + usefulskills: [sdk.skills.AmplifyDamage, sdk.skills.SkeletonMastery, sdk.skills.BoneArmor, sdk.skills.Decrepify], + precastSkills: [sdk.skills.BoneArmor], + wantedMerc: MercData[sdk.skills.Might], + stats: [], + skills: [], - charms: { - ResLife: { - max: 4, - have: [], - classid: sdk.items.SmallCharm, - stats: function (check) { - return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MaxHp) === 20); - } - }, + charms: { + ResLife: { + max: 4, + have: [], + classid: sdk.items.SmallCharm, + stats: function (check) { + return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MaxHp) === 20); + } + }, - ResMf: { - max: 4, - have: [], - classid: sdk.items.SmallCharm, - stats: function (check) { - return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MagicBonus) === 7); - } - }, + ResMf: { + max: 4, + have: [], + classid: sdk.items.SmallCharm, + stats: function (check) { + return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MagicBonus) === 7); + } + }, - Skiller: { - max: 2, - have: [], - classid: sdk.items.GrandCharm, - stats: function (check) { - return (!check.unique && check.classid === this.classid && check.getStat(sdk.stats.AddSkillTab, sdk.skills.tabs.NecroSummoning) === 1 - && check.getStat(sdk.stats.MaxHp) >= 40); - } - }, - }, + Skiller: { + max: 2, + have: [], + classid: sdk.items.GrandCharm, + stats: function (check) { + return (!check.unique && check.classid === this.classid && check.getStat(sdk.stats.AddSkillTab, sdk.skills.tabs.NecroSummoning) === 1 + && check.getStat(sdk.stats.MaxHp) >= 40); + } + }, + }, - AutoBuildTemplate: { - 1: { - Update: function () { - Config.AttackSkill = me.classic - ? [-1, sdk.skills.PoisonNova, -1, sdk.skills.PoisonNova, -1, sdk.skills.BoneSpear, -1] - : [-1, sdk.skills.BoneSpear, -1, sdk.skills.BoneSpear, -1, -1, -1]; - Config.LowManaSkill = [0, 0]; - Config.ActiveSummon = true; - Config.Skeletons = "max"; - Config.SkeletonMages = "max"; - Config.Revives = "max"; - Config.Golem = "Clay"; - Config.MPBuffer = me.expansion ? 4 : 6; - } - }, - }, + AutoBuildTemplate: { + 1: { + Update: function () { + Config.AttackSkill = me.classic + ? [-1, sdk.skills.PoisonNova, -1, sdk.skills.PoisonNova, -1, sdk.skills.BoneSpear, -1] + : [-1, sdk.skills.BoneSpear, -1, sdk.skills.BoneSpear, -1, -1, -1]; + Config.LowManaSkill = [0, 0]; + Config.ActiveSummon = true; + Config.Skeletons = "max"; + Config.SkeletonMages = "max"; + Config.Revives = "max"; + Config.Golem = "Clay"; + Config.MPBuffer = me.expansion ? 4 : 6; + } + }, + }, - respec: function () { - if (me.classic) { - return me.charlvl >= 75 && me.diablo; - } else { - return Check.haveItem("armor", "runeword", "Enigma"); - } - }, + respec: function () { + if (me.classic) { + return me.charlvl >= 75 && me.diablo; + } else { + return Check.haveItem("armor", "runeword", "Enigma"); + } + }, - active: function () { - return this.respec() && me.getSkill(sdk.skills.RaiseSkeleton, sdk.skills.subindex.HardPoints) === 20; - }, - }; - - build.stats = me.classic - ? [ - ["dexterity", 51], ["strength", 80], ["energy", 100], ["vitality", "all"] - ] - : [ - ["strength", 48], ["vitality", 165], ["strength", 61], - ["vitality", 252], ["strength", 156], ["vitality", "all"] - ]; + active: function () { + return this.respec() && me.getSkill(sdk.skills.RaiseSkeleton, sdk.skills.subindex.HardPoints) === 20; + }, + }; + + build.stats = me.classic + ? [ + ["dexterity", 51], ["strength", 80], ["energy", 100], ["vitality", "all"] + ] + : [ + ["strength", 48], ["vitality", 165], ["strength", 61], + ["vitality", 252], ["strength", 156], ["vitality", "all"] + ]; - build.skills = me.classic - ? [ - [sdk.skills.SummonResist, 1], - [sdk.skills.BonePrison, 1], - [sdk.skills.Decrepify, 1], - [sdk.skills.LowerResist, 1], - [sdk.skills.RaiseSkeleton, 20, false], - [sdk.skills.SkeletonMastery, 20, false], - [sdk.skills.PoisonNova, 20, false], - [sdk.skills.LowerResist, 5, false], - [sdk.skills.SummonResist, 5, false], - [sdk.skills.RaiseSkeletalMage, 20, false], - [sdk.skills.BonePrison, 20, false], - ] - : [ - [sdk.skills.SummonResist, 1], - [sdk.skills.BonePrison, 1], - [sdk.skills.Decrepify, 1], - [sdk.skills.RaiseSkeleton, 20, false], - [sdk.skills.SkeletonMastery, 20, false], - [sdk.skills.CorpseExplosion, 20, false], - [sdk.skills.AmplifyDamage, 20, false], - [sdk.skills.Revive, 20, false], - ]; + build.skills = me.classic + ? [ + [sdk.skills.SummonResist, 1], + [sdk.skills.BonePrison, 1], + [sdk.skills.Decrepify, 1], + [sdk.skills.LowerResist, 1], + [sdk.skills.RaiseSkeleton, 20, false], + [sdk.skills.SkeletonMastery, 20, false], + [sdk.skills.PoisonNova, 20, false], + [sdk.skills.LowerResist, 5, false], + [sdk.skills.SummonResist, 5, false], + [sdk.skills.RaiseSkeletalMage, 20, false], + [sdk.skills.BonePrison, 20, false], + ] + : [ + [sdk.skills.SummonResist, 1], + [sdk.skills.BonePrison, 1], + [sdk.skills.Decrepify, 1], + [sdk.skills.RaiseSkeleton, 20, false], + [sdk.skills.SkeletonMastery, 20, false], + [sdk.skills.CorpseExplosion, 20, false], + [sdk.skills.AmplifyDamage, 20, false], + [sdk.skills.Revive, 20, false], + ]; - let finalGear = me.classic - ? [ - // Weapon - Blackbog's Sharp - "[name] == cinquedeas && [quality] == unique # [ias] == 30 && [skillpoisonnova] == 4 # [tier] == 100000", - // Helm - Tarnhelm - "[name] == skullcap && [quality] == unique # [itemallskills] == 1 && [itemmagicbonus] >= 25 # [tier] == tierscore(item, 100000)", - // Shield - "[type] == shield && [quality] >= magic # [necromancerskills] == 2 && [allres] >= 16 # [tier] == tierscore(item, 100000)", - // Rings - SoJ - "[type] == ring && [quality] == unique # [itemmaxmanapercent] == 25 # [tier] == 100000", - // Amulet - "[type] == amulet && [quality] >= magic # [necromancerskills] == 2 && [fcr] == 10 # [tier] == tierscore(item, 100000)", - // Boots - "[type] == boots && [quality] >= magic # [frw] >= 20 && [fhr] == 10 && [coldresist]+[lightresist] >= 10 # [tier] == tierscore(item, 100000)", - // Belt - "[type] == belt && [quality] >= magic # [fhr] >= 20 && [maxhp] >= 40 && [fireresist]+[lightresist] >= 20 # [tier] == tierscore(item, 100000)", - // Gloves - Magefist - "[name] == lightgauntlets && [quality] == unique # [fcr] >= 20 && [addfireskills] == 1 # [tier] == tierscore(item, 100000)", - ] : [ - // Weapon - "([type] == wand || [type] == sword && ([quality] >= magic || [flag] == runeword) || [type] == knife && [quality] >= magic) && [flag] != ethereal # [secondarymindamage] == 0 && [itemchargedskill] >= 0 # [tier] == tierscore(item)", - // Helmet - Harlequin's Crest - "[name] == shako && [quality] == unique && [flag] != ethereal # [damageresist] == 10 # [tier] == tierscore(item, 100000)", - // Belt - Arach's - "[name] == spiderwebsash && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 90 # [tier] == tierscore(item, 100000)", - // Boots - Marrowalk - "[name] == boneweaveboots && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 170 # [tier] == tierscore(item, 100000)", - // Boots - War Traveler - "[name] == battleboots && [quality] == unique && [flag] != ethereal # [itemmagicbonus] >= 50 # [tier] == tierscore(item, 5000)", - // Armor - Enigma - "[type] == armor && [flag] != ethereal && [flag] == runeword # [itemallskills] == 2 # [tier] == 100000", - // Shield - "([type] == shield && ([quality] >= magic || [flag] == runeword) || [type] == voodooheads) && [flag] != ethereal # [itemchargedskill] >= 0 # [tier] == tierscore(item)", - // Final Gloves - Perfect 2x Upp'ed Magefist - "[name] == crusadergauntlets && [quality] == unique && [flag] != ethereal # [enhanceddefense] == 30 && [addfireskills] == 1 # [tier] == 110000", - // Gloves - 2x Upp'ed Magefist - "[name] == crusadergauntlets && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 20 && [addfireskills] == 1 # [tier] == tierscore(item, 100000)", - // Gloves - Upp'ed Magefist - "[name] == battlegauntlets && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 20 && [addfireskills] == 1 # [tier] == tierscore(item, 100000)", - // Gloves - Magefist - "[name] == lightgauntlets && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 20 && [addfireskills] == 1 # [tier] == tierscore(item, 100000)", - // Amulet - Maras - "[type] == amulet && [quality] == unique # [strength] == 5 && [coldresist] >= 30 # [tier] == tierscore(item, 100000)", - // Final Rings - SoJ & Perfect Raven Frost - "[type] == ring && [quality] == unique # [itemmaxmanapercent] == 25 # [tier] == 100000", - "[type] == ring && [quality] == unique # [dexterity] >= 20 # [tier] == 100000", - // Rings - Dwarf Star & Raven Frost - "[name] == ring && [quality] == unique # [maxhp] >= 40 && [magicdamagereduction] >= 12 # [tier] == 99000", - "[type] == ring && [quality] == unique # [dexterity] >= 20 # [tier] == 99000", - // Switch - CTA - "[minimumsockets] >= 5 && [flag] == runeword # [plusskillbattleorders] >= 1 # [secondarytier] == 100000", - // Switch - Spirit - "[name] == monarch && [flag] == runeword # [fcr] >= 25 && [maxmana] >= 89 # [secondarytier] == 110000", - // Merc Armor - Fortitude - "[type] == armor && [flag] == runeword # [enhanceddefense] >= 200 && [enhanceddamage] >= 300 # [merctier] == 100000", - // Merc Final Helmet - Eth Andy's - "[name] == demonhead && [quality] == unique && [flag] == ethereal # [strength] >= 25 && [enhanceddefense] >= 100 # [merctier] == 50000 + mercscore(item)", - // Merc Helmet - Andy's - "[name] == demonhead && [quality] == unique && [flag] != ethereal # [strength] >= 25 && [enhanceddefense] >= 100 # [merctier] == 40000 + mercscore(item)", - ]; + let finalGear = me.classic + ? [ + // Weapon - Blackbog's Sharp + "[name] == cinquedeas && [quality] == unique # [ias] == 30 && [skillpoisonnova] == 4 # [tier] == 100000", + // Helm - Tarnhelm + "[name] == skullcap && [quality] == unique # [itemallskills] == 1 && [itemmagicbonus] >= 25 # [tier] == tierscore(item, 100000)", + // Shield + "[type] == shield && [quality] >= magic # [necromancerskills] == 2 && [allres] >= 16 # [tier] == tierscore(item, 100000)", + // Rings - SoJ + "[type] == ring && [quality] == unique # [itemmaxmanapercent] == 25 # [tier] == 100000", + // Amulet + "[type] == amulet && [quality] >= magic # [necromancerskills] == 2 && [fcr] == 10 # [tier] == tierscore(item, 100000)", + // Boots + "[type] == boots && [quality] >= magic # [frw] >= 20 && [fhr] == 10 && [coldresist]+[lightresist] >= 10 # [tier] == tierscore(item, 100000)", + // Belt + "[type] == belt && [quality] >= magic # [fhr] >= 20 && [maxhp] >= 40 && [fireresist]+[lightresist] >= 20 # [tier] == tierscore(item, 100000)", + // Gloves - Magefist + "[name] == lightgauntlets && [quality] == unique # [fcr] >= 20 && [addfireskills] == 1 # [tier] == tierscore(item, 100000)", + ] : [ + // Weapon + "([type] == wand || [type] == sword && ([quality] >= magic || [flag] == runeword) || [type] == knife && [quality] >= magic) && [flag] != ethereal # [secondarymindamage] == 0 && [itemchargedskill] >= 0 # [tier] == tierscore(item)", + // Helmet - Harlequin's Crest + "[name] == shako && [quality] == unique && [flag] != ethereal # [damageresist] == 10 # [tier] == tierscore(item, 100000)", + // Belt - Arach's + "[name] == spiderwebsash && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 90 # [tier] == tierscore(item, 100000)", + // Boots - Marrowalk + "[name] == boneweaveboots && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 170 # [tier] == tierscore(item, 100000)", + // Boots - War Traveler + "[name] == battleboots && [quality] == unique && [flag] != ethereal # [itemmagicbonus] >= 50 # [tier] == tierscore(item, 5000)", + // Armor - Enigma + "[type] == armor && [flag] != ethereal && [flag] == runeword # [itemallskills] == 2 # [tier] == 100000", + // Shield + "([type] == shield && ([quality] >= magic || [flag] == runeword) || [type] == voodooheads) && [flag] != ethereal # [itemchargedskill] >= 0 # [tier] == tierscore(item)", + // Final Gloves - Perfect 2x Upp'ed Magefist + "[name] == crusadergauntlets && [quality] == unique && [flag] != ethereal # [enhanceddefense] == 30 && [addfireskills] == 1 # [tier] == 110000", + // Gloves - 2x Upp'ed Magefist + "[name] == crusadergauntlets && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 20 && [addfireskills] == 1 # [tier] == tierscore(item, 100000)", + // Gloves - Upp'ed Magefist + "[name] == battlegauntlets && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 20 && [addfireskills] == 1 # [tier] == tierscore(item, 100000)", + // Gloves - Magefist + "[name] == lightgauntlets && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 20 && [addfireskills] == 1 # [tier] == tierscore(item, 100000)", + // Amulet - Maras + "[type] == amulet && [quality] == unique # [strength] == 5 && [coldresist] >= 30 # [tier] == tierscore(item, 100000)", + // Final Rings - SoJ & Perfect Raven Frost + "[type] == ring && [quality] == unique # [itemmaxmanapercent] == 25 # [tier] == 100000", + "[type] == ring && [quality] == unique # [dexterity] >= 20 # [tier] == 100000", + // Rings - Dwarf Star & Raven Frost + "[name] == ring && [quality] == unique # [maxhp] >= 40 && [magicdamagereduction] >= 12 # [tier] == 99000", + "[type] == ring && [quality] == unique # [dexterity] >= 20 # [tier] == 99000", + // Switch - CTA + "[minimumsockets] >= 5 && [flag] == runeword # [plusskillbattleorders] >= 1 # [secondarytier] == 100000", + // Switch - Spirit + "[name] == monarch && [flag] == runeword # [fcr] >= 25 && [maxmana] >= 89 # [secondarytier] == 110000", + // Merc Armor - Fortitude + "[type] == armor && [flag] == runeword # [enhanceddefense] >= 200 && [enhanceddamage] >= 300 # [merctier] == 100000", + // Merc Final Helmet - Eth Andy's + "[name] == demonhead && [quality] == unique && [flag] == ethereal # [strength] >= 25 && [enhanceddefense] >= 100 # [merctier] == 50000 + mercscore(item)", + // Merc Helmet - Andy's + "[name] == demonhead && [quality] == unique && [flag] != ethereal # [strength] >= 25 && [enhanceddefense] >= 100 # [merctier] == 40000 + mercscore(item)", + ]; - NTIP.buildList(finalGear); - NTIP.buildFinalGear(finalGear); + NTIP.buildList(finalGear); + NTIP.buildFinalGear(finalGear); - return build; - })(); + return build; + })(); })(module); diff --git a/libs/SoloPlay/BuildFiles/necromancer/necromancer.js b/libs/SoloPlay/BuildFiles/necromancer/necromancer.js index 0407529f..a5256360 100644 --- a/libs/SoloPlay/BuildFiles/necromancer/necromancer.js +++ b/libs/SoloPlay/BuildFiles/necromancer/necromancer.js @@ -6,37 +6,37 @@ */ const CharInfo = { - respecOne: 26, - respecTwo: 0, - levelCap: (function() { - const currentDiff = sdk.difficulty.nameOf(me.diff); - const softcoreMode = { - "Normal": me.expansion ? 33 : 33, - "Nightmare": me.expansion ? 70 : 70, - "Hell": 100, - }; - const hardcoreMode = { - "Normal": me.expansion ? 36 : 33, - "Nightmare": me.expansion ? 71 : 70, - "Hell": 100, - }; + respecOne: 26, + respecTwo: 0, + levelCap: (function() { + const currentDiff = sdk.difficulty.nameOf(me.diff); + const softcoreMode = { + "Normal": me.expansion ? 33 : 33, + "Nightmare": me.expansion ? 70 : 70, + "Hell": 100, + }; + const hardcoreMode = { + "Normal": me.expansion ? 36 : 33, + "Nightmare": me.expansion ? 71 : 70, + "Hell": 100, + }; - return me.softcore ? softcoreMode[currentDiff] : hardcoreMode[currentDiff]; - })(), + return me.softcore ? softcoreMode[currentDiff] : hardcoreMode[currentDiff]; + })(), - getActiveBuild: function () { - const nSkills = me.getStat(sdk.stats.NewSkills); - const currLevel = me.charlvl; - const justRepeced = (nSkills >= currLevel); + getActiveBuild: function () { + const nSkills = me.getStat(sdk.stats.NewSkills); + const currLevel = me.charlvl; + const justRepeced = (nSkills >= currLevel); - switch (true) { - case currLevel < this.respecOne && !me.checkSkill(sdk.skills.BonePrison, sdk.skills.subindex.HardPoints): - return "Start"; - case Check.finalBuild().respec() && justRepeced: - case Check.finalBuild().active(): - return SetUp.finalBuild; - default: - return "Leveling"; - } - }, + switch (true) { + case currLevel < this.respecOne && !me.checkSkill(sdk.skills.BonePrison, sdk.skills.subindex.HardPoints): + return "Start"; + case Check.finalBuild().respec() && justRepeced: + case Check.finalBuild().active(): + return SetUp.finalBuild; + default: + return "Leveling"; + } + }, }; diff --git a/libs/SoloPlay/BuildFiles/paladin/paladin.AuradinBuild.js b/libs/SoloPlay/BuildFiles/paladin/paladin.AuradinBuild.js index 58a42e76..cb1c9f56 100644 --- a/libs/SoloPlay/BuildFiles/paladin/paladin.AuradinBuild.js +++ b/libs/SoloPlay/BuildFiles/paladin/paladin.AuradinBuild.js @@ -7,132 +7,132 @@ (function (module) { - module.exports = (function () { - const build = { - caster: false, - skillstab: sdk.skills.tabs.PalaCombat, - wantedskills: [sdk.skills.Zeal, sdk.skills.Conviction], - usefulskills: [sdk.skills.HolyShield, sdk.skills.ResistFire, sdk.skills.ResistLightning], - precastSkills: [sdk.skills.HolyShield], - usefulStats: [sdk.stats.PassiveLightningMastery, sdk.stats.PassiveLightningPierce, sdk.stats.PierceLtng, sdk.stats.PassiveFireMastery, sdk.stats.PassiveFirePierce, sdk.stats.PierceFire], - wantedMerc: MercData[sdk.skills.HolyFreeze], - stats: [ - ["strength", 103], ["dexterity", 136], ["vitality", 300], ["dexterity", "block"], ["vitality", "all"] - ], - skills: [ - [sdk.skills.Conviction, 20], - [sdk.skills.Zeal, 4], - [sdk.skills.ResistLightning, 20], - [sdk.skills.Salvation, 20], - [sdk.skills.ResistFire, 20], - [sdk.skills.Redemption, 1], - [sdk.skills.HolyShield, 15], - [sdk.skills.Zeal, 10], - ], + module.exports = (function () { + const build = { + caster: false, + skillstab: sdk.skills.tabs.PalaCombat, + wantedskills: [sdk.skills.Zeal, sdk.skills.Conviction], + usefulskills: [sdk.skills.HolyShield, sdk.skills.ResistFire, sdk.skills.ResistLightning], + precastSkills: [sdk.skills.HolyShield], + usefulStats: [sdk.stats.PassiveLightningMastery, sdk.stats.PassiveLightningPierce, sdk.stats.PierceLtng, sdk.stats.PassiveFireMastery, sdk.stats.PassiveFirePierce, sdk.stats.PierceFire], + wantedMerc: MercData[sdk.skills.HolyFreeze], + stats: [ + ["strength", 103], ["dexterity", 136], ["vitality", 300], ["dexterity", "block"], ["vitality", "all"] + ], + skills: [ + [sdk.skills.Conviction, 20], + [sdk.skills.Zeal, 4], + [sdk.skills.ResistLightning, 20], + [sdk.skills.Salvation, 20], + [sdk.skills.ResistFire, 20], + [sdk.skills.Redemption, 1], + [sdk.skills.HolyShield, 15], + [sdk.skills.Zeal, 10], + ], - charms: { - ResLife: { - max: 6, - have: [], - classid: sdk.items.SmallCharm, - stats: function (check) { - return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MaxHp) === 20); - } - }, + charms: { + ResLife: { + max: 6, + have: [], + classid: sdk.items.SmallCharm, + stats: function (check) { + return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MaxHp) === 20); + } + }, - ResMf: { - max: 2, - have: [], - classid: sdk.items.SmallCharm, - stats: function (check) { - return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MagicBonus) === 7); - } - }, + ResMf: { + max: 2, + have: [], + classid: sdk.items.SmallCharm, + stats: function (check) { + return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MagicBonus) === 7); + } + }, - Skiller: { - max: 2, - have: [], - classid: sdk.items.GrandCharm, - stats: function (check) { - return (!check.unique && check.classid === this.classid && check.getStat(sdk.stats.AddSkillTab, sdk.skills.tabs.Offensive) === 1 - && check.getStat(sdk.stats.MaxHp) >= 40); - } - }, - }, + Skiller: { + max: 2, + have: [], + classid: sdk.items.GrandCharm, + stats: function (check) { + return (!check.unique && check.classid === this.classid && check.getStat(sdk.stats.AddSkillTab, sdk.skills.tabs.Offensive) === 1 + && check.getStat(sdk.stats.MaxHp) >= 40); + } + }, + }, - AutoBuildTemplate: { - 1: { - Update: function () { - Config.Vigor = false; - Config.AttackSkill = [-1, sdk.skills.Zeal, sdk.skills.Conviction, sdk.skills.Zeal, sdk.skills.Conviction, -1, -1]; - Config.LowManaSkill = [-1, -1]; + AutoBuildTemplate: { + 1: { + Update: function () { + Config.Vigor = false; + Config.AttackSkill = [-1, sdk.skills.Zeal, sdk.skills.Conviction, sdk.skills.Zeal, sdk.skills.Conviction, -1, -1]; + Config.LowManaSkill = [-1, -1]; - if (!me.haveSome([{ name: sdk.locale.items.Dragon, itemtype: sdk.items.type.Armor }, { name: sdk.locale.items.HandofJustice }])) { - Config.SkipImmune = ["lightning and physical"]; - } else { - Config.SkipImmune = ["lightning and fire and physical"]; // Don't think this ever happens but should skip if it does - } + if (!me.haveSome([{ name: sdk.locale.items.Dragon, itemtype: sdk.items.type.Armor }, { name: sdk.locale.items.HandofJustice }])) { + Config.SkipImmune = ["lightning and physical"]; + } else { + Config.SkipImmune = ["lightning and fire and physical"]; // Don't think this ever happens but should skip if it does + } - Config.BeltColumn = ["hp", "hp", "mp", "rv"]; - SetUp.belt(); - } - }, - }, + Config.BeltColumn = ["hp", "hp", "mp", "rv"]; + SetUp.belt(); + } + }, + }, - respec: function () { - if (me.classic) { - return false; - } else { - return me.haveAll([{ name: sdk.locale.items.Dream, itemtype: sdk.items.type.AuricShields }, { name: sdk.locale.items.Dream, itemtype: sdk.items.type.Helm }]); - } - }, + respec: function () { + if (me.classic) { + return false; + } else { + return me.haveAll([{ name: sdk.locale.items.Dream, itemtype: sdk.items.type.AuricShields }, { name: sdk.locale.items.Dream, itemtype: sdk.items.type.Helm }]); + } + }, - active: function () { - return this.respec() && me.getSkill(sdk.skills.Conviction, sdk.skills.subindex.HardPoints) === 20; - }, - }; - - let finalGear = [ // autoequip final gear - // Final Weapon - HoJ - "[type] == sword && [flag] == runeword # [holyfireaura] >= 16 # [tier] == 120000", - // Weapon - Crescent Moon & Voice of Reason - "[type] == sword && [flag] == runeword # [ias] >= 20 && [passiveltngpierce] >= 35 # [tier] == 110000", - "[type] == sword && [flag] == runeword # [passivecoldpierce] >= 24 # [tier] == 102500", - // Helm - Dream - "[type] == helm && [flag] == runeword # [holyshockaura] >= 15 # [tier] == 110000", - // Belt - TGods - "[name] == warbelt && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 160 # [tier] == tierscore(item, 110000)", - // Boots - Gore Rider - "[name] == warboots && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 160 # [tier] == tierscore(item, 110000)", - // Armor - Dragon - "[type] == armor && [flag] != ethereal && [flag] == runeword # [holyfireaura] >= 14 # [tier] == 110000", - // Shield - Dream - "[type] == auricshields && [flag] != ethereal && [flag] == runeword # [holyshockaura] >= 15 # [tier] == 110000", - // Gloves - Laying of Hand's - "[name] == bramblemitts && [quality] == set && [flag] != ethereal # [ias] >= 20 # [tier] == 110000", - // Amulet - Highlords - "[type] == amulet && [quality] == unique # [lightresist] == 35 # [tier] == 100000", - // Final Rings - Perfect Raven Frost & Bul-Kathos' Wedding Band - "[type] == ring && [quality] == unique # [dexterity] == 20 && [tohit] == 250 # [tier] == 110000", - "[type] == ring && [quality] == unique # [maxstamina] == 50 && [lifeleech] == 5 # [tier] == 110000", - // Rings - Raven Frost && Bul-Kathos' Wedding Band - "[type] == ring && [quality] == unique # [dexterity] >= 15 && [tohit] >= 150 # [tier] == 100000", - "[type] == ring && [quality] == unique # [maxstamina] == 50 && [lifeleech] >= 3 # [tier] == 100000", - // Switch - CTA - "[minimumsockets] >= 5 && [flag] == runeword # [plusskillbattleorders] >= 1 # [secondarytier] == 100000", - // Merc Final Armor - Fortitude - "[type] == armor && [flag] == runeword # [enhanceddefense] >= 200 && [enhanceddamage] >= 300 # [merctier] == 100000", - // Merc Armor - Treachery - "[type] == armor && [flag] == runeword # [ias] == 45 && [coldresist] == 30 # [merctier] == 50000 + mercscore(item)", - // Merc Final Helmet - Eth Andy's - "[name] == demonhead && [quality] == unique && [flag] == ethereal # [strength] >= 25 && [enhanceddefense] >= 100 # [merctier] == 50000 + mercscore(item)", - // Merc Helmet - Andy's - "[name] == demonhead && [quality] == unique && [flag] != ethereal # [strength] >= 25 && [enhanceddefense] >= 100 # [merctier] == 40000 + mercscore(item)", - ]; + active: function () { + return this.respec() && me.getSkill(sdk.skills.Conviction, sdk.skills.subindex.HardPoints) === 20; + }, + }; + + let finalGear = [ // autoequip final gear + // Final Weapon - HoJ + "[type] == sword && [flag] == runeword # [holyfireaura] >= 16 # [tier] == 120000", + // Weapon - Crescent Moon & Voice of Reason + "[type] == sword && [flag] == runeword # [ias] >= 20 && [passiveltngpierce] >= 35 # [tier] == 110000", + "[type] == sword && [flag] == runeword # [passivecoldpierce] >= 24 # [tier] == 102500", + // Helm - Dream + "[type] == helm && [flag] == runeword # [holyshockaura] >= 15 # [tier] == 110000", + // Belt - TGods + "[name] == warbelt && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 160 # [tier] == tierscore(item, 110000)", + // Boots - Gore Rider + "[name] == warboots && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 160 # [tier] == tierscore(item, 110000)", + // Armor - Dragon + "[type] == armor && [flag] != ethereal && [flag] == runeword # [holyfireaura] >= 14 # [tier] == 110000", + // Shield - Dream + "[type] == auricshields && [flag] != ethereal && [flag] == runeword # [holyshockaura] >= 15 # [tier] == 110000", + // Gloves - Laying of Hand's + "[name] == bramblemitts && [quality] == set && [flag] != ethereal # [ias] >= 20 # [tier] == 110000", + // Amulet - Highlords + "[type] == amulet && [quality] == unique # [lightresist] == 35 # [tier] == 100000", + // Final Rings - Perfect Raven Frost & Bul-Kathos' Wedding Band + "[type] == ring && [quality] == unique # [dexterity] == 20 && [tohit] == 250 # [tier] == 110000", + "[type] == ring && [quality] == unique # [maxstamina] == 50 && [lifeleech] == 5 # [tier] == 110000", + // Rings - Raven Frost && Bul-Kathos' Wedding Band + "[type] == ring && [quality] == unique # [dexterity] >= 15 && [tohit] >= 150 # [tier] == 100000", + "[type] == ring && [quality] == unique # [maxstamina] == 50 && [lifeleech] >= 3 # [tier] == 100000", + // Switch - CTA + "[minimumsockets] >= 5 && [flag] == runeword # [plusskillbattleorders] >= 1 # [secondarytier] == 100000", + // Merc Final Armor - Fortitude + "[type] == armor && [flag] == runeword # [enhanceddefense] >= 200 && [enhanceddamage] >= 300 # [merctier] == 100000", + // Merc Armor - Treachery + "[type] == armor && [flag] == runeword # [ias] == 45 && [coldresist] == 30 # [merctier] == 50000 + mercscore(item)", + // Merc Final Helmet - Eth Andy's + "[name] == demonhead && [quality] == unique && [flag] == ethereal # [strength] >= 25 && [enhanceddefense] >= 100 # [merctier] == 50000 + mercscore(item)", + // Merc Helmet - Andy's + "[name] == demonhead && [quality] == unique && [flag] != ethereal # [strength] >= 25 && [enhanceddefense] >= 100 # [merctier] == 40000 + mercscore(item)", + ]; - NTIP.buildList(finalGear); - NTIP.buildFinalGear(finalGear); + NTIP.buildList(finalGear); + NTIP.buildFinalGear(finalGear); - return build; - })(); + return build; + })(); })(module); diff --git a/libs/SoloPlay/BuildFiles/paladin/paladin.ClassicauradinBuild.js b/libs/SoloPlay/BuildFiles/paladin/paladin.ClassicauradinBuild.js index df7d9d75..44d2a1ae 100644 --- a/libs/SoloPlay/BuildFiles/paladin/paladin.ClassicauradinBuild.js +++ b/libs/SoloPlay/BuildFiles/paladin/paladin.ClassicauradinBuild.js @@ -7,152 +7,152 @@ (function (module) { - module.exports = (function () { - // idea: since this is based on the classic build, could do tri-element instead of just normal auradin - // max Holy Shock/Freeze then use HoJ + Dragon Armor/Shield for level 44 Holy Fire - // could even then do infinity for the merc to still have some conviction - const build = { - caster: false, - skillstab: sdk.skills.tabs.PalaCombat, - wantedskills: [sdk.skills.Zeal, sdk.skills.HolyShock], - usefulskills: [sdk.skills.HolyShield, sdk.skills.HolyFreeze, sdk.skills.ResistCold, sdk.skills.ResistLightning], - precastSkills: [sdk.skills.HolyShield], - wantedMerc: MercData[sdk.skills.HolyFreeze], - skills: [ - [sdk.skills.Zeal, 4], - [sdk.skills.Redemption, 1], - [sdk.skills.HolyShield, 1], - [sdk.skills.HolyShock, 20], - [sdk.skills.HolyFreeze, 20], - [sdk.skills.Salvation, 20, false], - [sdk.skills.ResistLightning, 20, false], - [sdk.skills.Zeal, 20, false], - ], - stats: [], + module.exports = (function () { + // idea: since this is based on the classic build, could do tri-element instead of just normal auradin + // max Holy Shock/Freeze then use HoJ + Dragon Armor/Shield for level 44 Holy Fire + // could even then do infinity for the merc to still have some conviction + const build = { + caster: false, + skillstab: sdk.skills.tabs.PalaCombat, + wantedskills: [sdk.skills.Zeal, sdk.skills.HolyShock], + usefulskills: [sdk.skills.HolyShield, sdk.skills.HolyFreeze, sdk.skills.ResistCold, sdk.skills.ResistLightning], + precastSkills: [sdk.skills.HolyShield], + wantedMerc: MercData[sdk.skills.HolyFreeze], + skills: [ + [sdk.skills.Zeal, 4], + [sdk.skills.Redemption, 1], + [sdk.skills.HolyShield, 1], + [sdk.skills.HolyShock, 20], + [sdk.skills.HolyFreeze, 20], + [sdk.skills.Salvation, 20, false], + [sdk.skills.ResistLightning, 20, false], + [sdk.skills.Zeal, 20, false], + ], + stats: [], - charms: { - ResLife: { - max: 6, - have: [], - classid: sdk.items.SmallCharm, - stats: function (check) { - return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MaxHp) === 20); - } - }, + charms: { + ResLife: { + max: 6, + have: [], + classid: sdk.items.SmallCharm, + stats: function (check) { + return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MaxHp) === 20); + } + }, - ResMf: { - max: 2, - have: [], - classid: sdk.items.SmallCharm, - stats: function (check) { - return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MagicBonus) === 7); - } - }, + ResMf: { + max: 2, + have: [], + classid: sdk.items.SmallCharm, + stats: function (check) { + return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MagicBonus) === 7); + } + }, - Skiller: { - max: 2, - have: [], - classid: sdk.items.GrandCharm, - stats: function (check) { - return (!check.unique && check.classid === this.classid && check.getStat(sdk.stats.AddSkillTab, sdk.skills.tabs.Offensive) === 1 - && check.getStat(sdk.stats.MaxHp) >= 40); - } - }, - }, - - AutoBuildTemplate: { - 1: { - Update: function () { - Config.Vigor = false; - Config.AttackSkill = [-1, sdk.skills.Zeal, sdk.skills.HolyShock, sdk.skills.Zeal, sdk.skills.HolyShock, sdk.skills.Zeal, sdk.skills.HolyFreeze]; - Config.LowManaSkill = [-1, -1]; - Config.SkipImmune = ["lightning and cold and physical"]; // Don't think this ever happens but should skip if it does + Skiller: { + max: 2, + have: [], + classid: sdk.items.GrandCharm, + stats: function (check) { + return (!check.unique && check.classid === this.classid && check.getStat(sdk.stats.AddSkillTab, sdk.skills.tabs.Offensive) === 1 + && check.getStat(sdk.stats.MaxHp) >= 40); + } + }, + }, + + AutoBuildTemplate: { + 1: { + Update: function () { + Config.Vigor = false; + Config.AttackSkill = [-1, sdk.skills.Zeal, sdk.skills.HolyShock, sdk.skills.Zeal, sdk.skills.HolyShock, sdk.skills.Zeal, sdk.skills.HolyFreeze]; + Config.LowManaSkill = [-1, -1]; + Config.SkipImmune = ["lightning and cold and physical"]; // Don't think this ever happens but should skip if it does - Config.BeltColumn = ["hp", "hp", "mp", "rv"]; - } - }, - }, + Config.BeltColumn = ["hp", "hp", "mp", "rv"]; + } + }, + }, - respec: function () { - if (me.classic) { - return me.charlvl >= 75 && me.diablo; - } else { - return me.haveAll([{ name: sdk.locale.items.Dream, itemtype: sdk.items.type.AuricShields }, { name: sdk.locale.items.Dream, itemtype: sdk.items.type.Helm }]); - } - }, + respec: function () { + if (me.classic) { + return me.charlvl >= 75 && me.diablo; + } else { + return me.haveAll([{ name: sdk.locale.items.Dream, itemtype: sdk.items.type.AuricShields }, { name: sdk.locale.items.Dream, itemtype: sdk.items.type.Helm }]); + } + }, - active: function () { - return this.respec() && me.getSkill(sdk.skills.HolyShock, sdk.skills.subindex.HardPoints) === 20; - }, - }; - - build.stats = me.classic - ? [ - ["strength", 80], ["vitality", "all"] - ] - : [ - ["strength", 103], ["dexterity", 136], ["vitality", 300], - ["dexterity", "block"], ["vitality", "all"] - ]; - - let finalGear = me.classic - ? [ - // Weapon - "[name] == warscepter && [quality] >= magic # [paladinskills] == 2 && [ias] == 40 && [skillholyshock] >= 1 # [tier] == tierscore(item, 100000)", - // Helm - Tarnhelm - "[name] == skullcap && [quality] == unique # [itemallskills] == 1 && [itemmagicbonus] >= 25 # [tier] == tierscore(item, 100000)", - // Shield - "[type] == shield && [quality] >= magic # [paladinskills] == 2 && [allres] >= 16 # [tier] == tierscore(item, 100000)", - // Rings - SoJ - "[type] == ring && [quality] == unique # [itemmaxmanapercent] == 25 # [tier] == 100000", - // Amulet - "[type] == amulet && [quality] >= magic # [paladinskills] == 2 && [allres] >= 16 # [tier] == tierscore(item, 100000)", - // Boots - Hsaru's Iron Heel - "[name] == chainboots && [quality] == set # [frw] == 20 # [tier] == 100000", - // Belt - Hsaru's Iron Stay - "[name] == belt && [quality] == set # [coldresist] == 20 && [maxhp] == 20 # [tier] == 100000", - ] : [ - // Final Weapon - HoJ - "[type] == sword && [flag] == runeword # [holyfireaura] >= 16 # [tier] == 120000", - // Weapon - Crescent Moon - "[type] == sword && [flag] == runeword # [ias] >= 20 && [passiveltngpierce] >= 35 # [tier] == 110000", - // Weapon - Voice of Reason - "[type] == sword && [flag] == runeword # [passivecoldpierce] >= 24 # [tier] == 102500", - // Helm - Dream - "[type] == helm && [flag] == runeword # [holyshockaura] >= 15 # [tier] == 110000", - // Belt - TGods - "[name] == warbelt && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 160 # [tier] == tierscore(item, 110000)", - // Boots - Gore Rider - "[name] == warboots && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 160 # [tier] == tierscore(item, 110000)", - // Armor - Dragon - "[type] == armor && [flag] != ethereal && [flag] == runeword # [holyfireaura] >= 14 # [tier] == 110000", - // Shield - Dream - "[type] == auricshields && [flag] != ethereal && [flag] == runeword # [holyshockaura] >= 15 # [tier] == 110000", - // Gloves - Laying of Hand's - "[name] == bramblemitts && [quality] == set && [flag] != ethereal # [ias] >= 20 # [tier] == 110000", - // Amulet - Highlords - "[type] == amulet && [quality] == unique # [lightresist] == 35 # [tier] == 100000", - // Final Rings - Perfect Raven Frost & Bul-Kathos' Wedding Band - "[type] == ring && [quality] == unique # [dexterity] == 20 && [tohit] == 250 # [tier] == 110000", - "[type] == ring && [quality] == unique # [maxstamina] == 50 && [lifeleech] == 5 # [tier] == 110000", - // Rings - Raven Frost && Bul-Kathos' Wedding Band - "[type] == ring && [quality] == unique # [dexterity] >= 15 && [tohit] >= 150 # [tier] == 100000", - "[type] == ring && [quality] == unique # [maxstamina] == 50 && [lifeleech] >= 3 # [tier] == 100000", - // Switch - CTA - "[minimumsockets] >= 5 && [flag] == runeword # [plusskillbattleorders] >= 1 # [secondarytier] == 100000", - // Merc Final Armor - Fortitude - "[type] == armor && [flag] == runeword # [enhanceddefense] >= 200 && [enhanceddamage] >= 300 # [merctier] == 100000", - // Merc Armor - Treachery - "[type] == armor && [flag] == runeword # [ias] == 45 && [coldresist] == 30 # [merctier] == 50000 + mercscore(item)", - // Merc Final Helmet - Eth Andy's - "[name] == demonhead && [quality] == unique && [flag] == ethereal # [strength] >= 25 && [enhanceddefense] >= 100 # [merctier] == 50000 + mercscore(item)", - // Merc Helmet - Andy's - "[name] == demonhead && [quality] == unique && [flag] != ethereal # [strength] >= 25 && [enhanceddefense] >= 100 # [merctier] == 40000 + mercscore(item)", - ]; + active: function () { + return this.respec() && me.getSkill(sdk.skills.HolyShock, sdk.skills.subindex.HardPoints) === 20; + }, + }; + + build.stats = me.classic + ? [ + ["strength", 80], ["vitality", "all"] + ] + : [ + ["strength", 103], ["dexterity", 136], ["vitality", 300], + ["dexterity", "block"], ["vitality", "all"] + ]; + + let finalGear = me.classic + ? [ + // Weapon + "[name] == warscepter && [quality] >= magic # [paladinskills] == 2 && [ias] == 40 && [skillholyshock] >= 1 # [tier] == tierscore(item, 100000)", + // Helm - Tarnhelm + "[name] == skullcap && [quality] == unique # [itemallskills] == 1 && [itemmagicbonus] >= 25 # [tier] == tierscore(item, 100000)", + // Shield + "[type] == shield && [quality] >= magic # [paladinskills] == 2 && [allres] >= 16 # [tier] == tierscore(item, 100000)", + // Rings - SoJ + "[type] == ring && [quality] == unique # [itemmaxmanapercent] == 25 # [tier] == 100000", + // Amulet + "[type] == amulet && [quality] >= magic # [paladinskills] == 2 && [allres] >= 16 # [tier] == tierscore(item, 100000)", + // Boots - Hsaru's Iron Heel + "[name] == chainboots && [quality] == set # [frw] == 20 # [tier] == 100000", + // Belt - Hsaru's Iron Stay + "[name] == belt && [quality] == set # [coldresist] == 20 && [maxhp] == 20 # [tier] == 100000", + ] : [ + // Final Weapon - HoJ + "[type] == sword && [flag] == runeword # [holyfireaura] >= 16 # [tier] == 120000", + // Weapon - Crescent Moon + "[type] == sword && [flag] == runeword # [ias] >= 20 && [passiveltngpierce] >= 35 # [tier] == 110000", + // Weapon - Voice of Reason + "[type] == sword && [flag] == runeword # [passivecoldpierce] >= 24 # [tier] == 102500", + // Helm - Dream + "[type] == helm && [flag] == runeword # [holyshockaura] >= 15 # [tier] == 110000", + // Belt - TGods + "[name] == warbelt && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 160 # [tier] == tierscore(item, 110000)", + // Boots - Gore Rider + "[name] == warboots && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 160 # [tier] == tierscore(item, 110000)", + // Armor - Dragon + "[type] == armor && [flag] != ethereal && [flag] == runeword # [holyfireaura] >= 14 # [tier] == 110000", + // Shield - Dream + "[type] == auricshields && [flag] != ethereal && [flag] == runeword # [holyshockaura] >= 15 # [tier] == 110000", + // Gloves - Laying of Hand's + "[name] == bramblemitts && [quality] == set && [flag] != ethereal # [ias] >= 20 # [tier] == 110000", + // Amulet - Highlords + "[type] == amulet && [quality] == unique # [lightresist] == 35 # [tier] == 100000", + // Final Rings - Perfect Raven Frost & Bul-Kathos' Wedding Band + "[type] == ring && [quality] == unique # [dexterity] == 20 && [tohit] == 250 # [tier] == 110000", + "[type] == ring && [quality] == unique # [maxstamina] == 50 && [lifeleech] == 5 # [tier] == 110000", + // Rings - Raven Frost && Bul-Kathos' Wedding Band + "[type] == ring && [quality] == unique # [dexterity] >= 15 && [tohit] >= 150 # [tier] == 100000", + "[type] == ring && [quality] == unique # [maxstamina] == 50 && [lifeleech] >= 3 # [tier] == 100000", + // Switch - CTA + "[minimumsockets] >= 5 && [flag] == runeword # [plusskillbattleorders] >= 1 # [secondarytier] == 100000", + // Merc Final Armor - Fortitude + "[type] == armor && [flag] == runeword # [enhanceddefense] >= 200 && [enhanceddamage] >= 300 # [merctier] == 100000", + // Merc Armor - Treachery + "[type] == armor && [flag] == runeword # [ias] == 45 && [coldresist] == 30 # [merctier] == 50000 + mercscore(item)", + // Merc Final Helmet - Eth Andy's + "[name] == demonhead && [quality] == unique && [flag] == ethereal # [strength] >= 25 && [enhanceddefense] >= 100 # [merctier] == 50000 + mercscore(item)", + // Merc Helmet - Andy's + "[name] == demonhead && [quality] == unique && [flag] != ethereal # [strength] >= 25 && [enhanceddefense] >= 100 # [merctier] == 40000 + mercscore(item)", + ]; - NTIP.buildList(finalGear); - NTIP.buildFinalGear(finalGear); + NTIP.buildList(finalGear); + NTIP.buildFinalGear(finalGear); - return build; - })(); + return build; + })(); })(module); diff --git a/libs/SoloPlay/BuildFiles/paladin/paladin.HammerdinBuild.js b/libs/SoloPlay/BuildFiles/paladin/paladin.HammerdinBuild.js index 968e6dee..77115cc5 100644 --- a/libs/SoloPlay/BuildFiles/paladin/paladin.HammerdinBuild.js +++ b/libs/SoloPlay/BuildFiles/paladin/paladin.HammerdinBuild.js @@ -8,165 +8,165 @@ (function (module) { - module.exports = (function () { - const build = { - caster: true, - skillstab: sdk.skills.tabs.PalaCombat, - wantedskills: [sdk.skills.BlessedHammer, sdk.skills.Concentration], - usefulskills: [sdk.skills.HolyShield, sdk.skills.BlessedAim], - precastSkills: [sdk.skills.HolyShield], - wantedMerc: MercData[sdk.skills.HolyFreeze], - skills: [ - [sdk.skills.HolyShield, 1], - [sdk.skills.Meditation, 1], - [sdk.skills.Redemption, 1], - [sdk.skills.BlessedHammer, 20], - [sdk.skills.Concentration, 20], - [sdk.skills.Vigor, 20], - [sdk.skills.BlessedAim, 20], - [sdk.skills.HolyShield, 20] - ], - stats: [], + module.exports = (function () { + const build = { + caster: true, + skillstab: sdk.skills.tabs.PalaCombat, + wantedskills: [sdk.skills.BlessedHammer, sdk.skills.Concentration], + usefulskills: [sdk.skills.HolyShield, sdk.skills.BlessedAim], + precastSkills: [sdk.skills.HolyShield], + wantedMerc: MercData[sdk.skills.HolyFreeze], + skills: [ + [sdk.skills.HolyShield, 1], + [sdk.skills.Meditation, 1], + [sdk.skills.Redemption, 1], + [sdk.skills.BlessedHammer, 20], + [sdk.skills.Concentration, 20], + [sdk.skills.Vigor, 20], + [sdk.skills.BlessedAim, 20], + [sdk.skills.HolyShield, 20] + ], + stats: [], - charms: { - ResLife: { - max: 3, - have: [], - classid: sdk.items.SmallCharm, - stats: function (check) { - return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MaxHp) === 20); - } - }, + charms: { + ResLife: { + max: 3, + have: [], + classid: sdk.items.SmallCharm, + stats: function (check) { + return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MaxHp) === 20); + } + }, - ResMf: { - max: 2, - have: [], - classid: sdk.items.SmallCharm, - stats: function (check) { - return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MagicBonus) === 7); - } - }, + ResMf: { + max: 2, + have: [], + classid: sdk.items.SmallCharm, + stats: function (check) { + return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MagicBonus) === 7); + } + }, - ResFHR: { - max: 3, - have: [], - classid: sdk.items.SmallCharm, - stats: function (check) { - return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.FHR) === 5); - } - }, + ResFHR: { + max: 3, + have: [], + classid: sdk.items.SmallCharm, + stats: function (check) { + return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.FHR) === 5); + } + }, - Skiller: { - max: 2, - have: [], - classid: sdk.items.GrandCharm, - stats: function (check) { - return (!check.unique && check.classid === this.classid && check.getStat(sdk.stats.AddSkillTab, sdk.skills.tabs.PalaCombat) === 1 - && check.getStat(sdk.stats.MaxHp) >= 40); - } - }, - }, - - AutoBuildTemplate: { - 1: { - Update: function () { - Config.AttackSkill = [-1, sdk.skills.BlessedHammer, sdk.skills.Concentration, sdk.skills.BlessedHammer, sdk.skills.Concentration, sdk.skills.HolyBolt, sdk.skills.Concentration]; - Config.LowManaSkill = [0, sdk.skills.Concentration]; + Skiller: { + max: 2, + have: [], + classid: sdk.items.GrandCharm, + stats: function (check) { + return (!check.unique && check.classid === this.classid && check.getStat(sdk.stats.AddSkillTab, sdk.skills.tabs.PalaCombat) === 1 + && check.getStat(sdk.stats.MaxHp) >= 40); + } + }, + }, + + AutoBuildTemplate: { + 1: { + Update: function () { + Config.AttackSkill = [-1, sdk.skills.BlessedHammer, sdk.skills.Concentration, sdk.skills.BlessedHammer, sdk.skills.Concentration, sdk.skills.HolyBolt, sdk.skills.Concentration]; + Config.LowManaSkill = [0, sdk.skills.Concentration]; - if (me.hell && !me.accessToAct(5)) { - Config.SkipImmune = ["magic"]; - } - Config.BeltColumn = ["hp", "hp", "mp", "rv"]; - SetUp.belt(); - } - }, - }, + if (me.hell && !me.accessToAct(5)) { + Config.SkipImmune = ["magic"]; + } + Config.BeltColumn = ["hp", "hp", "mp", "rv"]; + SetUp.belt(); + } + }, + }, - respec: function () { - if (me.classic) { - return me.charlvl >= 75 && me.diablo; - } else { - return Check.haveItem("armor", "runeword", "Enigma"); - } - }, + respec: function () { + if (me.classic) { + return me.charlvl >= 75 && me.diablo; + } else { + return Check.haveItem("armor", "runeword", "Enigma"); + } + }, - active: function () { - return this.respec() && me.getSkill(sdk.skills.BlessedHammer, sdk.skills.subindex.HardPoints) === 20; - }, - }; + active: function () { + return this.respec() && me.getSkill(sdk.skills.BlessedHammer, sdk.skills.subindex.HardPoints) === 20; + }, + }; - build.stats = me.classic - ? [ - ["dexterity", 51], ["strength", 80], ["vitality", "all"] - ] - : [ - ["vitality", 60], ["dexterity", 30], ["strength", 27], - ["vitality", 91], ["dexterity", 44], ["strength", 30], - ["vitality", 96], ["dexterity", 59], ["strength", 60], - ["vitality", 109], ["dexterity", 77], ["strength", 89], - ["vitality", 137], ["dexterity", 89], ["strength", 103], - ["vitality", 173], ["dexterity", 103], - ["vitality", 208], ["dexterity", 118], - ["vitality", 243], ["dexterity", 133], - ["vitality", 279], ["dexterity", 147], - ["vitality", "all"] - ]; - - let finalGear = me.classic - ? [ - // Weapon - Spectral Shard - "[name] == blade && [quality] == unique # [fcr] == 20 && [allres] == 10 # [tier] == 100000", - // Rings - SoJ - "[type] == ring && [quality] == unique # [itemmaxmanapercent] == 25 # [tier] == 100000", - // Gloves - Magefist - "[name] == lightgauntlets && [quality] == unique # [fcr] >= 20 && [addfireskills] == 1 # [tier] == tierscore(item, 100000)", - ] - : [ - // Weapon - HotO - "[type] == mace && [flag] == runeword # [fcr] == 40 # [tier] == 100000", - // Helm - Harlequin's Crest - "[name] == shako && [quality] == unique && [flag] != ethereal # [damageresist] == 10 # [tier] == tierscore(item, 100000)", - // Belt - Arach's - "[name] == spiderwebsash && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 90 # [tier] == tierscore(item, 100000)", - // Final Boots - Sandstorm Treks - "[name] == scarabshellboots && [quality] == unique # [strength]+[vitality] >= 20 # [tier] == tierscore(item, 100000)", - // Boots - War Traveler - "[name] == battleboots && [quality] == unique && [flag] != ethereal # [itemmagicbonus] >= 50 # [tier] == tierscore(item, 5000)", - // Armor - Enigma - "[type] == armor && [flag] != ethereal && [flag] == runeword # [itemallskills] == 2 # [tier] == 100000", - // Final Shield - Spirit - "[type] == auricshields && [flag] == runeword # [fcr] == 35 && [maxmana] == 112 && [coldresist] == 80 # [tier] == 110000", - // Shield - Spirit - "[type] == auricshields && [flag] == runeword # [fcr] >= 25 && [maxmana] >= 89 && [coldresist] == 80 # [tier] == tierscore(item, 100000)", - // Shield - HoZ - "[name] == gildedshield && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 150 # [tier] == tierscore(item, 50000)", - // Final Gloves - Perfect Upp'ed Magefist - "[name] == battlegauntlets && [quality] == unique && [flag] != ethereal # [enhanceddefense] == 30 && [addfireskills] == 1 && [defense] == 71 # [tier] == 110000", - // Gloves - Upp'ed Magefist - "[name] == battlegauntlets && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 20 && [addfireskills] == 1 # [tier] == tierscore(item, 100000)", - // Gloves - Magefist - "[name] == lightgauntlets && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 20 && [addfireskills] == 1 # [tier] == tierscore(item, 100000)", - // Amulet - Maras - "[type] == amulet && [quality] == unique # [strength] == 5 && [coldresist] >= 30 # [tier] == tierscore(item, 100000)", - // Final Rings - SoJ & Perfect Raven Frost - "[type] == ring && [quality] == unique # [itemmaxmanapercent] == 25 # [tier] == 100000", - "[type] == ring && [quality] == unique # [dexterity] >= 20 # [tier] == 100000", - // Rings - Dwarf Star & Raven Frost - "[name] == ring && [quality] == unique # [maxhp] >= 40 && [magicdamagereduction] >= 12 # [tier] == 99000", - "[type] == ring && [quality] == unique # [dexterity] >= 20 # [tier] == 99000", - // Switch - CTA - "[minimumsockets] >= 5 && [flag] == runeword # [plusskillbattleorders] >= 1 # [secondarytier] == 100000", - // Merc Armor - Fortitude - "[type] == armor && [flag] == runeword # [enhanceddefense] >= 200 && [enhanceddamage] >= 300 # [merctier] == 100000", - // Merc Final Helmet - Eth Andy's - "[name] == demonhead && [quality] == unique && [flag] == ethereal # [strength] >= 25 && [enhanceddefense] >= 100 # [merctier] == 50000 + mercscore(item)", - // Merc Helmet - Andy's - "[name] == demonhead && [quality] == unique && [flag] != ethereal # [strength] >= 25 && [enhanceddefense] >= 100 # [merctier] == 40000 + mercscore(item)", - ]; + build.stats = me.classic + ? [ + ["dexterity", 51], ["strength", 80], ["vitality", "all"] + ] + : [ + ["vitality", 60], ["dexterity", 30], ["strength", 27], + ["vitality", 91], ["dexterity", 44], ["strength", 30], + ["vitality", 96], ["dexterity", 59], ["strength", 60], + ["vitality", 109], ["dexterity", 77], ["strength", 89], + ["vitality", 137], ["dexterity", 89], ["strength", 103], + ["vitality", 173], ["dexterity", 103], + ["vitality", 208], ["dexterity", 118], + ["vitality", 243], ["dexterity", 133], + ["vitality", 279], ["dexterity", 147], + ["vitality", "all"] + ]; + + let finalGear = me.classic + ? [ + // Weapon - Spectral Shard + "[name] == blade && [quality] == unique # [fcr] == 20 && [allres] == 10 # [tier] == 100000", + // Rings - SoJ + "[type] == ring && [quality] == unique # [itemmaxmanapercent] == 25 # [tier] == 100000", + // Gloves - Magefist + "[name] == lightgauntlets && [quality] == unique # [fcr] >= 20 && [addfireskills] == 1 # [tier] == tierscore(item, 100000)", + ] + : [ + // Weapon - HotO + "[type] == mace && [flag] == runeword # [fcr] == 40 # [tier] == 100000", + // Helm - Harlequin's Crest + "[name] == shako && [quality] == unique && [flag] != ethereal # [damageresist] == 10 # [tier] == tierscore(item, 100000)", + // Belt - Arach's + "[name] == spiderwebsash && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 90 # [tier] == tierscore(item, 100000)", + // Final Boots - Sandstorm Treks + "[name] == scarabshellboots && [quality] == unique # [strength]+[vitality] >= 20 # [tier] == tierscore(item, 100000)", + // Boots - War Traveler + "[name] == battleboots && [quality] == unique && [flag] != ethereal # [itemmagicbonus] >= 50 # [tier] == tierscore(item, 5000)", + // Armor - Enigma + "[type] == armor && [flag] != ethereal && [flag] == runeword # [itemallskills] == 2 # [tier] == 100000", + // Final Shield - Spirit + "[type] == auricshields && [flag] == runeword # [fcr] == 35 && [maxmana] == 112 && [coldresist] == 80 # [tier] == 110000", + // Shield - Spirit + "[type] == auricshields && [flag] == runeword # [fcr] >= 25 && [maxmana] >= 89 && [coldresist] == 80 # [tier] == tierscore(item, 100000)", + // Shield - HoZ + "[name] == gildedshield && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 150 # [tier] == tierscore(item, 50000)", + // Final Gloves - Perfect Upp'ed Magefist + "[name] == battlegauntlets && [quality] == unique && [flag] != ethereal # [enhanceddefense] == 30 && [addfireskills] == 1 && [defense] == 71 # [tier] == 110000", + // Gloves - Upp'ed Magefist + "[name] == battlegauntlets && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 20 && [addfireskills] == 1 # [tier] == tierscore(item, 100000)", + // Gloves - Magefist + "[name] == lightgauntlets && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 20 && [addfireskills] == 1 # [tier] == tierscore(item, 100000)", + // Amulet - Maras + "[type] == amulet && [quality] == unique # [strength] == 5 && [coldresist] >= 30 # [tier] == tierscore(item, 100000)", + // Final Rings - SoJ & Perfect Raven Frost + "[type] == ring && [quality] == unique # [itemmaxmanapercent] == 25 # [tier] == 100000", + "[type] == ring && [quality] == unique # [dexterity] >= 20 # [tier] == 100000", + // Rings - Dwarf Star & Raven Frost + "[name] == ring && [quality] == unique # [maxhp] >= 40 && [magicdamagereduction] >= 12 # [tier] == 99000", + "[type] == ring && [quality] == unique # [dexterity] >= 20 # [tier] == 99000", + // Switch - CTA + "[minimumsockets] >= 5 && [flag] == runeword # [plusskillbattleorders] >= 1 # [secondarytier] == 100000", + // Merc Armor - Fortitude + "[type] == armor && [flag] == runeword # [enhanceddefense] >= 200 && [enhanceddamage] >= 300 # [merctier] == 100000", + // Merc Final Helmet - Eth Andy's + "[name] == demonhead && [quality] == unique && [flag] == ethereal # [strength] >= 25 && [enhanceddefense] >= 100 # [merctier] == 50000 + mercscore(item)", + // Merc Helmet - Andy's + "[name] == demonhead && [quality] == unique && [flag] != ethereal # [strength] >= 25 && [enhanceddefense] >= 100 # [merctier] == 40000 + mercscore(item)", + ]; - NTIP.buildList(finalGear); - NTIP.buildFinalGear(finalGear); + NTIP.buildList(finalGear); + NTIP.buildFinalGear(finalGear); - return build; - })(); + return build; + })(); })(module); diff --git a/libs/SoloPlay/BuildFiles/paladin/paladin.HammershockBuild.js b/libs/SoloPlay/BuildFiles/paladin/paladin.HammershockBuild.js index f5179706..bf53e16f 100644 --- a/libs/SoloPlay/BuildFiles/paladin/paladin.HammershockBuild.js +++ b/libs/SoloPlay/BuildFiles/paladin/paladin.HammershockBuild.js @@ -7,152 +7,152 @@ (function (module) { - module.exports = (function () { - const build = { - caster: false, - skillstab: sdk.skills.tabs.PalaCombat, - wantedskills: [sdk.skills.BlessedHammer, sdk.skills.HolyShock], - usefulskills: [sdk.skills.HolyShield, sdk.skills.ResistLightning, sdk.skills.Zeal, sdk.skills.Concentration, sdk.skills.Vigor, sdk.skills.BlessedAim], - precastSkills: [sdk.skills.HolyShield], - usefulStats: [sdk.stats.PierceLtng, sdk.stats.PassiveLightningMastery, sdk.stats.PassiveLightningPierce], - wantedMerc: MercData[sdk.skills.HolyFreeze], - skills: [ - [sdk.skills.Zeal, 4], - [sdk.skills.Vengeance, 1], - [sdk.skills.Redemption, 1], - [sdk.skills.Salvation, 1], - [sdk.skills.HolyShield, 1], - [sdk.skills.Concentration, 1], - [sdk.skills.BlessedHammer, 20], - [sdk.skills.HolyShock, 20], - [sdk.skills.BlessedAim, 20, false], - [sdk.skills.ResistLightning, 20, false], - [sdk.skills.Zeal, 20, false], - ], - stats: [], + module.exports = (function () { + const build = { + caster: false, + skillstab: sdk.skills.tabs.PalaCombat, + wantedskills: [sdk.skills.BlessedHammer, sdk.skills.HolyShock], + usefulskills: [sdk.skills.HolyShield, sdk.skills.ResistLightning, sdk.skills.Zeal, sdk.skills.Concentration, sdk.skills.Vigor, sdk.skills.BlessedAim], + precastSkills: [sdk.skills.HolyShield], + usefulStats: [sdk.stats.PierceLtng, sdk.stats.PassiveLightningMastery, sdk.stats.PassiveLightningPierce], + wantedMerc: MercData[sdk.skills.HolyFreeze], + skills: [ + [sdk.skills.Zeal, 4], + [sdk.skills.Vengeance, 1], + [sdk.skills.Redemption, 1], + [sdk.skills.Salvation, 1], + [sdk.skills.HolyShield, 1], + [sdk.skills.Concentration, 1], + [sdk.skills.BlessedHammer, 20], + [sdk.skills.HolyShock, 20], + [sdk.skills.BlessedAim, 20, false], + [sdk.skills.ResistLightning, 20, false], + [sdk.skills.Zeal, 20, false], + ], + stats: [], - charms: { - ResLife: { - max: 6, - have: [], - classid: sdk.items.SmallCharm, - stats: function (check) { - return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MaxHp) === 20); - } - }, + charms: { + ResLife: { + max: 6, + have: [], + classid: sdk.items.SmallCharm, + stats: function (check) { + return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MaxHp) === 20); + } + }, - ResMf: { - max: 2, - have: [], - classid: sdk.items.SmallCharm, - stats: function (check) { - return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MagicBonus) === 7); - } - }, + ResMf: { + max: 2, + have: [], + classid: sdk.items.SmallCharm, + stats: function (check) { + return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MagicBonus) === 7); + } + }, - Skiller: { - max: 2, - have: [], - classid: sdk.items.GrandCharm, - stats: function (check) { - return (!check.unique && check.classid === this.classid && check.getStat(sdk.stats.AddSkillTab, sdk.skills.tabs.Offensive) === 1 - && check.getStat(sdk.stats.MaxHp) >= 40); - } - }, - }, + Skiller: { + max: 2, + have: [], + classid: sdk.items.GrandCharm, + stats: function (check) { + return (!check.unique && check.classid === this.classid && check.getStat(sdk.stats.AddSkillTab, sdk.skills.tabs.Offensive) === 1 + && check.getStat(sdk.stats.MaxHp) >= 40); + } + }, + }, - AutoBuildTemplate: { - 1: { - Update: function () { - Config.Vigor = false; - Config.AttackSkill = [-1, sdk.skills.BlessedHammer, sdk.skills.Concentration, sdk.skills.BlessedHammer, sdk.skills.Concentration, sdk.skills.Zeal, sdk.skills.HolyShock]; - Config.LowManaSkill = [-1, -1]; - Config.SkipImmune = ["magic and lightning and physical"]; // Don't think this ever happens but should skip if it does - Config.BeltColumn = ["hp", "hp", "mp", "rv"]; - SetUp.belt(); - } - }, - }, + AutoBuildTemplate: { + 1: { + Update: function () { + Config.Vigor = false; + Config.AttackSkill = [-1, sdk.skills.BlessedHammer, sdk.skills.Concentration, sdk.skills.BlessedHammer, sdk.skills.Concentration, sdk.skills.Zeal, sdk.skills.HolyShock]; + Config.LowManaSkill = [-1, -1]; + Config.SkipImmune = ["magic and lightning and physical"]; // Don't think this ever happens but should skip if it does + Config.BeltColumn = ["hp", "hp", "mp", "rv"]; + SetUp.belt(); + } + }, + }, - respec: function () { - if (me.classic) { - return me.charlvl >= 75 && me.diablo; - } else { - return Check.haveItem("scepter", "unique", "Heaven's Light") && Check.haveItem("armor", "runeword", "Enigma"); - } - }, + respec: function () { + if (me.classic) { + return me.charlvl >= 75 && me.diablo; + } else { + return Check.haveItem("scepter", "unique", "Heaven's Light") && Check.haveItem("armor", "runeword", "Enigma"); + } + }, - active: function () { - return this.respec() && (me.getSkill(sdk.skills.HolyShock, sdk.skills.subindex.HardPoints) === 20 && me.getSkill(sdk.skills.BlessedHammer, sdk.skills.subindex.HardPoints) === 20); - }, - }; + active: function () { + return this.respec() && (me.getSkill(sdk.skills.HolyShock, sdk.skills.subindex.HardPoints) === 20 && me.getSkill(sdk.skills.BlessedHammer, sdk.skills.subindex.HardPoints) === 20); + }, + }; - build.stats = me.classic - ? [ - ["strength", 80], ["vitality", "all"] - ] - : [ - ["strength", 103], ["dexterity", 136], ["vitality", 300], - ["dexterity", "block"], ["vitality", "all"] - ]; - - let finalGear = me.classic - ? [ - // Weapon - "[name] == warscepter && [quality] >= magic # [paladinskills] == 2 && [ias] == 40 && [skillholyshock] >= 1 # [tier] == tierscore(item, 100000)", - // Helm - Tarnhelm - "[name] == skullcap && [quality] == unique # [itemallskills] == 1 && [itemmagicbonus] >= 25 # [tier] == tierscore(item, 100000)", - // Shield - "[type] == shield && [quality] >= magic # [paladinskills] == 2 && [allres] >= 16 # [tier] == tierscore(item, 100000)", - // Rings - SoJ - "[type] == ring && [quality] == unique # [itemmaxmanapercent] == 25 # [tier] == 100000", - // Amulet - "[type] == amulet && [quality] >= magic # [paladinskills] == 2 && [allres] >= 16 # [tier] == tierscore(item, 100000)", - // Boots - Hsaru's Iron Heel - "[name] == chainboots && [quality] == set # [frw] == 20 # [tier] == 100000", - // Belt - Hsaru's Iron Stay - "[name] == belt && [quality] == set # [coldresist] == 20 && [maxhp] == 20 # [tier] == 100000", - ] - : [ - // Weapon - Heaven's Light - "[type] == scepter && [quality] == unique && [flag] != ethereal # [paladinskills] >= 2 && [enhanceddamage] >= 250 # [tier] == tierscore(item, 100000)", - // Helm - Harlequin's Crest - "[name] == shako && [quality] == unique && [flag] != ethereal # [damageresist] == 10 # [tier] == tierscore(item, 100000)", - // Belt - TGods - "[name] == warbelt && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 160 # [tier] == tierscore(item, 110000)", - // Boots - Gore Rider - "[name] == warboots && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 160 # [tier] == tierscore(item, 110000)", - // Armor - Enigma - "[type] == armor && [flag] != ethereal && [flag] == runeword # [itemallskills] == 2 # [tier] == 100000", - // Final Shield - Exile - "[type] == auricshields && [flag] == runeword # [defianceaura] >= 13 # [tier] == 110000", - // Shield - HoZ - "[name] == gildedshield && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 150 # [tier] == tierscore(item, 50000)", - // Gloves - Laying of Hand's - "[name] == bramblemitts && [quality] == set && [flag] != ethereal # [ias] >= 20 # [tier] == 110000", - // Amulet - Highlords - "[type] == amulet && [quality] == unique # [lightresist] == 35 # [tier] == 100000", - // Final Rings - Perfect Raven Frost & Bul-Kathos' Wedding Band - "[type] == ring && [quality] == unique # [dexterity] == 20 && [tohit] == 250 # [tier] == 110000", - "[type] == ring && [quality] == unique # [maxstamina] == 50 && [lifeleech] == 5 # [tier] == 110000", - // Rings - Raven Frost && Bul-Kathos' Wedding Band - "[type] == ring && [quality] == unique # [dexterity] >= 15 && [tohit] >= 150 # [tier] == 100000", - "[type] == ring && [quality] == unique # [maxstamina] == 50 && [lifeleech] >= 3 # [tier] == 100000", - // Switch - CTA - "[minimumsockets] >= 5 && [flag] == runeword # [plusskillbattleorders] >= 1 # [secondarytier] == 100000", - // Merc Final Armor - Fortitude - "[type] == armor && [flag] == runeword # [enhanceddefense] >= 200 && [enhanceddamage] >= 300 # [merctier] == 100000", - // Merc Armor - Treachery - "[type] == armor && [flag] == runeword # [ias] == 45 && [coldresist] == 30 # [merctier] == 50000 + mercscore(item)", - // Merc Final Helmet - Eth Andy's - "[name] == demonhead && [quality] == unique && [flag] == ethereal # [strength] >= 25 && [enhanceddefense] >= 100 # [merctier] == 50000 + mercscore(item)", - // Merc Helmet - Andy's - "[name] == demonhead && [quality] == unique && [flag] != ethereal # [strength] >= 25 && [enhanceddefense] >= 100 # [merctier] == 40000 + mercscore(item)", - ]; + build.stats = me.classic + ? [ + ["strength", 80], ["vitality", "all"] + ] + : [ + ["strength", 103], ["dexterity", 136], ["vitality", 300], + ["dexterity", "block"], ["vitality", "all"] + ]; + + let finalGear = me.classic + ? [ + // Weapon + "[name] == warscepter && [quality] >= magic # [paladinskills] == 2 && [ias] == 40 && [skillholyshock] >= 1 # [tier] == tierscore(item, 100000)", + // Helm - Tarnhelm + "[name] == skullcap && [quality] == unique # [itemallskills] == 1 && [itemmagicbonus] >= 25 # [tier] == tierscore(item, 100000)", + // Shield + "[type] == shield && [quality] >= magic # [paladinskills] == 2 && [allres] >= 16 # [tier] == tierscore(item, 100000)", + // Rings - SoJ + "[type] == ring && [quality] == unique # [itemmaxmanapercent] == 25 # [tier] == 100000", + // Amulet + "[type] == amulet && [quality] >= magic # [paladinskills] == 2 && [allres] >= 16 # [tier] == tierscore(item, 100000)", + // Boots - Hsaru's Iron Heel + "[name] == chainboots && [quality] == set # [frw] == 20 # [tier] == 100000", + // Belt - Hsaru's Iron Stay + "[name] == belt && [quality] == set # [coldresist] == 20 && [maxhp] == 20 # [tier] == 100000", + ] + : [ + // Weapon - Heaven's Light + "[type] == scepter && [quality] == unique && [flag] != ethereal # [paladinskills] >= 2 && [enhanceddamage] >= 250 # [tier] == tierscore(item, 100000)", + // Helm - Harlequin's Crest + "[name] == shako && [quality] == unique && [flag] != ethereal # [damageresist] == 10 # [tier] == tierscore(item, 100000)", + // Belt - TGods + "[name] == warbelt && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 160 # [tier] == tierscore(item, 110000)", + // Boots - Gore Rider + "[name] == warboots && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 160 # [tier] == tierscore(item, 110000)", + // Armor - Enigma + "[type] == armor && [flag] != ethereal && [flag] == runeword # [itemallskills] == 2 # [tier] == 100000", + // Final Shield - Exile + "[type] == auricshields && [flag] == runeword # [defianceaura] >= 13 # [tier] == 110000", + // Shield - HoZ + "[name] == gildedshield && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 150 # [tier] == tierscore(item, 50000)", + // Gloves - Laying of Hand's + "[name] == bramblemitts && [quality] == set && [flag] != ethereal # [ias] >= 20 # [tier] == 110000", + // Amulet - Highlords + "[type] == amulet && [quality] == unique # [lightresist] == 35 # [tier] == 100000", + // Final Rings - Perfect Raven Frost & Bul-Kathos' Wedding Band + "[type] == ring && [quality] == unique # [dexterity] == 20 && [tohit] == 250 # [tier] == 110000", + "[type] == ring && [quality] == unique # [maxstamina] == 50 && [lifeleech] == 5 # [tier] == 110000", + // Rings - Raven Frost && Bul-Kathos' Wedding Band + "[type] == ring && [quality] == unique # [dexterity] >= 15 && [tohit] >= 150 # [tier] == 100000", + "[type] == ring && [quality] == unique # [maxstamina] == 50 && [lifeleech] >= 3 # [tier] == 100000", + // Switch - CTA + "[minimumsockets] >= 5 && [flag] == runeword # [plusskillbattleorders] >= 1 # [secondarytier] == 100000", + // Merc Final Armor - Fortitude + "[type] == armor && [flag] == runeword # [enhanceddefense] >= 200 && [enhanceddamage] >= 300 # [merctier] == 100000", + // Merc Armor - Treachery + "[type] == armor && [flag] == runeword # [ias] == 45 && [coldresist] == 30 # [merctier] == 50000 + mercscore(item)", + // Merc Final Helmet - Eth Andy's + "[name] == demonhead && [quality] == unique && [flag] == ethereal # [strength] >= 25 && [enhanceddefense] >= 100 # [merctier] == 50000 + mercscore(item)", + // Merc Helmet - Andy's + "[name] == demonhead && [quality] == unique && [flag] != ethereal # [strength] >= 25 && [enhanceddefense] >= 100 # [merctier] == 40000 + mercscore(item)", + ]; - NTIP.buildList(finalGear); - NTIP.buildFinalGear(finalGear); + NTIP.buildList(finalGear); + NTIP.buildFinalGear(finalGear); - return build; - })(); + return build; + })(); })(module); diff --git a/libs/SoloPlay/BuildFiles/paladin/paladin.LevelingBuild.js b/libs/SoloPlay/BuildFiles/paladin/paladin.LevelingBuild.js index dd3fa37f..8deefa42 100644 --- a/libs/SoloPlay/BuildFiles/paladin/paladin.LevelingBuild.js +++ b/libs/SoloPlay/BuildFiles/paladin/paladin.LevelingBuild.js @@ -8,117 +8,117 @@ (function (module) { - module.exports = (function () { - const build = { - caster: true, - skillstab: sdk.skills.tabs.PalaCombat, - wantedskills: [sdk.skills.BlessedHammer, sdk.skills.Concentration], - usefulskills: [sdk.skills.HolyShield, sdk.skills.BlessedAim], - wantedMerc: MercData[sdk.skills.HolyFreeze], - stats: [], - skills: [ - [sdk.skills.Might, 1], - [sdk.skills.Smite, 1], - [sdk.skills.Prayer, 1], - [sdk.skills.HolyBolt, 1], - [sdk.skills.Defiance, 1], - [sdk.skills.Charge, 1], - [sdk.skills.BlessedAim, 1], - [sdk.skills.Cleansing, 1], - [sdk.skills.BlessedAim, 6], - [sdk.skills.BlessedHammer, 1], - [sdk.skills.Concentration, 1], - [sdk.skills.Vigor, 1], - [sdk.skills.BlessedAim, 7], - [sdk.skills.BlessedHammer, 2], - [sdk.skills.Concentration, 2], - [sdk.skills.Vigor, 2], - [sdk.skills.BlessedHammer, 7], - [sdk.skills.HolyShield, 1], - [sdk.skills.Meditation, 1], - [sdk.skills.BlessedHammer, 12], - [sdk.skills.Redemption, 1], - [sdk.skills.BlessedHammer, 20], - [sdk.skills.Concentration, 3], - [sdk.skills.Vigor, 3], - [sdk.skills.Concentration, 4], - [sdk.skills.Vigor, 4], - [sdk.skills.Concentration, 5], - [sdk.skills.Vigor, 5], - [sdk.skills.Concentration, 6], - [sdk.skills.Vigor, 6], - [sdk.skills.Concentration, 7], - [sdk.skills.Vigor, 7], - [sdk.skills.Concentration, 8], - [sdk.skills.Vigor, 8], - [sdk.skills.Concentration, 9], - [sdk.skills.Vigor, 9], - [sdk.skills.Concentration, 10], - [sdk.skills.Vigor, 10], - [sdk.skills.Concentration, 11], - [sdk.skills.Vigor, 11], - [sdk.skills.Concentration, 12], - [sdk.skills.Vigor, 12], - [sdk.skills.Concentration, 13], - [sdk.skills.Vigor, 13], - [sdk.skills.Concentration, 14], - [sdk.skills.Vigor, 14], - [sdk.skills.Concentration, 15], - [sdk.skills.Vigor, 15], - [sdk.skills.Concentration, 16], - [sdk.skills.Vigor, 16], - [sdk.skills.Concentration, 17], - [sdk.skills.Vigor, 17], - [sdk.skills.Concentration, 18], - [sdk.skills.Vigor, 18], - [sdk.skills.Concentration, 19], - [sdk.skills.Vigor, 19], - [sdk.skills.Concentration, 20], - [sdk.skills.Vigor, 20], - [sdk.skills.BlessedAim, 20], - [sdk.skills.HolyShield, 20] - ], + module.exports = (function () { + const build = { + caster: true, + skillstab: sdk.skills.tabs.PalaCombat, + wantedskills: [sdk.skills.BlessedHammer, sdk.skills.Concentration], + usefulskills: [sdk.skills.HolyShield, sdk.skills.BlessedAim], + wantedMerc: MercData[sdk.skills.HolyFreeze], + stats: [], + skills: [ + [sdk.skills.Might, 1], + [sdk.skills.Smite, 1], + [sdk.skills.Prayer, 1], + [sdk.skills.HolyBolt, 1], + [sdk.skills.Defiance, 1], + [sdk.skills.Charge, 1], + [sdk.skills.BlessedAim, 1], + [sdk.skills.Cleansing, 1], + [sdk.skills.BlessedAim, 6], + [sdk.skills.BlessedHammer, 1], + [sdk.skills.Concentration, 1], + [sdk.skills.Vigor, 1], + [sdk.skills.BlessedAim, 7], + [sdk.skills.BlessedHammer, 2], + [sdk.skills.Concentration, 2], + [sdk.skills.Vigor, 2], + [sdk.skills.BlessedHammer, 7], + [sdk.skills.HolyShield, 1], + [sdk.skills.Meditation, 1], + [sdk.skills.BlessedHammer, 12], + [sdk.skills.Redemption, 1], + [sdk.skills.BlessedHammer, 20], + [sdk.skills.Concentration, 3], + [sdk.skills.Vigor, 3], + [sdk.skills.Concentration, 4], + [sdk.skills.Vigor, 4], + [sdk.skills.Concentration, 5], + [sdk.skills.Vigor, 5], + [sdk.skills.Concentration, 6], + [sdk.skills.Vigor, 6], + [sdk.skills.Concentration, 7], + [sdk.skills.Vigor, 7], + [sdk.skills.Concentration, 8], + [sdk.skills.Vigor, 8], + [sdk.skills.Concentration, 9], + [sdk.skills.Vigor, 9], + [sdk.skills.Concentration, 10], + [sdk.skills.Vigor, 10], + [sdk.skills.Concentration, 11], + [sdk.skills.Vigor, 11], + [sdk.skills.Concentration, 12], + [sdk.skills.Vigor, 12], + [sdk.skills.Concentration, 13], + [sdk.skills.Vigor, 13], + [sdk.skills.Concentration, 14], + [sdk.skills.Vigor, 14], + [sdk.skills.Concentration, 15], + [sdk.skills.Vigor, 15], + [sdk.skills.Concentration, 16], + [sdk.skills.Vigor, 16], + [sdk.skills.Concentration, 17], + [sdk.skills.Vigor, 17], + [sdk.skills.Concentration, 18], + [sdk.skills.Vigor, 18], + [sdk.skills.Concentration, 19], + [sdk.skills.Vigor, 19], + [sdk.skills.Concentration, 20], + [sdk.skills.Vigor, 20], + [sdk.skills.BlessedAim, 20], + [sdk.skills.HolyShield, 20] + ], - AutoBuildTemplate: { - 1: { - Update: function () { - Config.TownHP = me.hardcore ? 0 : 35; - Config.AttackSkill = [ - -1, sdk.skills.BlessedHammer, sdk.skills.Concentration, - sdk.skills.BlessedHammer, sdk.skills.Concentration, - sdk.skills.HolyBolt, sdk.skills.Concentration - ]; - Config.LowManaSkill = [0, sdk.skills.Concentration]; - Config.BeltColumn = ["hp", "hp", "mp", "mp"]; - Config.HPBuffer = me.expansion ? 2 : 4; - Config.MPBuffer = me.expansion && me.charlvl < 80 ? 6 : me.classic ? 5 : 2; - (me.hell && !me.accessToAct(5)) && (Config.SkipImmune = ["magic"]); - SetUp.belt(); - } - } - }, + AutoBuildTemplate: { + 1: { + Update: function () { + Config.TownHP = me.hardcore ? 0 : 35; + Config.AttackSkill = [ + -1, sdk.skills.BlessedHammer, sdk.skills.Concentration, + sdk.skills.BlessedHammer, sdk.skills.Concentration, + sdk.skills.HolyBolt, sdk.skills.Concentration + ]; + Config.LowManaSkill = [0, sdk.skills.Concentration]; + Config.BeltColumn = ["hp", "hp", "mp", "mp"]; + Config.HPBuffer = me.expansion ? 2 : 4; + Config.MPBuffer = me.expansion && me.charlvl < 80 ? 6 : me.classic ? 5 : 2; + (me.hell && !me.accessToAct(5)) && (Config.SkipImmune = ["magic"]); + SetUp.belt(); + } + } + }, - active: function () { - return (me.charlvl > CharInfo.respecOne && me.charlvl > CharInfo.respecTwo && me.checkSkill(sdk.skills.Concentration, sdk.skills.subindex.HardPoints) && !Check.finalBuild().active()); - }, - }; + active: function () { + return (me.charlvl > CharInfo.respecOne && me.charlvl > CharInfo.respecTwo && me.checkSkill(sdk.skills.Concentration, sdk.skills.subindex.HardPoints) && !Check.finalBuild().active()); + }, + }; - // Has to be set after its loaded - build.stats = me.classic - ? [["dexterity", 51], ["strength", 80], ["vitality", "all"]] - : [ - ["vitality", 60], ["dexterity", 30], ["strength", 27], - ["vitality", 91], ["dexterity", 44], ["strength", 30], - ["vitality", 96], ["dexterity", 59], ["strength", 60], - ["vitality", 109], ["dexterity", 77], ["strength", 89], - ["vitality", 137], ["dexterity", 89], ["strength", 103], - ["vitality", 173], ["dexterity", 103], - ["vitality", 208], ["dexterity", 118], - ["vitality", 243], ["dexterity", 133], - ["vitality", 279], ["dexterity", 147], - ["vitality", "all"] - ]; - - return build; - })(); + // Has to be set after its loaded + build.stats = me.classic + ? [["dexterity", 51], ["strength", 80], ["vitality", "all"]] + : [ + ["vitality", 60], ["dexterity", 30], ["strength", 27], + ["vitality", 91], ["dexterity", 44], ["strength", 30], + ["vitality", 96], ["dexterity", 59], ["strength", 60], + ["vitality", 109], ["dexterity", 77], ["strength", 89], + ["vitality", 137], ["dexterity", 89], ["strength", 103], + ["vitality", 173], ["dexterity", 103], + ["vitality", 208], ["dexterity", 118], + ["vitality", 243], ["dexterity", 133], + ["vitality", 279], ["dexterity", 147], + ["vitality", "all"] + ]; + + return build; + })(); })(module); diff --git a/libs/SoloPlay/BuildFiles/paladin/paladin.SancdreamerBuild.js b/libs/SoloPlay/BuildFiles/paladin/paladin.SancdreamerBuild.js index 3749d8bf..7a015b14 100644 --- a/libs/SoloPlay/BuildFiles/paladin/paladin.SancdreamerBuild.js +++ b/libs/SoloPlay/BuildFiles/paladin/paladin.SancdreamerBuild.js @@ -10,128 +10,128 @@ (function (module) { - module.exports = (function () { - const build = { - caster: false, - skillstab: sdk.skills.tabs.PalaCombat, - wantedskills: [sdk.skills.Zeal, sdk.skills.Sanctuary], - usefulskills: [sdk.skills.HolyShield, sdk.skills.Sacrifice, sdk.skills.ResistLightning], - precastSkills: [sdk.skills.HolyShield], - usefulStats: [sdk.stats.PassiveLightningMastery, sdk.stats.PassiveLightningPierce, sdk.stats.PierceLtng, sdk.stats.PassiveMagMastery, sdk.stats.PassiveMagPierce], - wantedMerc: MercData[sdk.skills.HolyFreeze], - stats: [ - ["strength", 103], ["dexterity", 136], ["vitality", 300], ["dexterity", "block"], ["vitality", "all"] - ], - skills: [ - [sdk.skills.Sanctuary, 20], - [sdk.skills.Zeal, 4], - [sdk.skills.ResistLightning, 20], - [sdk.skills.Cleansing, 20], - [sdk.skills.Redemption, 1], - [sdk.skills.HolyShield, 15], - [sdk.skills.Sacrifice, 19], - ], + module.exports = (function () { + const build = { + caster: false, + skillstab: sdk.skills.tabs.PalaCombat, + wantedskills: [sdk.skills.Zeal, sdk.skills.Sanctuary], + usefulskills: [sdk.skills.HolyShield, sdk.skills.Sacrifice, sdk.skills.ResistLightning], + precastSkills: [sdk.skills.HolyShield], + usefulStats: [sdk.stats.PassiveLightningMastery, sdk.stats.PassiveLightningPierce, sdk.stats.PierceLtng, sdk.stats.PassiveMagMastery, sdk.stats.PassiveMagPierce], + wantedMerc: MercData[sdk.skills.HolyFreeze], + stats: [ + ["strength", 103], ["dexterity", 136], ["vitality", 300], ["dexterity", "block"], ["vitality", "all"] + ], + skills: [ + [sdk.skills.Sanctuary, 20], + [sdk.skills.Zeal, 4], + [sdk.skills.ResistLightning, 20], + [sdk.skills.Cleansing, 20], + [sdk.skills.Redemption, 1], + [sdk.skills.HolyShield, 15], + [sdk.skills.Sacrifice, 19], + ], - charms: { - ResLife: { - max: 6, - have: [], - classid: sdk.items.SmallCharm, - stats: function (check) { - return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MaxHp) === 20); - } - }, + charms: { + ResLife: { + max: 6, + have: [], + classid: sdk.items.SmallCharm, + stats: function (check) { + return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MaxHp) === 20); + } + }, - ResMf: { - max: 2, - have: [], - classid: sdk.items.SmallCharm, - stats: function (check) { - return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MagicBonus) === 7); - } - }, + ResMf: { + max: 2, + have: [], + classid: sdk.items.SmallCharm, + stats: function (check) { + return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MagicBonus) === 7); + } + }, - Skiller: { - max: 2, - have: [], - classid: sdk.items.GrandCharm, - stats: function (check) { - return (!check.unique && check.classid === this.classid && check.getStat(sdk.stats.AddSkillTab, sdk.skills.tabs.Offensive) === 1 - && check.getStat(sdk.stats.MaxHp) >= 40); - } - }, - }, + Skiller: { + max: 2, + have: [], + classid: sdk.items.GrandCharm, + stats: function (check) { + return (!check.unique && check.classid === this.classid && check.getStat(sdk.stats.AddSkillTab, sdk.skills.tabs.Offensive) === 1 + && check.getStat(sdk.stats.MaxHp) >= 40); + } + }, + }, - AutoBuildTemplate: { - 1: { - Update: function () { - Config.Vigor = false; - Config.AttackSkill = [-1, sdk.skills.Zeal, sdk.skills.Sanctuary, sdk.skills.Zeal, sdk.skills.Sanctuary, -1, -1]; - Config.LowManaSkill = [-1, -1]; + AutoBuildTemplate: { + 1: { + Update: function () { + Config.Vigor = false; + Config.AttackSkill = [-1, sdk.skills.Zeal, sdk.skills.Sanctuary, sdk.skills.Zeal, sdk.skills.Sanctuary, -1, -1]; + Config.LowManaSkill = [-1, -1]; - Config.SkipImmune = ["lightning and magic and physical"]; // Don't think this ever happens but should skip if it does - Config.BeltColumn = ["hp", "hp", "mp", "rv"]; - SetUp.belt(); - } - }, - }, + Config.SkipImmune = ["lightning and magic and physical"]; // Don't think this ever happens but should skip if it does + Config.BeltColumn = ["hp", "hp", "mp", "rv"]; + SetUp.belt(); + } + }, + }, - respec: function () { - if (me.classic) { - return false; - } else { - return me.haveAll([{ name: sdk.locale.items.Dream, itemtype: sdk.items.type.AuricShields }, { name: sdk.locale.items.Dream, itemtype: sdk.items.type.Helm }, - { name: sdk.locale.items.LastWish }]); - } - }, + respec: function () { + if (me.classic) { + return false; + } else { + return me.haveAll([{ name: sdk.locale.items.Dream, itemtype: sdk.items.type.AuricShields }, { name: sdk.locale.items.Dream, itemtype: sdk.items.type.Helm }, + { name: sdk.locale.items.LastWish }]); + } + }, - active: function () { - return this.respec() && me.getSkill(sdk.skills.Sanctuary, sdk.skills.subindex.HardPoints) === 20; - }, - }; - - let finalGear = [ // autoequip final gear - // Final Weapon - Last Wish - "[type] == sword && [flag] == runeword # [mightaura] >= 17 # [tier] == 120000", - // Temporary Weapon - Crescent Moon - "[type] == sword && [flag] == runeword # [ias] >= 20 && [passiveltngpierce] >= 35 # [tier] == 110000", - // Temporary Weapon - Voice of Reason - "[type] == sword && [flag] == runeword # [passivecoldpierce] >= 24 # [tier] == 102500", - // Helm - Dream - "[type] == helm && [flag] == runeword # [holyshockaura] >= 15 # [tier] == 110000", - // Belt - Verdungos - "[name] == mithrilcoil && [quality] == unique && [flag] != ethereal # [damageresist] == 15 # [tier] == tierscore(item, 110000)", - // Boots - Gore Rider - "[name] == warboots && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 160 # [tier] == tierscore(item, 110000)", - // Armor - CoH - "[type] == armor && [flag] == runeword && [flag] != ethereal # [fireresist] == 65 && [hpregen] == 7 # [tier] == 100000", - // Shield - Dream - "[type] == auricshields && [flag] != ethereal && [flag] == runeword # [holyshockaura] >= 15 # [tier] == 110000", - // Gloves - Laying of Hands - "[name] == bramblemitts && [quality] == set && [flag] != ethereal # [ias] >= 20 # [tier] == 110000", - // Amulet - Highlords - "[type] == amulet && [quality] == unique # [lightresist] == 35 # [tier] == 100000", - // Final Rings - Perfect Raven Frost & Perfect Wisp - "[type] == ring && [quality] == unique # [dexterity] == 20 && [tohit] == 250 # [tier] == 110000", - "[type] == ring && [quality] == unique # [itemabsorblightpercent] == 20 # [tier] == 110000", - // Rings - Raven Frost & Wisp - "[type] == ring && [quality] == unique # [dexterity] >= 15 && [tohit] >= 150 # [tier] == 100000", - "[type] == ring && [quality] == unique # [itemabsorblightpercent] >= 10 # [tier] == 100000", - // Switch - CTA - "[minimumsockets] >= 5 && [flag] == runeword # [plusskillbattleorders] >= 1 # [secondarytier] == 100000", - // Merc Final Armor - Fortitude - "[type] == armor && [flag] == runeword # [enhanceddefense] >= 200 && [enhanceddamage] >= 300 # [merctier] == 100000", - // Merc Armor - Treachery - "[type] == armor && [flag] == runeword # [ias] == 45 && [coldresist] == 30 # [merctier] == 50000 + mercscore(item)", - // Merc Final Helmet - Eth Andy's - "[name] == demonhead && [quality] == unique && [flag] == ethereal # [strength] >= 25 && [enhanceddefense] >= 100 # [merctier] == 50000 + mercscore(item)", - // Merc Helmet - Andy's - "[name] == demonhead && [quality] == unique && [flag] != ethereal # [strength] >= 25 && [enhanceddefense] >= 100 # [merctier] == 40000 + mercscore(item)", - ]; + active: function () { + return this.respec() && me.getSkill(sdk.skills.Sanctuary, sdk.skills.subindex.HardPoints) === 20; + }, + }; + + let finalGear = [ // autoequip final gear + // Final Weapon - Last Wish + "[type] == sword && [flag] == runeword # [mightaura] >= 17 # [tier] == 120000", + // Temporary Weapon - Crescent Moon + "[type] == sword && [flag] == runeword # [ias] >= 20 && [passiveltngpierce] >= 35 # [tier] == 110000", + // Temporary Weapon - Voice of Reason + "[type] == sword && [flag] == runeword # [passivecoldpierce] >= 24 # [tier] == 102500", + // Helm - Dream + "[type] == helm && [flag] == runeword # [holyshockaura] >= 15 # [tier] == 110000", + // Belt - Verdungos + "[name] == mithrilcoil && [quality] == unique && [flag] != ethereal # [damageresist] == 15 # [tier] == tierscore(item, 110000)", + // Boots - Gore Rider + "[name] == warboots && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 160 # [tier] == tierscore(item, 110000)", + // Armor - CoH + "[type] == armor && [flag] == runeword && [flag] != ethereal # [fireresist] == 65 && [hpregen] == 7 # [tier] == 100000", + // Shield - Dream + "[type] == auricshields && [flag] != ethereal && [flag] == runeword # [holyshockaura] >= 15 # [tier] == 110000", + // Gloves - Laying of Hands + "[name] == bramblemitts && [quality] == set && [flag] != ethereal # [ias] >= 20 # [tier] == 110000", + // Amulet - Highlords + "[type] == amulet && [quality] == unique # [lightresist] == 35 # [tier] == 100000", + // Final Rings - Perfect Raven Frost & Perfect Wisp + "[type] == ring && [quality] == unique # [dexterity] == 20 && [tohit] == 250 # [tier] == 110000", + "[type] == ring && [quality] == unique # [itemabsorblightpercent] == 20 # [tier] == 110000", + // Rings - Raven Frost & Wisp + "[type] == ring && [quality] == unique # [dexterity] >= 15 && [tohit] >= 150 # [tier] == 100000", + "[type] == ring && [quality] == unique # [itemabsorblightpercent] >= 10 # [tier] == 100000", + // Switch - CTA + "[minimumsockets] >= 5 && [flag] == runeword # [plusskillbattleorders] >= 1 # [secondarytier] == 100000", + // Merc Final Armor - Fortitude + "[type] == armor && [flag] == runeword # [enhanceddefense] >= 200 && [enhanceddamage] >= 300 # [merctier] == 100000", + // Merc Armor - Treachery + "[type] == armor && [flag] == runeword # [ias] == 45 && [coldresist] == 30 # [merctier] == 50000 + mercscore(item)", + // Merc Final Helmet - Eth Andy's + "[name] == demonhead && [quality] == unique && [flag] == ethereal # [strength] >= 25 && [enhanceddefense] >= 100 # [merctier] == 50000 + mercscore(item)", + // Merc Helmet - Andy's + "[name] == demonhead && [quality] == unique && [flag] != ethereal # [strength] >= 25 && [enhanceddefense] >= 100 # [merctier] == 40000 + mercscore(item)", + ]; - NTIP.buildList(finalGear); - NTIP.buildFinalGear(finalGear); + NTIP.buildList(finalGear); + NTIP.buildFinalGear(finalGear); - return build; - })(); + return build; + })(); })(module); diff --git a/libs/SoloPlay/BuildFiles/paladin/paladin.SmiterBuild.js b/libs/SoloPlay/BuildFiles/paladin/paladin.SmiterBuild.js index ba382ad0..61472a24 100644 --- a/libs/SoloPlay/BuildFiles/paladin/paladin.SmiterBuild.js +++ b/libs/SoloPlay/BuildFiles/paladin/paladin.SmiterBuild.js @@ -7,118 +7,118 @@ (function (module) { - module.exports = (function () { - const build = { - caster: false, - skillstab: sdk.skills.tabs.PalaCombat, - wantedskills: [sdk.skills.Smite, sdk.skills.Fanaticism], - usefulskills: [sdk.skills.HolyShield, sdk.skills.Salvation], - precastSkills: [sdk.skills.HolyShield], - wantedMerc: MercData[sdk.skills.HolyFreeze], - stats: [ - ["strength", 115], ["dexterity", 136], ["vitality", 300], - ["dexterity", "block"], ["vitality", "all"] - ], - skills: [ - [sdk.skills.Smite, 20], - [sdk.skills.HolyShield, 20], - [sdk.skills.Fanaticism, 20], - [sdk.skills.Salvation, 5], - [sdk.skills.ResistLightning, 15], - [sdk.skills.ResistFire, 14], - [sdk.skills.ResistCold, 10] - ], + module.exports = (function () { + const build = { + caster: false, + skillstab: sdk.skills.tabs.PalaCombat, + wantedskills: [sdk.skills.Smite, sdk.skills.Fanaticism], + usefulskills: [sdk.skills.HolyShield, sdk.skills.Salvation], + precastSkills: [sdk.skills.HolyShield], + wantedMerc: MercData[sdk.skills.HolyFreeze], + stats: [ + ["strength", 115], ["dexterity", 136], ["vitality", 300], + ["dexterity", "block"], ["vitality", "all"] + ], + skills: [ + [sdk.skills.Smite, 20], + [sdk.skills.HolyShield, 20], + [sdk.skills.Fanaticism, 20], + [sdk.skills.Salvation, 5], + [sdk.skills.ResistLightning, 15], + [sdk.skills.ResistFire, 14], + [sdk.skills.ResistCold, 10] + ], - charms: { - ResLife: { - max: 6, - have: [], - classid: sdk.items.SmallCharm, - stats: function (check) { - return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MaxHp) === 20); - } - }, + charms: { + ResLife: { + max: 6, + have: [], + classid: sdk.items.SmallCharm, + stats: function (check) { + return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MaxHp) === 20); + } + }, - ResMf: { - max: 2, - have: [], - classid: sdk.items.SmallCharm, - stats: function (check) { - return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MagicBonus) === 7); - } - }, + ResMf: { + max: 2, + have: [], + classid: sdk.items.SmallCharm, + stats: function (check) { + return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MagicBonus) === 7); + } + }, - Skiller: { - max: 2, - have: [], - classid: sdk.items.GrandCharm, - stats: function (check) { - return (!check.unique && check.classid === this.classid && check.getStat(sdk.stats.AddSkillTab, sdk.skills.tabs.PalaCombat) === 1 - && check.getStat(sdk.stats.MaxHp) >= 40); - } - }, - }, + Skiller: { + max: 2, + have: [], + classid: sdk.items.GrandCharm, + stats: function (check) { + return (!check.unique && check.classid === this.classid && check.getStat(sdk.stats.AddSkillTab, sdk.skills.tabs.PalaCombat) === 1 + && check.getStat(sdk.stats.MaxHp) >= 40); + } + }, + }, - AutoBuildTemplate: { - 1: { - Update: function () { - Config.AttackSkill = [-1, sdk.skills.Smite, sdk.skills.Fanaticism, sdk.skills.Smite, sdk.skills.Fanaticism, sdk.skills.BlessedHammer, sdk.skills.Concentration]; - Config.LowManaSkill = [0, sdk.skills.Fanaticism]; - Config.BeltColumn = ["hp", "hp", "mp", "rv"]; - SetUp.belt(); - } - }, - }, + AutoBuildTemplate: { + 1: { + Update: function () { + Config.AttackSkill = [-1, sdk.skills.Smite, sdk.skills.Fanaticism, sdk.skills.Smite, sdk.skills.Fanaticism, sdk.skills.BlessedHammer, sdk.skills.Concentration]; + Config.LowManaSkill = [0, sdk.skills.Fanaticism]; + Config.BeltColumn = ["hp", "hp", "mp", "rv"]; + SetUp.belt(); + } + }, + }, - respec: function () { - if (me.classic) { - return me.charlvl >= 75 && me.diablo; - } else { - return me.checkItem({ name: sdk.locale.items.Grief, itemtype: sdk.items.type.Sword }).have; - } - }, + respec: function () { + if (me.classic) { + return me.charlvl >= 75 && me.diablo; + } else { + return me.checkItem({ name: sdk.locale.items.Grief, itemtype: sdk.items.type.Sword }).have; + } + }, - active: function () { - return this.respec() && me.getSkill(sdk.skills.Smite, sdk.skills.subindex.HardPoints) === 20; - }, - }; - - let finalGear = [ // autoequip final gear - // Weapon - Grief - "[type] == sword && [flag] == runeword # [ias] >= 30 && [itemdeadlystrike] == 20 && [passivepoispierce] >= 20 # [tier] == 100000", - // Helm - GFace - "[name] == wingedhelm && [quality] == set && [flag] != ethereal # [fhr] >= 30 # [tier] == tierscore(item, 100000)", - // Belt - Tgods - "[name] == warbelt && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 160 # [tier] == tierscore(item, 100000)", - // Boots - Goblin Toes - "[name] == lightplatedboots && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 50 # [tier] == tierscore(item, 100000)", - // Armor - Enigma - "[type] == armor && [flag] != ethereal && [flag] == runeword # [itemallskills] == 2 # [tier] == 100000", - // Shield - HoZ - "[name] == gildedshield && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 150 # [tier] == tierscore(item, 100000)", - // Gloves - Drac's - "[name] == vampirebonegloves && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 100 && [strength] >= 12 && [lifeleech] >= 9 # [tier] == tierscore(item, 100000)", - // Amulet - Highlords - "[type] == amulet && [quality] == unique # [lightresist] == 35 # [tier] == 100000", - // Final Rings - Perfect Raven Frost & Bul-Kathos' Wedding Band - "[type] == ring && [quality] == unique # [dexterity] == 20 && [tohit] == 250 # [tier] == 110000", - "[type] == ring && [quality] == unique # [maxstamina] == 50 && [lifeleech] == 5 # [tier] == 110000", - // Rings - Raven Frost && Bul-Kathos' Wedding Band - "[type] == ring && [quality] == unique # [dexterity] >= 15 && [tohit] >= 150 # [tier] == 100000", - "[type] == ring && [quality] == unique # [maxstamina] == 50 && [lifeleech] >= 3 # [tier] == 100000", - // Switch - CTA - "[minimumsockets] >= 5 && [flag] == runeword # [plusskillbattleorders] >= 1 # [secondarytier] == 100000", - // Merc Final Armor - Fortitude - "[type] == armor && [flag] == runeword # [enhanceddefense] >= 200 && [enhanceddamage] >= 300 # [merctier] == 100000", - // Merc Final Helmet - Eth Andy's - "[name] == demonhead && [quality] == unique && [flag] == ethereal # [strength] >= 25 && [enhanceddefense] >= 100 # [merctier] == 50000 + mercscore(item)", - // Merc Helmet - Andy's - "[name] == demonhead && [quality] == unique && [flag] != ethereal # [strength] >= 25 && [enhanceddefense] >= 100 # [merctier] == 40000 + mercscore(item)", - ]; + active: function () { + return this.respec() && me.getSkill(sdk.skills.Smite, sdk.skills.subindex.HardPoints) === 20; + }, + }; + + let finalGear = [ // autoequip final gear + // Weapon - Grief + "[type] == sword && [flag] == runeword # [ias] >= 30 && [itemdeadlystrike] == 20 && [passivepoispierce] >= 20 # [tier] == 100000", + // Helm - GFace + "[name] == wingedhelm && [quality] == set && [flag] != ethereal # [fhr] >= 30 # [tier] == tierscore(item, 100000)", + // Belt - Tgods + "[name] == warbelt && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 160 # [tier] == tierscore(item, 100000)", + // Boots - Goblin Toes + "[name] == lightplatedboots && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 50 # [tier] == tierscore(item, 100000)", + // Armor - Enigma + "[type] == armor && [flag] != ethereal && [flag] == runeword # [itemallskills] == 2 # [tier] == 100000", + // Shield - HoZ + "[name] == gildedshield && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 150 # [tier] == tierscore(item, 100000)", + // Gloves - Drac's + "[name] == vampirebonegloves && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 100 && [strength] >= 12 && [lifeleech] >= 9 # [tier] == tierscore(item, 100000)", + // Amulet - Highlords + "[type] == amulet && [quality] == unique # [lightresist] == 35 # [tier] == 100000", + // Final Rings - Perfect Raven Frost & Bul-Kathos' Wedding Band + "[type] == ring && [quality] == unique # [dexterity] == 20 && [tohit] == 250 # [tier] == 110000", + "[type] == ring && [quality] == unique # [maxstamina] == 50 && [lifeleech] == 5 # [tier] == 110000", + // Rings - Raven Frost && Bul-Kathos' Wedding Band + "[type] == ring && [quality] == unique # [dexterity] >= 15 && [tohit] >= 150 # [tier] == 100000", + "[type] == ring && [quality] == unique # [maxstamina] == 50 && [lifeleech] >= 3 # [tier] == 100000", + // Switch - CTA + "[minimumsockets] >= 5 && [flag] == runeword # [plusskillbattleorders] >= 1 # [secondarytier] == 100000", + // Merc Final Armor - Fortitude + "[type] == armor && [flag] == runeword # [enhanceddefense] >= 200 && [enhanceddamage] >= 300 # [merctier] == 100000", + // Merc Final Helmet - Eth Andy's + "[name] == demonhead && [quality] == unique && [flag] == ethereal # [strength] >= 25 && [enhanceddefense] >= 100 # [merctier] == 50000 + mercscore(item)", + // Merc Helmet - Andy's + "[name] == demonhead && [quality] == unique && [flag] != ethereal # [strength] >= 25 && [enhanceddefense] >= 100 # [merctier] == 40000 + mercscore(item)", + ]; - NTIP.buildList(finalGear); - NTIP.buildFinalGear(finalGear); + NTIP.buildList(finalGear); + NTIP.buildFinalGear(finalGear); - return build; - })(); + return build; + })(); })(module); diff --git a/libs/SoloPlay/BuildFiles/paladin/paladin.StartBuild.js b/libs/SoloPlay/BuildFiles/paladin/paladin.StartBuild.js index ad5740c5..57cf23ce 100644 --- a/libs/SoloPlay/BuildFiles/paladin/paladin.StartBuild.js +++ b/libs/SoloPlay/BuildFiles/paladin/paladin.StartBuild.js @@ -7,95 +7,95 @@ (function (module, require) { - module.exports = (function () { - const build = { - AutoBuildTemplate: {}, - caster: false, - skillstab: sdk.skills.tabs.PalaCombat, - wantedskills: [sdk.skills.Zeal, sdk.skills.HolyFire], - usefulskills: [ - (me.checkSkill(sdk.skills.HolyFire, sdk.skills.subindex.SoftPoints) - ? sdk.skills.Sacrifice - : sdk.skills.Might), - sdk.skills.ResistFire - ], - wantedMerc: MercData[sdk.skills.HolyFreeze], - stats: [ - ["vitality", 80], - ["dexterity", 27], - ["strength", 47], - ["vitality", "all"], - ], - skills: [ - // Total skills points by respec = 20 - [sdk.skills.Might, 1], // charlevel -> 2 - [sdk.skills.Sacrifice, 1], // charlevel -> 3 - [sdk.skills.HolyFire, 1, false], // charlevel -> 6 - [sdk.skills.ResistFire, 4], // charlevel -> 5 - [sdk.skills.HolyFire, 3], // charlevel -> 8 - [sdk.skills.Smite, 1], // charlevel -> 10 - [sdk.skills.Zeal, 1], // charlevel -> 12 - [sdk.skills.Charge, 1], // charlevel -> 12 - [sdk.skills.Zeal, 4, false], // charlevel -> 15 - [sdk.skills.HolyFire, 6], // charlevel -> 17 - [sdk.skills.ResistFire, 16] // respec at 19 - ], + module.exports = (function () { + const build = { + AutoBuildTemplate: {}, + caster: false, + skillstab: sdk.skills.tabs.PalaCombat, + wantedskills: [sdk.skills.Zeal, sdk.skills.HolyFire], + usefulskills: [ + (me.checkSkill(sdk.skills.HolyFire, sdk.skills.subindex.SoftPoints) + ? sdk.skills.Sacrifice + : sdk.skills.Might), + sdk.skills.ResistFire + ], + wantedMerc: MercData[sdk.skills.HolyFreeze], + stats: [ + ["vitality", 80], + ["dexterity", 27], + ["strength", 47], + ["vitality", "all"], + ], + skills: [ + // Total skills points by respec = 20 + [sdk.skills.Might, 1], // charlevel -> 2 + [sdk.skills.Sacrifice, 1], // charlevel -> 3 + [sdk.skills.HolyFire, 1, false], // charlevel -> 6 + [sdk.skills.ResistFire, 4], // charlevel -> 5 + [sdk.skills.HolyFire, 3], // charlevel -> 8 + [sdk.skills.Smite, 1], // charlevel -> 10 + [sdk.skills.Zeal, 1], // charlevel -> 12 + [sdk.skills.Charge, 1], // charlevel -> 12 + [sdk.skills.Zeal, 4, false], // charlevel -> 15 + [sdk.skills.HolyFire, 6], // charlevel -> 17 + [sdk.skills.ResistFire, 16] // respec at 19 + ], - active: function () { - return me.charlvl < CharInfo.respecOne && !me.checkSkill(sdk.skills.BlessedAim, sdk.skills.subindex.HardPoints); - }, - }; + active: function () { + return me.charlvl < CharInfo.respecOne && !me.checkSkill(sdk.skills.BlessedAim, sdk.skills.subindex.HardPoints); + }, + }; - const { buildAutoBuildTempObj } = require("../../Utils/General"); - - build.AutoBuildTemplate[1] = buildAutoBuildTempObj(() => { - Config.SkipEnchant.indexOf("cold enchanted") === -1 && Config.SkipEnchant.push("cold enchanted"); - Config.ScanShrines.indexOf(sdk.shrines.Combat) === -1 && Config.ScanShrines.push(sdk.shrines.Combat); - Config.FieldID.Enabled = !Misc.checkQuest(sdk.quest.id.TheSearchForCain, sdk.quest.states.Completed); - Config.FieldID.UsedSpace = 0; + const { buildAutoBuildTempObj } = require("../../Utils/General"); + + build.AutoBuildTemplate[1] = buildAutoBuildTempObj(() => { + Config.SkipEnchant.indexOf("cold enchanted") === -1 && Config.SkipEnchant.push("cold enchanted"); + Config.ScanShrines.indexOf(sdk.shrines.Combat) === -1 && Config.ScanShrines.push(sdk.shrines.Combat); + Config.FieldID.Enabled = !Misc.checkQuest(sdk.quest.id.TheSearchForCain, sdk.quest.states.Completed); + Config.FieldID.UsedSpace = 0; - Config.BeltColumn = ["hp", "hp", "hp", "hp"]; - Config.HPBuffer = 8; - Config.AttackSkill = [-1, sdk.skills.Attack, -1, sdk.skills.Attack, -1, -1, -1]; - Config.LowManaSkill = [sdk.skills.Attack, -1]; - SetUp.belt(); - }); - build.AutoBuildTemplate[2] = buildAutoBuildTempObj(() => { - Config.SkipEnchant.indexOf("cold enchanted") === -1 && Config.SkipEnchant.push("cold enchanted"); - Config.ScanShrines.indexOf(sdk.shrines.Combat) === -1 && Config.ScanShrines.push(sdk.shrines.Combat); - Config.FieldID.Enabled = !Misc.checkQuest(sdk.quest.id.TheSearchForCain, sdk.quest.states.Completed); - Config.FieldID.UsedSpace = 0; + Config.BeltColumn = ["hp", "hp", "hp", "hp"]; + Config.HPBuffer = 8; + Config.AttackSkill = [-1, sdk.skills.Attack, -1, sdk.skills.Attack, -1, -1, -1]; + Config.LowManaSkill = [sdk.skills.Attack, -1]; + SetUp.belt(); + }); + build.AutoBuildTemplate[2] = buildAutoBuildTempObj(() => { + Config.SkipEnchant.indexOf("cold enchanted") === -1 && Config.SkipEnchant.push("cold enchanted"); + Config.ScanShrines.indexOf(sdk.shrines.Combat) === -1 && Config.ScanShrines.push(sdk.shrines.Combat); + Config.FieldID.Enabled = !Misc.checkQuest(sdk.quest.id.TheSearchForCain, sdk.quest.states.Completed); + Config.FieldID.UsedSpace = 0; - Config.BeltColumn = ["hp", "hp", "hp", "hp"]; - Config.HPBuffer = 8; - const bossSkill = (me.checkSkill(sdk.skills.Sacrifice, sdk.skills.subindex.HardPoints) ? sdk.skills.Sacrifice : sdk.skills.Attack); - Config.AttackSkill = [-1, bossSkill, sdk.skills.Might, sdk.skills.Attack, sdk.skills.Might, -1, -1]; - Config.LowManaSkill = [sdk.skills.Attack, sdk.skills.Might]; - }); - build.AutoBuildTemplate[6] = buildAutoBuildTempObj(() => { - Config.HPBuffer = 8; - const bossSkill = (me.checkSkill(sdk.skills.Sacrifice, sdk.skills.subindex.HardPoints) ? sdk.skills.Sacrifice : sdk.skills.Attack); - Config.AttackSkill = [-1, bossSkill, sdk.skills.HolyFire, sdk.skills.Attack, sdk.skills.HolyFire, sdk.skills.Attack, sdk.skills.Might]; - Config.LowManaSkill = [sdk.skills.Attack, sdk.skills.HolyFire]; - }); - build.AutoBuildTemplate[9] = buildAutoBuildTempObj(() => { - Config.HPBuffer = me.expansion ? 2 : 4; - Config.MPBuffer = 6; - Config.AttackSkill[0] = -1; - Config.AttackSkill[1] = (me.checkSkill(sdk.skills.Sacrifice, sdk.skills.subindex.HardPoints) ? sdk.skills.Sacrifice : sdk.skills.Attack); - Config.AttackSkill[2] = sdk.skills.HolyFire; - Config.AttackSkill[3] = sdk.skills.Attack; - Config.AttackSkill[4] = sdk.skills.HolyFire; - Config.AttackSkill[5] = sdk.skills.Attack; - Config.AttackSkill[6] = sdk.skills.Might; - }); - build.AutoBuildTemplate[12] = buildAutoBuildTempObj(() => { - if (me.checkSkill(sdk.skills.Zeal, sdk.skills.subindex.HardPoints)) { - Config.AttackSkill = [-1, sdk.skills.Zeal, sdk.skills.HolyFire, sdk.skills.Zeal, sdk.skills.HolyFire, 0, sdk.skills.Might]; - } - Config.Charge = true; - }); + Config.BeltColumn = ["hp", "hp", "hp", "hp"]; + Config.HPBuffer = 8; + const bossSkill = (me.checkSkill(sdk.skills.Sacrifice, sdk.skills.subindex.HardPoints) ? sdk.skills.Sacrifice : sdk.skills.Attack); + Config.AttackSkill = [-1, bossSkill, sdk.skills.Might, sdk.skills.Attack, sdk.skills.Might, -1, -1]; + Config.LowManaSkill = [sdk.skills.Attack, sdk.skills.Might]; + }); + build.AutoBuildTemplate[6] = buildAutoBuildTempObj(() => { + Config.HPBuffer = 8; + const bossSkill = (me.checkSkill(sdk.skills.Sacrifice, sdk.skills.subindex.HardPoints) ? sdk.skills.Sacrifice : sdk.skills.Attack); + Config.AttackSkill = [-1, bossSkill, sdk.skills.HolyFire, sdk.skills.Attack, sdk.skills.HolyFire, sdk.skills.Attack, sdk.skills.Might]; + Config.LowManaSkill = [sdk.skills.Attack, sdk.skills.HolyFire]; + }); + build.AutoBuildTemplate[9] = buildAutoBuildTempObj(() => { + Config.HPBuffer = me.expansion ? 2 : 4; + Config.MPBuffer = 6; + Config.AttackSkill[0] = -1; + Config.AttackSkill[1] = (me.checkSkill(sdk.skills.Sacrifice, sdk.skills.subindex.HardPoints) ? sdk.skills.Sacrifice : sdk.skills.Attack); + Config.AttackSkill[2] = sdk.skills.HolyFire; + Config.AttackSkill[3] = sdk.skills.Attack; + Config.AttackSkill[4] = sdk.skills.HolyFire; + Config.AttackSkill[5] = sdk.skills.Attack; + Config.AttackSkill[6] = sdk.skills.Might; + }); + build.AutoBuildTemplate[12] = buildAutoBuildTempObj(() => { + if (me.checkSkill(sdk.skills.Zeal, sdk.skills.subindex.HardPoints)) { + Config.AttackSkill = [-1, sdk.skills.Zeal, sdk.skills.HolyFire, sdk.skills.Zeal, sdk.skills.HolyFire, 0, sdk.skills.Might]; + } + Config.Charge = true; + }); - return build; - })(); + return build; + })(); })(module, require); diff --git a/libs/SoloPlay/BuildFiles/paladin/paladin.TorchadinBuild.js b/libs/SoloPlay/BuildFiles/paladin/paladin.TorchadinBuild.js index 9af4f0a6..98519d56 100644 --- a/libs/SoloPlay/BuildFiles/paladin/paladin.TorchadinBuild.js +++ b/libs/SoloPlay/BuildFiles/paladin/paladin.TorchadinBuild.js @@ -7,131 +7,131 @@ (function (module) { - module.exports = (function () { - const build = { - caster: false, - skillstab: sdk.skills.tabs.PalaCombat, - wantedskills: [sdk.skills.Zeal, sdk.skills.Conviction], - usefulskills: [sdk.skills.HolyShield, sdk.skills.ResistFire, sdk.skills.Salvation], - precastSkills: [sdk.skills.HolyShield], - usefulStats: [sdk.stats.PassiveFireMastery, sdk.stats.PassiveFirePierce, sdk.stats.PierceFire], - wantedMerc: MercData[sdk.skills.HolyFreeze], - stats: [ - ["strength", 103], ["dexterity", 136], - ["vitality", 300], ["dexterity", "block"], ["vitality", "all"] - ], - skills: [ - [sdk.skills.Conviction, 20], - [sdk.skills.Zeal, 4], - [sdk.skills.Salvation, 20], - [sdk.skills.ResistFire, 20], - [sdk.skills.Redemption, 1], - [sdk.skills.HolyShield, 15], - [sdk.skills.Zeal, 10], - [sdk.skills.Sacrifice, 20], - ], + module.exports = (function () { + const build = { + caster: false, + skillstab: sdk.skills.tabs.PalaCombat, + wantedskills: [sdk.skills.Zeal, sdk.skills.Conviction], + usefulskills: [sdk.skills.HolyShield, sdk.skills.ResistFire, sdk.skills.Salvation], + precastSkills: [sdk.skills.HolyShield], + usefulStats: [sdk.stats.PassiveFireMastery, sdk.stats.PassiveFirePierce, sdk.stats.PierceFire], + wantedMerc: MercData[sdk.skills.HolyFreeze], + stats: [ + ["strength", 103], ["dexterity", 136], + ["vitality", 300], ["dexterity", "block"], ["vitality", "all"] + ], + skills: [ + [sdk.skills.Conviction, 20], + [sdk.skills.Zeal, 4], + [sdk.skills.Salvation, 20], + [sdk.skills.ResistFire, 20], + [sdk.skills.Redemption, 1], + [sdk.skills.HolyShield, 15], + [sdk.skills.Zeal, 10], + [sdk.skills.Sacrifice, 20], + ], - charms: { - ResLife: { - max: 6, - have: [], - classid: sdk.items.SmallCharm, - stats: function (check) { - return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MaxHp) === 20); - } - }, + charms: { + ResLife: { + max: 6, + have: [], + classid: sdk.items.SmallCharm, + stats: function (check) { + return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MaxHp) === 20); + } + }, - ResMf: { - max: 2, - have: [], - classid: sdk.items.SmallCharm, - stats: function (check) { - return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MagicBonus) === 7); - } - }, + ResMf: { + max: 2, + have: [], + classid: sdk.items.SmallCharm, + stats: function (check) { + return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MagicBonus) === 7); + } + }, - Skiller: { - max: 2, - have: [], - classid: sdk.items.GrandCharm, - stats: function (check) { - return (!check.unique && check.classid === this.classid && check.getStat(sdk.stats.AddSkillTab, sdk.skills.tabs.Offensive) === 1 - && check.getStat(sdk.stats.MaxHp) >= 40); - } - }, - }, + Skiller: { + max: 2, + have: [], + classid: sdk.items.GrandCharm, + stats: function (check) { + return (!check.unique && check.classid === this.classid && check.getStat(sdk.stats.AddSkillTab, sdk.skills.tabs.Offensive) === 1 + && check.getStat(sdk.stats.MaxHp) >= 40); + } + }, + }, - AutoBuildTemplate: { - 1: { - Update: function () { - Config.Vigor = false; - Config.AttackSkill = [-1, sdk.skills.Zeal, sdk.skills.Conviction, sdk.skills.Zeal, sdk.skills.Conviction, -1, -1]; - Config.LowManaSkill = [-1, -1]; - Config.SkipImmune = ["fire and physical"]; - Config.BeltColumn = ["hp", "hp", "mp", "rv"]; - SetUp.belt(); - } - }, - }, + AutoBuildTemplate: { + 1: { + Update: function () { + Config.Vigor = false; + Config.AttackSkill = [-1, sdk.skills.Zeal, sdk.skills.Conviction, sdk.skills.Zeal, sdk.skills.Conviction, -1, -1]; + Config.LowManaSkill = [-1, -1]; + Config.SkipImmune = ["fire and physical"]; + Config.BeltColumn = ["hp", "hp", "mp", "rv"]; + SetUp.belt(); + } + }, + }, - respec: function () { - if (me.classic) { - return false; - } else { - return me.haveAll([{ name: sdk.locale.items.HandofJustice }, { name: sdk.locale.items.Dragon, itemtype: sdk.items.type.Armor }]); - } - }, + respec: function () { + if (me.classic) { + return false; + } else { + return me.haveAll([{ name: sdk.locale.items.HandofJustice }, { name: sdk.locale.items.Dragon, itemtype: sdk.items.type.Armor }]); + } + }, - active: function () { - return this.respec() && me.getSkill(sdk.skills.Conviction, sdk.skills.subindex.HardPoints) === 20; - }, - }; - - // autoequip final gear - let finalGear = [ - // Final Weapon - HoJ - "[type] == sword && [flag] == runeword # [holyfireaura] >= 16 # [tier] == 120000", - // Temporary Weapon - Crescent Moon - "[type] == sword && [flag] == runeword # [ias] >= 20 && [passiveltngpierce] >= 35 # [tier] == 110000", - // Temporary Weapon - Voice of Reason - "[type] == sword && [flag] == runeword # [passivecoldpierce] >= 24 # [tier] == 102500", - // Final Helm - Upp'ed Vamp Gaze - "[name] == bonevisage && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 100 && [lifeleech] >= 6 # [tier] == tierscore(item, 100000)", - // Helm - Vamp Gaze - "[name] == grimhelm && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 100 # [tier] == tierscore(item, 100000)", - // Belt - TGods - "[name] == warbelt && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 160 # [tier] == tierscore(item, 110000)", - // Boots - Gore Rider - "[name] == warboots && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 160 # [tier] == tierscore(item, 110000)", - // Armor - Dragon - "[type] == armor && [flag] != ethereal && [flag] == runeword # [holyfireaura] >= 14 # [tier] == 110000", - // Shield - Exile - "[type] == auricshields && [flag] == runeword # [defianceaura] >= 13 # [tier] == 110000", - // Gloves - Laying of Hands - "[name] == bramblemitts && [quality] == set && [flag] != ethereal # [ias] >= 20 # [tier] == 110000", - // Amulet - Highlords - "[type] == amulet && [quality] == unique # [lightresist] == 35 # [tier] == 100000", - // Final Rings - Perfect Raven Frost & Bul-Kathos' Wedding Band - "[type] == ring && [quality] == unique # [dexterity] == 20 && [tohit] == 250 # [tier] == 110000", - "[type] == ring && [quality] == unique # [maxstamina] == 50 && [lifeleech] == 5 # [tier] == 110000", - // Rings - Raven Frost && Bul-Kathos' Wedding Band - "[type] == ring && [quality] == unique # [dexterity] >= 15 && [tohit] >= 150 # [tier] == 100000", - "[type] == ring && [quality] == unique # [maxstamina] == 50 && [lifeleech] >= 3 # [tier] == 100000", - // Switch - CTA - "[minimumsockets] >= 5 && [flag] == runeword # [plusskillbattleorders] >= 1 # [secondarytier] == 100000", - // Merc Final Armor - Fortitude - "[type] == armor && [flag] == runeword # [enhanceddefense] >= 200 && [enhanceddamage] >= 300 # [merctier] == 100000", - // Merc Armor - Treachery - "[type] == armor && [flag] == runeword # [ias] == 45 && [coldresist] == 30 # [merctier] == 50000 + mercscore(item)", - // Merc Final Helmet - Eth Andy's - "[name] == demonhead && [quality] == unique && [flag] == ethereal # [strength] >= 25 && [enhanceddefense] >= 100 # [merctier] == 50000 + mercscore(item)", - // Merc Helmet - Andy's - "[name] == demonhead && [quality] == unique && [flag] != ethereal # [strength] >= 25 && [enhanceddefense] >= 100 # [merctier] == 40000 + mercscore(item)", - ]; + active: function () { + return this.respec() && me.getSkill(sdk.skills.Conviction, sdk.skills.subindex.HardPoints) === 20; + }, + }; + + // autoequip final gear + let finalGear = [ + // Final Weapon - HoJ + "[type] == sword && [flag] == runeword # [holyfireaura] >= 16 # [tier] == 120000", + // Temporary Weapon - Crescent Moon + "[type] == sword && [flag] == runeword # [ias] >= 20 && [passiveltngpierce] >= 35 # [tier] == 110000", + // Temporary Weapon - Voice of Reason + "[type] == sword && [flag] == runeword # [passivecoldpierce] >= 24 # [tier] == 102500", + // Final Helm - Upp'ed Vamp Gaze + "[name] == bonevisage && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 100 && [lifeleech] >= 6 # [tier] == tierscore(item, 100000)", + // Helm - Vamp Gaze + "[name] == grimhelm && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 100 # [tier] == tierscore(item, 100000)", + // Belt - TGods + "[name] == warbelt && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 160 # [tier] == tierscore(item, 110000)", + // Boots - Gore Rider + "[name] == warboots && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 160 # [tier] == tierscore(item, 110000)", + // Armor - Dragon + "[type] == armor && [flag] != ethereal && [flag] == runeword # [holyfireaura] >= 14 # [tier] == 110000", + // Shield - Exile + "[type] == auricshields && [flag] == runeword # [defianceaura] >= 13 # [tier] == 110000", + // Gloves - Laying of Hands + "[name] == bramblemitts && [quality] == set && [flag] != ethereal # [ias] >= 20 # [tier] == 110000", + // Amulet - Highlords + "[type] == amulet && [quality] == unique # [lightresist] == 35 # [tier] == 100000", + // Final Rings - Perfect Raven Frost & Bul-Kathos' Wedding Band + "[type] == ring && [quality] == unique # [dexterity] == 20 && [tohit] == 250 # [tier] == 110000", + "[type] == ring && [quality] == unique # [maxstamina] == 50 && [lifeleech] == 5 # [tier] == 110000", + // Rings - Raven Frost && Bul-Kathos' Wedding Band + "[type] == ring && [quality] == unique # [dexterity] >= 15 && [tohit] >= 150 # [tier] == 100000", + "[type] == ring && [quality] == unique # [maxstamina] == 50 && [lifeleech] >= 3 # [tier] == 100000", + // Switch - CTA + "[minimumsockets] >= 5 && [flag] == runeword # [plusskillbattleorders] >= 1 # [secondarytier] == 100000", + // Merc Final Armor - Fortitude + "[type] == armor && [flag] == runeword # [enhanceddefense] >= 200 && [enhanceddamage] >= 300 # [merctier] == 100000", + // Merc Armor - Treachery + "[type] == armor && [flag] == runeword # [ias] == 45 && [coldresist] == 30 # [merctier] == 50000 + mercscore(item)", + // Merc Final Helmet - Eth Andy's + "[name] == demonhead && [quality] == unique && [flag] == ethereal # [strength] >= 25 && [enhanceddefense] >= 100 # [merctier] == 50000 + mercscore(item)", + // Merc Helmet - Andy's + "[name] == demonhead && [quality] == unique && [flag] != ethereal # [strength] >= 25 && [enhanceddefense] >= 100 # [merctier] == 40000 + mercscore(item)", + ]; - NTIP.buildList(finalGear); - NTIP.buildFinalGear(finalGear); + NTIP.buildList(finalGear); + NTIP.buildFinalGear(finalGear); - return build; - })(); + return build; + })(); })(module); diff --git a/libs/SoloPlay/BuildFiles/paladin/paladin.ZealerBuild.js b/libs/SoloPlay/BuildFiles/paladin/paladin.ZealerBuild.js index 3c0435c3..5860146c 100644 --- a/libs/SoloPlay/BuildFiles/paladin/paladin.ZealerBuild.js +++ b/libs/SoloPlay/BuildFiles/paladin/paladin.ZealerBuild.js @@ -7,127 +7,127 @@ (function (module) { - module.exports = (function () { - const build = { - caster: false, - skillstab: sdk.skills.tabs.PalaCombat, - wantedskills: [sdk.skills.Zeal, sdk.skills.Fanaticism], - usefulskills: [sdk.skills.HolyShield, sdk.skills.ResistFire, sdk.skills.ResistLightning], - precastSkills: [sdk.skills.HolyShield], - wantedMerc: MercData[sdk.skills.HolyFreeze], - stats: [ - ["strength", 103], ["dexterity", 136], ["vitality", 300], - ["dexterity", "block"], ["vitality", "all"] - ], - skills: [ - [sdk.skills.Fanaticism, 20], - [sdk.skills.Sacrifice, 20], - [sdk.skills.Salvation, 1], - [sdk.skills.Redemption, 1], - [sdk.skills.Zeal, 10], - [sdk.skills.HolyShield, 15], // lvl 74 w/o quest skill pts - [sdk.skills.ResistLightning, 10, false], - [sdk.skills.ResistFire, 10, false], - [sdk.skills.ResistCold, 10, false], - ], + module.exports = (function () { + const build = { + caster: false, + skillstab: sdk.skills.tabs.PalaCombat, + wantedskills: [sdk.skills.Zeal, sdk.skills.Fanaticism], + usefulskills: [sdk.skills.HolyShield, sdk.skills.ResistFire, sdk.skills.ResistLightning], + precastSkills: [sdk.skills.HolyShield], + wantedMerc: MercData[sdk.skills.HolyFreeze], + stats: [ + ["strength", 103], ["dexterity", 136], ["vitality", 300], + ["dexterity", "block"], ["vitality", "all"] + ], + skills: [ + [sdk.skills.Fanaticism, 20], + [sdk.skills.Sacrifice, 20], + [sdk.skills.Salvation, 1], + [sdk.skills.Redemption, 1], + [sdk.skills.Zeal, 10], + [sdk.skills.HolyShield, 15], // lvl 74 w/o quest skill pts + [sdk.skills.ResistLightning, 10, false], + [sdk.skills.ResistFire, 10, false], + [sdk.skills.ResistCold, 10, false], + ], - charms: { - ResLife: { - max: 6, - have: [], - classid: sdk.items.SmallCharm, - stats: function (check) { - return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MaxHp) === 20); - } - }, + charms: { + ResLife: { + max: 6, + have: [], + classid: sdk.items.SmallCharm, + stats: function (check) { + return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MaxHp) === 20); + } + }, - ResMf: { - max: 2, - have: [], - classid: sdk.items.SmallCharm, - stats: function (check) { - return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MagicBonus) === 7); - } - }, + ResMf: { + max: 2, + have: [], + classid: sdk.items.SmallCharm, + stats: function (check) { + return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MagicBonus) === 7); + } + }, - Skiller: { - max: 2, - have: [], - classid: sdk.items.GrandCharm, - stats: function (check) { - return (!check.unique && check.classid === this.classid && check.getStat(sdk.stats.AddSkillTab, sdk.skills.tabs.PalaCombat) === 1 - && check.getStat(sdk.stats.MaxHp) >= 40); - } - }, - }, + Skiller: { + max: 2, + have: [], + classid: sdk.items.GrandCharm, + stats: function (check) { + return (!check.unique && check.classid === this.classid && check.getStat(sdk.stats.AddSkillTab, sdk.skills.tabs.PalaCombat) === 1 + && check.getStat(sdk.stats.MaxHp) >= 40); + } + }, + }, - AutoBuildTemplate: { - 1: { - Update: function () { - Config.AttackSkill = [-1, sdk.skills.Zeal, sdk.skills.Fanaticism, sdk.skills.Zeal, sdk.skills.Fanaticism, -1, -1]; - Config.LowManaSkill = [-1, -1]; - Config.BeltColumn = ["hp", "hp", "mp", "rv"]; - SetUp.belt(); - } - }, - }, + AutoBuildTemplate: { + 1: { + Update: function () { + Config.AttackSkill = [-1, sdk.skills.Zeal, sdk.skills.Fanaticism, sdk.skills.Zeal, sdk.skills.Fanaticism, -1, -1]; + Config.LowManaSkill = [-1, -1]; + Config.BeltColumn = ["hp", "hp", "mp", "rv"]; + SetUp.belt(); + } + }, + }, - respec: function () { - if (me.classic) { - return me.charlvl >= 75 && me.diablo; - } else { - return me.checkItem({ name: sdk.locale.items.Grief, itemtype: sdk.items.type.Sword }).have; - } - }, + respec: function () { + if (me.classic) { + return me.charlvl >= 75 && me.diablo; + } else { + return me.checkItem({ name: sdk.locale.items.Grief, itemtype: sdk.items.type.Sword }).have; + } + }, - active: function () { - return this.respec() && me.getSkill(sdk.skills.Fanaticism, sdk.skills.subindex.HardPoints) === 20; - }, - }; + active: function () { + return this.respec() && me.getSkill(sdk.skills.Fanaticism, sdk.skills.subindex.HardPoints) === 20; + }, + }; - // autoequip final gear - let finalGear = [ - // Weapon - Grief - "[type] == sword && [flag] == runeword # [ias] >= 30 && [itemdeadlystrike] == 20 && [passivepoispierce] >= 20 # [tier] == 100000", - // Final Helm - Upp'ed Vamp Gaze - "[name] == bonevisage && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 100 && [lifeleech] >= 6 # [tier] == tierscore(item, 100000)", - // Helm -Vamp Gaze - "[name] == grimhelm && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 100 # [tier] == tierscore(item, 100000)", - // Belt - TGods - "[name] == warbelt && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 160 # [tier] == tierscore(item, 110000)", - // Boots - Gore Rider - "[name] == warboots && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 160 # [tier] == tierscore(item, 110000)", - // Armor - Fortitude - "[type] == armor && [flag] != ethereal && [flag] == runeword # [enhanceddefense] >= 200 && [enhanceddamage] >= 300 # [tier] == 110000", - // Final Shield - Exile - "[type] == auricshields && [flag] == runeword # [defianceaura] >= 13 # [tier] == 110000", - // Shield - HoZ - "[name] == gildedshield && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 150 # [tier] == tierscore(item, 100000)", - // Gloves - Laying of Hand's - "[name] == bramblemitts && [quality] == set && [flag] != ethereal # [ias] >= 20 # [tier] == 110000", - // Amulet - Highlords - "[type] == amulet && [quality] == unique # [lightresist] == 35 # [tier] == 100000", - // Final Rings - Perfect Raven Frost & Bul-Kathos' Wedding Band - "[type] == ring && [quality] == unique # [dexterity] == 20 && [tohit] == 250 # [tier] == 110000", - "[type] == ring && [quality] == unique # [maxstamina] == 50 && [lifeleech] == 5 # [tier] == 110000", - // Rings - Raven Frost && Bul-Kathos' Wedding Band - "[type] == ring && [quality] == unique # [dexterity] >= 15 && [tohit] >= 150 # [tier] == 100000", - "[type] == ring && [quality] == unique # [maxstamina] == 50 && [lifeleech] >= 3 # [tier] == 100000", - // Switch - CTA - "[minimumsockets] >= 5 && [flag] == runeword # [plusskillbattleorders] >= 1 # [secondarytier] == 100000", - // Merc Final Armor - Fortitude - "[type] == armor && [flag] == runeword # [enhanceddefense] >= 200 && [enhanceddamage] >= 300 # [merctier] == 100000", - // Merc Armor - Treachery - "[type] == armor && [flag] == runeword # [ias] == 45 && [coldresist] == 30 # [merctier] == 50000 + mercscore(item)", - // Merc Final Helmet - Eth Andy's - "[name] == demonhead && [quality] == unique && [flag] == ethereal # [strength] >= 25 && [enhanceddefense] >= 100 # [merctier] == 50000 + mercscore(item)", - // Merc Helmet - Andy's - "[name] == demonhead && [quality] == unique && [flag] != ethereal # [strength] >= 25 && [enhanceddefense] >= 100 # [merctier] == 40000 + mercscore(item)", - ]; + // autoequip final gear + let finalGear = [ + // Weapon - Grief + "[type] == sword && [flag] == runeword # [ias] >= 30 && [itemdeadlystrike] == 20 && [passivepoispierce] >= 20 # [tier] == 100000", + // Final Helm - Upp'ed Vamp Gaze + "[name] == bonevisage && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 100 && [lifeleech] >= 6 # [tier] == tierscore(item, 100000)", + // Helm -Vamp Gaze + "[name] == grimhelm && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 100 # [tier] == tierscore(item, 100000)", + // Belt - TGods + "[name] == warbelt && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 160 # [tier] == tierscore(item, 110000)", + // Boots - Gore Rider + "[name] == warboots && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 160 # [tier] == tierscore(item, 110000)", + // Armor - Fortitude + "[type] == armor && [flag] != ethereal && [flag] == runeword # [enhanceddefense] >= 200 && [enhanceddamage] >= 300 # [tier] == 110000", + // Final Shield - Exile + "[type] == auricshields && [flag] == runeword # [defianceaura] >= 13 # [tier] == 110000", + // Shield - HoZ + "[name] == gildedshield && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 150 # [tier] == tierscore(item, 100000)", + // Gloves - Laying of Hand's + "[name] == bramblemitts && [quality] == set && [flag] != ethereal # [ias] >= 20 # [tier] == 110000", + // Amulet - Highlords + "[type] == amulet && [quality] == unique # [lightresist] == 35 # [tier] == 100000", + // Final Rings - Perfect Raven Frost & Bul-Kathos' Wedding Band + "[type] == ring && [quality] == unique # [dexterity] == 20 && [tohit] == 250 # [tier] == 110000", + "[type] == ring && [quality] == unique # [maxstamina] == 50 && [lifeleech] == 5 # [tier] == 110000", + // Rings - Raven Frost && Bul-Kathos' Wedding Band + "[type] == ring && [quality] == unique # [dexterity] >= 15 && [tohit] >= 150 # [tier] == 100000", + "[type] == ring && [quality] == unique # [maxstamina] == 50 && [lifeleech] >= 3 # [tier] == 100000", + // Switch - CTA + "[minimumsockets] >= 5 && [flag] == runeword # [plusskillbattleorders] >= 1 # [secondarytier] == 100000", + // Merc Final Armor - Fortitude + "[type] == armor && [flag] == runeword # [enhanceddefense] >= 200 && [enhanceddamage] >= 300 # [merctier] == 100000", + // Merc Armor - Treachery + "[type] == armor && [flag] == runeword # [ias] == 45 && [coldresist] == 30 # [merctier] == 50000 + mercscore(item)", + // Merc Final Helmet - Eth Andy's + "[name] == demonhead && [quality] == unique && [flag] == ethereal # [strength] >= 25 && [enhanceddefense] >= 100 # [merctier] == 50000 + mercscore(item)", + // Merc Helmet - Andy's + "[name] == demonhead && [quality] == unique && [flag] != ethereal # [strength] >= 25 && [enhanceddefense] >= 100 # [merctier] == 40000 + mercscore(item)", + ]; - NTIP.buildList(finalGear); - NTIP.buildFinalGear(finalGear); + NTIP.buildList(finalGear); + NTIP.buildFinalGear(finalGear); - return build; - })(); + return build; + })(); })(module); diff --git a/libs/SoloPlay/BuildFiles/paladin/paladin.js b/libs/SoloPlay/BuildFiles/paladin/paladin.js index 533fb95c..60de8ff4 100644 --- a/libs/SoloPlay/BuildFiles/paladin/paladin.js +++ b/libs/SoloPlay/BuildFiles/paladin/paladin.js @@ -6,38 +6,38 @@ */ const CharInfo = { - respecOne: 19, - respecTwo: 0, - levelCap: (function() { - const currentDiff = sdk.difficulty.nameOf(me.diff); - const softcoreMode = { - "Normal": me.expansion ? 33 : 33, - "Nightmare": me.expansion ? 65 : 65, - "Hell": 100, - }; - const hardcoreMode = { - "Normal": me.expansion ? 36 : 33, - "Nightmare": me.expansion ? 71 : 65, - "Hell": 100, - }; + respecOne: 19, + respecTwo: 0, + levelCap: (function() { + const currentDiff = sdk.difficulty.nameOf(me.diff); + const softcoreMode = { + "Normal": me.expansion ? 33 : 33, + "Nightmare": me.expansion ? 65 : 65, + "Hell": 100, + }; + const hardcoreMode = { + "Normal": me.expansion ? 36 : 33, + "Nightmare": me.expansion ? 71 : 65, + "Hell": 100, + }; - return me.softcore ? softcoreMode[currentDiff] : hardcoreMode[currentDiff]; - })(), + return me.softcore ? softcoreMode[currentDiff] : hardcoreMode[currentDiff]; + })(), - getActiveBuild: function () { - const nSkills = me.getStat(sdk.stats.NewSkills); - const currLevel = me.charlvl; - const justRepeced = (nSkills >= currLevel); + getActiveBuild: function () { + const nSkills = me.getStat(sdk.stats.NewSkills); + const currLevel = me.charlvl; + const justRepeced = (nSkills >= currLevel); - switch (true) { - case currLevel < this.respecOne: - case !justRepeced && currLevel > this.respecOne && !me.checkSkill(sdk.skills.Concentration, sdk.skills.subindex.HardPoints): - return "Start"; - case Check.finalBuild().respec() && justRepeced: - case Check.finalBuild().active(): - return SetUp.finalBuild; - default: - return "Leveling"; - } - }, + switch (true) { + case currLevel < this.respecOne: + case !justRepeced && currLevel > this.respecOne && !me.checkSkill(sdk.skills.Concentration, sdk.skills.subindex.HardPoints): + return "Start"; + case Check.finalBuild().respec() && justRepeced: + case Check.finalBuild().active(): + return SetUp.finalBuild; + default: + return "Leveling"; + } + }, }; diff --git a/libs/SoloPlay/BuildFiles/sorceress/sorceress.BlizzballerBuild.js b/libs/SoloPlay/BuildFiles/sorceress/sorceress.BlizzballerBuild.js index 7be602f3..c5755a7c 100644 --- a/libs/SoloPlay/BuildFiles/sorceress/sorceress.BlizzballerBuild.js +++ b/libs/SoloPlay/BuildFiles/sorceress/sorceress.BlizzballerBuild.js @@ -7,162 +7,162 @@ (function (module) { - module.exports = (function () { - const build = { - caster: true, - skillstab: sdk.skills.tabs.Cold, - wantedskills: [sdk.skills.Blizzard, sdk.skills.FireBall, sdk.skills.ColdMastery], - usefulskills: [sdk.skills.GlacialSpike, sdk.skills.Meteor, sdk.skills.FireMastery, sdk.skills.StaticField], - precastSkills: [sdk.skills.FrozenArmor], - usefulStats: [sdk.stats.PassiveColdPierce, sdk.stats.PassiveColdMastery, sdk.stats.PassiveFireMastery, sdk.stats.PassiveFirePierce], - wantedMerc: MercData[sdk.skills.HolyFreeze], - stats: [ - ["energy", 50], ["strength", 48], ["vitality", 165], - ["strength", 61], ["vitality", 252], ["strength", 127], - ["dexterity", "block"], ["vitality", "all"] - ], - skills: [ - [sdk.skills.Warmth, 1], - [sdk.skills.FrozenArmor, 1], - [sdk.skills.StaticField, 1], - [sdk.skills.Teleport, 1], - [sdk.skills.Meteor, 1], - [sdk.skills.FireMastery, 1, false], - [sdk.skills.ColdMastery, 1, false], - [sdk.skills.FireBall, 20], - [sdk.skills.Blizzard, 20], - [sdk.skills.GlacialSpike, 20], // lvl 75 w/o quest skills pts - [sdk.skills.Meteor, 20], - [sdk.skills.ColdMastery, 5], - [sdk.skills.FireBolt, 20], - ], + module.exports = (function () { + const build = { + caster: true, + skillstab: sdk.skills.tabs.Cold, + wantedskills: [sdk.skills.Blizzard, sdk.skills.FireBall, sdk.skills.ColdMastery], + usefulskills: [sdk.skills.GlacialSpike, sdk.skills.Meteor, sdk.skills.FireMastery, sdk.skills.StaticField], + precastSkills: [sdk.skills.FrozenArmor], + usefulStats: [sdk.stats.PassiveColdPierce, sdk.stats.PassiveColdMastery, sdk.stats.PassiveFireMastery, sdk.stats.PassiveFirePierce], + wantedMerc: MercData[sdk.skills.HolyFreeze], + stats: [ + ["energy", 50], ["strength", 48], ["vitality", 165], + ["strength", 61], ["vitality", 252], ["strength", 127], + ["dexterity", "block"], ["vitality", "all"] + ], + skills: [ + [sdk.skills.Warmth, 1], + [sdk.skills.FrozenArmor, 1], + [sdk.skills.StaticField, 1], + [sdk.skills.Teleport, 1], + [sdk.skills.Meteor, 1], + [sdk.skills.FireMastery, 1, false], + [sdk.skills.ColdMastery, 1, false], + [sdk.skills.FireBall, 20], + [sdk.skills.Blizzard, 20], + [sdk.skills.GlacialSpike, 20], // lvl 75 w/o quest skills pts + [sdk.skills.Meteor, 20], + [sdk.skills.ColdMastery, 5], + [sdk.skills.FireBolt, 20], + ], - charms: { - ResLife: { - max: 3, - have: [], - classid: sdk.items.SmallCharm, - stats: function (check) { - return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MaxHp) === 20); - } - }, + charms: { + ResLife: { + max: 3, + have: [], + classid: sdk.items.SmallCharm, + stats: function (check) { + return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MaxHp) === 20); + } + }, - ResMf: { - max: 2, - have: [], - classid: sdk.items.SmallCharm, - stats: function (check) { - return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MagicBonus) === 7); - } - }, + ResMf: { + max: 2, + have: [], + classid: sdk.items.SmallCharm, + stats: function (check) { + return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MagicBonus) === 7); + } + }, - ResFHR: { - max: 3, - have: [], - classid: sdk.items.SmallCharm, - stats: function (check) { - return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.FHR) === 5); - } - }, + ResFHR: { + max: 3, + have: [], + classid: sdk.items.SmallCharm, + stats: function (check) { + return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.FHR) === 5); + } + }, - SkillerFire: { - max: 1, - have: [], - classid: sdk.items.GrandCharm, - stats: function (check) { - return (!check.unique && check.classid === this.classid && check.getStat(sdk.stats.AddSkillTab, sdk.skills.tabs.Fire) === 1 - && check.getStat(sdk.stats.MaxHp) >= 40); - } - }, + SkillerFire: { + max: 1, + have: [], + classid: sdk.items.GrandCharm, + stats: function (check) { + return (!check.unique && check.classid === this.classid && check.getStat(sdk.stats.AddSkillTab, sdk.skills.tabs.Fire) === 1 + && check.getStat(sdk.stats.MaxHp) >= 40); + } + }, - SkillerCold: { - max: 1, - have: [], - classid: sdk.items.GrandCharm, - stats: function (check) { - return (!check.unique && check.classid === this.classid && check.getStat(sdk.stats.AddSkillTab, sdk.skills.tabs.Cold) === 1 - && check.getStat(sdk.stats.MaxHp) >= 40); - } - }, - }, + SkillerCold: { + max: 1, + have: [], + classid: sdk.items.GrandCharm, + stats: function (check) { + return (!check.unique && check.classid === this.classid && check.getStat(sdk.stats.AddSkillTab, sdk.skills.tabs.Cold) === 1 + && check.getStat(sdk.stats.MaxHp) >= 40); + } + }, + }, - AutoBuildTemplate: { - 1: { - Update: function () { - Config.AttackSkill = [-1, sdk.skills.Blizzard, sdk.skills.FireBall, sdk.skills.Blizzard, sdk.skills.FireBall, sdk.skills.Meteor, sdk.skills.GlacialSpike]; - Config.LowManaSkill = [-1, -1]; - Config.SkipImmune = ["fire and cold"]; - Config.HPBuffer = me.expansion ? 1 : 5; - Config.MPBuffer = me.expansion ? 1 : 5; - } - }, - }, + AutoBuildTemplate: { + 1: { + Update: function () { + Config.AttackSkill = [-1, sdk.skills.Blizzard, sdk.skills.FireBall, sdk.skills.Blizzard, sdk.skills.FireBall, sdk.skills.Meteor, sdk.skills.GlacialSpike]; + Config.LowManaSkill = [-1, -1]; + Config.SkipImmune = ["fire and cold"]; + Config.HPBuffer = me.expansion ? 1 : 5; + Config.MPBuffer = me.expansion ? 1 : 5; + } + }, + }, - respec: function () { - if (me.classic) { - return me.charlvl >= 75 && me.diablo; - } else { - return me.haveAll([ - { name: sdk.locale.items.TalRashasBelt, quality: sdk.items.quality.Set }, - { name: sdk.locale.items.TalRashasAmulet, quality: sdk.items.quality.Set }, - { name: sdk.locale.items.TalRashasArmor, quality: sdk.items.quality.Set }, - { name: sdk.locale.items.TalRashasOrb, quality: sdk.items.quality.Set }, - { name: sdk.locale.items.TalRashasHelmet, quality: sdk.items.quality.Set }, - ]); - } - }, + respec: function () { + if (me.classic) { + return me.charlvl >= 75 && me.diablo; + } else { + return me.haveAll([ + { name: sdk.locale.items.TalRashasBelt, quality: sdk.items.quality.Set }, + { name: sdk.locale.items.TalRashasAmulet, quality: sdk.items.quality.Set }, + { name: sdk.locale.items.TalRashasArmor, quality: sdk.items.quality.Set }, + { name: sdk.locale.items.TalRashasOrb, quality: sdk.items.quality.Set }, + { name: sdk.locale.items.TalRashasHelmet, quality: sdk.items.quality.Set }, + ]); + } + }, - active: function () { - return this.respec() && me.getSkill(sdk.skills.FireBall, sdk.skills.subindex.HardPoints) === 20 && me.getSkill(sdk.skills.Blizzard, sdk.skills.subindex.HardPoints) === 20; - }, - }; + active: function () { + return this.respec() && me.getSkill(sdk.skills.FireBall, sdk.skills.subindex.HardPoints) === 20 && me.getSkill(sdk.skills.Blizzard, sdk.skills.subindex.HardPoints) === 20; + }, + }; - // autoequip final gear - let finalGear = [ - // Weapon - Tals Orb - "[name] == swirlingcrystal && [quality] == set && [flag] != ethereal # [skilllightningmastery]+[skillfiremastery]+[skillcoldmastery] >= 3 # [tier] == tierscore(item, 100000)", - // Helmet - Tals Mask - "[name] == deathmask && [quality] == set && [flag] != ethereal # [coldresist]+[lightresist]+[fireresist]+[poisonresist] >= 60 # [tier] == tierscore(item, 100000)", - // Belt - Tals Belt - "[name] == meshbelt && [quality] == set && [flag] != ethereal # [itemmagicbonus] >= 10 # [tier] == tierscore(item, 100000)", - // Final Boots - Sandstorm Treks - "[name] == scarabshellboots && [quality] == unique # [strength]+[vitality] >= 20 # [tier] == tierscore(item, 100000)", - // Boots - War Traveler - "[name] == battleboots && [quality] == unique && [flag] != ethereal # [itemmagicbonus] >= 50 # [tier] == tierscore(item, 5000)", - // Armor - Tals Armor - "[name] == lacqueredplate && [quality] == set # [coldresist] >= 1 # [tier] == 100000", - // Final Shield - Sanctuary - "[name] == hyperion && [flag] == runeword # [fhr] >= 20 && [enhanceddefense] >= 130 && [fireresist] >= 50 # [tier] == 100000", - // Shield - Mosers - "[name] == roundshield && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 180 # [tier] == tierscore(item, 50000)", - // Final Gloves - Perfect Upp'ed Magefist - "[name] == battlegauntlets && [quality] == unique && [flag] != ethereal # [enhanceddefense] == 30 && [addfireskills] == 1 && [defense] == 71 # [tier] == 110000", - // Gloves - Upp'ed Magefist - "[name] == battlegauntlets && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 20 && [addfireskills] == 1 # [tier] == tierscore(item, 100000)", - // Gloves - Magefist - "[name] == lightgauntlets && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 20 && [addfireskills] == 1 # [tier] == tierscore(item, 100000)", - // Amulet - Tals Ammy - "[name] == amulet && [quality] == set # [lightresist] == 33 # [tier] == 100000", - // Final Rings - Perfect Raven Frost & Nagelring - "[type] == ring && [quality] == unique # [dexterity] == 20 # [tier] == 100000", - "[type] == ring && [quality] == unique # [itemmagicbonus] == 30 # [tier] == 100000", - // Rings - Raven Frost - "[type] == ring && [quality] == unique # [dexterity] >= 15 # [tier] == 90000", - // Switch - CTA - "[minimumsockets] >= 5 && [flag] == runeword # [plusskillbattleorders] >= 1 # [secondarytier] == 100000", - // Switch Shield - 1+ all skill - "[type] == shield # [itemallskills] >= 1 # [secondarytier] == tierscore(item, 100000)", - // Merc Armor - Fortitude - "[type] == armor && [flag] == runeword # [enhanceddefense] >= 200 && [enhanceddamage] >= 300 # [merctier] == 100000", - // Merc Final Helmet - Eth Andy's - "[name] == demonhead && [quality] == unique && [flag] == ethereal # [strength] >= 25 && [enhanceddefense] >= 100 # [merctier] == 50000 + mercscore(item)", - // Merc Helmet - Andy's - "[name] == demonhead && [quality] == unique && [flag] != ethereal # [strength] >= 25 && [enhanceddefense] >= 100 # [merctier] == 40000 + mercscore(item)", - ]; - - NTIP.buildList(finalGear); - NTIP.buildFinalGear(finalGear); + // autoequip final gear + let finalGear = [ + // Weapon - Tals Orb + "[name] == swirlingcrystal && [quality] == set && [flag] != ethereal # [skilllightningmastery]+[skillfiremastery]+[skillcoldmastery] >= 3 # [tier] == tierscore(item, 100000)", + // Helmet - Tals Mask + "[name] == deathmask && [quality] == set && [flag] != ethereal # [coldresist]+[lightresist]+[fireresist]+[poisonresist] >= 60 # [tier] == tierscore(item, 100000)", + // Belt - Tals Belt + "[name] == meshbelt && [quality] == set && [flag] != ethereal # [itemmagicbonus] >= 10 # [tier] == tierscore(item, 100000)", + // Final Boots - Sandstorm Treks + "[name] == scarabshellboots && [quality] == unique # [strength]+[vitality] >= 20 # [tier] == tierscore(item, 100000)", + // Boots - War Traveler + "[name] == battleboots && [quality] == unique && [flag] != ethereal # [itemmagicbonus] >= 50 # [tier] == tierscore(item, 5000)", + // Armor - Tals Armor + "[name] == lacqueredplate && [quality] == set # [coldresist] >= 1 # [tier] == 100000", + // Final Shield - Sanctuary + "[name] == hyperion && [flag] == runeword # [fhr] >= 20 && [enhanceddefense] >= 130 && [fireresist] >= 50 # [tier] == 100000", + // Shield - Mosers + "[name] == roundshield && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 180 # [tier] == tierscore(item, 50000)", + // Final Gloves - Perfect Upp'ed Magefist + "[name] == battlegauntlets && [quality] == unique && [flag] != ethereal # [enhanceddefense] == 30 && [addfireskills] == 1 && [defense] == 71 # [tier] == 110000", + // Gloves - Upp'ed Magefist + "[name] == battlegauntlets && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 20 && [addfireskills] == 1 # [tier] == tierscore(item, 100000)", + // Gloves - Magefist + "[name] == lightgauntlets && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 20 && [addfireskills] == 1 # [tier] == tierscore(item, 100000)", + // Amulet - Tals Ammy + "[name] == amulet && [quality] == set # [lightresist] == 33 # [tier] == 100000", + // Final Rings - Perfect Raven Frost & Nagelring + "[type] == ring && [quality] == unique # [dexterity] == 20 # [tier] == 100000", + "[type] == ring && [quality] == unique # [itemmagicbonus] == 30 # [tier] == 100000", + // Rings - Raven Frost + "[type] == ring && [quality] == unique # [dexterity] >= 15 # [tier] == 90000", + // Switch - CTA + "[minimumsockets] >= 5 && [flag] == runeword # [plusskillbattleorders] >= 1 # [secondarytier] == 100000", + // Switch Shield - 1+ all skill + "[type] == shield # [itemallskills] >= 1 # [secondarytier] == tierscore(item, 100000)", + // Merc Armor - Fortitude + "[type] == armor && [flag] == runeword # [enhanceddefense] >= 200 && [enhanceddamage] >= 300 # [merctier] == 100000", + // Merc Final Helmet - Eth Andy's + "[name] == demonhead && [quality] == unique && [flag] == ethereal # [strength] >= 25 && [enhanceddefense] >= 100 # [merctier] == 50000 + mercscore(item)", + // Merc Helmet - Andy's + "[name] == demonhead && [quality] == unique && [flag] != ethereal # [strength] >= 25 && [enhanceddefense] >= 100 # [merctier] == 40000 + mercscore(item)", + ]; + + NTIP.buildList(finalGear); + NTIP.buildFinalGear(finalGear); - return build; - })(); + return build; + })(); })(module); diff --git a/libs/SoloPlay/BuildFiles/sorceress/sorceress.BlovaBuild.js b/libs/SoloPlay/BuildFiles/sorceress/sorceress.BlovaBuild.js index a5448499..e701945a 100644 --- a/libs/SoloPlay/BuildFiles/sorceress/sorceress.BlovaBuild.js +++ b/libs/SoloPlay/BuildFiles/sorceress/sorceress.BlovaBuild.js @@ -7,152 +7,152 @@ (function (module) { - module.exports = (function () { - const build = { - caster: true, - skillstab: sdk.skills.tabs.Cold, - wantedskills: [sdk.skills.Blizzard, sdk.skills.Nova], - usefulskills: [sdk.skills.LightningMastery, sdk.skills.ColdMastery, sdk.skills.GlacialSpike], - precastSkills: [sdk.skills.FrozenArmor], - usefulStats: [sdk.stats.PassiveColdPierce, sdk.stats.PassiveColdMastery, sdk.stats.PassiveLightningMastery, sdk.stats.PassiveLightningPierce], - wantedMerc: MercData[sdk.skills.HolyFreeze], - stats: [ - ["strength", 156], ["dexterity", 35], ["vitality", "all"] - ], - skills: [ - [sdk.skills.Warmth, 1], - [sdk.skills.FrozenArmor, 1], - [sdk.skills.Teleport, 1], - [sdk.skills.Blizzard, 1], - [sdk.skills.Nova, 20], - [sdk.skills.LightningMastery, 20], - [sdk.skills.Blizzard, 20], - [sdk.skills.ColdMastery, 5], - [sdk.skills.IceBlast, 20], - [sdk.skills.GlacialSpike, 5], - [sdk.skills.IceBolt, 14], - ], + module.exports = (function () { + const build = { + caster: true, + skillstab: sdk.skills.tabs.Cold, + wantedskills: [sdk.skills.Blizzard, sdk.skills.Nova], + usefulskills: [sdk.skills.LightningMastery, sdk.skills.ColdMastery, sdk.skills.GlacialSpike], + precastSkills: [sdk.skills.FrozenArmor], + usefulStats: [sdk.stats.PassiveColdPierce, sdk.stats.PassiveColdMastery, sdk.stats.PassiveLightningMastery, sdk.stats.PassiveLightningPierce], + wantedMerc: MercData[sdk.skills.HolyFreeze], + stats: [ + ["strength", 156], ["dexterity", 35], ["vitality", "all"] + ], + skills: [ + [sdk.skills.Warmth, 1], + [sdk.skills.FrozenArmor, 1], + [sdk.skills.Teleport, 1], + [sdk.skills.Blizzard, 1], + [sdk.skills.Nova, 20], + [sdk.skills.LightningMastery, 20], + [sdk.skills.Blizzard, 20], + [sdk.skills.ColdMastery, 5], + [sdk.skills.IceBlast, 20], + [sdk.skills.GlacialSpike, 5], + [sdk.skills.IceBolt, 14], + ], - charms: { - ResLife: { - max: 3, - have: [], - classid: sdk.items.SmallCharm, - stats: function (check) { - return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MaxHp) === 20); - } - }, + charms: { + ResLife: { + max: 3, + have: [], + classid: sdk.items.SmallCharm, + stats: function (check) { + return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MaxHp) === 20); + } + }, - ResMf: { - max: 2, - have: [], - classid: sdk.items.SmallCharm, - stats: function (check) { - return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MagicBonus) === 7); - } - }, + ResMf: { + max: 2, + have: [], + classid: sdk.items.SmallCharm, + stats: function (check) { + return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MagicBonus) === 7); + } + }, - ResFHR: { - max: 3, - have: [], - classid: sdk.items.SmallCharm, - stats: function (check) { - return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.FHR) === 5); - } - }, + ResFHR: { + max: 3, + have: [], + classid: sdk.items.SmallCharm, + stats: function (check) { + return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.FHR) === 5); + } + }, - SkillerLight: { - max: 1, - have: [], - classid: sdk.items.GrandCharm, - stats: function (check) { - return (!check.unique && check.classid === this.classid && check.getStat(sdk.stats.AddSkillTab, sdk.skills.tabs.Lightning) === 1 - && check.getStat(sdk.stats.MaxHp) >= 40); - } - }, + SkillerLight: { + max: 1, + have: [], + classid: sdk.items.GrandCharm, + stats: function (check) { + return (!check.unique && check.classid === this.classid && check.getStat(sdk.stats.AddSkillTab, sdk.skills.tabs.Lightning) === 1 + && check.getStat(sdk.stats.MaxHp) >= 40); + } + }, - SkillerCold: { - max: 1, - have: [], - classid: sdk.items.GrandCharm, - stats: function (check) { - return (!check.unique && check.classid === this.classid && check.getStat(sdk.stats.AddSkillTab, sdk.skills.tabs.Cold) === 1 - && check.getStat(sdk.stats.MaxHp) >= 40); - } - }, - }, + SkillerCold: { + max: 1, + have: [], + classid: sdk.items.GrandCharm, + stats: function (check) { + return (!check.unique && check.classid === this.classid && check.getStat(sdk.stats.AddSkillTab, sdk.skills.tabs.Cold) === 1 + && check.getStat(sdk.stats.MaxHp) >= 40); + } + }, + }, - AutoBuildTemplate: { - 1: { - Update: function () { - Config.AttackSkill = [-1, sdk.skills.Blizzard, sdk.skills.Nova, sdk.skills.Blizzard, sdk.skills.Nova, -1, sdk.skills.IceBlast]; - Config.LowManaSkill = [-1, -1]; - Config.SkipImmune = ["lightning and cold"]; - Config.HPBuffer = me.expansion ? 1 : 5; - Config.MPBuffer = me.expansion ? 1 : 5; - } - }, - }, + AutoBuildTemplate: { + 1: { + Update: function () { + Config.AttackSkill = [-1, sdk.skills.Blizzard, sdk.skills.Nova, sdk.skills.Blizzard, sdk.skills.Nova, -1, sdk.skills.IceBlast]; + Config.LowManaSkill = [-1, -1]; + Config.SkipImmune = ["lightning and cold"]; + Config.HPBuffer = me.expansion ? 1 : 5; + Config.MPBuffer = me.expansion ? 1 : 5; + } + }, + }, - respec: function () { - if (me.classic) { - return me.charlvl >= 75 && me.diablo; - } else { - return (Attack.checkInfinity() || (me.data.merc.gear.includes(sdk.locale.items.Infinity) && !Misc.poll(() => me.getMerc(), 200, 50))); - } - }, + respec: function () { + if (me.classic) { + return me.charlvl >= 75 && me.diablo; + } else { + return (Attack.checkInfinity() || (me.data.merc.gear.includes(sdk.locale.items.Infinity) && !Misc.poll(() => me.getMerc(), 200, 50))); + } + }, - active: function () { - return this.respec() && me.getSkill(sdk.skills.Nova, sdk.skills.subindex.HardPoints) === 20 && me.getSkill(sdk.skills.Blizzard, sdk.skills.subindex.HardPoints) >= 1; - }, - }; - - // autoequip final gear - let finalGear = [ - // Weapon - HotO - "[type] == mace && [flag] == runeword # [itemallskills] == 3 # [tier] == 100000", - // Helmet - Griffons - "[name] == diadem && [quality] == unique && [flag] != ethereal # [fcr] == 25 # [tier] == tierscore(item, 100000)", - // Belt - Arach's - "[name] == spiderwebsash && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 90 # [tier] == tierscore(item, 100000)", - // Final Boots - Sandstorm Treks - "[name] == scarabshellboots && [quality] == unique # [strength]+[vitality] >= 20 # [tier] == tierscore(item, 100000)", - // Boots - War Traveler - "[name] == battleboots && [quality] == unique && [flag] != ethereal # [itemmagicbonus] >= 50 # [tier] == tierscore(item, 5000)", - // Armor - CoH - "[type] == armor && [flag] == runeword && [flag] != ethereal # [fireresist] == 65 && [hpregen] == 7 # [tier] == 100000", - // Shield - Spirit - "[type] == shield # [fcr] >= 25 && [maxmana] >= 89 # [tier] == tierscore(item, 100000)", - // Final Gloves - Perfect Upp'ed Magefist - "[name] == battlegauntlets && [quality] == unique && [flag] != ethereal # [enhanceddefense] == 30 && [addfireskills] == 1 && [defense] == 71 # [tier] == 110000", - // Gloves - Upp'ed Magefist - "[name] == battlegauntlets && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 20 && [addfireskills] == 1 # [tier] == tierscore(item, 100000)", - // Gloves - Magefist - "[name] == lightgauntlets && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 20 && [addfireskills] == 1 # [tier] == tierscore(item, 100000)", - // Amulet - Maras - "[type] == amulet && [quality] == unique # [strength] == 5 && [coldresist] >= 30 # [tier] == tierscore(item, 100000)", - // Final Rings - SoJ & Perfect Bul-Kathos' Wedding Band - "[type] == ring && [quality] == unique # [itemmaxmanapercent] == 25 # [tier] == 100000", - "[name] == ring && [quality] == unique # [maxstamina] == 50 && [lifeleech] == 5 # [tier] == 100000", - // Ring - Bul-Kathos' Wedding Band - "[name] == ring && [quality] == unique # [maxstamina] == 50 && [lifeleech] >= 3 # [tier] == 90000", - // Switch - CTA - "[minimumsockets] >= 5 && [flag] == runeword # [plusskillbattleorders] >= 1 # [secondarytier] == 100000", - // Switch Final Shield - Spirit - "[name] == monarch && [flag] == runeword # [fcr] >= 25 && [maxmana] >= 89 # [secondarytier] == 110000", - // Switch Temporary Shield - 1+ all skill - "[type] == shield # [itemallskills] >= 1 # [secondarytier] == tierscore(item, 50000)", - // Merc Armor - Fortitude - "[type] == armor && [flag] == runeword # [enhanceddefense] >= 200 && [enhanceddamage] >= 300 # [merctier] == 100000", - // Merc Final Helmet - Eth Andy's - "[name] == demonhead && [quality] == unique && [flag] == ethereal # [strength] >= 25 && [enhanceddefense] >= 100 # [merctier] == 50000 + mercscore(item)", - // Merc Helmet - Andy's - "[name] == demonhead && [quality] == unique && [flag] != ethereal # [strength] >= 25 && [enhanceddefense] >= 100 # [merctier] == 40000 + mercscore(item)", - ]; + active: function () { + return this.respec() && me.getSkill(sdk.skills.Nova, sdk.skills.subindex.HardPoints) === 20 && me.getSkill(sdk.skills.Blizzard, sdk.skills.subindex.HardPoints) >= 1; + }, + }; + + // autoequip final gear + let finalGear = [ + // Weapon - HotO + "[type] == mace && [flag] == runeword # [itemallskills] == 3 # [tier] == 100000", + // Helmet - Griffons + "[name] == diadem && [quality] == unique && [flag] != ethereal # [fcr] == 25 # [tier] == tierscore(item, 100000)", + // Belt - Arach's + "[name] == spiderwebsash && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 90 # [tier] == tierscore(item, 100000)", + // Final Boots - Sandstorm Treks + "[name] == scarabshellboots && [quality] == unique # [strength]+[vitality] >= 20 # [tier] == tierscore(item, 100000)", + // Boots - War Traveler + "[name] == battleboots && [quality] == unique && [flag] != ethereal # [itemmagicbonus] >= 50 # [tier] == tierscore(item, 5000)", + // Armor - CoH + "[type] == armor && [flag] == runeword && [flag] != ethereal # [fireresist] == 65 && [hpregen] == 7 # [tier] == 100000", + // Shield - Spirit + "[type] == shield # [fcr] >= 25 && [maxmana] >= 89 # [tier] == tierscore(item, 100000)", + // Final Gloves - Perfect Upp'ed Magefist + "[name] == battlegauntlets && [quality] == unique && [flag] != ethereal # [enhanceddefense] == 30 && [addfireskills] == 1 && [defense] == 71 # [tier] == 110000", + // Gloves - Upp'ed Magefist + "[name] == battlegauntlets && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 20 && [addfireskills] == 1 # [tier] == tierscore(item, 100000)", + // Gloves - Magefist + "[name] == lightgauntlets && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 20 && [addfireskills] == 1 # [tier] == tierscore(item, 100000)", + // Amulet - Maras + "[type] == amulet && [quality] == unique # [strength] == 5 && [coldresist] >= 30 # [tier] == tierscore(item, 100000)", + // Final Rings - SoJ & Perfect Bul-Kathos' Wedding Band + "[type] == ring && [quality] == unique # [itemmaxmanapercent] == 25 # [tier] == 100000", + "[name] == ring && [quality] == unique # [maxstamina] == 50 && [lifeleech] == 5 # [tier] == 100000", + // Ring - Bul-Kathos' Wedding Band + "[name] == ring && [quality] == unique # [maxstamina] == 50 && [lifeleech] >= 3 # [tier] == 90000", + // Switch - CTA + "[minimumsockets] >= 5 && [flag] == runeword # [plusskillbattleorders] >= 1 # [secondarytier] == 100000", + // Switch Final Shield - Spirit + "[name] == monarch && [flag] == runeword # [fcr] >= 25 && [maxmana] >= 89 # [secondarytier] == 110000", + // Switch Temporary Shield - 1+ all skill + "[type] == shield # [itemallskills] >= 1 # [secondarytier] == tierscore(item, 50000)", + // Merc Armor - Fortitude + "[type] == armor && [flag] == runeword # [enhanceddefense] >= 200 && [enhanceddamage] >= 300 # [merctier] == 100000", + // Merc Final Helmet - Eth Andy's + "[name] == demonhead && [quality] == unique && [flag] == ethereal # [strength] >= 25 && [enhanceddefense] >= 100 # [merctier] == 50000 + mercscore(item)", + // Merc Helmet - Andy's + "[name] == demonhead && [quality] == unique && [flag] != ethereal # [strength] >= 25 && [enhanceddefense] >= 100 # [merctier] == 40000 + mercscore(item)", + ]; - NTIP.buildList(finalGear); - NTIP.buildFinalGear(finalGear); + NTIP.buildList(finalGear); + NTIP.buildFinalGear(finalGear); - return build; - })(); + return build; + })(); })(module); diff --git a/libs/SoloPlay/BuildFiles/sorceress/sorceress.ColdBuild.js b/libs/SoloPlay/BuildFiles/sorceress/sorceress.ColdBuild.js index a8ba1899..0d494374 100644 --- a/libs/SoloPlay/BuildFiles/sorceress/sorceress.ColdBuild.js +++ b/libs/SoloPlay/BuildFiles/sorceress/sorceress.ColdBuild.js @@ -7,147 +7,147 @@ (function (module) { - module.exports = (function () { - const build = { - caster: true, - skillstab: sdk.skills.tabs.Cold, - wantedskills: [sdk.skills.Blizzard, sdk.skills.ColdMastery], - usefulskills: [sdk.skills.GlacialSpike, sdk.skills.IceBlast, sdk.skills.StaticField], - precastSkills: [sdk.skills.FrozenArmor], - usefulStats: [sdk.stats.PassiveColdPierce, sdk.stats.PassiveColdMastery], - wantedMerc: MercData[sdk.skills.HolyFreeze], - stats: [ - ["strength", 48], ["vitality", 165], ["strength", 61], - ["vitality", 252], ["strength", 127], ["dexterity", "block"], ["vitality", "all"] - ], - skills: [ - [sdk.skills.Warmth, 1], - [sdk.skills.StaticField, 1], - [sdk.skills.Teleport, 1], - [sdk.skills.FrozenArmor, 1], - [sdk.skills.Blizzard, 20], - [sdk.skills.ColdMastery, 17], - [sdk.skills.IceBlast, 20], // lvl 66 w/o quest skills pts - [sdk.skills.GlacialSpike, 20], - [sdk.skills.IceBolt, 20], - [sdk.skills.ColdMastery, 20] - ], - - charms: { - ResLife: { - max: 3, - have: [], - classid: sdk.items.SmallCharm, - stats: function (check) { - return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MaxHp) === 20); - } - }, + module.exports = (function () { + const build = { + caster: true, + skillstab: sdk.skills.tabs.Cold, + wantedskills: [sdk.skills.Blizzard, sdk.skills.ColdMastery], + usefulskills: [sdk.skills.GlacialSpike, sdk.skills.IceBlast, sdk.skills.StaticField], + precastSkills: [sdk.skills.FrozenArmor], + usefulStats: [sdk.stats.PassiveColdPierce, sdk.stats.PassiveColdMastery], + wantedMerc: MercData[sdk.skills.HolyFreeze], + stats: [ + ["strength", 48], ["vitality", 165], ["strength", 61], + ["vitality", 252], ["strength", 127], ["dexterity", "block"], ["vitality", "all"] + ], + skills: [ + [sdk.skills.Warmth, 1], + [sdk.skills.StaticField, 1], + [sdk.skills.Teleport, 1], + [sdk.skills.FrozenArmor, 1], + [sdk.skills.Blizzard, 20], + [sdk.skills.ColdMastery, 17], + [sdk.skills.IceBlast, 20], // lvl 66 w/o quest skills pts + [sdk.skills.GlacialSpike, 20], + [sdk.skills.IceBolt, 20], + [sdk.skills.ColdMastery, 20] + ], + + charms: { + ResLife: { + max: 3, + have: [], + classid: sdk.items.SmallCharm, + stats: function (check) { + return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MaxHp) === 20); + } + }, - ResMf: { - max: 3, - have: [], - classid: sdk.items.SmallCharm, - stats: function (check) { - return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MagicBonus) === 7); - } - }, + ResMf: { + max: 3, + have: [], + classid: sdk.items.SmallCharm, + stats: function (check) { + return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MagicBonus) === 7); + } + }, - ResFHR: { - max: 1, - have: [], - classid: sdk.items.SmallCharm, - stats: function (check) { - return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.FHR) === 5); - } - }, + ResFHR: { + max: 1, + have: [], + classid: sdk.items.SmallCharm, + stats: function (check) { + return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.FHR) === 5); + } + }, - Skiller: { - max: 2, - have: [], - classid: sdk.items.GrandCharm, - stats: function (check) { - return (!check.unique && check.classid === this.classid && check.getStat(sdk.stats.AddSkillTab, sdk.skills.tabs.Cold) === 1 - && check.getStat(sdk.stats.MaxHp) >= 40); - } - }, - }, + Skiller: { + max: 2, + have: [], + classid: sdk.items.GrandCharm, + stats: function (check) { + return (!check.unique && check.classid === this.classid && check.getStat(sdk.stats.AddSkillTab, sdk.skills.tabs.Cold) === 1 + && check.getStat(sdk.stats.MaxHp) >= 40); + } + }, + }, - AutoBuildTemplate: { - 1: { - Update: function () { - Config.AttackSkill = [-1, sdk.skills.Blizzard, sdk.skills.IceBlast, sdk.skills.Blizzard, sdk.skills.GlacialSpike, -1, -1]; - Config.LowManaSkill = [-1, -1]; - Config.SkipImmune = ["cold"]; - Config.HPBuffer = me.expansion ? 1 : 5; - Config.MPBuffer = me.expansion ? 1 : 5; - } - }, - }, + AutoBuildTemplate: { + 1: { + Update: function () { + Config.AttackSkill = [-1, sdk.skills.Blizzard, sdk.skills.IceBlast, sdk.skills.Blizzard, sdk.skills.GlacialSpike, -1, -1]; + Config.LowManaSkill = [-1, -1]; + Config.SkipImmune = ["cold"]; + Config.HPBuffer = me.expansion ? 1 : 5; + Config.MPBuffer = me.expansion ? 1 : 5; + } + }, + }, - respec: function () { - if (me.classic) { - return me.charlvl >= 75 && me.diablo; - } else { - return me.haveAll([ - { name: sdk.locale.items.TalRashasBelt, quality: sdk.items.quality.Set }, - { name: sdk.locale.items.TalRashasAmulet, quality: sdk.items.quality.Set }, - { name: sdk.locale.items.TalRashasArmor, quality: sdk.items.quality.Set }, - { name: sdk.locale.items.TalRashasOrb, quality: sdk.items.quality.Set }, - { name: sdk.locale.items.TalRashasHelmet, quality: sdk.items.quality.Set }, - ]) && me.hell && me.baal; - } - }, + respec: function () { + if (me.classic) { + return me.charlvl >= 75 && me.diablo; + } else { + return me.haveAll([ + { name: sdk.locale.items.TalRashasBelt, quality: sdk.items.quality.Set }, + { name: sdk.locale.items.TalRashasAmulet, quality: sdk.items.quality.Set }, + { name: sdk.locale.items.TalRashasArmor, quality: sdk.items.quality.Set }, + { name: sdk.locale.items.TalRashasOrb, quality: sdk.items.quality.Set }, + { name: sdk.locale.items.TalRashasHelmet, quality: sdk.items.quality.Set }, + ]) && me.hell && me.baal; + } + }, - active: function () { - return this.respec() && !me.checkSkill(sdk.skills.Meteor, sdk.skills.subindex.HardPoints); - }, - }; - - let finalGear = [ // autoequip final gear - // Weapon - Tals Orb - "[name] == swirlingcrystal && [quality] == set && [flag] != ethereal # [skilllightningmastery]+[skillfiremastery]+[skillcoldmastery] >= 3 # [tier] == tierscore(item, 100000)", - // Helmet - Tals Mask - "[name] == deathmask && [quality] == set && [flag] != ethereal # [coldresist]+[lightresist]+[fireresist]+[poisonresist] >= 60 # [tier] == tierscore(item, 100000)", - // Belt - Tals Belt - "[name] == meshbelt && [quality] == set && [flag] != ethereal # [itemmagicbonus] >= 10 # [tier] == tierscore(item, 100000)", - // Final Boots - Sandstorm Treks - "[name] == scarabshellboots && [quality] == unique # [strength]+[vitality] >= 20 # [tier] == tierscore(item, 100000)", - // Boots - War Traveler - "[name] == battleboots && [quality] == unique && [flag] != ethereal # [itemmagicbonus] >= 50 # [tier] == tierscore(item, 5000)", - // Armor - Tals Armor - "[name] == lacqueredplate && [quality] == set # [coldresist] >= 1 # [tier] == 100000", - // Final Shield - Sanctuary - "[name] == hyperion && [flag] == runeword # [fhr] >= 20 && [enhanceddefense] >= 130 && [fireresist] >= 50 # [tier] == 100000", - // Shield - Mosers - "[name] == roundshield && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 180 # [tier] == tierscore(item, 50000)", - // Final Gloves - Perfect Upp'ed Magefist - "[name] == battlegauntlets && [quality] == unique && [flag] != ethereal # [enhanceddefense] == 30 && [addfireskills] == 1 && [defense] == 71 # [tier] == 110000", - // Gloves - Upp'ed Magefist - "[name] == battlegauntlets && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 20 && [addfireskills] == 1 # [tier] == tierscore(item, 100000)", - // Gloves - Magefist - "[name] == lightgauntlets && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 20 && [addfireskills] == 1 # [tier] == tierscore(item, 100000)", - // Amulet - Tals Ammy - "[name] == amulet && [quality] == set # [lightresist] == 33 # [tier] == 100000", - // Final Rings - Perfect Raven Frost & Nagelring - "[type] == ring && [quality] == unique # [dexterity] == 20 # [tier] == 100000", - "[type] == ring && [quality] == unique # [itemmagicbonus] == 30 # [tier] == 100000", - // Rings - Raven Frost - "[type] == ring && [quality] == unique # [dexterity] >= 15 # [tier] == 90000", - // Switch - CTA - "[minimumsockets] >= 5 && [flag] == runeword # [plusskillbattleorders] >= 1 # [secondarytier] == 100000", - // Switch Shield - 1+ all skill - "[type] == shield # [itemallskills] >= 1 # [secondarytier] == tierscore(item, 100000)", - // Merc Armor - Fortitude - "[type] == armor && [flag] == runeword # [enhanceddefense] >= 200 && [enhanceddamage] >= 300 # [merctier] == 100000", - // Merc Final Helmet - Eth Andy's - "[name] == demonhead && [quality] == unique && [flag] == ethereal # [strength] >= 25 && [enhanceddefense] >= 100 # [merctier] == 50000 + mercscore(item)", - // Merc Helmet - Andy's - "[name] == demonhead && [quality] == unique && [flag] != ethereal # [strength] >= 25 && [enhanceddefense] >= 100 # [merctier] == 40000 + mercscore(item)", - ]; + active: function () { + return this.respec() && !me.checkSkill(sdk.skills.Meteor, sdk.skills.subindex.HardPoints); + }, + }; + + let finalGear = [ // autoequip final gear + // Weapon - Tals Orb + "[name] == swirlingcrystal && [quality] == set && [flag] != ethereal # [skilllightningmastery]+[skillfiremastery]+[skillcoldmastery] >= 3 # [tier] == tierscore(item, 100000)", + // Helmet - Tals Mask + "[name] == deathmask && [quality] == set && [flag] != ethereal # [coldresist]+[lightresist]+[fireresist]+[poisonresist] >= 60 # [tier] == tierscore(item, 100000)", + // Belt - Tals Belt + "[name] == meshbelt && [quality] == set && [flag] != ethereal # [itemmagicbonus] >= 10 # [tier] == tierscore(item, 100000)", + // Final Boots - Sandstorm Treks + "[name] == scarabshellboots && [quality] == unique # [strength]+[vitality] >= 20 # [tier] == tierscore(item, 100000)", + // Boots - War Traveler + "[name] == battleboots && [quality] == unique && [flag] != ethereal # [itemmagicbonus] >= 50 # [tier] == tierscore(item, 5000)", + // Armor - Tals Armor + "[name] == lacqueredplate && [quality] == set # [coldresist] >= 1 # [tier] == 100000", + // Final Shield - Sanctuary + "[name] == hyperion && [flag] == runeword # [fhr] >= 20 && [enhanceddefense] >= 130 && [fireresist] >= 50 # [tier] == 100000", + // Shield - Mosers + "[name] == roundshield && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 180 # [tier] == tierscore(item, 50000)", + // Final Gloves - Perfect Upp'ed Magefist + "[name] == battlegauntlets && [quality] == unique && [flag] != ethereal # [enhanceddefense] == 30 && [addfireskills] == 1 && [defense] == 71 # [tier] == 110000", + // Gloves - Upp'ed Magefist + "[name] == battlegauntlets && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 20 && [addfireskills] == 1 # [tier] == tierscore(item, 100000)", + // Gloves - Magefist + "[name] == lightgauntlets && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 20 && [addfireskills] == 1 # [tier] == tierscore(item, 100000)", + // Amulet - Tals Ammy + "[name] == amulet && [quality] == set # [lightresist] == 33 # [tier] == 100000", + // Final Rings - Perfect Raven Frost & Nagelring + "[type] == ring && [quality] == unique # [dexterity] == 20 # [tier] == 100000", + "[type] == ring && [quality] == unique # [itemmagicbonus] == 30 # [tier] == 100000", + // Rings - Raven Frost + "[type] == ring && [quality] == unique # [dexterity] >= 15 # [tier] == 90000", + // Switch - CTA + "[minimumsockets] >= 5 && [flag] == runeword # [plusskillbattleorders] >= 1 # [secondarytier] == 100000", + // Switch Shield - 1+ all skill + "[type] == shield # [itemallskills] >= 1 # [secondarytier] == tierscore(item, 100000)", + // Merc Armor - Fortitude + "[type] == armor && [flag] == runeword # [enhanceddefense] >= 200 && [enhanceddamage] >= 300 # [merctier] == 100000", + // Merc Final Helmet - Eth Andy's + "[name] == demonhead && [quality] == unique && [flag] == ethereal # [strength] >= 25 && [enhanceddefense] >= 100 # [merctier] == 50000 + mercscore(item)", + // Merc Helmet - Andy's + "[name] == demonhead && [quality] == unique && [flag] != ethereal # [strength] >= 25 && [enhanceddefense] >= 100 # [merctier] == 40000 + mercscore(item)", + ]; - NTIP.buildList(finalGear); - NTIP.buildFinalGear(finalGear); + NTIP.buildList(finalGear); + NTIP.buildFinalGear(finalGear); - return build; - })(); + return build; + })(); })(module); diff --git a/libs/SoloPlay/BuildFiles/sorceress/sorceress.EsorbBuild.js b/libs/SoloPlay/BuildFiles/sorceress/sorceress.EsorbBuild.js index 7907301b..afda5d5e 100644 --- a/libs/SoloPlay/BuildFiles/sorceress/sorceress.EsorbBuild.js +++ b/libs/SoloPlay/BuildFiles/sorceress/sorceress.EsorbBuild.js @@ -7,182 +7,182 @@ (function (module) { - module.exports = (function () { - const build = { - caster: true, - skillstab: sdk.skills.tabs.Lightning, - wantedskills: [sdk.skills.FrozenOrb, sdk.skills.ColdMastery], - usefulskills: [sdk.skills.Telekinesis, sdk.skills.EnergyShield, sdk.skills.StaticField], - precastSkills: [sdk.skills.FrozenArmor], - wantedMerc: MercData[sdk.skills.HolyFreeze], - skills: [ - [sdk.skills.Warmth, 1], - [sdk.skills.FrozenArmor, 1], - [sdk.skills.StaticField, 1], - [sdk.skills.Teleport, 10], - [sdk.skills.EnergyShield, 8], - [sdk.skills.Telekinesis, 20], - [sdk.skills.FrozenOrb, 20], - [sdk.skills.ColdMastery, 17], - [sdk.skills.EnergyShield, 10], - [sdk.skills.StaticField, 20], - ], - stats: [], + module.exports = (function () { + const build = { + caster: true, + skillstab: sdk.skills.tabs.Lightning, + wantedskills: [sdk.skills.FrozenOrb, sdk.skills.ColdMastery], + usefulskills: [sdk.skills.Telekinesis, sdk.skills.EnergyShield, sdk.skills.StaticField], + precastSkills: [sdk.skills.FrozenArmor], + wantedMerc: MercData[sdk.skills.HolyFreeze], + skills: [ + [sdk.skills.Warmth, 1], + [sdk.skills.FrozenArmor, 1], + [sdk.skills.StaticField, 1], + [sdk.skills.Teleport, 10], + [sdk.skills.EnergyShield, 8], + [sdk.skills.Telekinesis, 20], + [sdk.skills.FrozenOrb, 20], + [sdk.skills.ColdMastery, 17], + [sdk.skills.EnergyShield, 10], + [sdk.skills.StaticField, 20], + ], + stats: [], - charms: { - ResLife: { - max: 3, - have: [], - classid: sdk.items.SmallCharm, - stats: function (check) { - return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MaxHp) === 20); - } - }, + charms: { + ResLife: { + max: 3, + have: [], + classid: sdk.items.SmallCharm, + stats: function (check) { + return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MaxHp) === 20); + } + }, - ResMf: { - max: 2, - have: [], - classid: sdk.items.SmallCharm, - stats: function (check) { - return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MagicBonus) === 7); - } - }, + ResMf: { + max: 2, + have: [], + classid: sdk.items.SmallCharm, + stats: function (check) { + return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MagicBonus) === 7); + } + }, - ResFHR: { - max: 3, - have: [], - classid: sdk.items.SmallCharm, - stats: function (check) { - return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.FHR) === 5); - } - }, + ResFHR: { + max: 3, + have: [], + classid: sdk.items.SmallCharm, + stats: function (check) { + return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.FHR) === 5); + } + }, - SkillerLight: { - max: 1, - have: [], - classid: sdk.items.GrandCharm, - stats: function (check) { - return (!check.unique && check.classid === this.classid && check.getStat(sdk.stats.AddSkillTab, sdk.skills.tabs.Lightning) === 1 - && check.getStat(sdk.stats.MaxHp) >= 40); - } - }, + SkillerLight: { + max: 1, + have: [], + classid: sdk.items.GrandCharm, + stats: function (check) { + return (!check.unique && check.classid === this.classid && check.getStat(sdk.stats.AddSkillTab, sdk.skills.tabs.Lightning) === 1 + && check.getStat(sdk.stats.MaxHp) >= 40); + } + }, - SkillerCold: { - max: 1, - have: [], - classid: sdk.items.GrandCharm, - stats: function (check) { - return (!check.unique && check.classid === this.classid && check.getStat(sdk.stats.AddSkillTab, sdk.skills.tabs.Cold) === 1 - && check.getStat(sdk.stats.MaxHp) >= 40); - } - }, - }, - - AutoBuildTemplate: { - 1: { - Update: function () { - Config.AttackSkill = [-1, sdk.skills.FrozenOrb, sdk.skills.StaticField, sdk.skills.FrozenOrb, sdk.skills.StaticField, -1, -1]; - Config.LowManaSkill = [-1, -1]; - Config.SkipImmune = ["cold"]; - Config.HPBuffer = me.expansion ? 1 : 5; - Config.MPBuffer = me.expansion ? 1 : 5; - } - }, - }, + SkillerCold: { + max: 1, + have: [], + classid: sdk.items.GrandCharm, + stats: function (check) { + return (!check.unique && check.classid === this.classid && check.getStat(sdk.stats.AddSkillTab, sdk.skills.tabs.Cold) === 1 + && check.getStat(sdk.stats.MaxHp) >= 40); + } + }, + }, + + AutoBuildTemplate: { + 1: { + Update: function () { + Config.AttackSkill = [-1, sdk.skills.FrozenOrb, sdk.skills.StaticField, sdk.skills.FrozenOrb, sdk.skills.StaticField, -1, -1]; + Config.LowManaSkill = [-1, -1]; + Config.SkipImmune = ["cold"]; + Config.HPBuffer = me.expansion ? 1 : 5; + Config.MPBuffer = me.expansion ? 1 : 5; + } + }, + }, - respec: function () { - if (me.classic) { - return me.charlvl >= 75 && me.diablo; - } else { - return me.haveAll([ - { name: sdk.locale.items.TalRashasBelt, quality: sdk.items.quality.Set }, - { name: sdk.locale.items.TalRashasAmulet, quality: sdk.items.quality.Set }, - { name: sdk.locale.items.TalRashasArmor, quality: sdk.items.quality.Set }, - { name: sdk.locale.items.TalRashasOrb, quality: sdk.items.quality.Set }, - { name: sdk.locale.items.TalRashasHelmet, quality: sdk.items.quality.Set }, - ]) && me.hell && me.baal; - } - }, + respec: function () { + if (me.classic) { + return me.charlvl >= 75 && me.diablo; + } else { + return me.haveAll([ + { name: sdk.locale.items.TalRashasBelt, quality: sdk.items.quality.Set }, + { name: sdk.locale.items.TalRashasAmulet, quality: sdk.items.quality.Set }, + { name: sdk.locale.items.TalRashasArmor, quality: sdk.items.quality.Set }, + { name: sdk.locale.items.TalRashasOrb, quality: sdk.items.quality.Set }, + { name: sdk.locale.items.TalRashasHelmet, quality: sdk.items.quality.Set }, + ]) && me.hell && me.baal; + } + }, - active: function () { - return this.respec() && me.getSkill(sdk.skills.Telekinesis, sdk.skills.subindex.HardPoints) === 20; - }, - }; + active: function () { + return this.respec() && me.getSkill(sdk.skills.Telekinesis, sdk.skills.subindex.HardPoints) === 20; + }, + }; - build.stats = me.classic - ? [ - ["dexterity", 51], ["strength", 80], ["energy", 100], ["vitality", 125], - ["energy", 150], ["vitality", 150], ["energy", "all"] - ] - : [ - ["strength", 48], ["vitality", 165], ["strength", 61], - ["vitality", 252], ["strength", 127], ["dexterity", "block"], ["vitality", "all"] - ]; - - let finalGear = me.classic - ? [ - // Weapon - Spectral Shard - "[name] == blade && [quality] == unique # [fcr] == 20 && [allres] == 10 # [tier] == 100000", - // Helm - Tarnhelm - "[name] == skullcap && [quality] == unique # [itemallskills] == 1 && [itemmagicbonus] >= 25 # [tier] == tierscore(item, 100000)", - // Shield - "[type] == shield && [quality] >= magic # [sorceressskills] == 2 && [allres] >= 16 # [tier] == tierscore(item, 100000)", - // Rings - SoJ - "[type] == ring && [quality] == unique # [itemmaxmanapercent] == 25 # [tier] == 100000", - // Amulet - "[type] == amulet && [quality] >= magic # [sorceressskills] == 2 && [fcr] == 10 # [tier] == tierscore(item, 100000)", - // Boots - "[type] == boots && [quality] >= magic # [frw] >= 20 && [fhr] == 10 && [coldresist]+[lightresist] >= 10 # [tier] == tierscore(item, 100000)", - // Belt - "[type] == belt && [quality] >= magic # [fhr] >= 20 && [maxhp] >= 40 && [fireresist]+[lightresist] >= 20 # [tier] == tierscore(item, 100000)", - // Gloves - Magefist - "[name] == lightgauntlets && [quality] == unique # [fcr] >= 20 && [addfireskills] == 1 # [tier] == tierscore(item, 100000)", - ] - : [ - // Weapon - Tals Orb - "[name] == swirlingcrystal && [quality] == set && [flag] != ethereal # [skilllightningmastery]+[skillfiremastery]+[skillcoldmastery] >= 3 # [tier] == tierscore(item, 100000)", - // Helmet - Tals Mask - "[name] == deathmask && [quality] == set && [flag] != ethereal # [coldresist]+[lightresist]+[fireresist]+[poisonresist] >= 60 # [tier] == tierscore(item, 100000)", - // Belt - Tals Belt - "[name] == meshbelt && [quality] == set && [flag] != ethereal # [itemmagicbonus] >= 10 # [tier] == tierscore(item, 100000)", - // Final Boots - Sandstorm Treks - "[name] == scarabshellboots && [quality] == unique # [strength]+[vitality] >= 20 # [tier] == tierscore(item, 100000)", - // Boots - War Traveler - "[name] == battleboots && [quality] == unique && [flag] != ethereal # [itemmagicbonus] >= 50 # [tier] == tierscore(item, 5000)", - // Armor - Tals Armor - "[name] == lacqueredplate && [quality] == set # [coldresist] >= 1 # [tier] == 100000", - // Final Shield - Sanctuary - "[name] == hyperion && [flag] == runeword # [fhr] >= 20 && [enhanceddefense] >= 130 && [fireresist] >= 50 # [tier] == 100000", - // Shield - Mosers - "[name] == roundshield && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 180 # [tier] == tierscore(item, 50000)", - // Final Gloves - Perfect Upp'ed Magefist - "[name] == battlegauntlets && [quality] == unique && [flag] != ethereal # [enhanceddefense] == 30 && [addfireskills] == 1 && [defense] == 71 # [tier] == 110000", - // Gloves - Upp'ed Magefist - "[name] == battlegauntlets && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 20 && [addfireskills] == 1 # [tier] == tierscore(item, 100000)", - // Gloves - Magefist - "[name] == lightgauntlets && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 20 && [addfireskills] == 1 # [tier] == tierscore(item, 100000)", - // Amulet - Tals Ammy - "[name] == amulet && [quality] == set # [lightresist] == 33 # [tier] == 100000", - // Final Rings - Perfect Raven Frost & Nagelring - "[type] == ring && [quality] == unique # [dexterity] == 20 # [tier] == 100000", - "[type] == ring && [quality] == unique # [itemmagicbonus] == 30 # [tier] == 100000", - // Rings - Raven Frost - "[type] == ring && [quality] == unique # [dexterity] >= 15 # [tier] == 90000", - // Switch - CTA - "[minimumsockets] >= 5 && [flag] == runeword # [plusskillbattleorders] >= 1 # [secondarytier] == 100000", - // Switch Shield - 1+ all skill - "[type] == shield # [itemallskills] >= 1 # [secondarytier] == tierscore(item, 100000)", - // Merc Armor - Fortitude - "[type] == armor && [flag] == runeword # [enhanceddefense] >= 200 && [enhanceddamage] >= 300 # [merctier] == 100000", - // Merc Final Helmet - Eth Andy's - "[name] == demonhead && [quality] == unique && [flag] == ethereal # [strength] >= 25 && [enhanceddefense] >= 100 # [merctier] == 50000 + mercscore(item)", - // Merc Helmet - Andy's - "[name] == demonhead && [quality] == unique && [flag] != ethereal # [strength] >= 25 && [enhanceddefense] >= 100 # [merctier] == 40000 + mercscore(item)", - ]; + build.stats = me.classic + ? [ + ["dexterity", 51], ["strength", 80], ["energy", 100], ["vitality", 125], + ["energy", 150], ["vitality", 150], ["energy", "all"] + ] + : [ + ["strength", 48], ["vitality", 165], ["strength", 61], + ["vitality", 252], ["strength", 127], ["dexterity", "block"], ["vitality", "all"] + ]; + + let finalGear = me.classic + ? [ + // Weapon - Spectral Shard + "[name] == blade && [quality] == unique # [fcr] == 20 && [allres] == 10 # [tier] == 100000", + // Helm - Tarnhelm + "[name] == skullcap && [quality] == unique # [itemallskills] == 1 && [itemmagicbonus] >= 25 # [tier] == tierscore(item, 100000)", + // Shield + "[type] == shield && [quality] >= magic # [sorceressskills] == 2 && [allres] >= 16 # [tier] == tierscore(item, 100000)", + // Rings - SoJ + "[type] == ring && [quality] == unique # [itemmaxmanapercent] == 25 # [tier] == 100000", + // Amulet + "[type] == amulet && [quality] >= magic # [sorceressskills] == 2 && [fcr] == 10 # [tier] == tierscore(item, 100000)", + // Boots + "[type] == boots && [quality] >= magic # [frw] >= 20 && [fhr] == 10 && [coldresist]+[lightresist] >= 10 # [tier] == tierscore(item, 100000)", + // Belt + "[type] == belt && [quality] >= magic # [fhr] >= 20 && [maxhp] >= 40 && [fireresist]+[lightresist] >= 20 # [tier] == tierscore(item, 100000)", + // Gloves - Magefist + "[name] == lightgauntlets && [quality] == unique # [fcr] >= 20 && [addfireskills] == 1 # [tier] == tierscore(item, 100000)", + ] + : [ + // Weapon - Tals Orb + "[name] == swirlingcrystal && [quality] == set && [flag] != ethereal # [skilllightningmastery]+[skillfiremastery]+[skillcoldmastery] >= 3 # [tier] == tierscore(item, 100000)", + // Helmet - Tals Mask + "[name] == deathmask && [quality] == set && [flag] != ethereal # [coldresist]+[lightresist]+[fireresist]+[poisonresist] >= 60 # [tier] == tierscore(item, 100000)", + // Belt - Tals Belt + "[name] == meshbelt && [quality] == set && [flag] != ethereal # [itemmagicbonus] >= 10 # [tier] == tierscore(item, 100000)", + // Final Boots - Sandstorm Treks + "[name] == scarabshellboots && [quality] == unique # [strength]+[vitality] >= 20 # [tier] == tierscore(item, 100000)", + // Boots - War Traveler + "[name] == battleboots && [quality] == unique && [flag] != ethereal # [itemmagicbonus] >= 50 # [tier] == tierscore(item, 5000)", + // Armor - Tals Armor + "[name] == lacqueredplate && [quality] == set # [coldresist] >= 1 # [tier] == 100000", + // Final Shield - Sanctuary + "[name] == hyperion && [flag] == runeword # [fhr] >= 20 && [enhanceddefense] >= 130 && [fireresist] >= 50 # [tier] == 100000", + // Shield - Mosers + "[name] == roundshield && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 180 # [tier] == tierscore(item, 50000)", + // Final Gloves - Perfect Upp'ed Magefist + "[name] == battlegauntlets && [quality] == unique && [flag] != ethereal # [enhanceddefense] == 30 && [addfireskills] == 1 && [defense] == 71 # [tier] == 110000", + // Gloves - Upp'ed Magefist + "[name] == battlegauntlets && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 20 && [addfireskills] == 1 # [tier] == tierscore(item, 100000)", + // Gloves - Magefist + "[name] == lightgauntlets && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 20 && [addfireskills] == 1 # [tier] == tierscore(item, 100000)", + // Amulet - Tals Ammy + "[name] == amulet && [quality] == set # [lightresist] == 33 # [tier] == 100000", + // Final Rings - Perfect Raven Frost & Nagelring + "[type] == ring && [quality] == unique # [dexterity] == 20 # [tier] == 100000", + "[type] == ring && [quality] == unique # [itemmagicbonus] == 30 # [tier] == 100000", + // Rings - Raven Frost + "[type] == ring && [quality] == unique # [dexterity] >= 15 # [tier] == 90000", + // Switch - CTA + "[minimumsockets] >= 5 && [flag] == runeword # [plusskillbattleorders] >= 1 # [secondarytier] == 100000", + // Switch Shield - 1+ all skill + "[type] == shield # [itemallskills] >= 1 # [secondarytier] == tierscore(item, 100000)", + // Merc Armor - Fortitude + "[type] == armor && [flag] == runeword # [enhanceddefense] >= 200 && [enhanceddamage] >= 300 # [merctier] == 100000", + // Merc Final Helmet - Eth Andy's + "[name] == demonhead && [quality] == unique && [flag] == ethereal # [strength] >= 25 && [enhanceddefense] >= 100 # [merctier] == 50000 + mercscore(item)", + // Merc Helmet - Andy's + "[name] == demonhead && [quality] == unique && [flag] != ethereal # [strength] >= 25 && [enhanceddefense] >= 100 # [merctier] == 40000 + mercscore(item)", + ]; - NTIP.buildList(finalGear); - NTIP.buildFinalGear(finalGear); + NTIP.buildList(finalGear); + NTIP.buildFinalGear(finalGear); - return build; - })(); + return build; + })(); })(module); diff --git a/libs/SoloPlay/BuildFiles/sorceress/sorceress.LevelingBuild.js b/libs/SoloPlay/BuildFiles/sorceress/sorceress.LevelingBuild.js index 49af8cd9..6272b038 100644 --- a/libs/SoloPlay/BuildFiles/sorceress/sorceress.LevelingBuild.js +++ b/libs/SoloPlay/BuildFiles/sorceress/sorceress.LevelingBuild.js @@ -7,84 +7,84 @@ (function (module) { - module.exports = (function () { - const build = { - caster: true, - skillstab: sdk.skills.tabs.Cold, - wantedskills: [sdk.skills.Blizzard, sdk.skills.FireBall, sdk.skills.ColdMastery], - usefulskills: [sdk.skills.GlacialSpike, sdk.skills.Meteor, sdk.skills.FireMastery, sdk.skills.StaticField], - usefulStats: [sdk.stats.PassiveColdPierce, sdk.stats.PassiveColdMastery, sdk.stats.PassiveFireMastery, sdk.stats.PassiveFirePierce], - wantedMerc: MercData[sdk.skills.HolyFreeze], - stats: [], - skills: [], + module.exports = (function () { + const build = { + caster: true, + skillstab: sdk.skills.tabs.Cold, + wantedskills: [sdk.skills.Blizzard, sdk.skills.FireBall, sdk.skills.ColdMastery], + usefulskills: [sdk.skills.GlacialSpike, sdk.skills.Meteor, sdk.skills.FireMastery, sdk.skills.StaticField], + usefulStats: [sdk.stats.PassiveColdPierce, sdk.stats.PassiveColdMastery, sdk.stats.PassiveFireMastery, sdk.stats.PassiveFirePierce], + wantedMerc: MercData[sdk.skills.HolyFreeze], + stats: [], + skills: [], - active: function () { - return (me.charlvl > CharInfo.respecOne && me.charlvl > CharInfo.respecTwo && me.checkSkill(sdk.skills.FireMastery, sdk.skills.subindex.HardPoints) && !Check.finalBuild().active()); - }, + active: function () { + return (me.charlvl > CharInfo.respecOne && me.charlvl > CharInfo.respecTwo && me.checkSkill(sdk.skills.FireMastery, sdk.skills.subindex.HardPoints) && !Check.finalBuild().active()); + }, - AutoBuildTemplate: { - 1: { - Update: function () { - Config.AttackSkill = [-1, sdk.skills.Blizzard, sdk.skills.IceBlast, sdk.skills.Blizzard, sdk.skills.IceBlast, sdk.skills.Meteor, sdk.skills.FireBall]; - Config.LowManaSkill = [-1, -1]; - Config.SkipImmune = ["fire and cold"]; - Config.BeltColumn = ["hp", "hp", "mp", "mp"]; - Config.HPBuffer = me.expansion ? 2 : 5; - Config.MPBuffer = me.expansion && me.charlvl < 80 ? 6 : me.classic ? 5 : 2; - SetUp.belt(); - } - } - }, - }; - - // Has to be set after its loaded - build.stats = me.classic - ? [ - ["dexterity", 51], ["strength", 80], ["energy", 100], ["vitality", "all"] - ] : [ - ["energy", 50], ["strength", 48], ["vitality", 165], - ["strength", 61], ["vitality", 200], ["strength", 127], - ["vitality", 252], ["dexterity", "block"], ["vitality", "all"] - ]; - - build.skills = me.classic - ? [ - // Total skills at respec = 70 - [sdk.skills.Warmth, 1], // points left 69 - [sdk.skills.FrozenArmor, 1], // points left 68 - [sdk.skills.StaticField, 6], // points left 62 - [sdk.skills.Teleport, 4], // points left 57 - [sdk.skills.Meteor, 8], // points left 44 - [sdk.skills.FireMastery, 1], // points left 43 - [sdk.skills.ColdMastery, 1], // points left 42 - [sdk.skills.FrozenOrb, 1], // points left 36 - [sdk.skills.FireBall, 10], // points left 27 - [sdk.skills.Blizzard, 20], // points left 8 - [sdk.skills.IceBlast, 12], // points left 0 - [sdk.skills.Meteor, 10], - [sdk.skills.IceBlast, 20], - [sdk.skills.ColdMastery, 17], - [sdk.skills.FireBolt, 20], - ] : [ - // Total skills at respec = 70 - [sdk.skills.Warmth, 1], // points left 69 - [sdk.skills.FrozenArmor, 1], // points left 68 - [sdk.skills.StaticField, 1], // points left 67 - [sdk.skills.Teleport, 1], // points left 65 - [sdk.skills.Meteor, 1], // points left 59 - [sdk.skills.FireMastery, 1], // points left 58 - [sdk.skills.ColdMastery, 1], // points left 57 - [sdk.skills.FrozenOrb, 1], // points left 51 - [sdk.skills.FireBall, 20], // points left 32 - [sdk.skills.Blizzard, 20], // points left 13 - [sdk.skills.IceBlast, 15], // points left 0 - [sdk.skills.Meteor, 15], - [sdk.skills.IceBlast, 20], - [sdk.skills.Meteor, 20], - [sdk.skills.ColdMastery, 5], - [sdk.skills.FireBolt, 20], - ]; - - return build; - })(); + AutoBuildTemplate: { + 1: { + Update: function () { + Config.AttackSkill = [-1, sdk.skills.Blizzard, sdk.skills.IceBlast, sdk.skills.Blizzard, sdk.skills.IceBlast, sdk.skills.Meteor, sdk.skills.FireBall]; + Config.LowManaSkill = [-1, -1]; + Config.SkipImmune = ["fire and cold"]; + Config.BeltColumn = ["hp", "hp", "mp", "mp"]; + Config.HPBuffer = me.expansion ? 2 : 5; + Config.MPBuffer = me.expansion && me.charlvl < 80 ? 6 : me.classic ? 5 : 2; + SetUp.belt(); + } + } + }, + }; + + // Has to be set after its loaded + build.stats = me.classic + ? [ + ["dexterity", 51], ["strength", 80], ["energy", 100], ["vitality", "all"] + ] : [ + ["energy", 50], ["strength", 48], ["vitality", 165], + ["strength", 61], ["vitality", 200], ["strength", 127], + ["vitality", 252], ["dexterity", "block"], ["vitality", "all"] + ]; + + build.skills = me.classic + ? [ + // Total skills at respec = 70 + [sdk.skills.Warmth, 1], // points left 69 + [sdk.skills.FrozenArmor, 1], // points left 68 + [sdk.skills.StaticField, 6], // points left 62 + [sdk.skills.Teleport, 4], // points left 57 + [sdk.skills.Meteor, 8], // points left 44 + [sdk.skills.FireMastery, 1], // points left 43 + [sdk.skills.ColdMastery, 1], // points left 42 + [sdk.skills.FrozenOrb, 1], // points left 36 + [sdk.skills.FireBall, 10], // points left 27 + [sdk.skills.Blizzard, 20], // points left 8 + [sdk.skills.IceBlast, 12], // points left 0 + [sdk.skills.Meteor, 10], + [sdk.skills.IceBlast, 20], + [sdk.skills.ColdMastery, 17], + [sdk.skills.FireBolt, 20], + ] : [ + // Total skills at respec = 70 + [sdk.skills.Warmth, 1], // points left 69 + [sdk.skills.FrozenArmor, 1], // points left 68 + [sdk.skills.StaticField, 1], // points left 67 + [sdk.skills.Teleport, 1], // points left 65 + [sdk.skills.Meteor, 1], // points left 59 + [sdk.skills.FireMastery, 1], // points left 58 + [sdk.skills.ColdMastery, 1], // points left 57 + [sdk.skills.FrozenOrb, 1], // points left 51 + [sdk.skills.FireBall, 20], // points left 32 + [sdk.skills.Blizzard, 20], // points left 13 + [sdk.skills.IceBlast, 15], // points left 0 + [sdk.skills.Meteor, 15], + [sdk.skills.IceBlast, 20], + [sdk.skills.Meteor, 20], + [sdk.skills.ColdMastery, 5], + [sdk.skills.FireBolt, 20], + ]; + + return build; + })(); })(module); diff --git a/libs/SoloPlay/BuildFiles/sorceress/sorceress.LightningBuild.js b/libs/SoloPlay/BuildFiles/sorceress/sorceress.LightningBuild.js index 0dbf62eb..48ef2ddf 100644 --- a/libs/SoloPlay/BuildFiles/sorceress/sorceress.LightningBuild.js +++ b/libs/SoloPlay/BuildFiles/sorceress/sorceress.LightningBuild.js @@ -7,150 +7,150 @@ (function (module) { - module.exports = (function () { - const build = { - caster: true, - skillstab: sdk.skills.tabs.Lightning, - wantedskills: [sdk.skills.ChainLightning, sdk.skills.Lightning], - usefulskills: [sdk.skills.LightningMastery, sdk.skills.ChargedBolt, sdk.skills.Nova], - precastSkills: [sdk.skills.FrozenArmor], - usefulStats: [sdk.stats.PassiveLightningMastery, sdk.stats.PassiveLightningPierce], - wantedMerc: MercData[sdk.skills.HolyFreeze], - stats: [ - ["strength", 156], ["dexterity", 35], ["vitality", "all"] - ], - skills: [ - [sdk.skills.Warmth, 1], - [sdk.skills.StaticField, 1], - [sdk.skills.Teleport, 1], - [sdk.skills.FrozenArmor, 1], - [sdk.skills.ThunderStorm, 1], - [sdk.skills.LightningMastery, 1], - [sdk.skills.Lightning, 20], - [sdk.skills.ChainLightning, 20], - [sdk.skills.LightningMastery, 20], // lvl 69 w/o quest skill pts - [sdk.skills.Nova, 20], - [sdk.skills.ChargedBolt, 20], - ], + module.exports = (function () { + const build = { + caster: true, + skillstab: sdk.skills.tabs.Lightning, + wantedskills: [sdk.skills.ChainLightning, sdk.skills.Lightning], + usefulskills: [sdk.skills.LightningMastery, sdk.skills.ChargedBolt, sdk.skills.Nova], + precastSkills: [sdk.skills.FrozenArmor], + usefulStats: [sdk.stats.PassiveLightningMastery, sdk.stats.PassiveLightningPierce], + wantedMerc: MercData[sdk.skills.HolyFreeze], + stats: [ + ["strength", 156], ["dexterity", 35], ["vitality", "all"] + ], + skills: [ + [sdk.skills.Warmth, 1], + [sdk.skills.StaticField, 1], + [sdk.skills.Teleport, 1], + [sdk.skills.FrozenArmor, 1], + [sdk.skills.ThunderStorm, 1], + [sdk.skills.LightningMastery, 1], + [sdk.skills.Lightning, 20], + [sdk.skills.ChainLightning, 20], + [sdk.skills.LightningMastery, 20], // lvl 69 w/o quest skill pts + [sdk.skills.Nova, 20], + [sdk.skills.ChargedBolt, 20], + ], - charms: { - ResLife: { - max: 3, - have: [], - classid: sdk.items.SmallCharm, - stats: function (check) { - return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MaxHp) === 20); - } - }, + charms: { + ResLife: { + max: 3, + have: [], + classid: sdk.items.SmallCharm, + stats: function (check) { + return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MaxHp) === 20); + } + }, - ResMf: { - max: 2, - have: [], - classid: sdk.items.SmallCharm, - stats: function (check) { - return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MagicBonus) === 7); - } - }, + ResMf: { + max: 2, + have: [], + classid: sdk.items.SmallCharm, + stats: function (check) { + return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MagicBonus) === 7); + } + }, - ResFHR: { - max: 1, - have: [], - classid: sdk.items.SmallCharm, - stats: function (check) { - return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.FHR) === 5); - } - }, + ResFHR: { + max: 1, + have: [], + classid: sdk.items.SmallCharm, + stats: function (check) { + return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.FHR) === 5); + } + }, - LifeMana: { - max: 2, - have: [], - classid: sdk.items.SmallCharm, - stats: function (check) { - return (!check.unique && check.classid === this.classid && check.getStat(sdk.stats.MaxHp) === 20 && check.getStat(sdk.stats.MaxMana) === 17); - } - }, + LifeMana: { + max: 2, + have: [], + classid: sdk.items.SmallCharm, + stats: function (check) { + return (!check.unique && check.classid === this.classid && check.getStat(sdk.stats.MaxHp) === 20 && check.getStat(sdk.stats.MaxMana) === 17); + } + }, - Skiller: { - max: 2, - have: [], - classid: sdk.items.GrandCharm, - stats: function (check) { - return (!check.unique && check.classid === this.classid && check.getStat(sdk.stats.AddSkillTab, sdk.skills.tabs.Lightning) === 1 - && check.getStat(sdk.stats.MaxHp) >= 40); - } - }, - }, + Skiller: { + max: 2, + have: [], + classid: sdk.items.GrandCharm, + stats: function (check) { + return (!check.unique && check.classid === this.classid && check.getStat(sdk.stats.AddSkillTab, sdk.skills.tabs.Lightning) === 1 + && check.getStat(sdk.stats.MaxHp) >= 40); + } + }, + }, - AutoBuildTemplate: { - 1: { - Update: function () { - Config.AttackSkill = [-1, sdk.skills.Lightning, sdk.skills.ChainLightning, sdk.skills.ChainLightning, sdk.skills.Lightning, -1, -1]; - Config.LowManaSkill = [-1, -1]; - Config.SkipImmune = ["lightning"]; - } - }, - }, + AutoBuildTemplate: { + 1: { + Update: function () { + Config.AttackSkill = [-1, sdk.skills.Lightning, sdk.skills.ChainLightning, sdk.skills.ChainLightning, sdk.skills.Lightning, -1, -1]; + Config.LowManaSkill = [-1, -1]; + Config.SkipImmune = ["lightning"]; + } + }, + }, - respec: function () { - if (me.classic) { - return me.charlvl >= 75 && me.diablo; - } else { - return (Attack.checkInfinity() || (me.data.merc.gear.includes(sdk.locale.items.Infinity) && !Misc.poll(() => me.getMerc(), 200, 50))); - } - }, + respec: function () { + if (me.classic) { + return me.charlvl >= 75 && me.diablo; + } else { + return (Attack.checkInfinity() || (me.data.merc.gear.includes(sdk.locale.items.Infinity) && !Misc.poll(() => me.getMerc(), 200, 50))); + } + }, - active: function () { - return this.respec() && me.getSkill(sdk.skills.Lightning, sdk.skills.subindex.HardPoints) === 20 && !me.checkSkill(sdk.skills.Blizzard, sdk.skills.subindex.HardPoints); - }, - }; - - // autoequip final gear - let finalGear = [ - // Weapon - HotO - "[type] == mace && [flag] == runeword # [fcr] == 40 # [tier] == 100000", - // Helmet - Harlequin's Crest - "[name] == shako && [quality] == unique && [flag] != ethereal # [damageresist] >= 10 # [tier] == tierscore(item, 100000)", - // Belt - Arach's - "[name] == spiderwebsash && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 90 # [tier] == tierscore(item, 100000)", - // Boots - "[name] == battleboots && [quality] == unique && [flag] != ethereal # [itemmagicbonus] >= 50 # [tier] == tierscore(item, 5000)", // War Traveler - "[name] == scarabshellboots && [quality] == unique # [strength]+[vitality] >= 20 # [tier] == tierscore(item, 100000)", // Sandstorm Treks - // Armor - CoH - "[type] == armor && [flag] == runeword && [flag] != ethereal # [fireresist] == 65 && [hpregen] == 7 # [tier] == 100000", - // Shield - Spirit - "[type] == shield # [fcr] >= 25 && [maxmana] >= 89 # [tier] == tierscore(item, 100000)", - // Final Gloves - Perfect 2x Upp'ed Magefist - "[name] == crusadergauntlets && [quality] == unique && [flag] != ethereal # [enhanceddefense] == 30 && [addfireskills] == 1 # [tier] == 110000", - // Gloves - 2x Upp'ed Magefist - "[name] == crusadergauntlets && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 20 && [addfireskills] == 1 # [tier] == tierscore(item, 100000)", - // Gloves - Upp'ed Magefist - "[name] == battlegauntlets && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 20 && [addfireskills] == 1 # [tier] == tierscore(item, 100000)", - // Gloves - Magefist - "[name] == lightgauntlets && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 20 && [addfireskills] == 1 # [tier] == tierscore(item, 100000)", - // Amulet - Maras - "[type] == amulet && [quality] == unique # [strength] == 5 && [coldresist] >= 30 # [tier] == tierscore(item, 100000)", - // Final Rings - SoJ & Perfect Wisp - "[type] == ring && [quality] == unique # [itemmaxmanapercent] == 25 # [tier] == 100000", - "[name] == ring && [quality] == unique # [itemabsorblightpercent] == 20 # [tier] == tierscore(item, 110000)", - // Rings - Wisp - "[name] == ring && [quality] == unique # [itemabsorblightpercent] >= 10 # [tier] == tierscore(item, 100000)", - // Switch - CTA - "[minimumsockets] >= 5 && [flag] == runeword # [plusskillbattleorders] >= 1 # [secondarytier] == 100000", - // Switch Final Shield - Spirit - "[name] == monarch && [flag] == runeword # [fcr] >= 25 && [maxmana] >= 89 # [secondarytier] == 110000", - // Switch Temporary Shield - 1+ all skill - "[type] == shield # [itemallskills] >= 1 # [secondarytier] == tierscore(item, 50000)", - // Merc Armor - Fortitude - "[type] == armor && [flag] == runeword # [enhanceddefense] >= 200 && [enhanceddamage] >= 300 # [merctier] == 100000", - // Merc Final Helmet - Eth Andy's - "[name] == demonhead && [quality] == unique && [flag] == ethereal # [strength] >= 25 && [enhanceddefense] >= 100 # [merctier] == 50000 + mercscore(item)", - // Merc Helmet - Andy's - "[name] == demonhead && [quality] == unique && [flag] != ethereal # [strength] >= 25 && [enhanceddefense] >= 100 # [merctier] == 40000 + mercscore(item)", - ]; + active: function () { + return this.respec() && me.getSkill(sdk.skills.Lightning, sdk.skills.subindex.HardPoints) === 20 && !me.checkSkill(sdk.skills.Blizzard, sdk.skills.subindex.HardPoints); + }, + }; + + // autoequip final gear + let finalGear = [ + // Weapon - HotO + "[type] == mace && [flag] == runeword # [fcr] == 40 # [tier] == 100000", + // Helmet - Harlequin's Crest + "[name] == shako && [quality] == unique && [flag] != ethereal # [damageresist] >= 10 # [tier] == tierscore(item, 100000)", + // Belt - Arach's + "[name] == spiderwebsash && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 90 # [tier] == tierscore(item, 100000)", + // Boots + "[name] == battleboots && [quality] == unique && [flag] != ethereal # [itemmagicbonus] >= 50 # [tier] == tierscore(item, 5000)", // War Traveler + "[name] == scarabshellboots && [quality] == unique # [strength]+[vitality] >= 20 # [tier] == tierscore(item, 100000)", // Sandstorm Treks + // Armor - CoH + "[type] == armor && [flag] == runeword && [flag] != ethereal # [fireresist] == 65 && [hpregen] == 7 # [tier] == 100000", + // Shield - Spirit + "[type] == shield # [fcr] >= 25 && [maxmana] >= 89 # [tier] == tierscore(item, 100000)", + // Final Gloves - Perfect 2x Upp'ed Magefist + "[name] == crusadergauntlets && [quality] == unique && [flag] != ethereal # [enhanceddefense] == 30 && [addfireskills] == 1 # [tier] == 110000", + // Gloves - 2x Upp'ed Magefist + "[name] == crusadergauntlets && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 20 && [addfireskills] == 1 # [tier] == tierscore(item, 100000)", + // Gloves - Upp'ed Magefist + "[name] == battlegauntlets && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 20 && [addfireskills] == 1 # [tier] == tierscore(item, 100000)", + // Gloves - Magefist + "[name] == lightgauntlets && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 20 && [addfireskills] == 1 # [tier] == tierscore(item, 100000)", + // Amulet - Maras + "[type] == amulet && [quality] == unique # [strength] == 5 && [coldresist] >= 30 # [tier] == tierscore(item, 100000)", + // Final Rings - SoJ & Perfect Wisp + "[type] == ring && [quality] == unique # [itemmaxmanapercent] == 25 # [tier] == 100000", + "[name] == ring && [quality] == unique # [itemabsorblightpercent] == 20 # [tier] == tierscore(item, 110000)", + // Rings - Wisp + "[name] == ring && [quality] == unique # [itemabsorblightpercent] >= 10 # [tier] == tierscore(item, 100000)", + // Switch - CTA + "[minimumsockets] >= 5 && [flag] == runeword # [plusskillbattleorders] >= 1 # [secondarytier] == 100000", + // Switch Final Shield - Spirit + "[name] == monarch && [flag] == runeword # [fcr] >= 25 && [maxmana] >= 89 # [secondarytier] == 110000", + // Switch Temporary Shield - 1+ all skill + "[type] == shield # [itemallskills] >= 1 # [secondarytier] == tierscore(item, 50000)", + // Merc Armor - Fortitude + "[type] == armor && [flag] == runeword # [enhanceddefense] >= 200 && [enhanceddamage] >= 300 # [merctier] == 100000", + // Merc Final Helmet - Eth Andy's + "[name] == demonhead && [quality] == unique && [flag] == ethereal # [strength] >= 25 && [enhanceddefense] >= 100 # [merctier] == 50000 + mercscore(item)", + // Merc Helmet - Andy's + "[name] == demonhead && [quality] == unique && [flag] != ethereal # [strength] >= 25 && [enhanceddefense] >= 100 # [merctier] == 40000 + mercscore(item)", + ]; - NTIP.buildList(finalGear); - NTIP.buildFinalGear(finalGear); + NTIP.buildList(finalGear); + NTIP.buildFinalGear(finalGear); - return build; - })(); + return build; + })(); })(module); diff --git a/libs/SoloPlay/BuildFiles/sorceress/sorceress.MeteorbBuild.js b/libs/SoloPlay/BuildFiles/sorceress/sorceress.MeteorbBuild.js index 5c2872fa..a260983e 100644 --- a/libs/SoloPlay/BuildFiles/sorceress/sorceress.MeteorbBuild.js +++ b/libs/SoloPlay/BuildFiles/sorceress/sorceress.MeteorbBuild.js @@ -7,185 +7,185 @@ (function (module) { - module.exports = (function () { - const build = { - caster: true, - skillstab: sdk.skills.tabs.Fire, - wantedskills: [sdk.skills.FrozenOrb, sdk.skills.Meteor, sdk.skills.ColdMastery], - usefulskills: [sdk.skills.FireBall, sdk.skills.FireMastery, sdk.skills.StaticField], - precastSkills: [sdk.skills.FrozenArmor], - usefulStats: [sdk.stats.PassiveColdPierce, sdk.stats.PassiveColdMastery, sdk.stats.PassiveFireMastery, sdk.stats.PassiveFirePierce], - wantedMerc: MercData[sdk.skills.HolyFreeze], - skills: [ - [sdk.skills.Warmth, 1], - [sdk.skills.FrozenArmor, 1], - [sdk.skills.StaticField, 1], - [sdk.skills.Teleport, 1], - [sdk.skills.FireMastery, 1], - [sdk.skills.ColdMastery, 1], - [sdk.skills.FireBall, 14], - [sdk.skills.Meteor, 20], - [sdk.skills.FrozenOrb, 20], // 71 points w/o quest skill pts - [sdk.skills.ColdMastery, 12], - [sdk.skills.FireBall, 20], - [sdk.skills.FireMastery, 20], - [sdk.skills.FireBolt, 20], - ], - stats: [], + module.exports = (function () { + const build = { + caster: true, + skillstab: sdk.skills.tabs.Fire, + wantedskills: [sdk.skills.FrozenOrb, sdk.skills.Meteor, sdk.skills.ColdMastery], + usefulskills: [sdk.skills.FireBall, sdk.skills.FireMastery, sdk.skills.StaticField], + precastSkills: [sdk.skills.FrozenArmor], + usefulStats: [sdk.stats.PassiveColdPierce, sdk.stats.PassiveColdMastery, sdk.stats.PassiveFireMastery, sdk.stats.PassiveFirePierce], + wantedMerc: MercData[sdk.skills.HolyFreeze], + skills: [ + [sdk.skills.Warmth, 1], + [sdk.skills.FrozenArmor, 1], + [sdk.skills.StaticField, 1], + [sdk.skills.Teleport, 1], + [sdk.skills.FireMastery, 1], + [sdk.skills.ColdMastery, 1], + [sdk.skills.FireBall, 14], + [sdk.skills.Meteor, 20], + [sdk.skills.FrozenOrb, 20], // 71 points w/o quest skill pts + [sdk.skills.ColdMastery, 12], + [sdk.skills.FireBall, 20], + [sdk.skills.FireMastery, 20], + [sdk.skills.FireBolt, 20], + ], + stats: [], - charms: { - ResLife: { - max: 3, - have: [], - classid: sdk.items.SmallCharm, - stats: function (check) { - return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MaxHp) === 20); - } - }, + charms: { + ResLife: { + max: 3, + have: [], + classid: sdk.items.SmallCharm, + stats: function (check) { + return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MaxHp) === 20); + } + }, - ResMf: { - max: 2, - have: [], - classid: sdk.items.SmallCharm, - stats: function (check) { - return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MagicBonus) === 7); - } - }, + ResMf: { + max: 2, + have: [], + classid: sdk.items.SmallCharm, + stats: function (check) { + return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MagicBonus) === 7); + } + }, - ResFHR: { - max: 3, - have: [], - classid: sdk.items.SmallCharm, - stats: function (check) { - return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.FHR) === 5); - } - }, + ResFHR: { + max: 3, + have: [], + classid: sdk.items.SmallCharm, + stats: function (check) { + return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.FHR) === 5); + } + }, - SkillerFire: { - max: 1, - have: [], - classid: sdk.items.GrandCharm, - stats: function (check) { - return (!check.unique && check.classid === this.classid && check.getStat(sdk.stats.AddSkillTab, sdk.skills.tabs.Fire) === 1 - && check.getStat(sdk.stats.MaxHp) >= 40); - } - }, + SkillerFire: { + max: 1, + have: [], + classid: sdk.items.GrandCharm, + stats: function (check) { + return (!check.unique && check.classid === this.classid && check.getStat(sdk.stats.AddSkillTab, sdk.skills.tabs.Fire) === 1 + && check.getStat(sdk.stats.MaxHp) >= 40); + } + }, - SkillerCold: { - max: 1, - have: [], - classid: sdk.items.GrandCharm, - stats: function (check) { - return (!check.unique && check.classid === this.classid && check.getStat(sdk.stats.AddSkillTab, sdk.skills.tabs.Cold) === 1 - && check.getStat(sdk.stats.MaxHp) >= 40); - } - }, - }, - - AutoBuildTemplate: { - 1: { - Update: function () { - Config.AttackSkill = [-1, sdk.skills.Meteor, sdk.skills.FireBall, sdk.skills.Meteor, sdk.skills.FireBall, sdk.skills.FrozenOrb, sdk.skills.GlacialSpike]; - Config.LowManaSkill = [-1, -1]; - Config.SkipImmune = ["fire and cold"]; - Config.HPBuffer = me.expansion ? 1 : 5; - Config.MPBuffer = me.expansion ? 1 : 5; - } - }, - }, + SkillerCold: { + max: 1, + have: [], + classid: sdk.items.GrandCharm, + stats: function (check) { + return (!check.unique && check.classid === this.classid && check.getStat(sdk.stats.AddSkillTab, sdk.skills.tabs.Cold) === 1 + && check.getStat(sdk.stats.MaxHp) >= 40); + } + }, + }, + + AutoBuildTemplate: { + 1: { + Update: function () { + Config.AttackSkill = [-1, sdk.skills.Meteor, sdk.skills.FireBall, sdk.skills.Meteor, sdk.skills.FireBall, sdk.skills.FrozenOrb, sdk.skills.GlacialSpike]; + Config.LowManaSkill = [-1, -1]; + Config.SkipImmune = ["fire and cold"]; + Config.HPBuffer = me.expansion ? 1 : 5; + Config.MPBuffer = me.expansion ? 1 : 5; + } + }, + }, - respec: function () { - if (me.classic) { - return me.charlvl >= 75 && me.diablo; - } else { - return me.haveAll([ - { name: sdk.locale.items.TalRashasBelt, quality: sdk.items.quality.Set }, - { name: sdk.locale.items.TalRashasAmulet, quality: sdk.items.quality.Set }, - { name: sdk.locale.items.TalRashasArmor, quality: sdk.items.quality.Set }, - { name: sdk.locale.items.TalRashasOrb, quality: sdk.items.quality.Set }, - { name: sdk.locale.items.TalRashasHelmet, quality: sdk.items.quality.Set }, - ]); - } - }, + respec: function () { + if (me.classic) { + return me.charlvl >= 75 && me.diablo; + } else { + return me.haveAll([ + { name: sdk.locale.items.TalRashasBelt, quality: sdk.items.quality.Set }, + { name: sdk.locale.items.TalRashasAmulet, quality: sdk.items.quality.Set }, + { name: sdk.locale.items.TalRashasArmor, quality: sdk.items.quality.Set }, + { name: sdk.locale.items.TalRashasOrb, quality: sdk.items.quality.Set }, + { name: sdk.locale.items.TalRashasHelmet, quality: sdk.items.quality.Set }, + ]); + } + }, - active: function () { - return this.respec() && me.getSkill(sdk.skills.Meteor, sdk.skills.subindex.HardPoints) === 20 && me.getSkill(sdk.skills.FrozenOrb, sdk.skills.subindex.HardPoints) === 20; - }, - }; + active: function () { + return this.respec() && me.getSkill(sdk.skills.Meteor, sdk.skills.subindex.HardPoints) === 20 && me.getSkill(sdk.skills.FrozenOrb, sdk.skills.subindex.HardPoints) === 20; + }, + }; - build.stats = me.classic - ? [ - ["dexterity", 51], ["strength", 80], ["energy", 100], ["vitality", "all"] - ] - : [ - ["strength", 48], ["vitality", 165], ["strength", 61], - ["vitality", 252], ["strength", 127], ["dexterity", "block"], ["vitality", "all"] - ]; + build.stats = me.classic + ? [ + ["dexterity", 51], ["strength", 80], ["energy", 100], ["vitality", "all"] + ] + : [ + ["strength", 48], ["vitality", 165], ["strength", 61], + ["vitality", 252], ["strength", 127], ["dexterity", "block"], ["vitality", "all"] + ]; - let finalGear = me.classic - ? [ - // Weapon - Spectral Shard - "[name] == blade && [quality] == unique # [fcr] == 20 && [allres] == 10 # [tier] == 100000", - // Helm - Tarnhelm - "[name] == skullcap && [quality] == unique # [itemallskills] == 1 && [itemmagicbonus] >= 25 # [tier] == tierscore(item, 100000)", - // Shield - "[type] == shield && [quality] >= magic # [sorceressskills] == 2 && [allres] >= 16 # [tier] == tierscore(item, 100000)", - // Rings - SoJ - "[type] == ring && [quality] == unique # [itemmaxmanapercent] == 25 # [tier] == 100000", - // Amulet - "[type] == amulet && [quality] >= magic # [sorceressskills] == 2 && [fcr] == 10 # [tier] == tierscore(item, 100000)", - // Boots - "[type] == boots && [quality] >= magic # [frw] >= 20 && [fhr] == 10 && [coldresist]+[lightresist] >= 10 # [tier] == tierscore(item, 100000)", - // Belt - "[type] == belt && [quality] >= magic # [fhr] >= 20 && [maxhp] >= 40 && [fireresist]+[lightresist] >= 20 # [tier] == tierscore(item, 100000)", - // Gloves - Magefist - "[name] == lightgauntlets && [quality] == unique # [fcr] >= 20 && [addfireskills] == 1 # [tier] == tierscore(item, 100000)", - ] - : [ - // Weapon - Tals Orb - "[name] == swirlingcrystal && [quality] == set && [flag] != ethereal # [skilllightningmastery]+[skillfiremastery]+[skillcoldmastery] >= 3 # [tier] == tierscore(item, 100000)", - // Helmet - Tals Mask - "[name] == deathmask && [quality] == set && [flag] != ethereal # [coldresist]+[lightresist]+[fireresist]+[poisonresist] >= 60 # [tier] == tierscore(item, 100000)", - // Belt - Tals Belt - "[name] == meshbelt && [quality] == set && [flag] != ethereal # [itemmagicbonus] >= 10 # [tier] == tierscore(item, 100000)", - // Final Boots - Sandstorm Treks - "[name] == scarabshellboots && [quality] == unique # [strength]+[vitality] >= 20 # [tier] == tierscore(item, 100000)", - // Boots - War Traveler - "[name] == battleboots && [quality] == unique && [flag] != ethereal # [itemmagicbonus] >= 50 # [tier] == tierscore(item, 5000)", - // Armor - Tals Armor - "[name] == lacqueredplate && [quality] == set # [coldresist] >= 1 # [tier] == 100000", - // Final Shield - Sanctuary - "[name] == hyperion && [flag] == runeword # [fhr] >= 20 && [enhanceddefense] >= 130 && [fireresist] >= 50 # [tier] == 100000", - // Shield - Mosers - "[name] == roundshield && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 180 # [tier] == tierscore(item, 50000)", - // Final Gloves - Perfect Upp'ed Magefist - "[name] == battlegauntlets && [quality] == unique && [flag] != ethereal # [enhanceddefense] == 30 && [addfireskills] == 1 && [defense] == 71 # [tier] == 110000", - // Gloves - Upp'ed Magefist - "[name] == battlegauntlets && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 20 && [addfireskills] == 1 # [tier] == tierscore(item, 100000)", - // Gloves - Magefist - "[name] == lightgauntlets && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 20 && [addfireskills] == 1 # [tier] == tierscore(item, 100000)", - // Amulet - Tals Ammy - "[name] == amulet && [quality] == set # [lightresist] == 33 # [tier] == 100000", - // Final Rings - Perfect Raven Frost & Nagelring - "[type] == ring && [quality] == unique # [dexterity] >= 20 # [tier] == 100000", - "[type] == ring && [quality] == unique # [itemmagicbonus] >= 30 # [tier] == 100000", - // Rings - Raven Frost - "[type] == ring && [quality] == unique # [dexterity] >= 15 # [tier] == 100000", - // Switch - CTA - "[minimumsockets] >= 5 && [flag] == runeword # [plusskillbattleorders] >= 1 # [secondarytier] == 100000", - // Switch Shield - 1+ all skill - "[type] == shield # [itemallskills] >= 1 # [secondarytier] == tierscore(item, 100000)", - // Merc Final Armor - Fortitude - "[type] == armor && [flag] == runeword # [enhanceddefense] >= 200 && [enhanceddamage] >= 300 # [merctier] == 100000", - // Merc Final Helmet - Eth Andy's - "[name] == demonhead && [quality] == unique && [flag] == ethereal # [strength] >= 25 && [enhanceddefense] >= 100 # [merctier] == 50000 + mercscore(item)", - // Merc Helmet - Andy's - "[name] == demonhead && [quality] == unique && [flag] != ethereal # [strength] >= 25 && [enhanceddefense] >= 100 # [merctier] == 40000 + mercscore(item)", - ]; + let finalGear = me.classic + ? [ + // Weapon - Spectral Shard + "[name] == blade && [quality] == unique # [fcr] == 20 && [allres] == 10 # [tier] == 100000", + // Helm - Tarnhelm + "[name] == skullcap && [quality] == unique # [itemallskills] == 1 && [itemmagicbonus] >= 25 # [tier] == tierscore(item, 100000)", + // Shield + "[type] == shield && [quality] >= magic # [sorceressskills] == 2 && [allres] >= 16 # [tier] == tierscore(item, 100000)", + // Rings - SoJ + "[type] == ring && [quality] == unique # [itemmaxmanapercent] == 25 # [tier] == 100000", + // Amulet + "[type] == amulet && [quality] >= magic # [sorceressskills] == 2 && [fcr] == 10 # [tier] == tierscore(item, 100000)", + // Boots + "[type] == boots && [quality] >= magic # [frw] >= 20 && [fhr] == 10 && [coldresist]+[lightresist] >= 10 # [tier] == tierscore(item, 100000)", + // Belt + "[type] == belt && [quality] >= magic # [fhr] >= 20 && [maxhp] >= 40 && [fireresist]+[lightresist] >= 20 # [tier] == tierscore(item, 100000)", + // Gloves - Magefist + "[name] == lightgauntlets && [quality] == unique # [fcr] >= 20 && [addfireskills] == 1 # [tier] == tierscore(item, 100000)", + ] + : [ + // Weapon - Tals Orb + "[name] == swirlingcrystal && [quality] == set && [flag] != ethereal # [skilllightningmastery]+[skillfiremastery]+[skillcoldmastery] >= 3 # [tier] == tierscore(item, 100000)", + // Helmet - Tals Mask + "[name] == deathmask && [quality] == set && [flag] != ethereal # [coldresist]+[lightresist]+[fireresist]+[poisonresist] >= 60 # [tier] == tierscore(item, 100000)", + // Belt - Tals Belt + "[name] == meshbelt && [quality] == set && [flag] != ethereal # [itemmagicbonus] >= 10 # [tier] == tierscore(item, 100000)", + // Final Boots - Sandstorm Treks + "[name] == scarabshellboots && [quality] == unique # [strength]+[vitality] >= 20 # [tier] == tierscore(item, 100000)", + // Boots - War Traveler + "[name] == battleboots && [quality] == unique && [flag] != ethereal # [itemmagicbonus] >= 50 # [tier] == tierscore(item, 5000)", + // Armor - Tals Armor + "[name] == lacqueredplate && [quality] == set # [coldresist] >= 1 # [tier] == 100000", + // Final Shield - Sanctuary + "[name] == hyperion && [flag] == runeword # [fhr] >= 20 && [enhanceddefense] >= 130 && [fireresist] >= 50 # [tier] == 100000", + // Shield - Mosers + "[name] == roundshield && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 180 # [tier] == tierscore(item, 50000)", + // Final Gloves - Perfect Upp'ed Magefist + "[name] == battlegauntlets && [quality] == unique && [flag] != ethereal # [enhanceddefense] == 30 && [addfireskills] == 1 && [defense] == 71 # [tier] == 110000", + // Gloves - Upp'ed Magefist + "[name] == battlegauntlets && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 20 && [addfireskills] == 1 # [tier] == tierscore(item, 100000)", + // Gloves - Magefist + "[name] == lightgauntlets && [quality] == unique && [flag] != ethereal # [enhanceddefense] >= 20 && [addfireskills] == 1 # [tier] == tierscore(item, 100000)", + // Amulet - Tals Ammy + "[name] == amulet && [quality] == set # [lightresist] == 33 # [tier] == 100000", + // Final Rings - Perfect Raven Frost & Nagelring + "[type] == ring && [quality] == unique # [dexterity] >= 20 # [tier] == 100000", + "[type] == ring && [quality] == unique # [itemmagicbonus] >= 30 # [tier] == 100000", + // Rings - Raven Frost + "[type] == ring && [quality] == unique # [dexterity] >= 15 # [tier] == 100000", + // Switch - CTA + "[minimumsockets] >= 5 && [flag] == runeword # [plusskillbattleorders] >= 1 # [secondarytier] == 100000", + // Switch Shield - 1+ all skill + "[type] == shield # [itemallskills] >= 1 # [secondarytier] == tierscore(item, 100000)", + // Merc Final Armor - Fortitude + "[type] == armor && [flag] == runeword # [enhanceddefense] >= 200 && [enhanceddamage] >= 300 # [merctier] == 100000", + // Merc Final Helmet - Eth Andy's + "[name] == demonhead && [quality] == unique && [flag] == ethereal # [strength] >= 25 && [enhanceddefense] >= 100 # [merctier] == 50000 + mercscore(item)", + // Merc Helmet - Andy's + "[name] == demonhead && [quality] == unique && [flag] != ethereal # [strength] >= 25 && [enhanceddefense] >= 100 # [merctier] == 40000 + mercscore(item)", + ]; - NTIP.buildList(finalGear); - NTIP.buildFinalGear(finalGear); + NTIP.buildList(finalGear); + NTIP.buildFinalGear(finalGear); - return build; - })(); + return build; + })(); })(module); diff --git a/libs/SoloPlay/BuildFiles/sorceress/sorceress.StartBuild.js b/libs/SoloPlay/BuildFiles/sorceress/sorceress.StartBuild.js index 02621969..ecc27fa0 100644 --- a/libs/SoloPlay/BuildFiles/sorceress/sorceress.StartBuild.js +++ b/libs/SoloPlay/BuildFiles/sorceress/sorceress.StartBuild.js @@ -7,91 +7,91 @@ (function (module, require) { - module.exports = (function () { - const build = { - AutoBuildTemplate: {}, - caster: true, - skillstab: sdk.skills.tabs.Lightning, - wantedskills: [sdk.skills.ChargedBolt, sdk.skills.StaticField], - usefulskills: [sdk.skills.FrozenArmor, sdk.skills.Lightning], - wantedMerc: MercData[sdk.skills.HolyFreeze], - stats: [ - ["energy", 40], ["vitality", 15], ["energy", 45], - ["vitality", 20], ["energy", 50], ["strength", 15], - ["vitality", 25], ["energy", 60], ["vitality", 40], - ["strength", 35], ["vitality", "all"] - ], - skills: [ - [sdk.skills.ChargedBolt, 3, false], // charlvl 4 - [sdk.skills.IceBolt, 1], // charlvl 5 - [sdk.skills.FrozenArmor, 1], // charlvl 6 - [sdk.skills.Telekinesis, 1], // charlvl 7 - [sdk.skills.FrostNova, 1], // charlvl 8 - [sdk.skills.StaticField, 1, false], - [sdk.skills.IceBlast, 1, false], - [sdk.skills.StaticField, 4], // charlvl 10 - [sdk.skills.Teleport, 1, false], // charlvl 18 - [sdk.skills.Nova, 7], // charlvl 17 - [sdk.skills.StaticField, 6], // charlvl 20 - [sdk.skills.IceBlast, 1], // charlvl 22 - [sdk.skills.GlacialSpike, 1], // charlvl - [sdk.skills.IceBlast, 4, false], // charlvl 23 - [sdk.skills.Blizzard, 6, false], // charlvl 29 (never gets here) - [sdk.skills.ColdMastery, 1, false], // charlvl 30 (never gets here) - ], + module.exports = (function () { + const build = { + AutoBuildTemplate: {}, + caster: true, + skillstab: sdk.skills.tabs.Lightning, + wantedskills: [sdk.skills.ChargedBolt, sdk.skills.StaticField], + usefulskills: [sdk.skills.FrozenArmor, sdk.skills.Lightning], + wantedMerc: MercData[sdk.skills.HolyFreeze], + stats: [ + ["energy", 40], ["vitality", 15], ["energy", 45], + ["vitality", 20], ["energy", 50], ["strength", 15], + ["vitality", 25], ["energy", 60], ["vitality", 40], + ["strength", 35], ["vitality", "all"] + ], + skills: [ + [sdk.skills.ChargedBolt, 3, false], // charlvl 4 + [sdk.skills.IceBolt, 1], // charlvl 5 + [sdk.skills.FrozenArmor, 1], // charlvl 6 + [sdk.skills.Telekinesis, 1], // charlvl 7 + [sdk.skills.FrostNova, 1], // charlvl 8 + [sdk.skills.StaticField, 1, false], + [sdk.skills.IceBlast, 1, false], + [sdk.skills.StaticField, 4], // charlvl 10 + [sdk.skills.Teleport, 1, false], // charlvl 18 + [sdk.skills.Nova, 7], // charlvl 17 + [sdk.skills.StaticField, 6], // charlvl 20 + [sdk.skills.IceBlast, 1], // charlvl 22 + [sdk.skills.GlacialSpike, 1], // charlvl + [sdk.skills.IceBlast, 4, false], // charlvl 23 + [sdk.skills.Blizzard, 6, false], // charlvl 29 (never gets here) + [sdk.skills.ColdMastery, 1, false], // charlvl 30 (never gets here) + ], - active: function () { - return me.charlvl < CharInfo.respecOne && !me.checkSkill(sdk.skills.ColdMastery, sdk.skills.subindex.HardPoints); - }, - }; + active: function () { + return me.charlvl < CharInfo.respecOne && !me.checkSkill(sdk.skills.ColdMastery, sdk.skills.subindex.HardPoints); + }, + }; - const { buildAutoBuildTempObj } = require("../../Utils/General"); - - build.AutoBuildTemplate[1] = buildAutoBuildTempObj(() => { - Config.ScanShrines.indexOf(sdk.shrines.Combat) === -1 && Config.ScanShrines.push(sdk.shrines.Combat); - Config.FieldID.Enabled = !Misc.checkQuest(sdk.quest.id.TheSearchForCain, sdk.quest.states.Completed); - Config.FieldID.UsedSpace = 0; + const { buildAutoBuildTempObj } = require("../../Utils/General"); + + build.AutoBuildTemplate[1] = buildAutoBuildTempObj(() => { + Config.ScanShrines.indexOf(sdk.shrines.Combat) === -1 && Config.ScanShrines.push(sdk.shrines.Combat); + Config.FieldID.Enabled = !Misc.checkQuest(sdk.quest.id.TheSearchForCain, sdk.quest.states.Completed); + Config.FieldID.UsedSpace = 0; - Config.BeltColumn = ["hp", "hp", "hp", "hp"]; - Config.HPBuffer = 4; - Config.MPBuffer = 10; - Config.AttackSkill = [-1, sdk.skills.FireBolt, -1, sdk.skills.FireBolt, -1, 0, 0]; - Config.LowManaSkill = [0, 0]; - SetUp.belt(); - }); - build.AutoBuildTemplate[2] = buildAutoBuildTempObj(() => { - Config.ScanShrines.indexOf(sdk.shrines.Combat) === -1 && Config.ScanShrines.push(sdk.shrines.Combat); - Config.FieldID.Enabled = !Misc.checkQuest(sdk.quest.id.TheSearchForCain, sdk.quest.states.Completed); - Config.FieldID.UsedSpace = 0; + Config.BeltColumn = ["hp", "hp", "hp", "hp"]; + Config.HPBuffer = 4; + Config.MPBuffer = 10; + Config.AttackSkill = [-1, sdk.skills.FireBolt, -1, sdk.skills.FireBolt, -1, 0, 0]; + Config.LowManaSkill = [0, 0]; + SetUp.belt(); + }); + build.AutoBuildTemplate[2] = buildAutoBuildTempObj(() => { + Config.ScanShrines.indexOf(sdk.shrines.Combat) === -1 && Config.ScanShrines.push(sdk.shrines.Combat); + Config.FieldID.Enabled = !Misc.checkQuest(sdk.quest.id.TheSearchForCain, sdk.quest.states.Completed); + Config.FieldID.UsedSpace = 0; - Config.TownHP = me.hardcore ? 0 : 35; - Config.LowManaSkill = [0, 0]; - Config.BeltColumn = ["hp", "hp", "mp", "mp"]; - Config.HPBuffer = 4; - Config.MPBuffer = 10; - if (me.checkSkill(sdk.skills.IceBlast, sdk.skills.subindex.SoftPoints)) { - Config.AttackSkill = [-1, sdk.skills.ChargedBolt, sdk.skills.IceBlast, sdk.skills.ChargedBolt, sdk.skills.IceBlast, sdk.skills.IceBlast, 0]; - } else if (me.checkSkill(sdk.skills.IceBolt, sdk.skills.subindex.SoftPoints)) { - Config.AttackSkill = [-1, sdk.skills.ChargedBolt, sdk.skills.IceBolt, sdk.skills.ChargedBolt, sdk.skills.IceBolt, sdk.skills.IceBolt, 0]; - } else { - let secondSkill = Skill.canUse(sdk.skills.FireBolt) ? sdk.skills.FireBolt : sdk.skills.Attack; - Config.AttackSkill = [-1, sdk.skills.ChargedBolt, secondSkill, sdk.skills.ChargedBolt, secondSkill, 0, 0]; - } - SetUp.belt(); - }); - build.AutoBuildTemplate[12] = buildAutoBuildTempObj(() => { - Config.HPBuffer = 4; - Config.MPBuffer = 8; - Config.AttackSkill = [-1, sdk.skills.Nova, sdk.skills.ChargedBolt, sdk.skills.Nova, sdk.skills.ChargedBolt, sdk.skills.FrostNova, sdk.skills.IceBlast]; - Config.DodgeHP = 50; - Config.DodgeRange = me.checkSkill(sdk.skills.Blizzard, sdk.skills.subindex.SoftPoints) ? 15 : 7; - }); - build.AutoBuildTemplate[24] = buildAutoBuildTempObj(() => { - if (me.checkSkill(sdk.skills.Blizzard, sdk.skills.subindex.HardPoints)) { - Config.AttackSkill = [-1, sdk.skills.Blizzard, sdk.skills.Nova, sdk.skills.Blizzard, sdk.skills.Nova, sdk.skills.Nova, sdk.skills.ChargedBolt]; - } - }); + Config.TownHP = me.hardcore ? 0 : 35; + Config.LowManaSkill = [0, 0]; + Config.BeltColumn = ["hp", "hp", "mp", "mp"]; + Config.HPBuffer = 4; + Config.MPBuffer = 10; + if (me.checkSkill(sdk.skills.IceBlast, sdk.skills.subindex.SoftPoints)) { + Config.AttackSkill = [-1, sdk.skills.ChargedBolt, sdk.skills.IceBlast, sdk.skills.ChargedBolt, sdk.skills.IceBlast, sdk.skills.IceBlast, 0]; + } else if (me.checkSkill(sdk.skills.IceBolt, sdk.skills.subindex.SoftPoints)) { + Config.AttackSkill = [-1, sdk.skills.ChargedBolt, sdk.skills.IceBolt, sdk.skills.ChargedBolt, sdk.skills.IceBolt, sdk.skills.IceBolt, 0]; + } else { + let secondSkill = Skill.canUse(sdk.skills.FireBolt) ? sdk.skills.FireBolt : sdk.skills.Attack; + Config.AttackSkill = [-1, sdk.skills.ChargedBolt, secondSkill, sdk.skills.ChargedBolt, secondSkill, 0, 0]; + } + SetUp.belt(); + }); + build.AutoBuildTemplate[12] = buildAutoBuildTempObj(() => { + Config.HPBuffer = 4; + Config.MPBuffer = 8; + Config.AttackSkill = [-1, sdk.skills.Nova, sdk.skills.ChargedBolt, sdk.skills.Nova, sdk.skills.ChargedBolt, sdk.skills.FrostNova, sdk.skills.IceBlast]; + Config.DodgeHP = 50; + Config.DodgeRange = me.checkSkill(sdk.skills.Blizzard, sdk.skills.subindex.SoftPoints) ? 15 : 7; + }); + build.AutoBuildTemplate[24] = buildAutoBuildTempObj(() => { + if (me.checkSkill(sdk.skills.Blizzard, sdk.skills.subindex.HardPoints)) { + Config.AttackSkill = [-1, sdk.skills.Blizzard, sdk.skills.Nova, sdk.skills.Blizzard, sdk.skills.Nova, sdk.skills.Nova, sdk.skills.ChargedBolt]; + } + }); - return build; - })(); + return build; + })(); })(module, require); diff --git a/libs/SoloPlay/BuildFiles/sorceress/sorceress.SteppingBuild.js b/libs/SoloPlay/BuildFiles/sorceress/sorceress.SteppingBuild.js index 76e363ab..bd1148da 100644 --- a/libs/SoloPlay/BuildFiles/sorceress/sorceress.SteppingBuild.js +++ b/libs/SoloPlay/BuildFiles/sorceress/sorceress.SteppingBuild.js @@ -7,78 +7,78 @@ (function (module) { - module.exports = (function () { - const build = { - caster: true, - skillstab: sdk.skills.tabs.Cold, - wantedskills: [sdk.skills.Blizzard, sdk.skills.GlacialSpike, sdk.skills.ColdMastery], - usefulskills: [sdk.skills.IceBlast, sdk.skills.Warmth, sdk.skills.StaticField], - usefulStats: [sdk.stats.PassiveColdPierce, sdk.stats.PassiveColdMastery], - wantedMerc: MercData[sdk.skills.HolyFreeze], - stats: [], - skills: [], + module.exports = (function () { + const build = { + caster: true, + skillstab: sdk.skills.tabs.Cold, + wantedskills: [sdk.skills.Blizzard, sdk.skills.GlacialSpike, sdk.skills.ColdMastery], + usefulskills: [sdk.skills.IceBlast, sdk.skills.Warmth, sdk.skills.StaticField], + usefulStats: [sdk.stats.PassiveColdPierce, sdk.stats.PassiveColdMastery], + wantedMerc: MercData[sdk.skills.HolyFreeze], + stats: [], + skills: [], - active: function () { - return me.charlvl > CharInfo.respecOne && me.charlvl < CharInfo.respecTwo && me.checkSkill(sdk.skills.Blizzard, sdk.skills.subindex.HardPoints) && !me.checkSkill(sdk.skills.Nova, sdk.skills.subindex.HardPoints) && !me.checkSkill(sdk.skills.FireMastery, sdk.skills.subindex.HardPoints); - }, + active: function () { + return me.charlvl > CharInfo.respecOne && me.charlvl < CharInfo.respecTwo && me.checkSkill(sdk.skills.Blizzard, sdk.skills.subindex.HardPoints) && !me.checkSkill(sdk.skills.Nova, sdk.skills.subindex.HardPoints) && !me.checkSkill(sdk.skills.FireMastery, sdk.skills.subindex.HardPoints); + }, - AutoBuildTemplate: { - 1: { - Update: function () { - Config.AttackSkill = [-1, sdk.skills.Blizzard, sdk.skills.IceBlast, sdk.skills.Blizzard, sdk.skills.IceBlast, -1, -1]; - Config.BeltColumn = ["hp", "hp", "mp", "mp"]; - Config.HPBuffer = me.expansion && !me.normal ? 2 : 5; - Config.MPBuffer = (me.expansion && !me.normal || Item.getMercEquipped(sdk.body.RightArm).prefixnum === sdk.locale.items.Insight) ? 2 : 5; - Config.SkipImmune = ["cold"]; - SetUp.belt(); - } - } - }, - }; - - // Has to be set after its loaded - build.stats = me.classic - ? [ - ["energy", 60], ["vitality", 40], ["strength", 55], - ["energy", 80], ["vitality", 80], ["strength", 80], - ["energy", 100], ["vitality", "all"] - ] : [ - ["energy", 69], ["strength", 48], ["vitality", 165], - ["strength", 61], ["vitality", 200], ["strength", 100], - ["vitality", 252], ["dexterity", "block"], ["vitality", "all"] - ]; - - build.skills = me.classic - ? [ - // Total skills at respec = 27 (assume no izual quest points) - [sdk.skills.Warmth, 1], // points left 26 - [sdk.skills.FrozenArmor, 1], // points left 25 - [sdk.skills.StaticField, 6], // points left 19 - [sdk.skills.Teleport, 4], // points left 14 - [sdk.skills.Blizzard, 3], // points left 7 - [sdk.skills.IceBlast, 8], // points left 0 - [sdk.skills.ColdMastery, 1, false], - [sdk.skills.FrozenOrb, 1, false], - [sdk.skills.Blizzard, 20, false], - [sdk.skills.IceBlast, 20, false], - [sdk.skills.ColdMastery, 5], - [sdk.skills.GlacialSpike, 20, false], - ] : [ - // Total skills at respec = 27 (assume no izual quest points) - [sdk.skills.Warmth, 1], // points left 24 - [sdk.skills.FrozenArmor, 1], // points left 23 - [sdk.skills.StaticField, 1], // points left 22 - [sdk.skills.Teleport, 4], // points left 17 - [sdk.skills.Blizzard, 1], // points left 12 - [sdk.skills.IceBlast, 15], // points left 0 - [sdk.skills.ColdMastery, 1, false], - [sdk.skills.FrozenOrb, 1, false], - [sdk.skills.Blizzard, 20, false], - [sdk.skills.IceBlast, 20, false], - [sdk.skills.ColdMastery, 5], - [sdk.skills.GlacialSpike, 20, false], - ]; - - return build; - })(); + AutoBuildTemplate: { + 1: { + Update: function () { + Config.AttackSkill = [-1, sdk.skills.Blizzard, sdk.skills.IceBlast, sdk.skills.Blizzard, sdk.skills.IceBlast, -1, -1]; + Config.BeltColumn = ["hp", "hp", "mp", "mp"]; + Config.HPBuffer = me.expansion && !me.normal ? 2 : 5; + Config.MPBuffer = (me.expansion && !me.normal || Item.getMercEquipped(sdk.body.RightArm).prefixnum === sdk.locale.items.Insight) ? 2 : 5; + Config.SkipImmune = ["cold"]; + SetUp.belt(); + } + } + }, + }; + + // Has to be set after its loaded + build.stats = me.classic + ? [ + ["energy", 60], ["vitality", 40], ["strength", 55], + ["energy", 80], ["vitality", 80], ["strength", 80], + ["energy", 100], ["vitality", "all"] + ] : [ + ["energy", 69], ["strength", 48], ["vitality", 165], + ["strength", 61], ["vitality", 200], ["strength", 100], + ["vitality", 252], ["dexterity", "block"], ["vitality", "all"] + ]; + + build.skills = me.classic + ? [ + // Total skills at respec = 27 (assume no izual quest points) + [sdk.skills.Warmth, 1], // points left 26 + [sdk.skills.FrozenArmor, 1], // points left 25 + [sdk.skills.StaticField, 6], // points left 19 + [sdk.skills.Teleport, 4], // points left 14 + [sdk.skills.Blizzard, 3], // points left 7 + [sdk.skills.IceBlast, 8], // points left 0 + [sdk.skills.ColdMastery, 1, false], + [sdk.skills.FrozenOrb, 1, false], + [sdk.skills.Blizzard, 20, false], + [sdk.skills.IceBlast, 20, false], + [sdk.skills.ColdMastery, 5], + [sdk.skills.GlacialSpike, 20, false], + ] : [ + // Total skills at respec = 27 (assume no izual quest points) + [sdk.skills.Warmth, 1], // points left 24 + [sdk.skills.FrozenArmor, 1], // points left 23 + [sdk.skills.StaticField, 1], // points left 22 + [sdk.skills.Teleport, 4], // points left 17 + [sdk.skills.Blizzard, 1], // points left 12 + [sdk.skills.IceBlast, 15], // points left 0 + [sdk.skills.ColdMastery, 1, false], + [sdk.skills.FrozenOrb, 1, false], + [sdk.skills.Blizzard, 20, false], + [sdk.skills.IceBlast, 20, false], + [sdk.skills.ColdMastery, 5], + [sdk.skills.GlacialSpike, 20, false], + ]; + + return build; + })(); })(module); diff --git a/libs/SoloPlay/BuildFiles/sorceress/sorceress.js b/libs/SoloPlay/BuildFiles/sorceress/sorceress.js index 00add54e..ccb4ccba 100644 --- a/libs/SoloPlay/BuildFiles/sorceress/sorceress.js +++ b/libs/SoloPlay/BuildFiles/sorceress/sorceress.js @@ -6,40 +6,40 @@ */ const CharInfo = { - respecOne: me.expansion ? 26 : 26, - respecTwo: me.expansion ? 63 : 60, - levelCap: (function() { - const currentDiff = sdk.difficulty.nameOf(me.diff); - const softcoreMode = { - "Normal": me.expansion ? 33 : 33, - "Nightmare": me.expansion ? 64 : 60, - "Hell": 100, - }; - const hardcoreMode = { - "Normal": me.expansion ? 36 : 33, - "Nightmare": me.expansion ? 71 : 67, - "Hell": 100, - }; + respecOne: me.expansion ? 26 : 26, + respecTwo: me.expansion ? 63 : 60, + levelCap: (function() { + const currentDiff = sdk.difficulty.nameOf(me.diff); + const softcoreMode = { + "Normal": me.expansion ? 33 : 33, + "Nightmare": me.expansion ? 64 : 60, + "Hell": 100, + }; + const hardcoreMode = { + "Normal": me.expansion ? 36 : 33, + "Nightmare": me.expansion ? 71 : 67, + "Hell": 100, + }; - return me.softcore ? softcoreMode[currentDiff] : hardcoreMode[currentDiff]; - })(), + return me.softcore ? softcoreMode[currentDiff] : hardcoreMode[currentDiff]; + })(), - getActiveBuild: function () { - const nSkills = me.getStat(sdk.stats.NewSkills); - const currLevel = me.charlvl; - const justRepeced = (nSkills >= currLevel); + getActiveBuild: function () { + const nSkills = me.getStat(sdk.stats.NewSkills); + const currLevel = me.charlvl; + const justRepeced = (nSkills >= currLevel); - switch (true) { - case currLevel < this.respecOne && !me.checkSkill(sdk.skills.ColdMastery, sdk.skills.subindex.HardPoints): - return "Start"; - case currLevel >= this.respecOne && currLevel < this.respecTwo && justRepeced: - case currLevel >= this.respecOne && currLevel < this.respecTwo && me.checkSkill(sdk.skills.Blizzard, sdk.skills.subindex.HardPoints) && !me.checkSkill(sdk.skills.Nova, sdk.skills.subindex.HardPoints) && !me.checkSkill(sdk.skills.FireMastery, sdk.skills.subindex.HardPoints): - return "Stepping"; - case Check.finalBuild().respec() && justRepeced: - case Check.finalBuild().active(): - return SetUp.finalBuild; - default: - return "Leveling"; - } - }, + switch (true) { + case currLevel < this.respecOne && !me.checkSkill(sdk.skills.ColdMastery, sdk.skills.subindex.HardPoints): + return "Start"; + case currLevel >= this.respecOne && currLevel < this.respecTwo && justRepeced: + case currLevel >= this.respecOne && currLevel < this.respecTwo && me.checkSkill(sdk.skills.Blizzard, sdk.skills.subindex.HardPoints) && !me.checkSkill(sdk.skills.Nova, sdk.skills.subindex.HardPoints) && !me.checkSkill(sdk.skills.FireMastery, sdk.skills.subindex.HardPoints): + return "Stepping"; + case Check.finalBuild().respec() && justRepeced: + case Check.finalBuild().active(): + return SetUp.finalBuild; + default: + return "Leveling"; + } + }, }; diff --git a/libs/SoloPlay/Config/Amazon.js b/libs/SoloPlay/Config/Amazon.js index a47743cc..5cb70927 100644 --- a/libs/SoloPlay/Config/Amazon.js +++ b/libs/SoloPlay/Config/Amazon.js @@ -17,242 +17,242 @@ */ (function LoadConfig () { - includeIfNotIncluded("SoloPlay/Functions/MiscOverrides.js"); - includeIfNotIncluded("SoloPlay/Functions/Globals.js"); - - SetUp.include(); - SetUp.config(); - - /* Pickit configuration. */ - Config.PickRange = 40; - // Config.PickitFiles.push("kolton.nip"); - // Config.PickitFiles.push("LLD.nip"); - - /* Gambling configuration. */ - Config.GambleItems.push("Amulet"); - Config.GambleItems.push("Ring"); - Config.GambleItems.push("Circlet"); - Config.GambleItems.push("Coronet"); - - // AutoEquip setup - const levelingTiers = [ - // Weapon - "([type] == javelin || [type] == amazonjavelin) && [quality] >= normal && [flag] != ethereal && [wsm] <= 10 && [2handed] == 0 # [itemchargedskill] >= 0 # [tier] == tierscore(item)", - // "[name] == ceremonialjavelin && [quality] == unique && [flag] == ethereal # [itemchargedskill] >= 0 # [tier] == tierscore(item)", // too many issues with eth titans - ]; - - const expansionTiers = [ - // Switch - "[type] == wand && [quality] >= normal # [itemchargedskill] == 72 # [secondarytier] == 25000", // Weaken charged wand - "[type] == wand && [quality] >= normal # [itemchargedskill] == 91 # [secondarytier] == 50000 + chargeditemscore(item, 91)", // Lower Resist charged wand - // Charms - "[name] == smallcharm && [quality] == magic # # [invoquantity] == 8 && [charmtier] == charmscore(item)", - "[name] == grandcharm && [quality] == magic # # [invoquantity] == 2 && [charmtier] == charmscore(item)", - ]; - - NTIP.buildList(levelingTiers); - me.expansion && NTIP.buildList(expansionTiers); - - if (["Witchyzon", "Wfzon", "Faithbowzon"].indexOf(SetUp.currentBuild) === -1) { - NTIP.addLine("[type] == shield && ([quality] >= magic || [flag] == runeword) && [flag] != ethereal # [itemchargedskill] >= 0 # [tier] == tierscore(item)"); - NTIP.addLine("([type] == shield) && [quality] >= normal && [flag] != ethereal # [itemchargedskill] >= 0 && ([sockets] == 1 || [sockets] == 2) # [tier] == tierscore(item)"); - NTIP.addLine("me.classic && [type] == shield && [quality] >= normal # [itemchargedskill] >= 0 # [tier] == tierscore(item)"); - } - - /* Attack configuration. */ - Config.AttackSkill = [0, 0, 0, 0, 0]; - Config.LowManaSkill = [-1, -1]; - Config.MaxAttackCount = 1000; - Config.BossPriority = false; - Config.ClearType = 0; - Config.ClearPath = { Range: (Pather.canTeleport() ? 30 : 20), Spectype: sdk.monsters.spectype.All }; - - // Class specific config - Config.LightningFuryDelay = 10; // Lightning fury interval in seconds. LF is treated as timed skill. - Config.SummonValkyrie = true; // Summon Valkyrie - - Config.imbueables = [ - { name: sdk.items.MaidenJavelin, condition: () => me.normal && me.expansion }, - { name: sdk.items.CeremonialJavelin, condition: () => !me.normal && (me.charlvl < 48 || me.trueStr < 107 || me.trueDex < 151) && me.expansion }, - { name: sdk.items.MatriarchalJavelin, condition: () => (me.equipped.get(sdk.body.RightArm).tier < 100000 && me.trueStr >= 107 && me.trueDex >= 151 && me.expansion) }, - { name: sdk.items.Belt, condition: () => (me.normal && (me.equipped.get(sdk.body.RightArm).tier > 100000 || me.classic)) }, - { name: sdk.items.MeshBelt, condition: () => (!me.normal && me.charlvl < 46 && me.trueStr > 58 && (me.equipped.get(sdk.body.RightArm).tier > 100000 || me.classic)) }, - { name: sdk.items.SpiderwebSash, condition: () => (!me.normal && me.trueStr > 50 && (me.equipped.get(sdk.body.RightArm).tier > 100000 || me.classic)) }, - ]; - - let imbueArr = SetUp.imbueItems(); - - !me.smith && NTIP.buildList(imbueArr); - - if (me.equipped.get(sdk.body.RightArm).tier < 100000) { - Config.GambleItems.push("Javelin"); - Config.GambleItems.push("Pilum"); - Config.GambleItems.push("Short Spear"); - Config.GambleItems.push("Throwing Spear"); - } - - switch (me.gametype) { - case sdk.game.gametype.Classic: - // Res shield - if (me.equipped.get(sdk.body.LeftArm).tier < 487) { - includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/PDiamondShield.js"); - } - - break; - case sdk.game.gametype.Expansion: - NTIP.addLine("[name] >= Vexrune && [name] <= Zodrune"); - const { basicSocketables, addSocketableObj } = require("../Utils/General"); - - Config.socketables = Config.socketables.concat(basicSocketables.all); - Config.socketables.push(addSocketableObj(sdk.items.Bill, [], [], - me.normal, (item) => item.ilvl >= 26 && item.isBaseType - )); - Config.socketables.push(addSocketableObj(sdk.items.Shako, [sdk.items.runes.Um], [sdk.items.gems.Perfect.Ruby], - true, (item) => item.unique && !item.ethereal - )); - - Check.itemSockables(sdk.items.RoundShield, "unique", "Moser's Blessed Circle"); - Check.itemSockables(sdk.items.Shako, "unique", "Harlequin Crest"); - - /* Crafting */ - // Going with Blood but TODO is test HitPower vs Blood - if (me.equipped.get(sdk.body.Neck).tier < 100000) { - Config.Recipes.push([Recipe.Blood.Amulet]); - } - - if (me.equipped.get(sdk.body.RingLeft).tier < 100000) { - Config.Recipes.push([Recipe.Blood.Ring]); - } - - // FinalBuild specific setup - switch (SetUp.finalBuild) { - case "Witchyzon": - case "Faithbowzon": - case "Wfzon": - (["Witchyzon", "Wfzon", "Faithbowzon"].includes(SetUp.currentBuild)) && NTIP.addLine("[type] == bowquiver # # [maxquantity] == 1"); - - if (SetUp.finalBuild === "Wfzon") { - if (!Check.haveItem(sdk.items.HydraBow, "unique", "Windforce")) { - NTIP.addLine("[name] == hydrabow && [quality] == unique # [manaleech] >= 6 # [maxquantity] == 1"); - } - - Config.socketables.push(addSocketableObj(sdk.items.HydraBow, [sdk.items.runes.Shael], [sdk.items.runes.Amn], - true, (item) => item.unique - )); - } - - if ((SetUp.finalBuild === "Faithbowzon") && !me.checkItem({ name: sdk.locale.items.Faith, classid: sdk.items.GrandMatronBow }).have) { - includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/Faith.js"); - } - - if (!Check.haveItem(sdk.items.DiamondBow, "unique", "Witchwild String") - && (SetUp.finalBuild === "Witchyzon" || ["Wfzon", "Faithbowzon"].indexOf(SetUp.currentBuild) === -1)) { - NTIP.addLine("[name] == shortsiegebow && [quality] == unique # [fireresist] == 40 # [maxquantity] == 1"); - // Witchyzon only - keep the bow but don't upgrade it until we have our wanted belt - if ((SetUp.finalBuild === "Witchyzon" && Check.haveItem("vampirefangbelt", "unique", "Nosferatu's Coil")) || ["Wfzon", "Faithbowzon"].includes(SetUp.finalBuild)) { - Config.Recipes.push([Recipe.Unique.Weapon.ToElite, "Short Siege Bow", Roll.NonEth]); - } - } - - // Spirit shield - while lvling and Wf final switch - if ((me.ladder || Developer.addLadderRW) && (me.equipped.get(5).tier < 1000 - && (["Witchyzon", "Wfzon", "Faithbowzon"].indexOf(SetUp.currentBuild) === -1) - || (SetUp.finalBuild === "Wfzon" && me.equipped.get(12).prefixnum !== sdk.locale.items.Spirit))) { - includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/SpiritShield.js"); - } - - Config.socketables.push(addSocketableObj(sdk.items.DiamondBow, [sdk.items.runes.Nef, sdk.items.runes.Shael], [sdk.items.gems.Perfect.Skull], - false, (item) => item.unique - )); - Config.socketables.push(addSocketableObj(sdk.items.BoneVisage, [sdk.items.runes.Um], [sdk.items.gems.Perfect.Ruby], - true, (item) => item.unique && item.getStat(sdk.stats.LifeLeech) === 8 && item.getStat(sdk.stats.DamageResist) === 20 && !item.ethereal - )); - - break; - case "Javazon": - Config.SkipImmune = ["lightning and physical"]; - - if (me.checkSkill(sdk.skills.ChargedStrike, sdk.skills.subindex.HardPoints)) { - // "Monster name": [-1, -1], - Config.CustomAttack = { - "Fire Tower": [sdk.skills.ChargedStrike, -1], - }; - } - - // Infinity - if ((me.ladder || Developer.addLadderRW) && Item.getMercEquipped(sdk.body.RightArm).prefixnum !== sdk.locale.items.Infinity) { - includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/MercInfinity.js"); - } - - // Spirit shield - if ((me.ladder || Developer.addLadderRW) && (me.equipped.get(sdk.body.LeftArm).tier < 1000 || me.equipped.get(sdk.body.LeftArmSecondary).prefixnum !== sdk.locale.items.Spirit)) { - includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/SpiritShield.js"); - } - - break; - default: - break; - } - - // Call to Arms - if (!me.checkItem({ name: sdk.locale.items.CalltoArms }).have) { - includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/CallToArms.js"); - } - - // Chains of Honor - if (!me.checkItem({ name: sdk.locale.items.ChainsofHonor }).have) { - includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/ChainsOfHonor.js"); - } - - // Merc Insight - if ((me.ladder || Developer.addLadderRW) && Item.getMercEquipped(sdk.body.RightArm).tier < 3600) { - includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/MercInsight.js"); - } - - // Lore - if (me.equipped.get(sdk.body.Head).tier < 250) { - includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/Lore.js"); - } - - // Ancients' Pledge - if (me.equipped.get(sdk.body.LeftArm).tier < 500 && ["Witchyzon", "Wfzon", "Faithbowzon"].indexOf(SetUp.currentBuild) === -1) { - includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/AncientsPledge.js"); - } - - // Treachery - if (me.equipped.get(sdk.body.Armor).tier < 634) { - includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/Treachery.js"); - } - - // Merc Doom - if ((me.ladder || Developer.addLadderRW) && Item.getMercEquipped(sdk.body.RightArm).prefixnum !== 20532 && SetUp.finalBuild !== "Javazon") { - includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/MercDoom.js"); - } - - // Merc Fortitude - if (Item.getMercEquipped(sdk.body.Armor).prefixnum !== sdk.locale.items.Fortitude) { - includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/MercFortitude.js"); - } - - // Merc Treachery - if (Item.getMercEquipped(sdk.body.Armor).tier < 15000) { - includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/MercTreachery.js"); - } - - // Smoke - if (me.equipped.get(sdk.body.Armor).tier < 634) { - includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/Smoke.js"); - } - - // Stealth - if (me.equipped.get(sdk.body.Armor).tier < 233) { - includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/Stealth.js"); - } - - SoloWants.buildList(); - - break; - } - - return true; + includeIfNotIncluded("SoloPlay/Functions/MiscOverrides.js"); + includeIfNotIncluded("SoloPlay/Functions/Globals.js"); + + SetUp.include(); + SetUp.config(); + + /* Pickit configuration. */ + Config.PickRange = 40; + // Config.PickitFiles.push("kolton.nip"); + // Config.PickitFiles.push("LLD.nip"); + + /* Gambling configuration. */ + Config.GambleItems.push("Amulet"); + Config.GambleItems.push("Ring"); + Config.GambleItems.push("Circlet"); + Config.GambleItems.push("Coronet"); + + // AutoEquip setup + const levelingTiers = [ + // Weapon + "([type] == javelin || [type] == amazonjavelin) && [quality] >= normal && [flag] != ethereal && [wsm] <= 10 && [2handed] == 0 # [itemchargedskill] >= 0 # [tier] == tierscore(item)", + // "[name] == ceremonialjavelin && [quality] == unique && [flag] == ethereal # [itemchargedskill] >= 0 # [tier] == tierscore(item)", // too many issues with eth titans + ]; + + const expansionTiers = [ + // Switch + "[type] == wand && [quality] >= normal # [itemchargedskill] == 72 # [secondarytier] == 25000", // Weaken charged wand + "[type] == wand && [quality] >= normal # [itemchargedskill] == 91 # [secondarytier] == 50000 + chargeditemscore(item, 91)", // Lower Resist charged wand + // Charms + "[name] == smallcharm && [quality] == magic # # [invoquantity] == 8 && [charmtier] == charmscore(item)", + "[name] == grandcharm && [quality] == magic # # [invoquantity] == 2 && [charmtier] == charmscore(item)", + ]; + + NTIP.buildList(levelingTiers); + me.expansion && NTIP.buildList(expansionTiers); + + if (["Witchyzon", "Wfzon", "Faithbowzon"].indexOf(SetUp.currentBuild) === -1) { + NTIP.addLine("[type] == shield && ([quality] >= magic || [flag] == runeword) && [flag] != ethereal # [itemchargedskill] >= 0 # [tier] == tierscore(item)"); + NTIP.addLine("([type] == shield) && [quality] >= normal && [flag] != ethereal # [itemchargedskill] >= 0 && ([sockets] == 1 || [sockets] == 2) # [tier] == tierscore(item)"); + NTIP.addLine("me.classic && [type] == shield && [quality] >= normal # [itemchargedskill] >= 0 # [tier] == tierscore(item)"); + } + + /* Attack configuration. */ + Config.AttackSkill = [0, 0, 0, 0, 0]; + Config.LowManaSkill = [-1, -1]; + Config.MaxAttackCount = 1000; + Config.BossPriority = false; + Config.ClearType = 0; + Config.ClearPath = { Range: (Pather.canTeleport() ? 30 : 20), Spectype: sdk.monsters.spectype.All }; + + // Class specific config + Config.LightningFuryDelay = 10; // Lightning fury interval in seconds. LF is treated as timed skill. + Config.SummonValkyrie = true; // Summon Valkyrie + + Config.imbueables = [ + { name: sdk.items.MaidenJavelin, condition: () => me.normal && me.expansion }, + { name: sdk.items.CeremonialJavelin, condition: () => !me.normal && (me.charlvl < 48 || me.trueStr < 107 || me.trueDex < 151) && me.expansion }, + { name: sdk.items.MatriarchalJavelin, condition: () => (me.equipped.get(sdk.body.RightArm).tier < 100000 && me.trueStr >= 107 && me.trueDex >= 151 && me.expansion) }, + { name: sdk.items.Belt, condition: () => (me.normal && (me.equipped.get(sdk.body.RightArm).tier > 100000 || me.classic)) }, + { name: sdk.items.MeshBelt, condition: () => (!me.normal && me.charlvl < 46 && me.trueStr > 58 && (me.equipped.get(sdk.body.RightArm).tier > 100000 || me.classic)) }, + { name: sdk.items.SpiderwebSash, condition: () => (!me.normal && me.trueStr > 50 && (me.equipped.get(sdk.body.RightArm).tier > 100000 || me.classic)) }, + ]; + + let imbueArr = SetUp.imbueItems(); + + !me.smith && NTIP.buildList(imbueArr); + + if (me.equipped.get(sdk.body.RightArm).tier < 100000) { + Config.GambleItems.push("Javelin"); + Config.GambleItems.push("Pilum"); + Config.GambleItems.push("Short Spear"); + Config.GambleItems.push("Throwing Spear"); + } + + switch (me.gametype) { + case sdk.game.gametype.Classic: + // Res shield + if (me.equipped.get(sdk.body.LeftArm).tier < 487) { + includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/PDiamondShield.js"); + } + + break; + case sdk.game.gametype.Expansion: + NTIP.addLine("[name] >= Vexrune && [name] <= Zodrune"); + const { basicSocketables, addSocketableObj } = require("../Utils/General"); + + Config.socketables = Config.socketables.concat(basicSocketables.all); + Config.socketables.push(addSocketableObj(sdk.items.Bill, [], [], + me.normal, (item) => item.ilvl >= 26 && item.isBaseType + )); + Config.socketables.push(addSocketableObj(sdk.items.Shako, [sdk.items.runes.Um], [sdk.items.gems.Perfect.Ruby], + true, (item) => item.unique && !item.ethereal + )); + + Check.itemSockables(sdk.items.RoundShield, "unique", "Moser's Blessed Circle"); + Check.itemSockables(sdk.items.Shako, "unique", "Harlequin Crest"); + + /* Crafting */ + // Going with Blood but TODO is test HitPower vs Blood + if (me.equipped.get(sdk.body.Neck).tier < 100000) { + Config.Recipes.push([Recipe.Blood.Amulet]); + } + + if (me.equipped.get(sdk.body.RingLeft).tier < 100000) { + Config.Recipes.push([Recipe.Blood.Ring]); + } + + // FinalBuild specific setup + switch (SetUp.finalBuild) { + case "Witchyzon": + case "Faithbowzon": + case "Wfzon": + (["Witchyzon", "Wfzon", "Faithbowzon"].includes(SetUp.currentBuild)) && NTIP.addLine("[type] == bowquiver # # [maxquantity] == 1"); + + if (SetUp.finalBuild === "Wfzon") { + if (!Check.haveItem(sdk.items.HydraBow, "unique", "Windforce")) { + NTIP.addLine("[name] == hydrabow && [quality] == unique # [manaleech] >= 6 # [maxquantity] == 1"); + } + + Config.socketables.push(addSocketableObj(sdk.items.HydraBow, [sdk.items.runes.Shael], [sdk.items.runes.Amn], + true, (item) => item.unique + )); + } + + if ((SetUp.finalBuild === "Faithbowzon") && !me.checkItem({ name: sdk.locale.items.Faith, classid: sdk.items.GrandMatronBow }).have) { + includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/Faith.js"); + } + + if (!Check.haveItem(sdk.items.DiamondBow, "unique", "Witchwild String") + && (SetUp.finalBuild === "Witchyzon" || ["Wfzon", "Faithbowzon"].indexOf(SetUp.currentBuild) === -1)) { + NTIP.addLine("[name] == shortsiegebow && [quality] == unique # [fireresist] == 40 # [maxquantity] == 1"); + // Witchyzon only - keep the bow but don't upgrade it until we have our wanted belt + if ((SetUp.finalBuild === "Witchyzon" && Check.haveItem("vampirefangbelt", "unique", "Nosferatu's Coil")) || ["Wfzon", "Faithbowzon"].includes(SetUp.finalBuild)) { + Config.Recipes.push([Recipe.Unique.Weapon.ToElite, "Short Siege Bow", Roll.NonEth]); + } + } + + // Spirit shield - while lvling and Wf final switch + if ((me.ladder || Developer.addLadderRW) && (me.equipped.get(5).tier < 1000 + && (["Witchyzon", "Wfzon", "Faithbowzon"].indexOf(SetUp.currentBuild) === -1) + || (SetUp.finalBuild === "Wfzon" && me.equipped.get(12).prefixnum !== sdk.locale.items.Spirit))) { + includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/SpiritShield.js"); + } + + Config.socketables.push(addSocketableObj(sdk.items.DiamondBow, [sdk.items.runes.Nef, sdk.items.runes.Shael], [sdk.items.gems.Perfect.Skull], + false, (item) => item.unique + )); + Config.socketables.push(addSocketableObj(sdk.items.BoneVisage, [sdk.items.runes.Um], [sdk.items.gems.Perfect.Ruby], + true, (item) => item.unique && item.getStat(sdk.stats.LifeLeech) === 8 && item.getStat(sdk.stats.DamageResist) === 20 && !item.ethereal + )); + + break; + case "Javazon": + Config.SkipImmune = ["lightning and physical"]; + + if (me.checkSkill(sdk.skills.ChargedStrike, sdk.skills.subindex.HardPoints)) { + // "Monster name": [-1, -1], + Config.CustomAttack = { + "Fire Tower": [sdk.skills.ChargedStrike, -1], + }; + } + + // Infinity + if ((me.ladder || Developer.addLadderRW) && Item.getMercEquipped(sdk.body.RightArm).prefixnum !== sdk.locale.items.Infinity) { + includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/MercInfinity.js"); + } + + // Spirit shield + if ((me.ladder || Developer.addLadderRW) && (me.equipped.get(sdk.body.LeftArm).tier < 1000 || me.equipped.get(sdk.body.LeftArmSecondary).prefixnum !== sdk.locale.items.Spirit)) { + includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/SpiritShield.js"); + } + + break; + default: + break; + } + + // Call to Arms + if (!me.checkItem({ name: sdk.locale.items.CalltoArms }).have) { + includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/CallToArms.js"); + } + + // Chains of Honor + if (!me.checkItem({ name: sdk.locale.items.ChainsofHonor }).have) { + includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/ChainsOfHonor.js"); + } + + // Merc Insight + if ((me.ladder || Developer.addLadderRW) && Item.getMercEquipped(sdk.body.RightArm).tier < 3600) { + includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/MercInsight.js"); + } + + // Lore + if (me.equipped.get(sdk.body.Head).tier < 250) { + includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/Lore.js"); + } + + // Ancients' Pledge + if (me.equipped.get(sdk.body.LeftArm).tier < 500 && ["Witchyzon", "Wfzon", "Faithbowzon"].indexOf(SetUp.currentBuild) === -1) { + includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/AncientsPledge.js"); + } + + // Treachery + if (me.equipped.get(sdk.body.Armor).tier < 634) { + includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/Treachery.js"); + } + + // Merc Doom + if ((me.ladder || Developer.addLadderRW) && Item.getMercEquipped(sdk.body.RightArm).prefixnum !== 20532 && SetUp.finalBuild !== "Javazon") { + includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/MercDoom.js"); + } + + // Merc Fortitude + if (Item.getMercEquipped(sdk.body.Armor).prefixnum !== sdk.locale.items.Fortitude) { + includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/MercFortitude.js"); + } + + // Merc Treachery + if (Item.getMercEquipped(sdk.body.Armor).tier < 15000) { + includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/MercTreachery.js"); + } + + // Smoke + if (me.equipped.get(sdk.body.Armor).tier < 634) { + includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/Smoke.js"); + } + + // Stealth + if (me.equipped.get(sdk.body.Armor).tier < 233) { + includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/Stealth.js"); + } + + SoloWants.buildList(); + + break; + } + + return true; })(); diff --git a/libs/SoloPlay/Config/Assassin.js b/libs/SoloPlay/Config/Assassin.js index fba7f783..cb66e6e7 100644 --- a/libs/SoloPlay/Config/Assassin.js +++ b/libs/SoloPlay/Config/Assassin.js @@ -15,207 +15,207 @@ */ (function LoadConfig () { - includeIfNotIncluded("SoloPlay/Functions/MiscOverrides.js"); - includeIfNotIncluded("SoloPlay/Functions/Globals.js"); - - SetUp.include(); - SetUp.config(); - - /* Pickit configuration. */ - Config.PickRange = 40; - // Config.PickitFiles.push("kolton.nip"); - // Config.PickitFiles.push("LLD.nip"); - NTIP.addLine("[name] >= VexRune && [name] <= ZodRune"); - - /* Gambling configuration. */ - Config.GambleItems.push("Amulet"); - Config.GambleItems.push("Ring"); - // Config.GambleItems.push("Circlet"); - // Config.GambleItems.push("Coronet"); - - /* AutoEquip configuration. */ - Config.AutoEquip = true; - - // AutoEquip setup - const levelingTiers = [ - // Weapon - "([type] == knife || [type] == sword && [flag] == runeword || ([type] == handtohand || [type] == assassinclaw) && [quality] >= magic) && [flag] != ethereal && [2handed] == 0 # [itemchargedskill] >= 0 # [tier] == tierscore(item)", - // Shield - "[type] == shield && ([quality] >= magic || [flag] == runeword) && [flag] != ethereal # [itemchargedskill] >= 0 # [tier] == tierscore(item)", - // Switch - "[type] == wand && [quality] >= normal # [itemchargedskill] == 91 # [secondarytier] == 50000 + chargeditemscore(item, 91)", // Lower Resist charged wand - // Charms - "[name] == smallcharm && [quality] == magic # [maxhp] >= 1 # [invoquantity] == 2 && [charmtier] == charmscore(item)", - "[name] == smallcharm && [quality] == magic # [itemmagicbonus] >= 1 # [invoquantity] == 2 && [charmtier] == charmscore(item)", - "[name] == smallcharm && [quality] == magic # # [invoquantity] == 2 && [charmtier] == charmscore(item)", - "[name] == grandcharm && [quality] == magic # # [invoquantity] == 2 && [charmtier] == charmscore(item)", - // non runeword white items - "([type] == shield) && [quality] >= normal && [flag] != ethereal # [itemchargedskill] >= 0 && ([sockets] == 1 || [sockets] == 2) # [tier] == tierscore(item)", - ]; - - NTIP.buildList(levelingTiers); - - /* Attack configuration. */ - Config.AttackSkill = [0, 0, 0, 0, 0, 0, 0]; - Config.LowManaSkill = [0, 0]; - Config.MaxAttackCount = 1000; - Config.BossPriority = me.normal; - Config.ClearType = 0; - Config.ClearPath = { Range: (Pather.canTeleport() ? 30 : 20), Spectype: 0 }; - - /* Class specific configuration. */ - Config.UseTraps = true; - Config.Traps = [sdk.skills.LightningSentry, sdk.skills.LightningSentry, sdk.skills.LightningSentry, sdk.skills.DeathSentry, sdk.skills.DeathSentry]; - Config.BossTraps = [sdk.skills.LightningSentry, sdk.skills.LightningSentry, sdk.skills.LightningSentry, sdk.skills.LightningSentry, sdk.skills.LightningSentry]; - - Config.SummonShadow = me.checkSkill(sdk.skills.ShadowMaster, sdk.skills.subindex.HardPoints) ? "Master" : 0; - Config.UseFade = me.checkSkill(sdk.skills.Fade, sdk.skills.subindex.HardPoints); - Config.UseBoS = me.checkSkill(sdk.skills.BurstofSpeed, sdk.skills.subindex.HardPoints); - Config.UseVenom = false; - Config.UseCloakofShadows = me.checkSkill(sdk.skills.CloakofShadows, sdk.skills.subindex.HardPoints); - Config.AggressiveCloak = false; - - /* Dodge configuration. */ - Config.Dodge = me.checkSkill(sdk.skills.LightningSentry, sdk.skills.subindex.HardPoints); - Config.DodgeRange = 10; - Config.DodgeHP = 75; - - Config.imbueables = [ - { name: sdk.items.Claws, condition: () => (me.normal) }, - { name: sdk.items.HandScythe, condition: () => (!me.normal && me.equipped.get(sdk.body.RightArm).tier < 777 && (me.trueStr < 79 || me.trueDex < 79)) }, - { name: sdk.items.GreaterTalons, condition: () => (me.equipped.get(sdk.body.RightArm).tier < 777 && me.trueStr >= 79 && me.trueDex >= 79) }, - { name: sdk.items.Belt, condition: () => (me.normal && (me.equipped.get(sdk.body.RightArm).tier > 777)) }, - { name: sdk.items.MeshBelt, condition: () => (!me.normal && me.charlvl < 46 && me.trueStr > 58 && (me.equipped.get(sdk.body.RightArm).tier > 777)) }, - { name: sdk.items.SpiderwebSash, condition: () => (!me.normal && me.trueStr > 50 && (me.equipped.get(sdk.body.RightArm).tier > 777)) }, - ].filter((item) => item.condition()); - - let imbueArr = SetUp.imbueItems(); - - !me.smith && NTIP.buildList(imbueArr); - - const { basicSocketables, addSocketableObj } = require("../Utils/General"); - - Config.socketables = Config.socketables.concat(basicSocketables.caster, basicSocketables.all); - Config.socketables.push(addSocketableObj(sdk.items.Monarch, [], [], - !me.hell, (item) => !Check.haveBase("monarch", 4) && item.ilvl >= 41 && item.isBaseType && !item.ethereal - )); - Config.socketables.push(addSocketableObj(sdk.items.Shako, [sdk.items.runes.Um], [sdk.items.gems.Perfect.Ruby], - true, (item) => item.unique && !item.ethereal - )); - - switch (SetUp.finalBuild) { - case "Whirlsin": - Config.socketables.push(addSocketableObj(sdk.items.WingedHelm, [sdk.items.runes.Um], [sdk.items.gems.Perfect.Ruby], - true, (item) => item.unique && !item.ethereal - )); - - // Pride - if ((me.ladder || Developer.addLadderRW) && Item.getMercEquipped(sdk.body.RightArm).prefixnum !== sdk.locale.items.Pride) { - includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/MercPride.js"); - } - - // Chaos - if (!me.checkItem({ name: sdk.locale.items.Chaos }).have) { - includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/Chaos.js"); - } - - // Fury - if (!me.checkItem({ name: sdk.locale.items.Fury }).have) { - includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/Fury.js"); - } - - // Fortitude - if ((me.ladder || Developer.addLadderRW) && !me.checkItem({ name: sdk.locale.items.Fortitude, itemtype: sdk.items.type.Armor }).have) { - includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/Fortitude.js"); - } - - break; - default: - Config.socketables.push(addSocketableObj(sdk.items.Demonhead, [sdk.items.runes.Um], [sdk.items.gems.Perfect.Ruby], - true, (item) => item.unique && !item.ethereal - )); - - // Infinity - if ((me.ladder || Developer.addLadderRW) && Item.getMercEquipped(sdk.body.RightArm).prefixnum !== sdk.locale.items.Infinity) { - includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/MercInfinity.js"); - } - - // Heart of the Oak - if (!me.checkItem({ name: sdk.locale.items.HeartoftheOak }).have) { - includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/HeartOfTheOak.js"); - } - - // Enigma - if (!me.checkItem({ name: sdk.locale.items.Enigma }).have) { - includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/Enigma.js"); - } - - break; - } - - Check.itemSockables(sdk.items.RoundShield, "unique", "Moser's Blessed Circle"); - Check.itemSockables(sdk.items.Shako, "unique", "Harlequin Crest"); - - /* Crafting */ - if (me.equipped.get(sdk.body.Neck).tier < 100000) { - Config.Recipes.push([Recipe.Caster.Amulet]); - } - - if (me.equipped.get(sdk.body.RingLeft).tier < 100000) { - Config.Recipes.push([Recipe.Caster.Ring]); - } - - // Call to Arms - if (!me.checkItem({ name: sdk.locale.items.CalltoArms }).have) { - includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/CallToArms.js"); - } - - // Spirit Sword - if ((me.ladder || Developer.addLadderRW) && me.equipped.get(sdk.body.RightArm).tier < 777) { - includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/SpiritSword.js"); - } - - // Spirit shield - if ((me.ladder || Developer.addLadderRW) && (me.equipped.get(sdk.body.LeftArm).tier < 1000 || me.equipped.get(sdk.body.LeftArmSecondary).prefixnum !== sdk.locale.items.Spirit)) { - includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/SpiritShield.js"); - } - - // Merc Insight - if ((me.ladder || Developer.addLadderRW) && Item.getMercEquipped(sdk.body.RightArm).tier < 3600) { - includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/MercInsight.js"); - } - - // Lore - if (me.equipped.get(sdk.body.Head).tier < 315) { - includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/Lore.js"); - } - - // Ancients' Pledge - if (me.equipped.get(sdk.body.LeftArm).tier < 500) { - includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/AncientsPledge.js"); - } - - // Merc Fortitude - if (Item.getMercEquipped(sdk.body.Armor).prefixnum !== sdk.locale.items.Fortitude) { - includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/MercFortitude.js"); - } - - // Merc Treachery - if (Item.getMercEquipped(sdk.body.Armor).tier < 15000) { - includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/MercTreachery.js"); - } - - // Smoke - if (me.equipped.get(sdk.body.Armor).tier < 450) { - includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/Smoke.js"); - } - - // Stealth - if (me.equipped.get(sdk.body.Armor).tier < 233) { - includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/Stealth.js"); - } - - SoloWants.buildList(); - - return true; + includeIfNotIncluded("SoloPlay/Functions/MiscOverrides.js"); + includeIfNotIncluded("SoloPlay/Functions/Globals.js"); + + SetUp.include(); + SetUp.config(); + + /* Pickit configuration. */ + Config.PickRange = 40; + // Config.PickitFiles.push("kolton.nip"); + // Config.PickitFiles.push("LLD.nip"); + NTIP.addLine("[name] >= VexRune && [name] <= ZodRune"); + + /* Gambling configuration. */ + Config.GambleItems.push("Amulet"); + Config.GambleItems.push("Ring"); + // Config.GambleItems.push("Circlet"); + // Config.GambleItems.push("Coronet"); + + /* AutoEquip configuration. */ + Config.AutoEquip = true; + + // AutoEquip setup + const levelingTiers = [ + // Weapon + "([type] == knife || [type] == sword && [flag] == runeword || ([type] == handtohand || [type] == assassinclaw) && [quality] >= magic) && [flag] != ethereal && [2handed] == 0 # [itemchargedskill] >= 0 # [tier] == tierscore(item)", + // Shield + "[type] == shield && ([quality] >= magic || [flag] == runeword) && [flag] != ethereal # [itemchargedskill] >= 0 # [tier] == tierscore(item)", + // Switch + "[type] == wand && [quality] >= normal # [itemchargedskill] == 91 # [secondarytier] == 50000 + chargeditemscore(item, 91)", // Lower Resist charged wand + // Charms + "[name] == smallcharm && [quality] == magic # [maxhp] >= 1 # [invoquantity] == 2 && [charmtier] == charmscore(item)", + "[name] == smallcharm && [quality] == magic # [itemmagicbonus] >= 1 # [invoquantity] == 2 && [charmtier] == charmscore(item)", + "[name] == smallcharm && [quality] == magic # # [invoquantity] == 2 && [charmtier] == charmscore(item)", + "[name] == grandcharm && [quality] == magic # # [invoquantity] == 2 && [charmtier] == charmscore(item)", + // non runeword white items + "([type] == shield) && [quality] >= normal && [flag] != ethereal # [itemchargedskill] >= 0 && ([sockets] == 1 || [sockets] == 2) # [tier] == tierscore(item)", + ]; + + NTIP.buildList(levelingTiers); + + /* Attack configuration. */ + Config.AttackSkill = [0, 0, 0, 0, 0, 0, 0]; + Config.LowManaSkill = [0, 0]; + Config.MaxAttackCount = 1000; + Config.BossPriority = me.normal; + Config.ClearType = 0; + Config.ClearPath = { Range: (Pather.canTeleport() ? 30 : 20), Spectype: 0 }; + + /* Class specific configuration. */ + Config.UseTraps = true; + Config.Traps = [sdk.skills.LightningSentry, sdk.skills.LightningSentry, sdk.skills.LightningSentry, sdk.skills.DeathSentry, sdk.skills.DeathSentry]; + Config.BossTraps = [sdk.skills.LightningSentry, sdk.skills.LightningSentry, sdk.skills.LightningSentry, sdk.skills.LightningSentry, sdk.skills.LightningSentry]; + + Config.SummonShadow = me.checkSkill(sdk.skills.ShadowMaster, sdk.skills.subindex.HardPoints) ? "Master" : 0; + Config.UseFade = me.checkSkill(sdk.skills.Fade, sdk.skills.subindex.HardPoints); + Config.UseBoS = me.checkSkill(sdk.skills.BurstofSpeed, sdk.skills.subindex.HardPoints); + Config.UseVenom = false; + Config.UseCloakofShadows = me.checkSkill(sdk.skills.CloakofShadows, sdk.skills.subindex.HardPoints); + Config.AggressiveCloak = false; + + /* Dodge configuration. */ + Config.Dodge = me.checkSkill(sdk.skills.LightningSentry, sdk.skills.subindex.HardPoints); + Config.DodgeRange = 10; + Config.DodgeHP = 75; + + Config.imbueables = [ + { name: sdk.items.Claws, condition: () => (me.normal) }, + { name: sdk.items.HandScythe, condition: () => (!me.normal && me.equipped.get(sdk.body.RightArm).tier < 777 && (me.trueStr < 79 || me.trueDex < 79)) }, + { name: sdk.items.GreaterTalons, condition: () => (me.equipped.get(sdk.body.RightArm).tier < 777 && me.trueStr >= 79 && me.trueDex >= 79) }, + { name: sdk.items.Belt, condition: () => (me.normal && (me.equipped.get(sdk.body.RightArm).tier > 777)) }, + { name: sdk.items.MeshBelt, condition: () => (!me.normal && me.charlvl < 46 && me.trueStr > 58 && (me.equipped.get(sdk.body.RightArm).tier > 777)) }, + { name: sdk.items.SpiderwebSash, condition: () => (!me.normal && me.trueStr > 50 && (me.equipped.get(sdk.body.RightArm).tier > 777)) }, + ].filter((item) => item.condition()); + + let imbueArr = SetUp.imbueItems(); + + !me.smith && NTIP.buildList(imbueArr); + + const { basicSocketables, addSocketableObj } = require("../Utils/General"); + + Config.socketables = Config.socketables.concat(basicSocketables.caster, basicSocketables.all); + Config.socketables.push(addSocketableObj(sdk.items.Monarch, [], [], + !me.hell, (item) => !Check.haveBase("monarch", 4) && item.ilvl >= 41 && item.isBaseType && !item.ethereal + )); + Config.socketables.push(addSocketableObj(sdk.items.Shako, [sdk.items.runes.Um], [sdk.items.gems.Perfect.Ruby], + true, (item) => item.unique && !item.ethereal + )); + + switch (SetUp.finalBuild) { + case "Whirlsin": + Config.socketables.push(addSocketableObj(sdk.items.WingedHelm, [sdk.items.runes.Um], [sdk.items.gems.Perfect.Ruby], + true, (item) => item.unique && !item.ethereal + )); + + // Pride + if ((me.ladder || Developer.addLadderRW) && Item.getMercEquipped(sdk.body.RightArm).prefixnum !== sdk.locale.items.Pride) { + includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/MercPride.js"); + } + + // Chaos + if (!me.checkItem({ name: sdk.locale.items.Chaos }).have) { + includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/Chaos.js"); + } + + // Fury + if (!me.checkItem({ name: sdk.locale.items.Fury }).have) { + includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/Fury.js"); + } + + // Fortitude + if ((me.ladder || Developer.addLadderRW) && !me.checkItem({ name: sdk.locale.items.Fortitude, itemtype: sdk.items.type.Armor }).have) { + includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/Fortitude.js"); + } + + break; + default: + Config.socketables.push(addSocketableObj(sdk.items.Demonhead, [sdk.items.runes.Um], [sdk.items.gems.Perfect.Ruby], + true, (item) => item.unique && !item.ethereal + )); + + // Infinity + if ((me.ladder || Developer.addLadderRW) && Item.getMercEquipped(sdk.body.RightArm).prefixnum !== sdk.locale.items.Infinity) { + includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/MercInfinity.js"); + } + + // Heart of the Oak + if (!me.checkItem({ name: sdk.locale.items.HeartoftheOak }).have) { + includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/HeartOfTheOak.js"); + } + + // Enigma + if (!me.checkItem({ name: sdk.locale.items.Enigma }).have) { + includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/Enigma.js"); + } + + break; + } + + Check.itemSockables(sdk.items.RoundShield, "unique", "Moser's Blessed Circle"); + Check.itemSockables(sdk.items.Shako, "unique", "Harlequin Crest"); + + /* Crafting */ + if (me.equipped.get(sdk.body.Neck).tier < 100000) { + Config.Recipes.push([Recipe.Caster.Amulet]); + } + + if (me.equipped.get(sdk.body.RingLeft).tier < 100000) { + Config.Recipes.push([Recipe.Caster.Ring]); + } + + // Call to Arms + if (!me.checkItem({ name: sdk.locale.items.CalltoArms }).have) { + includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/CallToArms.js"); + } + + // Spirit Sword + if ((me.ladder || Developer.addLadderRW) && me.equipped.get(sdk.body.RightArm).tier < 777) { + includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/SpiritSword.js"); + } + + // Spirit shield + if ((me.ladder || Developer.addLadderRW) && (me.equipped.get(sdk.body.LeftArm).tier < 1000 || me.equipped.get(sdk.body.LeftArmSecondary).prefixnum !== sdk.locale.items.Spirit)) { + includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/SpiritShield.js"); + } + + // Merc Insight + if ((me.ladder || Developer.addLadderRW) && Item.getMercEquipped(sdk.body.RightArm).tier < 3600) { + includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/MercInsight.js"); + } + + // Lore + if (me.equipped.get(sdk.body.Head).tier < 315) { + includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/Lore.js"); + } + + // Ancients' Pledge + if (me.equipped.get(sdk.body.LeftArm).tier < 500) { + includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/AncientsPledge.js"); + } + + // Merc Fortitude + if (Item.getMercEquipped(sdk.body.Armor).prefixnum !== sdk.locale.items.Fortitude) { + includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/MercFortitude.js"); + } + + // Merc Treachery + if (Item.getMercEquipped(sdk.body.Armor).tier < 15000) { + includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/MercTreachery.js"); + } + + // Smoke + if (me.equipped.get(sdk.body.Armor).tier < 450) { + includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/Smoke.js"); + } + + // Stealth + if (me.equipped.get(sdk.body.Armor).tier < 233) { + includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/Stealth.js"); + } + + SoloWants.buildList(); + + return true; })(); diff --git a/libs/SoloPlay/Config/Barbarian.js b/libs/SoloPlay/Config/Barbarian.js index 7b5dc187..2634e14d 100644 --- a/libs/SoloPlay/Config/Barbarian.js +++ b/libs/SoloPlay/Config/Barbarian.js @@ -17,323 +17,323 @@ */ (function LoadConfig () { - includeIfNotIncluded("SoloPlay/Functions/MiscOverrides.js"); - includeIfNotIncluded("SoloPlay/Functions/Globals.js"); - - SetUp.include(); - SetUp.config(); - - /* Pickit configuration. */ - Config.PickRange = 40; - Config.FieldID.UsedSpace = 80; // how much space has been used before trying to field id, set to 0 to id after every item picked - // Config.PickitFiles.push("kolton.nip"); - // Config.PickitFiles.push("LLD.nip"); - - /* Gambling configuration. */ - Config.GambleItems.push("Amulet"); - Config.GambleItems.push("Ring"); - - /* AutoEquip configuration. */ - Config.AutoEquip = true; - - // AutoEquip setup - const levelingTiers = [ - // Weapon - "me.charlvl < 12 && [type] == sword && ([quality] >= normal || [flag] == runeword) && [flag] != ethereal && [wsm] <= 20 # [itemchargedskill] >= 0 # [tier] == tierscore(item)", - "[type] == sword && ([quality] >= magic || [flag] == runeword) && [flag] != ethereal && [wsm] <= 10 # [itemchargedskill] >= 0 # [tier] == tierscore(item)", - "[name] == phaseblade && [quality] == unique && [flag] == ethereal # [enhanceddamage] >= 100 && [ias] == 30 && [magicdamagereduction] >= 7 # [tier] == tierscore(item)", - // Helmet - "([type] == primalhelm) && ([quality] >= magic || [flag] == runeword) && [flag] != ethereal # [itemchargedskill] >= 0 # [tier] == tierscore(item)", - "([type] == primalhelm) && [quality] >= normal && [flag] != ethereal # [itemchargedskill] >= 0 && ([sockets] == 1 || [sockets] == 3) # [tier] == tierscore(item)", - ]; - - const expansionTiers = [ - // Charms - "[name] == smallcharm && [quality] == magic # # [invoquantity] == 8 && [charmtier] == charmscore(item)", - "[name] == grandcharm && [quality] == magic # # [invoquantity] == 2 && [charmtier] == charmscore(item)", - ]; - - NTIP.buildList(levelingTiers); - me.expansion && NTIP.buildList(expansionTiers); - - /* Attack configuration. */ - Config.AttackSkill = [-1, 0, 0, 0, 0]; - Config.LowManaSkill = me.getSkill(sdk.skills.DoubleSwing, sdk.skills.subindex.SoftPoints) >= 9 ? [sdk.skills.DoubleSwing, 0] : [0, -1]; - Config.MaxAttackCount = 1000; - Config.BossPriority = me.normal; - Config.ClearType = 0; - Config.ClearPath = { Range: (Pather.canTeleport() ? 30 : 10), Spectype: 0 }; - - // Class specific config - Config.FindItem = true; // Use Find Item skill on corpses after clearing. - Config.FindItemSwitch = false; // Switch to non-primary slot when using Find Item skills - - Config.imbueables = [ - { name: sdk.items.AvengerGuard, condition: () => (me.normal && me.expansion) }, - { name: sdk.items.SlayerGuard, condition: () => (!me.normal && me.trueStr >= 118 && me.expansion) }, - { name: sdk.items.CarnageHelm, condition: () => (me.equipped.get(sdk.body.Head).tier < 100000 && me.trueStr >= 106 && me.expansion) }, - { name: sdk.items.Belt, condition: () => (me.normal && (me.equipped.get(sdk.body.Head).tier > 100000 || me.classic)) }, - { name: sdk.items.MeshBelt, condition: () => (!me.normal && me.charlvl < 46 && me.trueStr > 58 && (me.equipped.get(sdk.body.RightArm).tier > 100000 || me.classic)) }, - { name: sdk.items.SpiderwebSash, condition: () => (!me.normal && me.trueStr > 50 && (me.equipped.get(sdk.body.RightArm).tier > 100000 || me.classic)) }, - ].filter((item) => item.condition()); - - let imbueArr = SetUp.imbueItems(); - - !me.smith && NTIP.buildList(imbueArr); - - switch (me.gametype) { - case sdk.game.gametype.Classic: - break; - case sdk.game.gametype.Expansion: - NTIP.addLine("[name] >= VexRune && [name] <= ZodRune"); - const { basicSocketables, addSocketableObj } = require("../Utils/General"); - - Config.socketables = Config.socketables.concat(basicSocketables.all); - Config.socketables.push(addSocketableObj(sdk.items.Flamberge, [], [], - true, (item) => me.normal && me.equipped.get(sdk.body.LeftArm).tier < 600 && !Check.haveBase("sword", 5) && !me.checkItem({ name: sdk.locale.items.Honor }).have && item.ilvl >= 41 && item.isBaseType && !item.ethereal - )); - Config.socketables.push(addSocketableObj(sdk.items.Zweihander, [], [], - true, (item) => me.equipped.get(sdk.body.LeftArm).tier < 1000 && !Check.haveBase("sword", 5) && !me.checkItem({ name: sdk.locale.items.Honor }).have && item.ilvl >= 41 && item.isBaseType && !item.ethereal - )); - - if (SetUp.finalBuild !== "Immortalwhirl") { - Config.socketables.push(addSocketableObj(sdk.items.SlayerGuard, [sdk.items.runes.Cham], [sdk.items.gems.Perfect.Ruby], - true, (item) => item.unique && !item.ethereal - )); - } - - if (["Immortalwhirl", "Singer"].indexOf(SetUp.finalBuild) === -1) { - // Grief - if ((me.ladder || Developer.addLadderRW) && (!me.checkItem({ name: sdk.locale.items.Grief }).have || (SetUp.finalBuild === "Whirlwind" && me.equipped.get(sdk.body.LeftArm).prefixnum !== sdk.locale.items.Grief))) { - includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/Grief.js"); - } - - // Fortitude - if ((me.ladder || Developer.addLadderRW) && SetUp.finalBuild !== "Uberconc" && me.checkItem({ name: sdk.locale.items.Grief }).have && !me.checkItem({ name: sdk.locale.items.Fortitude, itemtype: sdk.items.type.Armor }).have) { - includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/Fortitude.js"); - } - - // Doom - if ((me.ladder || Developer.addLadderRW) && Item.getMercEquipped(sdk.body.RightArm).prefixnum !== 20532) { - includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/MercDoom.js"); - } - } - - // FinalBuild specific setup - switch (SetUp.finalBuild) { - case "Uberconc": - if (me.checkItem({ name: sdk.locale.items.Grief }).have && SetUp.finalBuild === "Uberconc") { - // Add Stormshield - NTIP.addLine("[name] == monarch && [quality] == unique && [flag] != ethereal # [damageresist] >= 35 # [tier] == 100000"); - } - - // Chains of Honor - if (!me.checkItem({ name: sdk.locale.items.ChainsofHonor }).have) { - includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/ChainsOfHonor.js"); - } - - break; - case "Frenzy": - // Breathe of the Dying - if (!me.checkItem({ name: sdk.locale.items.BreathoftheDying }).have) { - includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/BreathoftheDying.js"); - } - - break; - case "Singer": - // Heart of the Oak - if (me.equipped.get(sdk.body.LeftArm).prefixnum !== sdk.locale.items.HeartoftheOak && me.checkItem({ name: sdk.locale.items.Enigma }).have) { - includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/HeartOfTheOak.js"); - } - - // Enigma - if (!me.checkItem({ name: sdk.locale.items.Enigma }).have) { - includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/Enigma.js"); - } - - break; - case "Immortalwhirl": - // Infinity - if ((me.ladder || Developer.addLadderRW) && Item.getMercEquipped(sdk.body.RightArm).prefixnum !== sdk.locale.items.Infinity) { - includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/MercInfinity.js"); - } - - Config.socketables.push(addSocketableObj(sdk.items.AvengerGuard, [sdk.items.runes.Ber], [sdk.items.gems.Perfect.Ruby], - false, (item) => item.set && !item.ethereal - )); - Config.socketables.push(addSocketableObj(sdk.items.OgreMaul, [sdk.items.runes.Shael], [], - false, (item) => item.set && !item.ethereal - )); - Config.socketables.push(addSocketableObj(sdk.items.SacredArmor, [sdk.items.runes.Ber], [sdk.items.gems.Perfect.Ruby], - true, (item) => item.set && !item.ethereal - )); - - Check.itemSockables(sdk.items.OgreMaul, "set", "Immortal King's Stone Crusher"); - - break; - case "Whirlwind": - break; - default: - break; - } - - /* Crafting */ - if (me.equipped.get(sdk.body.Neck).tier < 100000) { - Check.currentBuild().caster ? Config.Recipes.push([Recipe.Caster.Amulet]) : Config.Recipes.push([Recipe.Blood.Amulet]); - } - - if (me.equipped.get(sdk.body.RingLeft).tier < 100000) { - Check.currentBuild().caster ? Config.Recipes.push([Recipe.Caster.Ring]) : Config.Recipes.push([Recipe.Blood.Ring]); - } - - if (me.equipped.get(sdk.body.LeftArm).tier < 1370) { - if (me.rawStrength >= 150 && me.rawDexterity >= 88) { - // Upgrade Bloodletter to Elite - Config.Recipes.push([Recipe.Unique.Weapon.ToElite, "Gladius", Roll.NonEth]); - } - - if (me.rawStrength >= 25 && me.rawDexterity >= 136) { - // Upgrade Ginther's Rift to Elite - Config.Recipes.push([Recipe.Unique.Weapon.ToElite, "dimensionalblade", Roll.Eth]); - } - - if (!Check.haveItem("falcata", "unique", "Bloodletter")) { - NTIP.addLine("[name] == PulRune # # [maxquantity] == 1"); - NTIP.addLine("[name] == perfectemerald # # [maxquantity] == 1"); - // Bloodletter - NTIP.addLine("[name] == gladius && [quality] == unique && [flag] != ethereal # [enhanceddamage] >= 140 && [ias] >= 20 # [maxquantity] == 1"); - // upped Bloodletter - NTIP.addLine("[name] == falcata && [quality] == unique && [flag] != ethereal # [enhanceddamage] >= 140 && [ias] >= 20 # [maxquantity] == 1"); - } - - if (!Check.haveItem("dimensionalblade", "unique", "Ginther's Rift")) { - NTIP.addLine("[name] == PulRune # # [maxquantity] == 1"); - NTIP.addLine("[name] == perfectemerald # # [maxquantity] == 1"); - - // Have Pul rune before looking for eth ginther's - if (me.getItem(sdk.items.runes.Pul)) { - // Eth Ginther's Rift - NTIP.addLine("[name] == dimensionalblade && [quality] == unique && [flag] == ethereal # [enhanceddamage] >= 100 && [ias] == 30 && [magicdamagereduction] >= 7 # [maxquantity] == 1"); - } - - // upped Ginther's Rift - NTIP.addLine("[name] == phaseblade && [quality] == unique && [flag] == ethereal # [enhanceddamage] >= 100 && [ias] == 30 && [magicdamagereduction] >= 7 # [maxquantity] == 1"); - } - } - - // Lawbringer - Amn/Lem/Ko - if (me.equipped.get(sdk.body.LeftArm).tier < 1370) { - includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/Lawbringer.js"); - } - - // Voice Of Reason - Lem/Ko/El/Eld - if (me.equipped.get(sdk.body.RightArm).tier > 1100 && me.equipped.get(sdk.body.LeftArm).tier < 1270 && !Check.haveItem("ring", "unique", "Raven Frost")) { - includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/VoiceOfReason.js"); - } - - // Crescent Moon - Shael/Um/Tir - if (me.equipped.get(sdk.body.LeftArm).tier < 1100) { - includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/CrescentMoon.js"); - } - - if (me.equipped.get(sdk.body.LeftArm).tier < 1200) { - // Cube to Ko Rune - if (!me.getItem(sdk.items.runes.Ko)) { - Config.Recipes.push([Recipe.Rune, "Hel Rune"]); - Config.Recipes.push([Recipe.Rune, "Io Rune"]); - Config.Recipes.push([Recipe.Rune, "Lum Rune"]); - } - - // Cube to Lem Rune - if (!me.getItem(sdk.items.runes.Lem)) { - Config.Recipes.push([Recipe.Rune, "Dol Rune"]); - Config.Recipes.push([Recipe.Rune, "Io Rune"]); - Config.Recipes.push([Recipe.Rune, "Lum Rune"]); - Config.Recipes.push([Recipe.Rune, "Ko Rune"]); - Config.Recipes.push([Recipe.Rune, "Fal Rune"]); - } - } - - // Honor - Amn/El/Ith/Tir/Sol - if (me.equipped.get(sdk.body.LeftArm).tier < 1050) { - includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/Honor.js"); - } - - // Merc Insight - if ((me.ladder || Developer.addLadderRW) && Item.getMercEquipped(sdk.body.RightArm).tier < 3600) { - includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/MercInsight.js"); - } - - // Lore - if (me.equipped.get(sdk.body.Head).tier < 100000) { - includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/Lore.js"); - } - - // Merc Fortitude - if ((me.ladder || Developer.addLadderRW) && Item.getMercEquipped(sdk.body.Armor).prefixnum !== sdk.locale.items.Fortitude) { - includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/MercFortitude.js"); - } - - // Merc Treachery - if (Item.getMercEquipped(sdk.body.Armor).tier < 15000 && me.equipped.get(sdk.body.RightArm).tier > 1100) { - includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/MercTreachery.js"); - } - - // Treachery - if (me.equipped.get(sdk.body.Armor).tier < 634 && me.equipped.get(sdk.body.RightArm).tier > 1100) { - includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/Treachery.js"); - } - - // Smoke - if (me.equipped.get(sdk.body.Armor).tier < 350) { - includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/Smoke.js"); - } - - // Duress - if (me.equipped.get(sdk.body.Armor).tier < 600 && (me.checkItem({ name: sdk.locale.items.CrescentMoon }).have || me.equipped.get(sdk.body.LeftArm).tier > 900)) { - includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/Duress.js"); - } - - // Myth - if (me.equipped.get(sdk.body.Armor).tier < 340) { - includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/Myth.js"); - } - - // Kings Grace - Amn/Ral/Thul - if (me.equipped.get(sdk.body.LeftArm).tier < 770) { - includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/KingsGrace.js"); - } - - // Steel - Tir/El - if (me.equipped.get(sdk.body.LeftArm).tier < 500) { - includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/Steel.js"); - } - - // Spirit Sword - if ((me.ladder || Developer.addLadderRW) && me.equipped.get(sdk.body.LeftArmSecondary).prefixnum !== sdk.locale.items.Spirit) { - includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/SpiritSword.js"); - } - - // Malice - IthElEth - if (me.equipped.get(sdk.body.LeftArm).tier < 175) { - includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/Malice.js"); - } - - // Stealth - if (me.equipped.get(sdk.body.Armor).tier < 233) { - includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/Stealth.js"); - } - - /*if (me.equipped.get(sdk.body.Gloves).tier < 233) { - NTIP.addLine("[name] == heavygloves && [flag] != ethereal && [quality] == magic # [itemchargedskill] >= 0 # [maxquantity] == 1"); - Config.Recipes.push([Recipe.Blood.Gloves, "Heavy Gloves"]); // Craft Blood Gloves - }*/ - - Check.itemSockables(sdk.items.Ataghan, "unique", "Djinn Slayer"); - SoloWants.buildList(); - - break; - } - - return true; + includeIfNotIncluded("SoloPlay/Functions/MiscOverrides.js"); + includeIfNotIncluded("SoloPlay/Functions/Globals.js"); + + SetUp.include(); + SetUp.config(); + + /* Pickit configuration. */ + Config.PickRange = 40; + Config.FieldID.UsedSpace = 80; // how much space has been used before trying to field id, set to 0 to id after every item picked + // Config.PickitFiles.push("kolton.nip"); + // Config.PickitFiles.push("LLD.nip"); + + /* Gambling configuration. */ + Config.GambleItems.push("Amulet"); + Config.GambleItems.push("Ring"); + + /* AutoEquip configuration. */ + Config.AutoEquip = true; + + // AutoEquip setup + const levelingTiers = [ + // Weapon + "me.charlvl < 12 && [type] == sword && ([quality] >= normal || [flag] == runeword) && [flag] != ethereal && [wsm] <= 20 # [itemchargedskill] >= 0 # [tier] == tierscore(item)", + "[type] == sword && ([quality] >= magic || [flag] == runeword) && [flag] != ethereal && [wsm] <= 10 # [itemchargedskill] >= 0 # [tier] == tierscore(item)", + "[name] == phaseblade && [quality] == unique && [flag] == ethereal # [enhanceddamage] >= 100 && [ias] == 30 && [magicdamagereduction] >= 7 # [tier] == tierscore(item)", + // Helmet + "([type] == primalhelm) && ([quality] >= magic || [flag] == runeword) && [flag] != ethereal # [itemchargedskill] >= 0 # [tier] == tierscore(item)", + "([type] == primalhelm) && [quality] >= normal && [flag] != ethereal # [itemchargedskill] >= 0 && ([sockets] == 1 || [sockets] == 3) # [tier] == tierscore(item)", + ]; + + const expansionTiers = [ + // Charms + "[name] == smallcharm && [quality] == magic # # [invoquantity] == 8 && [charmtier] == charmscore(item)", + "[name] == grandcharm && [quality] == magic # # [invoquantity] == 2 && [charmtier] == charmscore(item)", + ]; + + NTIP.buildList(levelingTiers); + me.expansion && NTIP.buildList(expansionTiers); + + /* Attack configuration. */ + Config.AttackSkill = [-1, 0, 0, 0, 0]; + Config.LowManaSkill = me.getSkill(sdk.skills.DoubleSwing, sdk.skills.subindex.SoftPoints) >= 9 ? [sdk.skills.DoubleSwing, 0] : [0, -1]; + Config.MaxAttackCount = 1000; + Config.BossPriority = me.normal; + Config.ClearType = 0; + Config.ClearPath = { Range: (Pather.canTeleport() ? 30 : 10), Spectype: 0 }; + + // Class specific config + Config.FindItem = true; // Use Find Item skill on corpses after clearing. + Config.FindItemSwitch = false; // Switch to non-primary slot when using Find Item skills + + Config.imbueables = [ + { name: sdk.items.AvengerGuard, condition: () => (me.normal && me.expansion) }, + { name: sdk.items.SlayerGuard, condition: () => (!me.normal && me.trueStr >= 118 && me.expansion) }, + { name: sdk.items.CarnageHelm, condition: () => (me.equipped.get(sdk.body.Head).tier < 100000 && me.trueStr >= 106 && me.expansion) }, + { name: sdk.items.Belt, condition: () => (me.normal && (me.equipped.get(sdk.body.Head).tier > 100000 || me.classic)) }, + { name: sdk.items.MeshBelt, condition: () => (!me.normal && me.charlvl < 46 && me.trueStr > 58 && (me.equipped.get(sdk.body.RightArm).tier > 100000 || me.classic)) }, + { name: sdk.items.SpiderwebSash, condition: () => (!me.normal && me.trueStr > 50 && (me.equipped.get(sdk.body.RightArm).tier > 100000 || me.classic)) }, + ].filter((item) => item.condition()); + + let imbueArr = SetUp.imbueItems(); + + !me.smith && NTIP.buildList(imbueArr); + + switch (me.gametype) { + case sdk.game.gametype.Classic: + break; + case sdk.game.gametype.Expansion: + NTIP.addLine("[name] >= VexRune && [name] <= ZodRune"); + const { basicSocketables, addSocketableObj } = require("../Utils/General"); + + Config.socketables = Config.socketables.concat(basicSocketables.all); + Config.socketables.push(addSocketableObj(sdk.items.Flamberge, [], [], + true, (item) => me.normal && me.equipped.get(sdk.body.LeftArm).tier < 600 && !Check.haveBase("sword", 5) && !me.checkItem({ name: sdk.locale.items.Honor }).have && item.ilvl >= 41 && item.isBaseType && !item.ethereal + )); + Config.socketables.push(addSocketableObj(sdk.items.Zweihander, [], [], + true, (item) => me.equipped.get(sdk.body.LeftArm).tier < 1000 && !Check.haveBase("sword", 5) && !me.checkItem({ name: sdk.locale.items.Honor }).have && item.ilvl >= 41 && item.isBaseType && !item.ethereal + )); + + if (SetUp.finalBuild !== "Immortalwhirl") { + Config.socketables.push(addSocketableObj(sdk.items.SlayerGuard, [sdk.items.runes.Cham], [sdk.items.gems.Perfect.Ruby], + true, (item) => item.unique && !item.ethereal + )); + } + + if (["Immortalwhirl", "Singer"].indexOf(SetUp.finalBuild) === -1) { + // Grief + if ((me.ladder || Developer.addLadderRW) && (!me.checkItem({ name: sdk.locale.items.Grief }).have || (SetUp.finalBuild === "Whirlwind" && me.equipped.get(sdk.body.LeftArm).prefixnum !== sdk.locale.items.Grief))) { + includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/Grief.js"); + } + + // Fortitude + if ((me.ladder || Developer.addLadderRW) && SetUp.finalBuild !== "Uberconc" && me.checkItem({ name: sdk.locale.items.Grief }).have && !me.checkItem({ name: sdk.locale.items.Fortitude, itemtype: sdk.items.type.Armor }).have) { + includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/Fortitude.js"); + } + + // Doom + if ((me.ladder || Developer.addLadderRW) && Item.getMercEquipped(sdk.body.RightArm).prefixnum !== 20532) { + includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/MercDoom.js"); + } + } + + // FinalBuild specific setup + switch (SetUp.finalBuild) { + case "Uberconc": + if (me.checkItem({ name: sdk.locale.items.Grief }).have && SetUp.finalBuild === "Uberconc") { + // Add Stormshield + NTIP.addLine("[name] == monarch && [quality] == unique && [flag] != ethereal # [damageresist] >= 35 # [tier] == 100000"); + } + + // Chains of Honor + if (!me.checkItem({ name: sdk.locale.items.ChainsofHonor }).have) { + includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/ChainsOfHonor.js"); + } + + break; + case "Frenzy": + // Breathe of the Dying + if (!me.checkItem({ name: sdk.locale.items.BreathoftheDying }).have) { + includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/BreathoftheDying.js"); + } + + break; + case "Singer": + // Heart of the Oak + if (me.equipped.get(sdk.body.LeftArm).prefixnum !== sdk.locale.items.HeartoftheOak && me.checkItem({ name: sdk.locale.items.Enigma }).have) { + includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/HeartOfTheOak.js"); + } + + // Enigma + if (!me.checkItem({ name: sdk.locale.items.Enigma }).have) { + includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/Enigma.js"); + } + + break; + case "Immortalwhirl": + // Infinity + if ((me.ladder || Developer.addLadderRW) && Item.getMercEquipped(sdk.body.RightArm).prefixnum !== sdk.locale.items.Infinity) { + includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/MercInfinity.js"); + } + + Config.socketables.push(addSocketableObj(sdk.items.AvengerGuard, [sdk.items.runes.Ber], [sdk.items.gems.Perfect.Ruby], + false, (item) => item.set && !item.ethereal + )); + Config.socketables.push(addSocketableObj(sdk.items.OgreMaul, [sdk.items.runes.Shael], [], + false, (item) => item.set && !item.ethereal + )); + Config.socketables.push(addSocketableObj(sdk.items.SacredArmor, [sdk.items.runes.Ber], [sdk.items.gems.Perfect.Ruby], + true, (item) => item.set && !item.ethereal + )); + + Check.itemSockables(sdk.items.OgreMaul, "set", "Immortal King's Stone Crusher"); + + break; + case "Whirlwind": + break; + default: + break; + } + + /* Crafting */ + if (me.equipped.get(sdk.body.Neck).tier < 100000) { + Check.currentBuild().caster ? Config.Recipes.push([Recipe.Caster.Amulet]) : Config.Recipes.push([Recipe.Blood.Amulet]); + } + + if (me.equipped.get(sdk.body.RingLeft).tier < 100000) { + Check.currentBuild().caster ? Config.Recipes.push([Recipe.Caster.Ring]) : Config.Recipes.push([Recipe.Blood.Ring]); + } + + if (me.equipped.get(sdk.body.LeftArm).tier < 1370) { + if (me.rawStrength >= 150 && me.rawDexterity >= 88) { + // Upgrade Bloodletter to Elite + Config.Recipes.push([Recipe.Unique.Weapon.ToElite, "Gladius", Roll.NonEth]); + } + + if (me.rawStrength >= 25 && me.rawDexterity >= 136) { + // Upgrade Ginther's Rift to Elite + Config.Recipes.push([Recipe.Unique.Weapon.ToElite, "dimensionalblade", Roll.Eth]); + } + + if (!Check.haveItem("falcata", "unique", "Bloodletter")) { + NTIP.addLine("[name] == PulRune # # [maxquantity] == 1"); + NTIP.addLine("[name] == perfectemerald # # [maxquantity] == 1"); + // Bloodletter + NTIP.addLine("[name] == gladius && [quality] == unique && [flag] != ethereal # [enhanceddamage] >= 140 && [ias] >= 20 # [maxquantity] == 1"); + // upped Bloodletter + NTIP.addLine("[name] == falcata && [quality] == unique && [flag] != ethereal # [enhanceddamage] >= 140 && [ias] >= 20 # [maxquantity] == 1"); + } + + if (!Check.haveItem("dimensionalblade", "unique", "Ginther's Rift")) { + NTIP.addLine("[name] == PulRune # # [maxquantity] == 1"); + NTIP.addLine("[name] == perfectemerald # # [maxquantity] == 1"); + + // Have Pul rune before looking for eth ginther's + if (me.getItem(sdk.items.runes.Pul)) { + // Eth Ginther's Rift + NTIP.addLine("[name] == dimensionalblade && [quality] == unique && [flag] == ethereal # [enhanceddamage] >= 100 && [ias] == 30 && [magicdamagereduction] >= 7 # [maxquantity] == 1"); + } + + // upped Ginther's Rift + NTIP.addLine("[name] == phaseblade && [quality] == unique && [flag] == ethereal # [enhanceddamage] >= 100 && [ias] == 30 && [magicdamagereduction] >= 7 # [maxquantity] == 1"); + } + } + + // Lawbringer - Amn/Lem/Ko + if (me.equipped.get(sdk.body.LeftArm).tier < 1370) { + includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/Lawbringer.js"); + } + + // Voice Of Reason - Lem/Ko/El/Eld + if (me.equipped.get(sdk.body.RightArm).tier > 1100 && me.equipped.get(sdk.body.LeftArm).tier < 1270 && !Check.haveItem("ring", "unique", "Raven Frost")) { + includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/VoiceOfReason.js"); + } + + // Crescent Moon - Shael/Um/Tir + if (me.equipped.get(sdk.body.LeftArm).tier < 1100) { + includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/CrescentMoon.js"); + } + + if (me.equipped.get(sdk.body.LeftArm).tier < 1200) { + // Cube to Ko Rune + if (!me.getItem(sdk.items.runes.Ko)) { + Config.Recipes.push([Recipe.Rune, "Hel Rune"]); + Config.Recipes.push([Recipe.Rune, "Io Rune"]); + Config.Recipes.push([Recipe.Rune, "Lum Rune"]); + } + + // Cube to Lem Rune + if (!me.getItem(sdk.items.runes.Lem)) { + Config.Recipes.push([Recipe.Rune, "Dol Rune"]); + Config.Recipes.push([Recipe.Rune, "Io Rune"]); + Config.Recipes.push([Recipe.Rune, "Lum Rune"]); + Config.Recipes.push([Recipe.Rune, "Ko Rune"]); + Config.Recipes.push([Recipe.Rune, "Fal Rune"]); + } + } + + // Honor - Amn/El/Ith/Tir/Sol + if (me.equipped.get(sdk.body.LeftArm).tier < 1050) { + includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/Honor.js"); + } + + // Merc Insight + if ((me.ladder || Developer.addLadderRW) && Item.getMercEquipped(sdk.body.RightArm).tier < 3600) { + includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/MercInsight.js"); + } + + // Lore + if (me.equipped.get(sdk.body.Head).tier < 100000) { + includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/Lore.js"); + } + + // Merc Fortitude + if ((me.ladder || Developer.addLadderRW) && Item.getMercEquipped(sdk.body.Armor).prefixnum !== sdk.locale.items.Fortitude) { + includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/MercFortitude.js"); + } + + // Merc Treachery + if (Item.getMercEquipped(sdk.body.Armor).tier < 15000 && me.equipped.get(sdk.body.RightArm).tier > 1100) { + includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/MercTreachery.js"); + } + + // Treachery + if (me.equipped.get(sdk.body.Armor).tier < 634 && me.equipped.get(sdk.body.RightArm).tier > 1100) { + includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/Treachery.js"); + } + + // Smoke + if (me.equipped.get(sdk.body.Armor).tier < 350) { + includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/Smoke.js"); + } + + // Duress + if (me.equipped.get(sdk.body.Armor).tier < 600 && (me.checkItem({ name: sdk.locale.items.CrescentMoon }).have || me.equipped.get(sdk.body.LeftArm).tier > 900)) { + includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/Duress.js"); + } + + // Myth + if (me.equipped.get(sdk.body.Armor).tier < 340) { + includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/Myth.js"); + } + + // Kings Grace - Amn/Ral/Thul + if (me.equipped.get(sdk.body.LeftArm).tier < 770) { + includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/KingsGrace.js"); + } + + // Steel - Tir/El + if (me.equipped.get(sdk.body.LeftArm).tier < 500) { + includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/Steel.js"); + } + + // Spirit Sword + if ((me.ladder || Developer.addLadderRW) && me.equipped.get(sdk.body.LeftArmSecondary).prefixnum !== sdk.locale.items.Spirit) { + includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/SpiritSword.js"); + } + + // Malice - IthElEth + if (me.equipped.get(sdk.body.LeftArm).tier < 175) { + includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/Malice.js"); + } + + // Stealth + if (me.equipped.get(sdk.body.Armor).tier < 233) { + includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/Stealth.js"); + } + + /*if (me.equipped.get(sdk.body.Gloves).tier < 233) { + NTIP.addLine("[name] == heavygloves && [flag] != ethereal && [quality] == magic # [itemchargedskill] >= 0 # [maxquantity] == 1"); + Config.Recipes.push([Recipe.Blood.Gloves, "Heavy Gloves"]); // Craft Blood Gloves + }*/ + + Check.itemSockables(sdk.items.Ataghan, "unique", "Djinn Slayer"); + SoloWants.buildList(); + + break; + } + + return true; })(); diff --git a/libs/SoloPlay/Config/Druid.js b/libs/SoloPlay/Config/Druid.js index a84e7f04..7c7a1d97 100644 --- a/libs/SoloPlay/Config/Druid.js +++ b/libs/SoloPlay/Config/Druid.js @@ -17,232 +17,232 @@ */ (function LoadConfig () { - includeIfNotIncluded("SoloPlay/Functions/MiscOverrides.js"); - includeIfNotIncluded("SoloPlay/Functions/Globals.js"); - - SetUp.include(); - SetUp.config(); - - /* Pickit configuration. */ - Config.PickRange = 40; - // Config.PickitFiles.push("kolton.nip"); - // Config.PickitFiles.push("LLD.nip"); - NTIP.addLine("[name] >= VexRune && [name] <= ZodRune"); - - /* Gambling configuration. */ - Config.GambleItems.push("amulet"); - Config.GambleItems.push("ring"); - Config.GambleItems.push("circlet"); - Config.GambleItems.push("coronet"); - - // AutoEquip setup - const levelingTiers = [ - // Weapon - "([type] == wand || [type] == sword || [type] == mace || [type] == knife) && ([quality] >= magic || [flag] == runeword) && [flag] != ethereal && [2handed] == 0 # [itemchargedskill] >= 0 # [tier] == tierscore(item)", - // Helmet - "([type] == pelt) && ([quality] >= magic || [flag] == runeword) && [flag] != ethereal # [itemchargedskill] >= 0 # [tier] == tierscore(item)", - "([type] == pelt) && [quality] >= normal && [flag] != ethereal # [itemchargedskill] >= 0 && ([sockets] == 1 || [sockets] == 3) # [tier] == tierscore(item)", - // Shield - "[type] == shield && ([quality] >= magic || [flag] == runeword) && [flag] != ethereal # [itemchargedskill] >= 0 # [tier] == tierscore(item)", - // Charms - "[name] == smallcharm && [quality] == magic # [maxhp] >= 1 # [invoquantity] == 2 && [charmtier] == charmscore(item)", - "[name] == smallcharm && [quality] == magic # [itemmagicbonus] >= 1 # [invoquantity] == 2 && [charmtier] == charmscore(item)", - "[name] == smallcharm && [quality] == magic # # [invoquantity] == 2 && [charmtier] == charmscore(item)", - "[name] == grandcharm && [quality] == magic # # [invoquantity] == 2 && [charmtier] == charmscore(item)", - ]; - - NTIP.buildList(levelingTiers); - - let switchTiers = (["Wolf", "Plaguewolf"].includes(SetUp.currentBuild) - ? [ - "[name] == elderstaff && [quality] == unique # [itemallskills] >= 2 # [secondarytier] == tierscore(item)", // Ondal's - "[name] == archonstaff && [quality] == unique # [itemallskills] == 5 # [secondarytier] == tierscore(item)" // Mang Song's - ] - : [ - "[type] == wand && [quality] >= normal # [itemchargedskill] == 72 # [secondarytier] == 25000", // Weaken charged wand - "[name] == beardedaxe && [quality] == unique # [itemchargedskill] == 87 # [secondarytier] == 50000", // Spellsteel Decrepify charged axe - ]); - - NTIP.buildList(switchTiers); - - /* Attack configuration. */ - Config.AttackSkill = [0, 0, 0, 0, 0, 0, 0]; - Config.LowManaSkill = [0, 0]; - Config.MaxAttackCount = 1000; - Config.BossPriority = me.normal; - Config.ClearType = 0; - Config.ClearPath = { Range: (Pather.canTeleport() ? 30 : 10), Spectype: 0 }; - - /* Class specific configuration. */ - /* Wereform */ - Config.Wereform = false; // 0 / false - don't shapeshift, 1 / "Werewolf" - change to werewolf, 2 / "Werebear" - change to werebear - - /* Summons */ - Config.SummonRaven = false; - Config.SummonVine = 0; // 0 = disabled, 1 / "Poison Creeper", 2 / "Carrion Vine", 3 / "Solar Creeper" - Config.SummonSpirit = 0; // 0 = disabled, 1 / "Oak Sage", 2 / "Heart of Wolverine", 3 / "Spirit of Barbs" - Config.SummonAnimal = 0; // 0 = disabled, 1 or "Spirit Wolf" = summon spirit wolf, 2 or "Dire Wolf" = summon dire wolf, 3 or "Grizzly" = summon grizzly - - Config.imbueables = [ - { name: sdk.items.SpiritMask, condition: () => (me.normal) }, - { name: sdk.items.TotemicMask, condition: () => (!me.normal && me.equipped.get(sdk.body.Head).tier < 100000 && (me.charlvl < 66 || me.trueStr < 118)) }, - { name: sdk.items.DreamSpirit, condition: () => (me.equipped.get(sdk.body.Head).tier < 100000 && me.trueStr >= 118) }, - { name: sdk.items.Belt, condition: () => (me.normal && (me.equipped.get(sdk.body.Head).tier > 100000)) }, - { name: sdk.items.MeshBelt, condition: () => (!me.normal && me.charlvl < 46 && me.trueStr > 58 && (me.equipped.get(sdk.body.Head).tier > 100000)) }, - { name: sdk.items.SpiderwebSash, condition: () => (!me.normal && me.trueStr > 50 && (me.equipped.get(sdk.body.Head).tier > 100000)) }, - ].filter((item) => item.condition()); - - let imbueArr = SetUp.imbueItems(); - - !me.smith && NTIP.buildList(imbueArr); - - const { basicSocketables, addSocketableObj } = require("../Utils/General"); - - Config.socketables = Config.socketables.concat(basicSocketables.caster, basicSocketables.all); - Config.socketables.push(addSocketableObj(sdk.items.Monarch, [], [], - !me.hell, (item) => !Check.haveBase("monarch", 4) && item.ilvl >= 41 && item.isBaseType && !item.ethereal - )); - Config.socketables.push(addSocketableObj(sdk.items.TotemicMask, [sdk.items.runes.Um], [sdk.items.gems.Perfect.Ruby], - true, (item) => item.unique && !item.ethereal - )); - Config.socketables.push(addSocketableObj(sdk.items.Shako, [sdk.items.runes.Um], [sdk.items.gems.Perfect.Ruby], - true, (item) => item.unique && !item.ethereal - )); - - if (me.checkItem({ name: sdk.locale.items.CalltoArms }).have) { - // Spirit on swap - NTIP.addLine("[name] == monarch && [flag] == runeword # [fcr] >= 25 && [maxmana] >= 89 # [secondarytier] == 110000"); - } - - /* Crafting */ - if (me.equipped.get(sdk.body.Neck).tier < 100000) { - Check.currentBuild().caster ? Config.Recipes.push([Recipe.Caster.Amulet]) : Config.Recipes.push([Recipe.Blood.Amulet]); - } - - if (me.equipped.get(sdk.body.RingLeft).tier < 100000) { - Check.currentBuild().caster ? Config.Recipes.push([Recipe.Caster.Ring]) : Config.Recipes.push([Recipe.Blood.Ring]); - } - - // FinalBuild specific setup - switch (SetUp.finalBuild) { - case "Wind": - case "Elemental": - // Call to Arms - if (!me.checkItem({ name: sdk.locale.items.CalltoArms }).have) { - includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/CallToArms.js"); - } - - if (SetUp.finalBuild === "Elemental") { - Config.socketables.push(addSocketableObj(sdk.items.SkySpirit, [sdk.items.runes.Um], [sdk.items.gems.Perfect.Ruby], - true, (item) => item.unique && item.getStat(sdk.stats.PassiveFirePierce) === 20 && !item.ethereal - )); - - // Phoenix Shield - if ((me.ladder || Developer.addLadderRW) && SetUp.finalBuild === "Elemental" && me.checkItem({ name: sdk.locale.items.Enigma }).have && !me.checkItem({ name: sdk.locale.items.Phoenix, itemtype: sdk.items.type.Shield }).have) { - includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/PhoneixShield.js"); - } - } - - // Heart of the Oak - if (!me.checkItem({ name: sdk.locale.items.HeartoftheOak }).have) { - includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/HeartOfTheOak.js"); - } - - // Enigma - if (!me.checkItem({ name: sdk.locale.items.Enigma }).have) { - includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/Enigma.js"); - } - - // upgrade magefist - if (me.equipped.get(sdk.body.Gloves).tier < 110000) { - Config.Recipes.push([Recipe.Unique.Armor.ToExceptional, "Light Gauntlets", Roll.NonEth]); - Config.Recipes.push([Recipe.Unique.Armor.ToElite, "Battle Gauntlets", Roll.NonEth, "magefist"]); - } - - break; - case "Wolf": - case "Plaguewolf": - // Chains of Honor - if (!me.checkItem({ name: sdk.locale.items.ChainsofHonor }).have) { - includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/ChainsOfHonor.js"); - } - - if (SetUp.finalBuild === "Plaguewolf") { - // Grief - if (!me.checkItem({ name: sdk.locale.items.Grief }).have) { - includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/Grief.js"); - } - } else { - // Make sure to have CoH first - if (me.checkItem({ name: sdk.locale.items.ChainsofHonor }).have) { - // Upgrade Ribcracker to Elite - Config.Recipes.push([Recipe.Unique.Weapon.ToElite, "quarterstaff", Roll.NonEth]); - } - - // Don't have upgraded Ribcracker - if (!Check.haveItem("stalagmite", "unique", "Ribcracker")) { - // Perfect ribcracker - NTIP.addLine("[name] == quarterstaff && [quality] == unique # [enhanceddamage] == 300 && [ias] >= 50 # [maxquantity] == 1"); - // Perfect upped ribcracker - NTIP.addLine("[name] == stalagmite && [quality] == unique # [enhanceddamage] == 300 && [ias] >= 50 # [maxquantity] == 1"); - } - } - - break; - default: - break; - } - - Check.itemSockables(sdk.items.RoundShield, "unique", "Moser's Blessed Circle"); - Check.itemSockables(sdk.items.Shako, "unique", "Harlequin Crest"); - Check.itemSockables(sdk.items.TotemicMask, "unique", "Jalal's Mane"); - - // Spirit Sword - if ((me.ladder || Developer.addLadderRW) && me.equipped.get(sdk.body.RightArm).tier < 777) { - includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/SpiritSword.js"); - } - - // Spirit Shield - if ((me.ladder || Developer.addLadderRW) && (me.equipped.get(sdk.body.LeftArm).tier < 1000 || me.equipped.get(sdk.body.LeftArmSecondary).prefixnum !== sdk.locale.items.Spirit)) { - includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/SpiritShield.js"); - } - - // Merc Insight - if ((me.ladder || Developer.addLadderRW) && Item.getMercEquipped(sdk.body.RightArm).tier < 3600) { - includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/MercInsight.js"); - } - - // Lore - if (me.equipped.get(sdk.body.Head).tier < 100000) { - includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/Lore.js"); - } - - // Ancients' Pledge - if (me.equipped.get(sdk.body.LeftArm).tier < 500) { - includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/AncientsPledge.js"); - } - - // Merc Fortitude - if (Item.getMercEquipped(sdk.body.Armor).prefixnum !== sdk.locale.items.Fortitude) { - includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/MercFortitude.js"); - } - - // Merc Treachery - if (Item.getMercEquipped(sdk.body.Armor).tier < 15000) { - includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/MercTreachery.js"); - } - - // Smoke - if (me.equipped.get(sdk.body.Armor).tier < 634) { - includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/Smoke.js"); - } - - // Stealth - if (me.equipped.get(sdk.body.Armor).tier < 233) { - includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/Stealth.js"); - } - - SoloWants.buildList(); - - return true; + includeIfNotIncluded("SoloPlay/Functions/MiscOverrides.js"); + includeIfNotIncluded("SoloPlay/Functions/Globals.js"); + + SetUp.include(); + SetUp.config(); + + /* Pickit configuration. */ + Config.PickRange = 40; + // Config.PickitFiles.push("kolton.nip"); + // Config.PickitFiles.push("LLD.nip"); + NTIP.addLine("[name] >= VexRune && [name] <= ZodRune"); + + /* Gambling configuration. */ + Config.GambleItems.push("amulet"); + Config.GambleItems.push("ring"); + Config.GambleItems.push("circlet"); + Config.GambleItems.push("coronet"); + + // AutoEquip setup + const levelingTiers = [ + // Weapon + "([type] == wand || [type] == sword || [type] == mace || [type] == knife) && ([quality] >= magic || [flag] == runeword) && [flag] != ethereal && [2handed] == 0 # [itemchargedskill] >= 0 # [tier] == tierscore(item)", + // Helmet + "([type] == pelt) && ([quality] >= magic || [flag] == runeword) && [flag] != ethereal # [itemchargedskill] >= 0 # [tier] == tierscore(item)", + "([type] == pelt) && [quality] >= normal && [flag] != ethereal # [itemchargedskill] >= 0 && ([sockets] == 1 || [sockets] == 3) # [tier] == tierscore(item)", + // Shield + "[type] == shield && ([quality] >= magic || [flag] == runeword) && [flag] != ethereal # [itemchargedskill] >= 0 # [tier] == tierscore(item)", + // Charms + "[name] == smallcharm && [quality] == magic # [maxhp] >= 1 # [invoquantity] == 2 && [charmtier] == charmscore(item)", + "[name] == smallcharm && [quality] == magic # [itemmagicbonus] >= 1 # [invoquantity] == 2 && [charmtier] == charmscore(item)", + "[name] == smallcharm && [quality] == magic # # [invoquantity] == 2 && [charmtier] == charmscore(item)", + "[name] == grandcharm && [quality] == magic # # [invoquantity] == 2 && [charmtier] == charmscore(item)", + ]; + + NTIP.buildList(levelingTiers); + + let switchTiers = (["Wolf", "Plaguewolf"].includes(SetUp.currentBuild) + ? [ + "[name] == elderstaff && [quality] == unique # [itemallskills] >= 2 # [secondarytier] == tierscore(item)", // Ondal's + "[name] == archonstaff && [quality] == unique # [itemallskills] == 5 # [secondarytier] == tierscore(item)" // Mang Song's + ] + : [ + "[type] == wand && [quality] >= normal # [itemchargedskill] == 72 # [secondarytier] == 25000", // Weaken charged wand + "[name] == beardedaxe && [quality] == unique # [itemchargedskill] == 87 # [secondarytier] == 50000", // Spellsteel Decrepify charged axe + ]); + + NTIP.buildList(switchTiers); + + /* Attack configuration. */ + Config.AttackSkill = [0, 0, 0, 0, 0, 0, 0]; + Config.LowManaSkill = [0, 0]; + Config.MaxAttackCount = 1000; + Config.BossPriority = me.normal; + Config.ClearType = 0; + Config.ClearPath = { Range: (Pather.canTeleport() ? 30 : 10), Spectype: 0 }; + + /* Class specific configuration. */ + /* Wereform */ + Config.Wereform = false; // 0 / false - don't shapeshift, 1 / "Werewolf" - change to werewolf, 2 / "Werebear" - change to werebear + + /* Summons */ + Config.SummonRaven = false; + Config.SummonVine = 0; // 0 = disabled, 1 / "Poison Creeper", 2 / "Carrion Vine", 3 / "Solar Creeper" + Config.SummonSpirit = 0; // 0 = disabled, 1 / "Oak Sage", 2 / "Heart of Wolverine", 3 / "Spirit of Barbs" + Config.SummonAnimal = 0; // 0 = disabled, 1 or "Spirit Wolf" = summon spirit wolf, 2 or "Dire Wolf" = summon dire wolf, 3 or "Grizzly" = summon grizzly + + Config.imbueables = [ + { name: sdk.items.SpiritMask, condition: () => (me.normal) }, + { name: sdk.items.TotemicMask, condition: () => (!me.normal && me.equipped.get(sdk.body.Head).tier < 100000 && (me.charlvl < 66 || me.trueStr < 118)) }, + { name: sdk.items.DreamSpirit, condition: () => (me.equipped.get(sdk.body.Head).tier < 100000 && me.trueStr >= 118) }, + { name: sdk.items.Belt, condition: () => (me.normal && (me.equipped.get(sdk.body.Head).tier > 100000)) }, + { name: sdk.items.MeshBelt, condition: () => (!me.normal && me.charlvl < 46 && me.trueStr > 58 && (me.equipped.get(sdk.body.Head).tier > 100000)) }, + { name: sdk.items.SpiderwebSash, condition: () => (!me.normal && me.trueStr > 50 && (me.equipped.get(sdk.body.Head).tier > 100000)) }, + ].filter((item) => item.condition()); + + let imbueArr = SetUp.imbueItems(); + + !me.smith && NTIP.buildList(imbueArr); + + const { basicSocketables, addSocketableObj } = require("../Utils/General"); + + Config.socketables = Config.socketables.concat(basicSocketables.caster, basicSocketables.all); + Config.socketables.push(addSocketableObj(sdk.items.Monarch, [], [], + !me.hell, (item) => !Check.haveBase("monarch", 4) && item.ilvl >= 41 && item.isBaseType && !item.ethereal + )); + Config.socketables.push(addSocketableObj(sdk.items.TotemicMask, [sdk.items.runes.Um], [sdk.items.gems.Perfect.Ruby], + true, (item) => item.unique && !item.ethereal + )); + Config.socketables.push(addSocketableObj(sdk.items.Shako, [sdk.items.runes.Um], [sdk.items.gems.Perfect.Ruby], + true, (item) => item.unique && !item.ethereal + )); + + if (me.checkItem({ name: sdk.locale.items.CalltoArms }).have) { + // Spirit on swap + NTIP.addLine("[name] == monarch && [flag] == runeword # [fcr] >= 25 && [maxmana] >= 89 # [secondarytier] == 110000"); + } + + /* Crafting */ + if (me.equipped.get(sdk.body.Neck).tier < 100000) { + Check.currentBuild().caster ? Config.Recipes.push([Recipe.Caster.Amulet]) : Config.Recipes.push([Recipe.Blood.Amulet]); + } + + if (me.equipped.get(sdk.body.RingLeft).tier < 100000) { + Check.currentBuild().caster ? Config.Recipes.push([Recipe.Caster.Ring]) : Config.Recipes.push([Recipe.Blood.Ring]); + } + + // FinalBuild specific setup + switch (SetUp.finalBuild) { + case "Wind": + case "Elemental": + // Call to Arms + if (!me.checkItem({ name: sdk.locale.items.CalltoArms }).have) { + includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/CallToArms.js"); + } + + if (SetUp.finalBuild === "Elemental") { + Config.socketables.push(addSocketableObj(sdk.items.SkySpirit, [sdk.items.runes.Um], [sdk.items.gems.Perfect.Ruby], + true, (item) => item.unique && item.getStat(sdk.stats.PassiveFirePierce) === 20 && !item.ethereal + )); + + // Phoenix Shield + if ((me.ladder || Developer.addLadderRW) && SetUp.finalBuild === "Elemental" && me.checkItem({ name: sdk.locale.items.Enigma }).have && !me.checkItem({ name: sdk.locale.items.Phoenix, itemtype: sdk.items.type.Shield }).have) { + includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/PhoneixShield.js"); + } + } + + // Heart of the Oak + if (!me.checkItem({ name: sdk.locale.items.HeartoftheOak }).have) { + includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/HeartOfTheOak.js"); + } + + // Enigma + if (!me.checkItem({ name: sdk.locale.items.Enigma }).have) { + includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/Enigma.js"); + } + + // upgrade magefist + if (me.equipped.get(sdk.body.Gloves).tier < 110000) { + Config.Recipes.push([Recipe.Unique.Armor.ToExceptional, "Light Gauntlets", Roll.NonEth]); + Config.Recipes.push([Recipe.Unique.Armor.ToElite, "Battle Gauntlets", Roll.NonEth, "magefist"]); + } + + break; + case "Wolf": + case "Plaguewolf": + // Chains of Honor + if (!me.checkItem({ name: sdk.locale.items.ChainsofHonor }).have) { + includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/ChainsOfHonor.js"); + } + + if (SetUp.finalBuild === "Plaguewolf") { + // Grief + if (!me.checkItem({ name: sdk.locale.items.Grief }).have) { + includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/Grief.js"); + } + } else { + // Make sure to have CoH first + if (me.checkItem({ name: sdk.locale.items.ChainsofHonor }).have) { + // Upgrade Ribcracker to Elite + Config.Recipes.push([Recipe.Unique.Weapon.ToElite, "quarterstaff", Roll.NonEth]); + } + + // Don't have upgraded Ribcracker + if (!Check.haveItem("stalagmite", "unique", "Ribcracker")) { + // Perfect ribcracker + NTIP.addLine("[name] == quarterstaff && [quality] == unique # [enhanceddamage] == 300 && [ias] >= 50 # [maxquantity] == 1"); + // Perfect upped ribcracker + NTIP.addLine("[name] == stalagmite && [quality] == unique # [enhanceddamage] == 300 && [ias] >= 50 # [maxquantity] == 1"); + } + } + + break; + default: + break; + } + + Check.itemSockables(sdk.items.RoundShield, "unique", "Moser's Blessed Circle"); + Check.itemSockables(sdk.items.Shako, "unique", "Harlequin Crest"); + Check.itemSockables(sdk.items.TotemicMask, "unique", "Jalal's Mane"); + + // Spirit Sword + if ((me.ladder || Developer.addLadderRW) && me.equipped.get(sdk.body.RightArm).tier < 777) { + includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/SpiritSword.js"); + } + + // Spirit Shield + if ((me.ladder || Developer.addLadderRW) && (me.equipped.get(sdk.body.LeftArm).tier < 1000 || me.equipped.get(sdk.body.LeftArmSecondary).prefixnum !== sdk.locale.items.Spirit)) { + includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/SpiritShield.js"); + } + + // Merc Insight + if ((me.ladder || Developer.addLadderRW) && Item.getMercEquipped(sdk.body.RightArm).tier < 3600) { + includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/MercInsight.js"); + } + + // Lore + if (me.equipped.get(sdk.body.Head).tier < 100000) { + includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/Lore.js"); + } + + // Ancients' Pledge + if (me.equipped.get(sdk.body.LeftArm).tier < 500) { + includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/AncientsPledge.js"); + } + + // Merc Fortitude + if (Item.getMercEquipped(sdk.body.Armor).prefixnum !== sdk.locale.items.Fortitude) { + includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/MercFortitude.js"); + } + + // Merc Treachery + if (Item.getMercEquipped(sdk.body.Armor).tier < 15000) { + includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/MercTreachery.js"); + } + + // Smoke + if (me.equipped.get(sdk.body.Armor).tier < 634) { + includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/Smoke.js"); + } + + // Stealth + if (me.equipped.get(sdk.body.Armor).tier < 233) { + includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/Stealth.js"); + } + + SoloWants.buildList(); + + return true; })(); diff --git a/libs/SoloPlay/Config/Necromancer.js b/libs/SoloPlay/Config/Necromancer.js index de3daeed..0c97118d 100644 --- a/libs/SoloPlay/Config/Necromancer.js +++ b/libs/SoloPlay/Config/Necromancer.js @@ -16,209 +16,209 @@ */ (function LoadConfig () { - includeIfNotIncluded("SoloPlay/Functions/MiscOverrides.js"); - includeIfNotIncluded("SoloPlay/Functions/Globals.js"); - - SetUp.include(); - SetUp.config(); - - /* Necro specific Chicken configuration. */ - Config.IronGolemChicken = 30; - - /* Pickit configuration. */ - Config.PickRange = 40; - // Config.PickitFiles.push("kolton.nip"); - // Config.PickitFiles.push("LLD.nip"); - - /* Gambling configuration. */ - Config.GambleItems.push("Amulet"); - Config.GambleItems.push("Ring"); - Config.GambleItems.push("Circlet"); - Config.GambleItems.push("Coronet"); - - // AutoEquip setup - const levelingTiers = [ - // Weapon - "([type] == wand || [type] == sword || [type] == knife) && ([quality] >= magic || [flag] == runeword) && [flag] != ethereal && [2handed] == 0 # [itemchargedskill] >= 0 # [tier] == tierscore(item)", - "[type] == wand && [quality] >= normal && [flag] != ethereal && [2handed] == 0 # [itemchargedskill] >= 0 && [sockets] != 2 # [tier] == tierscore(item)", - // Shield - "([type] == shield && ([quality] >= magic || [flag] == runeword) || [type] == voodooheads) && [flag] != ethereal # [itemchargedskill] >= 0 # [tier] == tierscore(item)", - "[type] == voodooheads && [quality] >= normal && [flag] != ethereal # [itemchargedskill] >= 0 && [sockets] == 1 # [tier] == tierscore(item)", - "me.classic && [type] == shield && [quality] >= normal # [itemchargedskill] >= 0 # [tier] == tierscore(item)", - // non runeword white items - "([type] == shield) && [quality] >= normal && [flag] != ethereal && [flag] != runeword # [itemchargedskill] >= 0 && ([sockets] == 1 || [sockets] == 2) # [tier] == tierscore(item)", - ]; - - const expansionTiers = [ - "me.charlvl < 33 && [name] == smallcharm && [quality] == magic # [maxmana] >= 1 # [invoquantity] == 4 && [charmtier] == charmscore(item)", - "[name] == smallcharm && [quality] == magic # [maxhp] >= 1 # [invoquantity] == 2 && [charmtier] == charmscore(item)", - "[name] == smallcharm && [quality] == magic # [itemmagicbonus] >= 1 # [invoquantity] == 2 && [charmtier] == charmscore(item)", - "[name] == smallcharm && [quality] == magic # [fireresist]+[lightresist]+[coldresist]+[poisonresist] >= 1 # [invoquantity] == 2 && [charmtier] == charmscore(item)", - "[name] == grandcharm && [quality] == magic # # [invoquantity] == 2 && [charmtier] == charmscore(item)", - ]; - - NTIP.buildList(levelingTiers); - me.expansion && NTIP.buildList(expansionTiers); - - /* Attack configuration. */ - Skill.usePvpRange = true; - Config.AttackSkill = [0, 0, 0, 0, 0, 0, 0]; - Config.LowManaSkill = [0, 0]; - Config.MaxAttackCount = 1000; - Config.BossPriority = me.normal; - Config.ClearType = 0; - Config.ClearPath = { Range: (Pather.canTeleport() ? 30 : 20), Spectype: 0 }; - - /* Class specific configuration. */ - Config.Dodge = Check.haveItem("armor", "runeword", "Enigma"); - Config.DodgeRange = Check.haveItem("armor", "runeword", "Enigma") ? 10 : 5; - Config.DodgeHP = 90; // Dodge only if HP percent is less than or equal to Config.DodgeHP. 100 = always dodge. - - /* Summons. */ - Config.ReviveUnstackable = true; - Config.ActiveSummon = me.charlvl < 10 || SetUp.currentBuild === "Summon"; - Config.Golem = me.checkSkill(sdk.skills.ClayGolem, sdk.skills.subindex.HardPoints) ? "Clay" : "None"; - Config.Skeletons = (me.charlvl > 10 && SetUp.currentBuild !== "Summon") ? 0 : "max"; - Config.SkeletonMages = (me.charlvl > 10 && SetUp.currentBuild !== "Summon") ? 0 : "max"; - Config.Revives = (me.charlvl > 10 && SetUp.currentBuild !== "Summon") ? 0 : "max"; - - /* Skill Specific */ - Config.PoisonNovaDelay = 1; // In Seconds - Config.ExplodeCorpses = me.checkSkill(sdk.skills.CorpseExplosion, sdk.skills.subindex.HardPoints) - ? sdk.skills.CorpseExplosion - : me.checkSkill(sdk.skills.PoisonExplosion, sdk.skills.subindex.HardPoints) - ? sdk.skills.PoisonExplosion - : 0; - - Config.imbueables = [ - { name: sdk.items.DemonHead, condition: () => (me.normal && me.expansion) }, - { name: sdk.items.HierophantTrophy, condition: () => (!me.normal && (me.charlvl < 66 || me.trueStr < 106) && me.expansion) }, - { name: sdk.items.BloodlordSkull, condition: () => (me.equipped.get(sdk.body.LeftArm).tier < 1000 && me.expansion) }, - { name: sdk.items.Belt, condition: () => (me.normal && (me.equipped.get(sdk.body.LeftArm).tier > 1000 || me.classic)) }, - { name: sdk.items.MeshBelt, condition: () => (!me.normal && me.charlvl < 46 && me.trueStr > 58 && (me.equipped.get(sdk.body.LeftArm).tier > 1000 || me.classic)) }, - { name: sdk.items.SpiderwebSash, condition: () => (!me.normal && me.trueStr > 50 && (me.equipped.get(sdk.body.LeftArm).tier > 1000 || me.classic)) }, - ].filter((item) => item.condition()); - - let imbueArr = SetUp.imbueItems(); - - !me.smith && NTIP.buildList(imbueArr); - - switch (me.gametype) { - case sdk.game.gametype.Classic: - // Res shield - if (me.equipped.get(sdk.body.LeftArm).tier < 487) { - includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/PDiamondShield.js"); - } - - break; - case sdk.game.gametype.Expansion: - NTIP.addLine("[name] >= VexRune && [name] <= ZodRune"); - const { basicSocketables, addSocketableObj } = require("../Utils/General"); - - Config.socketables = Config.socketables.concat(basicSocketables.caster, basicSocketables.all); - Config.socketables.push(addSocketableObj(sdk.items.Monarch, [], [], - !me.hell, (item) => !Check.haveBase("monarch", 4) && item.ilvl >= 41 && item.isBaseType && !item.ethereal - )); - Config.socketables.push(addSocketableObj(sdk.items.Shako, [sdk.items.runes.Um], [sdk.items.gems.Perfect.Ruby], - true, (item) => item.unique && !item.ethereal - )); - - /* Crafting */ - if (me.equipped.get(sdk.body.Neck).tier < 100000) { - Config.Recipes.push([Recipe.Caster.Amulet]); - } - - // upgrade magefist - if (me.equipped.get(sdk.body.Gloves).tier < 110000) { - Config.Recipes.push([Recipe.Unique.Armor.ToExceptional, "Light Gauntlets", Roll.NonEth]); - Config.Recipes.push([Recipe.Unique.Armor.ToElite, "Battle Gauntlets", Roll.NonEth, "magefist"]); - } - - Check.itemSockables(sdk.items.RoundShield, "unique", "Moser's Blessed Circle"); - Check.itemSockables(sdk.items.Shako, "unique", "Harlequin Crest"); - - /* Crafting */ - if (me.equipped.get(sdk.body.Neck).tier < 100000) { - Config.Recipes.push([Recipe.Caster.Amulet]); - } - - if (me.equipped.get(sdk.body.RingLeft).tier < 100000) { - Config.Recipes.push([Recipe.Caster.Ring]); - } - - // Call to Arms - if (!me.checkItem({ name: sdk.locale.items.CalltoArms }).have) { - includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/CallToArms.js"); - } - - // White - if (SetUp.currentBuild !== "Summon") { - includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/White.js"); - } - - // Rhyme - if (me.equipped.get(sdk.body.LeftArm).tier < 650) { - includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/Rhyme.js"); - } - - // Enigma - if (!me.checkItem({ name: sdk.locale.items.Enigma }).have) { - includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/Enigma.js"); - } - - // Spirit Sword - if ((me.ladder || Developer.addLadderRW) && me.equipped.get(sdk.body.RightArm).tier < 777) { - includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/SpiritSword.js"); - } - - // Spirit shield - if ((me.ladder || Developer.addLadderRW) && (me.equipped.get(sdk.body.LeftArm).tier < 1000 || me.equipped.get(sdk.body.LeftArmSecondary).prefixnum !== sdk.locale.items.Spirit)) { - includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/SpiritShield.js"); - } - - // Merc Insight - if ((me.ladder || Developer.addLadderRW) && Item.getMercEquipped(sdk.body.RightArm).tier < 3600) { - includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/MercInsight.js"); - } - - if (!me.haveSome([{ name: sdk.locale.items.Enigma }, { name: sdk.locale.items.Bone }]) && me.equipped.get(sdk.body.Armor).tier < 650) { - includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/Bone.js"); - } - - // Lore - if (me.equipped.get(sdk.body.Head).tier < 315) { - includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/Lore.js"); - } - - // Ancients' Pledge - if (me.equipped.get(sdk.body.LeftArm).tier < 500) { - includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/AncientsPledge.js"); - } - - // Merc Fortitude - if (Item.getMercEquipped(sdk.body.Armor).prefixnum !== sdk.locale.items.Fortitude) { - includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/MercFortitude.js"); - } - - // Merc Treachery - if (Item.getMercEquipped(sdk.body.Armor).tier < 15000) { - includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/MercTreachery.js"); - } - - // Smoke - if (me.equipped.get(sdk.body.Armor).tier < 450) { - includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/Smoke.js"); - } - - // Stealth - if (me.equipped.get(sdk.body.Armor).tier < 233) { - includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/Stealth.js"); - } - - SoloWants.buildList(); - - break; - } - - return true; + includeIfNotIncluded("SoloPlay/Functions/MiscOverrides.js"); + includeIfNotIncluded("SoloPlay/Functions/Globals.js"); + + SetUp.include(); + SetUp.config(); + + /* Necro specific Chicken configuration. */ + Config.IronGolemChicken = 30; + + /* Pickit configuration. */ + Config.PickRange = 40; + // Config.PickitFiles.push("kolton.nip"); + // Config.PickitFiles.push("LLD.nip"); + + /* Gambling configuration. */ + Config.GambleItems.push("Amulet"); + Config.GambleItems.push("Ring"); + Config.GambleItems.push("Circlet"); + Config.GambleItems.push("Coronet"); + + // AutoEquip setup + const levelingTiers = [ + // Weapon + "([type] == wand || [type] == sword || [type] == knife) && ([quality] >= magic || [flag] == runeword) && [flag] != ethereal && [2handed] == 0 # [itemchargedskill] >= 0 # [tier] == tierscore(item)", + "[type] == wand && [quality] >= normal && [flag] != ethereal && [2handed] == 0 # [itemchargedskill] >= 0 && [sockets] != 2 # [tier] == tierscore(item)", + // Shield + "([type] == shield && ([quality] >= magic || [flag] == runeword) || [type] == voodooheads) && [flag] != ethereal # [itemchargedskill] >= 0 # [tier] == tierscore(item)", + "[type] == voodooheads && [quality] >= normal && [flag] != ethereal # [itemchargedskill] >= 0 && [sockets] == 1 # [tier] == tierscore(item)", + "me.classic && [type] == shield && [quality] >= normal # [itemchargedskill] >= 0 # [tier] == tierscore(item)", + // non runeword white items + "([type] == shield) && [quality] >= normal && [flag] != ethereal && [flag] != runeword # [itemchargedskill] >= 0 && ([sockets] == 1 || [sockets] == 2) # [tier] == tierscore(item)", + ]; + + const expansionTiers = [ + "me.charlvl < 33 && [name] == smallcharm && [quality] == magic # [maxmana] >= 1 # [invoquantity] == 4 && [charmtier] == charmscore(item)", + "[name] == smallcharm && [quality] == magic # [maxhp] >= 1 # [invoquantity] == 2 && [charmtier] == charmscore(item)", + "[name] == smallcharm && [quality] == magic # [itemmagicbonus] >= 1 # [invoquantity] == 2 && [charmtier] == charmscore(item)", + "[name] == smallcharm && [quality] == magic # [fireresist]+[lightresist]+[coldresist]+[poisonresist] >= 1 # [invoquantity] == 2 && [charmtier] == charmscore(item)", + "[name] == grandcharm && [quality] == magic # # [invoquantity] == 2 && [charmtier] == charmscore(item)", + ]; + + NTIP.buildList(levelingTiers); + me.expansion && NTIP.buildList(expansionTiers); + + /* Attack configuration. */ + Skill.usePvpRange = true; + Config.AttackSkill = [0, 0, 0, 0, 0, 0, 0]; + Config.LowManaSkill = [0, 0]; + Config.MaxAttackCount = 1000; + Config.BossPriority = me.normal; + Config.ClearType = 0; + Config.ClearPath = { Range: (Pather.canTeleport() ? 30 : 20), Spectype: 0 }; + + /* Class specific configuration. */ + Config.Dodge = Check.haveItem("armor", "runeword", "Enigma"); + Config.DodgeRange = Check.haveItem("armor", "runeword", "Enigma") ? 10 : 5; + Config.DodgeHP = 90; // Dodge only if HP percent is less than or equal to Config.DodgeHP. 100 = always dodge. + + /* Summons. */ + Config.ReviveUnstackable = true; + Config.ActiveSummon = me.charlvl < 10 || SetUp.currentBuild === "Summon"; + Config.Golem = me.checkSkill(sdk.skills.ClayGolem, sdk.skills.subindex.HardPoints) ? "Clay" : "None"; + Config.Skeletons = (me.charlvl > 10 && SetUp.currentBuild !== "Summon") ? 0 : "max"; + Config.SkeletonMages = (me.charlvl > 10 && SetUp.currentBuild !== "Summon") ? 0 : "max"; + Config.Revives = (me.charlvl > 10 && SetUp.currentBuild !== "Summon") ? 0 : "max"; + + /* Skill Specific */ + Config.PoisonNovaDelay = 1; // In Seconds + Config.ExplodeCorpses = me.checkSkill(sdk.skills.CorpseExplosion, sdk.skills.subindex.HardPoints) + ? sdk.skills.CorpseExplosion + : me.checkSkill(sdk.skills.PoisonExplosion, sdk.skills.subindex.HardPoints) + ? sdk.skills.PoisonExplosion + : 0; + + Config.imbueables = [ + { name: sdk.items.DemonHead, condition: () => (me.normal && me.expansion) }, + { name: sdk.items.HierophantTrophy, condition: () => (!me.normal && (me.charlvl < 66 || me.trueStr < 106) && me.expansion) }, + { name: sdk.items.BloodlordSkull, condition: () => (me.equipped.get(sdk.body.LeftArm).tier < 1000 && me.expansion) }, + { name: sdk.items.Belt, condition: () => (me.normal && (me.equipped.get(sdk.body.LeftArm).tier > 1000 || me.classic)) }, + { name: sdk.items.MeshBelt, condition: () => (!me.normal && me.charlvl < 46 && me.trueStr > 58 && (me.equipped.get(sdk.body.LeftArm).tier > 1000 || me.classic)) }, + { name: sdk.items.SpiderwebSash, condition: () => (!me.normal && me.trueStr > 50 && (me.equipped.get(sdk.body.LeftArm).tier > 1000 || me.classic)) }, + ].filter((item) => item.condition()); + + let imbueArr = SetUp.imbueItems(); + + !me.smith && NTIP.buildList(imbueArr); + + switch (me.gametype) { + case sdk.game.gametype.Classic: + // Res shield + if (me.equipped.get(sdk.body.LeftArm).tier < 487) { + includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/PDiamondShield.js"); + } + + break; + case sdk.game.gametype.Expansion: + NTIP.addLine("[name] >= VexRune && [name] <= ZodRune"); + const { basicSocketables, addSocketableObj } = require("../Utils/General"); + + Config.socketables = Config.socketables.concat(basicSocketables.caster, basicSocketables.all); + Config.socketables.push(addSocketableObj(sdk.items.Monarch, [], [], + !me.hell, (item) => !Check.haveBase("monarch", 4) && item.ilvl >= 41 && item.isBaseType && !item.ethereal + )); + Config.socketables.push(addSocketableObj(sdk.items.Shako, [sdk.items.runes.Um], [sdk.items.gems.Perfect.Ruby], + true, (item) => item.unique && !item.ethereal + )); + + /* Crafting */ + if (me.equipped.get(sdk.body.Neck).tier < 100000) { + Config.Recipes.push([Recipe.Caster.Amulet]); + } + + // upgrade magefist + if (me.equipped.get(sdk.body.Gloves).tier < 110000) { + Config.Recipes.push([Recipe.Unique.Armor.ToExceptional, "Light Gauntlets", Roll.NonEth]); + Config.Recipes.push([Recipe.Unique.Armor.ToElite, "Battle Gauntlets", Roll.NonEth, "magefist"]); + } + + Check.itemSockables(sdk.items.RoundShield, "unique", "Moser's Blessed Circle"); + Check.itemSockables(sdk.items.Shako, "unique", "Harlequin Crest"); + + /* Crafting */ + if (me.equipped.get(sdk.body.Neck).tier < 100000) { + Config.Recipes.push([Recipe.Caster.Amulet]); + } + + if (me.equipped.get(sdk.body.RingLeft).tier < 100000) { + Config.Recipes.push([Recipe.Caster.Ring]); + } + + // Call to Arms + if (!me.checkItem({ name: sdk.locale.items.CalltoArms }).have) { + includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/CallToArms.js"); + } + + // White + if (SetUp.currentBuild !== "Summon") { + includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/White.js"); + } + + // Rhyme + if (me.equipped.get(sdk.body.LeftArm).tier < 650) { + includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/Rhyme.js"); + } + + // Enigma + if (!me.checkItem({ name: sdk.locale.items.Enigma }).have) { + includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/Enigma.js"); + } + + // Spirit Sword + if ((me.ladder || Developer.addLadderRW) && me.equipped.get(sdk.body.RightArm).tier < 777) { + includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/SpiritSword.js"); + } + + // Spirit shield + if ((me.ladder || Developer.addLadderRW) && (me.equipped.get(sdk.body.LeftArm).tier < 1000 || me.equipped.get(sdk.body.LeftArmSecondary).prefixnum !== sdk.locale.items.Spirit)) { + includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/SpiritShield.js"); + } + + // Merc Insight + if ((me.ladder || Developer.addLadderRW) && Item.getMercEquipped(sdk.body.RightArm).tier < 3600) { + includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/MercInsight.js"); + } + + if (!me.haveSome([{ name: sdk.locale.items.Enigma }, { name: sdk.locale.items.Bone }]) && me.equipped.get(sdk.body.Armor).tier < 650) { + includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/Bone.js"); + } + + // Lore + if (me.equipped.get(sdk.body.Head).tier < 315) { + includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/Lore.js"); + } + + // Ancients' Pledge + if (me.equipped.get(sdk.body.LeftArm).tier < 500) { + includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/AncientsPledge.js"); + } + + // Merc Fortitude + if (Item.getMercEquipped(sdk.body.Armor).prefixnum !== sdk.locale.items.Fortitude) { + includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/MercFortitude.js"); + } + + // Merc Treachery + if (Item.getMercEquipped(sdk.body.Armor).tier < 15000) { + includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/MercTreachery.js"); + } + + // Smoke + if (me.equipped.get(sdk.body.Armor).tier < 450) { + includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/Smoke.js"); + } + + // Stealth + if (me.equipped.get(sdk.body.Armor).tier < 233) { + includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/Stealth.js"); + } + + SoloWants.buildList(); + + break; + } + + return true; })(); diff --git a/libs/SoloPlay/Config/Paladin.js b/libs/SoloPlay/Config/Paladin.js index 99cad1a2..2d3d98e7 100644 --- a/libs/SoloPlay/Config/Paladin.js +++ b/libs/SoloPlay/Config/Paladin.js @@ -22,492 +22,492 @@ // todo: clean-up how cubing to runes is handled (function LoadConfig () { - includeIfNotIncluded("SoloPlay/Functions/MiscOverrides.js"); - includeIfNotIncluded("SoloPlay/Functions/Globals.js"); - - SetUp.include(); - SetUp.config(); - - /* Pickit configuration. */ - Config.PickRange = 40; - // Config.PickitFiles.push("kolton.nip"); - // Config.PickitFiles.push("LLD.nip"); - - /* Gambling configuration. */ - Config.GambleItems.push("Amulet"); - Config.GambleItems.push("Ring"); - Config.GambleItems.push("Circlet"); - Config.GambleItems.push("Coronet"); - - let weapons = [ - sdk.items.type.Scepter, sdk.items.type.Mace, - sdk.items.type.Sword, sdk.items.type.Knife, sdk.items.type.Axe, - sdk.items.type.Wand, sdk.items.type.Hammer, sdk.items.type.Club - ].map(el => "[type] == " + el).join(" || "); - - // AutoEquip setup - const levelingTiers = [ - // Weapon - "(" + weapons + ") && ([quality] >= magic || [flag] == runeword) && [flag] != ethereal && [2handed] == 0 # [itemchargedskill] >= 0 # [tier] == tierscore(item)", - // Shield - "([type] == shield || [type] == auricshields) && ([quality] >= magic || [flag] == runeword) && [flag] != ethereal # [itemchargedskill] >= 0 # [tier] == tierscore(item)", - "me.classic && [type] == shield && [quality] >= normal # [itemchargedskill] >= 0 # [tier] == tierscore(item)", - // non runeword white items - "(" + weapons + ") && [quality] >= normal && [flag] != ethereal && [flag] != runeword && [2handed] == 0 # [itemchargedskill] >= 0 && ([sockets] == 1 || [sockets] == 3) # [tier] == tierscore(item)", - "([type] == shield || [type] == auricshields) && [quality] >= normal && [flag] != ethereal && [flag] != runeword # [itemchargedskill] >= 0 && ([sockets] == 1 || [sockets] == 2) # [tier] == tierscore(item)", - ]; - - let miscCharmQuantity = me.charlvl < 40 ? 6 : 3; - const expansionTiers = [ - // Switch - "[type] == wand && [quality] >= normal # [itemchargedskill] == 72 # [secondarytier] == 25000", // Weaken charged wand - "[name] == beardedaxe && [quality] == unique # [itemchargedskill] == 87 # [secondarytier] == 50000", // Spellsteel Decrepify charged axe - // Charms - "[name] == smallcharm && [quality] == magic # [maxhp] >= 1 # [invoquantity] == 2 && [charmtier] == charmscore(item)", - "[name] == smallcharm && [quality] == magic # [itemmagicbonus] >= 1 # [invoquantity] == 2 && [charmtier] == charmscore(item)", - "[name] == smallcharm && [quality] == magic # # [invoquantity] == " + miscCharmQuantity + " && [charmtier] == charmscore(item)", - "[name] == grandcharm && [quality] == magic # # [invoquantity] == 2 && [charmtier] == charmscore(item)", - ]; - - /* Gear */ - NTIP.buildList(levelingTiers); - me.expansion && NTIP.buildList(expansionTiers); - - /* Attack configuration. */ - Config.AttackSkill = [0, 0, 0, 0, 0, 0, 0]; - Config.LowManaSkill = [0, 0]; - Config.MaxAttackCount = 1000; - Config.BossPriority = me.normal; - Config.ClearType = 0; - Config.ClearPath = { Range: (Pather.canTeleport() ? 30 : 10), Spectype: 0 }; - - /* Class specific configuration. */ - Config.AvoidDolls = true; - Config.Vigor = true; - Config.Charge = true; - Config.Redemption = [45, 25]; - - // Maybe add auric shield? - Config.imbueables = [ - { name: sdk.items.WarScepter, condition: () => me.normal }, - { name: sdk.items.DivineScepter, condition: () => (!me.normal && (me.trueStr < 125 || me.trueDex < 60)) }, - { name: sdk.items.MightyScepter, condition: () => (me.equipped.get(sdk.body.RightArm).tier < 777 && (me.trueStr >= 125 || me.trueDex >= 60)) }, - { name: sdk.items.Belt, condition: () => (me.normal && (me.equipped.get(sdk.body.RightArm).tier > 777 || me.classic)) }, - { name: sdk.items.MeshBelt, condition: () => (!me.normal && me.charlvl < 46 && me.trueStr > 58 && (me.equipped.get(sdk.body.RightArm).tier > 777 || me.classic)) }, - { name: sdk.items.SpiderwebSash, condition: () => (!me.normal && me.trueStr > 50 && (me.equipped.get(sdk.body.RightArm).tier > 777 || me.classic)) }, - ]; - - let imbueArr = SetUp.imbueItems(); - - !me.smith && NTIP.buildList(imbueArr); - - switch (me.gametype) { - case sdk.game.gametype.Classic: - // Res shield - if (me.equipped.get(sdk.body.LeftArm).tier < 487) { - includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/PDiamondShield.js"); - } - - break; - case sdk.game.gametype.Expansion: - NTIP.addLine("[name] >= VexRune && [name] <= ZodRune"); - const { basicSocketables, addSocketableObj } = require("../Utils/General"); - - Config.socketables = Config.socketables.concat(basicSocketables.caster, basicSocketables.all); - Config.socketables.push(addSocketableObj(sdk.items.Shako, [sdk.items.runes.Um], [sdk.items.gems.Perfect.Ruby], - true, (item) => item.unique && !item.ethereal - )); - - /* Crafting */ - if (me.equipped.get(sdk.body.Neck).tier < 100000) { - Check.currentBuild().caster ? Config.Recipes.push([Recipe.Caster.Amulet]) : Config.Recipes.push([Recipe.Blood.Amulet]); - } - - if (me.equipped.get(sdk.body.RingLeft).tier < 100000) { - Check.currentBuild().caster ? Config.Recipes.push([Recipe.Caster.Ring]) : Config.Recipes.push([Recipe.Blood.Ring]); - } - - if (me.equipped.get(sdk.body.Gloves).tier < 110000) { - Config.Recipes.push([Recipe.Unique.Armor.ToExceptional, "Light Gauntlets", Roll.NonEth]); - } - - if (me.checkItem({ name: sdk.locale.items.CalltoArms }).have) { - // Spirit on swap - NTIP.addLine("[type] == auricshields && [flag] == runeword # [fcr] >= 25 && [maxmana] >= 89 # [secondarytier] == 110000"); - } - - // FinalBuild specific setup - let dreamerCheck; - - switch (SetUp.finalBuild) { - case "Smiter": - case "Zealer": - // Grief - if ((me.ladder || Developer.addLadderRW) && !me.checkItem({ name: sdk.locale.items.Grief }).have) { - includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/Grief.js"); - } - - if (SetUp.finalBuild === "Zealer") { - Config.socketables.push(addSocketableObj(sdk.items.GrimHelm, [sdk.items.runes.Ber], [sdk.items.gems.Perfect.Ruby], - true, (item) => item.unique && item.getStat(sdk.stats.DamageResist) === 20 && !item.ethereal - )); - Config.socketables.push(addSocketableObj(sdk.items.BoneVisage, [sdk.items.runes.Ber], [sdk.items.gems.Perfect.Ruby], - true, (item) => item.unique && item.getStat(sdk.stats.DamageResist) === 20 && !item.ethereal && item.fname.toLowerCase().includes("vampire gaze") - )); - - Check.itemSockables(sdk.items.GrimHelm, "unique", "Vampire Gaze"); - Check.itemSockables(sdk.items.BoneVisage, "unique", "Vampire Gaze"); - - if (!Check.haveItem("bonevisage", "unique", "Vampire Gaze")) { - // Upgrade Vamp Gaze to Elite - Config.Recipes.push([Recipe.Unique.Armor.ToElite, "Grim Helm", Roll.NonEth]); - } - - // Exile - if (!me.checkItem({ name: sdk.locale.items.Exile, itemtype: sdk.items.type.AuricShields }).have) { - includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/Exile.js"); - } - - // Fortitude - if ((me.ladder || Developer.addLadderRW) && !me.checkItem({ name: sdk.locale.items.Fortitude, itemtype: sdk.items.type.Armor }).have) { - includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/Fortitude.js"); - } - } - - break; - case "Hammerdin": - // Heart of the Oak - if (!me.checkItem({ name: sdk.locale.items.HeartoftheOak }).have) { - includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/HeartOfTheOak.js"); - } - - // Spirit Shield - if ((me.ladder || Developer.addLadderRW) && me.equipped.get(sdk.body.LeftArm).tier < 110000) { - includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/SpiritShield.js"); - } - - break; - case "Auradin": - dreamerCheck = me.haveAll([ - { name: sdk.locale.items.Dream, itemtype: sdk.items.type.AuricShields }, - { name: sdk.locale.items.Dream, itemtype: sdk.items.type.Helm } - ]); - - // Dream Shield - if ((me.ladder || Developer.addLadderRW) && !me.checkItem({ name: sdk.locale.items.Dream, itemtype: sdk.items.type.AuricShields }).have) { - includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/DreamShield.js"); - } - - // Dream Helm - if ((me.ladder || Developer.addLadderRW) && !me.checkItem({ name: sdk.locale.items.Dream, itemtype: sdk.items.type.Helm }).have) { - includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/DreamHelm.js"); - } - - if ((me.ladder || Developer.addLadderRW) && !dreamerCheck) { - // Cube to Jah rune - if (!me.getItem(sdk.items.runes.Jah)) { - if (me.checkItem({ name: sdk.locale.items.CalltoArms }).have) { - Config.Recipes.push([Recipe.Rune, "Mal Rune"]); - Config.Recipes.push([Recipe.Rune, "Ist Rune"]); - Config.Recipes.push([Recipe.Rune, "Gul Rune"]); - Config.Recipes.push([Recipe.Rune, "Vex Rune"]); - Config.Recipes.push([Recipe.Rune, "Ohm Rune"]); - } - - Config.Recipes.push([Recipe.Rune, "Lo Rune"]); - Config.Recipes.push([Recipe.Rune, "Sur Rune"]); - Config.Recipes.push([Recipe.Rune, "Ber Rune"]); - } - - } - - if (!me.checkItem({ name: sdk.locale.items.CalltoArms }).have) { - // Cube to Mal rune - if (!me.getItem(sdk.items.runes.Mal) && me.equipped.get(sdk.body.RightArm).tier >= 110000) { - Config.Recipes.push([Recipe.Rune, "Um Rune"]); - } - - // Cube to Ohm rune - if (!me.getItem(sdk.items.runes.Ohm)) { - Config.Recipes.push([Recipe.Rune, "Gul Rune"]); - Config.Recipes.push([Recipe.Rune, "Vex Rune"]); - } - } - - // Dragon Armor - if ((me.ladder || Developer.addLadderRW) && !me.checkItem({ name: sdk.locale.items.Dragon, itemtype: sdk.items.type.Armor }).have && dreamerCheck) { - includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/DragonArmor.js"); - } - - if (!me.checkItem({ name: sdk.locale.items.HandofJustice }).have && dreamerCheck) { - includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/HandOfJustice.js"); - - // Azurewrath - NTIP.addLine("[name] == phaseblade && [flag] != ethereal && [quality] == unique # [enhanceddamage] >= 230 && [sanctuaryaura] >= 10 # [tier] == 115000"); - } - - if (!me.haveAll([{ name: sdk.locale.items.CrescentMoon }, { name: sdk.locale.items.HandofJustice }]) && dreamerCheck) { - includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/CrescentMoon.js"); - - // Lightsabre - NTIP.addLine("[name] == phaseblade && [flag] != ethereal && [quality] == unique # [enhanceddamage] >= 150 && [itemabsorblightpercent] == 25 # [tier] == 105000"); - } - - if (!me.checkItem({ name: sdk.locale.items.VoiceofReason }).have && !me.haveSome([{ name: sdk.locale.items.CrescentMoon }, { name: sdk.locale.items.HandofJustice }]) && dreamerCheck) { - includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/VoiceOfReason.js"); - } - - if ((me.ladder || Developer.addLadderRW) && Item.getMercEquipped(sdk.body.Armor).prefixnum !== sdk.locale.items.Fortitude - && me.haveAll([{ name: sdk.locale.items.HandofJustice }, { name: sdk.locale.items.Dragon, itemtype: sdk.items.type.Armor }, - { name: sdk.locale.items.Dream, itemtype: sdk.items.type.AuricShields }, { name: sdk.locale.items.Dream, itemtype: sdk.items.type.Helm }])) { - includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/MercFortitude.js"); - } - - break; - case "Sancdreamer": - dreamerCheck = me.haveAll([{ name: sdk.locale.items.Dream, itemtype: sdk.items.type.AuricShields }, { name: sdk.locale.items.Dream, itemtype: sdk.items.type.Helm }]); - - // Dream Shield - if ((me.ladder || Developer.addLadderRW) && !me.checkItem({ name: sdk.locale.items.Dream, itemtype: sdk.items.type.AuricShields }).have) { - includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/DreamShield.js"); - } - - // Dream Helm - if ((me.ladder || Developer.addLadderRW) && !me.checkItem({ name: sdk.locale.items.Dream, itemtype: sdk.items.type.Helm }).have) { - includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/DreamHelm.js"); - } - - if ((me.ladder || Developer.addLadderRW) && !dreamerCheck) { - // Cube to Jah rune - if (!me.getItem(sdk.items.runes.Jah)) { - if (me.checkItem({ name: sdk.locale.items.CalltoArms }).have) { - Config.Recipes.push([Recipe.Rune, "Mal Rune"]); - Config.Recipes.push([Recipe.Rune, "Ist Rune"]); - Config.Recipes.push([Recipe.Rune, "Gul Rune"]); - Config.Recipes.push([Recipe.Rune, "Vex Rune"]); - Config.Recipes.push([Recipe.Rune, "Ohm Rune"]); - } - - Config.Recipes.push([Recipe.Rune, "Lo Rune"]); - Config.Recipes.push([Recipe.Rune, "Sur Rune"]); - Config.Recipes.push([Recipe.Rune, "Ber Rune"]); - } - } - - if (!me.checkItem({ name: sdk.locale.items.CalltoArms }).have) { - // Cube to Mal rune - if (!me.getItem(sdk.items.runes.Mal) && me.equipped.get(sdk.body.RightArm).tier >= 110000) { - Config.Recipes.push([Recipe.Rune, "Um Rune"]); - } - - // Cube to Ohm rune - if (!me.getItem(sdk.items.runes.Ohm)) { - Config.Recipes.push([Recipe.Rune, "Gul Rune"]); - Config.Recipes.push([Recipe.Rune, "Vex Rune"]); - } - } - - // Chains of Honor - if (!me.checkItem({ name: sdk.locale.items.ChainsofHonor }).have) { - includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/ChainsOfHonor.js"); - } - - if (!me.checkItem({ name: sdk.locale.items.LastWish }).have && dreamerCheck) { - includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/LastWish.js"); - } - - if (!me.haveAll([{ name: sdk.locale.items.CrescentMoon }, { name: sdk.locale.items.LastWish }]) && dreamerCheck) { - includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/CrescentMoon.js"); - - // Lightsabre - NTIP.addLine("[name] == phaseblade && [flag] != ethereal && [quality] == unique # [enhanceddamage] >= 150 && [itemabsorblightpercent] == 25 # [tier] == 105000"); - } - - if (!me.checkItem({ name: sdk.locale.items.VoiceofReason }).have && !me.haveSome([{ name: sdk.locale.items.CrescentMoon }, { name: sdk.locale.items.LastWish }]) && dreamerCheck) { - includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/VoiceOfReason.js"); - } - - if ((me.ladder || Developer.addLadderRW) && me.haveAll([{ name: sdk.locale.items.LastWish }, { name: sdk.locale.items.ChainsofHonor }, - { name: sdk.locale.items.Dream, itemtype: sdk.items.type.AuricShields }, { name: sdk.locale.items.Dream, itemtype: sdk.items.type.Helm }])) { - // Infinity - if ((me.ladder || Developer.addLadderRW) && Item.getMercEquipped(sdk.body.RightArm).prefixnum !== sdk.locale.items.Infinity) { - if (!isIncluded("SoloPlay/BuildFiles/Runewords/MercInfinity.js")) { - include("SoloPlay/BuildFiles/Runewords/MercInfinity.js"); - } - } - // Merc Fortitude - if (Item.getMercEquipped(sdk.body.Armor).prefixnum !== sdk.locale.items.Fortitude) { - if (!isIncluded("SoloPlay/BuildFiles/Runewords/MercFortitude.js")) { - include("SoloPlay/BuildFiles/Runewords/MercFortitude.js"); - } - } - } - - break; - case "Torchadin": - // Dragon Armor - if ((me.ladder || Developer.addLadderRW) && !me.checkItem({ name: sdk.locale.items.Dragon, itemtype: sdk.items.type.Armor }).have) { - includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/DragonArmor.js"); - } - - if (!me.checkItem({ name: sdk.locale.items.HandofJustice }).have) { - includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/HandOfJustice.js"); - - // Azurewrath - NTIP.addLine("[name] == phaseblade && [flag] != ethereal && [quality] == unique # [enhanceddamage] >= 230 && [sanctuaryaura] >= 10 # [tier] == 115000"); - } - - // Exile - if ((me.ladder || Developer.addLadderRW) && !me.checkItem({ name: sdk.locale.items.Exile, itemtype: sdk.items.type.AuricShields }).have) { - includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/Exile.js"); - } - - if ((me.ladder || Developer.addLadderRW) && !me.haveAll([{ name: sdk.locale.items.HandofJustice }, - { name: sdk.locale.items.Exile, itemtype: sdk.items.type.AuricShields }, { name: sdk.locale.items.Dragon, itemtype: sdk.items.type.Armor }])) { - // Cube to Cham rune - if (!me.getItem(sdk.items.runes.Cham) || !me.getItem(sdk.items.runes.Sur) || !me.getItem(sdk.items.runes.Lo)) { - if (me.checkItem({ name: sdk.locale.items.CalltoArms }).have) { - Config.Recipes.push([Recipe.Rune, "Mal Rune"]); - Config.Recipes.push([Recipe.Rune, "Ist Rune"]); - Config.Recipes.push([Recipe.Rune, "Gul Rune"]); - - if (me.checkItem({ name: sdk.locale.items.Exile, itemtype: sdk.items.type.AuricShields }).have) { - Config.Recipes.push([Recipe.Rune, "Vex Rune"]); - Config.Recipes.push([Recipe.Rune, "Ohm Rune"]); - } else if (!me.checkItem({ name: sdk.locale.items.Exile, itemtype: sdk.items.type.AuricShields }).have && !me.getItem(sdk.items.runes.Ohm)) { - Config.Recipes.push([Recipe.Rune, "Vex Rune"]); - } - } - - if (me.checkItem({ name: sdk.locale.items.Dragon, itemtype: sdk.items.type.Armor }).have) { - Config.Recipes.push([Recipe.Rune, "Lo Rune"]); - Config.Recipes.push([Recipe.Rune, "Sur Rune"]); - } else if ((!me.haveAll([{ name: sdk.locale.items.Dragon, itemtype: sdk.items.type.Armor }, { name: sdk.locale.items.HandofJustice }]) && !me.getItem(sdk.items.runes.Sur))) { - Config.Recipes.push([Recipe.Rune, "Lo Rune"]); - } - - Config.Recipes.push([Recipe.Rune, "Ber Rune"]); - Config.Recipes.push([Recipe.Rune, "Jah Rune"]); - } - } - - if (!me.checkItem({ name: sdk.locale.items.CalltoArms }).have) { - // Cube to Mal rune - if (!me.getItem(sdk.items.runes.Mal) && me.equipped.get(sdk.body.RightArm).tier >= 110000) { - Config.Recipes.push([Recipe.Rune, "Um Rune"]); - } - - // Cube to Ohm rune - if (!me.getItem(sdk.items.runes.Ohm)) { - Config.Recipes.push([Recipe.Rune, "Gul Rune"]); - Config.Recipes.push([Recipe.Rune, "Vex Rune"]); - } - } - - if (!me.haveAll([{ name: sdk.locale.items.CrescentMoon }, { name: sdk.locale.items.HandofJustice }])) { - includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/CrescentMoon.js"); - - // Lightsabre - NTIP.addLine("[name] == phaseblade && [flag] != ethereal && [quality] == unique # [enhanceddamage] >= 150 && [itemabsorblightpercent] == 25 # [tier] == 105000"); - } - - if (!me.checkItem({ name: sdk.locale.items.VoiceofReason }).have && !me.haveSome([{ name: sdk.locale.items.CrescentMoon }, { name: sdk.locale.items.HandofJustice }])) { - includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/VoiceOfReason.js"); - } - - if ((me.ladder || Developer.addLadderRW) && Item.getMercEquipped(sdk.body.Armor).prefixnum !== sdk.locale.items.Fortitude - && me.haveAll([{ name: sdk.locale.items.HandofJustice }, { name: sdk.locale.items.Dragon, itemtype: sdk.items.type.Armor }, { name: sdk.locale.items.Exile, itemtype: sdk.items.type.AuricShields }])) { - includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/MercFortitude.js"); - } - break; - default: - break; - } - - Check.itemSockables(sdk.items.RoundShield, "unique", "Moser's Blessed Circle"); - Check.itemSockables(sdk.items.Shako, "unique", "Harlequin Crest"); - - // Call to Arms - if (!me.checkItem({ name: sdk.locale.items.CalltoArms }).have) { - includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/CallToArms.js"); - } - - // Enigma - Don't make if not Smiter or Hammerdin - if (!me.checkItem({ name: sdk.locale.items.Enigma }).have && ["Hammerdin", "Smiter"].includes(SetUp.finalBuild)) { - includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/Enigma.js"); - } - - // Spirit Sword - if ((me.ladder || Developer.addLadderRW) && me.equipped.get(sdk.body.RightArm).tier < 777) { - includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/SpiritSword.js"); - } - - // Spirit Shield - if ((me.ladder || Developer.addLadderRW) && (me.equipped.get(sdk.body.LeftArm).tier < 1000 || me.equipped.get(sdk.body.LeftArmSecondary).prefixnum !== sdk.locale.items.Spirit)) { - includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/SpiritShield.js"); - } - - // Merc Insight - if ((me.ladder || Developer.addLadderRW) && Item.getMercEquipped(sdk.body.RightArm).tier < 3600) { - includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/MercInsight.js"); - } - - // Lore - if (me.equipped.get(sdk.body.Head).tier < 315) { - includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/Lore.js"); - } - - /** - * @todo Rhyme and Splendor - */ - - // Ancients' Pledge - if (me.equipped.get(sdk.body.LeftArm).tier < 500) { - includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/AncientsPledge.js"); - } - - // Merc Fortitude - if (Item.getMercEquipped(sdk.body.Armor).prefixnum !== sdk.locale.items.Fortitude && ["Hammerdin", "Smiter"].includes(SetUp.finalBuild)) { - includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/MercFortitude.js"); - } - - // Merc Treachery - if (Item.getMercEquipped(sdk.body.Armor).tier < 15000) { - includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/MercTreachery.js"); - } - - // Smoke - if (me.equipped.get(sdk.body.Armor).tier < 450) { - includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/Smoke.js"); - } - - // Stealth - if (me.equipped.get(sdk.body.Armor).tier < 233) { - includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/Stealth.js"); - } - - /* if (SetUp.currentBuild === "Start") { - let { maxStr, maxDex } = Check.currentBuild(); - let basic = "[flag] != ethereal && [quality] >= normal && [quality] <= superior"; - let myStatsReq = "[strreq] <= " + maxStr + " && [dexreq] <= " + maxDex; - let statsReq = "[sockets] == 2"; - let quantityReq = "[maxquantity] == 1"; - // add Steel RW - NTIP.buildList([ - "[name] == TirRune # # [maxquantity] == 2", - "[name] == ElRune # # [maxquantity] == 2", - "([type] == sword || [type] == mace || [type] == axe) && " + basic + " && " + myStatsReq + " # " + statsReq + " # " + quantityReq, - ]); - - // need to make runewords able to process types, maybe a hacky method of just taking a nip line? - // or could create a base item data module and loop through whatever meets the reqs but that feels ugly too - Config.KeepRunewords.push("([type] == sword || [type] == mace || [type] == axe) # [enhanceddamage] >= 20 && [ias] >= 25"); - } */ - - SoloWants.buildList(); - - break; - } - - return true; + includeIfNotIncluded("SoloPlay/Functions/MiscOverrides.js"); + includeIfNotIncluded("SoloPlay/Functions/Globals.js"); + + SetUp.include(); + SetUp.config(); + + /* Pickit configuration. */ + Config.PickRange = 40; + // Config.PickitFiles.push("kolton.nip"); + // Config.PickitFiles.push("LLD.nip"); + + /* Gambling configuration. */ + Config.GambleItems.push("Amulet"); + Config.GambleItems.push("Ring"); + Config.GambleItems.push("Circlet"); + Config.GambleItems.push("Coronet"); + + let weapons = [ + sdk.items.type.Scepter, sdk.items.type.Mace, + sdk.items.type.Sword, sdk.items.type.Knife, sdk.items.type.Axe, + sdk.items.type.Wand, sdk.items.type.Hammer, sdk.items.type.Club + ].map(el => "[type] == " + el).join(" || "); + + // AutoEquip setup + const levelingTiers = [ + // Weapon + "(" + weapons + ") && ([quality] >= magic || [flag] == runeword) && [flag] != ethereal && [2handed] == 0 # [itemchargedskill] >= 0 # [tier] == tierscore(item)", + // Shield + "([type] == shield || [type] == auricshields) && ([quality] >= magic || [flag] == runeword) && [flag] != ethereal # [itemchargedskill] >= 0 # [tier] == tierscore(item)", + "me.classic && [type] == shield && [quality] >= normal # [itemchargedskill] >= 0 # [tier] == tierscore(item)", + // non runeword white items + "(" + weapons + ") && [quality] >= normal && [flag] != ethereal && [flag] != runeword && [2handed] == 0 # [itemchargedskill] >= 0 && ([sockets] == 1 || [sockets] == 3) # [tier] == tierscore(item)", + "([type] == shield || [type] == auricshields) && [quality] >= normal && [flag] != ethereal && [flag] != runeword # [itemchargedskill] >= 0 && ([sockets] == 1 || [sockets] == 2) # [tier] == tierscore(item)", + ]; + + let miscCharmQuantity = me.charlvl < 40 ? 6 : 3; + const expansionTiers = [ + // Switch + "[type] == wand && [quality] >= normal # [itemchargedskill] == 72 # [secondarytier] == 25000", // Weaken charged wand + "[name] == beardedaxe && [quality] == unique # [itemchargedskill] == 87 # [secondarytier] == 50000", // Spellsteel Decrepify charged axe + // Charms + "[name] == smallcharm && [quality] == magic # [maxhp] >= 1 # [invoquantity] == 2 && [charmtier] == charmscore(item)", + "[name] == smallcharm && [quality] == magic # [itemmagicbonus] >= 1 # [invoquantity] == 2 && [charmtier] == charmscore(item)", + "[name] == smallcharm && [quality] == magic # # [invoquantity] == " + miscCharmQuantity + " && [charmtier] == charmscore(item)", + "[name] == grandcharm && [quality] == magic # # [invoquantity] == 2 && [charmtier] == charmscore(item)", + ]; + + /* Gear */ + NTIP.buildList(levelingTiers); + me.expansion && NTIP.buildList(expansionTiers); + + /* Attack configuration. */ + Config.AttackSkill = [0, 0, 0, 0, 0, 0, 0]; + Config.LowManaSkill = [0, 0]; + Config.MaxAttackCount = 1000; + Config.BossPriority = me.normal; + Config.ClearType = 0; + Config.ClearPath = { Range: (Pather.canTeleport() ? 30 : 10), Spectype: 0 }; + + /* Class specific configuration. */ + Config.AvoidDolls = true; + Config.Vigor = true; + Config.Charge = true; + Config.Redemption = [45, 25]; + + // Maybe add auric shield? + Config.imbueables = [ + { name: sdk.items.WarScepter, condition: () => me.normal }, + { name: sdk.items.DivineScepter, condition: () => (!me.normal && (me.trueStr < 125 || me.trueDex < 60)) }, + { name: sdk.items.MightyScepter, condition: () => (me.equipped.get(sdk.body.RightArm).tier < 777 && (me.trueStr >= 125 || me.trueDex >= 60)) }, + { name: sdk.items.Belt, condition: () => (me.normal && (me.equipped.get(sdk.body.RightArm).tier > 777 || me.classic)) }, + { name: sdk.items.MeshBelt, condition: () => (!me.normal && me.charlvl < 46 && me.trueStr > 58 && (me.equipped.get(sdk.body.RightArm).tier > 777 || me.classic)) }, + { name: sdk.items.SpiderwebSash, condition: () => (!me.normal && me.trueStr > 50 && (me.equipped.get(sdk.body.RightArm).tier > 777 || me.classic)) }, + ]; + + let imbueArr = SetUp.imbueItems(); + + !me.smith && NTIP.buildList(imbueArr); + + switch (me.gametype) { + case sdk.game.gametype.Classic: + // Res shield + if (me.equipped.get(sdk.body.LeftArm).tier < 487) { + includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/PDiamondShield.js"); + } + + break; + case sdk.game.gametype.Expansion: + NTIP.addLine("[name] >= VexRune && [name] <= ZodRune"); + const { basicSocketables, addSocketableObj } = require("../Utils/General"); + + Config.socketables = Config.socketables.concat(basicSocketables.caster, basicSocketables.all); + Config.socketables.push(addSocketableObj(sdk.items.Shako, [sdk.items.runes.Um], [sdk.items.gems.Perfect.Ruby], + true, (item) => item.unique && !item.ethereal + )); + + /* Crafting */ + if (me.equipped.get(sdk.body.Neck).tier < 100000) { + Check.currentBuild().caster ? Config.Recipes.push([Recipe.Caster.Amulet]) : Config.Recipes.push([Recipe.Blood.Amulet]); + } + + if (me.equipped.get(sdk.body.RingLeft).tier < 100000) { + Check.currentBuild().caster ? Config.Recipes.push([Recipe.Caster.Ring]) : Config.Recipes.push([Recipe.Blood.Ring]); + } + + if (me.equipped.get(sdk.body.Gloves).tier < 110000) { + Config.Recipes.push([Recipe.Unique.Armor.ToExceptional, "Light Gauntlets", Roll.NonEth]); + } + + if (me.checkItem({ name: sdk.locale.items.CalltoArms }).have) { + // Spirit on swap + NTIP.addLine("[type] == auricshields && [flag] == runeword # [fcr] >= 25 && [maxmana] >= 89 # [secondarytier] == 110000"); + } + + // FinalBuild specific setup + let dreamerCheck; + + switch (SetUp.finalBuild) { + case "Smiter": + case "Zealer": + // Grief + if ((me.ladder || Developer.addLadderRW) && !me.checkItem({ name: sdk.locale.items.Grief }).have) { + includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/Grief.js"); + } + + if (SetUp.finalBuild === "Zealer") { + Config.socketables.push(addSocketableObj(sdk.items.GrimHelm, [sdk.items.runes.Ber], [sdk.items.gems.Perfect.Ruby], + true, (item) => item.unique && item.getStat(sdk.stats.DamageResist) === 20 && !item.ethereal + )); + Config.socketables.push(addSocketableObj(sdk.items.BoneVisage, [sdk.items.runes.Ber], [sdk.items.gems.Perfect.Ruby], + true, (item) => item.unique && item.getStat(sdk.stats.DamageResist) === 20 && !item.ethereal && item.fname.toLowerCase().includes("vampire gaze") + )); + + Check.itemSockables(sdk.items.GrimHelm, "unique", "Vampire Gaze"); + Check.itemSockables(sdk.items.BoneVisage, "unique", "Vampire Gaze"); + + if (!Check.haveItem("bonevisage", "unique", "Vampire Gaze")) { + // Upgrade Vamp Gaze to Elite + Config.Recipes.push([Recipe.Unique.Armor.ToElite, "Grim Helm", Roll.NonEth]); + } + + // Exile + if (!me.checkItem({ name: sdk.locale.items.Exile, itemtype: sdk.items.type.AuricShields }).have) { + includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/Exile.js"); + } + + // Fortitude + if ((me.ladder || Developer.addLadderRW) && !me.checkItem({ name: sdk.locale.items.Fortitude, itemtype: sdk.items.type.Armor }).have) { + includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/Fortitude.js"); + } + } + + break; + case "Hammerdin": + // Heart of the Oak + if (!me.checkItem({ name: sdk.locale.items.HeartoftheOak }).have) { + includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/HeartOfTheOak.js"); + } + + // Spirit Shield + if ((me.ladder || Developer.addLadderRW) && me.equipped.get(sdk.body.LeftArm).tier < 110000) { + includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/SpiritShield.js"); + } + + break; + case "Auradin": + dreamerCheck = me.haveAll([ + { name: sdk.locale.items.Dream, itemtype: sdk.items.type.AuricShields }, + { name: sdk.locale.items.Dream, itemtype: sdk.items.type.Helm } + ]); + + // Dream Shield + if ((me.ladder || Developer.addLadderRW) && !me.checkItem({ name: sdk.locale.items.Dream, itemtype: sdk.items.type.AuricShields }).have) { + includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/DreamShield.js"); + } + + // Dream Helm + if ((me.ladder || Developer.addLadderRW) && !me.checkItem({ name: sdk.locale.items.Dream, itemtype: sdk.items.type.Helm }).have) { + includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/DreamHelm.js"); + } + + if ((me.ladder || Developer.addLadderRW) && !dreamerCheck) { + // Cube to Jah rune + if (!me.getItem(sdk.items.runes.Jah)) { + if (me.checkItem({ name: sdk.locale.items.CalltoArms }).have) { + Config.Recipes.push([Recipe.Rune, "Mal Rune"]); + Config.Recipes.push([Recipe.Rune, "Ist Rune"]); + Config.Recipes.push([Recipe.Rune, "Gul Rune"]); + Config.Recipes.push([Recipe.Rune, "Vex Rune"]); + Config.Recipes.push([Recipe.Rune, "Ohm Rune"]); + } + + Config.Recipes.push([Recipe.Rune, "Lo Rune"]); + Config.Recipes.push([Recipe.Rune, "Sur Rune"]); + Config.Recipes.push([Recipe.Rune, "Ber Rune"]); + } + + } + + if (!me.checkItem({ name: sdk.locale.items.CalltoArms }).have) { + // Cube to Mal rune + if (!me.getItem(sdk.items.runes.Mal) && me.equipped.get(sdk.body.RightArm).tier >= 110000) { + Config.Recipes.push([Recipe.Rune, "Um Rune"]); + } + + // Cube to Ohm rune + if (!me.getItem(sdk.items.runes.Ohm)) { + Config.Recipes.push([Recipe.Rune, "Gul Rune"]); + Config.Recipes.push([Recipe.Rune, "Vex Rune"]); + } + } + + // Dragon Armor + if ((me.ladder || Developer.addLadderRW) && !me.checkItem({ name: sdk.locale.items.Dragon, itemtype: sdk.items.type.Armor }).have && dreamerCheck) { + includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/DragonArmor.js"); + } + + if (!me.checkItem({ name: sdk.locale.items.HandofJustice }).have && dreamerCheck) { + includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/HandOfJustice.js"); + + // Azurewrath + NTIP.addLine("[name] == phaseblade && [flag] != ethereal && [quality] == unique # [enhanceddamage] >= 230 && [sanctuaryaura] >= 10 # [tier] == 115000"); + } + + if (!me.haveAll([{ name: sdk.locale.items.CrescentMoon }, { name: sdk.locale.items.HandofJustice }]) && dreamerCheck) { + includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/CrescentMoon.js"); + + // Lightsabre + NTIP.addLine("[name] == phaseblade && [flag] != ethereal && [quality] == unique # [enhanceddamage] >= 150 && [itemabsorblightpercent] == 25 # [tier] == 105000"); + } + + if (!me.checkItem({ name: sdk.locale.items.VoiceofReason }).have && !me.haveSome([{ name: sdk.locale.items.CrescentMoon }, { name: sdk.locale.items.HandofJustice }]) && dreamerCheck) { + includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/VoiceOfReason.js"); + } + + if ((me.ladder || Developer.addLadderRW) && Item.getMercEquipped(sdk.body.Armor).prefixnum !== sdk.locale.items.Fortitude + && me.haveAll([{ name: sdk.locale.items.HandofJustice }, { name: sdk.locale.items.Dragon, itemtype: sdk.items.type.Armor }, + { name: sdk.locale.items.Dream, itemtype: sdk.items.type.AuricShields }, { name: sdk.locale.items.Dream, itemtype: sdk.items.type.Helm }])) { + includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/MercFortitude.js"); + } + + break; + case "Sancdreamer": + dreamerCheck = me.haveAll([{ name: sdk.locale.items.Dream, itemtype: sdk.items.type.AuricShields }, { name: sdk.locale.items.Dream, itemtype: sdk.items.type.Helm }]); + + // Dream Shield + if ((me.ladder || Developer.addLadderRW) && !me.checkItem({ name: sdk.locale.items.Dream, itemtype: sdk.items.type.AuricShields }).have) { + includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/DreamShield.js"); + } + + // Dream Helm + if ((me.ladder || Developer.addLadderRW) && !me.checkItem({ name: sdk.locale.items.Dream, itemtype: sdk.items.type.Helm }).have) { + includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/DreamHelm.js"); + } + + if ((me.ladder || Developer.addLadderRW) && !dreamerCheck) { + // Cube to Jah rune + if (!me.getItem(sdk.items.runes.Jah)) { + if (me.checkItem({ name: sdk.locale.items.CalltoArms }).have) { + Config.Recipes.push([Recipe.Rune, "Mal Rune"]); + Config.Recipes.push([Recipe.Rune, "Ist Rune"]); + Config.Recipes.push([Recipe.Rune, "Gul Rune"]); + Config.Recipes.push([Recipe.Rune, "Vex Rune"]); + Config.Recipes.push([Recipe.Rune, "Ohm Rune"]); + } + + Config.Recipes.push([Recipe.Rune, "Lo Rune"]); + Config.Recipes.push([Recipe.Rune, "Sur Rune"]); + Config.Recipes.push([Recipe.Rune, "Ber Rune"]); + } + } + + if (!me.checkItem({ name: sdk.locale.items.CalltoArms }).have) { + // Cube to Mal rune + if (!me.getItem(sdk.items.runes.Mal) && me.equipped.get(sdk.body.RightArm).tier >= 110000) { + Config.Recipes.push([Recipe.Rune, "Um Rune"]); + } + + // Cube to Ohm rune + if (!me.getItem(sdk.items.runes.Ohm)) { + Config.Recipes.push([Recipe.Rune, "Gul Rune"]); + Config.Recipes.push([Recipe.Rune, "Vex Rune"]); + } + } + + // Chains of Honor + if (!me.checkItem({ name: sdk.locale.items.ChainsofHonor }).have) { + includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/ChainsOfHonor.js"); + } + + if (!me.checkItem({ name: sdk.locale.items.LastWish }).have && dreamerCheck) { + includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/LastWish.js"); + } + + if (!me.haveAll([{ name: sdk.locale.items.CrescentMoon }, { name: sdk.locale.items.LastWish }]) && dreamerCheck) { + includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/CrescentMoon.js"); + + // Lightsabre + NTIP.addLine("[name] == phaseblade && [flag] != ethereal && [quality] == unique # [enhanceddamage] >= 150 && [itemabsorblightpercent] == 25 # [tier] == 105000"); + } + + if (!me.checkItem({ name: sdk.locale.items.VoiceofReason }).have && !me.haveSome([{ name: sdk.locale.items.CrescentMoon }, { name: sdk.locale.items.LastWish }]) && dreamerCheck) { + includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/VoiceOfReason.js"); + } + + if ((me.ladder || Developer.addLadderRW) && me.haveAll([{ name: sdk.locale.items.LastWish }, { name: sdk.locale.items.ChainsofHonor }, + { name: sdk.locale.items.Dream, itemtype: sdk.items.type.AuricShields }, { name: sdk.locale.items.Dream, itemtype: sdk.items.type.Helm }])) { + // Infinity + if ((me.ladder || Developer.addLadderRW) && Item.getMercEquipped(sdk.body.RightArm).prefixnum !== sdk.locale.items.Infinity) { + if (!isIncluded("SoloPlay/BuildFiles/Runewords/MercInfinity.js")) { + include("SoloPlay/BuildFiles/Runewords/MercInfinity.js"); + } + } + // Merc Fortitude + if (Item.getMercEquipped(sdk.body.Armor).prefixnum !== sdk.locale.items.Fortitude) { + if (!isIncluded("SoloPlay/BuildFiles/Runewords/MercFortitude.js")) { + include("SoloPlay/BuildFiles/Runewords/MercFortitude.js"); + } + } + } + + break; + case "Torchadin": + // Dragon Armor + if ((me.ladder || Developer.addLadderRW) && !me.checkItem({ name: sdk.locale.items.Dragon, itemtype: sdk.items.type.Armor }).have) { + includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/DragonArmor.js"); + } + + if (!me.checkItem({ name: sdk.locale.items.HandofJustice }).have) { + includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/HandOfJustice.js"); + + // Azurewrath + NTIP.addLine("[name] == phaseblade && [flag] != ethereal && [quality] == unique # [enhanceddamage] >= 230 && [sanctuaryaura] >= 10 # [tier] == 115000"); + } + + // Exile + if ((me.ladder || Developer.addLadderRW) && !me.checkItem({ name: sdk.locale.items.Exile, itemtype: sdk.items.type.AuricShields }).have) { + includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/Exile.js"); + } + + if ((me.ladder || Developer.addLadderRW) && !me.haveAll([{ name: sdk.locale.items.HandofJustice }, + { name: sdk.locale.items.Exile, itemtype: sdk.items.type.AuricShields }, { name: sdk.locale.items.Dragon, itemtype: sdk.items.type.Armor }])) { + // Cube to Cham rune + if (!me.getItem(sdk.items.runes.Cham) || !me.getItem(sdk.items.runes.Sur) || !me.getItem(sdk.items.runes.Lo)) { + if (me.checkItem({ name: sdk.locale.items.CalltoArms }).have) { + Config.Recipes.push([Recipe.Rune, "Mal Rune"]); + Config.Recipes.push([Recipe.Rune, "Ist Rune"]); + Config.Recipes.push([Recipe.Rune, "Gul Rune"]); + + if (me.checkItem({ name: sdk.locale.items.Exile, itemtype: sdk.items.type.AuricShields }).have) { + Config.Recipes.push([Recipe.Rune, "Vex Rune"]); + Config.Recipes.push([Recipe.Rune, "Ohm Rune"]); + } else if (!me.checkItem({ name: sdk.locale.items.Exile, itemtype: sdk.items.type.AuricShields }).have && !me.getItem(sdk.items.runes.Ohm)) { + Config.Recipes.push([Recipe.Rune, "Vex Rune"]); + } + } + + if (me.checkItem({ name: sdk.locale.items.Dragon, itemtype: sdk.items.type.Armor }).have) { + Config.Recipes.push([Recipe.Rune, "Lo Rune"]); + Config.Recipes.push([Recipe.Rune, "Sur Rune"]); + } else if ((!me.haveAll([{ name: sdk.locale.items.Dragon, itemtype: sdk.items.type.Armor }, { name: sdk.locale.items.HandofJustice }]) && !me.getItem(sdk.items.runes.Sur))) { + Config.Recipes.push([Recipe.Rune, "Lo Rune"]); + } + + Config.Recipes.push([Recipe.Rune, "Ber Rune"]); + Config.Recipes.push([Recipe.Rune, "Jah Rune"]); + } + } + + if (!me.checkItem({ name: sdk.locale.items.CalltoArms }).have) { + // Cube to Mal rune + if (!me.getItem(sdk.items.runes.Mal) && me.equipped.get(sdk.body.RightArm).tier >= 110000) { + Config.Recipes.push([Recipe.Rune, "Um Rune"]); + } + + // Cube to Ohm rune + if (!me.getItem(sdk.items.runes.Ohm)) { + Config.Recipes.push([Recipe.Rune, "Gul Rune"]); + Config.Recipes.push([Recipe.Rune, "Vex Rune"]); + } + } + + if (!me.haveAll([{ name: sdk.locale.items.CrescentMoon }, { name: sdk.locale.items.HandofJustice }])) { + includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/CrescentMoon.js"); + + // Lightsabre + NTIP.addLine("[name] == phaseblade && [flag] != ethereal && [quality] == unique # [enhanceddamage] >= 150 && [itemabsorblightpercent] == 25 # [tier] == 105000"); + } + + if (!me.checkItem({ name: sdk.locale.items.VoiceofReason }).have && !me.haveSome([{ name: sdk.locale.items.CrescentMoon }, { name: sdk.locale.items.HandofJustice }])) { + includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/VoiceOfReason.js"); + } + + if ((me.ladder || Developer.addLadderRW) && Item.getMercEquipped(sdk.body.Armor).prefixnum !== sdk.locale.items.Fortitude + && me.haveAll([{ name: sdk.locale.items.HandofJustice }, { name: sdk.locale.items.Dragon, itemtype: sdk.items.type.Armor }, { name: sdk.locale.items.Exile, itemtype: sdk.items.type.AuricShields }])) { + includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/MercFortitude.js"); + } + break; + default: + break; + } + + Check.itemSockables(sdk.items.RoundShield, "unique", "Moser's Blessed Circle"); + Check.itemSockables(sdk.items.Shako, "unique", "Harlequin Crest"); + + // Call to Arms + if (!me.checkItem({ name: sdk.locale.items.CalltoArms }).have) { + includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/CallToArms.js"); + } + + // Enigma - Don't make if not Smiter or Hammerdin + if (!me.checkItem({ name: sdk.locale.items.Enigma }).have && ["Hammerdin", "Smiter"].includes(SetUp.finalBuild)) { + includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/Enigma.js"); + } + + // Spirit Sword + if ((me.ladder || Developer.addLadderRW) && me.equipped.get(sdk.body.RightArm).tier < 777) { + includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/SpiritSword.js"); + } + + // Spirit Shield + if ((me.ladder || Developer.addLadderRW) && (me.equipped.get(sdk.body.LeftArm).tier < 1000 || me.equipped.get(sdk.body.LeftArmSecondary).prefixnum !== sdk.locale.items.Spirit)) { + includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/SpiritShield.js"); + } + + // Merc Insight + if ((me.ladder || Developer.addLadderRW) && Item.getMercEquipped(sdk.body.RightArm).tier < 3600) { + includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/MercInsight.js"); + } + + // Lore + if (me.equipped.get(sdk.body.Head).tier < 315) { + includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/Lore.js"); + } + + /** + * @todo Rhyme and Splendor + */ + + // Ancients' Pledge + if (me.equipped.get(sdk.body.LeftArm).tier < 500) { + includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/AncientsPledge.js"); + } + + // Merc Fortitude + if (Item.getMercEquipped(sdk.body.Armor).prefixnum !== sdk.locale.items.Fortitude && ["Hammerdin", "Smiter"].includes(SetUp.finalBuild)) { + includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/MercFortitude.js"); + } + + // Merc Treachery + if (Item.getMercEquipped(sdk.body.Armor).tier < 15000) { + includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/MercTreachery.js"); + } + + // Smoke + if (me.equipped.get(sdk.body.Armor).tier < 450) { + includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/Smoke.js"); + } + + // Stealth + if (me.equipped.get(sdk.body.Armor).tier < 233) { + includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/Stealth.js"); + } + + /* if (SetUp.currentBuild === "Start") { + let { maxStr, maxDex } = Check.currentBuild(); + let basic = "[flag] != ethereal && [quality] >= normal && [quality] <= superior"; + let myStatsReq = "[strreq] <= " + maxStr + " && [dexreq] <= " + maxDex; + let statsReq = "[sockets] == 2"; + let quantityReq = "[maxquantity] == 1"; + // add Steel RW + NTIP.buildList([ + "[name] == TirRune # # [maxquantity] == 2", + "[name] == ElRune # # [maxquantity] == 2", + "([type] == sword || [type] == mace || [type] == axe) && " + basic + " && " + myStatsReq + " # " + statsReq + " # " + quantityReq, + ]); + + // need to make runewords able to process types, maybe a hacky method of just taking a nip line? + // or could create a base item data module and loop through whatever meets the reqs but that feels ugly too + Config.KeepRunewords.push("([type] == sword || [type] == mace || [type] == axe) # [enhanceddamage] >= 20 && [ias] >= 25"); + } */ + + SoloWants.buildList(); + + break; + } + + return true; })(); diff --git a/libs/SoloPlay/Config/Sorceress.js b/libs/SoloPlay/Config/Sorceress.js index 4715aea9..ae95467f 100644 --- a/libs/SoloPlay/Config/Sorceress.js +++ b/libs/SoloPlay/Config/Sorceress.js @@ -18,250 +18,250 @@ */ (function LoadConfig () { - includeIfNotIncluded("SoloPlay/Functions/MiscOverrides.js"); - includeIfNotIncluded("SoloPlay/Functions/Globals.js"); - - SetUp.include(); - SetUp.config(); - - /* Pickit configuration. */ - Config.PickRange = 40; - // Config.PickitFiles.push("kolton.nip"); - // Config.PickitFiles.push("test.nip"); - - /* Gambling configuration. */ - // TODO: should gambling be re-written to try and gamble for our current lowest tier'd item - // for example if our gloves are the lowest tier then only gamble gloves or maybe just make the others conditional like why include - // gambling for rings/ammys if we have our end game one - Config.GambleItems.push("Amulet"); - Config.GambleItems.push("Ring"); - Config.GambleItems.push("Circlet"); - Config.GambleItems.push("Coronet"); - - // AutoEquip setup - const levelingTiers = [ - // Weapon - "me.normal && [type] == orb && [quality] >= normal && [flag] != ethereal # [itemchargedskill] >= 0 # [tier] == tierscore(item)", - "me.charlvl > 1 && ([type] == orb || [type] == wand || [type] == sword || [type] == knife) && ([quality] >= magic || [flag] == runeword) && [flag] != ethereal && [2handed] == 0 # [itemchargedskill] >= 0 # [tier] == tierscore(item)", - "me.classic && [type] == staff && [quality] >= magic # [itemchargedskill] >= 0 # [tier] == tierscore(item)", - // Shield - "[type] == shield && ([quality] >= magic || [flag] == runeword) && [flag] != ethereal # [itemchargedskill] >= 0 # [tier] == tierscore(item)", - "me.classic && [type] == shield && [quality] >= normal # [itemchargedskill] >= 0 # [tier] == tierscore(item)", - // non runeword white items - "([type] == shield) && [quality] >= normal && [flag] != ethereal && [flag] != runeword # [itemchargedskill] >= 0 && ([sockets] == 1 || [sockets] == 2) # [tier] == tierscore(item)", - ]; - - const expansionTiers = [ - // Switch - "[type] == wand && [quality] >= Normal # [itemchargedskill] == 72 # [secondarytier] == 25000", // Weaken charged wand - "[type] == wand && [quality] >= Normal # [itemchargedskill] == 91 # [secondarytier] == 50000 + chargeditemscore(item, 91)", // Lower Resist charged wand - // Charms - "[name] == smallcharm && [quality] == magic # [maxhp] >= 1 # [invoquantity] == 2 && [charmtier] == charmscore(item)", - "[name] == smallcharm && [quality] == magic # [itemmagicbonus] >= 1 # [invoquantity] == 2 && [charmtier] == charmscore(item)", - "[name] == smallcharm && [quality] == magic # [fireresist]+[lightresist]+[coldresist]+[poisonresist] >= 1 # [invoquantity] == 2 && [charmtier] == charmscore(item)", - "me.charlvl < 40 && [name] == smallcharm && [quality] == magic ## [invoquantity] == 4 && [charmtier] == charmscore(item)", - "[name] == grandcharm && [quality] == magic # # [invoquantity] == 2 && [charmtier] == charmscore(item)", - ]; - - if (SetUp.currentBuild !== "Start") { - NTIP.addLine("([type] == orb || [type] == wand || [type] == sword || [type] == knife) && ([quality] >= magic || [flag] == runeword) && [flag] == ethereal && [2handed] == 0 # [itemchargedskill] >= 0 # [tier] == tierscore(item)"); - } - - /* Attack configuration. */ - Skill.usePvpRange = true; - Config.AttackSkill = [0, 0, 0, 0, 0, 0, 0]; - Config.LowManaSkill = [-1, -1]; - Config.MaxAttackCount = 1000; - Config.BossPriority = false; - Config.ClearType = 0; - Config.ClearPath = { Range: 30, Spectype: (me.hell && Pather.canTeleport() ? 0xF : 0) }; - - /* Monster skip configuration. */ - Pather.canTeleport() && me.lightRes < 75 && Config.SkipEnchant.push("lightning enchanted"); - - /* Class specific configuration. */ - Config.UseTelekinesis = true; // use telekinesis if have skill - Config.UseColdArmor = true; - Config.Dodge = !!(me.charlvl >= CharInfo.respecOne); // Move away from monsters that get too close. Don't use with short-ranged attacks like Poison Dagger. - Config.DodgeRange = 15; // Distance to keep from monsters. - Config.DodgeHP = me.hardcore ? 90 : 75; // Dodge only if HP percent is less than or equal to Config.DodgeHP. 100 = always dodge. - Config.TeleStomp = false; // Use merc to attack bosses if they're immune to attacks, but not to physical damage - Config.CastStatic = me.classic ? 15 : [25, 33, 50][me.diff]; - - /* Gear */ - NTIP.buildList(levelingTiers); - me.expansion && NTIP.buildList(expansionTiers); - - Config.imbueables = [ - { name: sdk.items.JaredsStone, condition: () => (me.normal && me.expansion) }, - { name: sdk.items.SwirlingCrystal, condition: () => (!me.normal && me.charlvl < 66 && me.expansion) }, - { name: sdk.items.DimensionalShard, condition: () => (me.equipped.get(sdk.body.RightArm).tier < 777 && me.expansion) }, - { name: sdk.items.Belt, condition: () => (me.normal && (me.equipped.get(sdk.body.RightArm).tier > 777 || me.classic)) }, - { name: sdk.items.MeshBelt, condition: () => (!me.normal && me.charlvl < 46 && me.trueStr > 58 && (me.equipped.get(sdk.body.RightArm).tier > 777 || me.classic)) }, - { name: sdk.items.SpiderwebSash, condition: () => (!me.normal && me.trueStr > 50 && (me.equipped.get(sdk.body.RightArm).tier > 777 || me.classic)) }, - ].filter((item) => item.condition()); - - let imbueArr = SetUp.imbueItems(); - - !me.smith && NTIP.buildList(imbueArr); - - switch (me.gametype) { - case sdk.game.gametype.Classic: - // Res shield - if ((me.equipped.get(sdk.body.LeftArm).tier < 487 && !me.equipped.get(sdk.body.RightArm).twoHanded) || (me.equipped.get(sdk.body.RightArm).tier < 487 && me.equipped.get(sdk.body.RightArm).twoHanded)) { - includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/PDiamondShield.js"); - } - - break; - case sdk.game.gametype.Expansion: - NTIP.addLine("[name] >= VexRune && [name] <= ZodRune"); - const { basicSocketables, addSocketableObj } = require("../Utils/General"); - - /* Crafting */ - if (me.equipped.get(sdk.body.Neck).tier < 100000) { - Config.Recipes.push([Recipe.Caster.Amulet]); - } - - { - let maxedMage = me.getItemsEx() - .filter(i => i.itemType === sdk.items.type.Gloves) - .find(i => NTIP.GetTier(i) >= 110000); - - if (!maxedMage) { - Config.Recipes.push([Recipe.Unique.Armor.ToExceptional, "Light Gauntlets", Roll.NonEth]); - ["Blova", "Lightning"].includes(SetUp.finalBuild) && Config.Recipes.push([Recipe.Unique.Armor.ToElite, "Battle Gauntlets", Roll.NonEth, "magefist"]); - } - } - - Config.socketables = Config.socketables.concat(basicSocketables.caster, basicSocketables.all); - - // FinalBuild specific setup - switch (SetUp.finalBuild) { - case "Blova": - case "Lightning": - // Infinity - if ((me.ladder || Developer.addLadderRW) && Item.getMercEquipped(sdk.body.RightArm).prefixnum !== sdk.locale.items.Infinity) { - includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/MercInfinity.js"); - } - - // Spirit Shield - if ((me.ladder || Developer.addLadderRW) && SetUp.currentBuild === SetUp.finalBuild && (me.equipped.get(sdk.body.LeftArm).tier < 1000 || me.equipped.get(sdk.body.LeftArmSecondary).prefixnum !== sdk.locale.items.Spirit)) { - includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/SpiritShield.js"); - } - - // Chains of Honor - if (!me.checkItem({ name: sdk.locale.items.ChainsofHonor }).have) { - includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/ChainsOfHonor.js"); - } - - // Heart of the Oak - if (!me.checkItem({ name: sdk.locale.items.HeartoftheOak }).have) { - includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/HeartOfTheOak.js"); - } - - Config.socketables.push(addSocketableObj(sdk.items.Monarch, [], [], - !me.hell, (item) => !Check.haveBase("monarch", 4) && item.ilvl >= 41 && item.isBaseType && !item.ethereal - )); - Config.socketables.push(addSocketableObj(sdk.items.Shako, [sdk.items.runes.Um], [sdk.items.gems.Perfect.Ruby], - true, (item) => item.unique && !item.ethereal - )); - - Check.itemSockables(sdk.items.Shako, "unique", "Harlequin Crest"); - - break; - case "Meteorb": - case "Cold": - case "Blizzballer": - Config.socketables.push(addSocketableObj(sdk.items.DeathMask, [sdk.items.runes.Um], [sdk.items.gems.Perfect.Ruby], - true, (item) => item.set && !item.ethereal - )); - Config.socketables.push(addSocketableObj(sdk.items.LacqueredPlate, [sdk.items.runes.Ber], [sdk.items.gems.Perfect.Ruby], - true, (item) => item.set && !item.ethereal - )); - Config.socketables.push(addSocketableObj(sdk.items.SwirlingCrystal, [sdk.items.runes.Ist], [], - false, (item) => item.set && !item.ethereal - )); - - Check.itemSockables(sdk.items.LacqueredPlate, "set", "Tal Rasha's Guardianship"); - Check.itemSockables(sdk.items.DeathMask, "set", "Tal Rasha's Horadric Crest"); - - break; - default: - break; - } - - // Go ahead and keep two P-diamonds prior to finding a moser's unless already using a better shield - if (!Check.haveItem("shield", "unique", "Moser's Blessed Circle") - && !me.haveSome([{ name: sdk.locale.items.Sanctuary }, { name: sdk.locale.items.Spirit, itemtype: sdk.items.type.Shield }])) { - NTIP.addLine("[name] == perfectdiamond # # [maxquantity] == 2"); - - if (Item.getQuantityOwned(me.getItem(sdk.items.gems.Perfect.Diamond)) < 2) { - Config.Recipes.push([Recipe.Gem, "flawlessdiamond"]); - } - } - - Check.itemSockables(sdk.items.RoundShield, "unique", "Moser's Blessed Circle"); - - // Sanctuary - if (!me.haveSome([{ name: sdk.locale.items.Sanctuary }, { name: sdk.locale.items.Spirit, itemtype: sdk.items.type.Shield }]) - && ["Blova", "Lightning"].indexOf(SetUp.currentBuild) === -1) { - includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/Sanctuary.js"); - } - - // Call to Arms - if (!me.checkItem({ name: sdk.locale.items.CalltoArms }).have) { - includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/CallToArms.js"); - } - - // Spirit Sword - if ((me.ladder || Developer.addLadderRW) && me.equipped.get(sdk.body.RightArm).tier < 777) { - includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/SpiritSword.js"); - } - - // Merc Insight - if ((me.ladder || Developer.addLadderRW) && Item.getMercEquipped(sdk.body.RightArm).tier < 3600) { - includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/MercInsight.js"); - } - - // Lore - if (me.equipped.get(sdk.body.Head).tier < 315) { - includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/Lore.js"); - } - - // Ancients' Pledge - if (me.equipped.get(sdk.body.LeftArm).tier < 500) { - includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/AncientsPledge.js"); - } - - // Merc Fortitude - if (Item.getMercEquipped(sdk.body.Armor).prefixnum !== sdk.locale.items.Fortitude) { - includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/MercFortitude.js"); - } - - // Bone - if (me.equipped.get(sdk.body.Armor).tier < 450) { - includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/Bone.js"); - } - - // Merc Treachery - if (Item.getMercEquipped(sdk.body.Armor).tier < 15000) { - includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/MercTreachery.js"); - } - - // Smoke - if (me.equipped.get(sdk.body.Armor).tier < 300) { - includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/Smoke.js"); - } - - // Stealth - if (me.equipped.get(sdk.body.Armor).tier < 233) { - includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/Stealth.js"); - } - - SoloWants.buildList(); - - break; - } - - return true; + includeIfNotIncluded("SoloPlay/Functions/MiscOverrides.js"); + includeIfNotIncluded("SoloPlay/Functions/Globals.js"); + + SetUp.include(); + SetUp.config(); + + /* Pickit configuration. */ + Config.PickRange = 40; + // Config.PickitFiles.push("kolton.nip"); + // Config.PickitFiles.push("test.nip"); + + /* Gambling configuration. */ + // TODO: should gambling be re-written to try and gamble for our current lowest tier'd item + // for example if our gloves are the lowest tier then only gamble gloves or maybe just make the others conditional like why include + // gambling for rings/ammys if we have our end game one + Config.GambleItems.push("Amulet"); + Config.GambleItems.push("Ring"); + Config.GambleItems.push("Circlet"); + Config.GambleItems.push("Coronet"); + + // AutoEquip setup + const levelingTiers = [ + // Weapon + "me.normal && [type] == orb && [quality] >= normal && [flag] != ethereal # [itemchargedskill] >= 0 # [tier] == tierscore(item)", + "me.charlvl > 1 && ([type] == orb || [type] == wand || [type] == sword || [type] == knife) && ([quality] >= magic || [flag] == runeword) && [flag] != ethereal && [2handed] == 0 # [itemchargedskill] >= 0 # [tier] == tierscore(item)", + "me.classic && [type] == staff && [quality] >= magic # [itemchargedskill] >= 0 # [tier] == tierscore(item)", + // Shield + "[type] == shield && ([quality] >= magic || [flag] == runeword) && [flag] != ethereal # [itemchargedskill] >= 0 # [tier] == tierscore(item)", + "me.classic && [type] == shield && [quality] >= normal # [itemchargedskill] >= 0 # [tier] == tierscore(item)", + // non runeword white items + "([type] == shield) && [quality] >= normal && [flag] != ethereal && [flag] != runeword # [itemchargedskill] >= 0 && ([sockets] == 1 || [sockets] == 2) # [tier] == tierscore(item)", + ]; + + const expansionTiers = [ + // Switch + "[type] == wand && [quality] >= Normal # [itemchargedskill] == 72 # [secondarytier] == 25000", // Weaken charged wand + "[type] == wand && [quality] >= Normal # [itemchargedskill] == 91 # [secondarytier] == 50000 + chargeditemscore(item, 91)", // Lower Resist charged wand + // Charms + "[name] == smallcharm && [quality] == magic # [maxhp] >= 1 # [invoquantity] == 2 && [charmtier] == charmscore(item)", + "[name] == smallcharm && [quality] == magic # [itemmagicbonus] >= 1 # [invoquantity] == 2 && [charmtier] == charmscore(item)", + "[name] == smallcharm && [quality] == magic # [fireresist]+[lightresist]+[coldresist]+[poisonresist] >= 1 # [invoquantity] == 2 && [charmtier] == charmscore(item)", + "me.charlvl < 40 && [name] == smallcharm && [quality] == magic ## [invoquantity] == 4 && [charmtier] == charmscore(item)", + "[name] == grandcharm && [quality] == magic # # [invoquantity] == 2 && [charmtier] == charmscore(item)", + ]; + + if (SetUp.currentBuild !== "Start") { + NTIP.addLine("([type] == orb || [type] == wand || [type] == sword || [type] == knife) && ([quality] >= magic || [flag] == runeword) && [flag] == ethereal && [2handed] == 0 # [itemchargedskill] >= 0 # [tier] == tierscore(item)"); + } + + /* Attack configuration. */ + Skill.usePvpRange = true; + Config.AttackSkill = [0, 0, 0, 0, 0, 0, 0]; + Config.LowManaSkill = [-1, -1]; + Config.MaxAttackCount = 1000; + Config.BossPriority = false; + Config.ClearType = 0; + Config.ClearPath = { Range: 30, Spectype: (me.hell && Pather.canTeleport() ? 0xF : 0) }; + + /* Monster skip configuration. */ + Pather.canTeleport() && me.lightRes < 75 && Config.SkipEnchant.push("lightning enchanted"); + + /* Class specific configuration. */ + Config.UseTelekinesis = true; // use telekinesis if have skill + Config.UseColdArmor = true; + Config.Dodge = !!(me.charlvl >= CharInfo.respecOne); // Move away from monsters that get too close. Don't use with short-ranged attacks like Poison Dagger. + Config.DodgeRange = 15; // Distance to keep from monsters. + Config.DodgeHP = me.hardcore ? 90 : 75; // Dodge only if HP percent is less than or equal to Config.DodgeHP. 100 = always dodge. + Config.TeleStomp = false; // Use merc to attack bosses if they're immune to attacks, but not to physical damage + Config.CastStatic = me.classic ? 15 : [25, 33, 50][me.diff]; + + /* Gear */ + NTIP.buildList(levelingTiers); + me.expansion && NTIP.buildList(expansionTiers); + + Config.imbueables = [ + { name: sdk.items.JaredsStone, condition: () => (me.normal && me.expansion) }, + { name: sdk.items.SwirlingCrystal, condition: () => (!me.normal && me.charlvl < 66 && me.expansion) }, + { name: sdk.items.DimensionalShard, condition: () => (me.equipped.get(sdk.body.RightArm).tier < 777 && me.expansion) }, + { name: sdk.items.Belt, condition: () => (me.normal && (me.equipped.get(sdk.body.RightArm).tier > 777 || me.classic)) }, + { name: sdk.items.MeshBelt, condition: () => (!me.normal && me.charlvl < 46 && me.trueStr > 58 && (me.equipped.get(sdk.body.RightArm).tier > 777 || me.classic)) }, + { name: sdk.items.SpiderwebSash, condition: () => (!me.normal && me.trueStr > 50 && (me.equipped.get(sdk.body.RightArm).tier > 777 || me.classic)) }, + ].filter((item) => item.condition()); + + let imbueArr = SetUp.imbueItems(); + + !me.smith && NTIP.buildList(imbueArr); + + switch (me.gametype) { + case sdk.game.gametype.Classic: + // Res shield + if ((me.equipped.get(sdk.body.LeftArm).tier < 487 && !me.equipped.get(sdk.body.RightArm).twoHanded) || (me.equipped.get(sdk.body.RightArm).tier < 487 && me.equipped.get(sdk.body.RightArm).twoHanded)) { + includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/PDiamondShield.js"); + } + + break; + case sdk.game.gametype.Expansion: + NTIP.addLine("[name] >= VexRune && [name] <= ZodRune"); + const { basicSocketables, addSocketableObj } = require("../Utils/General"); + + /* Crafting */ + if (me.equipped.get(sdk.body.Neck).tier < 100000) { + Config.Recipes.push([Recipe.Caster.Amulet]); + } + + { + let maxedMage = me.getItemsEx() + .filter(i => i.itemType === sdk.items.type.Gloves) + .find(i => NTIP.GetTier(i) >= 110000); + + if (!maxedMage) { + Config.Recipes.push([Recipe.Unique.Armor.ToExceptional, "Light Gauntlets", Roll.NonEth]); + ["Blova", "Lightning"].includes(SetUp.finalBuild) && Config.Recipes.push([Recipe.Unique.Armor.ToElite, "Battle Gauntlets", Roll.NonEth, "magefist"]); + } + } + + Config.socketables = Config.socketables.concat(basicSocketables.caster, basicSocketables.all); + + // FinalBuild specific setup + switch (SetUp.finalBuild) { + case "Blova": + case "Lightning": + // Infinity + if ((me.ladder || Developer.addLadderRW) && Item.getMercEquipped(sdk.body.RightArm).prefixnum !== sdk.locale.items.Infinity) { + includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/MercInfinity.js"); + } + + // Spirit Shield + if ((me.ladder || Developer.addLadderRW) && SetUp.currentBuild === SetUp.finalBuild && (me.equipped.get(sdk.body.LeftArm).tier < 1000 || me.equipped.get(sdk.body.LeftArmSecondary).prefixnum !== sdk.locale.items.Spirit)) { + includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/SpiritShield.js"); + } + + // Chains of Honor + if (!me.checkItem({ name: sdk.locale.items.ChainsofHonor }).have) { + includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/ChainsOfHonor.js"); + } + + // Heart of the Oak + if (!me.checkItem({ name: sdk.locale.items.HeartoftheOak }).have) { + includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/HeartOfTheOak.js"); + } + + Config.socketables.push(addSocketableObj(sdk.items.Monarch, [], [], + !me.hell, (item) => !Check.haveBase("monarch", 4) && item.ilvl >= 41 && item.isBaseType && !item.ethereal + )); + Config.socketables.push(addSocketableObj(sdk.items.Shako, [sdk.items.runes.Um], [sdk.items.gems.Perfect.Ruby], + true, (item) => item.unique && !item.ethereal + )); + + Check.itemSockables(sdk.items.Shako, "unique", "Harlequin Crest"); + + break; + case "Meteorb": + case "Cold": + case "Blizzballer": + Config.socketables.push(addSocketableObj(sdk.items.DeathMask, [sdk.items.runes.Um], [sdk.items.gems.Perfect.Ruby], + true, (item) => item.set && !item.ethereal + )); + Config.socketables.push(addSocketableObj(sdk.items.LacqueredPlate, [sdk.items.runes.Ber], [sdk.items.gems.Perfect.Ruby], + true, (item) => item.set && !item.ethereal + )); + Config.socketables.push(addSocketableObj(sdk.items.SwirlingCrystal, [sdk.items.runes.Ist], [], + false, (item) => item.set && !item.ethereal + )); + + Check.itemSockables(sdk.items.LacqueredPlate, "set", "Tal Rasha's Guardianship"); + Check.itemSockables(sdk.items.DeathMask, "set", "Tal Rasha's Horadric Crest"); + + break; + default: + break; + } + + // Go ahead and keep two P-diamonds prior to finding a moser's unless already using a better shield + if (!Check.haveItem("shield", "unique", "Moser's Blessed Circle") + && !me.haveSome([{ name: sdk.locale.items.Sanctuary }, { name: sdk.locale.items.Spirit, itemtype: sdk.items.type.Shield }])) { + NTIP.addLine("[name] == perfectdiamond # # [maxquantity] == 2"); + + if (Item.getQuantityOwned(me.getItem(sdk.items.gems.Perfect.Diamond)) < 2) { + Config.Recipes.push([Recipe.Gem, "flawlessdiamond"]); + } + } + + Check.itemSockables(sdk.items.RoundShield, "unique", "Moser's Blessed Circle"); + + // Sanctuary + if (!me.haveSome([{ name: sdk.locale.items.Sanctuary }, { name: sdk.locale.items.Spirit, itemtype: sdk.items.type.Shield }]) + && ["Blova", "Lightning"].indexOf(SetUp.currentBuild) === -1) { + includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/Sanctuary.js"); + } + + // Call to Arms + if (!me.checkItem({ name: sdk.locale.items.CalltoArms }).have) { + includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/CallToArms.js"); + } + + // Spirit Sword + if ((me.ladder || Developer.addLadderRW) && me.equipped.get(sdk.body.RightArm).tier < 777) { + includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/SpiritSword.js"); + } + + // Merc Insight + if ((me.ladder || Developer.addLadderRW) && Item.getMercEquipped(sdk.body.RightArm).tier < 3600) { + includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/MercInsight.js"); + } + + // Lore + if (me.equipped.get(sdk.body.Head).tier < 315) { + includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/Lore.js"); + } + + // Ancients' Pledge + if (me.equipped.get(sdk.body.LeftArm).tier < 500) { + includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/AncientsPledge.js"); + } + + // Merc Fortitude + if (Item.getMercEquipped(sdk.body.Armor).prefixnum !== sdk.locale.items.Fortitude) { + includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/MercFortitude.js"); + } + + // Bone + if (me.equipped.get(sdk.body.Armor).tier < 450) { + includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/Bone.js"); + } + + // Merc Treachery + if (Item.getMercEquipped(sdk.body.Armor).tier < 15000) { + includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/MercTreachery.js"); + } + + // Smoke + if (me.equipped.get(sdk.body.Armor).tier < 300) { + includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/Smoke.js"); + } + + // Stealth + if (me.equipped.get(sdk.body.Armor).tier < 233) { + includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/Stealth.js"); + } + + SoloWants.buildList(); + + break; + } + + return true; })(); diff --git a/libs/SoloPlay/Functions/AttackOverrides.js b/libs/SoloPlay/Functions/AttackOverrides.js index a36c6b15..98bf629d 100644 --- a/libs/SoloPlay/Functions/AttackOverrides.js +++ b/libs/SoloPlay/Functions/AttackOverrides.js @@ -12,84 +12,84 @@ Attack.stopClear = false; Attack.Result.NOOP = 4; Attack.init = function () { - const CLASSNAME = sdk.player.class.nameOf(me.classid); - if (Config.Wereform) { - include("core/Attacks/wereform.js"); - } else if (Config.CustomClassAttack && FileTools.exists("libs/core/Attacks/" + Config.CustomClassAttack + ".js")) { - console.log("Loading custom attack file"); - include("core/Attacks/" + Config.CustomClassAttack + ".js"); - } else { - if (!include("SoloPlay/Functions/ClassAttackOverrides/" + CLASSNAME + "Attacks.js")) { - console.log(sdk.colors.Red + "Failed to include: " + "SoloPlay/Functions/ClassAttackOverrides/" + CLASSNAME + "Attacks.js"); - console.log(sdk.colors.Blue + "Loading default attacks instead"); - include("core/Attacks/" + CLASSNAME + ".js"); - } - } - - if (Config.AttackSkill[1] < 0 || Config.AttackSkill[3] < 0) { - showConsole(); - console.warn("ÿc1Bad attack config. Don't expect your bot to attack."); - } - - this.getPrimarySlot(); - Skill.init(); - - if (me.expansion) { - Precast.checkCTA(); - this.checkInfinity(); - this.checkAuradin(); - this.getCurrentChargedSkillIds(true); - this.checkBowOnSwitch(true); - } + const CLASSNAME = sdk.player.class.nameOf(me.classid); + if (Config.Wereform) { + include("core/Attacks/wereform.js"); + } else if (Config.CustomClassAttack && FileTools.exists("libs/core/Attacks/" + Config.CustomClassAttack + ".js")) { + console.log("Loading custom attack file"); + include("core/Attacks/" + Config.CustomClassAttack + ".js"); + } else { + if (!include("SoloPlay/Functions/ClassAttackOverrides/" + CLASSNAME + "Attacks.js")) { + console.log(sdk.colors.Red + "Failed to include: " + "SoloPlay/Functions/ClassAttackOverrides/" + CLASSNAME + "Attacks.js"); + console.log(sdk.colors.Blue + "Loading default attacks instead"); + include("core/Attacks/" + CLASSNAME + ".js"); + } + } + + if (Config.AttackSkill[1] < 0 || Config.AttackSkill[3] < 0) { + showConsole(); + console.warn("ÿc1Bad attack config. Don't expect your bot to attack."); + } + + this.getPrimarySlot(); + Skill.init(); + + if (me.expansion) { + Precast.checkCTA(); + this.checkInfinity(); + this.checkAuradin(); + this.getCurrentChargedSkillIds(true); + this.checkBowOnSwitch(true); + } }; Attack.decideSkill = function (unit) { - let skills = { timed: -1, untimed: -1 }; - if (!unit) return skills; + let skills = { timed: -1, untimed: -1 }; + if (!unit) return skills; - const index = (unit.isSpecial || unit.isPlayer) ? 1 : 3; - const classid = unit.classid; + const index = (unit.isSpecial || unit.isPlayer) ? 1 : 3; + const classid = unit.classid; - // Get timed skill - let checkSkill = Attack.getCustomAttack(unit) ? Attack.getCustomAttack(unit)[0] : Config.AttackSkill[index]; + // Get timed skill + let checkSkill = Attack.getCustomAttack(unit) ? Attack.getCustomAttack(unit)[0] : Config.AttackSkill[index]; - if (Attack.checkResist(unit, checkSkill) && Attack.validSpot(unit.x, unit.y, checkSkill, classid)) { - skills.timed = checkSkill; - } else if (Config.AttackSkill[5] > -1 && Attack.checkResist(unit, Config.AttackSkill[5]) && Attack.validSpot(unit.x, unit.y, Config.AttackSkill[5], classid)) { - skills.timed = Config.AttackSkill[5]; - } + if (Attack.checkResist(unit, checkSkill) && Attack.validSpot(unit.x, unit.y, checkSkill, classid)) { + skills.timed = checkSkill; + } else if (Config.AttackSkill[5] > -1 && Attack.checkResist(unit, Config.AttackSkill[5]) && Attack.validSpot(unit.x, unit.y, Config.AttackSkill[5], classid)) { + skills.timed = Config.AttackSkill[5]; + } - // Get untimed skill - checkSkill = Attack.getCustomAttack(unit) ? Attack.getCustomAttack(unit)[1] : Config.AttackSkill[index + 1]; + // Get untimed skill + checkSkill = Attack.getCustomAttack(unit) ? Attack.getCustomAttack(unit)[1] : Config.AttackSkill[index + 1]; - if (Attack.checkResist(unit, checkSkill) && Attack.validSpot(unit.x, unit.y, checkSkill)) { - skills.untimed = checkSkill; - } else if (Config.AttackSkill[6] > -1 && Attack.checkResist(unit, Config.AttackSkill[6]) && Attack.validSpot(unit.x, unit.y, Config.AttackSkill[6], classid)) { - skills.untimed = Config.AttackSkill[6]; - } + if (Attack.checkResist(unit, checkSkill) && Attack.validSpot(unit.x, unit.y, checkSkill)) { + skills.untimed = checkSkill; + } else if (Config.AttackSkill[6] > -1 && Attack.checkResist(unit, Config.AttackSkill[6]) && Attack.validSpot(unit.x, unit.y, Config.AttackSkill[6], classid)) { + skills.untimed = Config.AttackSkill[6]; + } - // Low mana timed skill - if (Config.LowManaSkill[0] > -1 && Skill.getManaCost(skills.timed) > me.mp && Attack.checkResist(unit, Config.LowManaSkill[0])) { - skills.timed = Config.LowManaSkill[0]; - } + // Low mana timed skill + if (Config.LowManaSkill[0] > -1 && Skill.getManaCost(skills.timed) > me.mp && Attack.checkResist(unit, Config.LowManaSkill[0])) { + skills.timed = Config.LowManaSkill[0]; + } - // Low mana untimed skill - if (Config.LowManaSkill[1] > -1 && Skill.getManaCost(skills.untimed) > me.mp && Attack.checkResist(unit, Config.LowManaSkill[1])) { - skills.untimed = Config.LowManaSkill[1]; - } + // Low mana untimed skill + if (Config.LowManaSkill[1] > -1 && Skill.getManaCost(skills.untimed) > me.mp && Attack.checkResist(unit, Config.LowManaSkill[1])) { + skills.untimed = Config.LowManaSkill[1]; + } - return skills; + return skills; }; Attack.getLowerResistPercent = function () { - const calc = (level) => Math.floor(Math.min(25 + (45 * ((110 * level) / (level + 6)) / 100), 70)); - if (me.expansion && CharData.skillData.haveChargedSkillOnSwitch(sdk.skills.LowerResist)) { - return calc(CharData.skillData.chargedSkillsOnSwitch.find(chargeSkill => chargeSkill.skill === sdk.skills.LowerResist).level); - } - if (Skill.canUse(sdk.skills.LowerResist)) { - return calc(me.getSkill(sdk.skills.LowerResist, sdk.skills.subindex.SoftPoints)); - } - return 0; + const calc = (level) => Math.floor(Math.min(25 + (45 * ((110 * level) / (level + 6)) / 100), 70)); + if (me.expansion && CharData.skillData.haveChargedSkillOnSwitch(sdk.skills.LowerResist)) { + return calc(CharData.skillData.chargedSkillsOnSwitch.find(chargeSkill => chargeSkill.skill === sdk.skills.LowerResist).level); + } + if (Skill.canUse(sdk.skills.LowerResist)) { + return calc(me.getSkill(sdk.skills.LowerResist, sdk.skills.subindex.SoftPoints)); + } + return 0; }; /** @@ -100,55 +100,55 @@ Attack.getLowerResistPercent = function () { * @returns {boolean} true if they are not immune */ Attack.checkResist = function (unit, val = -1, maxres = 100) { - if (!unit || !unit.type || unit.type === sdk.unittype.Player) return true; - - const damageType = typeof val === "number" ? this.getSkillElement(val) : val; - const addLowerRes = !!(Skill.canUse(sdk.skills.LowerResist) || CharData.skillData.haveChargedSkillOnSwitch(sdk.skills.LowerResist)) && unit.curseable; - - // Static handler - if (val === sdk.skills.StaticField && this.getResist(unit, damageType) < 100) { - return unit.hpPercent > Config.CastStatic; - } - - // TODO: sometimes unit is out of range of conviction so need to check that - // baal in throne room doesn't have getState - if (this.infinity && ["fire", "lightning", "cold"].includes(damageType) && unit.getState) { - if (!unit.getState(sdk.states.Conviction)) { - if (addLowerRes && !unit.getState(sdk.states.LowerResist) && ((unit.isSpecial) || me.necromancer)) { - let lowerResPercent = this.getLowerResistPercent(); - return (this.getResist(unit, damageType) - (Math.floor((lowerResPercent + 85) / 5))) < 100; - } - return this.getResist(unit, damageType) < 117; - } - - return this.getResist(unit, damageType) < maxres; - } - - if (this.auradin && ["physical", "fire", "cold", "lightning"].includes(damageType) && me.getState(sdk.states.Conviction) && unit.getState) { - // our main dps is not physical despite using zeal - if (damageType === "physical") return true; - if (!unit.getState(sdk.states.Conviction)) return (this.getResist(unit, damageType) - (this.getConvictionPercent() / 5) < 100); - - let valid = false; - - // check unit's fire resistance - (me.getState(sdk.states.HolyFire)) && (valid = this.getResist(unit, "fire") < maxres); - // check unit's light resistance but only if the above check failed - (me.getState(sdk.states.HolyShock) && !valid) && (valid = this.getResist(unit, "lightning") < maxres); - - // TODO: maybe if still invalid at this point check physical resistance? Although if we are an auradin our physcial dps is low - - return valid; - } - - if (addLowerRes && ["fire", "lightning", "cold", "poison"].includes(damageType) && unit.getState) { - let lowerResPercent = this.getLowerResistPercent(); - if (!unit.getState(sdk.states.LowerResist) && ((unit.isSpecial && me.gold > 500000) || me.necromancer)) { - return (this.getResist(unit, damageType) - (Math.floor(lowerResPercent / 5)) < 100); - } - } - - return this.getResist(unit, damageType) < maxres; + if (!unit || !unit.type || unit.type === sdk.unittype.Player) return true; + + const damageType = typeof val === "number" ? this.getSkillElement(val) : val; + const addLowerRes = !!(Skill.canUse(sdk.skills.LowerResist) || CharData.skillData.haveChargedSkillOnSwitch(sdk.skills.LowerResist)) && unit.curseable; + + // Static handler + if (val === sdk.skills.StaticField && this.getResist(unit, damageType) < 100) { + return unit.hpPercent > Config.CastStatic; + } + + // TODO: sometimes unit is out of range of conviction so need to check that + // baal in throne room doesn't have getState + if (this.infinity && ["fire", "lightning", "cold"].includes(damageType) && unit.getState) { + if (!unit.getState(sdk.states.Conviction)) { + if (addLowerRes && !unit.getState(sdk.states.LowerResist) && ((unit.isSpecial) || me.necromancer)) { + let lowerResPercent = this.getLowerResistPercent(); + return (this.getResist(unit, damageType) - (Math.floor((lowerResPercent + 85) / 5))) < 100; + } + return this.getResist(unit, damageType) < 117; + } + + return this.getResist(unit, damageType) < maxres; + } + + if (this.auradin && ["physical", "fire", "cold", "lightning"].includes(damageType) && me.getState(sdk.states.Conviction) && unit.getState) { + // our main dps is not physical despite using zeal + if (damageType === "physical") return true; + if (!unit.getState(sdk.states.Conviction)) return (this.getResist(unit, damageType) - (this.getConvictionPercent() / 5) < 100); + + let valid = false; + + // check unit's fire resistance + (me.getState(sdk.states.HolyFire)) && (valid = this.getResist(unit, "fire") < maxres); + // check unit's light resistance but only if the above check failed + (me.getState(sdk.states.HolyShock) && !valid) && (valid = this.getResist(unit, "lightning") < maxres); + + // TODO: maybe if still invalid at this point check physical resistance? Although if we are an auradin our physcial dps is low + + return valid; + } + + if (addLowerRes && ["fire", "lightning", "cold", "poison"].includes(damageType) && unit.getState) { + let lowerResPercent = this.getLowerResistPercent(); + if (!unit.getState(sdk.states.LowerResist) && ((unit.isSpecial && me.gold > 500000) || me.necromancer)) { + return (this.getResist(unit, damageType) - (Math.floor(lowerResPercent / 5)) < 100); + } + } + + return this.getResist(unit, damageType) < maxres; }; /** @@ -157,25 +157,25 @@ Attack.checkResist = function (unit, val = -1, maxres = 100) { * @todo Maybe make this a prototype and use game data to also check if should attack not just can based on effort? */ Attack.canAttack = function (unit) { - if (!unit) return false; - if (unit.isMonster) { - // Unique/Champion - if (unit.isSpecial) { - if (Attack.checkResist(unit, this.getSkillElement(Config.AttackSkill[1])) || Attack.checkResist(unit, this.getSkillElement(Config.AttackSkill[2]))) { - return true; - } - } else { - if (Attack.checkResist(unit, this.getSkillElement(Config.AttackSkill[3])) || Attack.checkResist(unit, this.getSkillElement(Config.AttackSkill[4]))) { - return true; - } - } - - if (Config.AttackSkill.length === 7) { - return Attack.checkResist(unit, this.getSkillElement(Config.AttackSkill[5])) || Attack.checkResist(unit, this.getSkillElement(Config.AttackSkill[6])); - } - } - - return false; + if (!unit) return false; + if (unit.isMonster) { + // Unique/Champion + if (unit.isSpecial) { + if (Attack.checkResist(unit, this.getSkillElement(Config.AttackSkill[1])) || Attack.checkResist(unit, this.getSkillElement(Config.AttackSkill[2]))) { + return true; + } + } else { + if (Attack.checkResist(unit, this.getSkillElement(Config.AttackSkill[3])) || Attack.checkResist(unit, this.getSkillElement(Config.AttackSkill[4]))) { + return true; + } + } + + if (Config.AttackSkill.length === 7) { + return Attack.checkResist(unit, this.getSkillElement(Config.AttackSkill[5])) || Attack.checkResist(unit, this.getSkillElement(Config.AttackSkill[6])); + } + } + + return false; }; /** @@ -185,35 +185,35 @@ Attack.canAttack = function (unit) { * @returns {boolean} */ Attack.openChests = function (range, x, y) { - if (!Config.OpenChests.Enabled || !Misc.openChestsEnabled) return false; - range === undefined && (range = 10); - x === undefined && (x = me.x); - y === undefined && (y = me.y); - - if (me.getMobCount(range) > 1) return false; - - let list = []; - const ids = ["chest", "chest3", "weaponrack", "armorstand"]; - let unit = Game.getObject(); - - if (unit) { - do { - if (unit.name - && getDistance(unit, x, y) <= range - && unit.mode === sdk.objects.mode.Inactive - && ids.includes(unit.name.toLowerCase()) - && unit.getMobCount(10) === 0) { - list.push(copyUnit(unit)); - } - } while (unit.getNext()); - } - - while (list.length) { - list.sort(Sort.units); - Misc.openChest(list.shift()) && Pickit.pickItems(); - } - - return true; + if (!Config.OpenChests.Enabled || !Misc.openChestsEnabled) return false; + range === undefined && (range = 10); + x === undefined && (x = me.x); + y === undefined && (y = me.y); + + if (me.getMobCount(range) > 1) return false; + + let list = []; + const ids = ["chest", "chest3", "weaponrack", "armorstand"]; + let unit = Game.getObject(); + + if (unit) { + do { + if (unit.name + && getDistance(unit, x, y) <= range + && unit.mode === sdk.objects.mode.Inactive + && ids.includes(unit.name.toLowerCase()) + && unit.getMobCount(10) === 0) { + list.push(copyUnit(unit)); + } + } while (unit.getNext()); + } + + while (list.length) { + list.sort(Sort.units); + Misc.openChest(list.shift()) && Pickit.pickItems(); + } + + return true; }; /** @@ -221,120 +221,120 @@ Attack.openChests = function (range, x, y) { * @returns {boolean} */ Attack.killTarget = function (name) { - if (!name || Config.AttackSkill[1] < 0) return false; - typeof name === "string" && (name = name.toLowerCase()); - let target = (typeof name === "object" ? name : Misc.poll(() => Game.getMonster(name), 2000, 100)); - - if (!target) { - console.warn("ÿc8KillTargetÿc0 :: " + name + " not found. Performing Attack.Clear(25)"); - return (Attack.clear(25) && Pickit.pickItems()); - } - - // exit if target is immune - if (target && !Attack.canAttack(target) && !Attack.canTeleStomp(target)) { - console.warn("ÿc8KillTargetÿc0 :: Attack failed. " + target.name + " is immune."); - return false; - } - - const findTarget = function (gid, loc) { - let path = getPath(me.area, me.x, me.y, loc.x, loc.y, 1, 5); - if (!path) return false; - - if (path.some(function (node) { - Pather.walkTo(node.x, node.y); - return Game.getMonster(-1, -1, gid); - })) { - return Game.getMonster(-1, -1, gid); - } else { - return false; - } - }; - - // think doing this might be safer for non-teleporters, alot of the time they end up either stuck in recursive node action <-> clear loop - // or try to bull their way through mobs to the boss and instead should try to clear to them but without the loop - if (!Pather.canTeleport() && Attack.clear(15, 0, target)) return true; - - const who = (!!target.name ? target.name : name); - const gid = target.gid; - let errorInfo = ""; - let [retry, attackCount] = [0, 0]; - let lastLoc = { x: me.x, y: me.y }; - let tick = getTickCount(); - - try { - console.log("ÿc7Kill ÿc0:: " + who); - // disable opening chests while killing unit - Misc.openChestsEnabled = false; - - while (attackCount < Config.MaxAttackCount && target.attackable && !this.skipCheck(target)) { - // Check if unit got invalidated, happens if necro raises a skeleton from the boss's corpse. - if (!target || !copyUnit(target).x) { - target = Game.getMonster(-1, -1, gid); - !target && (target = findTarget(gid, lastLoc)); - - if (!target) { - console.warn("ÿc1Failed to kill " + who + " (couldn't relocate unit)"); - break; - } - } - - Config.Dodge && me.hpPercent <= Config.DodgeHP && this.deploy(target, Config.DodgeRange, 5, 9); - attackCount > 0 && attackCount % 15 === 0 && Skill.getRange(Config.AttackSkill[1]) < 4 && Packet.flash(me.gid); - me.overhead("KillTarget: " + target.name + " health " + target.hpPercent + " % left"); - let result = ClassAttack.doAttack(target, attackCount % 15 === 0); - - if (result === this.Result.FAILED) { - if (retry++ > 3) { - errorInfo = " (doAttack failed)"; - - break; - } - - Packet.flash(me.gid); - } else if (result === this.Result.CANTATTACK) { - errorInfo = " (No valid attack skills)"; - - break; - } else if (result === this.Result.NEEDMANA) { - continue; - } else { - retry = 0; - } - - lastLoc = { x: me.x, y: me.y }; - attackCount++; - - if (target.dead || Config.FastPick || (attackCount > 0 && attackCount % 5 === 0)) { - Config.FastPick ? Pickit.fastPick() : Pickit.essessntialsPick(false, true); - } - } - - attackCount === Config.MaxAttackCount && (errorInfo = " (attackCount exceeded: " + attackCount + ")"); - ClassAttack.afterAttack(); - Pickit.pickItems(); - - if (!!target && target.attackable) { - console.warn("ÿc1Failed to kill ÿc0" + who + errorInfo); - } else { - console.log("ÿc7Killed ÿc0:: " + who + "ÿc0 - ÿc7Duration: ÿc0" + Time.format(getTickCount() - tick)); - } - } finally { - // re-enable - Misc.openChestsEnabled = true; - } - - return (!target || !copyUnit(target).x || target.dead || !target.attackable); + if (!name || Config.AttackSkill[1] < 0) return false; + typeof name === "string" && (name = name.toLowerCase()); + let target = (typeof name === "object" ? name : Misc.poll(() => Game.getMonster(name), 2000, 100)); + + if (!target) { + console.warn("ÿc8KillTargetÿc0 :: " + name + " not found. Performing Attack.Clear(25)"); + return (Attack.clear(25) && Pickit.pickItems()); + } + + // exit if target is immune + if (target && !Attack.canAttack(target) && !Attack.canTeleStomp(target)) { + console.warn("ÿc8KillTargetÿc0 :: Attack failed. " + target.name + " is immune."); + return false; + } + + const findTarget = function (gid, loc) { + let path = getPath(me.area, me.x, me.y, loc.x, loc.y, 1, 5); + if (!path) return false; + + if (path.some(function (node) { + Pather.walkTo(node.x, node.y); + return Game.getMonster(-1, -1, gid); + })) { + return Game.getMonster(-1, -1, gid); + } else { + return false; + } + }; + + // think doing this might be safer for non-teleporters, alot of the time they end up either stuck in recursive node action <-> clear loop + // or try to bull their way through mobs to the boss and instead should try to clear to them but without the loop + if (!Pather.canTeleport() && Attack.clear(15, 0, target)) return true; + + const who = (!!target.name ? target.name : name); + const gid = target.gid; + let errorInfo = ""; + let [retry, attackCount] = [0, 0]; + let lastLoc = { x: me.x, y: me.y }; + let tick = getTickCount(); + + try { + console.log("ÿc7Kill ÿc0:: " + who); + // disable opening chests while killing unit + Misc.openChestsEnabled = false; + + while (attackCount < Config.MaxAttackCount && target.attackable && !this.skipCheck(target)) { + // Check if unit got invalidated, happens if necro raises a skeleton from the boss's corpse. + if (!target || !copyUnit(target).x) { + target = Game.getMonster(-1, -1, gid); + !target && (target = findTarget(gid, lastLoc)); + + if (!target) { + console.warn("ÿc1Failed to kill " + who + " (couldn't relocate unit)"); + break; + } + } + + Config.Dodge && me.hpPercent <= Config.DodgeHP && this.deploy(target, Config.DodgeRange, 5, 9); + attackCount > 0 && attackCount % 15 === 0 && Skill.getRange(Config.AttackSkill[1]) < 4 && Packet.flash(me.gid); + me.overhead("KillTarget: " + target.name + " health " + target.hpPercent + " % left"); + let result = ClassAttack.doAttack(target, attackCount % 15 === 0); + + if (result === this.Result.FAILED) { + if (retry++ > 3) { + errorInfo = " (doAttack failed)"; + + break; + } + + Packet.flash(me.gid); + } else if (result === this.Result.CANTATTACK) { + errorInfo = " (No valid attack skills)"; + + break; + } else if (result === this.Result.NEEDMANA) { + continue; + } else { + retry = 0; + } + + lastLoc = { x: me.x, y: me.y }; + attackCount++; + + if (target.dead || Config.FastPick || (attackCount > 0 && attackCount % 5 === 0)) { + Config.FastPick ? Pickit.fastPick() : Pickit.essessntialsPick(false, true); + } + } + + attackCount === Config.MaxAttackCount && (errorInfo = " (attackCount exceeded: " + attackCount + ")"); + ClassAttack.afterAttack(); + Pickit.pickItems(); + + if (!!target && target.attackable) { + console.warn("ÿc1Failed to kill ÿc0" + who + errorInfo); + } else { + console.log("ÿc7Killed ÿc0:: " + who + "ÿc0 - ÿc7Duration: ÿc0" + Time.format(getTickCount() - tick)); + } + } finally { + // re-enable + Misc.openChestsEnabled = true; + } + + return (!target || !copyUnit(target).x || target.dead || !target.attackable); }; Attack.clearLocations = function (list = []) { - for (let x = 0; x < list.length; x++) { - Attack.clear(20); - Pather.moveTo(list[x][0], list[x][1]); - Attack.clear(20); - Pickit.pickItems(); - } - - return true; + for (let x = 0; x < list.length; x++) { + Attack.clear(20); + Pather.moveTo(list[x][0], list[x][1]); + Attack.clear(20); + Pickit.pickItems(); + } + + return true; }; /** @@ -346,257 +346,257 @@ Attack.clearLocations = function (list = []) { * @returns {boolean} */ Attack.clearPos = function (x, y, range = 15, pickit = true) { - while (!me.gameReady) { - delay(40); - } - - if (typeof (range) !== "number") throw new Error("Attack.clear: range must be a number."); - if (Config.AttackSkill[1] < 0 || Config.AttackSkill[3] < 0 || Attack.stopClear || !x || !y) return false; - - let i, start, skillCheck; - let [monsterList, gidAttack] = [[], []]; - let [retry, attackCount] = [0, 0]; - let target = Game.getMonster(); - - if (target) { - do { - if (target.attackable && !this.skipCheck(target) && this.canAttack(target)) { - // Speed optimization - don't go through monster list until there's at least one within clear range - if (!start && getDistance(target, x, y) <= range && (Pather.useTeleport() || !checkCollision(me, target, sdk.collision.WallOrRanged))) { - start = true; - } - - monsterList.push(copyUnit(target)); - } - } while (target.getNext()); - } - - while (start && monsterList.length > 0 && attackCount < 300) { - if (me.dead || Attack.stopClear) return false; - - monsterList.sort(this.sortMonsters); - target = copyUnit(monsterList[0]); - - if (target.x !== undefined - && (getDistance(target, x, y) <= range || (this.getScarinessLevel(target) > 7 && getDistance(me, target) <= range)) && target.attackable) { - Config.Dodge && me.hpPercent <= Config.DodgeHP && this.deploy(target, Config.DodgeRange, 5, 9); - let checkMobAttackCount = gidAttack.find(g => g.gid === target.gid); - let checkAttackSkill = (!!checkMobAttackCount && checkMobAttackCount.attacks > 0 && checkMobAttackCount.attacks % 3 === 0); - let result = ClassAttack.doAttack(target, checkAttackSkill); - - if (result) { - retry = 0; - - if (result === this.Result.CANTATTACK) { - monsterList.shift(); - - continue; - } else if (result === this.Result.NEEDMANA) { - continue; - } - - for (i = 0; i < gidAttack.length; i += 1) { - if (gidAttack[i].gid === target.gid) { - break; - } - } - - (i === gidAttack.length) && gidAttack.push({ gid: target.gid, attacks: 0, name: target.name }); - gidAttack[i].attacks += 1; - attackCount += 1; - let isSpecial = target.isSpecial; - let secAttack = me.barbarian ? (isSpecial ? 2 : 4) : 5; - - if (Config.AttackSkill[secAttack] > -1 && (!Attack.checkResist(target, Config.AttackSkill[isSpecial ? 1 : 3]) - || (me.paladin && Config.AttackSkill[isSpecial ? 1 : 3] === sdk.skills.BlessedHammer && !ClassAttack.getHammerPosition(target)))) { - skillCheck = Config.AttackSkill[secAttack]; - } else { - skillCheck = Config.AttackSkill[isSpecial ? 1 : 3]; - } - - // Desync/bad position handler - switch (skillCheck) { - case sdk.skills.BlessedHammer: - // Tele in random direction with Blessed Hammer - if (gidAttack[i].attacks > 0 && gidAttack[i].attacks % (isSpecial ? 4 : 2) === 0) { - let coord = CollMap.getRandCoordinate(me.x, -1, 1, me.y, -1, 1, 5); - Pather.moveTo(coord.x, coord.y); - } - - break; - default: - // Flash with melee skills - if (gidAttack[i].attacks > 0 && gidAttack[i].attacks % (isSpecial ? 15 : 5) === 0 && Skill.getRange(skillCheck) < 4) { - Packet.flash(me.gid); - } - - break; - } - - // Skip non-unique monsters after 15 attacks, except in Throne of Destruction - if (!me.inArea(sdk.areas.ThroneofDestruction) && !isSpecial && gidAttack[i].attacks > 15) { - console.warn("ÿc1Skipping " + target.name + " " + target.gid + " " + gidAttack[i].attacks); - monsterList.shift(); - } - - if (target.dead || Config.FastPick || attackCount % 5 === 0) { - Config.FastPick ? Pickit.fastPick() : Pickit.essessntialsPick(false, true); - } - } else { - if (retry++ > 3) { - monsterList.shift(); - retry = 0; - } - - Packet.flash(me.gid); - } - } else { - monsterList.shift(); - } - } - - ClassAttack.afterAttack(pickit); - pickit && Attack.openChests(range, x, y); - attackCount > 0 && pickit && Pickit.pickItems(); - if ([x, y].distance > 5) { - Developer.debugging.pathing && console.debug("Returning to position " + x + "/" + y + " distance: " + [x, y].distance); - Pather.moveTo(x, y); - } - - return true; + while (!me.gameReady) { + delay(40); + } + + if (typeof (range) !== "number") throw new Error("Attack.clear: range must be a number."); + if (Config.AttackSkill[1] < 0 || Config.AttackSkill[3] < 0 || Attack.stopClear || !x || !y) return false; + + let i, start, skillCheck; + let [monsterList, gidAttack] = [[], []]; + let [retry, attackCount] = [0, 0]; + let target = Game.getMonster(); + + if (target) { + do { + if (target.attackable && !this.skipCheck(target) && this.canAttack(target)) { + // Speed optimization - don't go through monster list until there's at least one within clear range + if (!start && getDistance(target, x, y) <= range && (Pather.useTeleport() || !checkCollision(me, target, sdk.collision.WallOrRanged))) { + start = true; + } + + monsterList.push(copyUnit(target)); + } + } while (target.getNext()); + } + + while (start && monsterList.length > 0 && attackCount < 300) { + if (me.dead || Attack.stopClear) return false; + + monsterList.sort(this.sortMonsters); + target = copyUnit(monsterList[0]); + + if (target.x !== undefined + && (getDistance(target, x, y) <= range || (this.getScarinessLevel(target) > 7 && getDistance(me, target) <= range)) && target.attackable) { + Config.Dodge && me.hpPercent <= Config.DodgeHP && this.deploy(target, Config.DodgeRange, 5, 9); + let checkMobAttackCount = gidAttack.find(g => g.gid === target.gid); + let checkAttackSkill = (!!checkMobAttackCount && checkMobAttackCount.attacks > 0 && checkMobAttackCount.attacks % 3 === 0); + let result = ClassAttack.doAttack(target, checkAttackSkill); + + if (result) { + retry = 0; + + if (result === this.Result.CANTATTACK) { + monsterList.shift(); + + continue; + } else if (result === this.Result.NEEDMANA) { + continue; + } + + for (i = 0; i < gidAttack.length; i += 1) { + if (gidAttack[i].gid === target.gid) { + break; + } + } + + (i === gidAttack.length) && gidAttack.push({ gid: target.gid, attacks: 0, name: target.name }); + gidAttack[i].attacks += 1; + attackCount += 1; + let isSpecial = target.isSpecial; + let secAttack = me.barbarian ? (isSpecial ? 2 : 4) : 5; + + if (Config.AttackSkill[secAttack] > -1 && (!Attack.checkResist(target, Config.AttackSkill[isSpecial ? 1 : 3]) + || (me.paladin && Config.AttackSkill[isSpecial ? 1 : 3] === sdk.skills.BlessedHammer && !ClassAttack.getHammerPosition(target)))) { + skillCheck = Config.AttackSkill[secAttack]; + } else { + skillCheck = Config.AttackSkill[isSpecial ? 1 : 3]; + } + + // Desync/bad position handler + switch (skillCheck) { + case sdk.skills.BlessedHammer: + // Tele in random direction with Blessed Hammer + if (gidAttack[i].attacks > 0 && gidAttack[i].attacks % (isSpecial ? 4 : 2) === 0) { + let coord = CollMap.getRandCoordinate(me.x, -1, 1, me.y, -1, 1, 5); + Pather.moveTo(coord.x, coord.y); + } + + break; + default: + // Flash with melee skills + if (gidAttack[i].attacks > 0 && gidAttack[i].attacks % (isSpecial ? 15 : 5) === 0 && Skill.getRange(skillCheck) < 4) { + Packet.flash(me.gid); + } + + break; + } + + // Skip non-unique monsters after 15 attacks, except in Throne of Destruction + if (!me.inArea(sdk.areas.ThroneofDestruction) && !isSpecial && gidAttack[i].attacks > 15) { + console.warn("ÿc1Skipping " + target.name + " " + target.gid + " " + gidAttack[i].attacks); + monsterList.shift(); + } + + if (target.dead || Config.FastPick || attackCount % 5 === 0) { + Config.FastPick ? Pickit.fastPick() : Pickit.essessntialsPick(false, true); + } + } else { + if (retry++ > 3) { + monsterList.shift(); + retry = 0; + } + + Packet.flash(me.gid); + } + } else { + monsterList.shift(); + } + } + + ClassAttack.afterAttack(pickit); + pickit && Attack.openChests(range, x, y); + attackCount > 0 && pickit && Pickit.pickItems(); + if ([x, y].distance > 5) { + Developer.debugging.pathing && console.debug("Returning to position " + x + "/" + y + " distance: " + [x, y].distance); + Pather.moveTo(x, y); + } + + return true; }; Attack.buildMonsterList = function (skipBlocked = false) { - skipBlocked === true && (skipBlocked = 0x4); - let monList = []; - let monster = Game.getMonster(); + skipBlocked === true && (skipBlocked = 0x4); + let monList = []; + let monster = Game.getMonster(); - if (monster) { - do { - monster.attackable && monList.push(copyUnit(monster)); - } while (monster.getNext()); - } + if (monster) { + do { + monster.attackable && monList.push(copyUnit(monster)); + } while (monster.getNext()); + } - return skipBlocked === 0x4 ? monList.filter(mob => !checkCollision(me, mob, skipBlocked)) : monList; + return skipBlocked === 0x4 ? monList.filter(mob => !checkCollision(me, mob, skipBlocked)) : monList; }; // Clear an entire area based on settings Attack.clearLevelEx = function (givenSettings = {}) { - function RoomSort(a, b) { - return getDistance(myRoom[0], myRoom[1], a[0], a[1]) - getDistance(myRoom[0], myRoom[1], b[0], b[1]); - } - - // credit @jaenstr - const settings = Object.assign({}, { - spectype: Config.ClearType, - quitWhen: () => {} - }, givenSettings); - - let room = getRoom(); - if (!room) return false; - - const currentArea = getArea().id; - let result, myRoom, previousArea, rooms = []; - - do { - rooms.push([room.x * 5 + room.xsize / 2, room.y * 5 + room.ysize / 2]); - } while (room.getNext()); - - while (rooms.length > 0) { - // get the first room + initialize myRoom var - !myRoom && (room = getRoom(me.x, me.y)); - - if (room) { - // use previous room to calculate distance - if (room instanceof Array) { - myRoom = [room[0], room[1]]; - } else { - // create a new room to calculate distance (first room, done only once) - myRoom = [room.x * 5 + room.xsize / 2, room.y * 5 + room.ysize / 2]; - } - } - - rooms.sort(RoomSort); - room = rooms.shift(); - result = Pather.getNearestWalkable(room[0], room[1], 18, 3); - - if (result) { - Pather.moveTo(result[0], result[1], 3, settings.spectype); - previousArea = result; - - if (settings.quitWhen()) return true; - if (!this.clear(40, settings.spectype)) { - break; - } - } else if (currentArea !== getArea().id) { - // Make sure bot does not get stuck in different area. - Pather.moveTo(previousArea[0], previousArea[1], 3, settings.spectype); - } - } - - return true; + function RoomSort(a, b) { + return getDistance(myRoom[0], myRoom[1], a[0], a[1]) - getDistance(myRoom[0], myRoom[1], b[0], b[1]); + } + + // credit @jaenstr + const settings = Object.assign({}, { + spectype: Config.ClearType, + quitWhen: () => {} + }, givenSettings); + + let room = getRoom(); + if (!room) return false; + + const currentArea = getArea().id; + let result, myRoom, previousArea, rooms = []; + + do { + rooms.push([room.x * 5 + room.xsize / 2, room.y * 5 + room.ysize / 2]); + } while (room.getNext()); + + while (rooms.length > 0) { + // get the first room + initialize myRoom var + !myRoom && (room = getRoom(me.x, me.y)); + + if (room) { + // use previous room to calculate distance + if (room instanceof Array) { + myRoom = [room[0], room[1]]; + } else { + // create a new room to calculate distance (first room, done only once) + myRoom = [room.x * 5 + room.xsize / 2, room.y * 5 + room.ysize / 2]; + } + } + + rooms.sort(RoomSort); + room = rooms.shift(); + result = Pather.getNearestWalkable(room[0], room[1], 18, 3); + + if (result) { + Pather.moveTo(result[0], result[1], 3, settings.spectype); + previousArea = result; + + if (settings.quitWhen()) return true; + if (!this.clear(40, settings.spectype)) { + break; + } + } else if (currentArea !== getArea().id) { + // Make sure bot does not get stuck in different area. + Pather.moveTo(previousArea[0], previousArea[1], 3, settings.spectype); + } + } + + return true; }; // Clear an entire area until area is done or level is reached Attack.clearLevelUntilLevel = function (charlvl = undefined, spectype = 0) { - function RoomSort(a, b) { - return getDistance(myRoom[0], myRoom[1], a[0], a[1]) - getDistance(myRoom[0], myRoom[1], b[0], b[1]); - } - - let room = getRoom(); - if (!room) return false; - - !charlvl && (charlvl = me.charlvl + 1); - let result, myRoom, previousArea; - let rooms = []; - const currentArea = getArea().id; - - do { - rooms.push([room.x * 5 + room.xsize / 2, room.y * 5 + room.ysize / 2]); - } while (room.getNext()); - - myPrint("Starting Clear until level My level: " + me.charlvl + " wanted level: " + charlvl); - - while (rooms.length > 0) { - // get the first room + initialize myRoom var - !myRoom && (room = getRoom(me.x, me.y)); - - if (room) { - if (room instanceof Array) { // use previous room to calculate distance - myRoom = [room[0], room[1]]; - } else { // create a new room to calculate distance (first room, done only once) - myRoom = [room.x * 5 + room.xsize / 2, room.y * 5 + room.ysize / 2]; - } - } - - rooms.sort(RoomSort); - room = rooms.shift(); - result = Pather.getNearestWalkable(room[0], room[1], 18, 3); - - if (result) { - Pather.moveTo(result[0], result[1], 3, spectype); - previousArea = result; - - if (Attack.stopClear) { - Attack.stopClear = false; // Reset value - return true; - } - - if (me.charlvl >= charlvl) { - myPrint("Clear until level requirment met. My level: " + me.charlvl + " wanted level: " + charlvl); - return true; - } - - if (!this.clear(40, spectype)) { - break; - } - } else if (currentArea !== getArea().id) { - // Make sure bot does not get stuck in different area. - Pather.moveTo(previousArea[0], previousArea[1], 3, spectype); - } - } - - return true; + function RoomSort(a, b) { + return getDistance(myRoom[0], myRoom[1], a[0], a[1]) - getDistance(myRoom[0], myRoom[1], b[0], b[1]); + } + + let room = getRoom(); + if (!room) return false; + + !charlvl && (charlvl = me.charlvl + 1); + let result, myRoom, previousArea; + let rooms = []; + const currentArea = getArea().id; + + do { + rooms.push([room.x * 5 + room.xsize / 2, room.y * 5 + room.ysize / 2]); + } while (room.getNext()); + + myPrint("Starting Clear until level My level: " + me.charlvl + " wanted level: " + charlvl); + + while (rooms.length > 0) { + // get the first room + initialize myRoom var + !myRoom && (room = getRoom(me.x, me.y)); + + if (room) { + if (room instanceof Array) { // use previous room to calculate distance + myRoom = [room[0], room[1]]; + } else { // create a new room to calculate distance (first room, done only once) + myRoom = [room.x * 5 + room.xsize / 2, room.y * 5 + room.ysize / 2]; + } + } + + rooms.sort(RoomSort); + room = rooms.shift(); + result = Pather.getNearestWalkable(room[0], room[1], 18, 3); + + if (result) { + Pather.moveTo(result[0], result[1], 3, spectype); + previousArea = result; + + if (Attack.stopClear) { + Attack.stopClear = false; // Reset value + return true; + } + + if (me.charlvl >= charlvl) { + myPrint("Clear until level requirment met. My level: " + me.charlvl + " wanted level: " + charlvl); + return true; + } + + if (!this.clear(40, spectype)) { + break; + } + } else if (currentArea !== getArea().id) { + // Make sure bot does not get stuck in different area. + Pather.moveTo(previousArea[0], previousArea[1], 3, spectype); + } + } + + return true; }; /** @@ -614,293 +614,293 @@ Attack.clearLevelUntilLevel = function (charlvl = undefined, spectype = 0) { * - should we stop clearing after boss is killed if we are using bossid? */ Attack.clear = function (range = 25, spectype = 0, bossId = false, sortfunc = undefined, pickit = true) { - while (!me.gameReady) { - delay(40); - } - - if (typeof (range) !== "number") throw new Error("Attack.clear: range must be a number."); - if (Config.AttackSkill[1] < 0 || Config.AttackSkill[3] < 0 || Attack.stopClear) return false; - const canTeleport = Pather.canTeleport(); - !sortfunc && (sortfunc = canTeleport ? this.sortMonsters : Attack.walkingSortMonsters); - - let i, boss, orgx, orgy, start, skillCheck; - let gidAttack = []; - let tick = getTickCount(); - let [killedBoss, logged] = [false, false]; - let [retry, attackCount] = [0, 0]; - let clearResult = Attack.Result.NOOP; - - if (bossId) { - boss = Misc.poll(function () { - switch (true) { - case typeof bossId === "object": - return bossId; - case ((typeof bossId === "number" && bossId > 999)): - return Game.getMonster(-1, -1, bossId); - default: - return Game.getMonster(bossId); - } - }, 2000, 100); - - if (!boss) { - console.warn("Attack.clear: " + bossId + " not found"); - return Attack.clear(10); - } - - ({ orgx, orgy } = { orgx: boss.x, orgy: boss.y }); - } else { - ({ orgx, orgy } = { orgx: me.x, orgy: me.y }); - } - - let monsterList = []; - let target = Game.getMonster(); - - if (target) { - do { - if ((!spectype || (target.spectype & spectype)) && target.attackable && !this.skipCheck(target)) { - // Speed optimization - don't go through monster list until there's at least one within clear range - if (!start && getDistance(target, orgx, orgy) <= range && (canTeleport || !checkCollision(me, target, sdk.collision.BlockWall))) { - start = true; - } - - monsterList.push(copyUnit(target)); - } - } while (target.getNext()); - } - - // if (!canTeleport && range < 10 && !me.inArea(sdk.areas.BloodMoor) && monsterList.some(mon => mon.isFallen)) { - // // handle shamans if they are just out of the range we were meant to clear but we have fallens in the list - // } - - while (start && monsterList.length > 0 && attackCount < 300) { - if (me.dead || Attack.stopClear) return false; - - boss && (({ orgx, orgy } = { orgx: boss.x, orgy: boss.y })); - monsterList.sort(sortfunc); - target = copyUnit(monsterList[0]); - - if (target.x !== undefined - && (getDistance(target, orgx, orgy) <= range || (this.getScarinessLevel(target) > 7 && getDistance(me, target) <= range)) && target.attackable) { - Config.Dodge && me.hpPercent <= Config.DodgeHP && this.deploy(target, Config.DodgeRange, 5, 9); - tick = getTickCount(); - - if (!logged && boss && boss.gid === target.gid) { - logged = true; - console.log("ÿc7Clear ÿc0:: " + (!!target.name ? target.name : bossId)); - } - const checkMobAttackCount = gidAttack.find(g => g.gid === target.gid); - const checkAttackSkill = (!!checkMobAttackCount && checkMobAttackCount.attacks > 0 && checkMobAttackCount.attacks % 3 === 0); - const result = ClassAttack.doAttack(target, checkAttackSkill); - - if (result) { - retry = 0; - - if (result === this.Result.CANTATTACK) { - monsterList.shift(); - - continue; - } else if (result === this.Result.NEEDMANA) { - continue; - } - - for (i = 0; i < gidAttack.length; i += 1) { - if (gidAttack[i].gid === target.gid) { - break; - } - } - - (i === gidAttack.length) && gidAttack.push({ gid: target.gid, attacks: 0, name: target.name }); - gidAttack[i].attacks += 1; - attackCount += 1; - let isSpecial = target.isSpecial; - let secAttack = me.barbarian ? (isSpecial ? 2 : 4) : 5; - - if (Config.AttackSkill[secAttack] > -1 && (!Attack.checkResist(target, Config.AttackSkill[isSpecial ? 1 : 3]) - || (me.paladin && Config.AttackSkill[isSpecial ? 1 : 3] === sdk.skills.BlessedHammer && !ClassAttack.getHammerPosition(target)))) { - skillCheck = Config.AttackSkill[secAttack]; - } else { - skillCheck = Config.AttackSkill[isSpecial ? 1 : 3]; - } - - // Desync/bad position handler - switch (skillCheck) { - case sdk.skills.BlessedHammer: - // Tele in random direction with Blessed Hammer - if (gidAttack[i].attacks > 0 && gidAttack[i].attacks % (isSpecial ? 4 : 2) === 0) { - let coord = CollMap.getRandCoordinate(me.x, -1, 1, me.y, -1, 1, 5); - Pather.moveTo(coord.x, coord.y); - } - - break; - default: - // Flash with melee skills - if (gidAttack[i].attacks > 0 && gidAttack[i].attacks % (isSpecial ? 15 : 5) === 0 && Skill.getRange(skillCheck) < 4) { - Packet.flash(me.gid); - } - - break; - } - - // Skip non-unique monsters after 15 attacks, except in Throne of Destruction - if (!me.inArea(sdk.areas.ThroneofDestruction) && !isSpecial && gidAttack[i].attacks > 15) { - console.warn("ÿc1Skipping " + target.name + " " + target.gid + " " + gidAttack[i].attacks); - monsterList.shift(); - } - - // we cleared this monster so subtract amount of attacks from current attack count in order to prevent pre-maturely ending clear - if (target.dead) { - clearResult = Attack.Result.SUCCESS; - if (boss && boss.gid === target.gid) { - killedBoss = true; - console.log("ÿc7Cleared ÿc0:: " + (!!target.name ? target.name : bossId) + "ÿc0 - ÿc7Duration: ÿc0" + Time.format(getTickCount() - tick)); - } - attackCount -= gidAttack[i].attacks; - } - if (target.dead || Config.FastPick || attackCount % 5 === 0) { - Config.FastPick ? Pickit.fastPick() : Pickit.essessntialsPick(false, true); - } - } else { - if (Coords_1.isBlockedBetween(me, target)) { - let collCheck = Coords_1.getCollisionBetweenCoords(me.x, me.y, target.x, target.y); - if (collCheck !== sdk.collision.MonsterObject) { - console.log("ÿc1Skipping " + target.name + " because they are blocked. Collision: " + collCheck.toString(16)); - monsterList.shift(); - retry = 0; - } - } - - if (retry++ > 3) { - monsterList.shift(); - retry = 0; - } - - Packet.flash(me.gid); - } - } else { - monsterList.shift(); - } - } - - ClassAttack.afterAttack(pickit); - this.openChests(range, orgx, orgy); - attackCount > 0 && pickit && Pickit.pickItems(); - - if (boss && !killedBoss) { - // check if boss corpse is around - // sometimes this fails, need better check for it - if (boss.dead) { - console.log("ÿc7Cleared ÿc0:: " + (!!boss.name ? boss.name : bossId) + "ÿc0 - ÿc7Duration: ÿc0" + Time.format(getTickCount() - tick)); - } else { - console.log("ÿc7Clear ÿc0:: ÿc1Failed to clear ÿc0:: " + (!!boss.name ? boss.name : bossId)); - return Attack.Result.FAILED; - } - } - - return clearResult; + while (!me.gameReady) { + delay(40); + } + + if (typeof (range) !== "number") throw new Error("Attack.clear: range must be a number."); + if (Config.AttackSkill[1] < 0 || Config.AttackSkill[3] < 0 || Attack.stopClear) return false; + const canTeleport = Pather.canTeleport(); + !sortfunc && (sortfunc = canTeleport ? this.sortMonsters : Attack.walkingSortMonsters); + + let i, boss, orgx, orgy, start, skillCheck; + let gidAttack = []; + let tick = getTickCount(); + let [killedBoss, logged] = [false, false]; + let [retry, attackCount] = [0, 0]; + let clearResult = Attack.Result.NOOP; + + if (bossId) { + boss = Misc.poll(function () { + switch (true) { + case typeof bossId === "object": + return bossId; + case ((typeof bossId === "number" && bossId > 999)): + return Game.getMonster(-1, -1, bossId); + default: + return Game.getMonster(bossId); + } + }, 2000, 100); + + if (!boss) { + console.warn("Attack.clear: " + bossId + " not found"); + return Attack.clear(10); + } + + ({ orgx, orgy } = { orgx: boss.x, orgy: boss.y }); + } else { + ({ orgx, orgy } = { orgx: me.x, orgy: me.y }); + } + + let monsterList = []; + let target = Game.getMonster(); + + if (target) { + do { + if ((!spectype || (target.spectype & spectype)) && target.attackable && !this.skipCheck(target)) { + // Speed optimization - don't go through monster list until there's at least one within clear range + if (!start && getDistance(target, orgx, orgy) <= range && (canTeleport || !checkCollision(me, target, sdk.collision.BlockWall))) { + start = true; + } + + monsterList.push(copyUnit(target)); + } + } while (target.getNext()); + } + + // if (!canTeleport && range < 10 && !me.inArea(sdk.areas.BloodMoor) && monsterList.some(mon => mon.isFallen)) { + // // handle shamans if they are just out of the range we were meant to clear but we have fallens in the list + // } + + while (start && monsterList.length > 0 && attackCount < 300) { + if (me.dead || Attack.stopClear) return false; + + boss && (({ orgx, orgy } = { orgx: boss.x, orgy: boss.y })); + monsterList.sort(sortfunc); + target = copyUnit(monsterList[0]); + + if (target.x !== undefined + && (getDistance(target, orgx, orgy) <= range || (this.getScarinessLevel(target) > 7 && getDistance(me, target) <= range)) && target.attackable) { + Config.Dodge && me.hpPercent <= Config.DodgeHP && this.deploy(target, Config.DodgeRange, 5, 9); + tick = getTickCount(); + + if (!logged && boss && boss.gid === target.gid) { + logged = true; + console.log("ÿc7Clear ÿc0:: " + (!!target.name ? target.name : bossId)); + } + const checkMobAttackCount = gidAttack.find(g => g.gid === target.gid); + const checkAttackSkill = (!!checkMobAttackCount && checkMobAttackCount.attacks > 0 && checkMobAttackCount.attacks % 3 === 0); + const result = ClassAttack.doAttack(target, checkAttackSkill); + + if (result) { + retry = 0; + + if (result === this.Result.CANTATTACK) { + monsterList.shift(); + + continue; + } else if (result === this.Result.NEEDMANA) { + continue; + } + + for (i = 0; i < gidAttack.length; i += 1) { + if (gidAttack[i].gid === target.gid) { + break; + } + } + + (i === gidAttack.length) && gidAttack.push({ gid: target.gid, attacks: 0, name: target.name }); + gidAttack[i].attacks += 1; + attackCount += 1; + let isSpecial = target.isSpecial; + let secAttack = me.barbarian ? (isSpecial ? 2 : 4) : 5; + + if (Config.AttackSkill[secAttack] > -1 && (!Attack.checkResist(target, Config.AttackSkill[isSpecial ? 1 : 3]) + || (me.paladin && Config.AttackSkill[isSpecial ? 1 : 3] === sdk.skills.BlessedHammer && !ClassAttack.getHammerPosition(target)))) { + skillCheck = Config.AttackSkill[secAttack]; + } else { + skillCheck = Config.AttackSkill[isSpecial ? 1 : 3]; + } + + // Desync/bad position handler + switch (skillCheck) { + case sdk.skills.BlessedHammer: + // Tele in random direction with Blessed Hammer + if (gidAttack[i].attacks > 0 && gidAttack[i].attacks % (isSpecial ? 4 : 2) === 0) { + let coord = CollMap.getRandCoordinate(me.x, -1, 1, me.y, -1, 1, 5); + Pather.moveTo(coord.x, coord.y); + } + + break; + default: + // Flash with melee skills + if (gidAttack[i].attacks > 0 && gidAttack[i].attacks % (isSpecial ? 15 : 5) === 0 && Skill.getRange(skillCheck) < 4) { + Packet.flash(me.gid); + } + + break; + } + + // Skip non-unique monsters after 15 attacks, except in Throne of Destruction + if (!me.inArea(sdk.areas.ThroneofDestruction) && !isSpecial && gidAttack[i].attacks > 15) { + console.warn("ÿc1Skipping " + target.name + " " + target.gid + " " + gidAttack[i].attacks); + monsterList.shift(); + } + + // we cleared this monster so subtract amount of attacks from current attack count in order to prevent pre-maturely ending clear + if (target.dead) { + clearResult = Attack.Result.SUCCESS; + if (boss && boss.gid === target.gid) { + killedBoss = true; + console.log("ÿc7Cleared ÿc0:: " + (!!target.name ? target.name : bossId) + "ÿc0 - ÿc7Duration: ÿc0" + Time.format(getTickCount() - tick)); + } + attackCount -= gidAttack[i].attacks; + } + if (target.dead || Config.FastPick || attackCount % 5 === 0) { + Config.FastPick ? Pickit.fastPick() : Pickit.essessntialsPick(false, true); + } + } else { + if (Coords_1.isBlockedBetween(me, target)) { + let collCheck = Coords_1.getCollisionBetweenCoords(me.x, me.y, target.x, target.y); + if (collCheck !== sdk.collision.MonsterObject) { + console.log("ÿc1Skipping " + target.name + " because they are blocked. Collision: " + collCheck.toString(16)); + monsterList.shift(); + retry = 0; + } + } + + if (retry++ > 3) { + monsterList.shift(); + retry = 0; + } + + Packet.flash(me.gid); + } + } else { + monsterList.shift(); + } + } + + ClassAttack.afterAttack(pickit); + this.openChests(range, orgx, orgy); + attackCount > 0 && pickit && Pickit.pickItems(); + + if (boss && !killedBoss) { + // check if boss corpse is around + // sometimes this fails, need better check for it + if (boss.dead) { + console.log("ÿc7Cleared ÿc0:: " + (!!boss.name ? boss.name : bossId) + "ÿc0 - ÿc7Duration: ÿc0" + Time.format(getTickCount() - tick)); + } else { + console.log("ÿc7Clear ÿc0:: ÿc1Failed to clear ÿc0:: " + (!!boss.name ? boss.name : bossId)); + return Attack.Result.FAILED; + } + } + + return clearResult; }; // Take a array of coords - path and clear // pick parameter is range of items to pick // From legacy sonic Attack.clearCoordList = function (list, pick) { - for (let node of list) { - Attack.clear(node.radius); - Pather.moveTo(node.x, node.y); - Attack.clear(node.radius); - pick && Pickit.pickItems(pick); - } + for (let node of list) { + Attack.clear(node.radius); + Pather.moveTo(node.x, node.y); + Attack.clear(node.radius); + pick && Pickit.pickItems(pick); + } }; Attack.checkBowOnSwitch = function (firstInit = false) { - const preBow = CharData.skillData.bow.onSwitch; - const checkTypes = [sdk.items.type.AmazonBow, sdk.items.type.Bow, sdk.items.type.Crossbow, sdk.items.type.BowQuiver, sdk.items.type.CrossbowQuiver]; - - me.weaponswitch !== sdk.player.slot.Main && me.switchWeapons(sdk.player.slot.Main); - const items = me.getItemsEx().filter(item => item && item.isEquipped && item.isOnSwap && checkTypes.includes(item.itemType)); - if (preBow && !items.some(item => [sdk.items.type.AmazonBow, sdk.items.type.Bow, sdk.items.type.Crossbow].includes(item.itemType))) { - CharData.skillData.bow.resetBowData(); - return; - } - items.forEach(item => { - if ([sdk.items.type.AmazonBow, sdk.items.type.Bow, sdk.items.type.Crossbow].includes(item.itemType)) { - CharData.skillData.bow.onSwitch = true; - if (CharData.skillData.bow.bowGid !== item.gid) { - CharData.skillData.bow.setBowInfo(item, firstInit); - } - } - if ([sdk.items.type.BowQuiver, sdk.items.type.CrossbowQuiver].includes(item.itemType)) { - if (CharData.skillData.bow.quiverType !== item.itemType) { - CharData.skillData.bow.setArrowInfo(item, firstInit); - } - } - }); + const preBow = CharData.skillData.bow.onSwitch; + const checkTypes = [sdk.items.type.AmazonBow, sdk.items.type.Bow, sdk.items.type.Crossbow, sdk.items.type.BowQuiver, sdk.items.type.CrossbowQuiver]; + + me.weaponswitch !== sdk.player.slot.Main && me.switchWeapons(sdk.player.slot.Main); + const items = me.getItemsEx().filter(item => item && item.isEquipped && item.isOnSwap && checkTypes.includes(item.itemType)); + if (preBow && !items.some(item => [sdk.items.type.AmazonBow, sdk.items.type.Bow, sdk.items.type.Crossbow].includes(item.itemType))) { + CharData.skillData.bow.resetBowData(); + return; + } + items.forEach(item => { + if ([sdk.items.type.AmazonBow, sdk.items.type.Bow, sdk.items.type.Crossbow].includes(item.itemType)) { + CharData.skillData.bow.onSwitch = true; + if (CharData.skillData.bow.bowGid !== item.gid) { + CharData.skillData.bow.setBowInfo(item, firstInit); + } + } + if ([sdk.items.type.BowQuiver, sdk.items.type.CrossbowQuiver].includes(item.itemType)) { + if (CharData.skillData.bow.quiverType !== item.itemType) { + CharData.skillData.bow.setArrowInfo(item, firstInit); + } + } + }); }; Attack.haveDependancy = function (itemType) { - return [sdk.items.type.AmazonBow, sdk.items.type.Bow, sdk.items.type.Crossbow].includes(itemType) ? me.getItem("aqv", sdk.items.mode.Equipped) : me.getItem("cqv", sdk.items.mode.Equipped); + return [sdk.items.type.AmazonBow, sdk.items.type.Bow, sdk.items.type.Crossbow].includes(itemType) ? me.getItem("aqv", sdk.items.mode.Equipped) : me.getItem("cqv", sdk.items.mode.Equipped); }; Attack.useBowOnSwitch = function (unit, skillId = 0, switchBack = true) { - if (!CharData.skillData.bow.onSwitch) return false; - if (!this.haveDependancy(CharData.skillData.bow.bowType)) return false; - return Skill.switchCast(skillId, { hand: sdk.skills.hand.Left, x: unit, switchBack: switchBack }); + if (!CharData.skillData.bow.onSwitch) return false; + if (!this.haveDependancy(CharData.skillData.bow.bowType)) return false; + return Skill.switchCast(skillId, { hand: sdk.skills.hand.Left, x: unit, switchBack: switchBack }); }; // maybe store the copyUnit of the item or at least gid so we don't need to iterate through all our items to find the one with the charged skill when we need it Attack.getCurrentChargedSkillIds = function (init = false) { - const chargeSkillObj = (skill, level, gid) => ({ skill: skill, level: level, gid: gid }); - let [currentChargedSkills, chargedSkills, chargedSkillsOnSwitch] = [[], [], []]; - - // Item must be equipped - removed charms as I don't think at any point using hydra from torch has ever been worth it - me.getItemsEx(-1) - .filter(item => item && ((item.isEquipped /* && !item.rare */))) - .forEach(function (item) { - let stats = item.getStat(-2); - - if (stats.hasOwnProperty(sdk.stats.ChargedSkill)) { - if (stats[sdk.stats.ChargedSkill] instanceof Array) { - for (let i = 0; i < stats[sdk.stats.ChargedSkill].length; i += 1) { - if (stats[sdk.stats.ChargedSkill][i] !== undefined) { - // add to total list of skillIds - if (stats[sdk.stats.ChargedSkill][i].charges > 0 && !currentChargedSkills.includes(stats[sdk.stats.ChargedSkill][i].skill)) { - currentChargedSkills.push(stats[sdk.stats.ChargedSkill][i].skill); - chargedSkills.push(chargeSkillObj(stats[sdk.stats.ChargedSkill][i].skill, stats[sdk.stats.ChargedSkill][i].level, item.gid)); - } - - // add to switch only list for use with swtich casting - if (stats[sdk.stats.ChargedSkill][i].charges > 0 && !chargedSkillsOnSwitch.some(chargeSkill => chargeSkill.skill === stats[sdk.stats.ChargedSkill][i].skill) && item.isOnSwap) { - chargedSkillsOnSwitch.push(chargeSkillObj(stats[sdk.stats.ChargedSkill][i].skill, stats[sdk.stats.ChargedSkill][i].level, item.gid)); - } - } - } - } else { - // add to total list - if (stats[sdk.stats.ChargedSkill].charges > 0 && !currentChargedSkills.includes(stats[sdk.stats.ChargedSkill].skill)) { - currentChargedSkills.push(stats[sdk.stats.ChargedSkill].skill); - chargedSkills.push(chargeSkillObj(stats[sdk.stats.ChargedSkill].skill, stats[sdk.stats.ChargedSkill].level, item.gid)); - } - - // add to switch only list for use with swtich casting - if (stats[sdk.stats.ChargedSkill].charges > 0 && !chargedSkillsOnSwitch.some(chargeSkill => chargeSkill.skill === stats[sdk.stats.ChargedSkill].skill) && item.isOnSwap) { - chargedSkillsOnSwitch.push(chargeSkillObj(stats[sdk.stats.ChargedSkill].skill, stats[sdk.stats.ChargedSkill].level, item.gid)); - } - } - } - }); - - // only update other threads if this isn't being called from Attack.init - if (CharData.skillData.currentChargedSkills.length > 0 || init) { - switch (true) { - case !currentChargedSkills.equals(CharData.skillData.currentChargedSkills): - case Object.keys(Misc.recursiveSearch(chargedSkillsOnSwitch, CharData.skillData.chargedSkillsOnSwitch)).length > 0: - case Object.keys(Misc.recursiveSearch(chargedSkills, CharData.skillData.chargedSkills)).length > 0: - CharData.skillData.init(currentChargedSkills, chargedSkills, chargedSkillsOnSwitch); - !init && CharData.skillData.update(); - break; - } - } - - return true; + const chargeSkillObj = (skill, level, gid) => ({ skill: skill, level: level, gid: gid }); + let [currentChargedSkills, chargedSkills, chargedSkillsOnSwitch] = [[], [], []]; + + // Item must be equipped - removed charms as I don't think at any point using hydra from torch has ever been worth it + me.getItemsEx(-1) + .filter(item => item && ((item.isEquipped /* && !item.rare */))) + .forEach(function (item) { + let stats = item.getStat(-2); + + if (stats.hasOwnProperty(sdk.stats.ChargedSkill)) { + if (stats[sdk.stats.ChargedSkill] instanceof Array) { + for (let i = 0; i < stats[sdk.stats.ChargedSkill].length; i += 1) { + if (stats[sdk.stats.ChargedSkill][i] !== undefined) { + // add to total list of skillIds + if (stats[sdk.stats.ChargedSkill][i].charges > 0 && !currentChargedSkills.includes(stats[sdk.stats.ChargedSkill][i].skill)) { + currentChargedSkills.push(stats[sdk.stats.ChargedSkill][i].skill); + chargedSkills.push(chargeSkillObj(stats[sdk.stats.ChargedSkill][i].skill, stats[sdk.stats.ChargedSkill][i].level, item.gid)); + } + + // add to switch only list for use with swtich casting + if (stats[sdk.stats.ChargedSkill][i].charges > 0 && !chargedSkillsOnSwitch.some(chargeSkill => chargeSkill.skill === stats[sdk.stats.ChargedSkill][i].skill) && item.isOnSwap) { + chargedSkillsOnSwitch.push(chargeSkillObj(stats[sdk.stats.ChargedSkill][i].skill, stats[sdk.stats.ChargedSkill][i].level, item.gid)); + } + } + } + } else { + // add to total list + if (stats[sdk.stats.ChargedSkill].charges > 0 && !currentChargedSkills.includes(stats[sdk.stats.ChargedSkill].skill)) { + currentChargedSkills.push(stats[sdk.stats.ChargedSkill].skill); + chargedSkills.push(chargeSkillObj(stats[sdk.stats.ChargedSkill].skill, stats[sdk.stats.ChargedSkill].level, item.gid)); + } + + // add to switch only list for use with swtich casting + if (stats[sdk.stats.ChargedSkill].charges > 0 && !chargedSkillsOnSwitch.some(chargeSkill => chargeSkill.skill === stats[sdk.stats.ChargedSkill].skill) && item.isOnSwap) { + chargedSkillsOnSwitch.push(chargeSkillObj(stats[sdk.stats.ChargedSkill].skill, stats[sdk.stats.ChargedSkill].level, item.gid)); + } + } + } + }); + + // only update other threads if this isn't being called from Attack.init + if (CharData.skillData.currentChargedSkills.length > 0 || init) { + switch (true) { + case !currentChargedSkills.equals(CharData.skillData.currentChargedSkills): + case Object.keys(Misc.recursiveSearch(chargedSkillsOnSwitch, CharData.skillData.chargedSkillsOnSwitch)).length > 0: + case Object.keys(Misc.recursiveSearch(chargedSkills, CharData.skillData.chargedSkills)).length > 0: + CharData.skillData.init(currentChargedSkills, chargedSkills, chargedSkillsOnSwitch); + !init && CharData.skillData.update(); + break; + } + } + + return true; }; /** @@ -908,37 +908,37 @@ Attack.getCurrentChargedSkillIds = function (init = false) { * @returns {boolean} */ Attack.getItemCharges = function (skillId) { - if (!skillId) return false; - - let chargedItems = [], validCharge = function (itemCharge) { - return itemCharge.skill === skillId && itemCharge.charges > 1; - }; - - // Item must equipped, or a charm in inventory - me.getItemsEx(-1) - .filter(item => item && (item.isEquipped && !item.rare || (item.isInInventory && item.isCharm))) - .forEach(function (item) { - let stats = item.getStat(-2); - - if (stats.hasOwnProperty(sdk.stats.ChargedSkill)) { - if (stats[sdk.stats.ChargedSkill] instanceof Array) { - stats = stats[sdk.stats.ChargedSkill].filter(validCharge); - stats.length && chargedItems.push({ - charge: stats.first(), - item: item - }); - } else { - if (stats[sdk.stats.ChargedSkill].skill === skillId && stats[sdk.stats.ChargedSkill].charges > 1) { - chargedItems.push({ - charge: stats[sdk.stats.ChargedSkill].charges, - item: item - }); - } - } - } - }); - - return !!(chargedItems.length > 0); + if (!skillId) return false; + + let chargedItems = [], validCharge = function (itemCharge) { + return itemCharge.skill === skillId && itemCharge.charges > 1; + }; + + // Item must equipped, or a charm in inventory + me.getItemsEx(-1) + .filter(item => item && (item.isEquipped && !item.rare || (item.isInInventory && item.isCharm))) + .forEach(function (item) { + let stats = item.getStat(-2); + + if (stats.hasOwnProperty(sdk.stats.ChargedSkill)) { + if (stats[sdk.stats.ChargedSkill] instanceof Array) { + stats = stats[sdk.stats.ChargedSkill].filter(validCharge); + stats.length && chargedItems.push({ + charge: stats.first(), + item: item + }); + } else { + if (stats[sdk.stats.ChargedSkill].skill === skillId && stats[sdk.stats.ChargedSkill].charges > 1) { + chargedItems.push({ + charge: stats[sdk.stats.ChargedSkill].charges, + item: item + }); + } + } + } + }); + + return !!(chargedItems.length > 0); }; /** @@ -947,18 +947,18 @@ Attack.getItemCharges = function (skillId) { * @returns {boolean} */ Attack.castCharges = function (skillId, unit) { - if (!skillId || !unit || !Skill.wereFormCheck(skillId) - || (me.inTown && !Skill.townSkill(skillId))) { - return false; - } - - try { - me.castChargedSkillEx(skillId, unit) && delay(25); - } finally { - me.weaponswitch === 1 && me.switchWeapons(0); - } - - return true; + if (!skillId || !unit || !Skill.wereFormCheck(skillId) + || (me.inTown && !Skill.townSkill(skillId))) { + return false; + } + + try { + me.castChargedSkillEx(skillId, unit) && delay(25); + } finally { + me.weaponswitch === 1 && me.switchWeapons(0); + } + + return true; }; /** @@ -967,18 +967,18 @@ Attack.castCharges = function (skillId, unit) { * @returns {boolean} */ Attack.switchCastCharges = function (skillId, unit) { - if (!skillId || !unit || !Skill.wereFormCheck(skillId) - || (me.inTown && !Skill.townSkill(skillId))) { - return false; - } - - try { - me.castSwitchChargedSkill(skillId, unit) && delay(25); - } finally { - me.weaponswitch === 1 && me.switchWeapons(0); - } - - return true; + if (!skillId || !unit || !Skill.wereFormCheck(skillId) + || (me.inTown && !Skill.townSkill(skillId))) { + return false; + } + + try { + me.castSwitchChargedSkill(skillId, unit) && delay(25); + } finally { + me.weaponswitch === 1 && me.switchWeapons(0); + } + + return true; }; /** @@ -987,51 +987,51 @@ Attack.switchCastCharges = function (skillId, unit) { * @returns {boolean} */ Attack.dollAvoid = function (unit) { - if (!unit) return false; - let distance = 14; - - for (let i = 0; i < 2 * Math.PI; i += Math.PI / 6) { - let cx = Math.round(Math.cos(i) * distance); - let cy = Math.round(Math.sin(i) * distance); - if (Attack.validSpot(unit.x + cx, unit.y + cy)) { - // don't clear while trying to reposition - return Pather.moveToEx(unit.x + cx, unit.y + cy, { clearSettings: { allowClearing: false } }); - } - } - - return false; + if (!unit) return false; + let distance = 14; + + for (let i = 0; i < 2 * Math.PI; i += Math.PI / 6) { + let cx = Math.round(Math.cos(i) * distance); + let cy = Math.round(Math.sin(i) * distance); + if (Attack.validSpot(unit.x + cx, unit.y + cy)) { + // don't clear while trying to reposition + return Pather.moveToEx(unit.x + cx, unit.y + cy, { clearSettings: { allowClearing: false } }); + } + } + + return false; }; // Its the inverse of spotOnDistance, its a spot going in the direction of the spot Attack.inverseSpotDistance = function (spot, distance, otherSpot) { - otherSpot === undefined && (otherSpot = me); - let x = otherSpot.x, y = otherSpot.y, area = otherSpot.area; - let nodes = getPath(area, x, y, spot.x, spot.y, 2, 5); - return nodes && nodes.find((node) => node.distance > distance) || { x: x, y: y }; + otherSpot === undefined && (otherSpot = me); + let x = otherSpot.x, y = otherSpot.y, area = otherSpot.area; + let nodes = getPath(area, x, y, spot.x, spot.y, 2, 5); + return nodes && nodes.find((node) => node.distance > distance) || { x: x, y: y }; }; Attack.shouldDodge = function (coord, monster) { - return !!monster && getUnits(sdk.unittype.Missile) - // for every missle that isnt from our merc - .filter(missile => missile && monster && monster.gid === missile.owner) - // if any - .some(missile => { - let xoff = Math.abs(coord.x - missile.targetx); - let yoff = Math.abs(coord.y - missile.targety); - let xdist = Math.abs(coord.x - missile.x); - let ydist = Math.abs(coord.y - missile.y); - - // If missile wants to hit is and is close to us - return xoff < 7 && yoff < 7 && xdist < 13 && ydist < 13; - }); + return !!monster && getUnits(sdk.unittype.Missile) + // for every missle that isnt from our merc + .filter(missile => missile && monster && monster.gid === missile.owner) + // if any + .some(missile => { + let xoff = Math.abs(coord.x - missile.targetx); + let yoff = Math.abs(coord.y - missile.targety); + let xdist = Math.abs(coord.x - missile.x); + let ydist = Math.abs(coord.y - missile.y); + + // If missile wants to hit is and is close to us + return xoff < 7 && yoff < 7 && xdist < 13 && ydist < 13; + }); }; new Overrides.Override(Attack, Attack.sortMonsters, function(orignal, unitA, unitB) { - let stateCheck = (m) => [sdk.states.Fanaticism, sdk.states.Conviction].some(state => m.getState(state)); - if ((unitA.isSpecial && stateCheck(unitA)) && (unitB.isSpecial && stateCheck(unitB))) return getDistance(me, unitA) - getDistance(me, unitB); - if (unitA.isSpecial && stateCheck(unitA)) return -1; - if (unitB.isSpecial && stateCheck(unitB)) return 1; - return orignal(unitA, unitB); + let stateCheck = (m) => [sdk.states.Fanaticism, sdk.states.Conviction].some(state => m.getState(state)); + if ((unitA.isSpecial && stateCheck(unitA)) && (unitB.isSpecial && stateCheck(unitB))) return getDistance(me, unitA) - getDistance(me, unitB); + if (unitA.isSpecial && stateCheck(unitA)) return -1; + if (unitB.isSpecial && stateCheck(unitB)) return 1; + return orignal(unitA, unitB); }).apply(); /** @@ -1039,326 +1039,326 @@ new Overrides.Override(Attack, Attack.sortMonsters, function(orignal, unitA, uni * @param {Monster} unitB */ Attack.walkingSortMonsters = function (unitA, unitB) { - // sort main bosses first - if ((unitA.isPrimeEvil) && (unitB.isPrimeEvil)) return getDistance(me, unitA) - getDistance(me, unitB); - if (unitA.isPrimeEvil) return -1; - if (unitB.isPrimeEvil) return 1; - - // Barb optimization - if (me.barbarian) { - if (!Attack.checkResist(unitA, Attack.getSkillElement(Config.AttackSkill[(unitA.isSpecial) ? 1 : 3]))) { - return 1; - } - - if (!Attack.checkResist(unitB, Attack.getSkillElement(Config.AttackSkill[(unitB.isSpecial) ? 1 : 3]))) { - return -1; - } - } - - // Put monsters under Attract curse at the end of the list - They are helping us - if (unitA.getState(sdk.states.Attract)) return 1; - if (unitB.getState(sdk.states.Attract)) return -1; - - const ids = [ - sdk.monsters.OblivionKnight1, sdk.monsters.OblivionKnight2, sdk.monsters.OblivionKnight3, sdk.monsters.FallenShaman, sdk.monsters.CarverShaman, sdk.monsters.CarverShaman2, - sdk.monsters.DevilkinShaman, sdk.monsters.DevilkinShaman2, sdk.monsters.DarkShaman1, sdk.monsters.DarkShaman2, sdk.monsters.WarpedShaman, sdk.monsters.HollowOne, sdk.monsters.Guardian1, - sdk.monsters.Guardian2, sdk.monsters.Unraveler1, sdk.monsters.Unraveler2, sdk.monsters.Ancient1, sdk.monsters.BaalSubjectMummy, sdk.monsters.BloodRaven, sdk.monsters.RatManShaman, - sdk.monsters.FetishShaman, sdk.monsters.FlayerShaman1, sdk.monsters.FlayerShaman2, sdk.monsters.SoulKillerShaman1, sdk.monsters.SoulKillerShaman2, sdk.monsters.StygianDollShaman1, - sdk.monsters.StygianDollShaman2, sdk.monsters.FleshSpawner1, sdk.monsters.FleshSpawner2, sdk.monsters.StygianHag, sdk.monsters.Grotesque1, sdk.monsters.Ancient2, sdk.monsters.Ancient3, - sdk.monsters.Grotesque2, sdk.monsters.FoulCrowNest, sdk.monsters.BlackVultureNest, sdk.monsters.BloodHawkNest, sdk.monsters.BloodHookNest, - sdk.monsters.BloodWingNest, sdk.monsters.CloudStalkerNest, sdk.monsters.FeederNest, sdk.monsters.SuckerNest - ]; - - if (!me.inArea(sdk.areas.ClawViperTempleLvl2) && ids.includes(unitA.classid) && ids.includes(unitB.classid)) { - // Kill "scary" uniques first (like Bishibosh) - if ((unitA.isUnique) && (unitB.isUnique)) return getDistance(me, unitA) - getDistance(me, unitB); - if (unitA.isUnique) return -1; - if (unitB.isUnique) return 1; - - return getDistance(me, unitA) - getDistance(me, unitB); - } - - if (ids.includes(unitA.classid)) return -1; - if (ids.includes(unitB.classid)) return 1; - - if ((unitA.isSuperUnique) && (unitB.isSuperUnique)) return getDistance(me, unitA) - getDistance(me, unitB); - if (unitA.isSuperUnique) return -1; - if (unitB.isSuperUnique) return 1; - - // fallens are annoying, put them later if we have line of sight of another monster - if (unitA.isFallen && unitB.isFallen) return getDistance(me, unitA) - getDistance(me, unitB); - if (!unitA.isFallen && !checkCollision(me, unitA, (sdk.collision.BlockWall | sdk.collision.LineOfSight | sdk.collision.Ranged))) return -1; - if (!unitB.isFallen && !checkCollision(me, unitA, (sdk.collision.BlockWall | sdk.collision.LineOfSight | sdk.collision.Ranged))) return 1; - - return getDistance(me, unitA) - getDistance(me, unitB); + // sort main bosses first + if ((unitA.isPrimeEvil) && (unitB.isPrimeEvil)) return getDistance(me, unitA) - getDistance(me, unitB); + if (unitA.isPrimeEvil) return -1; + if (unitB.isPrimeEvil) return 1; + + // Barb optimization + if (me.barbarian) { + if (!Attack.checkResist(unitA, Attack.getSkillElement(Config.AttackSkill[(unitA.isSpecial) ? 1 : 3]))) { + return 1; + } + + if (!Attack.checkResist(unitB, Attack.getSkillElement(Config.AttackSkill[(unitB.isSpecial) ? 1 : 3]))) { + return -1; + } + } + + // Put monsters under Attract curse at the end of the list - They are helping us + if (unitA.getState(sdk.states.Attract)) return 1; + if (unitB.getState(sdk.states.Attract)) return -1; + + const ids = [ + sdk.monsters.OblivionKnight1, sdk.monsters.OblivionKnight2, sdk.monsters.OblivionKnight3, sdk.monsters.FallenShaman, sdk.monsters.CarverShaman, sdk.monsters.CarverShaman2, + sdk.monsters.DevilkinShaman, sdk.monsters.DevilkinShaman2, sdk.monsters.DarkShaman1, sdk.monsters.DarkShaman2, sdk.monsters.WarpedShaman, sdk.monsters.HollowOne, sdk.monsters.Guardian1, + sdk.monsters.Guardian2, sdk.monsters.Unraveler1, sdk.monsters.Unraveler2, sdk.monsters.Ancient1, sdk.monsters.BaalSubjectMummy, sdk.monsters.BloodRaven, sdk.monsters.RatManShaman, + sdk.monsters.FetishShaman, sdk.monsters.FlayerShaman1, sdk.monsters.FlayerShaman2, sdk.monsters.SoulKillerShaman1, sdk.monsters.SoulKillerShaman2, sdk.monsters.StygianDollShaman1, + sdk.monsters.StygianDollShaman2, sdk.monsters.FleshSpawner1, sdk.monsters.FleshSpawner2, sdk.monsters.StygianHag, sdk.monsters.Grotesque1, sdk.monsters.Ancient2, sdk.monsters.Ancient3, + sdk.monsters.Grotesque2, sdk.monsters.FoulCrowNest, sdk.monsters.BlackVultureNest, sdk.monsters.BloodHawkNest, sdk.monsters.BloodHookNest, + sdk.monsters.BloodWingNest, sdk.monsters.CloudStalkerNest, sdk.monsters.FeederNest, sdk.monsters.SuckerNest + ]; + + if (!me.inArea(sdk.areas.ClawViperTempleLvl2) && ids.includes(unitA.classid) && ids.includes(unitB.classid)) { + // Kill "scary" uniques first (like Bishibosh) + if ((unitA.isUnique) && (unitB.isUnique)) return getDistance(me, unitA) - getDistance(me, unitB); + if (unitA.isUnique) return -1; + if (unitB.isUnique) return 1; + + return getDistance(me, unitA) - getDistance(me, unitB); + } + + if (ids.includes(unitA.classid)) return -1; + if (ids.includes(unitB.classid)) return 1; + + if ((unitA.isSuperUnique) && (unitB.isSuperUnique)) return getDistance(me, unitA) - getDistance(me, unitB); + if (unitA.isSuperUnique) return -1; + if (unitB.isSuperUnique) return 1; + + // fallens are annoying, put them later if we have line of sight of another monster + if (unitA.isFallen && unitB.isFallen) return getDistance(me, unitA) - getDistance(me, unitB); + if (!unitA.isFallen && !checkCollision(me, unitA, (sdk.collision.BlockWall | sdk.collision.LineOfSight | sdk.collision.Ranged))) return -1; + if (!unitB.isFallen && !checkCollision(me, unitA, (sdk.collision.BlockWall | sdk.collision.LineOfSight | sdk.collision.Ranged))) return 1; + + return getDistance(me, unitA) - getDistance(me, unitB); }; Attack.pwnDury = function () { - let duriel = Misc.poll(() => Game.getMonster(sdk.monsters.Duriel)); - - if (!duriel) return false; - const tick = getTickCount(); - const gid = duriel.gid; - const saveSpots = [ - { x: 22648, y: 15688 }, - { x: 22624, y: 15725 }, - ]; - - // @todo - keep track of last position to attempt relocating dury if we've lost reference - try { - Attack.stopClear = true; - while (!duriel.dead) { - if (getTickCount() - tick > Time.minutes(10)) { - break; - } - if (!duriel || !copyUnit(duriel).x) { - duriel = Misc.poll(() => Game.getMonster(-1, -1, gid), 1000, 80); - if (!duriel || !duriel.attackable) return true; - } - //ToDo; figure out static - if (duriel.getState(sdk.states.Frozen) && duriel.distance < 7 || duriel.distance < 12) { - let safeSpot = saveSpots.sort((a, b) => getDistance(duriel, b) - getDistance(duriel, a)).first(); - Pather.teleportTo(safeSpot.x, safeSpot.y); - } - ClassAttack.doAttack(duriel, true); - } - } finally { - Attack.stopClear = false; - } - - return true; + let duriel = Misc.poll(() => Game.getMonster(sdk.monsters.Duriel)); + + if (!duriel) return false; + const tick = getTickCount(); + const gid = duriel.gid; + const saveSpots = [ + { x: 22648, y: 15688 }, + { x: 22624, y: 15725 }, + ]; + + // @todo - keep track of last position to attempt relocating dury if we've lost reference + try { + Attack.stopClear = true; + while (!duriel.dead) { + if (getTickCount() - tick > Time.minutes(10)) { + break; + } + if (!duriel || !copyUnit(duriel).x) { + duriel = Misc.poll(() => Game.getMonster(-1, -1, gid), 1000, 80); + if (!duriel || !duriel.attackable) return true; + } + //ToDo; figure out static + if (duriel.getState(sdk.states.Frozen) && duriel.distance < 7 || duriel.distance < 12) { + let safeSpot = saveSpots.sort((a, b) => getDistance(duriel, b) - getDistance(duriel, a)).first(); + Pather.teleportTo(safeSpot.x, safeSpot.y); + } + ClassAttack.doAttack(duriel, true); + } + } finally { + Attack.stopClear = false; + } + + return true; }; Attack.pwnMeph = function () { - // TODO: fill out + // TODO: fill out }; // Credit @Jaenster - modified by me(theBGuy) for other classes Attack.pwnDia = function () { - // Can't farcast if our skill main attack isn't meant for it - if ((!me.sorceress && !me.necromancer && !me.assassin) - || (["Poison", "Summon"].includes(SetUp.currentBuild)) - || (Skill.getRange(Config.AttackSkill[1]) < 10)) { - return false; - } - - const calculateSpots = function (center, skillRange) { - let coords = []; - for (let i = 0; i < 360; i++) { - coords.push({ - x: Math.floor(center.x + skillRange * Math.cos(i) + 0.5), - y: Math.floor(center.y + skillRange * Math.sin(i) + 0.5), - }); - } - // only unique spots - return coords.filter((e, i, s) => s.indexOf(e) === i).filter(el => Attack.validSpot(el.x, el.y)); - }; - - const checkMobs = function () { - let mobs = getUnits(sdk.unittype.Monster).filter(function(el) { - return !!el && el.attackable && el.classid !== sdk.monsters.Diablo && el.distance < 20; - }); - return mobs; - }; - - const getDiablo = function () { - let check = checkMobs(); - !!check && Attack.clearList(check); - return Game.getMonster(sdk.monsters.Diablo); - }; - { - let nearSpot = Pather.spotOnDistance({ x: 7792, y: 5292 }, 35, { returnSpotOnError: false }); - Pather.moveToUnit(nearSpot); - } - - let dia = Misc.poll(getDiablo, 15e3, 30); - - if (!dia) { - // Move to Star - Pather.moveTo(7788, 5292, 3, 30); - dia = Misc.poll(getDiablo, 15e3, 30); - } - - if (!dia) { - console.log("No diablo"); - return false; - } - - let tick = getTickCount(); - let lastPosition = { x: 7791, y: 5293 }; - let manaTP, manaSK, manaStatic, rangeStatic; - let [minDist, maxDist, minRange, maxRange] = (() => { - // set values - switch (me.classid) { - case sdk.player.class.Sorceress: - [manaTP, manaSK] = [Skill.getManaCost(sdk.skills.Teleport), Skill.getManaCost(Config.AttackSkill[1])]; - [manaStatic, rangeStatic] = [Skill.getManaCost(sdk.skills.StaticField), Skill.getManaCost(sdk.skills.StaticField)]; - - switch (Config.AttackSkill[1]) { - case sdk.skills.FrozenOrb: - return [15, 20, 10, 20]; - case sdk.skills.Lightning: - return [20, 25, 18, 25]; - default: - case sdk.skills.Blizzard: - case sdk.skills.Meteor: - return [40, 45, 15, 58]; - } - case sdk.player.class.Necromancer: - return [35, 40, 15, 50]; - case sdk.player.class.Assassin: - return [25, 30, 15, 30]; - default: - return [15, 20, 10, 20]; - } - })(); - - const shouldWalk = function (spot) { - if (!Pather.canTeleport()) return true; - return (spot.distance < 10 || me.gold < 10000 || me.mpPercent < 50); - }; - - Attack.stopClear = true; - - do { - // give up in 10 minutes - if (getTickCount() - tick > Time.minutes(10)) { - break; - } - - while ((dia = getDiablo())) { - if (dia.dead) { - me.overhead("Diablo's dead"); - break; - } - - if (getDistance(me, dia) < minDist || getDistance(me, dia) > maxDist || getTickCount() - tick > 25e3) { - let spot = calculateSpots(dia, ((minRange + maxRange) / 2)) - .filter((loc) => getDistance(me, loc) > minRange && getDistance(me, loc) < maxRange /*todo, in neighbour room*/) - .filter(function (loc) { - let collision = getCollision(me.area, loc.x, loc.y); - // noinspection JSBitwiseOperatorUsage - let isLava = !!(collision & Coords_1.BlockBits.IsOnFloor); - // this spot is on lava, fuck this - if (isLava) return false; - // noinspection JSBitwiseOperatorUsage - return !(collision & (Coords_1.BlockBits.BlockWall)); - }) - .sort((a, b) => getDistance(me, a) - getDistance(me, b)) - .first(); - tick = getTickCount(); - if (spot !== undefined) { - shouldWalk(spot) ? Pather.walkTo(spot.x, spot.y) : Pather.moveTo(spot.x, spot.y, 15, false); - } - } - - if (me.sorceress && me.mp < manaSK + manaTP) { - me.overhead("Dont attack, save mana for teleport"); - delay(10); - continue; - } - - if (me.necromancer || me.assassin) { - me.overhead("FarCasting: Diablo's health " + dia.hpPercent + " % left"); - ClassAttack.farCast(dia); - } else { - // If we got enough mana to teleport close to diablo, static the bitch, and jump back - let diabloMissiles = getUnits(sdk.unittype.Missile).filter(function (unit) { - let _a; - return ((_a = unit.getParent()) === null || _a === void 0 ? void 0 : _a.gid) === dia.gid; - }); - console.log("Diablo missiles: " + diabloMissiles.length); - console.log("Diablo mode:" + dia.mode); - me.overhead("Dia life " + (~~(dia.hp / 128 * 100)).toString() + "%"); - if (me.mp > manaStatic + manaTP + manaTP && diabloMissiles.length < 3 && !dia.attacking && dia.hpPercent > Config.CastStatic) { - let [x, y] = me; - ClassAttack.switchCurse(dia, true); // curse him if we can - // re-check his mode - if (!dia.attacking) { - // Find a spot close to Diablo - let spot = Pather.spotOnDistance(dia, rangeStatic * (2 / 3), { returnSpotOnError: false }); - Pather.moveToEx(spot.x, spot.y, { allowClearing: false }); - Skill.cast(sdk.skills.StaticField); - // move back to previous spot - Pather.moveToEx(x, y, { allowClearing: false }); - } - } - Skill.cast(Config.AttackSkill[1], sdk.skills.hand.Right, dia); - - if (!!dia && !checkCollision(me, dia, Coords_1.Collision.BLOCK_MISSILE) && Skill.getRange(Config.AttackSkill[2]) > 15) { - Skill.cast(Config.AttackSkill[2], sdk.skills.hand.Right, dia); - } - } - } - - if (dia && dia.dead) { - break; - } - - if (!dia) { - let path = getPath(me.area, me.x, me.y, lastPosition.x, lastPosition.y, 1, 5); - - // failed to make a path from me to the old spot - if (!path) { - break; - } - - // walk close to old node, if we dont find dia continue - if (!path.some(function (node) { - Pather.walkTo(node.x, node.y); - return getDiablo(); - })) { - break; - } - } - } while (true); - - !!dia ? Pather.moveTo(dia) : Pather.moveTo(7774, 5305); - Pickit.pickItems(); - Pather.moveTo(7792, 5291); // Move back to star - Pickit.pickItems(); - Attack.stopClear = false; - - return dia; + // Can't farcast if our skill main attack isn't meant for it + if ((!me.sorceress && !me.necromancer && !me.assassin) + || (["Poison", "Summon"].includes(SetUp.currentBuild)) + || (Skill.getRange(Config.AttackSkill[1]) < 10)) { + return false; + } + + const calculateSpots = function (center, skillRange) { + let coords = []; + for (let i = 0; i < 360; i++) { + coords.push({ + x: Math.floor(center.x + skillRange * Math.cos(i) + 0.5), + y: Math.floor(center.y + skillRange * Math.sin(i) + 0.5), + }); + } + // only unique spots + return coords.filter((e, i, s) => s.indexOf(e) === i).filter(el => Attack.validSpot(el.x, el.y)); + }; + + const checkMobs = function () { + let mobs = getUnits(sdk.unittype.Monster).filter(function(el) { + return !!el && el.attackable && el.classid !== sdk.monsters.Diablo && el.distance < 20; + }); + return mobs; + }; + + const getDiablo = function () { + let check = checkMobs(); + !!check && Attack.clearList(check); + return Game.getMonster(sdk.monsters.Diablo); + }; + { + let nearSpot = Pather.spotOnDistance({ x: 7792, y: 5292 }, 35, { returnSpotOnError: false }); + Pather.moveToUnit(nearSpot); + } + + let dia = Misc.poll(getDiablo, 15e3, 30); + + if (!dia) { + // Move to Star + Pather.moveTo(7788, 5292, 3, 30); + dia = Misc.poll(getDiablo, 15e3, 30); + } + + if (!dia) { + console.log("No diablo"); + return false; + } + + let tick = getTickCount(); + let lastPosition = { x: 7791, y: 5293 }; + let manaTP, manaSK, manaStatic, rangeStatic; + let [minDist, maxDist, minRange, maxRange] = (() => { + // set values + switch (me.classid) { + case sdk.player.class.Sorceress: + [manaTP, manaSK] = [Skill.getManaCost(sdk.skills.Teleport), Skill.getManaCost(Config.AttackSkill[1])]; + [manaStatic, rangeStatic] = [Skill.getManaCost(sdk.skills.StaticField), Skill.getManaCost(sdk.skills.StaticField)]; + + switch (Config.AttackSkill[1]) { + case sdk.skills.FrozenOrb: + return [15, 20, 10, 20]; + case sdk.skills.Lightning: + return [20, 25, 18, 25]; + default: + case sdk.skills.Blizzard: + case sdk.skills.Meteor: + return [40, 45, 15, 58]; + } + case sdk.player.class.Necromancer: + return [35, 40, 15, 50]; + case sdk.player.class.Assassin: + return [25, 30, 15, 30]; + default: + return [15, 20, 10, 20]; + } + })(); + + const shouldWalk = function (spot) { + if (!Pather.canTeleport()) return true; + return (spot.distance < 10 || me.gold < 10000 || me.mpPercent < 50); + }; + + Attack.stopClear = true; + + do { + // give up in 10 minutes + if (getTickCount() - tick > Time.minutes(10)) { + break; + } + + while ((dia = getDiablo())) { + if (dia.dead) { + me.overhead("Diablo's dead"); + break; + } + + if (getDistance(me, dia) < minDist || getDistance(me, dia) > maxDist || getTickCount() - tick > 25e3) { + let spot = calculateSpots(dia, ((minRange + maxRange) / 2)) + .filter((loc) => getDistance(me, loc) > minRange && getDistance(me, loc) < maxRange /*todo, in neighbour room*/) + .filter(function (loc) { + let collision = getCollision(me.area, loc.x, loc.y); + // noinspection JSBitwiseOperatorUsage + let isLava = !!(collision & Coords_1.BlockBits.IsOnFloor); + // this spot is on lava, fuck this + if (isLava) return false; + // noinspection JSBitwiseOperatorUsage + return !(collision & (Coords_1.BlockBits.BlockWall)); + }) + .sort((a, b) => getDistance(me, a) - getDistance(me, b)) + .first(); + tick = getTickCount(); + if (spot !== undefined) { + shouldWalk(spot) ? Pather.walkTo(spot.x, spot.y) : Pather.moveTo(spot.x, spot.y, 15, false); + } + } + + if (me.sorceress && me.mp < manaSK + manaTP) { + me.overhead("Dont attack, save mana for teleport"); + delay(10); + continue; + } + + if (me.necromancer || me.assassin) { + me.overhead("FarCasting: Diablo's health " + dia.hpPercent + " % left"); + ClassAttack.farCast(dia); + } else { + // If we got enough mana to teleport close to diablo, static the bitch, and jump back + let diabloMissiles = getUnits(sdk.unittype.Missile).filter(function (unit) { + let _a; + return ((_a = unit.getParent()) === null || _a === void 0 ? void 0 : _a.gid) === dia.gid; + }); + console.log("Diablo missiles: " + diabloMissiles.length); + console.log("Diablo mode:" + dia.mode); + me.overhead("Dia life " + (~~(dia.hp / 128 * 100)).toString() + "%"); + if (me.mp > manaStatic + manaTP + manaTP && diabloMissiles.length < 3 && !dia.attacking && dia.hpPercent > Config.CastStatic) { + let [x, y] = me; + ClassAttack.switchCurse(dia, true); // curse him if we can + // re-check his mode + if (!dia.attacking) { + // Find a spot close to Diablo + let spot = Pather.spotOnDistance(dia, rangeStatic * (2 / 3), { returnSpotOnError: false }); + Pather.moveToEx(spot.x, spot.y, { allowClearing: false }); + Skill.cast(sdk.skills.StaticField); + // move back to previous spot + Pather.moveToEx(x, y, { allowClearing: false }); + } + } + Skill.cast(Config.AttackSkill[1], sdk.skills.hand.Right, dia); + + if (!!dia && !checkCollision(me, dia, Coords_1.Collision.BLOCK_MISSILE) && Skill.getRange(Config.AttackSkill[2]) > 15) { + Skill.cast(Config.AttackSkill[2], sdk.skills.hand.Right, dia); + } + } + } + + if (dia && dia.dead) { + break; + } + + if (!dia) { + let path = getPath(me.area, me.x, me.y, lastPosition.x, lastPosition.y, 1, 5); + + // failed to make a path from me to the old spot + if (!path) { + break; + } + + // walk close to old node, if we dont find dia continue + if (!path.some(function (node) { + Pather.walkTo(node.x, node.y); + return getDiablo(); + })) { + break; + } + } + } while (true); + + !!dia ? Pather.moveTo(dia) : Pather.moveTo(7774, 5305); + Pickit.pickItems(); + Pather.moveTo(7792, 5291); // Move back to star + Pickit.pickItems(); + Attack.stopClear = false; + + return dia; }; Attack.pwnAncients = function () { - // @todo fillout + // @todo fillout }; Attack.deploy = function (unit, distance = 10, spread = 5, range = 9) { - if (arguments.length < 4) throw new Error("deploy: Not enough arguments supplied"); + if (arguments.length < 4) throw new Error("deploy: Not enough arguments supplied"); - let index, currCount; - let count = 999; - let monList = (this.buildMonsterList() || []).sort(Sort.units); + let index, currCount; + let count = 999; + let monList = (this.buildMonsterList() || []).sort(Sort.units); - if (this.getMonsterCount(me.x, me.y, 15, monList) === 0) return true; + if (this.getMonsterCount(me.x, me.y, 15, monList) === 0) return true; - CollMap.getNearbyRooms(unit.x, unit.y); - let grid = this.buildGrid(unit.x - distance, unit.x + distance, unit.y - distance, unit.y + distance, spread); + CollMap.getNearbyRooms(unit.x, unit.y); + let grid = this.buildGrid(unit.x - distance, unit.x + distance, unit.y - distance, unit.y + distance, spread); - if (!grid.length) return false; - grid.sort(function (a, b) { - return getDistance(b.x, b.y, unit.x, unit.y) - getDistance(a.x, a.y, unit.x, unit.y); - }); + if (!grid.length) return false; + grid.sort(function (a, b) { + return getDistance(b.x, b.y, unit.x, unit.y) - getDistance(a.x, a.y, unit.x, unit.y); + }); - for (let i = 0; i < grid.length; i += 1) { - if (!(CollMap.getColl(grid[i].x, grid[i].y, true) & sdk.collision.BlockWall) && !CollMap.checkColl(unit, { x: grid[i].x, y: grid[i].y }, sdk.collision.Ranged)) { - currCount = this.getMonsterCount(grid[i].x, grid[i].y, range, monList); + for (let i = 0; i < grid.length; i += 1) { + if (!(CollMap.getColl(grid[i].x, grid[i].y, true) & sdk.collision.BlockWall) && !CollMap.checkColl(unit, { x: grid[i].x, y: grid[i].y }, sdk.collision.Ranged)) { + currCount = this.getMonsterCount(grid[i].x, grid[i].y, range, monList); - if (currCount < count) { - index = i; - count = currCount; - } + if (currCount < count) { + index = i; + count = currCount; + } - if (currCount === 0) { - break; - } - } - } + if (currCount === 0) { + break; + } + } + } - return typeof index === "number" ? Pather.moveTo(grid[index].x, grid[index].y, 0) : false; + return typeof index === "number" ? Pather.moveTo(grid[index].x, grid[index].y, 0) : false; }; /** @@ -1371,148 +1371,148 @@ Attack.deploy = function (unit, distance = 10, spread = 5, range = 9) { * @returns {boolean} */ Attack.getIntoPosition = function (unit = false, distance = 0, coll = 0, walk = false, force = false) { - if (!unit || !unit.x || !unit.y) return false; - Developer.debugging.pathing && console.time("getIntoPosition"); - const useTele = Pather.useTeleport(); - walk === true && (walk = 1); - - if (distance < 4 && (!unit.hasOwnProperty("mode") || !unit.dead)) { - /** - * if we are surrounded by monsters it can be near impossible to get into position - * what would be good is if we are surrounded either pick an AoE skill and cast or - * just attack whatever monster is the nearest to us, this would also be a good place - * for necro's use of terror and barbs use of howl/leap/leapAttack/whirlwind - */ - // we are actually able to walk to where we want to go, hopefully prevent wall hugging - if (walk && (unit.distance < 8 || !CollMap.checkColl(me, unit, sdk.collision.WallOrRanged | sdk.collision.Objects | sdk.collision.IsOnFloor))) { - Pather.walkTo(unit.x, unit.y, 3); - } else if (walk && (unit.distance < 4 && CollMap.checkColl(me, unit, sdk.collision.MonsterIsOnFloorDarkArea))) { - console.debug("Are we in a doorway?"); - return true; - } else { - // don't clear while trying to reposition - Pather.moveToEx(unit.x, unit.y, { clearSettings: { allowClearing: !useTele, range: useTele ? 10 : 5 } }); - } - - return !CollMap.checkColl(me, unit, coll); - } - - let cx, cy, currCount, count = 999, potentialSpot = { x: undefined, y: undefined }; - let coords = []; - let fullDistance = distance; - const name = unit.hasOwnProperty("name") ? unit.name : ""; - const angle = Math.round(Math.atan2(me.y - unit.y, me.x - unit.x) * 180 / Math.PI); - const angles = [0, 15, -15, 30, -30, 45, -45, 60, -60, 75, -75, 90, -90, 135, -135, 180]; - - let caster = (force && !me.inTown); - - // let t = getTickCount(); - - for (let n = 0; n < 3; n += 1) { - const nearMobs = getUnits(sdk.unittype.Monster).filter(m => m.getStat(sdk.stats.Alignment) !== 2); - (n > 0) && (distance -= Math.floor(fullDistance / 3 - 1)); - - for (let i = 0; i < angles.length; i += 1) { - cx = Math.round((Math.cos((angle + angles[i]) * Math.PI / 180)) * distance + unit.x); - cy = Math.round((Math.sin((angle + angles[i]) * Math.PI / 180)) * distance + unit.y); - (Pather.checkSpot(cx, cy, sdk.collision.BlockWall, false)) && coords.push({ x: cx, y: cy }); - } - - if (coords.length > 0) { - coords.sort(Sort.units); - - // If one of the valid positions is a position I am at already - and we aren't trying to force a new spot - if (!force) { - for (let i = 0; i < coords.length; i += 1) { - if ((getDistance(me, coords[i].x, coords[i].y) < 1 - && !CollMap.checkColl(unit, { x: coords[i].x, y: coords[i].y }, sdk.collision.WallOrRanged | sdk.collision.Objects | sdk.collision.IsOnFloor, 1)) - || (getDistance(me, coords[i].x, coords[i].y) <= 5 && me.getMobCount(6) > 2)) { - return true; - } - } - } - - for (let i = 0; i < coords.length; i += 1) { - // Valid position found - no collision between the spot and the unit - if (!CollMap.checkColl({ x: coords[i].x, y: coords[i].y }, unit, coll, 1)) { - // currCount = coords[i].mobCount({ range: 7 }); - Developer.debugging.pathing && console.time("countMobs"); - currCount = nearMobs.filter(m => getDistance(coords[i].x, coords[i].y, m.x, m.y) < 8).length; - Developer.debugging.pathing && console.timeEnd("countMobs"); - - // this might be a valid spot but also check the mob count at that node - if (caster) { - potentialSpot.x === undefined && (potentialSpot = { x: coords[i].x, y: coords[i].y }); - - if (currCount < count) { - count = currCount; - potentialSpot = { x: coords[i].x, y: coords[i].y }; - Developer.debugging.pathing && console.log(sdk.colors.Blue + "CheckedSpot" + sdk.colors.Yellow + ": x: " + coords[i].x + " y: " + coords[i].y + " mob amount: " + sdk.colors.NeonGreen + count); - } - - if (currCount !== 0) { - Developer.debugging.pathing && console.log(sdk.colors.Red + "Not Zero, check next: currCount: " + sdk.colors.NeonGreen + " " + currCount); - continue; - } - } - - // I am already in my optimal position - if (coords[i].distance < 3) return true; - - // we are actually able to walk to where we want to go, hopefully prevent wall hugging - if (walk && (coords[i].distance < 6 || !CollMap.checkColl(me, unit, sdk.collision.WallOrRanged | sdk.collision.Objects | sdk.collision.IsOnFloor))) { - Pather.walkTo(coords[i].x, coords[i].y, 2); - } else { - Pather.moveToEx(coords[i].x, coords[i].y, { clearSettings: { allowClearing: !useTele, range: useTele ? 10 : 5, retry: 3 } }); - } - - Developer.debugging.pathing && console.log(sdk.colors.Purple + "SecondCheck :: " + sdk.colors.Yellow + "Moving to: x: " + coords[i].x + " y: " + coords[i].y + " mob amount: " + sdk.colors.NeonGreen + currCount); - Developer.debugging.pathing && console.timeEnd("getIntoPosition"); - return true; - } - } - } - } - - if (caster && potentialSpot.x !== undefined) { - if (potentialSpot.distance < 3) return true; - if ((() => { - if (Pather.useTeleport() && Pather.teleportTo(potentialSpot.x, potentialSpot.y)) { - return true; - } - switch (walk) { - case 1: - return Pather.walkTo(potentialSpot.x, potentialSpot.y, 2); - case 2: - default: - if (potentialSpot.distance < 6 && !CollMap.checkColl(me, potentialSpot, sdk.collision.WallOrRanged)) { - return Pather.walkTo(potentialSpot.x, potentialSpot.y, 2); - } - return Pather.moveTo(potentialSpot.x, potentialSpot.y, 1); - } - })()) { - Developer.debugging.pathing && console.log(sdk.colors.Orange + "DefaultCheck :: " + sdk.colors.Yellow + "Moving to: x: " + potentialSpot.x + " y: " + potentialSpot.y + " mob amount: " + sdk.colors.NeonGreen + count); - Developer.debugging.pathing && console.timeEnd("getIntoPosition"); - return true; - } - } - - console.warn("ÿc4Attackÿc0: Failed to get into valid position" + (name ? " for: " + name : "")); - - return false; + if (!unit || !unit.x || !unit.y) return false; + Developer.debugging.pathing && console.time("getIntoPosition"); + const useTele = Pather.useTeleport(); + walk === true && (walk = 1); + + if (distance < 4 && (!unit.hasOwnProperty("mode") || !unit.dead)) { + /** + * if we are surrounded by monsters it can be near impossible to get into position + * what would be good is if we are surrounded either pick an AoE skill and cast or + * just attack whatever monster is the nearest to us, this would also be a good place + * for necro's use of terror and barbs use of howl/leap/leapAttack/whirlwind + */ + // we are actually able to walk to where we want to go, hopefully prevent wall hugging + if (walk && (unit.distance < 8 || !CollMap.checkColl(me, unit, sdk.collision.WallOrRanged | sdk.collision.Objects | sdk.collision.IsOnFloor))) { + Pather.walkTo(unit.x, unit.y, 3); + } else if (walk && (unit.distance < 4 && CollMap.checkColl(me, unit, sdk.collision.MonsterIsOnFloorDarkArea))) { + console.debug("Are we in a doorway?"); + return true; + } else { + // don't clear while trying to reposition + Pather.moveToEx(unit.x, unit.y, { clearSettings: { allowClearing: !useTele, range: useTele ? 10 : 5 } }); + } + + return !CollMap.checkColl(me, unit, coll); + } + + let cx, cy, currCount, count = 999, potentialSpot = { x: undefined, y: undefined }; + let coords = []; + let fullDistance = distance; + const name = unit.hasOwnProperty("name") ? unit.name : ""; + const angle = Math.round(Math.atan2(me.y - unit.y, me.x - unit.x) * 180 / Math.PI); + const angles = [0, 15, -15, 30, -30, 45, -45, 60, -60, 75, -75, 90, -90, 135, -135, 180]; + + let caster = (force && !me.inTown); + + // let t = getTickCount(); + + for (let n = 0; n < 3; n += 1) { + const nearMobs = getUnits(sdk.unittype.Monster).filter(m => m.getStat(sdk.stats.Alignment) !== 2); + (n > 0) && (distance -= Math.floor(fullDistance / 3 - 1)); + + for (let i = 0; i < angles.length; i += 1) { + cx = Math.round((Math.cos((angle + angles[i]) * Math.PI / 180)) * distance + unit.x); + cy = Math.round((Math.sin((angle + angles[i]) * Math.PI / 180)) * distance + unit.y); + (Pather.checkSpot(cx, cy, sdk.collision.BlockWall, false)) && coords.push({ x: cx, y: cy }); + } + + if (coords.length > 0) { + coords.sort(Sort.units); + + // If one of the valid positions is a position I am at already - and we aren't trying to force a new spot + if (!force) { + for (let i = 0; i < coords.length; i += 1) { + if ((getDistance(me, coords[i].x, coords[i].y) < 1 + && !CollMap.checkColl(unit, { x: coords[i].x, y: coords[i].y }, sdk.collision.WallOrRanged | sdk.collision.Objects | sdk.collision.IsOnFloor, 1)) + || (getDistance(me, coords[i].x, coords[i].y) <= 5 && me.getMobCount(6) > 2)) { + return true; + } + } + } + + for (let i = 0; i < coords.length; i += 1) { + // Valid position found - no collision between the spot and the unit + if (!CollMap.checkColl({ x: coords[i].x, y: coords[i].y }, unit, coll, 1)) { + // currCount = coords[i].mobCount({ range: 7 }); + Developer.debugging.pathing && console.time("countMobs"); + currCount = nearMobs.filter(m => getDistance(coords[i].x, coords[i].y, m.x, m.y) < 8).length; + Developer.debugging.pathing && console.timeEnd("countMobs"); + + // this might be a valid spot but also check the mob count at that node + if (caster) { + potentialSpot.x === undefined && (potentialSpot = { x: coords[i].x, y: coords[i].y }); + + if (currCount < count) { + count = currCount; + potentialSpot = { x: coords[i].x, y: coords[i].y }; + Developer.debugging.pathing && console.log(sdk.colors.Blue + "CheckedSpot" + sdk.colors.Yellow + ": x: " + coords[i].x + " y: " + coords[i].y + " mob amount: " + sdk.colors.NeonGreen + count); + } + + if (currCount !== 0) { + Developer.debugging.pathing && console.log(sdk.colors.Red + "Not Zero, check next: currCount: " + sdk.colors.NeonGreen + " " + currCount); + continue; + } + } + + // I am already in my optimal position + if (coords[i].distance < 3) return true; + + // we are actually able to walk to where we want to go, hopefully prevent wall hugging + if (walk && (coords[i].distance < 6 || !CollMap.checkColl(me, unit, sdk.collision.WallOrRanged | sdk.collision.Objects | sdk.collision.IsOnFloor))) { + Pather.walkTo(coords[i].x, coords[i].y, 2); + } else { + Pather.moveToEx(coords[i].x, coords[i].y, { clearSettings: { allowClearing: !useTele, range: useTele ? 10 : 5, retry: 3 } }); + } + + Developer.debugging.pathing && console.log(sdk.colors.Purple + "SecondCheck :: " + sdk.colors.Yellow + "Moving to: x: " + coords[i].x + " y: " + coords[i].y + " mob amount: " + sdk.colors.NeonGreen + currCount); + Developer.debugging.pathing && console.timeEnd("getIntoPosition"); + return true; + } + } + } + } + + if (caster && potentialSpot.x !== undefined) { + if (potentialSpot.distance < 3) return true; + if ((() => { + if (Pather.useTeleport() && Pather.teleportTo(potentialSpot.x, potentialSpot.y)) { + return true; + } + switch (walk) { + case 1: + return Pather.walkTo(potentialSpot.x, potentialSpot.y, 2); + case 2: + default: + if (potentialSpot.distance < 6 && !CollMap.checkColl(me, potentialSpot, sdk.collision.WallOrRanged)) { + return Pather.walkTo(potentialSpot.x, potentialSpot.y, 2); + } + return Pather.moveTo(potentialSpot.x, potentialSpot.y, 1); + } + })()) { + Developer.debugging.pathing && console.log(sdk.colors.Orange + "DefaultCheck :: " + sdk.colors.Yellow + "Moving to: x: " + potentialSpot.x + " y: " + potentialSpot.y + " mob amount: " + sdk.colors.NeonGreen + count); + Developer.debugging.pathing && console.timeEnd("getIntoPosition"); + return true; + } + } + + console.warn("ÿc4Attackÿc0: Failed to get into valid position" + (name ? " for: " + name : "")); + + return false; }; Attack.castableSpot = function (x = undefined, y = undefined) { - // Just in case - if (!me.area || !x || !y) return false; + // Just in case + if (!me.area || !x || !y) return false; - let result; + let result; - try { // Treat thrown errors as invalid spot - result = getCollision(me.area, x, y); - } catch (e) { - return false; - } + try { // Treat thrown errors as invalid spot + result = getCollision(me.area, x, y); + } catch (e) { + return false; + } - return !(result === undefined || !!(result & Coords_1.BlockBits.Casting) || !!(result & Coords_1.Collision.BLOCK_MISSILE) || (result & sdk.collision.Objects) || (result & sdk.collision.BlockWall)); + return !(result === undefined || !!(result & Coords_1.BlockBits.Casting) || !!(result & Coords_1.Collision.BLOCK_MISSILE) || (result & sdk.collision.Objects) || (result & sdk.collision.BlockWall)); }; diff --git a/libs/SoloPlay/Functions/AutoBuild.js b/libs/SoloPlay/Functions/AutoBuild.js index f0f6484f..23854ce9 100644 --- a/libs/SoloPlay/Functions/AutoBuild.js +++ b/libs/SoloPlay/Functions/AutoBuild.js @@ -8,81 +8,81 @@ js_strict(true); const AutoBuild = new function AutoBuild () { - Config.AutoBuild.DebugMode && (Config.AutoBuild.Verbose = true); - const debug = !!Config.AutoBuild.DebugMode; - const verbose = !!Config.AutoBuild.Verbose; + Config.AutoBuild.DebugMode && (Config.AutoBuild.Verbose = true); + const debug = !!Config.AutoBuild.DebugMode; + const verbose = !!Config.AutoBuild.Verbose; - const log = (message) => FileTools.appendText(getLogFilename(), message + "\n"); - const getCurrentScript = () => getScript(true).name.toLowerCase(); + const log = (message) => FileTools.appendText(getLogFilename(), message + "\n"); + const getCurrentScript = () => getScript(true).name.toLowerCase(); - const buildTemplate = me.currentBuild.AutoBuildTemplate; - let configUpdateLevel = 0; - let lastSuccessfulUpdateLevel = 0; + const buildTemplate = me.currentBuild.AutoBuildTemplate; + let configUpdateLevel = 0; + let lastSuccessfulUpdateLevel = 0; - // Apply all Update functions from the build template in order from level 1 to me.charlvl. - // By reapplying all of the changes to the Config object, we preserve - // the state of the Config file without altering the saved char config. - function applyConfigUpdates () { - let cLvl = me.charlvl; - debug && this.print("Updating Config from level " + configUpdateLevel + " to " + cLvl); - let reapply = true; + // Apply all Update functions from the build template in order from level 1 to me.charlvl. + // By reapplying all of the changes to the Config object, we preserve + // the state of the Config file without altering the saved char config. + function applyConfigUpdates () { + let cLvl = me.charlvl; + debug && this.print("Updating Config from level " + configUpdateLevel + " to " + cLvl); + let reapply = true; - while (configUpdateLevel < cLvl) { - configUpdateLevel += 1; - Skill.init(); - if (buildTemplate[configUpdateLevel] !== undefined) { - buildTemplate[configUpdateLevel].Update.apply(Config); - lastSuccessfulUpdateLevel = configUpdateLevel; - } else if (reapply) { - // re-apply from the last successful update - this is helpful if inside the build file there are conditional statements - buildTemplate[lastSuccessfulUpdateLevel].Update.apply(Config); - reapply = false; - } - } - } + while (configUpdateLevel < cLvl) { + configUpdateLevel += 1; + Skill.init(); + if (buildTemplate[configUpdateLevel] !== undefined) { + buildTemplate[configUpdateLevel].Update.apply(Config); + lastSuccessfulUpdateLevel = configUpdateLevel; + } else if (reapply) { + // re-apply from the last successful update - this is helpful if inside the build file there are conditional statements + buildTemplate[lastSuccessfulUpdateLevel].Update.apply(Config); + reapply = false; + } + } + } - function getLogFilename () { - let d = new Date(); - let dateString = d.getMonth() + "_" + d.getDate() + "_" + d.getFullYear(); - return "logs/AutoBuild." + me.realm + "." + me.charname + "." + dateString + ".log"; - } + function getLogFilename () { + let d = new Date(); + let dateString = d.getMonth() + "_" + d.getDate() + "_" + d.getFullYear(); + return "logs/AutoBuild." + me.realm + "." + me.charname + "." + dateString + ".log"; + } - function initialize () { - let currentScript = getCurrentScript(); - this.print("Including build template " + SetUp._buildTemplate + " into " + currentScript); + function initialize () { + let currentScript = getCurrentScript(); + this.print("Including build template " + SetUp._buildTemplate + " into " + currentScript); - if (!buildTemplate) throw new Error("Failed to include template: " + SetUp._buildTemplate); + if (!buildTemplate) throw new Error("Failed to include template: " + SetUp._buildTemplate); - // All threads except soloplay.js use this event listener - // to update their thread-local Config object - if (currentScript !== "libs\\soloplay\\soloplay.js") { - addEventListener("scriptmsg", levelUpHandler); - } + // All threads except soloplay.js use this event listener + // to update their thread-local Config object + if (currentScript !== "libs\\soloplay\\soloplay.js") { + addEventListener("scriptmsg", levelUpHandler); + } - // Resynchronize our Config object with all past changes - // made to it by AutoBuild system - applyConfigUpdates(); - } + // Resynchronize our Config object with all past changes + // made to it by AutoBuild system + applyConfigUpdates(); + } - function levelUpHandler (obj) { - if (typeof obj === "object" && obj.hasOwnProperty("event") && obj.event === "level up") { - applyConfigUpdates(); - } - } + function levelUpHandler (obj) { + if (typeof obj === "object" && obj.hasOwnProperty("event") && obj.event === "level up") { + applyConfigUpdates(); + } + } - // Only print to console from autobuildthread.js, - // but log from all scripts - function myPrint () { - if (!debug && !verbose) return; - let args = Array.prototype.slice.call(arguments); - args.unshift("AutoBuild:"); - let result = args.join(" "); - verbose && print.call(this, result); - debug && log.call(this, result); - } + // Only print to console from autobuildthread.js, + // but log from all scripts + function myPrint () { + if (!debug && !verbose) return; + let args = Array.prototype.slice.call(arguments); + args.unshift("AutoBuild:"); + let result = args.join(" "); + verbose && print.call(this, result); + debug && log.call(this, result); + } - this.levelUpHandler = levelUpHandler; - this.print = myPrint; - this.initialize = initialize; - this.applyConfigUpdates = applyConfigUpdates; + this.levelUpHandler = levelUpHandler; + this.print = myPrint; + this.initialize = initialize; + this.applyConfigUpdates = applyConfigUpdates; }; diff --git a/libs/SoloPlay/Functions/AutoMuleOverrides.js b/libs/SoloPlay/Functions/AutoMuleOverrides.js index 22535a47..8bd7fac1 100644 --- a/libs/SoloPlay/Functions/AutoMuleOverrides.js +++ b/libs/SoloPlay/Functions/AutoMuleOverrides.js @@ -13,61 +13,61 @@ includeIfNotIncluded("systems/automule/Automule.js"); AutoMule.getMuleItems = function () { - let info = this.getInfo(); + let info = this.getInfo(); - if (!info || !info.hasOwnProperty("muleInfo")) { - return false; - } - - const muleOrphans = !!(info.muleInfo.hasOwnProperty("muleOrphans") && info.muleInfo.muleOrphans); - - /** - * @param {ItemUnit} item - */ - const questItem = (item) => [ - sdk.items.quest.KeytotheCairnStones, sdk.items.quest.ScrollofInifuss, sdk.items.quest.HoradricMalus, sdk.items.quest.WirtsLeg, - sdk.items.quest.HoradricStaff, sdk.items.quest.ShaftoftheHoradricStaff, sdk.items.quest.ViperAmulet, sdk.items.quest.Cube, - sdk.items.quest.KhalimsBrain, sdk.items.quest.KhalimsEye, sdk.items.quest.KhalimsHeart, sdk.items.quest.KhalimsFlail, - sdk.items.quest.DecoyGidbinn, sdk.items.quest.TheGidbinn, sdk.items.quest.KhalimsWill, sdk.items.quest.PotofLife, - sdk.items.quest.MephistosSoulstone, sdk.items.quest.HellForgeHammer, - sdk.items.quest.MalahsPotion, sdk.items.quest.ScrollofResistance, - ].includes(item.classid); + if (!info || !info.hasOwnProperty("muleInfo")) { + return false; + } + + const muleOrphans = !!(info.muleInfo.hasOwnProperty("muleOrphans") && info.muleInfo.muleOrphans); + + /** + * @param {ItemUnit} item + */ + const questItem = (item) => [ + sdk.items.quest.KeytotheCairnStones, sdk.items.quest.ScrollofInifuss, sdk.items.quest.HoradricMalus, sdk.items.quest.WirtsLeg, + sdk.items.quest.HoradricStaff, sdk.items.quest.ShaftoftheHoradricStaff, sdk.items.quest.ViperAmulet, sdk.items.quest.Cube, + sdk.items.quest.KhalimsBrain, sdk.items.quest.KhalimsEye, sdk.items.quest.KhalimsHeart, sdk.items.quest.KhalimsFlail, + sdk.items.quest.DecoyGidbinn, sdk.items.quest.TheGidbinn, sdk.items.quest.KhalimsWill, sdk.items.quest.PotofLife, + sdk.items.quest.MephistosSoulstone, sdk.items.quest.HellForgeHammer, + sdk.items.quest.MalahsPotion, sdk.items.quest.ScrollofResistance, + ].includes(item.classid); - /** - * @param {ItemUnit} item - */ - const isAKey = (item) => [sdk.items.quest.KeyofTerror, sdk.items.quest.KeyofHate, sdk.items.quest.KeyofDestruction].includes(item.classid); - - /** - * check if wanted by any of the systems - * @param {ItemUnit} item - * @returns {boolean} if item is wanted by various systems - */ - const isWanted = (item) => (AutoMule.cubingIngredient(item) || AutoMule.runewordIngredient(item) || AutoMule.utilityIngredient(item) || SoloWants.keepItem(item)); + /** + * @param {ItemUnit} item + */ + const isAKey = (item) => [sdk.items.quest.KeyofTerror, sdk.items.quest.KeyofHate, sdk.items.quest.KeyofDestruction].includes(item.classid); + + /** + * check if wanted by any of the systems + * @param {ItemUnit} item + * @returns {boolean} if item is wanted by various systems + */ + const isWanted = (item) => (AutoMule.cubingIngredient(item) || AutoMule.runewordIngredient(item) || AutoMule.utilityIngredient(item) || SoloWants.keepItem(item)); - // lets be more explicit about what we want to mule - let items = me.getItemsEx() - .filter(function (item) { - // we don't mule items that are equipped or are junk - if (!item.isInStorage || Town.ignoreType(item.itemType)) return false; - // don't mule excluded items - if (AutoMule.matchItem(item, Config.AutoMule.Exclude)) return false; - // don't mule quest items - if (questItem(item)) return false; - // don't mule wanted autoequip items - if (AutoEquip.wanted(item)) return false; - // don't mule items in locked spots - not exactly applicable for soloplay but including it - if (item.isInInventory && Storage.Inventory.IsLocked(item, Config.Inventory)) return false; - // don't mule items wanted by one of the various systems - checks that it's not on the force mule list - // might be worth it to ignore force for soloplay in this case, muleing an item we need would slow down progression - if (isWanted(item) && !AutoMule.matchItem(item, Config.AutoMule.Force.concat(Config.AutoMule.Trigger))) return false; - // don't mule keys if part of torchsystem, again shouldn't really be used with soloplay but still including it - if (isAKey(item) && TorchSystem.getFarmers() && TorchSystem.isFarmer()) return false; - // we've gotten this far, mule items that are on the force list - if (AutoMule.matchItem(item, Config.AutoMule.Force.concat(Config.AutoMule.Trigger))) return true; - // alright that handles the basics -- now normal pickit check - return (Pickit.checkItem(item).result > 0 && NTIP.CheckItem(item, NTIP_CheckListNoTier, true).result === 1) || (item.isInStash && muleOrphans); - }); + // lets be more explicit about what we want to mule + let items = me.getItemsEx() + .filter(function (item) { + // we don't mule items that are equipped or are junk + if (!item.isInStorage || Town.ignoreType(item.itemType)) return false; + // don't mule excluded items + if (AutoMule.matchItem(item, Config.AutoMule.Exclude)) return false; + // don't mule quest items + if (questItem(item)) return false; + // don't mule wanted autoequip items + if (AutoEquip.wanted(item)) return false; + // don't mule items in locked spots - not exactly applicable for soloplay but including it + if (item.isInInventory && Storage.Inventory.IsLocked(item, Config.Inventory)) return false; + // don't mule items wanted by one of the various systems - checks that it's not on the force mule list + // might be worth it to ignore force for soloplay in this case, muleing an item we need would slow down progression + if (isWanted(item) && !AutoMule.matchItem(item, Config.AutoMule.Force.concat(Config.AutoMule.Trigger))) return false; + // don't mule keys if part of torchsystem, again shouldn't really be used with soloplay but still including it + if (isAKey(item) && TorchSystem.getFarmers() && TorchSystem.isFarmer()) return false; + // we've gotten this far, mule items that are on the force list + if (AutoMule.matchItem(item, Config.AutoMule.Force.concat(Config.AutoMule.Trigger))) return true; + // alright that handles the basics -- now normal pickit check + return (Pickit.checkItem(item).result > 0 && NTIP.CheckItem(item, NTIP_CheckListNoTier, true).result === 1) || (item.isInStash && muleOrphans); + }); - return items; + return items; }; diff --git a/libs/SoloPlay/Functions/AutoStatOverrides.js b/libs/SoloPlay/Functions/AutoStatOverrides.js index ff4c4af1..bb33c41b 100644 --- a/libs/SoloPlay/Functions/AutoStatOverrides.js +++ b/libs/SoloPlay/Functions/AutoStatOverrides.js @@ -9,41 +9,41 @@ includeIfNotIncluded("core/Auto/AutoStat.js"); AutoStat.init = function (statBuildOrder, save = 0, block = 0, bulkStat = true) { - AutoStat.statBuildOrder = statBuildOrder; - AutoStat.save = save; - AutoStat.block = block; - AutoStat.bulkStat = bulkStat; + AutoStat.statBuildOrder = statBuildOrder; + AutoStat.save = save; + AutoStat.block = block; + AutoStat.bulkStat = bulkStat; - let usedStatPoints = false; + let usedStatPoints = false; - if (!AutoStat.statBuildOrder || !AutoStat.statBuildOrder.length) { - console.log("AutoStat: No build array specified"); + if (!AutoStat.statBuildOrder || !AutoStat.statBuildOrder.length) { + console.log("AutoStat: No build array specified"); - return false; - } + return false; + } - while (me.getStat(sdk.stats.StatPts) > AutoStat.save) { - AutoStat.addStatPoint() && (usedStatPoints = true); - delay(150 + me.ping); // spending multiple single stat at a time with short delay may cause r/d + while (me.getStat(sdk.stats.StatPts) > AutoStat.save) { + AutoStat.addStatPoint() && (usedStatPoints = true); + delay(150 + me.ping); // spending multiple single stat at a time with short delay may cause r/d - // break out of loop if we have stat points available but finished allocating as configured - if (me.getStat(sdk.stats.StatPts) === AutoStat.remaining) { - AutoStat.count += 1; - } + // break out of loop if we have stat points available but finished allocating as configured + if (me.getStat(sdk.stats.StatPts) === AutoStat.remaining) { + AutoStat.count += 1; + } - if (AutoStat.count > 2) { - break; - } - } + if (AutoStat.count > 2) { + break; + } + } - if (usedStatPoints) { - me.data.level = me.charlvl; - me.data.strength = me.rawStrength; - me.data.dexterity = me.rawDexterity; - CharData.updateData("me", me.data) && me.update(); - } + if (usedStatPoints) { + me.data.level = me.charlvl; + me.data.strength = me.rawStrength; + me.data.dexterity = me.rawDexterity; + CharData.updateData("me", me.data) && me.update(); + } - console.log("AutoStat: Finished allocating stat points"); + console.log("AutoStat: Finished allocating stat points"); - return true; + return true; }; diff --git a/libs/SoloPlay/Functions/CharmEquip.js b/libs/SoloPlay/Functions/CharmEquip.js index 3a564247..0b0f55fe 100644 --- a/libs/SoloPlay/Functions/CharmEquip.js +++ b/libs/SoloPlay/Functions/CharmEquip.js @@ -6,717 +6,717 @@ */ const CharmEquip = (function () { - /** - * Goals: - * need to be able to define what types of charms we want while leveling, and upgrade based on that - * need to be able to define what types of charms we want for final build, upgrade to that - * need to be able to handle different invoquantity values of final charms vs leveling charms - * need to be abel to handle final charms and leveling charms being the same type, in situation where we have enough of a final charm so compare it as a noraml leveling charm - * need to differentiate bewtween cubing charm or pickit wanted charm vs autoequip charm - * example: - * Imagine we are an auradin and we have 9 small charms in our inventory, Seven 5allres/20life and Two random life charms. Our build tells us we should keep 6 of the 5/20s - * so we should keep those. That leaves us with One 5/20 and Two random life charms, we should then compare the tier values and keep the highest of the two then sell or drop the third. - * As it is now, what happens is we don't compare the 7th 5/20 and we add that to the sell list while keeping the 2 lower charms. If we directly add it to the backup then the invoquantity - * gets read from the finalBuild file so instead of only keeping two it says we should keep 6. - */ - - /** - * Iterate over charm checklist, pickit result 0 and 4 get sold - * Otherwise if its not in the stash already and not a final charm try and stash it. I don't remember why I checked if it wasn't a final charm - * @param {ItemUnit[]} checkList - * @param {boolean} verbose - */ - const spliceCharmCheckList = function (checkList = [], verbose = false) { - for (let i = 0; i < checkList.length; i++) { - const currCharm = checkList[i]; - if (!currCharm || [Pickit.Result.UNWANTED, Pickit.Result.TRASH].includes(Pickit.checkItem(currCharm).result)) continue; - if (!currCharm.isInStash && !me.data.charmGids.includes(currCharm.gid)) { - if (!Storage.Stash.MoveTo(currCharm)) { - verbose && Item.logger("Dropped", currCharm); - currCharm.drop(); - } else { - if (verbose) { - Cubing.checkItem(currCharm) ? Item.logItem("Stashed Cubing Ingredient", currCharm) : Item.logItem("Stashed", currCharm); - } - } - } - - checkList.splice(i, 1); - i -= 1; - } - }; - - const spliceCharmKeepList = function (keep = [], sell = [], verbose = false) { - if (!keep.length) return; - const id = keep[0].classid; - const cInfo = (() => CharData.charms.get(id).count() || { max: 0 })(); - - // sort through kept charms - if (keep.length > cInfo.max) { - keep.sort((a, b) => NTIP.GetCharmTier(b) - NTIP.GetCharmTier(a)); - - // everything after the cap (need a better method for this in the instances where the max cap is less then leveling wanted cap) - for (let i = cInfo.max; i < keep.length; i++) { - if (!!keep[i].classid && !CharmEquip.check(keep[i])) { - sell.push(keep[i]); - verbose && console.log("ÿc8Kolbot-SoloPlayÿc0: CharmEquip Add " + keep[i].fname + " to checkList"); - keep.splice(i, 1); - i -= 1; - } - } - } - }; - - /** - * @constructor - * @param {number} classid - */ - function CharmTypeEquip (classid) { - this.classid = classid; - this.name = (() => { - switch (classid) { - case sdk.items.SmallCharm: - return "Small"; - case sdk.items.LargeCharm: - return "Large"; - case sdk.items.GrandCharm: - return "Grand"; - default: - return "Unknown"; - } - })(); - /** @type {boolean} */ - this.debugging = Developer.debugging[this.name.toLowerCase() + "Charm"]; - } - - /** - * Handle charm autoequip - * @param {ItemUnit[]} charmList - * @returns {{ keep: ItemUnit[], sell: ItemUnit[] }} - */ - CharmTypeEquip.prototype.autoEquip = function (charmList = []) { - let items = (charmList.length ? charmList : me.getItemsEx()) - .filter((charm) => charm.isInStorage && charm.classid === this.classid && charm.magic); - - if (!items.length) { - this.debugging && console.debug("No charms found"); - return { keep: [], sell: [] }; - } - - let charms = CharmEquip.sort(items, this.debugging); - spliceCharmKeepList(charms.keep, charms.checkList, this.debugging); - - this.debugging && console.log(this.name + " charm checklist length: " + charms.checkList.length); - spliceCharmCheckList(charms.checkList, this.debugging); - - return { keep: charms.keep, sell: charms.checkList }; - }; - - const _smallCharm = new CharmTypeEquip(sdk.items.SmallCharm); - const _largeCharm = new CharmTypeEquip(sdk.items.LargeCharm); - const _grandCharm = new CharmTypeEquip(sdk.items.GrandCharm); - - return { - /** @type {Set} */ - keptGids: new Set(), - - /** - * @param {ItemUnit} item - * @returns {boolean} - */ - hasCharmTier: (item) => me.expansion && Config.AutoEquip && NTIP.GetCharmTier(item) > 0, - - /** - * @param {ItemUnit} item - * @returns {boolean} - */ - isFinalCharm: (item) => me.data.charmGids.includes(item.gid), - - init: function () { - // No charms in classic - if (me.classic) return; - let myCharms = me.getItemsEx().filter(item => item.isInStorage && item.isCharm && item.magic); - let changed = false; - - const finalCharmKeys = Object.keys(me.data.charms); - const check = function (list = [], charms = []) { - for (let i = 0; i < list.length; i++) { - if (!charms.some(c => c.gid === list[i])) { - console.log("A charm was removed from our final list - updated it"); - me.data.charmGids.remove(list[i]); - list.splice(i, 1); - i--; - changed = true; - } - } - }; - - for (let i = 0; i < finalCharmKeys.length; i++) { - let cKey = finalCharmKeys[i]; - switch (me.data.charms[cKey].classid) { - case sdk.items.SmallCharm: - check(me.data.charms[cKey].have, myCharms); - - break; - case sdk.items.LargeCharm: - check(me.data.charms[cKey].have, myCharms); - - break; - case sdk.items.GrandCharm: - check(me.data.charms[cKey].have, myCharms); - - break; - } - } - - changed && me.update(); - }, - - /** - * @param {ItemUnit} charm - * @returns {string | false} - */ - getCharmType: function (charm) { - if (!charm || !charm.isCharm) return false; - if (charm.unique) return "unique"; - if (!NTIP.hasStats(charm) && NTIP.GetCharmTier(charm) > 0) return "misc"; - - let charmType = ""; - const skillerStats = me.getSkillTabs(); - - if (charm.getStat(sdk.stats.AddSkillTab, skillerStats[0])) { - charmType = "skillerTypeA"; - } else if (charm.getStat(sdk.stats.AddSkillTab, skillerStats[1])) { - charmType = "skillerTypeB"; - } else if (charm.getStat(sdk.stats.AddSkillTab, skillerStats[2])) { - charmType = "skillerTypeC"; - } - - switch (charm.prefix) { - case "Shimmering": - case "Azure": - case "Lapis": - case "Cobalt": - case "Sapphire": - case "Crimson": - case "Russet": - case "Garnet": - case "Ruby": - case "Tangerine": - case "Ocher": - case "Coral": - case "Amber": - case "Beryl": - case "Viridian": - case "Jade": - case "Emerald": - charmType = "resist"; - break; - } - - if (!charmType || charmType === "") { - switch (charm.suffix) { - case "of Fortune": - case "of Good Luck": - charmType = "magicfind"; - break; - case "of Life": - case "of Substinence": // Odd issue, seems to be misspelled wherever item.suffix pulls info from - case "of Vita": - charmType = "life"; - break; - } - } - - if (!charmType || charmType === "") { - switch (charm.prefix) { - case "Red": - case "Sanguinary": - case "Bloody": - case "Jagged": - case "Forked": - case "Serrated": - case "Bronze": - case "Iron": - case "Steel": - case "Fine": - case "Sharp": - charmType = "damage"; - break; - case "Snowy": - case "Shivering": - case "Boreal": - case "Hibernal": - case "Ember": - case "Smoldering": - case "Smoking": - case "Flaming": - case "Static": - case "Glowing": - case "Arcing": - case "Shocking": - case "Septic": - case "Foul": - case "Toxic": - case "Pestilant": - charmType = "elemental"; - break; - } - } - - if (!charmType || charmType === "") { - switch (charm.suffix) { - case "of Craftmanship": - case "of Quality": - case "of Maiming": - charmType = "damage"; - break; - case "of Strength": - case "of Dexterity": - charmType = "stats"; - break; - case "of Blight": - case "of Venom": - case "of Pestilence": - case "of Anthrax": - case "of Frost": - case "of Icicle": - case "of Glacier": - case "of Winter": - case "of Flame": - case "of Burning": - case "of Incineration": - case "of Shock": - case "of Lightning": - case "of Thunder": - case "of Storms": - charmType = "elemental"; - break; - } - } - - if (!charmType || charmType === "") { - switch (charm.prefix) { - case "Stout": - case "Burly": - case "Stalwart": - charmType = "misc"; - break; - case "Rugged": - charmType = "misc"; - break; - case "Lizard's": - case "Snake's": - case "Serpent's": - charmType = "mana"; - break; - } - } - - if (!charmType || charmType === "") { - switch (charm.suffix) { - case "of Balance": - case "of Greed": - case "of Inertia": - charmType = "misc"; - break; - } - } - - return charmType; - }, - - /** - * Handle small charm autoequip - * @param {ItemUnit[]} charmList - * @returns {{ keep: ItemUnit[], sell: ItemUnit[] }} - */ - smallCharm: function (charmList = []) { - return _smallCharm.autoEquip(charmList); - }, - /** - * Handle large charm autoequip - * @param {ItemUnit[]} charmList - * @returns {{ keep: ItemUnit[], sell: ItemUnit[] }} - */ - largeCharm: function (charmList = []) { - return _largeCharm.autoEquip(charmList); - }, - /** - * Handle grand charm autoequip - * @param {ItemUnit[]} charmList - * @returns {{ keep: ItemUnit[], sell: ItemUnit[] }} - */ - grandCharm: function (charmList = []) { - return _grandCharm.autoEquip(charmList); - }, - - /** - * @param {ItemUnit[]} items - * @param {boolean} verbose - * @returns {{ skillerTypeA: ItemUnit[], skillerTypeB: ItemUnit[], skillerTypeC: ItemUnit[], resist: ItemUnit[], life: ItemUnit[], magicfind: ItemUnit[], damage: ItemUnit[], elemental: ItemUnit[], backup: ItemUnit[], keep: ItemUnit[], checkList: ItemUnit[] }}} - */ - sort: function (items = [], verbose = false) { - let charms = { - skillerTypeA: [], - skillerTypeB: [], - skillerTypeC: [], - resist: [], - life: [], - magicfind: [], - damage: [], - elemental: [], - backup: [], - keep: [], - checkList: [] - }; - - if (!items.length) { - verbose && console.log("No charms found"); - return charms; - } - - const addToCheckList = (item) => charms.checkList.indexOf(item) === -1 && charms.checkList.push(item); - const addToBackUp = (item) => charms.backup.indexOf(item) === -1 && charms.backup.push(item); - - const sortCharms = (arr = [], verbose = false, backUpCheck = true) => { - let invoquantity = NTIP.getInvoQuantity(arr[0]); - (invoquantity === undefined || invoquantity === -1) && (invoquantity = 2); - let charmType = CharmEquip.getCharmType(arr[0]); - verbose && console.log("Amount of " + charmType + " Charms: " + arr.length + " invoquantity: " + invoquantity); - arr.length > 1 && arr.sort((a, b) => NTIP.GetCharmTier(b) - NTIP.GetCharmTier(a)); - - if (arr.length > invoquantity) { - verbose && arr.forEach((el, index) => console.log(charmType + "[" + index + "] = " + NTIP.GetCharmTier(el))); - - for (let i = invoquantity; i < arr.length; i++) { - backUpCheck ? addToBackUp(arr[i]) : addToCheckList(arr[i]); - - arr.splice(i, 1); - i -= 1; - } - } - }; - - verbose && console.log("Amount of items: " + items.length); - items.length > 1 && items.sort((a, b) => NTIP.GetCharmTier(b) - NTIP.GetCharmTier(a)); - - const finalCharmInfo = Check.finalBuild().finalCharms; - const finalCharmKeys = Object.keys(finalCharmInfo); - - let found = false; - - while (items.length > 0) { - let gid = items[0].gid; - let item = items.shift(); - - if (!item.identified) { - let idTool = me.getIdTool(); - - if (idTool) { - item.isInStash && Town.openStash(); - Town.identifyItem(item, idTool); - - } else if (item.isInStash && (getUIFlag(sdk.uiflags.Stash) || Town.openStash())) { - Storage.Inventory.MoveTo(item); - Town.identify(); - } - - if (!Game.getItem(-1, -1, gid)) { - verbose && console.log("Sold charm during Town.identify()"); - items.shift(); - - continue; - } - } - - if (me.data.charmGids.includes(item.gid)) { - charms.keep.push(item); - - continue; - } - - let next = false; - - for (let i = 0; i < finalCharmKeys.length; i++) { - let cKey = finalCharmKeys[i]; - try { - if (!!me.data.charms[cKey] && me.data.charms[cKey].have.indexOf(item.gid) === -1 - && me.data.charms[cKey].have.length < me.data.charms[cKey].max) { - if (finalCharmInfo[cKey].stats(item)) { - console.debug(item.fname); - me.data.charmGids.push(item.gid); - me.data.charms[cKey].have.push(item.gid); - charms.keep.push(item); - found = true; - next = true; - - break; - } - } - } catch (e) { - console.error(e); - } - } - - if (next) { - continue; - } - - if (NTIP.GetCharmTier(item) <= 0) { - verbose && console.log("No tier. Adding to checkList: " + item.fname); - addToCheckList(item); - } else if (!NTIP.hasStats(item) && NTIP.GetCharmTier(item) > 0) { - verbose && console.log("Multiple Misc charm: " + item.fname); - charms.backup.push(item); - } else { - let charmType = CharmEquip.getCharmType(item); - switch (charmType) { - case "skillerTypeA": - case "skillerTypeB": - case "skillerTypeC": - case "resist": - case "life": - case "magicfind": - case "damage": - case "elemental": - charms[charmType].push(item); - verbose && console.log(charmType + ": " + item.fname); - - break; - default: - addToCheckList(item); - verbose && console.log("Failed all checks. Adding to checkList: " + item.fname); - - break; - } - } - } - - if (found) { - me.update(); - } - - if (!charms.skillerTypeA.length && !charms.skillerTypeB.length && !charms.skillerTypeC.length - && !charms.damage.length && !charms.resist.length && !charms.elemental.length && !charms.life.length && !charms.backup.length) { - verbose && console.log("No Charms"); - return charms; - } - - charms.skillerTypeA.length > 0 && sortCharms(charms.skillerTypeA, verbose); - charms.skillerTypeB.length > 0 && sortCharms(charms.skillerTypeB, verbose); - charms.skillerTypeC.length > 0 && sortCharms(charms.skillerTypeC, verbose); - charms.resist.length > 0 && sortCharms(charms.resist, verbose); - charms.life.length > 0 && sortCharms(charms.life, verbose); - charms.magicfind.length > 0 && sortCharms(charms.magicfind, verbose); - charms.damage.length > 0 && sortCharms(charms.damage, verbose); - charms.elemental.length > 0 && sortCharms(charms.elemental, verbose); - - // If stats are unspecifed, this will filter charms and keep highest based on invoquantity. If no invoquantity defined it will keep two of that type - charms.backup.length > 0 && sortCharms(charms.backup, verbose, false); - charms.keep = charms.keep.concat(charms.skillerTypeA, charms.skillerTypeB, charms.skillerTypeC, charms.resist, charms.life, charms.magicfind, charms.damage, charms.elemental, charms.backup); - verbose && charms.checkList.forEach((el, index) => console.log("checkList[" + index + "] = " + NTIP.GetCharmTier(el) + " " + el.fname)); - - return charms; - }, - - /** - * @param {ItemUnit} item - * @returns {boolean} - */ - check: function (item) { - if (!item || NTIP.GetCharmTier(item) <= 0 || !item.isCharm) return false; - // Annhilus, Hellfire Torch, Gheeds - Handled by a different function so return true to keep - if (item.isCharm && item.unique) return true; - // is one of our final charms - if (me.data.charmGids.includes(item.gid)) return true; - // is in our checkList - if (CharmEquip.keptGids.has(item.gid)) return true; - - let lowestCharm; - let items = me.getItemsEx() - .filter(charm => charm.classid === item.classid && charm.isInStorage && charm.magic && NTIP.GetCharmTier(charm) > 0); - if (!items.length) return true; - - let quantityCap = NTIP.getInvoQuantity(item); - let have = 0; - let charms = CharmEquip.sort(items); - let charmType = CharmEquip.getCharmType(item); - let cInfo, newList = []; - - switch (item.classid) { - case sdk.items.SmallCharm: - cInfo = CharData.charms.get("small").count(); - - if (cInfo.curr && (cInfo.curr / cInfo.max) * 100 >= 75) { - // chop off past our cap - newList = charms.keep - .sort((a, b) => NTIP.GetCharmTier(b) - NTIP.GetCharmTier(a)) - .slice(0, cInfo.max); - // check if it made the cut - if (!newList.find(i => i.gid === item.gid)) return false; - lowestCharm = newList.last(); - return !!(NTIP.GetCharmTier(item) >= NTIP.GetCharmTier(lowestCharm) || item.gid === lowestCharm.gid); - } - - break; - case sdk.items.LargeCharm: - cInfo = CharData.charms.get("large").count(); - - if (cInfo.curr && (cInfo.curr / cInfo.max) * 100 >= 75) { - // chop off past our cap - newList = charms.keep - .sort((a, b) => NTIP.GetCharmTier(b) - NTIP.GetCharmTier(a)) - .slice(0, cInfo.max); - // check if it made the cut - if (!newList.find(i => i.gid === item.gid)) return false; - lowestCharm = newList.last(); - return !!(NTIP.GetCharmTier(item) >= NTIP.GetCharmTier(lowestCharm) || item.gid === lowestCharm.gid); - } - - break; - case sdk.items.GrandCharm: - cInfo = CharData.charms.get("grand").count(); - - if (cInfo.curr && (cInfo.curr / cInfo.max) * 100 >= 50) { - // chop off past our cap - newList = charms.keep - .sort((a, b) => NTIP.GetCharmTier(b) - NTIP.GetCharmTier(a)) - .slice(0, cInfo.max); - // check if it made the cut - if (!newList.find(i => i.gid === item.gid)) return false; - lowestCharm = newList.last(); - return !!(NTIP.GetCharmTier(item) >= NTIP.GetCharmTier(lowestCharm) || item.gid === lowestCharm.gid); - } - - break; - } - - switch (charmType) { - case "skillerTypeA": - case "skillerTypeB": - case "skillerTypeC": - case "resist": - case "life": - case "magicfind": - case "damage": - case "elemental": - have = charms[charmType].length; - lowestCharm = charms[charmType].last(); - if ((charms[charmType].findIndex(c => c.gid === lowestCharm.gid) + 1) > quantityCap) return false; - - break; - default: - have = charms.backup.length; - lowestCharm = charms.backup.last(); - if ((charms.backup.findIndex(c => c.gid === lowestCharm.gid) + 1) > quantityCap) return false; - // console.debug("Lowest Charm index " + (charms.backup.findIndex(c => c.gid === lowestCharm.gid)) + " out of " + charms.backup.length); - - break; - } - - if (!lowestCharm) { - // console.debug("Didn't find any other charms of this type " + charmType); - return true; - } - - if (item.gid === lowestCharm.gid) { - // console.debug("Same charm"); - return true; - } - - let [tierParamItem, tierLowestItem] = [NTIP.GetCharmTier(item), NTIP.GetCharmTier(lowestCharm)]; - - if (tierParamItem === tierLowestItem) { - // console.debug("Same tier value"); - // super hacky - arbritrary comparsion of xpos if the tier value is the same - return (have < quantityCap) || (item.isInInventory && lowestCharm.isInInventory && item.x > lowestCharm.y) || (item.isInInventory && !lowestCharm.isInInventory); - } - - return (tierParamItem >= tierLowestItem); - }, - - run: function () { - // No charms in classic - if (me.classic) return; - - console.log("ÿc8Kolbot-SoloPlayÿc0: Entering charm auto equip"); - let tick = getTickCount(); - let charms = me.getItemsEx() - .filter(item => item.isInStorage && item.isCharm && item.magic); - // don't do anything if we don't have any charms - if ((!charms.length) - // don't do anything if we have the same charms as last time - || ((CharmEquip.keptGids.size && charms.every(c => CharmEquip.keptGids.has(c.gid))))) { - console.log("ÿc8Kolbot-SoloPlayÿc0: Exiting charm auto equip. Time elapsed: " + Time.format(getTickCount() - tick)); - return; - } - CharmEquip.keptGids.clear(); - let totalKeep = [], totalSell = []; - let GCs = CharmEquip.grandCharm(charms); - let LCs = CharmEquip.largeCharm(charms); - let SCs = CharmEquip.smallCharm(charms); - let specialCharms = charms.filter((charm) => charm.unique); - let verbose = !!(Developer.debugging.smallCharm || Developer.debugging.largeCharm || Developer.debugging.grandCharm); - - if (verbose) { - console.log("Grand Charms Keep: " + GCs.keep.length + ", Sell: " + GCs.sell.length); - console.log("Large Charms Keep: " + LCs.keep.length + ", Sell: " + LCs.sell.length); - console.log("Small Charms Keep: " + SCs.keep.length + ", Sell: " + SCs.sell.length); - } - - totalKeep = totalKeep.concat(SCs.keep, LCs.keep, GCs.keep, specialCharms); - for (let i = 0; i < totalKeep.length; i++) { - if (!CharmEquip.check(totalKeep[i])) { - totalSell.push(totalKeep[i]); - totalKeep.splice(i, 1); - i--; - } - } - totalSell = totalSell - .concat(SCs.sell, LCs.sell, GCs.sell) - .filter((charm) => NTIP.CheckItem(charm, NTIP_CheckListNoTier) === Pickit.Result.UNWANTED); - totalKeep.length > 0 && console.log("ÿc8Kolbot-SoloPlayÿc0: Total Charms Kept: " + totalKeep.length); - - if (totalSell.length > 0) { - console.log("ÿc8Kolbot-SoloPlayÿc0: Total Charms Sell: " + totalSell.length); - - for (let i = 0; i < totalSell.length; i++) { - totalSell[i].isInStash && !getUIFlag(sdk.uiflags.Stash) && Town.openStash(); - if (totalSell[i].isInStash && (!totalSell[i].sellable || !Storage.Inventory.MoveTo(totalSell[i]))) { - totalSell[i].drop(); - totalSell.splice(i, 1); - i -= 1; - } - } - - Town.initNPC("Shop", "clearInventory"); - - if (getUIFlag(sdk.uiflags.Shop) || (Config.PacketShopping && getInteractedNPC() && getInteractedNPC().itemcount > 0)) { - for (let i = 0; i < totalSell.length; i++) { - console.log("ÿc8Kolbot-SoloPlayÿc0: Sell old charm " + totalSell[i].name); - verbose && Item.logger("Sold", totalSell[i]); - verbose && Item.logItem("CharmEquip Sold", totalSell[i]); - totalSell[i].sell(); - } - } - } - - if (totalKeep.length > 0) { - for (let i = 0; i < totalKeep.length; i++) { - CharmEquip.keptGids.add(totalKeep[i].gid); - if (totalKeep[i].isInStash && !Cubing.checkItem(totalKeep[i])) { - !getUIFlag(sdk.uiflags.Stash) && Town.openStash() && delay(300 + me.ping); - if (Storage.Inventory.CanFit(totalKeep[i]) && Storage.Inventory.MoveTo(totalKeep[i])) { - verbose && Item.logItem("CharmEquip Equipped", totalKeep[i]); - } - } - } - } - - me.cancelUIFlags(); - - console.log("ÿc8Kolbot-SoloPlayÿc0: Exiting charm auto equip. Time elapsed: " + Time.format(getTickCount() - tick)); - }, - }; + /** + * Goals: + * need to be able to define what types of charms we want while leveling, and upgrade based on that + * need to be able to define what types of charms we want for final build, upgrade to that + * need to be able to handle different invoquantity values of final charms vs leveling charms + * need to be abel to handle final charms and leveling charms being the same type, in situation where we have enough of a final charm so compare it as a noraml leveling charm + * need to differentiate bewtween cubing charm or pickit wanted charm vs autoequip charm + * example: + * Imagine we are an auradin and we have 9 small charms in our inventory, Seven 5allres/20life and Two random life charms. Our build tells us we should keep 6 of the 5/20s + * so we should keep those. That leaves us with One 5/20 and Two random life charms, we should then compare the tier values and keep the highest of the two then sell or drop the third. + * As it is now, what happens is we don't compare the 7th 5/20 and we add that to the sell list while keeping the 2 lower charms. If we directly add it to the backup then the invoquantity + * gets read from the finalBuild file so instead of only keeping two it says we should keep 6. + */ + + /** + * Iterate over charm checklist, pickit result 0 and 4 get sold + * Otherwise if its not in the stash already and not a final charm try and stash it. I don't remember why I checked if it wasn't a final charm + * @param {ItemUnit[]} checkList + * @param {boolean} verbose + */ + const spliceCharmCheckList = function (checkList = [], verbose = false) { + for (let i = 0; i < checkList.length; i++) { + const currCharm = checkList[i]; + if (!currCharm || [Pickit.Result.UNWANTED, Pickit.Result.TRASH].includes(Pickit.checkItem(currCharm).result)) continue; + if (!currCharm.isInStash && !me.data.charmGids.includes(currCharm.gid)) { + if (!Storage.Stash.MoveTo(currCharm)) { + verbose && Item.logger("Dropped", currCharm); + currCharm.drop(); + } else { + if (verbose) { + Cubing.checkItem(currCharm) ? Item.logItem("Stashed Cubing Ingredient", currCharm) : Item.logItem("Stashed", currCharm); + } + } + } + + checkList.splice(i, 1); + i -= 1; + } + }; + + const spliceCharmKeepList = function (keep = [], sell = [], verbose = false) { + if (!keep.length) return; + const id = keep[0].classid; + const cInfo = (() => CharData.charms.get(id).count() || { max: 0 })(); + + // sort through kept charms + if (keep.length > cInfo.max) { + keep.sort((a, b) => NTIP.GetCharmTier(b) - NTIP.GetCharmTier(a)); + + // everything after the cap (need a better method for this in the instances where the max cap is less then leveling wanted cap) + for (let i = cInfo.max; i < keep.length; i++) { + if (!!keep[i].classid && !CharmEquip.check(keep[i])) { + sell.push(keep[i]); + verbose && console.log("ÿc8Kolbot-SoloPlayÿc0: CharmEquip Add " + keep[i].fname + " to checkList"); + keep.splice(i, 1); + i -= 1; + } + } + } + }; + + /** + * @constructor + * @param {number} classid + */ + function CharmTypeEquip (classid) { + this.classid = classid; + this.name = (() => { + switch (classid) { + case sdk.items.SmallCharm: + return "Small"; + case sdk.items.LargeCharm: + return "Large"; + case sdk.items.GrandCharm: + return "Grand"; + default: + return "Unknown"; + } + })(); + /** @type {boolean} */ + this.debugging = Developer.debugging[this.name.toLowerCase() + "Charm"]; + } + + /** + * Handle charm autoequip + * @param {ItemUnit[]} charmList + * @returns {{ keep: ItemUnit[], sell: ItemUnit[] }} + */ + CharmTypeEquip.prototype.autoEquip = function (charmList = []) { + let items = (charmList.length ? charmList : me.getItemsEx()) + .filter((charm) => charm.isInStorage && charm.classid === this.classid && charm.magic); + + if (!items.length) { + this.debugging && console.debug("No charms found"); + return { keep: [], sell: [] }; + } + + let charms = CharmEquip.sort(items, this.debugging); + spliceCharmKeepList(charms.keep, charms.checkList, this.debugging); + + this.debugging && console.log(this.name + " charm checklist length: " + charms.checkList.length); + spliceCharmCheckList(charms.checkList, this.debugging); + + return { keep: charms.keep, sell: charms.checkList }; + }; + + const _smallCharm = new CharmTypeEquip(sdk.items.SmallCharm); + const _largeCharm = new CharmTypeEquip(sdk.items.LargeCharm); + const _grandCharm = new CharmTypeEquip(sdk.items.GrandCharm); + + return { + /** @type {Set} */ + keptGids: new Set(), + + /** + * @param {ItemUnit} item + * @returns {boolean} + */ + hasCharmTier: (item) => me.expansion && Config.AutoEquip && NTIP.GetCharmTier(item) > 0, + + /** + * @param {ItemUnit} item + * @returns {boolean} + */ + isFinalCharm: (item) => me.data.charmGids.includes(item.gid), + + init: function () { + // No charms in classic + if (me.classic) return; + let myCharms = me.getItemsEx().filter(item => item.isInStorage && item.isCharm && item.magic); + let changed = false; + + const finalCharmKeys = Object.keys(me.data.charms); + const check = function (list = [], charms = []) { + for (let i = 0; i < list.length; i++) { + if (!charms.some(c => c.gid === list[i])) { + console.log("A charm was removed from our final list - updated it"); + me.data.charmGids.remove(list[i]); + list.splice(i, 1); + i--; + changed = true; + } + } + }; + + for (let i = 0; i < finalCharmKeys.length; i++) { + let cKey = finalCharmKeys[i]; + switch (me.data.charms[cKey].classid) { + case sdk.items.SmallCharm: + check(me.data.charms[cKey].have, myCharms); + + break; + case sdk.items.LargeCharm: + check(me.data.charms[cKey].have, myCharms); + + break; + case sdk.items.GrandCharm: + check(me.data.charms[cKey].have, myCharms); + + break; + } + } + + changed && me.update(); + }, + + /** + * @param {ItemUnit} charm + * @returns {string | false} + */ + getCharmType: function (charm) { + if (!charm || !charm.isCharm) return false; + if (charm.unique) return "unique"; + if (!NTIP.hasStats(charm) && NTIP.GetCharmTier(charm) > 0) return "misc"; + + let charmType = ""; + const skillerStats = me.getSkillTabs(); + + if (charm.getStat(sdk.stats.AddSkillTab, skillerStats[0])) { + charmType = "skillerTypeA"; + } else if (charm.getStat(sdk.stats.AddSkillTab, skillerStats[1])) { + charmType = "skillerTypeB"; + } else if (charm.getStat(sdk.stats.AddSkillTab, skillerStats[2])) { + charmType = "skillerTypeC"; + } + + switch (charm.prefix) { + case "Shimmering": + case "Azure": + case "Lapis": + case "Cobalt": + case "Sapphire": + case "Crimson": + case "Russet": + case "Garnet": + case "Ruby": + case "Tangerine": + case "Ocher": + case "Coral": + case "Amber": + case "Beryl": + case "Viridian": + case "Jade": + case "Emerald": + charmType = "resist"; + break; + } + + if (!charmType || charmType === "") { + switch (charm.suffix) { + case "of Fortune": + case "of Good Luck": + charmType = "magicfind"; + break; + case "of Life": + case "of Substinence": // Odd issue, seems to be misspelled wherever item.suffix pulls info from + case "of Vita": + charmType = "life"; + break; + } + } + + if (!charmType || charmType === "") { + switch (charm.prefix) { + case "Red": + case "Sanguinary": + case "Bloody": + case "Jagged": + case "Forked": + case "Serrated": + case "Bronze": + case "Iron": + case "Steel": + case "Fine": + case "Sharp": + charmType = "damage"; + break; + case "Snowy": + case "Shivering": + case "Boreal": + case "Hibernal": + case "Ember": + case "Smoldering": + case "Smoking": + case "Flaming": + case "Static": + case "Glowing": + case "Arcing": + case "Shocking": + case "Septic": + case "Foul": + case "Toxic": + case "Pestilant": + charmType = "elemental"; + break; + } + } + + if (!charmType || charmType === "") { + switch (charm.suffix) { + case "of Craftmanship": + case "of Quality": + case "of Maiming": + charmType = "damage"; + break; + case "of Strength": + case "of Dexterity": + charmType = "stats"; + break; + case "of Blight": + case "of Venom": + case "of Pestilence": + case "of Anthrax": + case "of Frost": + case "of Icicle": + case "of Glacier": + case "of Winter": + case "of Flame": + case "of Burning": + case "of Incineration": + case "of Shock": + case "of Lightning": + case "of Thunder": + case "of Storms": + charmType = "elemental"; + break; + } + } + + if (!charmType || charmType === "") { + switch (charm.prefix) { + case "Stout": + case "Burly": + case "Stalwart": + charmType = "misc"; + break; + case "Rugged": + charmType = "misc"; + break; + case "Lizard's": + case "Snake's": + case "Serpent's": + charmType = "mana"; + break; + } + } + + if (!charmType || charmType === "") { + switch (charm.suffix) { + case "of Balance": + case "of Greed": + case "of Inertia": + charmType = "misc"; + break; + } + } + + return charmType; + }, + + /** + * Handle small charm autoequip + * @param {ItemUnit[]} charmList + * @returns {{ keep: ItemUnit[], sell: ItemUnit[] }} + */ + smallCharm: function (charmList = []) { + return _smallCharm.autoEquip(charmList); + }, + /** + * Handle large charm autoequip + * @param {ItemUnit[]} charmList + * @returns {{ keep: ItemUnit[], sell: ItemUnit[] }} + */ + largeCharm: function (charmList = []) { + return _largeCharm.autoEquip(charmList); + }, + /** + * Handle grand charm autoequip + * @param {ItemUnit[]} charmList + * @returns {{ keep: ItemUnit[], sell: ItemUnit[] }} + */ + grandCharm: function (charmList = []) { + return _grandCharm.autoEquip(charmList); + }, + + /** + * @param {ItemUnit[]} items + * @param {boolean} verbose + * @returns {{ skillerTypeA: ItemUnit[], skillerTypeB: ItemUnit[], skillerTypeC: ItemUnit[], resist: ItemUnit[], life: ItemUnit[], magicfind: ItemUnit[], damage: ItemUnit[], elemental: ItemUnit[], backup: ItemUnit[], keep: ItemUnit[], checkList: ItemUnit[] }}} + */ + sort: function (items = [], verbose = false) { + let charms = { + skillerTypeA: [], + skillerTypeB: [], + skillerTypeC: [], + resist: [], + life: [], + magicfind: [], + damage: [], + elemental: [], + backup: [], + keep: [], + checkList: [] + }; + + if (!items.length) { + verbose && console.log("No charms found"); + return charms; + } + + const addToCheckList = (item) => charms.checkList.indexOf(item) === -1 && charms.checkList.push(item); + const addToBackUp = (item) => charms.backup.indexOf(item) === -1 && charms.backup.push(item); + + const sortCharms = (arr = [], verbose = false, backUpCheck = true) => { + let invoquantity = NTIP.getInvoQuantity(arr[0]); + (invoquantity === undefined || invoquantity === -1) && (invoquantity = 2); + let charmType = CharmEquip.getCharmType(arr[0]); + verbose && console.log("Amount of " + charmType + " Charms: " + arr.length + " invoquantity: " + invoquantity); + arr.length > 1 && arr.sort((a, b) => NTIP.GetCharmTier(b) - NTIP.GetCharmTier(a)); + + if (arr.length > invoquantity) { + verbose && arr.forEach((el, index) => console.log(charmType + "[" + index + "] = " + NTIP.GetCharmTier(el))); + + for (let i = invoquantity; i < arr.length; i++) { + backUpCheck ? addToBackUp(arr[i]) : addToCheckList(arr[i]); + + arr.splice(i, 1); + i -= 1; + } + } + }; + + verbose && console.log("Amount of items: " + items.length); + items.length > 1 && items.sort((a, b) => NTIP.GetCharmTier(b) - NTIP.GetCharmTier(a)); + + const finalCharmInfo = Check.finalBuild().finalCharms; + const finalCharmKeys = Object.keys(finalCharmInfo); + + let found = false; + + while (items.length > 0) { + let gid = items[0].gid; + let item = items.shift(); + + if (!item.identified) { + let idTool = me.getIdTool(); + + if (idTool) { + item.isInStash && Town.openStash(); + Town.identifyItem(item, idTool); + + } else if (item.isInStash && (getUIFlag(sdk.uiflags.Stash) || Town.openStash())) { + Storage.Inventory.MoveTo(item); + Town.identify(); + } + + if (!Game.getItem(-1, -1, gid)) { + verbose && console.log("Sold charm during Town.identify()"); + items.shift(); + + continue; + } + } + + if (me.data.charmGids.includes(item.gid)) { + charms.keep.push(item); + + continue; + } + + let next = false; + + for (let i = 0; i < finalCharmKeys.length; i++) { + let cKey = finalCharmKeys[i]; + try { + if (!!me.data.charms[cKey] && me.data.charms[cKey].have.indexOf(item.gid) === -1 + && me.data.charms[cKey].have.length < me.data.charms[cKey].max) { + if (finalCharmInfo[cKey].stats(item)) { + console.debug(item.fname); + me.data.charmGids.push(item.gid); + me.data.charms[cKey].have.push(item.gid); + charms.keep.push(item); + found = true; + next = true; + + break; + } + } + } catch (e) { + console.error(e); + } + } + + if (next) { + continue; + } + + if (NTIP.GetCharmTier(item) <= 0) { + verbose && console.log("No tier. Adding to checkList: " + item.fname); + addToCheckList(item); + } else if (!NTIP.hasStats(item) && NTIP.GetCharmTier(item) > 0) { + verbose && console.log("Multiple Misc charm: " + item.fname); + charms.backup.push(item); + } else { + let charmType = CharmEquip.getCharmType(item); + switch (charmType) { + case "skillerTypeA": + case "skillerTypeB": + case "skillerTypeC": + case "resist": + case "life": + case "magicfind": + case "damage": + case "elemental": + charms[charmType].push(item); + verbose && console.log(charmType + ": " + item.fname); + + break; + default: + addToCheckList(item); + verbose && console.log("Failed all checks. Adding to checkList: " + item.fname); + + break; + } + } + } + + if (found) { + me.update(); + } + + if (!charms.skillerTypeA.length && !charms.skillerTypeB.length && !charms.skillerTypeC.length + && !charms.damage.length && !charms.resist.length && !charms.elemental.length && !charms.life.length && !charms.backup.length) { + verbose && console.log("No Charms"); + return charms; + } + + charms.skillerTypeA.length > 0 && sortCharms(charms.skillerTypeA, verbose); + charms.skillerTypeB.length > 0 && sortCharms(charms.skillerTypeB, verbose); + charms.skillerTypeC.length > 0 && sortCharms(charms.skillerTypeC, verbose); + charms.resist.length > 0 && sortCharms(charms.resist, verbose); + charms.life.length > 0 && sortCharms(charms.life, verbose); + charms.magicfind.length > 0 && sortCharms(charms.magicfind, verbose); + charms.damage.length > 0 && sortCharms(charms.damage, verbose); + charms.elemental.length > 0 && sortCharms(charms.elemental, verbose); + + // If stats are unspecifed, this will filter charms and keep highest based on invoquantity. If no invoquantity defined it will keep two of that type + charms.backup.length > 0 && sortCharms(charms.backup, verbose, false); + charms.keep = charms.keep.concat(charms.skillerTypeA, charms.skillerTypeB, charms.skillerTypeC, charms.resist, charms.life, charms.magicfind, charms.damage, charms.elemental, charms.backup); + verbose && charms.checkList.forEach((el, index) => console.log("checkList[" + index + "] = " + NTIP.GetCharmTier(el) + " " + el.fname)); + + return charms; + }, + + /** + * @param {ItemUnit} item + * @returns {boolean} + */ + check: function (item) { + if (!item || NTIP.GetCharmTier(item) <= 0 || !item.isCharm) return false; + // Annhilus, Hellfire Torch, Gheeds - Handled by a different function so return true to keep + if (item.isCharm && item.unique) return true; + // is one of our final charms + if (me.data.charmGids.includes(item.gid)) return true; + // is in our checkList + if (CharmEquip.keptGids.has(item.gid)) return true; + + let lowestCharm; + let items = me.getItemsEx() + .filter(charm => charm.classid === item.classid && charm.isInStorage && charm.magic && NTIP.GetCharmTier(charm) > 0); + if (!items.length) return true; + + let quantityCap = NTIP.getInvoQuantity(item); + let have = 0; + let charms = CharmEquip.sort(items); + let charmType = CharmEquip.getCharmType(item); + let cInfo, newList = []; + + switch (item.classid) { + case sdk.items.SmallCharm: + cInfo = CharData.charms.get("small").count(); + + if (cInfo.curr && (cInfo.curr / cInfo.max) * 100 >= 75) { + // chop off past our cap + newList = charms.keep + .sort((a, b) => NTIP.GetCharmTier(b) - NTIP.GetCharmTier(a)) + .slice(0, cInfo.max); + // check if it made the cut + if (!newList.find(i => i.gid === item.gid)) return false; + lowestCharm = newList.last(); + return !!(NTIP.GetCharmTier(item) >= NTIP.GetCharmTier(lowestCharm) || item.gid === lowestCharm.gid); + } + + break; + case sdk.items.LargeCharm: + cInfo = CharData.charms.get("large").count(); + + if (cInfo.curr && (cInfo.curr / cInfo.max) * 100 >= 75) { + // chop off past our cap + newList = charms.keep + .sort((a, b) => NTIP.GetCharmTier(b) - NTIP.GetCharmTier(a)) + .slice(0, cInfo.max); + // check if it made the cut + if (!newList.find(i => i.gid === item.gid)) return false; + lowestCharm = newList.last(); + return !!(NTIP.GetCharmTier(item) >= NTIP.GetCharmTier(lowestCharm) || item.gid === lowestCharm.gid); + } + + break; + case sdk.items.GrandCharm: + cInfo = CharData.charms.get("grand").count(); + + if (cInfo.curr && (cInfo.curr / cInfo.max) * 100 >= 50) { + // chop off past our cap + newList = charms.keep + .sort((a, b) => NTIP.GetCharmTier(b) - NTIP.GetCharmTier(a)) + .slice(0, cInfo.max); + // check if it made the cut + if (!newList.find(i => i.gid === item.gid)) return false; + lowestCharm = newList.last(); + return !!(NTIP.GetCharmTier(item) >= NTIP.GetCharmTier(lowestCharm) || item.gid === lowestCharm.gid); + } + + break; + } + + switch (charmType) { + case "skillerTypeA": + case "skillerTypeB": + case "skillerTypeC": + case "resist": + case "life": + case "magicfind": + case "damage": + case "elemental": + have = charms[charmType].length; + lowestCharm = charms[charmType].last(); + if ((charms[charmType].findIndex(c => c.gid === lowestCharm.gid) + 1) > quantityCap) return false; + + break; + default: + have = charms.backup.length; + lowestCharm = charms.backup.last(); + if ((charms.backup.findIndex(c => c.gid === lowestCharm.gid) + 1) > quantityCap) return false; + // console.debug("Lowest Charm index " + (charms.backup.findIndex(c => c.gid === lowestCharm.gid)) + " out of " + charms.backup.length); + + break; + } + + if (!lowestCharm) { + // console.debug("Didn't find any other charms of this type " + charmType); + return true; + } + + if (item.gid === lowestCharm.gid) { + // console.debug("Same charm"); + return true; + } + + let [tierParamItem, tierLowestItem] = [NTIP.GetCharmTier(item), NTIP.GetCharmTier(lowestCharm)]; + + if (tierParamItem === tierLowestItem) { + // console.debug("Same tier value"); + // super hacky - arbritrary comparsion of xpos if the tier value is the same + return (have < quantityCap) || (item.isInInventory && lowestCharm.isInInventory && item.x > lowestCharm.y) || (item.isInInventory && !lowestCharm.isInInventory); + } + + return (tierParamItem >= tierLowestItem); + }, + + run: function () { + // No charms in classic + if (me.classic) return; + + console.log("ÿc8Kolbot-SoloPlayÿc0: Entering charm auto equip"); + let tick = getTickCount(); + let charms = me.getItemsEx() + .filter(item => item.isInStorage && item.isCharm && item.magic); + // don't do anything if we don't have any charms + if ((!charms.length) + // don't do anything if we have the same charms as last time + || ((CharmEquip.keptGids.size && charms.every(c => CharmEquip.keptGids.has(c.gid))))) { + console.log("ÿc8Kolbot-SoloPlayÿc0: Exiting charm auto equip. Time elapsed: " + Time.format(getTickCount() - tick)); + return; + } + CharmEquip.keptGids.clear(); + let totalKeep = [], totalSell = []; + let GCs = CharmEquip.grandCharm(charms); + let LCs = CharmEquip.largeCharm(charms); + let SCs = CharmEquip.smallCharm(charms); + let specialCharms = charms.filter((charm) => charm.unique); + let verbose = !!(Developer.debugging.smallCharm || Developer.debugging.largeCharm || Developer.debugging.grandCharm); + + if (verbose) { + console.log("Grand Charms Keep: " + GCs.keep.length + ", Sell: " + GCs.sell.length); + console.log("Large Charms Keep: " + LCs.keep.length + ", Sell: " + LCs.sell.length); + console.log("Small Charms Keep: " + SCs.keep.length + ", Sell: " + SCs.sell.length); + } + + totalKeep = totalKeep.concat(SCs.keep, LCs.keep, GCs.keep, specialCharms); + for (let i = 0; i < totalKeep.length; i++) { + if (!CharmEquip.check(totalKeep[i])) { + totalSell.push(totalKeep[i]); + totalKeep.splice(i, 1); + i--; + } + } + totalSell = totalSell + .concat(SCs.sell, LCs.sell, GCs.sell) + .filter((charm) => NTIP.CheckItem(charm, NTIP_CheckListNoTier) === Pickit.Result.UNWANTED); + totalKeep.length > 0 && console.log("ÿc8Kolbot-SoloPlayÿc0: Total Charms Kept: " + totalKeep.length); + + if (totalSell.length > 0) { + console.log("ÿc8Kolbot-SoloPlayÿc0: Total Charms Sell: " + totalSell.length); + + for (let i = 0; i < totalSell.length; i++) { + totalSell[i].isInStash && !getUIFlag(sdk.uiflags.Stash) && Town.openStash(); + if (totalSell[i].isInStash && (!totalSell[i].sellable || !Storage.Inventory.MoveTo(totalSell[i]))) { + totalSell[i].drop(); + totalSell.splice(i, 1); + i -= 1; + } + } + + Town.initNPC("Shop", "clearInventory"); + + if (getUIFlag(sdk.uiflags.Shop) || (Config.PacketShopping && getInteractedNPC() && getInteractedNPC().itemcount > 0)) { + for (let i = 0; i < totalSell.length; i++) { + console.log("ÿc8Kolbot-SoloPlayÿc0: Sell old charm " + totalSell[i].name); + verbose && Item.logger("Sold", totalSell[i]); + verbose && Item.logItem("CharmEquip Sold", totalSell[i]); + totalSell[i].sell(); + } + } + } + + if (totalKeep.length > 0) { + for (let i = 0; i < totalKeep.length; i++) { + CharmEquip.keptGids.add(totalKeep[i].gid); + if (totalKeep[i].isInStash && !Cubing.checkItem(totalKeep[i])) { + !getUIFlag(sdk.uiflags.Stash) && Town.openStash() && delay(300 + me.ping); + if (Storage.Inventory.CanFit(totalKeep[i]) && Storage.Inventory.MoveTo(totalKeep[i])) { + verbose && Item.logItem("CharmEquip Equipped", totalKeep[i]); + } + } + } + } + + me.cancelUIFlags(); + + console.log("ÿc8Kolbot-SoloPlayÿc0: Exiting charm auto equip. Time elapsed: " + Time.format(getTickCount() - tick)); + }, + }; })(); diff --git a/libs/SoloPlay/Functions/ClassAttackOverrides/AmazonAttacks-WIP.js b/libs/SoloPlay/Functions/ClassAttackOverrides/AmazonAttacks-WIP.js index cae86115..4874429a 100644 --- a/libs/SoloPlay/Functions/ClassAttackOverrides/AmazonAttacks-WIP.js +++ b/libs/SoloPlay/Functions/ClassAttackOverrides/AmazonAttacks-WIP.js @@ -14,415 +14,415 @@ ClassAttack.decoyTick = getTickCount(); // todo: take into account auras as well - conviction/fanat/might can all be dangerous // mayve include dolls too? let inDanger = function (unit) { - let nearUnits = getUnits(sdk.unittype.Monster).filter((mon) => mon.attackable && getDistance(unit, mon) < 10); - let dangerClose = nearUnits.find(mon => mon.getEnchant(sdk.enchant.ManaBurn) || mon.getEnchant(sdk.enchant.LightningEnchanted)); - return { - check: nearUnits.length > me.maxNearMonsters || dangerClose, - mobs: nearUnits.length - }; + let nearUnits = getUnits(sdk.unittype.Monster).filter((mon) => mon.attackable && getDistance(unit, mon) < 10); + let dangerClose = nearUnits.find(mon => mon.getEnchant(sdk.enchant.ManaBurn) || mon.getEnchant(sdk.enchant.LightningEnchanted)); + return { + check: nearUnits.length > me.maxNearMonsters || dangerClose, + mobs: nearUnits.length + }; }; ClassAttack.decideSkill = function (unit) { - let skills = { timed: -1, untimed: -1 }; - if (!unit) return skills; + let skills = { timed: -1, untimed: -1 }; + if (!unit) return skills; - let index = (unit.isSpecial || unit.isPlayer) ? 1 : 3; + let index = (unit.isSpecial || unit.isPlayer) ? 1 : 3; - // Get timed skill - let checkSkill = Attack.getCustomAttack(unit) ? Attack.getCustomAttack(unit)[0] : Config.AttackSkill[index]; + // Get timed skill + let checkSkill = Attack.getCustomAttack(unit) ? Attack.getCustomAttack(unit)[0] : Config.AttackSkill[index]; - if (Attack.checkResist(unit, checkSkill) && Attack.validSpot(unit.x, unit.y, checkSkill)) { - skills.timed = checkSkill; - } else if (Config.AttackSkill[5] > -1 && Attack.checkResist(unit, Config.AttackSkill[5]) && Attack.validSpot(unit.x, unit.y, Config.AttackSkill[5])) { - skills.timed = Config.AttackSkill[5]; - } + if (Attack.checkResist(unit, checkSkill) && Attack.validSpot(unit.x, unit.y, checkSkill)) { + skills.timed = checkSkill; + } else if (Config.AttackSkill[5] > -1 && Attack.checkResist(unit, Config.AttackSkill[5]) && Attack.validSpot(unit.x, unit.y, Config.AttackSkill[5])) { + skills.timed = Config.AttackSkill[5]; + } - // Get untimed skill - checkSkill = Attack.getCustomAttack(unit) ? Attack.getCustomAttack(unit)[1] : Config.AttackSkill[index + 1]; + // Get untimed skill + checkSkill = Attack.getCustomAttack(unit) ? Attack.getCustomAttack(unit)[1] : Config.AttackSkill[index + 1]; - if (Attack.checkResist(unit, checkSkill) && Attack.validSpot(unit.x, unit.y, checkSkill)) { - skills.untimed = checkSkill; - } else if (Config.AttackSkill[6] > -1 && Attack.checkResist(unit, Config.AttackSkill[6]) && Attack.validSpot(unit.x, unit.y, Config.AttackSkill[6])) { - skills.untimed = Config.AttackSkill[6]; - } + if (Attack.checkResist(unit, checkSkill) && Attack.validSpot(unit.x, unit.y, checkSkill)) { + skills.untimed = checkSkill; + } else if (Config.AttackSkill[6] > -1 && Attack.checkResist(unit, Config.AttackSkill[6]) && Attack.validSpot(unit.x, unit.y, Config.AttackSkill[6])) { + skills.untimed = Config.AttackSkill[6]; + } - // Low mana timed skill - if (Config.LowManaSkill[0] > -1 && Skill.getManaCost(skills.untimed) > me.mp && Attack.checkResist(unit, Config.LowManaSkill[0])) { - skills.timed = Config.LowManaSkill[0]; - } + // Low mana timed skill + if (Config.LowManaSkill[0] > -1 && Skill.getManaCost(skills.untimed) > me.mp && Attack.checkResist(unit, Config.LowManaSkill[0])) { + skills.timed = Config.LowManaSkill[0]; + } - // Low mana untimed skill - if (Config.LowManaSkill[1] > -1 && Skill.getManaCost(skills.untimed) > me.mp && Attack.checkResist(unit, Config.LowManaSkill[1])) { - skills.untimed = Config.LowManaSkill[1]; - } + // Low mana untimed skill + if (Config.LowManaSkill[1] > -1 && Skill.getManaCost(skills.untimed) > me.mp && Attack.checkResist(unit, Config.LowManaSkill[1])) { + skills.untimed = Config.LowManaSkill[1]; + } - return skills; + return skills; }; ClassAttack.doAttack = function (unit) { - if (!unit) return Attack.Result.SUCCESS; - let gid = unit.gid; - let needRepair = me.charlvl < 5 ? [] : me.needRepair(); - - if ((Config.MercWatch && me.needMerc()) || needRepair.length > 0) { - console.log("towncheck"); - - if (Town.visitTown(!!needRepair.length)) { - // lost reference to the mob we were attacking - if (!unit || !copyUnit(unit).x || !Game.getMonster(-1, -1, gid) || unit.dead) { - return Attack.Result.SUCCESS; - } - } - } - - let mercRevive = 0; - let gold = me.gold; - let index = ((unit.isSpecial) || unit.isPlayer) ? 1 : 3; - - // todo: assign main attack damage, if we have a range skill but we are attacking a mob thats resistant with our close up skill, move away and use far attack - // figure out better slow-missiles casting - // need to re-write to include damage calculations - when should we use poison over light or phys over light - const data = { - innerSight: { - skill: sdk.skills.InnerSight, - level: me.getSkill(sdk.skills.InnerSight, sdk.skills.subindex.SoftPoints), - range: 15, - mana: Skill.getManaCost(sdk.skills.InnerSight), - use: function () { - return this.level > 0; - } - }, - slowMissiles: { - skill: sdk.skills.SlowMissiles, - level: me.getSkill(sdk.skills.SlowMissiles, sdk.skills.subindex.SoftPoints), - range: 15, - mana: Skill.getManaCost(sdk.skills.SlowMissiles), - use: function () { - return this.level > 0; - } - }, - decoy: { - skill: sdk.skills.Dopplezon, - level: me.getSkill(sdk.skills.Dopplezon, sdk.skills.subindex.SoftPoints), - range: 20, - mana: Skill.getManaCost(sdk.skills.Dopplezon), - duration: Skill.getDuration(sdk.skills.Dopplezon), - force: false, - use: function () { - return ((this.level > 0 && !me.normal) || this.force); - } - }, - lightFury: { - skill: sdk.skills.LightningFury, - level: me.getSkill(sdk.skills.LightningFury, sdk.skills.subindex.SoftPoints), - range: Skill.getRange(sdk.skills.LightningFury), - mana: Skill.getManaCost(sdk.skills.LightningFury), - force: false, - use: function () { - return (this.level >= 10 || this.force); - } - }, - plagueJav: { - skill: sdk.skills.PlagueJavelin, - level: me.getSkill(sdk.skills.PlagueJavelin, sdk.skills.subindex.SoftPoints), - range: Skill.getRange(sdk.skills.PlagueJavelin), - mana: Skill.getManaCost(sdk.skills.PlagueJavelin), - force: false, - use: function () { - return ((!me.normal && this.level > 0) || this.level >= 15 || this.force); - } - }, - jab: { - skill: sdk.skills.Jab, - level: me.getSkill(sdk.skills.Jab, sdk.skills.subindex.SoftPoints), - range: Skill.getRange(sdk.skills.Jab), - mana: Skill.getManaCost(sdk.skills.Jab), - use: function () { - return (this.level > 0 && me.equipped.get(sdk.body.RightArm).tier >= 1000); - } - }, - }; - - // Pre-attacks Section -----------------------------------------------------------------------------------------------------------------// - if (data.slowMissiles.use()) { - if (!unit.getState(sdk.states.SlowMissiles)) { - if ((unit.distance > 3 || unit.getEnchant(sdk.enchant.LightningEnchanted)) && unit.distance < 13 && !checkCollision(me, unit, sdk.collision.Ranged)) { - // Act Bosses and mini-bosses are immune to Slow Missles and pointless to use on lister or Cows, Use Inner-Sight instead - if ([156, 211, 242, 243, 544, 571, 391, 365, 267, 229].includes(unit.classid)) { - // Check if already in this state - if (!unit.getState(sdk.states.InnerSight)) { - Skill.cast(sdk.skills.InnerSight, sdk.skills.hand.Right, unit); - } - } else { - Skill.cast(sdk.skills.SlowMissiles, sdk.skills.hand.Right, unit); - } - } - } - } - - // if inDanger and within melee distance should we try to find a better spot? - if (inDanger(unit).check) { - data.lightFury.level && (data.lightFury.force = data.lightFury.level > 10); - data.plagueJav.level && (data.plagueJav.force = data.plagueJav.level > 10); - data.decoy.level && (data.decoy.force = true); - } - - if (data.innerSight.use()) { - if (!unit.getState(sdk.states.InnerSight) && unit.distance > 3 && unit.distance < 13 && !checkCollision(me, unit, sdk.collision.Ranged)) { - Skill.cast(sdk.skills.InnerSight, sdk.skills.hand.Right, unit); - } - } - - // Handle Switch casting - let commonCheck = (gold > 500000 || unit.isBoss || [sdk.areas.ChaosSanctuary, sdk.areas.ThroneofDestruction].includes(me.area)); - if (me.expansion && index === 1 && unit.curseable) { - if (CharData.skillData.haveChargedSkillOnSwitch(sdk.skills.LowerResist) - && !unit.getState(sdk.states.LowerResist) && commonCheck && !checkCollision(me, unit, sdk.collision.Ranged)) { - // Switch cast lower resist - Attack.switchCastCharges(sdk.skills.LowerResist, unit); - } - - if (CharData.skillData.haveChargedSkillOnSwitch(sdk.skills.Weaken) && !unit.getState(sdk.states.Weaken) - && !unit.getState(sdk.states.LowerResist) && commonCheck && !checkCollision(me, unit, sdk.collision.Ranged)) { - // Switch cast weaken - Attack.switchCastCharges(sdk.skills.Weaken, unit); - } - } - - // specials and dolls for now, should make dolls much less dangerous with the reduction of their damage - if (Precast.haveCTA > -1 && unit.curseable && (index === 1 || unit.isDoll) - && unit.distance < 5 && !unit.getState(sdk.states.BattleCry) && unit.curseable) { - Skill.switchCast(sdk.skills.BattleCry, { oSkill: true }); - } - - if (data.decoy.use()) { - // Act Bosses or Immune to my main boss skill - if ((unit.isPrimeEvil) || !Attack.checkResist(unit, Config.AttackSkill[1]) || data.decoy.force) { - Misc.poll(() => !me.skillDelay, 1000, 40); - - // Don't use decoy if within melee distance - if (unit.distance > 5) { - // Check to see if decoy has already been cast - let decoy = me.getMinionCount(8); - - if ((!decoy || data.decoy.force) && (getTickCount() - this.decoyTick >= data.decoy.duration)) { - if (unit.distance > 10 || checkCollision(me, unit, 0x7)) { - if (!Attack.getIntoPosition(unit, 10, 0x7)) { - return Attack.Result.FAILED; - } - } - - let coord = CollMap.getRandCoordinate(unit.x, -2, 2, unit.y, -2, 2); - !!coord && Skill.cast(sdk.skills.Decoy, sdk.skills.hand.Right, coord.x, coord.y); - - // Check if it was a sucess - !!me.getMinionCount(8) && (this.decoyTick = getTickCount()); - } - } - } - } - - // Only try attacking light immunes if I have my end game javelin - preAttack with Plague Javelin - if (data.plagueJav.use() && !Attack.checkResist(unit, "lightning")) { - if (unit.distance <= 15 && !checkCollision(me, unit, sdk.collision.Ranged)) { - // Cast Slow-Missles, then proceed with Plague Jav. Lowers amount of damage from projectiles. - !unit.getState(sdk.states.SlowMissiles) && data.slowMissiles.use() && Skill.cast(sdk.skills.SlowMissiles, sdk.skills.hand.Right, unit); - - // Handle Switch casting - if (!unit.dead) { - // should we switch cast any mob thats light immune? - if (!unit.getState(sdk.states.LowerResist) && unit.curseable && commonCheck && !checkCollision(me, unit, sdk.collision.Ranged)) { - // Switch cast lower resist - Attack.switchCastCharges(sdk.skills.LowerResist, unit); - } - } - - if (Attack.checkResist(unit, "poison") && !me.skillDelay && !unit.dead) { - Skill.cast(sdk.skills.PlagueJavelin, Skill.getHand(sdk.skills.PlagueJavelin), unit); - } - - if (!data.jab.use() && data.jab.level) { - // We are within melee distance might as well use jab rather than stand there - // Make sure monster is not physical immune - if (unit.distance < 4 && Attack.checkResist(unit, "physical")) { - if (checkCollision(me, unit, 0x7)) { - if (!Attack.getIntoPosition(unit, 3, 0x7)) { - return Attack.Result.FAILED; - } - } - - !unit.dead && Skill.cast(sdk.skills.Jab, Skill.getHand(sdk.skills.Jab), unit); - - return Attack.Result.SUCCESS; - } - - return Attack.Result.SUCCESS; - } - } - } - - // Only try attacking immunes if I have my end game javelin and they aren't lightning enchanted - use jab as main attack - // why? don't remember reason I did this - // if (data.jab.use() && !Attack.checkResist(unit, Config.AttackSkill[1]) && Attack.checkResist(unit, "physical") && !unit.getEnchant(sdk.enchant.LightningEnchanted)) { - // if ((unit.distance > 3 || checkCollision(me, unit, sdk.collision.Ranged)) && !Attack.getIntoPosition(unit, 3, sdk.collision.BlockWall)) { - // return Attack.Result.FAILED; - // } - - // !unit.dead && Skill.cast(sdk.skills.Jab, Skill.getHand(sdk.skills.Jab), unit); - - // return Attack.Result.SUCCESS; - // } - - if (data.plagueJav.use() && Attack.checkResist(unit, "poison") && !unit.getState(sdk.states.Poison) && !me.skillDelay) { - if (((data.plagueJav.force || unit.distance >= 8) && unit.distance <= 25) && !checkCollision(me, unit, sdk.collision.Ranged)) { - Skill.cast(sdk.skills.PlagueJavelin, Skill.getHand(sdk.skills.PlagueJavelin), unit); - } - } - - if (data.lightFury.use()) { - if ((unit.distance >= 8 && unit.distance <= 25) && !checkCollision(me, unit, sdk.collision.Ranged)) { - if (Skill.cast(sdk.skills.LightningFury, Skill.getHand(sdk.skills.LightningFury), unit) && data.lightFury.force) return Attack.Result.SUCCESS; - } - } - - let skills = this.decideSkill(unit); - let result = this.doCast(unit, skills.timed, skills.untimed); - - if (result === Attack.Result.CANTATTACK && Attack.canTeleStomp(unit)) { - let merc = me.getMerc(); - - while (unit.attackable) { - if (!unit) return Attack.Result.SUCCESS; - - if (me.needMerc()) { - if (Config.MercWatch && mercRevive++ < 1) { - Town.visitTown(); - } else { - return Attack.Result.CANTATTACK; - } - - (merc === undefined || !merc) && (merc = me.getMerc()); - } - - if (!!merc && getDistance(merc, unit) > 5) { - Pather.moveToUnit(unit); - - let spot = Attack.findSafeSpot(unit, 10, 5, 9); - !!spot && Pather.walkTo(spot.x, spot.y); - } - - let closeMob = Attack.getNearestMonster({ skipGid: gid }); - - if (!!closeMob) { - let findSkill = this.decideSkill(closeMob); - (this.doCast(closeMob, findSkill.timed, findSkill.untimed) === 1) || (data.decoy.level && Skill.cast(sdk.skills.Decoy, sdk.skills.hand.Right, unit)); - } - } - - return Attack.Result.SUCCESS; - } - - return result; + if (!unit) return Attack.Result.SUCCESS; + let gid = unit.gid; + let needRepair = me.charlvl < 5 ? [] : me.needRepair(); + + if ((Config.MercWatch && me.needMerc()) || needRepair.length > 0) { + console.log("towncheck"); + + if (Town.visitTown(!!needRepair.length)) { + // lost reference to the mob we were attacking + if (!unit || !copyUnit(unit).x || !Game.getMonster(-1, -1, gid) || unit.dead) { + return Attack.Result.SUCCESS; + } + } + } + + let mercRevive = 0; + let gold = me.gold; + let index = ((unit.isSpecial) || unit.isPlayer) ? 1 : 3; + + // todo: assign main attack damage, if we have a range skill but we are attacking a mob thats resistant with our close up skill, move away and use far attack + // figure out better slow-missiles casting + // need to re-write to include damage calculations - when should we use poison over light or phys over light + const data = { + innerSight: { + skill: sdk.skills.InnerSight, + level: me.getSkill(sdk.skills.InnerSight, sdk.skills.subindex.SoftPoints), + range: 15, + mana: Skill.getManaCost(sdk.skills.InnerSight), + use: function () { + return this.level > 0; + } + }, + slowMissiles: { + skill: sdk.skills.SlowMissiles, + level: me.getSkill(sdk.skills.SlowMissiles, sdk.skills.subindex.SoftPoints), + range: 15, + mana: Skill.getManaCost(sdk.skills.SlowMissiles), + use: function () { + return this.level > 0; + } + }, + decoy: { + skill: sdk.skills.Dopplezon, + level: me.getSkill(sdk.skills.Dopplezon, sdk.skills.subindex.SoftPoints), + range: 20, + mana: Skill.getManaCost(sdk.skills.Dopplezon), + duration: Skill.getDuration(sdk.skills.Dopplezon), + force: false, + use: function () { + return ((this.level > 0 && !me.normal) || this.force); + } + }, + lightFury: { + skill: sdk.skills.LightningFury, + level: me.getSkill(sdk.skills.LightningFury, sdk.skills.subindex.SoftPoints), + range: Skill.getRange(sdk.skills.LightningFury), + mana: Skill.getManaCost(sdk.skills.LightningFury), + force: false, + use: function () { + return (this.level >= 10 || this.force); + } + }, + plagueJav: { + skill: sdk.skills.PlagueJavelin, + level: me.getSkill(sdk.skills.PlagueJavelin, sdk.skills.subindex.SoftPoints), + range: Skill.getRange(sdk.skills.PlagueJavelin), + mana: Skill.getManaCost(sdk.skills.PlagueJavelin), + force: false, + use: function () { + return ((!me.normal && this.level > 0) || this.level >= 15 || this.force); + } + }, + jab: { + skill: sdk.skills.Jab, + level: me.getSkill(sdk.skills.Jab, sdk.skills.subindex.SoftPoints), + range: Skill.getRange(sdk.skills.Jab), + mana: Skill.getManaCost(sdk.skills.Jab), + use: function () { + return (this.level > 0 && me.equipped.get(sdk.body.RightArm).tier >= 1000); + } + }, + }; + + // Pre-attacks Section -----------------------------------------------------------------------------------------------------------------// + if (data.slowMissiles.use()) { + if (!unit.getState(sdk.states.SlowMissiles)) { + if ((unit.distance > 3 || unit.getEnchant(sdk.enchant.LightningEnchanted)) && unit.distance < 13 && !checkCollision(me, unit, sdk.collision.Ranged)) { + // Act Bosses and mini-bosses are immune to Slow Missles and pointless to use on lister or Cows, Use Inner-Sight instead + if ([156, 211, 242, 243, 544, 571, 391, 365, 267, 229].includes(unit.classid)) { + // Check if already in this state + if (!unit.getState(sdk.states.InnerSight)) { + Skill.cast(sdk.skills.InnerSight, sdk.skills.hand.Right, unit); + } + } else { + Skill.cast(sdk.skills.SlowMissiles, sdk.skills.hand.Right, unit); + } + } + } + } + + // if inDanger and within melee distance should we try to find a better spot? + if (inDanger(unit).check) { + data.lightFury.level && (data.lightFury.force = data.lightFury.level > 10); + data.plagueJav.level && (data.plagueJav.force = data.plagueJav.level > 10); + data.decoy.level && (data.decoy.force = true); + } + + if (data.innerSight.use()) { + if (!unit.getState(sdk.states.InnerSight) && unit.distance > 3 && unit.distance < 13 && !checkCollision(me, unit, sdk.collision.Ranged)) { + Skill.cast(sdk.skills.InnerSight, sdk.skills.hand.Right, unit); + } + } + + // Handle Switch casting + let commonCheck = (gold > 500000 || unit.isBoss || [sdk.areas.ChaosSanctuary, sdk.areas.ThroneofDestruction].includes(me.area)); + if (me.expansion && index === 1 && unit.curseable) { + if (CharData.skillData.haveChargedSkillOnSwitch(sdk.skills.LowerResist) + && !unit.getState(sdk.states.LowerResist) && commonCheck && !checkCollision(me, unit, sdk.collision.Ranged)) { + // Switch cast lower resist + Attack.switchCastCharges(sdk.skills.LowerResist, unit); + } + + if (CharData.skillData.haveChargedSkillOnSwitch(sdk.skills.Weaken) && !unit.getState(sdk.states.Weaken) + && !unit.getState(sdk.states.LowerResist) && commonCheck && !checkCollision(me, unit, sdk.collision.Ranged)) { + // Switch cast weaken + Attack.switchCastCharges(sdk.skills.Weaken, unit); + } + } + + // specials and dolls for now, should make dolls much less dangerous with the reduction of their damage + if (Precast.haveCTA > -1 && unit.curseable && (index === 1 || unit.isDoll) + && unit.distance < 5 && !unit.getState(sdk.states.BattleCry) && unit.curseable) { + Skill.switchCast(sdk.skills.BattleCry, { oSkill: true }); + } + + if (data.decoy.use()) { + // Act Bosses or Immune to my main boss skill + if ((unit.isPrimeEvil) || !Attack.checkResist(unit, Config.AttackSkill[1]) || data.decoy.force) { + Misc.poll(() => !me.skillDelay, 1000, 40); + + // Don't use decoy if within melee distance + if (unit.distance > 5) { + // Check to see if decoy has already been cast + let decoy = me.getMinionCount(8); + + if ((!decoy || data.decoy.force) && (getTickCount() - this.decoyTick >= data.decoy.duration)) { + if (unit.distance > 10 || checkCollision(me, unit, 0x7)) { + if (!Attack.getIntoPosition(unit, 10, 0x7)) { + return Attack.Result.FAILED; + } + } + + let coord = CollMap.getRandCoordinate(unit.x, -2, 2, unit.y, -2, 2); + !!coord && Skill.cast(sdk.skills.Decoy, sdk.skills.hand.Right, coord.x, coord.y); + + // Check if it was a sucess + !!me.getMinionCount(8) && (this.decoyTick = getTickCount()); + } + } + } + } + + // Only try attacking light immunes if I have my end game javelin - preAttack with Plague Javelin + if (data.plagueJav.use() && !Attack.checkResist(unit, "lightning")) { + if (unit.distance <= 15 && !checkCollision(me, unit, sdk.collision.Ranged)) { + // Cast Slow-Missles, then proceed with Plague Jav. Lowers amount of damage from projectiles. + !unit.getState(sdk.states.SlowMissiles) && data.slowMissiles.use() && Skill.cast(sdk.skills.SlowMissiles, sdk.skills.hand.Right, unit); + + // Handle Switch casting + if (!unit.dead) { + // should we switch cast any mob thats light immune? + if (!unit.getState(sdk.states.LowerResist) && unit.curseable && commonCheck && !checkCollision(me, unit, sdk.collision.Ranged)) { + // Switch cast lower resist + Attack.switchCastCharges(sdk.skills.LowerResist, unit); + } + } + + if (Attack.checkResist(unit, "poison") && !me.skillDelay && !unit.dead) { + Skill.cast(sdk.skills.PlagueJavelin, Skill.getHand(sdk.skills.PlagueJavelin), unit); + } + + if (!data.jab.use() && data.jab.level) { + // We are within melee distance might as well use jab rather than stand there + // Make sure monster is not physical immune + if (unit.distance < 4 && Attack.checkResist(unit, "physical")) { + if (checkCollision(me, unit, 0x7)) { + if (!Attack.getIntoPosition(unit, 3, 0x7)) { + return Attack.Result.FAILED; + } + } + + !unit.dead && Skill.cast(sdk.skills.Jab, Skill.getHand(sdk.skills.Jab), unit); + + return Attack.Result.SUCCESS; + } + + return Attack.Result.SUCCESS; + } + } + } + + // Only try attacking immunes if I have my end game javelin and they aren't lightning enchanted - use jab as main attack + // why? don't remember reason I did this + // if (data.jab.use() && !Attack.checkResist(unit, Config.AttackSkill[1]) && Attack.checkResist(unit, "physical") && !unit.getEnchant(sdk.enchant.LightningEnchanted)) { + // if ((unit.distance > 3 || checkCollision(me, unit, sdk.collision.Ranged)) && !Attack.getIntoPosition(unit, 3, sdk.collision.BlockWall)) { + // return Attack.Result.FAILED; + // } + + // !unit.dead && Skill.cast(sdk.skills.Jab, Skill.getHand(sdk.skills.Jab), unit); + + // return Attack.Result.SUCCESS; + // } + + if (data.plagueJav.use() && Attack.checkResist(unit, "poison") && !unit.getState(sdk.states.Poison) && !me.skillDelay) { + if (((data.plagueJav.force || unit.distance >= 8) && unit.distance <= 25) && !checkCollision(me, unit, sdk.collision.Ranged)) { + Skill.cast(sdk.skills.PlagueJavelin, Skill.getHand(sdk.skills.PlagueJavelin), unit); + } + } + + if (data.lightFury.use()) { + if ((unit.distance >= 8 && unit.distance <= 25) && !checkCollision(me, unit, sdk.collision.Ranged)) { + if (Skill.cast(sdk.skills.LightningFury, Skill.getHand(sdk.skills.LightningFury), unit) && data.lightFury.force) return Attack.Result.SUCCESS; + } + } + + let skills = this.decideSkill(unit); + let result = this.doCast(unit, skills.timed, skills.untimed); + + if (result === Attack.Result.CANTATTACK && Attack.canTeleStomp(unit)) { + let merc = me.getMerc(); + + while (unit.attackable) { + if (!unit) return Attack.Result.SUCCESS; + + if (me.needMerc()) { + if (Config.MercWatch && mercRevive++ < 1) { + Town.visitTown(); + } else { + return Attack.Result.CANTATTACK; + } + + (merc === undefined || !merc) && (merc = me.getMerc()); + } + + if (!!merc && getDistance(merc, unit) > 5) { + Pather.moveToUnit(unit); + + let spot = Attack.findSafeSpot(unit, 10, 5, 9); + !!spot && Pather.walkTo(spot.x, spot.y); + } + + let closeMob = Attack.getNearestMonster({ skipGid: gid }); + + if (!!closeMob) { + let findSkill = this.decideSkill(closeMob); + (this.doCast(closeMob, findSkill.timed, findSkill.untimed) === 1) || (data.decoy.level && Skill.cast(sdk.skills.Decoy, sdk.skills.hand.Right, unit)); + } + } + + return Attack.Result.SUCCESS; + } + + return result; }; ClassAttack.afterAttack = function () { - Precast.doPrecast(false); + Precast.doPrecast(false); - let needRepair = me.charlvl < 5 ? [] : me.needRepair(); - - // Repair check, make sure i have a tome - if (needRepair.length > 0 && me.getItem(sdk.items.TomeofTownPortal)) { - Town.visitTown(true); - } + let needRepair = me.charlvl < 5 ? [] : me.needRepair(); + + // Repair check, make sure i have a tome + if (needRepair.length > 0 && me.getItem(sdk.items.TomeofTownPortal)) { + Town.visitTown(true); + } - this.lightFuryTick = 0; + this.lightFuryTick = 0; }; // Returns: 0 - fail, 1 - success, 2 - no valid attack skills ClassAttack.doCast = function (unit, timedSkill, untimedSkill) { - let walk; - - // No valid skills can be found - if (timedSkill < 0 && untimedSkill < 0) return Attack.Result.CANTATTACK; - - // Arrow/bolt check - if (this.bowCheck) { - switch (true) { - case this.bowCheck === "bow" && !me.getItem("aqv", sdk.items.mode.Equipped): - case this.bowCheck === "crossbow" && !me.getItem("cqv", sdk.items.mode.Equipped): - console.log("Bow check"); - Town.visitTown(); - - break; - } - } - - if (timedSkill > -1 && (!me.getState(sdk.states.SkillDelay) || !Skill.isTimed(timedSkill))) { - switch (timedSkill) { - case sdk.skills.LightningFury: - if (!this.lightFuryTick || getTickCount() - this.lightFuryTick > Config.LightningFuryDelay * 1000) { - if (unit.distance > Skill.getRange(timedSkill) || checkCollision(me, unit, sdk.collision.Ranged)) { - if (!Attack.getIntoPosition(unit, Skill.getRange(timedSkill), sdk.collision.Ranged)) { - return Attack.Result.FAILED; - } - } - - if (!unit.dead && Skill.cast(timedSkill, Skill.getHand(timedSkill), unit)) { - this.lightFuryTick = getTickCount(); - } - - return Attack.Result.SUCCESS; - } - - break; - default: - // If main attack skill is lightning strike and charged strike's skill level is at least level 15, check current monster count. If monster count is less than 3, use CS as its more effective with small mobs - if (timedSkill === sdk.skills.LightningStrike && me.getSkill(sdk.skills.ChargedStrike, sdk.skills.subindex.SoftPoints) >= 15) { - if (me.getMobCount(15, Coords_1.BlockBits.LineOfSight | Coords_1.BlockBits.Ranged | Coords_1.BlockBits.ClosedDoor | Coords_1.BlockBits.BlockWall) <= 3) { - timedSkill = sdk.skills.ChargedStrike; - } - } - - if (Skill.getRange(timedSkill) < 4 && !Attack.validSpot(unit.x, unit.y)) return Attack.Result.FAILED; - - if (unit.distance > Skill.getRange(timedSkill) || checkCollision(me, unit, sdk.collision.Ranged)) { - // Allow short-distance walking for melee skills - walk = Skill.getRange(timedSkill) < 4 && unit.distance < 10 && !checkCollision(me, unit, sdk.collision.BlockWall); - - if (!Attack.getIntoPosition(unit, Skill.getRange(timedSkill), sdk.collision.Ranged, walk)) { - return Attack.Result.FAILED; - } - } - - !unit.dead && Skill.cast(timedSkill, Skill.getHand(timedSkill), unit); - - return Attack.Result.SUCCESS; - } - } - - if (untimedSkill > -1) { - if (Skill.getRange(untimedSkill) < 4 && !Attack.validSpot(unit.x, unit.y)) return Attack.Result.FAILED; - - if (unit.distance > Skill.getRange(untimedSkill) || checkCollision(me, unit, sdk.collision.Ranged)) { - // Allow short-distance walking for melee skills - walk = Skill.getRange(untimedSkill) < 4 && unit.distance < 10 && !checkCollision(me, unit, sdk.collision.BlockWall); - - if (!Attack.getIntoPosition(unit, Skill.getRange(untimedSkill), sdk.collision.Ranged, walk)) { - return Attack.Result.FAILED; - } - } - - !unit.dead && Skill.cast(untimedSkill, Skill.getHand(untimedSkill), unit); - - return Attack.Result.SUCCESS; - } - - Misc.poll(() => !me.skillDelay, 1000, 40); - - // Wait for Lightning Fury timeout - while (this.lightFuryTick && getTickCount() - this.lightFuryTick < Config.LightningFuryDelay * 1000) { - delay(40); - } - - return Attack.Result.SUCCESS; + let walk; + + // No valid skills can be found + if (timedSkill < 0 && untimedSkill < 0) return Attack.Result.CANTATTACK; + + // Arrow/bolt check + if (this.bowCheck) { + switch (true) { + case this.bowCheck === "bow" && !me.getItem("aqv", sdk.items.mode.Equipped): + case this.bowCheck === "crossbow" && !me.getItem("cqv", sdk.items.mode.Equipped): + console.log("Bow check"); + Town.visitTown(); + + break; + } + } + + if (timedSkill > -1 && (!me.getState(sdk.states.SkillDelay) || !Skill.isTimed(timedSkill))) { + switch (timedSkill) { + case sdk.skills.LightningFury: + if (!this.lightFuryTick || getTickCount() - this.lightFuryTick > Config.LightningFuryDelay * 1000) { + if (unit.distance > Skill.getRange(timedSkill) || checkCollision(me, unit, sdk.collision.Ranged)) { + if (!Attack.getIntoPosition(unit, Skill.getRange(timedSkill), sdk.collision.Ranged)) { + return Attack.Result.FAILED; + } + } + + if (!unit.dead && Skill.cast(timedSkill, Skill.getHand(timedSkill), unit)) { + this.lightFuryTick = getTickCount(); + } + + return Attack.Result.SUCCESS; + } + + break; + default: + // If main attack skill is lightning strike and charged strike's skill level is at least level 15, check current monster count. If monster count is less than 3, use CS as its more effective with small mobs + if (timedSkill === sdk.skills.LightningStrike && me.getSkill(sdk.skills.ChargedStrike, sdk.skills.subindex.SoftPoints) >= 15) { + if (me.getMobCount(15, Coords_1.BlockBits.LineOfSight | Coords_1.BlockBits.Ranged | Coords_1.BlockBits.ClosedDoor | Coords_1.BlockBits.BlockWall) <= 3) { + timedSkill = sdk.skills.ChargedStrike; + } + } + + if (Skill.getRange(timedSkill) < 4 && !Attack.validSpot(unit.x, unit.y)) return Attack.Result.FAILED; + + if (unit.distance > Skill.getRange(timedSkill) || checkCollision(me, unit, sdk.collision.Ranged)) { + // Allow short-distance walking for melee skills + walk = Skill.getRange(timedSkill) < 4 && unit.distance < 10 && !checkCollision(me, unit, sdk.collision.BlockWall); + + if (!Attack.getIntoPosition(unit, Skill.getRange(timedSkill), sdk.collision.Ranged, walk)) { + return Attack.Result.FAILED; + } + } + + !unit.dead && Skill.cast(timedSkill, Skill.getHand(timedSkill), unit); + + return Attack.Result.SUCCESS; + } + } + + if (untimedSkill > -1) { + if (Skill.getRange(untimedSkill) < 4 && !Attack.validSpot(unit.x, unit.y)) return Attack.Result.FAILED; + + if (unit.distance > Skill.getRange(untimedSkill) || checkCollision(me, unit, sdk.collision.Ranged)) { + // Allow short-distance walking for melee skills + walk = Skill.getRange(untimedSkill) < 4 && unit.distance < 10 && !checkCollision(me, unit, sdk.collision.BlockWall); + + if (!Attack.getIntoPosition(unit, Skill.getRange(untimedSkill), sdk.collision.Ranged, walk)) { + return Attack.Result.FAILED; + } + } + + !unit.dead && Skill.cast(untimedSkill, Skill.getHand(untimedSkill), unit); + + return Attack.Result.SUCCESS; + } + + Misc.poll(() => !me.skillDelay, 1000, 40); + + // Wait for Lightning Fury timeout + while (this.lightFuryTick && getTickCount() - this.lightFuryTick < Config.LightningFuryDelay * 1000) { + delay(40); + } + + return Attack.Result.SUCCESS; }; diff --git a/libs/SoloPlay/Functions/ClassAttackOverrides/AmazonAttacks.js b/libs/SoloPlay/Functions/ClassAttackOverrides/AmazonAttacks.js index 9da657e8..b377f829 100644 --- a/libs/SoloPlay/Functions/ClassAttackOverrides/AmazonAttacks.js +++ b/libs/SoloPlay/Functions/ClassAttackOverrides/AmazonAttacks.js @@ -22,317 +22,317 @@ ClassAttack.decoyTick = getTickCount(); * @returns {AttackResult} */ ClassAttack.doAttack = function (unit, preattack, once) { - // unit became invalidated - if (!unit || !unit.attackable) return Attack.Result.SUCCESS; - - let gid = unit.gid; - let needRepair = me.charlvl < 5 ? [] : me.needRepair(); - - if ((Config.MercWatch && me.needMerc()) || needRepair.length > 0) { - console.log("towncheck"); - - if (Town.visitTown(!!needRepair.length)) { - // lost reference to the mob we were attacking - if (!unit || !copyUnit(unit).x || !Game.getMonster(-1, -1, gid) || unit.dead) { - return Attack.Result.SUCCESS; - } - } - } - - let gold = me.gold; - let preattackRange = Skill.getRange(Config.AttackSkill[0]); - let decoyDuration = Skill.getDuration(sdk.skills.Dopplezon); - const index = (unit.isSpecial || unit.isPlayer) ? 1 : 3; - const useSkills = { - InnerSight: false, - SlowMissiles: false, - Jab: false, - Plague: false, - LightFury: false, - }; - - useSkills.InnerSight = Skill.canUse(sdk.skills.InnerSight); - useSkills.SlowMissiles = Skill.canUse(sdk.skills.SlowMissiles); - useSkills.Decoy = (Skill.canUse(sdk.skills.Dopplezon) && !me.normal); - - // check weapon - let [allowThrowing, forcePlague] = [false, false]; - let equippedWep = me.getEquippedItem(sdk.body.RightArm); - if (equippedWep) { - allowThrowing = (equippedWep.ethereal && equippedWep.quantityPercent > 25); - useSkills.Jab = (NTIP.GetTier(equippedWep) >= 1000 && Skill.canUse(sdk.skills.Jab)); - if (allowThrowing) { - useSkills.Plague = (!me.normal && Skill.canUse(sdk.skills.PlagueJavelin)); - useSkills.LightFury = (me.getSkill(sdk.skills.LightningFury, sdk.skills.subindex.SoftPoints) >= 10); - forcePlague = (me.getSkill(sdk.skills.PlagueJavelin, sdk.skills.subindex.SoftPoints) >= 15); // Extra poison damage then attack - } - } else { - console.warn("We don't have a weapon?"); - console.debug("Go to town, maybe can get one."); - Town.visitTown(true); - // we are probably screwed if we can't get a weapon, maybe go back a difficulty? - } - - // Precast Section -----------------------------------------------------------------------------------------------------------------// - if (useSkills.SlowMissiles) { - if (!unit.getState(sdk.states.SlowMissiles)) { - if ((unit.distance > 3 || unit.getEnchant(sdk.enchant.LightningEnchanted)) && unit.distance < 13 && !checkCollision(me, unit, sdk.collision.Ranged)) { - // Act Bosses and mini-bosses are immune to Slow Missles and pointless to use on lister or Cows, Use Inner-Sight instead - if ([sdk.monsters.HellBovine].includes(unit.classid) || unit.isBoss) { - // Check if already in this state - if (useSkills.InnerSight && !unit.getState(sdk.states.InnerSight)) { - Skill.cast(sdk.skills.InnerSight, sdk.skills.hand.Right, unit); - } - } else { - Skill.cast(sdk.skills.SlowMissiles, sdk.skills.hand.Right, unit); - } - } - } - } - - if (allowThrowing && Skill.canUse(sdk.skills.LightningFury) && unit.getEnchant(sdk.enchant.ManaBurn) && unit.getMobCount(7) > 2) { - useSkills.LightFury = true; - } - - if (allowThrowing && Skill.canUse(sdk.skills.PlagueJavelin) && unit.getEnchant(sdk.enchant.ManaBurn) && unit.getMobCount(7) > 2) { - forcePlague = true; - } - - if (useSkills.InnerSight) { - if (!unit.getState(sdk.states.InnerSight) && unit.distance > 3 && unit.distance < 13 && !checkCollision(me, unit, sdk.collision.Ranged)) { - Skill.cast(sdk.skills.InnerSight, sdk.skills.hand.Right, unit); - } - } - - // Handle Switch casting - let commonCheck = (gold > 500000 || unit.isBoss || [sdk.areas.ChaosSanctuary, sdk.areas.ThroneofDestruction].includes(me.area)); - if (me.expansion && index === 1 && unit.curseable) { - if (CharData.skillData.haveChargedSkillOnSwitch(sdk.skills.LowerResist) - && !unit.getState(sdk.states.LowerResist) && commonCheck && !checkCollision(me, unit, sdk.collision.Ranged)) { - // Switch cast lower resist - Attack.switchCastCharges(sdk.skills.LowerResist, unit); - } - - if (CharData.skillData.haveChargedSkillOnSwitch(sdk.skills.Weaken) && !unit.getState(sdk.states.Weaken) - && !unit.getState(sdk.states.LowerResist) && commonCheck && !checkCollision(me, unit, sdk.collision.Ranged)) { - // Switch cast weaken - Attack.switchCastCharges(sdk.skills.Weaken, unit); - } - } - - // specials and dolls for now, should make dolls much less dangerous with the reduction of their damage - if (Precast.haveCTA > -1 && unit.curseable && (index === 1 || unit.isDoll) - && unit.distance < 5 && !unit.getState(sdk.states.BattleCry) && unit.curseable) { - Skill.switchCast(sdk.skills.BattleCry, { oSkill: true }); - } - - if (useSkills.Decoy) { - // Act Bosses or Immune to my main boss skill - if ((unit.isPrimeEvil) || !Attack.checkResist(unit, Config.AttackSkill[1])) { - Misc.poll(() => !me.skillDelay, 1000, 40); - - // Don't use decoy if within melee distance - if (unit.distance > 4) { - // Check to see if decoy has already been cast - let decoy = Misc.poll(() => Game.getMonster(sdk.summons.Dopplezon), 1000, 10); - - if (!decoy && (getTickCount() - this.decoyTick >= decoyDuration) && unit.distance > 4) { - if (unit.distance > 10 || checkCollision(me, unit, sdk.collision.Ranged)) { - if (!Attack.getIntoPosition(unit, 10, sdk.collision.Ranged)) { - return Attack.Result.FAILED; - } - } - - let coord = CollMap.getRandCoordinate(unit.x, -2, 2, unit.y, -2, 2); - !!coord && Skill.cast(sdk.skills.Decoy, sdk.skills.hand.Right, coord.x, coord.y); - - // Check if it was a sucess - !!me.getMinionCount(sdk.summons.type.Dopplezon) && (this.decoyTick = getTickCount()); - } - } - } - } - - // Only try attacking light immunes if I have my end game javelin - preAttack with Plague Javelin - if ((useSkills.Plague) && !Attack.checkResist(unit, "lightning")) { - if ((unit.distance <= 15) && !checkCollision(me, unit, sdk.collision.Ranged)) { - // Cast Slow-Missles, then proceed with Plague Jav. Lowers amount of damage from projectiles. - !unit.getState(sdk.states.SlowMissiles) && useSkills.SlowMissiles && Skill.cast(sdk.skills.SlowMissiles, sdk.skills.hand.Right, unit); - - // Handle Switch casting - if (!unit.dead) { - if (CharData.skillData.haveChargedSkillOnSwitch(sdk.skills.LowerResist) - && !unit.getState(sdk.states.LowerResist) && unit.curseable && commonCheck && !checkCollision(me, unit, sdk.collision.Ranged)) { - // Switch cast lower resist - Attack.switchCastCharges(sdk.skills.LowerResist, unit); - } - } - - if (Attack.checkResist(unit, "poison") && !me.skillDelay && !unit.dead) { - Skill.cast(sdk.skills.PlagueJavelin, Skill.getHand(sdk.skills.PlagueJavelin), unit); - } - - if (!useSkills.Jab) { - // We are within melee distance might as well use jab rather than stand there - // Make sure monster is not physical immune - if (unit.distance < 4 && Attack.checkResist(unit, "physical")) { - if (Skill.canUse(sdk.skills.Jab)) { - if (unit.distance > 3 || checkCollision(me, unit, sdk.collision.Ranged)) { - if (!Attack.getIntoPosition(unit, 3, sdk.collision.BlockWall)) { - return Attack.Result.FAILED; - } - } - - !unit.dead && Skill.cast(sdk.skills.Jab, Skill.getHand(sdk.skills.Jab), unit); - - return Attack.Result.SUCCESS; - } - } - - return Attack.Result.SUCCESS; - } - } - } - - // Only try attacking immunes if I have my end game javelin and they aren't lightning enchanted - use jab as main attack - if (useSkills.Jab && !Attack.checkResist(unit, Config.AttackSkill[1]) && Attack.checkResist(unit, "physical") && !unit.getEnchant(sdk.enchant.LightningEnchanted)) { - if ((unit.distance > 3 || checkCollision(me, unit, sdk.collision.Ranged)) && !Attack.getIntoPosition(unit, 3, sdk.collision.BlockWall)) { - return Attack.Result.FAILED; - } - - !unit.dead && Skill.cast(sdk.skills.Jab, Skill.getHand(sdk.skills.Jab), unit); - - return Attack.Result.SUCCESS; - } - - if (forcePlague && Attack.checkResist(unit, "poison") && !unit.getState(sdk.states.Poison) && !me.skillDelay) { - if ((unit.distance >= 8 && unit.distance <= 15) && !checkCollision(me, unit, sdk.collision.Ranged)) { - Skill.cast(sdk.skills.PlagueJavelin, Skill.getHand(sdk.skills.PlagueJavelin), unit); - } - } - - if (useSkills.LightFury) { - if ((unit.distance >= 8 && unit.distance <= 15) && !checkCollision(me, unit, sdk.collision.Ranged)) { - Skill.cast(sdk.skills.LightningFury, Skill.getHand(sdk.skills.LightningFury), unit); - } - } - - if (preattack && Config.AttackSkill[0] > 0 && [sdk.skills.InnerSight, sdk.skills.SlowMissiles].indexOf(Config.AttackSkill[0]) === -1 - && Attack.checkResist(unit, Config.AttackSkill[0]) && (!me.skillDelay || !Skill.isTimed(Config.AttackSkill[0]))) { - if (unit.distance > preattackRange || checkCollision(me, unit, sdk.collision.Ranged)) { - if (!Attack.getIntoPosition(unit, preattackRange, sdk.collision.Ranged)) { - return Attack.Result.FAILED; - } - } - - Skill.cast(Config.AttackSkill[0], Skill.getHand(Config.AttackSkill[0]), unit); - - return Attack.Result.SUCCESS; - } - - let mercRevive = 0; - let skills = this.decideSkill(unit); - - const switchBowAttack = (unit, attackSkill) => { - if (Attack.getIntoPosition(unit, 20, sdk.collision.Ranged)) { - try { - const checkForShamans = unit.isFallen && !me.inArea(sdk.areas.BloodMoor); - for (let i = 0; i < 5 && unit.attackable; i++) { - if (checkForShamans && !once) { - // before we waste time let's see if there is a shaman we should kill - const shaman = getUnits(sdk.unittype.Monster) - .filter(mon => mon.distance < 20 && mon.isShaman && mon.attackable) - .sort((a, b) => a.distance - b.distance).first(); - if (shaman) return ClassAttack.doAttack(shaman, null, true); - } - if (!Attack.useBowOnSwitch(unit, attackSkill, i === 5)) return Attack.Result.FAILED; - if (unit.distance < 8 || me.inDanger()) { - if (once) return Attack.Result.FAILED; - let closeMob = getUnits(sdk.unittype.Monster) - .filter(mon => mon.distance < 10 && mon.attackable && mon.gid !== gid) - .sort(Attack.walkingSortMonsters).first(); - if (closeMob) return ClassAttack.doAttack(closeMob, null, true); - } - } - } finally { - me.weaponswitch !== sdk.player.slot.Main && me.switchWeapons(sdk.player.slot.Main); - } - } - return unit.dead ? Attack.Result.SUCCESS : Attack.Result.FAILED; - }; - - // @todo damage/effort comparison vs our normal skill - if (CharData.skillData.bow.onSwitch - && (index !== 1 || !unit.name.includes(getLocaleString(sdk.locale.text.Ghostly))) - && (unit.distance >= 8 || (unit.isMoving && (unit.targetx !== me.x || unit.targety !== me.y))) - && ([sdk.skills.Attack, sdk.skills.Jab].includes(skills.timed) || Skill.getManaCost(skills.timed) > me.mp)) { - let arrowSkill = (() => { - // todo - better determination of skills - if (Skill.canUse(sdk.skills.MagicArrow) && Skill.getManaCost(sdk.skills.MagicArrow) < me.mp) return sdk.skills.MagicArrow; - if (Skill.canUse(sdk.skills.FireArrow) && Skill.getManaCost(sdk.skills.FireArrow) < me.mp) return sdk.skills.FireArrow; - return 0; - })(); - if (switchBowAttack(unit, arrowSkill) === Attack.Result.SUCCESS) return Attack.Result.SUCCESS; - } - - if ([sdk.skills.Attack, sdk.skills.Jab].includes(skills.timed) - && (unit.distance >= 12 || (unit.distance > 4 && unit.isMoving && (unit.targetx !== me.x || unit.targety !== me.y)) || unit.coldEnchanted)) { - let item = me.getItemsEx().filter(item => item.isEquipped && item.bodylocation === sdk.body.RightArm).first(); - if (item && (item.getStat(sdk.stats.Quantity) * 100 / getBaseStat("items", item.classid, "maxstack")) > 30) { - skills.timed = sdk.skills.Throw; - } - } - - let result = this.doCast(unit, skills.timed, skills.untimed); - - if (result === Attack.Result.CANTATTACK && Attack.canTeleStomp(unit)) { - let merc = me.getMerc(); - - while (unit.attackable) { - if (!unit) return Attack.Result.SUCCESS; - - if (me.needMerc()) { - if (Config.MercWatch && mercRevive++ < 1) { - Town.visitTown(); - } else { - return Attack.Result.CANTATTACK; - } - - (merc === undefined || !merc) && (merc = me.getMerc()); - } - - if (!!merc && getDistance(merc, unit) > 5) { - Pather.moveToUnit(unit); - - let spot = Attack.findSafeSpot(unit, 10, 5, 9); - !!spot && Pather.walkTo(spot.x, spot.y); - } - - let closeMob = Attack.getNearestMonster({ skipGid: gid }); - - if (!!closeMob) { - let findSkill = this.decideSkill(closeMob); - (this.doCast(closeMob, findSkill.timed, findSkill.untimed) === 1) || (Skill.canUse(sdk.skills.Decoy) && Skill.cast(sdk.skills.Decoy, sdk.skills.hand.Right, unit)); - } - } - - return Attack.Result.SUCCESS; - } - - return result; + // unit became invalidated + if (!unit || !unit.attackable) return Attack.Result.SUCCESS; + + let gid = unit.gid; + let needRepair = me.charlvl < 5 ? [] : me.needRepair(); + + if ((Config.MercWatch && me.needMerc()) || needRepair.length > 0) { + console.log("towncheck"); + + if (Town.visitTown(!!needRepair.length)) { + // lost reference to the mob we were attacking + if (!unit || !copyUnit(unit).x || !Game.getMonster(-1, -1, gid) || unit.dead) { + return Attack.Result.SUCCESS; + } + } + } + + let gold = me.gold; + let preattackRange = Skill.getRange(Config.AttackSkill[0]); + let decoyDuration = Skill.getDuration(sdk.skills.Dopplezon); + const index = (unit.isSpecial || unit.isPlayer) ? 1 : 3; + const useSkills = { + InnerSight: false, + SlowMissiles: false, + Jab: false, + Plague: false, + LightFury: false, + }; + + useSkills.InnerSight = Skill.canUse(sdk.skills.InnerSight); + useSkills.SlowMissiles = Skill.canUse(sdk.skills.SlowMissiles); + useSkills.Decoy = (Skill.canUse(sdk.skills.Dopplezon) && !me.normal); + + // check weapon + let [allowThrowing, forcePlague] = [false, false]; + let equippedWep = me.getEquippedItem(sdk.body.RightArm); + if (equippedWep) { + allowThrowing = (equippedWep.ethereal && equippedWep.quantityPercent > 25); + useSkills.Jab = (NTIP.GetTier(equippedWep) >= 1000 && Skill.canUse(sdk.skills.Jab)); + if (allowThrowing) { + useSkills.Plague = (!me.normal && Skill.canUse(sdk.skills.PlagueJavelin)); + useSkills.LightFury = (me.getSkill(sdk.skills.LightningFury, sdk.skills.subindex.SoftPoints) >= 10); + forcePlague = (me.getSkill(sdk.skills.PlagueJavelin, sdk.skills.subindex.SoftPoints) >= 15); // Extra poison damage then attack + } + } else { + console.warn("We don't have a weapon?"); + console.debug("Go to town, maybe can get one."); + Town.visitTown(true); + // we are probably screwed if we can't get a weapon, maybe go back a difficulty? + } + + // Precast Section -----------------------------------------------------------------------------------------------------------------// + if (useSkills.SlowMissiles) { + if (!unit.getState(sdk.states.SlowMissiles)) { + if ((unit.distance > 3 || unit.getEnchant(sdk.enchant.LightningEnchanted)) && unit.distance < 13 && !checkCollision(me, unit, sdk.collision.Ranged)) { + // Act Bosses and mini-bosses are immune to Slow Missles and pointless to use on lister or Cows, Use Inner-Sight instead + if ([sdk.monsters.HellBovine].includes(unit.classid) || unit.isBoss) { + // Check if already in this state + if (useSkills.InnerSight && !unit.getState(sdk.states.InnerSight)) { + Skill.cast(sdk.skills.InnerSight, sdk.skills.hand.Right, unit); + } + } else { + Skill.cast(sdk.skills.SlowMissiles, sdk.skills.hand.Right, unit); + } + } + } + } + + if (allowThrowing && Skill.canUse(sdk.skills.LightningFury) && unit.getEnchant(sdk.enchant.ManaBurn) && unit.getMobCount(7) > 2) { + useSkills.LightFury = true; + } + + if (allowThrowing && Skill.canUse(sdk.skills.PlagueJavelin) && unit.getEnchant(sdk.enchant.ManaBurn) && unit.getMobCount(7) > 2) { + forcePlague = true; + } + + if (useSkills.InnerSight) { + if (!unit.getState(sdk.states.InnerSight) && unit.distance > 3 && unit.distance < 13 && !checkCollision(me, unit, sdk.collision.Ranged)) { + Skill.cast(sdk.skills.InnerSight, sdk.skills.hand.Right, unit); + } + } + + // Handle Switch casting + let commonCheck = (gold > 500000 || unit.isBoss || [sdk.areas.ChaosSanctuary, sdk.areas.ThroneofDestruction].includes(me.area)); + if (me.expansion && index === 1 && unit.curseable) { + if (CharData.skillData.haveChargedSkillOnSwitch(sdk.skills.LowerResist) + && !unit.getState(sdk.states.LowerResist) && commonCheck && !checkCollision(me, unit, sdk.collision.Ranged)) { + // Switch cast lower resist + Attack.switchCastCharges(sdk.skills.LowerResist, unit); + } + + if (CharData.skillData.haveChargedSkillOnSwitch(sdk.skills.Weaken) && !unit.getState(sdk.states.Weaken) + && !unit.getState(sdk.states.LowerResist) && commonCheck && !checkCollision(me, unit, sdk.collision.Ranged)) { + // Switch cast weaken + Attack.switchCastCharges(sdk.skills.Weaken, unit); + } + } + + // specials and dolls for now, should make dolls much less dangerous with the reduction of their damage + if (Precast.haveCTA > -1 && unit.curseable && (index === 1 || unit.isDoll) + && unit.distance < 5 && !unit.getState(sdk.states.BattleCry) && unit.curseable) { + Skill.switchCast(sdk.skills.BattleCry, { oSkill: true }); + } + + if (useSkills.Decoy) { + // Act Bosses or Immune to my main boss skill + if ((unit.isPrimeEvil) || !Attack.checkResist(unit, Config.AttackSkill[1])) { + Misc.poll(() => !me.skillDelay, 1000, 40); + + // Don't use decoy if within melee distance + if (unit.distance > 4) { + // Check to see if decoy has already been cast + let decoy = Misc.poll(() => Game.getMonster(sdk.summons.Dopplezon), 1000, 10); + + if (!decoy && (getTickCount() - this.decoyTick >= decoyDuration) && unit.distance > 4) { + if (unit.distance > 10 || checkCollision(me, unit, sdk.collision.Ranged)) { + if (!Attack.getIntoPosition(unit, 10, sdk.collision.Ranged)) { + return Attack.Result.FAILED; + } + } + + let coord = CollMap.getRandCoordinate(unit.x, -2, 2, unit.y, -2, 2); + !!coord && Skill.cast(sdk.skills.Decoy, sdk.skills.hand.Right, coord.x, coord.y); + + // Check if it was a sucess + !!me.getMinionCount(sdk.summons.type.Dopplezon) && (this.decoyTick = getTickCount()); + } + } + } + } + + // Only try attacking light immunes if I have my end game javelin - preAttack with Plague Javelin + if ((useSkills.Plague) && !Attack.checkResist(unit, "lightning")) { + if ((unit.distance <= 15) && !checkCollision(me, unit, sdk.collision.Ranged)) { + // Cast Slow-Missles, then proceed with Plague Jav. Lowers amount of damage from projectiles. + !unit.getState(sdk.states.SlowMissiles) && useSkills.SlowMissiles && Skill.cast(sdk.skills.SlowMissiles, sdk.skills.hand.Right, unit); + + // Handle Switch casting + if (!unit.dead) { + if (CharData.skillData.haveChargedSkillOnSwitch(sdk.skills.LowerResist) + && !unit.getState(sdk.states.LowerResist) && unit.curseable && commonCheck && !checkCollision(me, unit, sdk.collision.Ranged)) { + // Switch cast lower resist + Attack.switchCastCharges(sdk.skills.LowerResist, unit); + } + } + + if (Attack.checkResist(unit, "poison") && !me.skillDelay && !unit.dead) { + Skill.cast(sdk.skills.PlagueJavelin, Skill.getHand(sdk.skills.PlagueJavelin), unit); + } + + if (!useSkills.Jab) { + // We are within melee distance might as well use jab rather than stand there + // Make sure monster is not physical immune + if (unit.distance < 4 && Attack.checkResist(unit, "physical")) { + if (Skill.canUse(sdk.skills.Jab)) { + if (unit.distance > 3 || checkCollision(me, unit, sdk.collision.Ranged)) { + if (!Attack.getIntoPosition(unit, 3, sdk.collision.BlockWall)) { + return Attack.Result.FAILED; + } + } + + !unit.dead && Skill.cast(sdk.skills.Jab, Skill.getHand(sdk.skills.Jab), unit); + + return Attack.Result.SUCCESS; + } + } + + return Attack.Result.SUCCESS; + } + } + } + + // Only try attacking immunes if I have my end game javelin and they aren't lightning enchanted - use jab as main attack + if (useSkills.Jab && !Attack.checkResist(unit, Config.AttackSkill[1]) && Attack.checkResist(unit, "physical") && !unit.getEnchant(sdk.enchant.LightningEnchanted)) { + if ((unit.distance > 3 || checkCollision(me, unit, sdk.collision.Ranged)) && !Attack.getIntoPosition(unit, 3, sdk.collision.BlockWall)) { + return Attack.Result.FAILED; + } + + !unit.dead && Skill.cast(sdk.skills.Jab, Skill.getHand(sdk.skills.Jab), unit); + + return Attack.Result.SUCCESS; + } + + if (forcePlague && Attack.checkResist(unit, "poison") && !unit.getState(sdk.states.Poison) && !me.skillDelay) { + if ((unit.distance >= 8 && unit.distance <= 15) && !checkCollision(me, unit, sdk.collision.Ranged)) { + Skill.cast(sdk.skills.PlagueJavelin, Skill.getHand(sdk.skills.PlagueJavelin), unit); + } + } + + if (useSkills.LightFury) { + if ((unit.distance >= 8 && unit.distance <= 15) && !checkCollision(me, unit, sdk.collision.Ranged)) { + Skill.cast(sdk.skills.LightningFury, Skill.getHand(sdk.skills.LightningFury), unit); + } + } + + if (preattack && Config.AttackSkill[0] > 0 && [sdk.skills.InnerSight, sdk.skills.SlowMissiles].indexOf(Config.AttackSkill[0]) === -1 + && Attack.checkResist(unit, Config.AttackSkill[0]) && (!me.skillDelay || !Skill.isTimed(Config.AttackSkill[0]))) { + if (unit.distance > preattackRange || checkCollision(me, unit, sdk.collision.Ranged)) { + if (!Attack.getIntoPosition(unit, preattackRange, sdk.collision.Ranged)) { + return Attack.Result.FAILED; + } + } + + Skill.cast(Config.AttackSkill[0], Skill.getHand(Config.AttackSkill[0]), unit); + + return Attack.Result.SUCCESS; + } + + let mercRevive = 0; + let skills = this.decideSkill(unit); + + const switchBowAttack = (unit, attackSkill) => { + if (Attack.getIntoPosition(unit, 20, sdk.collision.Ranged)) { + try { + const checkForShamans = unit.isFallen && !me.inArea(sdk.areas.BloodMoor); + for (let i = 0; i < 5 && unit.attackable; i++) { + if (checkForShamans && !once) { + // before we waste time let's see if there is a shaman we should kill + const shaman = getUnits(sdk.unittype.Monster) + .filter(mon => mon.distance < 20 && mon.isShaman && mon.attackable) + .sort((a, b) => a.distance - b.distance).first(); + if (shaman) return ClassAttack.doAttack(shaman, null, true); + } + if (!Attack.useBowOnSwitch(unit, attackSkill, i === 5)) return Attack.Result.FAILED; + if (unit.distance < 8 || me.inDanger()) { + if (once) return Attack.Result.FAILED; + let closeMob = getUnits(sdk.unittype.Monster) + .filter(mon => mon.distance < 10 && mon.attackable && mon.gid !== gid) + .sort(Attack.walkingSortMonsters).first(); + if (closeMob) return ClassAttack.doAttack(closeMob, null, true); + } + } + } finally { + me.weaponswitch !== sdk.player.slot.Main && me.switchWeapons(sdk.player.slot.Main); + } + } + return unit.dead ? Attack.Result.SUCCESS : Attack.Result.FAILED; + }; + + // @todo damage/effort comparison vs our normal skill + if (CharData.skillData.bow.onSwitch + && (index !== 1 || !unit.name.includes(getLocaleString(sdk.locale.text.Ghostly))) + && (unit.distance >= 8 || (unit.isMoving && (unit.targetx !== me.x || unit.targety !== me.y))) + && ([sdk.skills.Attack, sdk.skills.Jab].includes(skills.timed) || Skill.getManaCost(skills.timed) > me.mp)) { + let arrowSkill = (() => { + // todo - better determination of skills + if (Skill.canUse(sdk.skills.MagicArrow) && Skill.getManaCost(sdk.skills.MagicArrow) < me.mp) return sdk.skills.MagicArrow; + if (Skill.canUse(sdk.skills.FireArrow) && Skill.getManaCost(sdk.skills.FireArrow) < me.mp) return sdk.skills.FireArrow; + return 0; + })(); + if (switchBowAttack(unit, arrowSkill) === Attack.Result.SUCCESS) return Attack.Result.SUCCESS; + } + + if ([sdk.skills.Attack, sdk.skills.Jab].includes(skills.timed) + && (unit.distance >= 12 || (unit.distance > 4 && unit.isMoving && (unit.targetx !== me.x || unit.targety !== me.y)) || unit.coldEnchanted)) { + let item = me.getItemsEx().filter(item => item.isEquipped && item.bodylocation === sdk.body.RightArm).first(); + if (item && (item.getStat(sdk.stats.Quantity) * 100 / getBaseStat("items", item.classid, "maxstack")) > 30) { + skills.timed = sdk.skills.Throw; + } + } + + let result = this.doCast(unit, skills.timed, skills.untimed); + + if (result === Attack.Result.CANTATTACK && Attack.canTeleStomp(unit)) { + let merc = me.getMerc(); + + while (unit.attackable) { + if (!unit) return Attack.Result.SUCCESS; + + if (me.needMerc()) { + if (Config.MercWatch && mercRevive++ < 1) { + Town.visitTown(); + } else { + return Attack.Result.CANTATTACK; + } + + (merc === undefined || !merc) && (merc = me.getMerc()); + } + + if (!!merc && getDistance(merc, unit) > 5) { + Pather.moveToUnit(unit); + + let spot = Attack.findSafeSpot(unit, 10, 5, 9); + !!spot && Pather.walkTo(spot.x, spot.y); + } + + let closeMob = Attack.getNearestMonster({ skipGid: gid }); + + if (!!closeMob) { + let findSkill = this.decideSkill(closeMob); + (this.doCast(closeMob, findSkill.timed, findSkill.untimed) === 1) || (Skill.canUse(sdk.skills.Decoy) && Skill.cast(sdk.skills.Decoy, sdk.skills.hand.Right, unit)); + } + } + + return Attack.Result.SUCCESS; + } + + return result; }; ClassAttack.afterAttack = function () { - Precast.doPrecast(false); + Precast.doPrecast(false); - let needRepair = me.needRepair(); - - // Repair check, make sure i have a tome - if (needRepair.length > 0 && me.canTpToTown()) { - Town.visitTown(true); - } + let needRepair = me.needRepair(); + + // Repair check, make sure i have a tome + if (needRepair.length > 0 && me.canTpToTown()) { + Town.visitTown(true); + } - this.lightFuryTick = 0; + this.lightFuryTick = 0; }; /** @@ -342,104 +342,104 @@ ClassAttack.afterAttack = function () { * @returns {AttackResult} */ ClassAttack.doCast = function (unit, timedSkill, untimedSkill) { - // No valid skills can be found - if (timedSkill < 0 && untimedSkill < 0) return Attack.Result.CANTATTACK; - // unit became invalidated - if (!unit || !unit.attackable) return Attack.Result.SUCCESS; - me.weaponswitch !== sdk.player.slot.Main && me.switchWeapons(sdk.player.slot.Main); - - let walk; - - // Arrow/bolt check - if (this.bowCheck) { - switch (true) { - case this.bowCheck === "bow" && !me.getItem("aqv", sdk.items.mode.Equipped): - case this.bowCheck === "crossbow" && !me.getItem("cqv", sdk.items.mode.Equipped): - console.log("Bow check"); - Town.visitTown(); - - break; - } - } - - if (timedSkill > -1 && (!me.getState(sdk.states.SkillDelay) || !Skill.isTimed(timedSkill))) { - switch (timedSkill) { - case sdk.skills.Throw: - case sdk.skills.PlagueJavelin: - case sdk.skills.LightningFury: - if (timedSkill === sdk.skills.LightningFury && this.lightFuryTick && getTickCount() - this.lightFuryTick < Time.seconds(Config.LightningFuryDelay)) { - break; - } - let tsRange = timedSkill === sdk.skills.Throw && (unit.isShaman || unit.isUnraveler) ? Skill.getRange(timedSkill) - 5 : Skill.getRange(timedSkill); - if (unit.distance > Skill.getRange(tsRange) || checkCollision(me, unit, sdk.collision.BlockMissile)) { - if (!Attack.getIntoPosition(unit, Skill.getRange(tsRange), sdk.collision.BlockMissile)) { - return Attack.Result.FAILED; - } - } - - let preHealth = unit.hp; - let targetPoint = GameData.targetPointForSkill(timedSkill, unit); - - if (unit.attackable) { - if (targetPoint) { - Skill.cast(timedSkill, Skill.getHand(timedSkill), targetPoint.x, targetPoint.y); - } else { - Skill.cast(timedSkill, Skill.getHand(timedSkill), unit); - } - if (Misc.poll(() => unit.dead || unit.hp < preHealth, 300, 50)) { - timedSkill === sdk.skills.LightningFury && (this.lightFuryTick = getTickCount()); - } - } - - break; - default: - // If main attack skill is lightning strike and charged strike's skill level is at least level 15, check current monster count. If monster count is less than 3, use CS as its more effective with small mobs - if (timedSkill === sdk.skills.LightningStrike && me.getSkill(sdk.skills.ChargedStrike, sdk.skills.subindex.SoftPoints) >= 15) { - if (me.getMobCount(15, Coords_1.BlockBits.LineOfSight | Coords_1.BlockBits.Ranged | Coords_1.BlockBits.ClosedDoor | Coords_1.BlockBits.BlockWall) <= 3) { - timedSkill = sdk.skills.ChargedStrike; - } - } - - if (Skill.getRange(timedSkill) < 4 && !Attack.validSpot(unit.x, unit.y)) return Attack.Result.FAILED; - - if (unit.distance > Skill.getRange(timedSkill) || checkCollision(me, unit, sdk.collision.Ranged)) { - // Allow short-distance walking for melee skills - walk = Skill.getRange(timedSkill) < 4 && unit.distance < 10 && !checkCollision(me, unit, sdk.collision.BlockWall); - - if (!Attack.getIntoPosition(unit, Skill.getRange(timedSkill), sdk.collision.Ranged, walk)) { - return Attack.Result.FAILED; - } - } - - !unit.dead && Skill.cast(timedSkill, Skill.getHand(timedSkill), unit); - - return Attack.Result.SUCCESS; - } - } - - if (untimedSkill > -1) { - if (Skill.getRange(untimedSkill) < 4 && !Attack.validSpot(unit.x, unit.y)) return Attack.Result.FAILED; - - if (unit.distance > Skill.getRange(untimedSkill) || checkCollision(me, unit, sdk.collision.Ranged)) { - // Allow short-distance walking for melee skills - walk = Skill.getRange(untimedSkill) < 4 && unit.distance < 10 && !checkCollision(me, unit, sdk.collision.BlockWall); - - if (!Attack.getIntoPosition(unit, Skill.getRange(untimedSkill), sdk.collision.Ranged, walk)) { - return Attack.Result.FAILED; - } - } - - !unit.dead && Skill.cast(untimedSkill, Skill.getHand(untimedSkill), unit); - - return Attack.Result.SUCCESS; - } - - Misc.poll(() => !me.skillDelay, 1000, 40); - - // Wait for Lightning Fury timeout - while (this.lightFuryTick && getTickCount() - this.lightFuryTick < Config.LightningFuryDelay * 1000) { - delay(40); - } - - return Attack.Result.SUCCESS; + // No valid skills can be found + if (timedSkill < 0 && untimedSkill < 0) return Attack.Result.CANTATTACK; + // unit became invalidated + if (!unit || !unit.attackable) return Attack.Result.SUCCESS; + me.weaponswitch !== sdk.player.slot.Main && me.switchWeapons(sdk.player.slot.Main); + + let walk; + + // Arrow/bolt check + if (this.bowCheck) { + switch (true) { + case this.bowCheck === "bow" && !me.getItem("aqv", sdk.items.mode.Equipped): + case this.bowCheck === "crossbow" && !me.getItem("cqv", sdk.items.mode.Equipped): + console.log("Bow check"); + Town.visitTown(); + + break; + } + } + + if (timedSkill > -1 && (!me.getState(sdk.states.SkillDelay) || !Skill.isTimed(timedSkill))) { + switch (timedSkill) { + case sdk.skills.Throw: + case sdk.skills.PlagueJavelin: + case sdk.skills.LightningFury: + if (timedSkill === sdk.skills.LightningFury && this.lightFuryTick && getTickCount() - this.lightFuryTick < Time.seconds(Config.LightningFuryDelay)) { + break; + } + let tsRange = timedSkill === sdk.skills.Throw && (unit.isShaman || unit.isUnraveler) ? Skill.getRange(timedSkill) - 5 : Skill.getRange(timedSkill); + if (unit.distance > Skill.getRange(tsRange) || checkCollision(me, unit, sdk.collision.BlockMissile)) { + if (!Attack.getIntoPosition(unit, Skill.getRange(tsRange), sdk.collision.BlockMissile)) { + return Attack.Result.FAILED; + } + } + + let preHealth = unit.hp; + let targetPoint = GameData.targetPointForSkill(timedSkill, unit); + + if (unit.attackable) { + if (targetPoint) { + Skill.cast(timedSkill, Skill.getHand(timedSkill), targetPoint.x, targetPoint.y); + } else { + Skill.cast(timedSkill, Skill.getHand(timedSkill), unit); + } + if (Misc.poll(() => unit.dead || unit.hp < preHealth, 300, 50)) { + timedSkill === sdk.skills.LightningFury && (this.lightFuryTick = getTickCount()); + } + } + + break; + default: + // If main attack skill is lightning strike and charged strike's skill level is at least level 15, check current monster count. If monster count is less than 3, use CS as its more effective with small mobs + if (timedSkill === sdk.skills.LightningStrike && me.getSkill(sdk.skills.ChargedStrike, sdk.skills.subindex.SoftPoints) >= 15) { + if (me.getMobCount(15, Coords_1.BlockBits.LineOfSight | Coords_1.BlockBits.Ranged | Coords_1.BlockBits.ClosedDoor | Coords_1.BlockBits.BlockWall) <= 3) { + timedSkill = sdk.skills.ChargedStrike; + } + } + + if (Skill.getRange(timedSkill) < 4 && !Attack.validSpot(unit.x, unit.y)) return Attack.Result.FAILED; + + if (unit.distance > Skill.getRange(timedSkill) || checkCollision(me, unit, sdk.collision.Ranged)) { + // Allow short-distance walking for melee skills + walk = Skill.getRange(timedSkill) < 4 && unit.distance < 10 && !checkCollision(me, unit, sdk.collision.BlockWall); + + if (!Attack.getIntoPosition(unit, Skill.getRange(timedSkill), sdk.collision.Ranged, walk)) { + return Attack.Result.FAILED; + } + } + + !unit.dead && Skill.cast(timedSkill, Skill.getHand(timedSkill), unit); + + return Attack.Result.SUCCESS; + } + } + + if (untimedSkill > -1) { + if (Skill.getRange(untimedSkill) < 4 && !Attack.validSpot(unit.x, unit.y)) return Attack.Result.FAILED; + + if (unit.distance > Skill.getRange(untimedSkill) || checkCollision(me, unit, sdk.collision.Ranged)) { + // Allow short-distance walking for melee skills + walk = Skill.getRange(untimedSkill) < 4 && unit.distance < 10 && !checkCollision(me, unit, sdk.collision.BlockWall); + + if (!Attack.getIntoPosition(unit, Skill.getRange(untimedSkill), sdk.collision.Ranged, walk)) { + return Attack.Result.FAILED; + } + } + + !unit.dead && Skill.cast(untimedSkill, Skill.getHand(untimedSkill), unit); + + return Attack.Result.SUCCESS; + } + + Misc.poll(() => !me.skillDelay, 1000, 40); + + // Wait for Lightning Fury timeout + while (this.lightFuryTick && getTickCount() - this.lightFuryTick < Config.LightningFuryDelay * 1000) { + delay(40); + } + + return Attack.Result.SUCCESS; }; diff --git a/libs/SoloPlay/Functions/ClassAttackOverrides/AssassinAttacks.js b/libs/SoloPlay/Functions/ClassAttackOverrides/AssassinAttacks.js index 109ff63e..bfb45d7b 100644 --- a/libs/SoloPlay/Functions/ClassAttackOverrides/AssassinAttacks.js +++ b/libs/SoloPlay/Functions/ClassAttackOverrides/AssassinAttacks.js @@ -13,266 +13,266 @@ includeIfNotIncluded("core/Attacks/Assassin.js"); ClassAttack.mindBlast = function (unit) { - if (!unit || !Skill.canUse(sdk.skills.MindBlast)) return; - // Main bosses - if (unit.isPrimeEvil) return; - // Duriel's Lair, Arreat Summit, Worldstone Chamber - if ([sdk.areas.DurielsLair, sdk.areas.ArreatSummit, sdk.areas.WorldstoneChamber].includes(me.area)) return; - - const mindBlastMpCost = Skill.getManaCost(sdk.skills.MindBlast); - let list = getUnits(sdk.unittype.Monster) - .filter(function (mob) { - if (mob.attackable && !mob.isStunned && !mob.isUnderLowerRes && !mob.isUnique) { - let dist = mob.distance; - return (dist <= 6 || (dist >= 20 && dist <= 30)); - } - return false; - }) - .sort(Sort.units); - - if (list.length >= 1) { - for (let i = 0; i < list.length; i++) { - if (!list[i].dead && !checkCollision(me, list[i], sdk.collision.BlockWall) && me.mp > mindBlastMpCost * 2) { - me.overhead("MindBlasting " + list[i].name); - Skill.cast(sdk.skills.MindBlast, sdk.skills.hand.Right, list[i]); - } - } - } + if (!unit || !Skill.canUse(sdk.skills.MindBlast)) return; + // Main bosses + if (unit.isPrimeEvil) return; + // Duriel's Lair, Arreat Summit, Worldstone Chamber + if ([sdk.areas.DurielsLair, sdk.areas.ArreatSummit, sdk.areas.WorldstoneChamber].includes(me.area)) return; + + const mindBlastMpCost = Skill.getManaCost(sdk.skills.MindBlast); + let list = getUnits(sdk.unittype.Monster) + .filter(function (mob) { + if (mob.attackable && !mob.isStunned && !mob.isUnderLowerRes && !mob.isUnique) { + let dist = mob.distance; + return (dist <= 6 || (dist >= 20 && dist <= 30)); + } + return false; + }) + .sort(Sort.units); + + if (list.length >= 1) { + for (let i = 0; i < list.length; i++) { + if (!list[i].dead && !checkCollision(me, list[i], sdk.collision.BlockWall) && me.mp > mindBlastMpCost * 2) { + me.overhead("MindBlasting " + list[i].name); + Skill.cast(sdk.skills.MindBlast, sdk.skills.hand.Right, list[i]); + } + } + } }; ClassAttack.switchCurse = function (unit, force) { - if (CharData.skillData.haveChargedSkill([sdk.skills.SlowMissiles, sdk.skills.LowerResist, sdk.skills.Weaken]) && unit.curseable) { - const gold = me.gold; - const isBoss = unit.isBoss; - const dangerZone = [sdk.areas.ChaosSanctuary, sdk.areas.ThroneofDestruction].includes(me.area); - if (force && checkCollision(me, unit, sdk.collision.Ranged)) { - if (!Attack.getIntoPosition(unit, 35, sdk.collision.Ranged)) return; - } - // If we have slow missles we might as well use it, currently only on Lighting Enchanted mobs as they are dangerous - // Might be worth it to use on souls too TODO: test this idea - if (CharData.skillData.haveChargedSkill(sdk.skills.SlowMissiles) && gold > 500000 && !isBoss - && unit.getEnchant(sdk.enchant.LightningEnchanted) && !unit.getState(sdk.states.SlowMissiles) - && !checkCollision(me, unit, sdk.collision.Ranged)) { - // Cast slow missiles - Attack.castCharges(sdk.skills.SlowMissiles, unit); - } - // Handle Switch casting - if (CharData.skillData.haveChargedSkillOnSwitch(sdk.skills.LowerResist) - && (gold > 500000 || isBoss || dangerZone) - && !unit.getState(sdk.states.LowerResist) - && !checkCollision(me, unit, sdk.collision.Ranged)) { - // Switch cast lower resist - Attack.switchCastCharges(sdk.skills.LowerResist, unit); - } - - if (CharData.skillData.haveChargedSkillOnSwitch(sdk.skills.Weaken) - && (gold > 500000 || isBoss || dangerZone) - && !unit.getState(sdk.states.Weaken) && !unit.getState(sdk.states.LowerResist) - && !checkCollision(me, unit, sdk.collision.Ranged)) { - // Switch cast weaken - Attack.switchCastCharges(sdk.skills.Weaken, unit); - } - } + if (CharData.skillData.haveChargedSkill([sdk.skills.SlowMissiles, sdk.skills.LowerResist, sdk.skills.Weaken]) && unit.curseable) { + const gold = me.gold; + const isBoss = unit.isBoss; + const dangerZone = [sdk.areas.ChaosSanctuary, sdk.areas.ThroneofDestruction].includes(me.area); + if (force && checkCollision(me, unit, sdk.collision.Ranged)) { + if (!Attack.getIntoPosition(unit, 35, sdk.collision.Ranged)) return; + } + // If we have slow missles we might as well use it, currently only on Lighting Enchanted mobs as they are dangerous + // Might be worth it to use on souls too TODO: test this idea + if (CharData.skillData.haveChargedSkill(sdk.skills.SlowMissiles) && gold > 500000 && !isBoss + && unit.getEnchant(sdk.enchant.LightningEnchanted) && !unit.getState(sdk.states.SlowMissiles) + && !checkCollision(me, unit, sdk.collision.Ranged)) { + // Cast slow missiles + Attack.castCharges(sdk.skills.SlowMissiles, unit); + } + // Handle Switch casting + if (CharData.skillData.haveChargedSkillOnSwitch(sdk.skills.LowerResist) + && (gold > 500000 || isBoss || dangerZone) + && !unit.getState(sdk.states.LowerResist) + && !checkCollision(me, unit, sdk.collision.Ranged)) { + // Switch cast lower resist + Attack.switchCastCharges(sdk.skills.LowerResist, unit); + } + + if (CharData.skillData.haveChargedSkillOnSwitch(sdk.skills.Weaken) + && (gold > 500000 || isBoss || dangerZone) + && !unit.getState(sdk.states.Weaken) && !unit.getState(sdk.states.LowerResist) + && !checkCollision(me, unit, sdk.collision.Ranged)) { + // Switch cast weaken + Attack.switchCastCharges(sdk.skills.Weaken, unit); + } + } }; ClassAttack.placeTraps = function (unit, amount) { - let traps = 0; - - this.lastTrapPos = { x: unit.x, y: unit.y }; - - for (let i = -1; i <= 1; i += 1) { - for (let j = -1; j <= 1; j += 1) { - // Used for X formation - if (Math.abs(i) === Math.abs(j)) { - // Unit can be an object with x, y props too, that's why having "mode" prop is checked - if (traps >= amount || (unit.hasOwnProperty("mode") && unit.dead)) return true; - - // Duriel, Mephisto, Diablo, Baal, other players - if ((unit.hasOwnProperty("classid") && [sdk.monsters.Duriel, sdk.monsters.Mephisto, sdk.monsters.Diablo, sdk.monsters.Baal].includes(unit.classid)) - || (unit.hasOwnProperty("type") && unit.isPlayer)) { - if (traps >= Config.BossTraps.length) { - return true; - } - - Skill.cast(Config.BossTraps[traps], sdk.skills.hand.Right, unit.x + i, unit.y + j); - } else { - if (traps >= Config.Traps.length) return true; - - switch (Config.Traps[traps]) { - case sdk.skills.ChargedBoltSentry: - case sdk.skills.LightningSentry: - // Immune to lightning but not immune to fire, use fire trap if available - if (!Attack.checkResist(unit, "lightning") && Attack.checkResist(unit, "fire")) { - if (Skill.canUse(sdk.skills.WakeofFire)) { - Skill.cast(sdk.skills.WakeofFire, sdk.skills.hand.Right, unit.x + i, unit.y + j); - } else if (Skill.canUse(sdk.skills.WakeofInferno)) { - Skill.cast(sdk.skills.WakeofInferno, sdk.skills.hand.Right, unit.x + i, unit.y + j); - } - - break; - } else { - Skill.cast(Config.Traps[traps], sdk.skills.hand.Right, unit.x + i, unit.y + j); - } - - break; - case sdk.skills.WakeofFire: - case sdk.skills.WakeofInferno: - // Immune to fire but not immune to lightning, use light trap if available - if (!Attack.checkResist(unit, "fire") && Attack.checkResist(unit, "lightning")) { - if (Skill.canUse(sdk.skills.LightningSentry)) { - Skill.cast(sdk.skills.LightningSentry, sdk.skills.hand.Right, unit.x + i, unit.y + j); - } else if (Skill.canUse(sdk.skills.ChargedBoltSentry)) { - Skill.cast(sdk.skills.ChargedBoltSentry, sdk.skills.hand.Right, unit.x + i, unit.y + j); - } - - break; - } else { - Skill.cast(Config.Traps[traps], sdk.skills.hand.Right, unit.x + i, unit.y + j); - } - - break; - default: - Skill.cast(Config.Traps[traps], sdk.skills.hand.Right, unit.x + i, unit.y + j); - - break; - } - } - - traps += 1; - } - } - } - - return true; + let traps = 0; + + this.lastTrapPos = { x: unit.x, y: unit.y }; + + for (let i = -1; i <= 1; i += 1) { + for (let j = -1; j <= 1; j += 1) { + // Used for X formation + if (Math.abs(i) === Math.abs(j)) { + // Unit can be an object with x, y props too, that's why having "mode" prop is checked + if (traps >= amount || (unit.hasOwnProperty("mode") && unit.dead)) return true; + + // Duriel, Mephisto, Diablo, Baal, other players + if ((unit.hasOwnProperty("classid") && [sdk.monsters.Duriel, sdk.monsters.Mephisto, sdk.monsters.Diablo, sdk.monsters.Baal].includes(unit.classid)) + || (unit.hasOwnProperty("type") && unit.isPlayer)) { + if (traps >= Config.BossTraps.length) { + return true; + } + + Skill.cast(Config.BossTraps[traps], sdk.skills.hand.Right, unit.x + i, unit.y + j); + } else { + if (traps >= Config.Traps.length) return true; + + switch (Config.Traps[traps]) { + case sdk.skills.ChargedBoltSentry: + case sdk.skills.LightningSentry: + // Immune to lightning but not immune to fire, use fire trap if available + if (!Attack.checkResist(unit, "lightning") && Attack.checkResist(unit, "fire")) { + if (Skill.canUse(sdk.skills.WakeofFire)) { + Skill.cast(sdk.skills.WakeofFire, sdk.skills.hand.Right, unit.x + i, unit.y + j); + } else if (Skill.canUse(sdk.skills.WakeofInferno)) { + Skill.cast(sdk.skills.WakeofInferno, sdk.skills.hand.Right, unit.x + i, unit.y + j); + } + + break; + } else { + Skill.cast(Config.Traps[traps], sdk.skills.hand.Right, unit.x + i, unit.y + j); + } + + break; + case sdk.skills.WakeofFire: + case sdk.skills.WakeofInferno: + // Immune to fire but not immune to lightning, use light trap if available + if (!Attack.checkResist(unit, "fire") && Attack.checkResist(unit, "lightning")) { + if (Skill.canUse(sdk.skills.LightningSentry)) { + Skill.cast(sdk.skills.LightningSentry, sdk.skills.hand.Right, unit.x + i, unit.y + j); + } else if (Skill.canUse(sdk.skills.ChargedBoltSentry)) { + Skill.cast(sdk.skills.ChargedBoltSentry, sdk.skills.hand.Right, unit.x + i, unit.y + j); + } + + break; + } else { + Skill.cast(Config.Traps[traps], sdk.skills.hand.Right, unit.x + i, unit.y + j); + } + + break; + default: + Skill.cast(Config.Traps[traps], sdk.skills.hand.Right, unit.x + i, unit.y + j); + + break; + } + } + + traps += 1; + } + } + } + + return true; }; ClassAttack.doAttack = function (unit, preattack) { - if (!unit) return Attack.Result.SUCCESS; - let gid = unit.gid; - - if (Config.MercWatch && me.needMerc()) { - console.log("mercwatch"); - - if (Town.visitTown()) { - // lost reference to the mob we were attacking - if (!unit || !copyUnit(unit).x || !Game.getMonster(-1, -1, gid) || unit.dead) { - return Attack.Result.SUCCESS; - } - } - } - - let mercRevive = 0; - let shouldUseCloak = (Skill.canUse(sdk.skills.CloakofShadows) && !unit.isUnderLowerRes && unit.getMobCount(15, sdk.collision.BlockWall) > 1); - const index = (unit.isSpecial || unit.isPlayer) ? 1 : 3; - - this.mindBlast(unit); - - if (preattack && Config.AttackSkill[0] > 0 && Attack.checkResist(unit, Config.AttackSkill[0]) && (!me.skillDelay || !Skill.isTimed(Config.AttackSkill[0]))) { - if (unit.distance > Skill.getRange(Config.AttackSkill[0]) || checkCollision(me, unit, sdk.collision.Ranged)) { - if (!Attack.getIntoPosition(unit, Skill.getRange(Config.AttackSkill[0]), sdk.collision.Ranged)) { - return Attack.Result.FAILED; - } - } - - Skill.cast(Config.AttackSkill[0], Skill.getHand(Config.AttackSkill[0]), unit); - - return Attack.Result.SUCCESS; - } - - // Cloak of Shadows (Aggressive) - can't be cast again until previous one runs out and next to useless if cast in precast sequence (won't blind anyone) - if (Config.AggressiveCloak && Config.UseCloakofShadows && shouldUseCloak && !me.skillDelay && !me.getState(sdk.states.CloakofShadows)) { - if (unit.distance < 20) { - Skill.cast(sdk.skills.CloakofShadows, sdk.skills.hand.Right); - } else if (!Attack.getIntoPosition(unit, 20, sdk.collision.Ranged)) { - return Attack.Result.FAILED; - } - } - - let checkTraps = this.checkTraps(unit); - - if (checkTraps) { - if (unit.distance > this.trapRange || checkCollision(me, unit, sdk.collision.Ranged)) { - if (!Attack.getIntoPosition(unit, this.trapRange, sdk.collision.Ranged) || (checkCollision(me, unit, sdk.collision.BlockWall) && (getCollision(me.area, unit.x, unit.y) & sdk.collision.BlockWall))) { - return Attack.Result.FAILED; - } - } - - this.placeTraps(unit, checkTraps); - } - - // Cloak of Shadows (Defensive; default) - can't be cast again until previous one runs out and next to useless if cast in precast sequence (won't blind anyone) - if (!Config.AggressiveCloak && Config.UseCloakofShadows && shouldUseCloak && unit.distance < 20 && !me.skillDelay && !me.getState(sdk.states.CloakofShadows)) { - Skill.cast(sdk.skills.CloakofShadows, sdk.skills.hand.Right); - } - - // Handle Switch casting - if (index === 1 && !unit.dead) { - ClassAttack.switchCurse(unit); - } - - let skills = Attack.decideSkill(unit); - let result = this.doCast(unit, skills.timed, skills.untimed); - - if (result === Attack.Result.CANTATTACK && Attack.canTeleStomp(unit)) { - let merc = me.getMerc(); - - while (unit.attackable) { - if (!unit) return Attack.Result.SUCCESS; - - if (me.needMerc()) { - if (Config.MercWatch && mercRevive++ < 1) { - Town.visitTown(); - } else { - return Attack.Result.CANTATTACK; - } - - (merc === undefined || !merc) && (merc = me.getMerc()); - } - - if (!!merc && getDistance(merc, unit) > 5) { - Pather.moveToUnit(unit); - - let spot = Attack.findSafeSpot(unit, 10, 5, 9); - !!spot && Pather.walkTo(spot.x, spot.y); - } - - let closeMob = Attack.getNearestMonster({ skipGid: gid }); - !!closeMob && this.doCast(closeMob, skills.timed, skills.untimed); - } - - return Attack.Result.SUCCESS; - } - - return result; + if (!unit) return Attack.Result.SUCCESS; + let gid = unit.gid; + + if (Config.MercWatch && me.needMerc()) { + console.log("mercwatch"); + + if (Town.visitTown()) { + // lost reference to the mob we were attacking + if (!unit || !copyUnit(unit).x || !Game.getMonster(-1, -1, gid) || unit.dead) { + return Attack.Result.SUCCESS; + } + } + } + + let mercRevive = 0; + let shouldUseCloak = (Skill.canUse(sdk.skills.CloakofShadows) && !unit.isUnderLowerRes && unit.getMobCount(15, sdk.collision.BlockWall) > 1); + const index = (unit.isSpecial || unit.isPlayer) ? 1 : 3; + + this.mindBlast(unit); + + if (preattack && Config.AttackSkill[0] > 0 && Attack.checkResist(unit, Config.AttackSkill[0]) && (!me.skillDelay || !Skill.isTimed(Config.AttackSkill[0]))) { + if (unit.distance > Skill.getRange(Config.AttackSkill[0]) || checkCollision(me, unit, sdk.collision.Ranged)) { + if (!Attack.getIntoPosition(unit, Skill.getRange(Config.AttackSkill[0]), sdk.collision.Ranged)) { + return Attack.Result.FAILED; + } + } + + Skill.cast(Config.AttackSkill[0], Skill.getHand(Config.AttackSkill[0]), unit); + + return Attack.Result.SUCCESS; + } + + // Cloak of Shadows (Aggressive) - can't be cast again until previous one runs out and next to useless if cast in precast sequence (won't blind anyone) + if (Config.AggressiveCloak && Config.UseCloakofShadows && shouldUseCloak && !me.skillDelay && !me.getState(sdk.states.CloakofShadows)) { + if (unit.distance < 20) { + Skill.cast(sdk.skills.CloakofShadows, sdk.skills.hand.Right); + } else if (!Attack.getIntoPosition(unit, 20, sdk.collision.Ranged)) { + return Attack.Result.FAILED; + } + } + + let checkTraps = this.checkTraps(unit); + + if (checkTraps) { + if (unit.distance > this.trapRange || checkCollision(me, unit, sdk.collision.Ranged)) { + if (!Attack.getIntoPosition(unit, this.trapRange, sdk.collision.Ranged) || (checkCollision(me, unit, sdk.collision.BlockWall) && (getCollision(me.area, unit.x, unit.y) & sdk.collision.BlockWall))) { + return Attack.Result.FAILED; + } + } + + this.placeTraps(unit, checkTraps); + } + + // Cloak of Shadows (Defensive; default) - can't be cast again until previous one runs out and next to useless if cast in precast sequence (won't blind anyone) + if (!Config.AggressiveCloak && Config.UseCloakofShadows && shouldUseCloak && unit.distance < 20 && !me.skillDelay && !me.getState(sdk.states.CloakofShadows)) { + Skill.cast(sdk.skills.CloakofShadows, sdk.skills.hand.Right); + } + + // Handle Switch casting + if (index === 1 && !unit.dead) { + ClassAttack.switchCurse(unit); + } + + let skills = Attack.decideSkill(unit); + let result = this.doCast(unit, skills.timed, skills.untimed); + + if (result === Attack.Result.CANTATTACK && Attack.canTeleStomp(unit)) { + let merc = me.getMerc(); + + while (unit.attackable) { + if (!unit) return Attack.Result.SUCCESS; + + if (me.needMerc()) { + if (Config.MercWatch && mercRevive++ < 1) { + Town.visitTown(); + } else { + return Attack.Result.CANTATTACK; + } + + (merc === undefined || !merc) && (merc = me.getMerc()); + } + + if (!!merc && getDistance(merc, unit) > 5) { + Pather.moveToUnit(unit); + + let spot = Attack.findSafeSpot(unit, 10, 5, 9); + !!spot && Pather.walkTo(spot.x, spot.y); + } + + let closeMob = Attack.getNearestMonster({ skipGid: gid }); + !!closeMob && this.doCast(closeMob, skills.timed, skills.untimed); + } + + return Attack.Result.SUCCESS; + } + + return result; }; ClassAttack.farCast = function (unit) { - let timedSkill = Config.AttackSkill[1], untimedSkill = Config.AttackSkill[2]; + let timedSkill = Config.AttackSkill[1], untimedSkill = Config.AttackSkill[2]; - // No valid skills can be found - if (timedSkill < 0 && untimedSkill < 0) return false; + // No valid skills can be found + if (timedSkill < 0 && untimedSkill < 0) return false; - let checkTraps = this.checkTraps(unit); + let checkTraps = this.checkTraps(unit); - if (checkTraps) { - if (unit.distance > 30 || checkCollision(me, unit, sdk.collision.Ranged)) { - if (!Attack.getIntoPosition(unit, 30, sdk.collision.Ranged) || (checkCollision(me, unit, sdk.collision.BlockWall) && (getCollision(me.area, unit.x, unit.y) & sdk.collision.BlockWall))) { - return false; - } - } + if (checkTraps) { + if (unit.distance > 30 || checkCollision(me, unit, sdk.collision.Ranged)) { + if (!Attack.getIntoPosition(unit, 30, sdk.collision.Ranged) || (checkCollision(me, unit, sdk.collision.BlockWall) && (getCollision(me.area, unit.x, unit.y) & sdk.collision.BlockWall))) { + return false; + } + } - this.placeTraps(unit, checkTraps); - } + this.placeTraps(unit, checkTraps); + } - ClassAttack.switchCurse(unit); + ClassAttack.switchCurse(unit); - if (timedSkill > -1 && (!me.skillDelay || !Skill.isTimed(timedSkill))) { - !unit.dead && Skill.cast(timedSkill, Skill.getHand(timedSkill), unit); - } + if (timedSkill > -1 && (!me.skillDelay || !Skill.isTimed(timedSkill))) { + !unit.dead && Skill.cast(timedSkill, Skill.getHand(timedSkill), unit); + } - if (untimedSkill > -1) { - !unit.dead && Skill.cast(untimedSkill, Skill.getHand(untimedSkill), unit); - } + if (untimedSkill > -1) { + !unit.dead && Skill.cast(untimedSkill, Skill.getHand(untimedSkill), unit); + } - return true; + return true; }; diff --git a/libs/SoloPlay/Functions/ClassAttackOverrides/BarbarianAttacks.js b/libs/SoloPlay/Functions/ClassAttackOverrides/BarbarianAttacks.js index e9613c47..d5ac024f 100644 --- a/libs/SoloPlay/Functions/ClassAttackOverrides/BarbarianAttacks.js +++ b/libs/SoloPlay/Functions/ClassAttackOverrides/BarbarianAttacks.js @@ -14,439 +14,439 @@ includeIfNotIncluded("core/Attacks/Barbarian.js"); * - use leap/leap attack with dodge, useful if we can't tele it provides a similar benefit */ (function () { - ClassAttack.warCryTick = 0; - - const howlCheck = function () { - let levelCheck = (me.getSkill(sdk.skills.Howl, sdk.skills.subindex.SoftPoints) + me.charlvl + 1); - return getUnits(sdk.unittype.Monster).filter(function (el) { - return (!!el && el.attackable && el.distance < 6 && el.scareable && GameData.monsterLevel(el.classid, me.area) < levelCheck && !el.isStunned - && [sdk.states.BattleCry, sdk.states.AmplifyDamage, sdk.states.Decrepify, sdk.states.Terror, sdk.states.Taunt].every(state => !el.getState(state)) - && !checkCollision(me, el, Coords_1.Collision.BLOCK_MISSILE)); - }).length > me.maxNearMonsters; - }; - - const battleCryCheck = function () { - return getUnits(sdk.unittype.Monster).some(function (el) { - if (el === undefined) return false; - return (el.attackable && el.distance < 5 && el.curseable - && [sdk.states.BattleCry, sdk.states.AmplifyDamage, sdk.states.Decrepify, sdk.states.Terror, sdk.states.Taunt].every(state => !el.getState(state)) - && !checkCollision(me, el, Coords_1.Collision.BLOCK_MISSILE)); - }); - }; - - const warCryCheck = function () { - return getUnits(sdk.unittype.Monster).some(function (el) { - if (el === undefined) return false; - return (el.attackable && el.distance < 5 && !(el.isSpecial) && el.curseable - && ![sdk.monsters.Andariel, sdk.monsters.Duriel, sdk.monsters.Mephisto, sdk.monsters.Diablo, sdk.monsters.Baal, sdk.monsters.Tentacle1, - sdk.monsters.BaalClone, sdk.monsters.KorlictheProtector, sdk.monsters.TalictheDefender, sdk.monsters.MadawctheGuardian].includes(el.classid) - && (!el.isStunned || getTickCount() - ClassAttack.warCryTick >= 1500) && !checkCollision(me, el, Coords_1.Collision.BLOCK_MISSILE)); - }); - }; - - ClassAttack.tauntMonsters = function (unit, attackSkill, data) { - // Don't have skill - // Only mob in these areas are bosses - // Can't taunt Main bosses or MinionsofDestruction - if (!Skill.canUse(sdk.skills.Taunt) || !data) return; - if ([sdk.areas.DurielsLair, sdk.areas.ArreatSummit, sdk.areas.WorldstoneChamber].includes(me.area)) return; - if (unit.isPrimeEvil || unit.classid === sdk.monsters.ListerTheTormenter) return; - - let range = (!me.inArea(sdk.areas.ThroneofDestruction) ? 15 : 30); - let rangedMobsClassIDs = [ - sdk.monsters.Afflicted, sdk.monsters.Tainted, sdk.monsters.Misshapen1, sdk.monsters.Disfigured, sdk.monsters.Damned1, sdk.monsters.Gloam1, sdk.monsters.SwampGhost, - sdk.monsters.BurningSoul2, sdk.monsters.BlackSoul1, sdk.monsters.GhoulLord1, sdk.monsters.NightLord, sdk.monsters.DarkLord1, sdk.monsters.BloodLord1, - sdk.monsters.Banished, sdk.monsters.SkeletonArcher, sdk.monsters.ReturnedArcher1, sdk.monsters.BoneArcher1, sdk.monsters.BurningDeadArcher1, sdk.monsters.HorrorArcher1, - sdk.monsters.Sexton, sdk.monsters.Cantor, sdk.monsters.Heirophant1, sdk.monsters.DoomKnight, sdk.monsters.VenomLord1, sdk.monsters.Horror1, sdk.monsters.Horror2, - sdk.monsters.Horror3, sdk.monsters.Horror4, sdk.monsters.Horror5, sdk.monsters.Lord1, sdk.monsters.Lord2, sdk.monsters.Lord3, sdk.monsters.Lord4, - sdk.monsters.Lord4, sdk.monsters.Afflicted2, sdk.monsters.Tainted, sdk.monsters.Misshapen2, sdk.monsters.Disfigured2, sdk.monsters.Damned2, sdk.monsters.DarkShaman2, - sdk.monsters.DevilkinShaman, sdk.monsters.DarkShaman2, sdk.monsters.DarkLord2 - ]; - let dangerousAndSummoners = [ - sdk.monsters.Dominus2, sdk.monsters.Witch1, sdk.monsters.VileWitch2, sdk.monsters.Gloam2, sdk.monsters.BlackSoul2, sdk.monsters.BurningSoul1, - sdk.monsters.FallenShaman, sdk.monsters.CarverShaman2, sdk.monsters.DevilkinShaman2, sdk.monsters.DarkShaman1, sdk.monsters.HollowOne, sdk.monsters.Guardian1, - sdk.monsters.Unraveler1, sdk.monsters.Ancient1, sdk.monsters.BaalSubjectMummy, sdk.monsters.Council4, sdk.monsters.VenomLord2, sdk.monsters.Ancient2, - sdk.monsters.Ancient3, sdk.monsters.Succubusexp1, sdk.monsters.VileTemptress, sdk.monsters.StygianHarlot, sdk.monsters.Temptress1, sdk.monsters.Temptress2, - sdk.monsters.Dominus1, sdk.monsters.VileWitch1, sdk.monsters.StygianFury, sdk.monsters.Witch2, sdk.monsters.Witch3 - ]; - - [sdk.areas.RiverofFlame, sdk.areas.ChaosSanctuary].includes(me.area) && rangedMobsClassIDs.push(sdk.monsters.Strangler1, sdk.monsters.StormCaster1); - - let list = getUnits(sdk.unittype.Monster) - .filter(function (mob) { - return ([sdk.monsters.spectype.All, sdk.monsters.spectype.Minion].includes(mob.spectype) - && [sdk.states.BattleCry, sdk.states.Decrepify, sdk.states.Taunt].every(state => !mob.getState(state)) - && ((rangedMobsClassIDs.includes(mob.classid) && mob.distance <= range) || (dangerousAndSummoners.includes(mob.classid) && mob.distance <= 30))); - }) - .sort(Sort.units); - - if (list.length >= 1) { - for (let i = 0; i < list.length; i++) { - let currMob = list[i]; - if (battleCryCheck() && Skill.cast(sdk.skills.BattleCry, sdk.skills.hand.Right)) { - continue; - } - - if (data.howl.have && !data.warCry.have && data.howl.mana < me.mp && howlCheck()) { - Skill.cast(sdk.skills.Howl, sdk.skills.hand.Right); - } else if (data.warCry.have && data.warCry.mana < me.mp && warCryCheck()) { - Skill.cast(sdk.skills.WarCry, sdk.skills.hand.Right); - } - - if (!!currMob && !currMob.dead && [sdk.states.Terror, sdk.states.BattleCry, sdk.states.Decrepify, sdk.states.Taunt].every(state => !currMob.getState(state)) - && data.taunt.mana < me.mp && !Coords_1.isBlockedBetween(me, currMob)) { - me.overhead("Taunting: " + currMob.name + " | classid: " + currMob.classid); - Skill.cast(sdk.skills.Taunt, sdk.skills.hand.Right, currMob); - } - - this.doCast(unit, attackSkill, data); - } - } - }; - - /** - * @param {Monster} unit - * @param {boolean} preattack - * @returns {AttackResult} - */ - ClassAttack.doAttack = function (unit, preattack = false) { - if (unit === undefined || !unit || unit.dead) return true; - - let gid = unit.gid; - let needRepair = [], gold = me.gold; - me.charlvl >= 5 && (needRepair = me.needRepair()); - - if ((Config.MercWatch && me.needMerc()) || needRepair.length > 0) { - console.log("towncheck"); - - if (Town.visitTown(!!needRepair.length)) { - // lost reference to the mob we were attacking - if (!unit || !copyUnit(unit).x || !Game.getMonster(-1, -1, gid) || unit.dead) { - return Attack.Result.SUCCESS; - } - } - } - - const index = (unit.isSpecial || unit.isPlayer) ? 1 : 3; - let attackSkill = Attack.getCustomAttack(unit) ? Attack.getCustomAttack(unit)[0] : Config.AttackSkill[index]; - - if (!Attack.checkResist(unit, attackSkill)) { - attackSkill = -1; - - if (Config.AttackSkill[index + 1] > -1 && Skill.canUse(Config.AttackSkill[index + 1]) && Attack.checkResist(unit, Config.AttackSkill[index + 1])) { - attackSkill = Config.AttackSkill[index + 1]; - } - } - - if (me.expansion && index === 1 && !unit.dead) { - if (CharData.skillData.haveChargedSkill(sdk.skills.SlowMissiles) && unit.getEnchant(sdk.enchant.LightningEnchanted) && !unit.getState(sdk.states.SlowMissiles) && unit.curseable && - (gold > 500000 && !unit.isBoss) && !checkCollision(me, unit, sdk.collision.Ranged)) { - // Cast slow missiles - Attack.castCharges(sdk.skills.SlowMissiles, unit); - } - - if (CharData.skillData.haveChargedSkill(sdk.skills.InnerSight) && !unit.getState(sdk.states.InnerSight) && unit.curseable && - gold > 500000 && !checkCollision(me, unit, sdk.collision.Ranged)) { - // Cast slow missiles - Attack.castCharges(sdk.skills.InnerSight, unit); - } - } - - const buildDataObj = (skillId = -1, reqLvl = 1) => ({ - have: false, skill: skillId, range: Infinity, mana: Infinity, timed: false, reqLvl: reqLvl, - assignValues: function (range) { - this.have = Skill.canUse(this.skill); - if (!this.have) return; - this.range = range || Skill.getRange(this.skill); - this.mana = Skill.getManaCost(this.skill); - this.timed = Skill.isTimed(this.skill); - } - }); - const currLvl = me.charlvl; - const data = { - switchCast: false, - howl: buildDataObj(sdk.skills.Howl, 1), - bash: buildDataObj(sdk.skills.Bash, 1), - taunt: buildDataObj(sdk.skills.Taunt, 6), - leap: buildDataObj(sdk.skills.Leap, 6), - doubleSwing: buildDataObj(sdk.skills.DoubleSwing, 6), - stun: buildDataObj(sdk.skills.Stun, 12), - battleCry: buildDataObj(sdk.skills.BattleCry, 18), - concentrate: buildDataObj(sdk.skills.Concentrate, 18), - leapAttack: buildDataObj(sdk.skills.LeapAttack, 18), - grimWard: buildDataObj(sdk.skills.GrimWard, 24), - warCry: buildDataObj(sdk.skills.WarCry, 30), - whirlwind: buildDataObj(sdk.skills.Whirlwind, 30), - main: buildDataObj(Config.AttackSkill[index], 1), - secondary: buildDataObj(Config.AttackSkill[index + 1], 1), - }; - - // TODO: calculate damage values for physcial attacks - Object.keys(data).forEach(k => typeof data[k] === "object" && currLvl >= data[k].reqLvl && data[k].assignValues()); - // console.debug(data); - - // Low mana skill - if (Skill.getManaCost(attackSkill) > me.mp && Config.LowManaSkill[0] > -1 && Attack.checkResist(unit, Config.LowManaSkill[0])) { - attackSkill = Config.LowManaSkill[0]; - } - - if ([sdk.skills.DoubleSwing, sdk.skills.DoubleThrow, sdk.skills.Frenzy].includes(attackSkill) && !me.dualWielding || !Skill.canUse(attackSkill)) { - let oneHandSk = [data.bash, data.stun, data.concentrate, data.leapAttack, data.whirlwind] - .filter((skill) => skill.have && me.mp > skill.mana) - .sort((a, b) => GameData.physicalAttackDamage(b.skill) - GameData.physicalAttackDamage(a.skill)).first(); - attackSkill = oneHandSk ? oneHandSk.skill : 0; - } - - if (data.howl.have && attackSkill !== sdk.skills.Whirlwind && data.howl.mana < me.mp && howlCheck() && me.hpPercent <= 85) { - data.grimWard.have ? this.grimWard(6) : Skill.cast(sdk.skills.Howl, sdk.skills.hand.Right); - } - - data.taunt.have && this.tauntMonsters(unit, attackSkill, data); - - if (!unit.dead && data.battleCry.have && !me.skillDelay) { - // Unit not already in Battle Cry, decrepify, terror, or taunt state. Don't want to overwrite helpful cureses - if ([sdk.states.BattleCry, sdk.states.Decrepify, sdk.states.Terror, sdk.states.Taunt].every(state => !unit.getState(state))) { - if (unit.distance > data.battleCry.range || checkCollision(me, unit, sdk.collision.Ranged)) { - if (!Attack.getIntoPosition(unit, data.battleCry.range, sdk.collision.Ranged)) { - return Attack.Result.FAILED; - } - } - - if (unit.distance < data.battleCry.range) { - data.switchCast - ? Skill.switchCast(sdk.skills.BattleCry, { hand: sdk.skills.hand.Right, switchBack: !data.warCry.have }) - : Skill.cast(sdk.skills.BattleCry, sdk.skills.hand.Right); - } - } - } - - // TODO: write GameData.killableSummonsByWarCry - if (data.warCry.have && data.warCry.mana < me.mp && !me.skillDelay && warCryCheck()) { - data.switchCast ? Skill.switchCast(sdk.skills.WarCry, { hand: 0 }) : Skill.cast(sdk.skills.WarCry, sdk.skills.hand.Right, unit); - this.warCryTick = getTickCount(); - } - - // Probably going to get rid of preattack - if (preattack && Config.AttackSkill[0] > 0 && Config.AttackSkill[0] !== sdk.skills.WarCry && Skill.canUse(Config.AttackSkill[0]) - && Attack.checkResist(unit, Attack.getSkillElement(Config.AttackSkill[0])) && (Skill.getManaCost(Config.AttackSkill[0]) < me.mp) - && (!me.getState(sdk.states.SkillDelay) || !Skill.isTimed(Config.AttackSkill[0]))) { - if (unit.distance > Skill.getRange(Config.AttackSkill[0]) || checkCollision(me, unit, sdk.collision.Ranged)) { - if (!Attack.getIntoPosition(unit, Skill.getRange(Config.AttackSkill[0]), sdk.collision.Ranged)) { - return Attack.Result.FAILED; - } - } - - Skill.cast(Config.AttackSkill[0], Skill.getHand(Config.AttackSkill[0]), unit); - - return Attack.Result.SUCCESS; - } - - if (index === 1) { - if (data.howl.have && attackSkill !== sdk.skills.Whirlwind && data.howl.mana < me.mp && howlCheck()) { - data.grimWard.have ? this.grimWard(6) : !data.warCry.have ? Skill.cast(sdk.skills.Howl, Skill.getHand(sdk.skills.Howl)) : null; - } - } - - if (attackSkill === sdk.skills.DoubleThrow && (me.getWeaponQuantity() <= 3 || me.getWeaponQuantity(sdk.body.LeftArm) <= 3) && data.secondary.have) { - attackSkill = data.secondary.skill; - } - - // Telestomp with barb is pointless - return this.doCast(unit, attackSkill, data); - }; - - ClassAttack.doCast = function (unit, attackSkill, data) { - // In case of failing to switch back to main weapon slot - me.weaponswitch === 1 && me.switchWeapons(0); - // No attack skill - if (attackSkill < 0 || !data) return Attack.Result.CANTATTACK; - - let walk; - - switch (attackSkill) { - case sdk.skills.Whirlwind: - if (unit.distance > Skill.getRange(attackSkill) || checkCollision(me, unit, sdk.collision.BlockWall)) { - if (!Attack.getIntoPosition(unit, Skill.getRange(attackSkill), sdk.collision.BlockWall, 2)) { - return Attack.Result.FAILED; - } - } - - !unit.dead && Attack.whirlwind(unit); - - return Attack.Result.SUCCESS; - default: - if (Skill.getRange(attackSkill) < 4 && !Attack.validSpot(unit.x, unit.y, attackSkill, unit.classid)) { - return Attack.Result.FAILED; - } - - if (unit.distance > Skill.getRange(attackSkill) || checkCollision(me, unit, sdk.collision.Ranged)) { - walk = (Skill.getRange(attackSkill) < 4 && unit.distance < 10 && !checkCollision(me, unit, sdk.collision.BlockWall)); - - // think this should be re-written in pather with some form of leap pathing similar to teleport - // leap/leap attack is incredibly useful because we can leap straight to chaos or over mobs/doors/some walls ect - if (data.leapAttack.have && !checkCollision(me, unit, sdk.collision.BlockWall) && unit.distance > 6) { - Skill.cast(sdk.skills.LeapAttack, sdk.skills.hand.Right, unit.x, unit.y); - } - - if (!Attack.getIntoPosition(unit, Skill.getRange(attackSkill), sdk.collision.Ranged, walk)) { - return Attack.Result.FAILED; - } - } - - if (!unit.dead) { - Skill.cast(attackSkill, Skill.getHand(attackSkill), unit); - - if (!unit.dead && attackSkill === sdk.skills.Berserk && me.dualWielding - && Skill.canUse(sdk.skills.Frenzy) && unit.distance < 4 && !me.getState(sdk.states.Frenzy)) { - Skill.cast(sdk.skills.Frenzy, Skill.getHand(sdk.skills.Frenzy), unit); - } - - if (!unit.dead && attackSkill === sdk.skills.Berserk && data.concentrate.have && me.mp > data.concentrate.mana) { - Skill.cast(sdk.skills.Concentrate, Skill.getHand(sdk.skills.Concentrate), unit); - } - - // Remove this for now, needs more data calculations to decide if its actually worth using (% dmg, %crushing blow, # of mobs filtering phys immunes unless maybe we do ele dmg from something) - // if (useWhirl && !unit.dead && (me.getMobCount(6, Coords_1.Collision.BLOCK_MISSILE | Coords_1.BlockBits.BlockWall) >= 3 || ([156, 211, 242, 243, 544, 571].indexOf(unit.classid) > -1) && !me.hell)) { - // this.whirlwind(unit); - // } - } - - return Attack.Result.SUCCESS; - } - }; - - ClassAttack.afterAttack = function (pickit = false) { - Precast.doPrecast(false); - - let needRepair = me.charlvl < 5 ? [] : me.needRepair(); - - // Repair check, make sure i have a tome - if (needRepair.length > 0 && me.getItem(sdk.items.TomeofTownPortal)) { - Town.visitTown(true); - } - - pickit && this.findItem(10); - }; - - ClassAttack.findItemIgnoreGids = []; - ClassAttack.findItem = function (range = 10) { - if (!Config.FindItem || !Skill.canUse(sdk.skills.FindItem)) return false; - - Config.FindItemSwitch = (me.expansion && Precast.getBetterSlot(sdk.skills.FindItem)); - let pick = false; - let corpseList = []; - const { x: orgX, y: orgY } = me; - - MainLoop: - for (let i = 0; i < 3; i++) { - let corpse = Game.getMonster(); - - if (corpse) { - do { - if (corpse.dead && getDistance(corpse, orgX, orgY) <= range && this.checkCorpse(corpse)) { - corpseList.push(copyUnit(corpse)); - } - } while (corpse.getNext()); - } - - if (corpseList.length > 0) { - pick = true; - - while (corpseList.length > 0) { - if (this.checkCloseMonsters(10)) { - Config.FindItemSwitch && me.switchWeapons(Attack.getPrimarySlot()); - Attack.clearPos(me.x, me.y, 10, false); - - continue MainLoop; - } - - corpseList.sort(Sort.units); - const check = corpseList.shift(); - let attempted = false; - let invalidated = false; - // get the actual corpse rather than the copied unit - corpse = Game.getMonster(check.classid, sdk.monsters.mode.Dead, check.gid); - - if (this.checkCorpse(corpse)) { - if (corpse.distance > 30 || Coords_1.isBlockedBetween(me, corpse)) { - Pather.moveNearUnit(corpse, 5); - } - - Config.FindItemSwitch && me.switchWeapons(Attack.getPrimarySlot() ^ 1); - - CorpseLoop: - for (let j = 0; j < 3; j += 1) { - // sometimes corpse can become invalidated - necro summoned from it or baal wave clearing, ect - // this still doesn't seem to capture baal wave clearing - if (j > 0) { - corpse = Game.getMonster(check.classid, sdk.monsters.mode.Dead, check.gid); - if (!this.checkCorpse(corpse)) { - invalidated = true; - break; - } - } - // see if we can find a new position if we failed the first time - sometimes findItem is bugged - j > 0 && Attack.getIntoPosition(corpse, 5, sdk.collision.BlockWall, Pather.useTeleport(), true); - // only delay if we actually casted the skill - if (Skill.cast(sdk.skills.FindItem, sdk.skills.hand.Right, corpse)) { - let tick = getTickCount(); - attempted = true; - - while (getTickCount() - tick < 1000) { - if (corpse.getState(sdk.states.CorpseNoSelect)) { - Config.FastPick ? Pickit.fastPick() : Pickit.pickItems(range); - - break CorpseLoop; - } - - delay(10); - } - } - } - } - - if (attempted && !invalidated && corpse && !corpse.getState(sdk.states.CorpseNoSelect)) { - !me.inArea(sdk.areas.ThroneofDestruction) && D2Bot.printToConsole("Failed to hork " + JSON.stringify(corpse) + " at " + getAreaName(me.area)); - console.debug("Failed to hork " + JSON.stringify(corpse) + " at " + getAreaName(me.area)); - } - } - } - } - - Config.FindItemSwitch && me.weaponswitch === 1 && me.switchWeapons(Attack.getPrimarySlot()); - pick && Pickit.pickItems(); - - return true; - }; - - /** - * @param {Monster} unit - * @param {number} [range] - * @returns {boolean} - */ - ClassAttack.grimWard = function (unit, range = 10) { - if (!Skill.canUse(sdk.skills.GrimWard)) return false; - if (!unit || !unit.dead) return false; - - let corpseList = getUnits(sdk.unittype.Monster) - .filter(mon => mon.dead && mon.distance < 30 && getDistance(mon, unit) <= range && this.checkCorpse(mon)) - .sort(((a, b) => getDistance(a, unit) - getDistance(b, unit))); - - for (let corpse of corpseList) { - // corpseList uses copyUnit, so we need to get the actual corpse - let checkCorpse = Game.getMonster(corpse.classid, -1, corpse.gid); - - if (checkCorpse && this.checkCorpse(checkCorpse)) { - for (let j = 0; j < 3; j += 1) { - if (Skill.cast(sdk.skills.GrimWard, sdk.skills.hand.Right, checkCorpse)) { - if (Misc.poll(() => checkCorpse.getState(sdk.states.CorpseNoSelect), 1000)) { - return true; - } - } - } - } - } - - return false; - }; + ClassAttack.warCryTick = 0; + + const howlCheck = function () { + let levelCheck = (me.getSkill(sdk.skills.Howl, sdk.skills.subindex.SoftPoints) + me.charlvl + 1); + return getUnits(sdk.unittype.Monster).filter(function (el) { + return (!!el && el.attackable && el.distance < 6 && el.scareable && GameData.monsterLevel(el.classid, me.area) < levelCheck && !el.isStunned + && [sdk.states.BattleCry, sdk.states.AmplifyDamage, sdk.states.Decrepify, sdk.states.Terror, sdk.states.Taunt].every(state => !el.getState(state)) + && !checkCollision(me, el, Coords_1.Collision.BLOCK_MISSILE)); + }).length > me.maxNearMonsters; + }; + + const battleCryCheck = function () { + return getUnits(sdk.unittype.Monster).some(function (el) { + if (el === undefined) return false; + return (el.attackable && el.distance < 5 && el.curseable + && [sdk.states.BattleCry, sdk.states.AmplifyDamage, sdk.states.Decrepify, sdk.states.Terror, sdk.states.Taunt].every(state => !el.getState(state)) + && !checkCollision(me, el, Coords_1.Collision.BLOCK_MISSILE)); + }); + }; + + const warCryCheck = function () { + return getUnits(sdk.unittype.Monster).some(function (el) { + if (el === undefined) return false; + return (el.attackable && el.distance < 5 && !(el.isSpecial) && el.curseable + && ![sdk.monsters.Andariel, sdk.monsters.Duriel, sdk.monsters.Mephisto, sdk.monsters.Diablo, sdk.monsters.Baal, sdk.monsters.Tentacle1, + sdk.monsters.BaalClone, sdk.monsters.KorlictheProtector, sdk.monsters.TalictheDefender, sdk.monsters.MadawctheGuardian].includes(el.classid) + && (!el.isStunned || getTickCount() - ClassAttack.warCryTick >= 1500) && !checkCollision(me, el, Coords_1.Collision.BLOCK_MISSILE)); + }); + }; + + ClassAttack.tauntMonsters = function (unit, attackSkill, data) { + // Don't have skill + // Only mob in these areas are bosses + // Can't taunt Main bosses or MinionsofDestruction + if (!Skill.canUse(sdk.skills.Taunt) || !data) return; + if ([sdk.areas.DurielsLair, sdk.areas.ArreatSummit, sdk.areas.WorldstoneChamber].includes(me.area)) return; + if (unit.isPrimeEvil || unit.classid === sdk.monsters.ListerTheTormenter) return; + + let range = (!me.inArea(sdk.areas.ThroneofDestruction) ? 15 : 30); + let rangedMobsClassIDs = [ + sdk.monsters.Afflicted, sdk.monsters.Tainted, sdk.monsters.Misshapen1, sdk.monsters.Disfigured, sdk.monsters.Damned1, sdk.monsters.Gloam1, sdk.monsters.SwampGhost, + sdk.monsters.BurningSoul2, sdk.monsters.BlackSoul1, sdk.monsters.GhoulLord1, sdk.monsters.NightLord, sdk.monsters.DarkLord1, sdk.monsters.BloodLord1, + sdk.monsters.Banished, sdk.monsters.SkeletonArcher, sdk.monsters.ReturnedArcher1, sdk.monsters.BoneArcher1, sdk.monsters.BurningDeadArcher1, sdk.monsters.HorrorArcher1, + sdk.monsters.Sexton, sdk.monsters.Cantor, sdk.monsters.Heirophant1, sdk.monsters.DoomKnight, sdk.monsters.VenomLord1, sdk.monsters.Horror1, sdk.monsters.Horror2, + sdk.monsters.Horror3, sdk.monsters.Horror4, sdk.monsters.Horror5, sdk.monsters.Lord1, sdk.monsters.Lord2, sdk.monsters.Lord3, sdk.monsters.Lord4, + sdk.monsters.Lord4, sdk.monsters.Afflicted2, sdk.monsters.Tainted, sdk.monsters.Misshapen2, sdk.monsters.Disfigured2, sdk.monsters.Damned2, sdk.monsters.DarkShaman2, + sdk.monsters.DevilkinShaman, sdk.monsters.DarkShaman2, sdk.monsters.DarkLord2 + ]; + let dangerousAndSummoners = [ + sdk.monsters.Dominus2, sdk.monsters.Witch1, sdk.monsters.VileWitch2, sdk.monsters.Gloam2, sdk.monsters.BlackSoul2, sdk.monsters.BurningSoul1, + sdk.monsters.FallenShaman, sdk.monsters.CarverShaman2, sdk.monsters.DevilkinShaman2, sdk.monsters.DarkShaman1, sdk.monsters.HollowOne, sdk.monsters.Guardian1, + sdk.monsters.Unraveler1, sdk.monsters.Ancient1, sdk.monsters.BaalSubjectMummy, sdk.monsters.Council4, sdk.monsters.VenomLord2, sdk.monsters.Ancient2, + sdk.monsters.Ancient3, sdk.monsters.Succubusexp1, sdk.monsters.VileTemptress, sdk.monsters.StygianHarlot, sdk.monsters.Temptress1, sdk.monsters.Temptress2, + sdk.monsters.Dominus1, sdk.monsters.VileWitch1, sdk.monsters.StygianFury, sdk.monsters.Witch2, sdk.monsters.Witch3 + ]; + + [sdk.areas.RiverofFlame, sdk.areas.ChaosSanctuary].includes(me.area) && rangedMobsClassIDs.push(sdk.monsters.Strangler1, sdk.monsters.StormCaster1); + + let list = getUnits(sdk.unittype.Monster) + .filter(function (mob) { + return ([sdk.monsters.spectype.All, sdk.monsters.spectype.Minion].includes(mob.spectype) + && [sdk.states.BattleCry, sdk.states.Decrepify, sdk.states.Taunt].every(state => !mob.getState(state)) + && ((rangedMobsClassIDs.includes(mob.classid) && mob.distance <= range) || (dangerousAndSummoners.includes(mob.classid) && mob.distance <= 30))); + }) + .sort(Sort.units); + + if (list.length >= 1) { + for (let i = 0; i < list.length; i++) { + let currMob = list[i]; + if (battleCryCheck() && Skill.cast(sdk.skills.BattleCry, sdk.skills.hand.Right)) { + continue; + } + + if (data.howl.have && !data.warCry.have && data.howl.mana < me.mp && howlCheck()) { + Skill.cast(sdk.skills.Howl, sdk.skills.hand.Right); + } else if (data.warCry.have && data.warCry.mana < me.mp && warCryCheck()) { + Skill.cast(sdk.skills.WarCry, sdk.skills.hand.Right); + } + + if (!!currMob && !currMob.dead && [sdk.states.Terror, sdk.states.BattleCry, sdk.states.Decrepify, sdk.states.Taunt].every(state => !currMob.getState(state)) + && data.taunt.mana < me.mp && !Coords_1.isBlockedBetween(me, currMob)) { + me.overhead("Taunting: " + currMob.name + " | classid: " + currMob.classid); + Skill.cast(sdk.skills.Taunt, sdk.skills.hand.Right, currMob); + } + + this.doCast(unit, attackSkill, data); + } + } + }; + + /** + * @param {Monster} unit + * @param {boolean} preattack + * @returns {AttackResult} + */ + ClassAttack.doAttack = function (unit, preattack = false) { + if (unit === undefined || !unit || unit.dead) return true; + + let gid = unit.gid; + let needRepair = [], gold = me.gold; + me.charlvl >= 5 && (needRepair = me.needRepair()); + + if ((Config.MercWatch && me.needMerc()) || needRepair.length > 0) { + console.log("towncheck"); + + if (Town.visitTown(!!needRepair.length)) { + // lost reference to the mob we were attacking + if (!unit || !copyUnit(unit).x || !Game.getMonster(-1, -1, gid) || unit.dead) { + return Attack.Result.SUCCESS; + } + } + } + + const index = (unit.isSpecial || unit.isPlayer) ? 1 : 3; + let attackSkill = Attack.getCustomAttack(unit) ? Attack.getCustomAttack(unit)[0] : Config.AttackSkill[index]; + + if (!Attack.checkResist(unit, attackSkill)) { + attackSkill = -1; + + if (Config.AttackSkill[index + 1] > -1 && Skill.canUse(Config.AttackSkill[index + 1]) && Attack.checkResist(unit, Config.AttackSkill[index + 1])) { + attackSkill = Config.AttackSkill[index + 1]; + } + } + + if (me.expansion && index === 1 && !unit.dead) { + if (CharData.skillData.haveChargedSkill(sdk.skills.SlowMissiles) && unit.getEnchant(sdk.enchant.LightningEnchanted) && !unit.getState(sdk.states.SlowMissiles) && unit.curseable && + (gold > 500000 && !unit.isBoss) && !checkCollision(me, unit, sdk.collision.Ranged)) { + // Cast slow missiles + Attack.castCharges(sdk.skills.SlowMissiles, unit); + } + + if (CharData.skillData.haveChargedSkill(sdk.skills.InnerSight) && !unit.getState(sdk.states.InnerSight) && unit.curseable && + gold > 500000 && !checkCollision(me, unit, sdk.collision.Ranged)) { + // Cast slow missiles + Attack.castCharges(sdk.skills.InnerSight, unit); + } + } + + const buildDataObj = (skillId = -1, reqLvl = 1) => ({ + have: false, skill: skillId, range: Infinity, mana: Infinity, timed: false, reqLvl: reqLvl, + assignValues: function (range) { + this.have = Skill.canUse(this.skill); + if (!this.have) return; + this.range = range || Skill.getRange(this.skill); + this.mana = Skill.getManaCost(this.skill); + this.timed = Skill.isTimed(this.skill); + } + }); + const currLvl = me.charlvl; + const data = { + switchCast: false, + howl: buildDataObj(sdk.skills.Howl, 1), + bash: buildDataObj(sdk.skills.Bash, 1), + taunt: buildDataObj(sdk.skills.Taunt, 6), + leap: buildDataObj(sdk.skills.Leap, 6), + doubleSwing: buildDataObj(sdk.skills.DoubleSwing, 6), + stun: buildDataObj(sdk.skills.Stun, 12), + battleCry: buildDataObj(sdk.skills.BattleCry, 18), + concentrate: buildDataObj(sdk.skills.Concentrate, 18), + leapAttack: buildDataObj(sdk.skills.LeapAttack, 18), + grimWard: buildDataObj(sdk.skills.GrimWard, 24), + warCry: buildDataObj(sdk.skills.WarCry, 30), + whirlwind: buildDataObj(sdk.skills.Whirlwind, 30), + main: buildDataObj(Config.AttackSkill[index], 1), + secondary: buildDataObj(Config.AttackSkill[index + 1], 1), + }; + + // TODO: calculate damage values for physcial attacks + Object.keys(data).forEach(k => typeof data[k] === "object" && currLvl >= data[k].reqLvl && data[k].assignValues()); + // console.debug(data); + + // Low mana skill + if (Skill.getManaCost(attackSkill) > me.mp && Config.LowManaSkill[0] > -1 && Attack.checkResist(unit, Config.LowManaSkill[0])) { + attackSkill = Config.LowManaSkill[0]; + } + + if ([sdk.skills.DoubleSwing, sdk.skills.DoubleThrow, sdk.skills.Frenzy].includes(attackSkill) && !me.dualWielding || !Skill.canUse(attackSkill)) { + let oneHandSk = [data.bash, data.stun, data.concentrate, data.leapAttack, data.whirlwind] + .filter((skill) => skill.have && me.mp > skill.mana) + .sort((a, b) => GameData.physicalAttackDamage(b.skill) - GameData.physicalAttackDamage(a.skill)).first(); + attackSkill = oneHandSk ? oneHandSk.skill : 0; + } + + if (data.howl.have && attackSkill !== sdk.skills.Whirlwind && data.howl.mana < me.mp && howlCheck() && me.hpPercent <= 85) { + data.grimWard.have ? this.grimWard(6) : Skill.cast(sdk.skills.Howl, sdk.skills.hand.Right); + } + + data.taunt.have && this.tauntMonsters(unit, attackSkill, data); + + if (!unit.dead && data.battleCry.have && !me.skillDelay) { + // Unit not already in Battle Cry, decrepify, terror, or taunt state. Don't want to overwrite helpful cureses + if ([sdk.states.BattleCry, sdk.states.Decrepify, sdk.states.Terror, sdk.states.Taunt].every(state => !unit.getState(state))) { + if (unit.distance > data.battleCry.range || checkCollision(me, unit, sdk.collision.Ranged)) { + if (!Attack.getIntoPosition(unit, data.battleCry.range, sdk.collision.Ranged)) { + return Attack.Result.FAILED; + } + } + + if (unit.distance < data.battleCry.range) { + data.switchCast + ? Skill.switchCast(sdk.skills.BattleCry, { hand: sdk.skills.hand.Right, switchBack: !data.warCry.have }) + : Skill.cast(sdk.skills.BattleCry, sdk.skills.hand.Right); + } + } + } + + // TODO: write GameData.killableSummonsByWarCry + if (data.warCry.have && data.warCry.mana < me.mp && !me.skillDelay && warCryCheck()) { + data.switchCast ? Skill.switchCast(sdk.skills.WarCry, { hand: 0 }) : Skill.cast(sdk.skills.WarCry, sdk.skills.hand.Right, unit); + this.warCryTick = getTickCount(); + } + + // Probably going to get rid of preattack + if (preattack && Config.AttackSkill[0] > 0 && Config.AttackSkill[0] !== sdk.skills.WarCry && Skill.canUse(Config.AttackSkill[0]) + && Attack.checkResist(unit, Attack.getSkillElement(Config.AttackSkill[0])) && (Skill.getManaCost(Config.AttackSkill[0]) < me.mp) + && (!me.getState(sdk.states.SkillDelay) || !Skill.isTimed(Config.AttackSkill[0]))) { + if (unit.distance > Skill.getRange(Config.AttackSkill[0]) || checkCollision(me, unit, sdk.collision.Ranged)) { + if (!Attack.getIntoPosition(unit, Skill.getRange(Config.AttackSkill[0]), sdk.collision.Ranged)) { + return Attack.Result.FAILED; + } + } + + Skill.cast(Config.AttackSkill[0], Skill.getHand(Config.AttackSkill[0]), unit); + + return Attack.Result.SUCCESS; + } + + if (index === 1) { + if (data.howl.have && attackSkill !== sdk.skills.Whirlwind && data.howl.mana < me.mp && howlCheck()) { + data.grimWard.have ? this.grimWard(6) : !data.warCry.have ? Skill.cast(sdk.skills.Howl, Skill.getHand(sdk.skills.Howl)) : null; + } + } + + if (attackSkill === sdk.skills.DoubleThrow && (me.getWeaponQuantity() <= 3 || me.getWeaponQuantity(sdk.body.LeftArm) <= 3) && data.secondary.have) { + attackSkill = data.secondary.skill; + } + + // Telestomp with barb is pointless + return this.doCast(unit, attackSkill, data); + }; + + ClassAttack.doCast = function (unit, attackSkill, data) { + // In case of failing to switch back to main weapon slot + me.weaponswitch === 1 && me.switchWeapons(0); + // No attack skill + if (attackSkill < 0 || !data) return Attack.Result.CANTATTACK; + + let walk; + + switch (attackSkill) { + case sdk.skills.Whirlwind: + if (unit.distance > Skill.getRange(attackSkill) || checkCollision(me, unit, sdk.collision.BlockWall)) { + if (!Attack.getIntoPosition(unit, Skill.getRange(attackSkill), sdk.collision.BlockWall, 2)) { + return Attack.Result.FAILED; + } + } + + !unit.dead && Attack.whirlwind(unit); + + return Attack.Result.SUCCESS; + default: + if (Skill.getRange(attackSkill) < 4 && !Attack.validSpot(unit.x, unit.y, attackSkill, unit.classid)) { + return Attack.Result.FAILED; + } + + if (unit.distance > Skill.getRange(attackSkill) || checkCollision(me, unit, sdk.collision.Ranged)) { + walk = (Skill.getRange(attackSkill) < 4 && unit.distance < 10 && !checkCollision(me, unit, sdk.collision.BlockWall)); + + // think this should be re-written in pather with some form of leap pathing similar to teleport + // leap/leap attack is incredibly useful because we can leap straight to chaos or over mobs/doors/some walls ect + if (data.leapAttack.have && !checkCollision(me, unit, sdk.collision.BlockWall) && unit.distance > 6) { + Skill.cast(sdk.skills.LeapAttack, sdk.skills.hand.Right, unit.x, unit.y); + } + + if (!Attack.getIntoPosition(unit, Skill.getRange(attackSkill), sdk.collision.Ranged, walk)) { + return Attack.Result.FAILED; + } + } + + if (!unit.dead) { + Skill.cast(attackSkill, Skill.getHand(attackSkill), unit); + + if (!unit.dead && attackSkill === sdk.skills.Berserk && me.dualWielding + && Skill.canUse(sdk.skills.Frenzy) && unit.distance < 4 && !me.getState(sdk.states.Frenzy)) { + Skill.cast(sdk.skills.Frenzy, Skill.getHand(sdk.skills.Frenzy), unit); + } + + if (!unit.dead && attackSkill === sdk.skills.Berserk && data.concentrate.have && me.mp > data.concentrate.mana) { + Skill.cast(sdk.skills.Concentrate, Skill.getHand(sdk.skills.Concentrate), unit); + } + + // Remove this for now, needs more data calculations to decide if its actually worth using (% dmg, %crushing blow, # of mobs filtering phys immunes unless maybe we do ele dmg from something) + // if (useWhirl && !unit.dead && (me.getMobCount(6, Coords_1.Collision.BLOCK_MISSILE | Coords_1.BlockBits.BlockWall) >= 3 || ([156, 211, 242, 243, 544, 571].indexOf(unit.classid) > -1) && !me.hell)) { + // this.whirlwind(unit); + // } + } + + return Attack.Result.SUCCESS; + } + }; + + ClassAttack.afterAttack = function (pickit = false) { + Precast.doPrecast(false); + + let needRepair = me.charlvl < 5 ? [] : me.needRepair(); + + // Repair check, make sure i have a tome + if (needRepair.length > 0 && me.getItem(sdk.items.TomeofTownPortal)) { + Town.visitTown(true); + } + + pickit && this.findItem(10); + }; + + ClassAttack.findItemIgnoreGids = []; + ClassAttack.findItem = function (range = 10) { + if (!Config.FindItem || !Skill.canUse(sdk.skills.FindItem)) return false; + + Config.FindItemSwitch = (me.expansion && Precast.getBetterSlot(sdk.skills.FindItem)); + let pick = false; + let corpseList = []; + const { x: orgX, y: orgY } = me; + + MainLoop: + for (let i = 0; i < 3; i++) { + let corpse = Game.getMonster(); + + if (corpse) { + do { + if (corpse.dead && getDistance(corpse, orgX, orgY) <= range && this.checkCorpse(corpse)) { + corpseList.push(copyUnit(corpse)); + } + } while (corpse.getNext()); + } + + if (corpseList.length > 0) { + pick = true; + + while (corpseList.length > 0) { + if (this.checkCloseMonsters(10)) { + Config.FindItemSwitch && me.switchWeapons(Attack.getPrimarySlot()); + Attack.clearPos(me.x, me.y, 10, false); + + continue MainLoop; + } + + corpseList.sort(Sort.units); + const check = corpseList.shift(); + let attempted = false; + let invalidated = false; + // get the actual corpse rather than the copied unit + corpse = Game.getMonster(check.classid, sdk.monsters.mode.Dead, check.gid); + + if (this.checkCorpse(corpse)) { + if (corpse.distance > 30 || Coords_1.isBlockedBetween(me, corpse)) { + Pather.moveNearUnit(corpse, 5); + } + + Config.FindItemSwitch && me.switchWeapons(Attack.getPrimarySlot() ^ 1); + + CorpseLoop: + for (let j = 0; j < 3; j += 1) { + // sometimes corpse can become invalidated - necro summoned from it or baal wave clearing, ect + // this still doesn't seem to capture baal wave clearing + if (j > 0) { + corpse = Game.getMonster(check.classid, sdk.monsters.mode.Dead, check.gid); + if (!this.checkCorpse(corpse)) { + invalidated = true; + break; + } + } + // see if we can find a new position if we failed the first time - sometimes findItem is bugged + j > 0 && Attack.getIntoPosition(corpse, 5, sdk.collision.BlockWall, Pather.useTeleport(), true); + // only delay if we actually casted the skill + if (Skill.cast(sdk.skills.FindItem, sdk.skills.hand.Right, corpse)) { + let tick = getTickCount(); + attempted = true; + + while (getTickCount() - tick < 1000) { + if (corpse.getState(sdk.states.CorpseNoSelect)) { + Config.FastPick ? Pickit.fastPick() : Pickit.pickItems(range); + + break CorpseLoop; + } + + delay(10); + } + } + } + } + + if (attempted && !invalidated && corpse && !corpse.getState(sdk.states.CorpseNoSelect)) { + !me.inArea(sdk.areas.ThroneofDestruction) && D2Bot.printToConsole("Failed to hork " + JSON.stringify(corpse) + " at " + getAreaName(me.area)); + console.debug("Failed to hork " + JSON.stringify(corpse) + " at " + getAreaName(me.area)); + } + } + } + } + + Config.FindItemSwitch && me.weaponswitch === 1 && me.switchWeapons(Attack.getPrimarySlot()); + pick && Pickit.pickItems(); + + return true; + }; + + /** + * @param {Monster} unit + * @param {number} [range] + * @returns {boolean} + */ + ClassAttack.grimWard = function (unit, range = 10) { + if (!Skill.canUse(sdk.skills.GrimWard)) return false; + if (!unit || !unit.dead) return false; + + let corpseList = getUnits(sdk.unittype.Monster) + .filter(mon => mon.dead && mon.distance < 30 && getDistance(mon, unit) <= range && this.checkCorpse(mon)) + .sort(((a, b) => getDistance(a, unit) - getDistance(b, unit))); + + for (let corpse of corpseList) { + // corpseList uses copyUnit, so we need to get the actual corpse + let checkCorpse = Game.getMonster(corpse.classid, -1, corpse.gid); + + if (checkCorpse && this.checkCorpse(checkCorpse)) { + for (let j = 0; j < 3; j += 1) { + if (Skill.cast(sdk.skills.GrimWard, sdk.skills.hand.Right, checkCorpse)) { + if (Misc.poll(() => checkCorpse.getState(sdk.states.CorpseNoSelect), 1000)) { + return true; + } + } + } + } + } + + return false; + }; })(); diff --git a/libs/SoloPlay/Functions/ClassAttackOverrides/DruidAttacks.js b/libs/SoloPlay/Functions/ClassAttackOverrides/DruidAttacks.js index 2aea7f20..70b4aa1f 100644 --- a/libs/SoloPlay/Functions/ClassAttackOverrides/DruidAttacks.js +++ b/libs/SoloPlay/Functions/ClassAttackOverrides/DruidAttacks.js @@ -13,292 +13,292 @@ includeIfNotIncluded("core/Attacks/Druid.js"); (function () { - /** - * @constructor - * @param {number} skillId - * @param {number} reqLvl - * @param {number} range - */ - function ClassData (skillId = -1, range = 0) { - this.have = false; - this.skill = skillId; - this.range = range ? range : Skill.getRange(skillId); - this.mana = Infinity; - this.dmg = 0; - this.timed = Skill.isTimed(skillId); - this.reqLvl = getBaseStat("skills", skillId, "reqlevel"); - } - - /** - * Initialize data values - * @param {number} [range] - * @returns {void} - */ - ClassData.prototype.assignValues = function (range) { - this.have = Skill.canUse(this.skill); - if (!this.have) return; - this.range = range || Skill.getRange(this.skill); - this.mana = Skill.getManaCost(this.skill); - }; - - /** - * Calculate effective damage for a certain monster unit - * @param {Monster} unit - * @returns {void} - */ - ClassData.prototype.calcDmg = function (unit) { - if (!this.have) return; - this.dmg = GameData.avgSkillDamage(this.skill, unit); - }; - - const AttackData = { - "Attack": new ClassData(sdk.skills.Attack, 4), - "Firestorm": new ClassData(sdk.skills.Firestorm), - "MoltenBoulder": new ClassData(sdk.skills.MoltenBoulder), - "ArcticBlast": new ClassData(sdk.skills.ArcticBlast), - "Fissure": new ClassData(sdk.skills.Fissure), - "Twister": new ClassData(sdk.skills.Twister), - "Tornado": new ClassData(sdk.skills.Tornado), - "Volcano": new ClassData(sdk.skills.Volcano), - // "Hurricane": new ClassData(sdk.skills.Hurricane), - // "Armageddon": new ClassData(sdk.skills.Armageddon), - }; - - /** - * hacky for now - fire skills are wonky with the skill delays - * better solution might be tracking what was last cast and the actual - * delay of each - */ - AttackData.Firestorm.timed = false; - - /** - * The keys never change so this makes it easier to iterate without calling Object.keys each time - * @type {Array} - */ - const AttackDataKeys = Object.keys(AttackData); - - /** - * Helper function to re-init AttackData - * @param {number} currLvl - * @todo decide when AttackData need to be re-initialized becasue doing it every attack is a waste - */ - const initAttackData = (currLvl = me.charlvl) => { - AttackDataKeys.forEach(sk => { - if (currLvl >= AttackData[sk].reqLvl) { - AttackData[sk].assignValues(); - } - }); - }; - - /** - * Helper function to init damage value for unit - * @param {Monster} unit - */ - const setDamageValues = (unit) => { - AttackDataKeys.forEach(sk => { - if (AttackData[sk].have) { - AttackData[sk].calcDmg(unit); - } - }); - }; - - /** - * Check if this skill is the most damaging - * @param {ClassData} skill - * @returns {boolean} - */ - const isHighestDmg = (skill) => { - for (let key of AttackDataKeys) { - if (AttackData[key].dmg > skill.dmg) { - return false; - } - } - return true; - }; - - /** - * Used to handle times when there isn't a valid skill we can use, to prevent throwing error - */ - const DummyData = new ClassData(-1, -1); - - /** - * @param {Monster} unit - * @param {boolean} recheck - * @returns {AttackResult} - */ - ClassAttack.doAttack = function (unit, recheck) { - if (!unit) return Attack.Result.SUCCESS; - let gid = unit.gid; - - if (Config.MercWatch && me.needMerc()) { - console.log("mercwatch"); - - if (Town.visitTown()) { - // lost reference to the mob we were attacking - if (!unit || !copyUnit(unit).x || !Game.getMonster(-1, -1, gid) || unit.dead) { - return Attack.Result.SUCCESS; - } - } - } - - let mercRevive = 0; - let gold = me.gold; - const currLvl = me.charlvl; - const index = (unit.isSpecial || unit.isPlayer) ? 1 : 3; - - // maybe every couple attacks or just the first one? - Precast.doPrecast(); - - if (index === 1 && !unit.dead && unit.curseable) { - const commonCheck = (gold > 500000 || unit.isBoss || [sdk.areas.ChaosSanctuary, sdk.areas.ThroneofDestruction].includes(me.area)); - - if (CharData.skillData.haveChargedSkill(sdk.skills.SlowMissiles) && unit.getEnchant(sdk.enchant.LightningEnchanted) && !unit.getState(sdk.states.SlowMissiles) - && (gold > 500000 && !unit.isBoss) && !checkCollision(me, unit, sdk.collision.Ranged)) { - // Cast slow missiles - Attack.castCharges(sdk.skills.SlowMissiles, unit); - } - - if (CharData.skillData.haveChargedSkill(sdk.skills.InnerSight) && !unit.getState(sdk.states.InnerSight) - && gold > 500000 && !checkCollision(me, unit, sdk.collision.Ranged)) { - // Cast slow missiles - Attack.castCharges(sdk.skills.InnerSight, unit); - } - - if (CharData.skillData.haveChargedSkillOnSwitch(sdk.skills.Decrepify) - && !unit.getState(sdk.states.Decrepify) && commonCheck && !checkCollision(me, unit, sdk.collision.Ranged)) { - // Switch cast decrepify - Attack.switchCastCharges(sdk.skills.Decrepify, unit); - } - - if (CharData.skillData.haveChargedSkillOnSwitch(sdk.skills.Weaken) - && !unit.getState(sdk.states.Weaken) && !unit.getState(sdk.states.Decrepify) && commonCheck && !checkCollision(me, unit, sdk.collision.Ranged)) { - // Switch cast weaken - Attack.switchCastCharges(sdk.skills.Weaken, unit); - } - } - - // specials and dolls for now, should make dolls much less dangerous with the reduction of their damage - if (Precast.haveCTA > -1 && !unit.dead && (index === 1 || unit.isDoll) - && unit.distance < 5 && !unit.getState(sdk.states.BattleCry) && unit.curseable) { - Skill.switchCast(sdk.skills.BattleCry, { oSkill: true }); - } - - initAttackData(currLvl); - setDamageValues(unit); - - let selectedSkillKey = AttackDataKeys - .filter(k => AttackData[k].have && me.mp > AttackData[k].mana && (!AttackData[k].timed || !me.skillDelay)) - .sort((a, b) => AttackData[b].dmg - AttackData[a].dmg) - .first(); - if (!selectedSkillKey) return Attack.Result.FAILED; - - /** @type {ClassData} */ - let selectedSkill = typeof AttackData[selectedSkillKey] === "object" ? AttackData[selectedSkillKey] : DummyData; - - switch (selectedSkill.skill) { - case sdk.skills.Attack: - if (!me.normal || (me.charlvl > 6 && !me.checkForMobs({ range: 10, coll: (sdk.collision.BlockWall | sdk.collision.ClosedDoor) }))) { - selectedSkill = DummyData; - } - } - - // console.debug(AttackData); - // console.debug("Choose skill :: " + getSkillById(selectedSkill.skill) + " Damage: " + selectedSkill.dmg); - - let result = ClassAttack.doCast(unit, selectedSkill); - - if (result === Attack.Result.CANTATTACK && Attack.canTeleStomp(unit)) { - let merc = me.getMerc(); - - while (unit.attackable) { - if (!unit) return Attack.Result.SUCCESS; - - if (me.needMerc()) { - if (Config.MercWatch && mercRevive++ < 1) { - Town.visitTown(); - } else { - return Attack.Result.CANTATTACK; - } - - (merc === undefined || !merc) && (merc = me.getMerc()); - } - - if (!!merc && getDistance(merc, unit) > 5) { - Pather.moveToUnit(unit); - - let spot = Attack.findSafeSpot(unit, 10, 5, 9); - !!spot && Pather.walkTo(spot.x, spot.y); - } - - let closeMob = Attack.getNearestMonster({ skipGid: gid }); - !!closeMob && ClassAttack.doCast(closeMob, selectedSkill); - } - - return Attack.Result.SUCCESS; - } - - return result; - }; - - /** - * @param {Monster} unit - * @param {number} timedSkill - * @param {number} untimedSkill - * @returns {AttackResult} - */ - ClassAttack.doCast = function (unit, choosenSkill) { - let { skill, range, mana, timed } = choosenSkill; - // unit became invalidated - if (!unit || !unit.attackable) return Attack.Result.SUCCESS; - if (!!skill && me.mp < mana) { - return Attack.Result.NEEDMANA; - } - // No valid skills can be found - if (skill < 0) return Attack.Result.CANTATTACK; - - /** - * @todo handling targetting for fissure/molten moulder/volcano - */ - - if (range > 8 && me.inDanger()) { - Attack.getIntoPosition(unit, range + 1, Coords_1.Collision.BLOCK_MISSILE, true); - } - - if (!me.skillDelay || !timed) { - switch (skill) { - case sdk.skills.Tornado: - if (Math.ceil(unit.distance) > range || checkCollision(me, unit, sdk.collision.Ranged)) { - if (!Attack.getIntoPosition(unit, range, sdk.collision.Ranged)) { - return Attack.Result.FAILED; - } - } - - // Randomized x coord changes tornado path and prevents constant missing - if (!unit.dead) { - Skill.cast(skill, Skill.getHand(skill), unit.x + rand(-1, 1), unit.y); - } - - return Attack.Result.SUCCESS; - default: - if (range < 4 && !Attack.validSpot(unit.x, unit.y)) return Attack.Result.FAILED; - - if (Math.ceil(unit.distance) > range || checkCollision(me, unit, sdk.collision.Ranged)) { - // Allow short-distance walking for melee skills - let walk = range < 4 && unit.distance < 10 && !checkCollision(me, unit, sdk.collision.BlockWall); - - if (!Attack.getIntoPosition(unit, range, sdk.collision.Ranged, walk)) { - return Attack.Result.FAILED; - } - } - - !unit.dead && Skill.cast(skill, Skill.getHand(skill), unit); - - return Attack.Result.SUCCESS; - } - } - - Misc.poll(() => !me.skillDelay, 1000, 40); - - return Attack.Result.SUCCESS; - }; + /** + * @constructor + * @param {number} skillId + * @param {number} reqLvl + * @param {number} range + */ + function ClassData (skillId = -1, range = 0) { + this.have = false; + this.skill = skillId; + this.range = range ? range : Skill.getRange(skillId); + this.mana = Infinity; + this.dmg = 0; + this.timed = Skill.isTimed(skillId); + this.reqLvl = getBaseStat("skills", skillId, "reqlevel"); + } + + /** + * Initialize data values + * @param {number} [range] + * @returns {void} + */ + ClassData.prototype.assignValues = function (range) { + this.have = Skill.canUse(this.skill); + if (!this.have) return; + this.range = range || Skill.getRange(this.skill); + this.mana = Skill.getManaCost(this.skill); + }; + + /** + * Calculate effective damage for a certain monster unit + * @param {Monster} unit + * @returns {void} + */ + ClassData.prototype.calcDmg = function (unit) { + if (!this.have) return; + this.dmg = GameData.avgSkillDamage(this.skill, unit); + }; + + const AttackData = { + "Attack": new ClassData(sdk.skills.Attack, 4), + "Firestorm": new ClassData(sdk.skills.Firestorm), + "MoltenBoulder": new ClassData(sdk.skills.MoltenBoulder), + "ArcticBlast": new ClassData(sdk.skills.ArcticBlast), + "Fissure": new ClassData(sdk.skills.Fissure), + "Twister": new ClassData(sdk.skills.Twister), + "Tornado": new ClassData(sdk.skills.Tornado), + "Volcano": new ClassData(sdk.skills.Volcano), + // "Hurricane": new ClassData(sdk.skills.Hurricane), + // "Armageddon": new ClassData(sdk.skills.Armageddon), + }; + + /** + * hacky for now - fire skills are wonky with the skill delays + * better solution might be tracking what was last cast and the actual + * delay of each + */ + AttackData.Firestorm.timed = false; + + /** + * The keys never change so this makes it easier to iterate without calling Object.keys each time + * @type {Array} + */ + const AttackDataKeys = Object.keys(AttackData); + + /** + * Helper function to re-init AttackData + * @param {number} currLvl + * @todo decide when AttackData need to be re-initialized becasue doing it every attack is a waste + */ + const initAttackData = (currLvl = me.charlvl) => { + AttackDataKeys.forEach(sk => { + if (currLvl >= AttackData[sk].reqLvl) { + AttackData[sk].assignValues(); + } + }); + }; + + /** + * Helper function to init damage value for unit + * @param {Monster} unit + */ + const setDamageValues = (unit) => { + AttackDataKeys.forEach(sk => { + if (AttackData[sk].have) { + AttackData[sk].calcDmg(unit); + } + }); + }; + + /** + * Check if this skill is the most damaging + * @param {ClassData} skill + * @returns {boolean} + */ + const isHighestDmg = (skill) => { + for (let key of AttackDataKeys) { + if (AttackData[key].dmg > skill.dmg) { + return false; + } + } + return true; + }; + + /** + * Used to handle times when there isn't a valid skill we can use, to prevent throwing error + */ + const DummyData = new ClassData(-1, -1); + + /** + * @param {Monster} unit + * @param {boolean} recheck + * @returns {AttackResult} + */ + ClassAttack.doAttack = function (unit, recheck) { + if (!unit) return Attack.Result.SUCCESS; + let gid = unit.gid; + + if (Config.MercWatch && me.needMerc()) { + console.log("mercwatch"); + + if (Town.visitTown()) { + // lost reference to the mob we were attacking + if (!unit || !copyUnit(unit).x || !Game.getMonster(-1, -1, gid) || unit.dead) { + return Attack.Result.SUCCESS; + } + } + } + + let mercRevive = 0; + let gold = me.gold; + const currLvl = me.charlvl; + const index = (unit.isSpecial || unit.isPlayer) ? 1 : 3; + + // maybe every couple attacks or just the first one? + Precast.doPrecast(); + + if (index === 1 && !unit.dead && unit.curseable) { + const commonCheck = (gold > 500000 || unit.isBoss || [sdk.areas.ChaosSanctuary, sdk.areas.ThroneofDestruction].includes(me.area)); + + if (CharData.skillData.haveChargedSkill(sdk.skills.SlowMissiles) && unit.getEnchant(sdk.enchant.LightningEnchanted) && !unit.getState(sdk.states.SlowMissiles) + && (gold > 500000 && !unit.isBoss) && !checkCollision(me, unit, sdk.collision.Ranged)) { + // Cast slow missiles + Attack.castCharges(sdk.skills.SlowMissiles, unit); + } + + if (CharData.skillData.haveChargedSkill(sdk.skills.InnerSight) && !unit.getState(sdk.states.InnerSight) + && gold > 500000 && !checkCollision(me, unit, sdk.collision.Ranged)) { + // Cast slow missiles + Attack.castCharges(sdk.skills.InnerSight, unit); + } + + if (CharData.skillData.haveChargedSkillOnSwitch(sdk.skills.Decrepify) + && !unit.getState(sdk.states.Decrepify) && commonCheck && !checkCollision(me, unit, sdk.collision.Ranged)) { + // Switch cast decrepify + Attack.switchCastCharges(sdk.skills.Decrepify, unit); + } + + if (CharData.skillData.haveChargedSkillOnSwitch(sdk.skills.Weaken) + && !unit.getState(sdk.states.Weaken) && !unit.getState(sdk.states.Decrepify) && commonCheck && !checkCollision(me, unit, sdk.collision.Ranged)) { + // Switch cast weaken + Attack.switchCastCharges(sdk.skills.Weaken, unit); + } + } + + // specials and dolls for now, should make dolls much less dangerous with the reduction of their damage + if (Precast.haveCTA > -1 && !unit.dead && (index === 1 || unit.isDoll) + && unit.distance < 5 && !unit.getState(sdk.states.BattleCry) && unit.curseable) { + Skill.switchCast(sdk.skills.BattleCry, { oSkill: true }); + } + + initAttackData(currLvl); + setDamageValues(unit); + + let selectedSkillKey = AttackDataKeys + .filter(k => AttackData[k].have && me.mp > AttackData[k].mana && (!AttackData[k].timed || !me.skillDelay)) + .sort((a, b) => AttackData[b].dmg - AttackData[a].dmg) + .first(); + if (!selectedSkillKey) return Attack.Result.FAILED; + + /** @type {ClassData} */ + let selectedSkill = typeof AttackData[selectedSkillKey] === "object" ? AttackData[selectedSkillKey] : DummyData; + + switch (selectedSkill.skill) { + case sdk.skills.Attack: + if (!me.normal || (me.charlvl > 6 && !me.checkForMobs({ range: 10, coll: (sdk.collision.BlockWall | sdk.collision.ClosedDoor) }))) { + selectedSkill = DummyData; + } + } + + // console.debug(AttackData); + // console.debug("Choose skill :: " + getSkillById(selectedSkill.skill) + " Damage: " + selectedSkill.dmg); + + let result = ClassAttack.doCast(unit, selectedSkill); + + if (result === Attack.Result.CANTATTACK && Attack.canTeleStomp(unit)) { + let merc = me.getMerc(); + + while (unit.attackable) { + if (!unit) return Attack.Result.SUCCESS; + + if (me.needMerc()) { + if (Config.MercWatch && mercRevive++ < 1) { + Town.visitTown(); + } else { + return Attack.Result.CANTATTACK; + } + + (merc === undefined || !merc) && (merc = me.getMerc()); + } + + if (!!merc && getDistance(merc, unit) > 5) { + Pather.moveToUnit(unit); + + let spot = Attack.findSafeSpot(unit, 10, 5, 9); + !!spot && Pather.walkTo(spot.x, spot.y); + } + + let closeMob = Attack.getNearestMonster({ skipGid: gid }); + !!closeMob && ClassAttack.doCast(closeMob, selectedSkill); + } + + return Attack.Result.SUCCESS; + } + + return result; + }; + + /** + * @param {Monster} unit + * @param {number} timedSkill + * @param {number} untimedSkill + * @returns {AttackResult} + */ + ClassAttack.doCast = function (unit, choosenSkill) { + let { skill, range, mana, timed } = choosenSkill; + // unit became invalidated + if (!unit || !unit.attackable) return Attack.Result.SUCCESS; + if (!!skill && me.mp < mana) { + return Attack.Result.NEEDMANA; + } + // No valid skills can be found + if (skill < 0) return Attack.Result.CANTATTACK; + + /** + * @todo handling targetting for fissure/molten moulder/volcano + */ + + if (range > 8 && me.inDanger()) { + Attack.getIntoPosition(unit, range + 1, Coords_1.Collision.BLOCK_MISSILE, true); + } + + if (!me.skillDelay || !timed) { + switch (skill) { + case sdk.skills.Tornado: + if (Math.ceil(unit.distance) > range || checkCollision(me, unit, sdk.collision.Ranged)) { + if (!Attack.getIntoPosition(unit, range, sdk.collision.Ranged)) { + return Attack.Result.FAILED; + } + } + + // Randomized x coord changes tornado path and prevents constant missing + if (!unit.dead) { + Skill.cast(skill, Skill.getHand(skill), unit.x + rand(-1, 1), unit.y); + } + + return Attack.Result.SUCCESS; + default: + if (range < 4 && !Attack.validSpot(unit.x, unit.y)) return Attack.Result.FAILED; + + if (Math.ceil(unit.distance) > range || checkCollision(me, unit, sdk.collision.Ranged)) { + // Allow short-distance walking for melee skills + let walk = range < 4 && unit.distance < 10 && !checkCollision(me, unit, sdk.collision.BlockWall); + + if (!Attack.getIntoPosition(unit, range, sdk.collision.Ranged, walk)) { + return Attack.Result.FAILED; + } + } + + !unit.dead && Skill.cast(skill, Skill.getHand(skill), unit); + + return Attack.Result.SUCCESS; + } + } + + Misc.poll(() => !me.skillDelay, 1000, 40); + + return Attack.Result.SUCCESS; + }; })(); diff --git a/libs/SoloPlay/Functions/ClassAttackOverrides/PaladinAttacks.js b/libs/SoloPlay/Functions/ClassAttackOverrides/PaladinAttacks.js index e4d2d634..e0a6faed 100644 --- a/libs/SoloPlay/Functions/ClassAttackOverrides/PaladinAttacks.js +++ b/libs/SoloPlay/Functions/ClassAttackOverrides/PaladinAttacks.js @@ -12,401 +12,401 @@ includeIfNotIncluded("core/Attacks/Paladin.js"); */ const MercWatch = { - last: 0, + last: 0, }; // eslint-disable-next-line no-unused-vars ClassAttack.doAttack = function (unit = undefined, preattack = false, once = false) { - if (!unit || !unit.attackable) return Attack.Result.SUCCESS; - - let gid = unit.gid; - let mercRevive = 0; - let gold = me.gold; - let [attackSkill, aura] = [-1, -1]; - const index = (unit.isSpecial || unit.isPlayer) ? 1 : 3; - - // prevent running back to town quickly if our merc is just weak - if (Config.MercWatch && me.needMerc() && getTickCount() - MercWatch.last > Time.seconds(5)) { - console.log("mercwatch"); - - if (Town.visitTown()) { - // lost reference to the mob we were attacking - if (!unit || !copyUnit(unit).x || !Game.getMonster(-1, -1, gid) || unit.dead) { - return Attack.Result.SUCCESS; - } - } - MercWatch.last = getTickCount(); - gold = me.gold; // reset value after town - } - - if (me.expansion && index === 1 && unit.curseable) { - const commonCheck = (gold > 500000 || unit.isBoss || [sdk.areas.ChaosSanctuary, sdk.areas.ThroneofDestruction].includes(me.area)); - - if (CharData.skillData.haveChargedSkill(sdk.skills.SlowMissiles) - && unit.getEnchant(sdk.enchant.LightningEnchanted) && !unit.getState(sdk.states.SlowMissiles) - && (gold > 500000 && !unit.isBoss) && !checkCollision(me, unit, sdk.collision.Ranged)) { - // Cast slow missiles - Attack.castCharges(sdk.skills.SlowMissiles, unit); - } - - if (CharData.skillData.haveChargedSkill(sdk.skills.InnerSight) && !unit.getState(sdk.states.InnerSight) - && gold > 500000 && !checkCollision(me, unit, sdk.collision.Ranged)) { - // Cast Inner sight - Attack.castCharges(sdk.skills.InnerSight, unit); - } - - if (CharData.skillData.haveChargedSkillOnSwitch(sdk.skills.Decrepify) - && !unit.getState(sdk.states.Decrepify) && commonCheck && !checkCollision(me, unit, sdk.collision.Ranged)) { - // Switch cast decrepify - Attack.switchCastCharges(sdk.skills.Decrepify, unit); - } - - if (CharData.skillData.haveChargedSkillOnSwitch(sdk.skills.Weaken) - && !unit.getState(sdk.states.Weaken) && !unit.getState(sdk.states.Decrepify) && commonCheck && !checkCollision(me, unit, sdk.collision.Ranged)) { - // Switch cast weaken - Attack.switchCastCharges(sdk.skills.Weaken, unit); - } - } - - // specials and dolls for now, should make dolls much less dangerous with the reduction of their damage - if (Precast.haveCTA > -1 && !unit.dead && (index === 1 || unit.isDoll) - && unit.distance < 5 && !unit.getState(sdk.states.BattleCry) && unit.curseable) { - Skill.switchCast(sdk.skills.BattleCry, { oSkill: true }); - } - - if (Attack.getCustomAttack(unit)) { - [attackSkill, aura] = Attack.getCustomAttack(unit); - } else { - attackSkill = Config.AttackSkill[index]; - aura = Config.AttackSkill[index + 1]; - } - - // Classic auradin check - if (this.attackAuras.includes(aura)) { - // Monster immune to primary aura - if (!Attack.checkResist(unit, aura)) { - // Reset skills - [attackSkill, aura] = [-1, -1]; - - // Set to secondary if not immune, check if using secondary attack aura if not check main skill for immunity - if (Config.AttackSkill[5] > -1 && Attack.checkResist(unit, (this.attackAuras.includes(Config.AttackSkill[6]) ? Config.AttackSkill[6] : Config.AttackSkill[5]))) { - attackSkill = Config.AttackSkill[5]; - aura = Config.AttackSkill[6]; - } - } - } else { - // Monster immune to primary skill - if (!Attack.checkResist(unit, attackSkill)) { - // Reset skills - [attackSkill, aura] = [-1, -1]; - - // Set to secondary if not immune - if (Config.AttackSkill[5] > -1 && Attack.checkResist(unit, Config.AttackSkill[5])) { - attackSkill = Config.AttackSkill[5]; - aura = Config.AttackSkill[6]; - } - } - } - - if (attackSkill === sdk.skills.Attack && !unit.isFallen && Skill.canUse(sdk.skills.Sacrifice) && me.hpPercent > 75) { - attackSkill = sdk.skills.Sacrifice; - } - - // Low mana skill - if (Config.LowManaSkill[0] > -1 && Skill.getManaCost(attackSkill) > me.mp && Attack.checkResist(unit, Config.LowManaSkill[0])) { - [attackSkill, aura] = Config.LowManaSkill; - } - - /** - * @param {Monster} unit - * @returns {AttackResult} - */ - const switchBowAttack = (unit) => { - if (Attack.getIntoPosition(unit, 20, sdk.collision.Ranged)) { - try { - const checkForShamans = unit.isFallen && !me.inArea(sdk.areas.BloodMoor); - for (let i = 0; i < 5 && unit.attackable; i++) { - if (checkForShamans && !once) { - // before we waste time let's see if there is a shaman we should kill - const shaman = getUnits(sdk.unittype.Monster) - .filter(mon => mon.distance < 20 && mon.isShaman && mon.attackable) - .sort((a, b) => a.distance - b.distance).first(); - if (shaman) return ClassAttack.doAttack(shaman, null, true); - } - if (!Attack.useBowOnSwitch(unit, sdk.skills.Attack, i === 5)) return Attack.Result.FAILED; - if (unit.distance < 8 || me.inDanger()) { - if (once) return Attack.Result.FAILED; - let closeMob = getUnits(sdk.unittype.Monster) - .filter(mon => mon.distance < 10 && mon.attackable && mon.gid !== gid) - .sort(Attack.walkingSortMonsters).first(); - if (closeMob) return ClassAttack.doAttack(closeMob, null, true); - } - } - } finally { - me.weaponswitch !== sdk.player.slot.Main && me.switchWeapons(sdk.player.slot.Main); - } - } - return unit.dead ? Attack.Result.SUCCESS : Attack.Result.FAILED; - }; - - if (CharData.skillData.bow.onSwitch - && (index !== 1 || !unit.name.includes(getLocaleString(sdk.locale.text.Ghostly))) - && (unit.distance >= 12 || (unit.distance >= 8 && unit.isMoving && (unit.targetx !== me.x || unit.targety !== me.y))) - && ([-1, sdk.skills.Attack].includes(attackSkill) || Skill.getManaCost(attackSkill) > me.mp)) { - if (switchBowAttack(unit) === Attack.Result.SUCCESS) return Attack.Result.SUCCESS; - } - - let result = this.doCast(unit, attackSkill, aura); - - if (result === Attack.Result.CANTATTACK && Attack.canTeleStomp(unit)) { - let merc = me.getMerc(); - - while (unit.attackable) { - if (!unit) return Attack.Result.SUCCESS; - - if (me.needMerc()) { - if (Config.MercWatch && mercRevive++ < 1) { - Town.visitTown(); - } else { - return Attack.Result.CANTATTACK; - } - - (merc === undefined || !merc) && (merc = me.getMerc()); - } - - if (!!merc && getDistance(merc, unit) > 5) { - Pather.moveToUnit(unit); - - let spot = Attack.findSafeSpot(unit, 10, 5, 9); - !!spot && Pather.walkTo(spot.x, spot.y); - } - - let closeMob = Attack.getNearestMonster({ skipGid: gid }); - !!closeMob && this.doCast(closeMob, attackSkill, aura); - } - - return Attack.Result.SUCCESS; - } - - return result; + if (!unit || !unit.attackable) return Attack.Result.SUCCESS; + + let gid = unit.gid; + let mercRevive = 0; + let gold = me.gold; + let [attackSkill, aura] = [-1, -1]; + const index = (unit.isSpecial || unit.isPlayer) ? 1 : 3; + + // prevent running back to town quickly if our merc is just weak + if (Config.MercWatch && me.needMerc() && getTickCount() - MercWatch.last > Time.seconds(5)) { + console.log("mercwatch"); + + if (Town.visitTown()) { + // lost reference to the mob we were attacking + if (!unit || !copyUnit(unit).x || !Game.getMonster(-1, -1, gid) || unit.dead) { + return Attack.Result.SUCCESS; + } + } + MercWatch.last = getTickCount(); + gold = me.gold; // reset value after town + } + + if (me.expansion && index === 1 && unit.curseable) { + const commonCheck = (gold > 500000 || unit.isBoss || [sdk.areas.ChaosSanctuary, sdk.areas.ThroneofDestruction].includes(me.area)); + + if (CharData.skillData.haveChargedSkill(sdk.skills.SlowMissiles) + && unit.getEnchant(sdk.enchant.LightningEnchanted) && !unit.getState(sdk.states.SlowMissiles) + && (gold > 500000 && !unit.isBoss) && !checkCollision(me, unit, sdk.collision.Ranged)) { + // Cast slow missiles + Attack.castCharges(sdk.skills.SlowMissiles, unit); + } + + if (CharData.skillData.haveChargedSkill(sdk.skills.InnerSight) && !unit.getState(sdk.states.InnerSight) + && gold > 500000 && !checkCollision(me, unit, sdk.collision.Ranged)) { + // Cast Inner sight + Attack.castCharges(sdk.skills.InnerSight, unit); + } + + if (CharData.skillData.haveChargedSkillOnSwitch(sdk.skills.Decrepify) + && !unit.getState(sdk.states.Decrepify) && commonCheck && !checkCollision(me, unit, sdk.collision.Ranged)) { + // Switch cast decrepify + Attack.switchCastCharges(sdk.skills.Decrepify, unit); + } + + if (CharData.skillData.haveChargedSkillOnSwitch(sdk.skills.Weaken) + && !unit.getState(sdk.states.Weaken) && !unit.getState(sdk.states.Decrepify) && commonCheck && !checkCollision(me, unit, sdk.collision.Ranged)) { + // Switch cast weaken + Attack.switchCastCharges(sdk.skills.Weaken, unit); + } + } + + // specials and dolls for now, should make dolls much less dangerous with the reduction of their damage + if (Precast.haveCTA > -1 && !unit.dead && (index === 1 || unit.isDoll) + && unit.distance < 5 && !unit.getState(sdk.states.BattleCry) && unit.curseable) { + Skill.switchCast(sdk.skills.BattleCry, { oSkill: true }); + } + + if (Attack.getCustomAttack(unit)) { + [attackSkill, aura] = Attack.getCustomAttack(unit); + } else { + attackSkill = Config.AttackSkill[index]; + aura = Config.AttackSkill[index + 1]; + } + + // Classic auradin check + if (this.attackAuras.includes(aura)) { + // Monster immune to primary aura + if (!Attack.checkResist(unit, aura)) { + // Reset skills + [attackSkill, aura] = [-1, -1]; + + // Set to secondary if not immune, check if using secondary attack aura if not check main skill for immunity + if (Config.AttackSkill[5] > -1 && Attack.checkResist(unit, (this.attackAuras.includes(Config.AttackSkill[6]) ? Config.AttackSkill[6] : Config.AttackSkill[5]))) { + attackSkill = Config.AttackSkill[5]; + aura = Config.AttackSkill[6]; + } + } + } else { + // Monster immune to primary skill + if (!Attack.checkResist(unit, attackSkill)) { + // Reset skills + [attackSkill, aura] = [-1, -1]; + + // Set to secondary if not immune + if (Config.AttackSkill[5] > -1 && Attack.checkResist(unit, Config.AttackSkill[5])) { + attackSkill = Config.AttackSkill[5]; + aura = Config.AttackSkill[6]; + } + } + } + + if (attackSkill === sdk.skills.Attack && !unit.isFallen && Skill.canUse(sdk.skills.Sacrifice) && me.hpPercent > 75) { + attackSkill = sdk.skills.Sacrifice; + } + + // Low mana skill + if (Config.LowManaSkill[0] > -1 && Skill.getManaCost(attackSkill) > me.mp && Attack.checkResist(unit, Config.LowManaSkill[0])) { + [attackSkill, aura] = Config.LowManaSkill; + } + + /** + * @param {Monster} unit + * @returns {AttackResult} + */ + const switchBowAttack = (unit) => { + if (Attack.getIntoPosition(unit, 20, sdk.collision.Ranged)) { + try { + const checkForShamans = unit.isFallen && !me.inArea(sdk.areas.BloodMoor); + for (let i = 0; i < 5 && unit.attackable; i++) { + if (checkForShamans && !once) { + // before we waste time let's see if there is a shaman we should kill + const shaman = getUnits(sdk.unittype.Monster) + .filter(mon => mon.distance < 20 && mon.isShaman && mon.attackable) + .sort((a, b) => a.distance - b.distance).first(); + if (shaman) return ClassAttack.doAttack(shaman, null, true); + } + if (!Attack.useBowOnSwitch(unit, sdk.skills.Attack, i === 5)) return Attack.Result.FAILED; + if (unit.distance < 8 || me.inDanger()) { + if (once) return Attack.Result.FAILED; + let closeMob = getUnits(sdk.unittype.Monster) + .filter(mon => mon.distance < 10 && mon.attackable && mon.gid !== gid) + .sort(Attack.walkingSortMonsters).first(); + if (closeMob) return ClassAttack.doAttack(closeMob, null, true); + } + } + } finally { + me.weaponswitch !== sdk.player.slot.Main && me.switchWeapons(sdk.player.slot.Main); + } + } + return unit.dead ? Attack.Result.SUCCESS : Attack.Result.FAILED; + }; + + if (CharData.skillData.bow.onSwitch + && (index !== 1 || !unit.name.includes(getLocaleString(sdk.locale.text.Ghostly))) + && (unit.distance >= 12 || (unit.distance >= 8 && unit.isMoving && (unit.targetx !== me.x || unit.targety !== me.y))) + && ([-1, sdk.skills.Attack].includes(attackSkill) || Skill.getManaCost(attackSkill) > me.mp)) { + if (switchBowAttack(unit) === Attack.Result.SUCCESS) return Attack.Result.SUCCESS; + } + + let result = this.doCast(unit, attackSkill, aura); + + if (result === Attack.Result.CANTATTACK && Attack.canTeleStomp(unit)) { + let merc = me.getMerc(); + + while (unit.attackable) { + if (!unit) return Attack.Result.SUCCESS; + + if (me.needMerc()) { + if (Config.MercWatch && mercRevive++ < 1) { + Town.visitTown(); + } else { + return Attack.Result.CANTATTACK; + } + + (merc === undefined || !merc) && (merc = me.getMerc()); + } + + if (!!merc && getDistance(merc, unit) > 5) { + Pather.moveToUnit(unit); + + let spot = Attack.findSafeSpot(unit, 10, 5, 9); + !!spot && Pather.walkTo(spot.x, spot.y); + } + + let closeMob = Attack.getNearestMonster({ skipGid: gid }); + !!closeMob && this.doCast(closeMob, attackSkill, aura); + } + + return Attack.Result.SUCCESS; + } + + return result; }; ClassAttack.reposition = function (x, y) { - if (typeof x !== "number" || typeof y !== "number") return false; - if ([x, y].distance > 1) { - if (Pather.useTeleport()) { - [x, y].distance > 30 ? Pather.moveTo(x, y) : Pather.teleportTo(x, y, 3); - } else { - if ([x, y].distance <= 4 && !CollMap.checkColl(me, { x: x, y: y }, sdk.collision.BlockWalk, 3)) { - Misc.click(0, 0, x, y); - } else if (!CollMap.checkColl(me, { x: x, y: y }, sdk.collision.BlockWalk, 3)) { - Pather.walkTo(x, y); - } else { - // don't clear while trying to reposition - Pather.moveToEx(x, y, { clearSettings: { allowClearing: false } }); - } - - delay(200); - } - } - - return true; + if (typeof x !== "number" || typeof y !== "number") return false; + if ([x, y].distance > 1) { + if (Pather.useTeleport()) { + [x, y].distance > 30 ? Pather.moveTo(x, y) : Pather.teleportTo(x, y, 3); + } else { + if ([x, y].distance <= 4 && !CollMap.checkColl(me, { x: x, y: y }, sdk.collision.BlockWalk, 3)) { + Misc.click(0, 0, x, y); + } else if (!CollMap.checkColl(me, { x: x, y: y }, sdk.collision.BlockWalk, 3)) { + Pather.walkTo(x, y); + } else { + // don't clear while trying to reposition + Pather.moveToEx(x, y, { clearSettings: { allowClearing: false } }); + } + + delay(200); + } + } + + return true; }; ClassAttack.getHammerPosition = function (unit) { - let x, y, positions, baseId = getBaseStat("monstats", unit.classid, "baseid"); - let size = getBaseStat("monstats2", baseId, "sizex"); - const coll = unit.isMonsterObject ? sdk.collision.WallOrRanged : sdk.collision.BlockWalk; - const canTele = Pather.canTeleport(); - - // in case base stat returns something outrageous - (typeof size !== "number" || size < 1 || size > 3) && (size = 3); - - switch (unit.type) { - case sdk.unittype.Player: - ({ x, y } = unit); - positions = [[x + 2, y], [x + 2, y + 1]]; - - break; - case sdk.unittype.Monster: - let commonCheck = (unit.isMoving && unit.distance < 10); - x = commonCheck && getDistance(me, unit.targetx, unit.targety) > 5 ? unit.targetx : unit.x; - y = commonCheck && getDistance(me, unit.targetx, unit.targety) > 5 ? unit.targety : unit.y; - positions = [[x + 2, y + 1], [x, y + 3], [x + 2, y - 1], [x - 2, y + 2], [x - 5, y]]; - size === 3 && positions.unshift([x + 2, y + 2]); - - break; - } - - // If one of the valid positions is a position im at already - for (let i = 0; i < positions.length; i += 1) { - let check = { x: positions[i][0], y: positions[i][1] }; - if (canTele && check.distance < 1) return true; - if (!canTele && (check.distance < 1 && !CollMap.checkColl(unit, check, coll, 1)) - || (check.distance <= 4 && me.getMobCount(6) > 2)) { - return true; - } - } - - for (let i = 0; i < positions.length; i += 1) { - let check = { x: positions[i][0], y: positions[i][1] }; - - if (Attack.validSpot(check.x, check.y) && !CollMap.checkColl(unit, check, coll, 0)) { - if (this.reposition(check.x, check.y)) return true; - } - } - - console.debug("Failed to find a hammer position for " + unit.name + " distance from me: " + unit.distance); - - return false; + let x, y, positions, baseId = getBaseStat("monstats", unit.classid, "baseid"); + let size = getBaseStat("monstats2", baseId, "sizex"); + const coll = unit.isMonsterObject ? sdk.collision.WallOrRanged : sdk.collision.BlockWalk; + const canTele = Pather.canTeleport(); + + // in case base stat returns something outrageous + (typeof size !== "number" || size < 1 || size > 3) && (size = 3); + + switch (unit.type) { + case sdk.unittype.Player: + ({ x, y } = unit); + positions = [[x + 2, y], [x + 2, y + 1]]; + + break; + case sdk.unittype.Monster: + let commonCheck = (unit.isMoving && unit.distance < 10); + x = commonCheck && getDistance(me, unit.targetx, unit.targety) > 5 ? unit.targetx : unit.x; + y = commonCheck && getDistance(me, unit.targetx, unit.targety) > 5 ? unit.targety : unit.y; + positions = [[x + 2, y + 1], [x, y + 3], [x + 2, y - 1], [x - 2, y + 2], [x - 5, y]]; + size === 3 && positions.unshift([x + 2, y + 2]); + + break; + } + + // If one of the valid positions is a position im at already + for (let i = 0; i < positions.length; i += 1) { + let check = { x: positions[i][0], y: positions[i][1] }; + if (canTele && check.distance < 1) return true; + if (!canTele && (check.distance < 1 && !CollMap.checkColl(unit, check, coll, 1)) + || (check.distance <= 4 && me.getMobCount(6) > 2)) { + return true; + } + } + + for (let i = 0; i < positions.length; i += 1) { + let check = { x: positions[i][0], y: positions[i][1] }; + + if (Attack.validSpot(check.x, check.y) && !CollMap.checkColl(unit, check, coll, 0)) { + if (this.reposition(check.x, check.y)) return true; + } + } + + console.debug("Failed to find a hammer position for " + unit.name + " distance from me: " + unit.distance); + + return false; }; ClassAttack.doCast = function (unit, attackSkill = -1, aura = -1) { - if (attackSkill < 0) return Attack.Result.CANTATTACK; - // unit became invalidated - if (!unit || !unit.attackable) return Attack.Result.SUCCESS; - me.weaponswitch !== sdk.player.slot.Main && me.switchWeapons(sdk.player.slot.Main); - - const currSkill = { - Hand: Skill.getHand(attackSkill), - Range: Skill.getRange(attackSkill) - }; - - switch (attackSkill) { - case sdk.skills.BlessedHammer: - // todo: add doll avoid to other classes - if (Config.AvoidDolls && unit.isDoll) { - this.dollAvoid(unit); - aura > -1 && Skill.setSkill(aura, sdk.skills.hand.Right); - Skill.cast(attackSkill, currSkill.Hand, unit); - - return Attack.Result.SUCCESS; - } - - // todo: maybe if we are currently surrounded and no tele to just attack from where we are - // hammers cut a pretty wide arc so likely this would be enough to clear our path - if (!this.getHammerPosition(unit)) { - // Fallback to secondary skill if it exists - if (Config.AttackSkill[5] > -1 && Config.AttackSkill[5] !== sdk.skills.BlessedHammer && Attack.checkResist(unit, Config.AttackSkill[5])) { - return this.doCast(unit, Config.AttackSkill[5], Config.AttackSkill[6]); - } - - return Attack.Result.FAILED; - } - - if (unit.distance > 9 || !unit.attackable) return Attack.Result.SUCCESS; - - aura > -1 && Skill.setSkill(aura, sdk.skills.hand.Right); - - for (let i = 0; i < 3; i += 1) { - Skill.cast(attackSkill, currSkill.Hand, unit); - - if (!unit.attackable || unit.distance > 9 || unit.isPlayer) { - break; - } - } - - return Attack.Result.SUCCESS; - case sdk.skills.HolyBolt: - if (unit.distance > currSkill.Range + 3 || CollMap.checkColl(me, unit, sdk.collision.Ranged)) { - if (!Attack.getIntoPosition(unit, currSkill.Range, sdk.collision.Ranged)) { - return Attack.Result.FAILED; - } - } - - CollMap.reset(); - - if (unit.distance > currSkill.Range || CollMap.checkColl(me, unit, sdk.collision.FriendlyRanged, 2)) { - if (!Attack.getIntoPosition(unit, currSkill.Range, sdk.collision.FriendlyRanged, true)) { - return Attack.Result.FAILED; - } - } - - if (!unit.dead) { - aura > -1 && Skill.setSkill(aura, sdk.skills.hand.Right); - Skill.cast(attackSkill, currSkill.Hand, unit); - } - - return Attack.Result.SUCCESS; - case sdk.skills.FistoftheHeavens: - if (!me.skillDelay) { - if (unit.distance > currSkill.Range || CollMap.checkColl(me, unit, sdk.collision.FriendlyRanged, 2)) { - if (!Attack.getIntoPosition(unit, currSkill.Range, sdk.collision.FriendlyRanged, true)) { - return Attack.Result.FAILED; - } - } - - if (!unit.dead) { - aura > -1 && Skill.setSkill(aura, sdk.skills.hand.Right); - Skill.cast(attackSkill, currSkill.Hand, unit); - - return Attack.Result.SUCCESS; - } - } - - break; - case sdk.skills.Attack: - case sdk.skills.Sacrifice: - case sdk.skills.Zeal: - case sdk.skills.Vengeance: - if (!Attack.validSpot(unit.x, unit.y, attackSkill, unit.classid)) { - return Attack.Result.FAILED; - } - - // 3591 - wall/line of sight/ranged/items/objects/closeddoor - if (unit.distance > 3 || checkCollision(me, unit, sdk.collision.WallOrRanged)) { - if (!Attack.getIntoPosition(unit, 3, sdk.collision.WallOrRanged, true)) { - return Attack.Result.FAILED; - } - } - - if (unit.attackable) { - aura > -1 && Skill.setSkill(aura, sdk.skills.hand.Right); - return (Skill.cast(attackSkill, sdk.skills.hand.LeftNoShift, unit) ? Attack.Result.SUCCESS : Attack.Result.FAILED); - } - - break; - default: - if (currSkill.Range < 4 && !Attack.validSpot(unit.x, unit.y, attackSkill, unit.classid)) return Attack.Result.FAILED; - - if (unit.distance > currSkill.Range || checkCollision(me, unit, sdk.collision.Ranged)) { - let walk = (attackSkill !== sdk.skills.Smite && currSkill.Range < 4 && unit.distance < 10 && !checkCollision(me, unit, sdk.collision.BlockWall)); - - // walk short distances instead of tele for melee attacks. teleport if failed to walk - if (!Attack.getIntoPosition(unit, currSkill.Range, sdk.collision.Ranged, walk)) return Attack.Result.FAILED; - } - - if (!unit.dead) { - aura > -1 && Skill.setSkill(aura, sdk.skills.hand.Right); - Skill.cast(attackSkill, currSkill.Hand, unit); - } - - return Attack.Result.SUCCESS; - } - - Misc.poll(() => !me.skillDelay, 1000, 40); - - return Attack.Result.SUCCESS; + if (attackSkill < 0) return Attack.Result.CANTATTACK; + // unit became invalidated + if (!unit || !unit.attackable) return Attack.Result.SUCCESS; + me.weaponswitch !== sdk.player.slot.Main && me.switchWeapons(sdk.player.slot.Main); + + const currSkill = { + Hand: Skill.getHand(attackSkill), + Range: Skill.getRange(attackSkill) + }; + + switch (attackSkill) { + case sdk.skills.BlessedHammer: + // todo: add doll avoid to other classes + if (Config.AvoidDolls && unit.isDoll) { + this.dollAvoid(unit); + aura > -1 && Skill.setSkill(aura, sdk.skills.hand.Right); + Skill.cast(attackSkill, currSkill.Hand, unit); + + return Attack.Result.SUCCESS; + } + + // todo: maybe if we are currently surrounded and no tele to just attack from where we are + // hammers cut a pretty wide arc so likely this would be enough to clear our path + if (!this.getHammerPosition(unit)) { + // Fallback to secondary skill if it exists + if (Config.AttackSkill[5] > -1 && Config.AttackSkill[5] !== sdk.skills.BlessedHammer && Attack.checkResist(unit, Config.AttackSkill[5])) { + return this.doCast(unit, Config.AttackSkill[5], Config.AttackSkill[6]); + } + + return Attack.Result.FAILED; + } + + if (unit.distance > 9 || !unit.attackable) return Attack.Result.SUCCESS; + + aura > -1 && Skill.setSkill(aura, sdk.skills.hand.Right); + + for (let i = 0; i < 3; i += 1) { + Skill.cast(attackSkill, currSkill.Hand, unit); + + if (!unit.attackable || unit.distance > 9 || unit.isPlayer) { + break; + } + } + + return Attack.Result.SUCCESS; + case sdk.skills.HolyBolt: + if (unit.distance > currSkill.Range + 3 || CollMap.checkColl(me, unit, sdk.collision.Ranged)) { + if (!Attack.getIntoPosition(unit, currSkill.Range, sdk.collision.Ranged)) { + return Attack.Result.FAILED; + } + } + + CollMap.reset(); + + if (unit.distance > currSkill.Range || CollMap.checkColl(me, unit, sdk.collision.FriendlyRanged, 2)) { + if (!Attack.getIntoPosition(unit, currSkill.Range, sdk.collision.FriendlyRanged, true)) { + return Attack.Result.FAILED; + } + } + + if (!unit.dead) { + aura > -1 && Skill.setSkill(aura, sdk.skills.hand.Right); + Skill.cast(attackSkill, currSkill.Hand, unit); + } + + return Attack.Result.SUCCESS; + case sdk.skills.FistoftheHeavens: + if (!me.skillDelay) { + if (unit.distance > currSkill.Range || CollMap.checkColl(me, unit, sdk.collision.FriendlyRanged, 2)) { + if (!Attack.getIntoPosition(unit, currSkill.Range, sdk.collision.FriendlyRanged, true)) { + return Attack.Result.FAILED; + } + } + + if (!unit.dead) { + aura > -1 && Skill.setSkill(aura, sdk.skills.hand.Right); + Skill.cast(attackSkill, currSkill.Hand, unit); + + return Attack.Result.SUCCESS; + } + } + + break; + case sdk.skills.Attack: + case sdk.skills.Sacrifice: + case sdk.skills.Zeal: + case sdk.skills.Vengeance: + if (!Attack.validSpot(unit.x, unit.y, attackSkill, unit.classid)) { + return Attack.Result.FAILED; + } + + // 3591 - wall/line of sight/ranged/items/objects/closeddoor + if (unit.distance > 3 || checkCollision(me, unit, sdk.collision.WallOrRanged)) { + if (!Attack.getIntoPosition(unit, 3, sdk.collision.WallOrRanged, true)) { + return Attack.Result.FAILED; + } + } + + if (unit.attackable) { + aura > -1 && Skill.setSkill(aura, sdk.skills.hand.Right); + return (Skill.cast(attackSkill, sdk.skills.hand.LeftNoShift, unit) ? Attack.Result.SUCCESS : Attack.Result.FAILED); + } + + break; + default: + if (currSkill.Range < 4 && !Attack.validSpot(unit.x, unit.y, attackSkill, unit.classid)) return Attack.Result.FAILED; + + if (unit.distance > currSkill.Range || checkCollision(me, unit, sdk.collision.Ranged)) { + let walk = (attackSkill !== sdk.skills.Smite && currSkill.Range < 4 && unit.distance < 10 && !checkCollision(me, unit, sdk.collision.BlockWall)); + + // walk short distances instead of tele for melee attacks. teleport if failed to walk + if (!Attack.getIntoPosition(unit, currSkill.Range, sdk.collision.Ranged, walk)) return Attack.Result.FAILED; + } + + if (!unit.dead) { + aura > -1 && Skill.setSkill(aura, sdk.skills.hand.Right); + Skill.cast(attackSkill, currSkill.Hand, unit); + } + + return Attack.Result.SUCCESS; + } + + Misc.poll(() => !me.skillDelay, 1000, 40); + + return Attack.Result.SUCCESS; }; ClassAttack.afterAttack = function () { - Precast.doPrecast(false); - - if (Skill.canUse(sdk.skills.Cleansing) && me.hpPercent < 85 && me.getState(sdk.states.Poison) - && !me.checkForMobs({ range: 12, coll: Coords_1.BlockBits.BlockWall }) && Skill.setSkill(sdk.skills.Cleansing, sdk.skills.hand.Right)) { - me.overhead("Delaying for a second to get rid of Poison"); - Misc.poll(() => (!me.getState(sdk.states.Poison) || me.mode === sdk.player.mode.GettingHit), 1500, 50); - } - - if (Skill.canUse(sdk.skills.Meditation) && me.mpPercent < 50 && !me.getState(sdk.states.Meditation) - && Skill.setSkill(sdk.skills.Meditation, sdk.skills.hand.Right)) { - Misc.poll(() => (me.mpPercent >= 50 || me.mode === sdk.player.mode.GettingHit), 1500, 50); - } - - if (Skill.canUse(sdk.skills.Redemption) && Config.Redemption instanceof Array - && (me.hpPercent < Config.Redemption[0] || me.mpPercent < Config.Redemption[1]) - && Attack.checkNearCorpses(me) > 2 && Skill.setSkill(sdk.skills.Redemption, sdk.skills.hand.Right)) { - Misc.poll(() => (me.hpPercent >= Config.Redemption[0] && me.mpPercent >= Config.Redemption[1]), 1500, 50); - } + Precast.doPrecast(false); + + if (Skill.canUse(sdk.skills.Cleansing) && me.hpPercent < 85 && me.getState(sdk.states.Poison) + && !me.checkForMobs({ range: 12, coll: Coords_1.BlockBits.BlockWall }) && Skill.setSkill(sdk.skills.Cleansing, sdk.skills.hand.Right)) { + me.overhead("Delaying for a second to get rid of Poison"); + Misc.poll(() => (!me.getState(sdk.states.Poison) || me.mode === sdk.player.mode.GettingHit), 1500, 50); + } + + if (Skill.canUse(sdk.skills.Meditation) && me.mpPercent < 50 && !me.getState(sdk.states.Meditation) + && Skill.setSkill(sdk.skills.Meditation, sdk.skills.hand.Right)) { + Misc.poll(() => (me.mpPercent >= 50 || me.mode === sdk.player.mode.GettingHit), 1500, 50); + } + + if (Skill.canUse(sdk.skills.Redemption) && Config.Redemption instanceof Array + && (me.hpPercent < Config.Redemption[0] || me.mpPercent < Config.Redemption[1]) + && Attack.checkNearCorpses(me) > 2 && Skill.setSkill(sdk.skills.Redemption, sdk.skills.hand.Right)) { + Misc.poll(() => (me.hpPercent >= Config.Redemption[0] && me.mpPercent >= Config.Redemption[1]), 1500, 50); + } }; diff --git a/libs/SoloPlay/Functions/ClassAttackOverrides/SorceressAttacks.js b/libs/SoloPlay/Functions/ClassAttackOverrides/SorceressAttacks.js index 7e68424c..6d202dc3 100644 --- a/libs/SoloPlay/Functions/ClassAttackOverrides/SorceressAttacks.js +++ b/libs/SoloPlay/Functions/ClassAttackOverrides/SorceressAttacks.js @@ -8,652 +8,652 @@ includeIfNotIncluded("core/Attacks/Sorceress.js"); (function () { - /** - * Can we slow this monster - * @param {Monster} unit - * @param {boolean} freezeable - * @returns {boolean} - */ - const slowable = function (unit, freezeable = false) { - return (!!unit && unit.attackable // those that we can attack - && Attack.checkResist(unit, "cold") - // those that are not frozen yet and those that can be frozen or not yet chilled - && (freezeable ? !unit.isFrozen && !unit.getStat(sdk.stats.CannotbeFrozen) : !unit.isChilled) - && ![sdk.monsters.Andariel, sdk.monsters.Lord5].includes(unit.classid)); - }; - - const frostNovaCheck = function () { - // don't build whole list - since we are just trying if at least one passes the test - // todo - test to time difference between these two methods - let mob = Game.getMonster(); - if (mob) { - do { - if (mob.distance < 7 && ![sdk.monsters.Andariel].includes(mob.classid) && mob.attackable - && !mob.isChilled && Attack.checkResist(mob, "cold") && !checkCollision(me, mob, Coords_1.Collision.BLOCK_MISSILE)) { - return true; - } - } while (mob.getNext()); - } - return false; - }; - - /** - * @param {Monster} unit - */ - const battleCryCheck = function (unit, force = false) { - // specials and dolls for now, should make dolls much less dangerous with the reduction of their damage - if (Precast.haveCTA > -1 && !unit.dead && (force || unit.isSpecial || unit.isDoll) - && unit.distance < 5 && !unit.getState(sdk.states.BattleCry) && unit.curseable) { - console.debug("BATTLECRY"); - Skill.switchCast(sdk.skills.BattleCry, { oSkill: true }); - } - }; - - /** - * @constructor - * @param {number} skillId - * @param {number} reqLvl - * @param {number} range - */ - function ClassData (skillId = -1, range = 0) { - this.have = false; - this.skill = skillId; - this.range = range ? range : Skill.getRange(skillId); - this.mana = Infinity; - this.dmg = 0; - this.timed = Skill.isTimed(skillId); - this.reqLvl = getBaseStat("skills", skillId, "reqlevel"); - } - - /** - * Initialize data values - * @param {number} [range] - * @returns {void} - */ - ClassData.prototype.assignValues = function (range) { - this.have = Skill.canUse(this.skill); - if (!this.have) return; - this.range = range || Skill.getRange(this.skill); - this.mana = Skill.getManaCost(this.skill); - }; - - /** - * Calculate effective damage for a certain monster unit - * @param {Monster} unit - * @returns {void} - */ - ClassData.prototype.calcDmg = function (unit) { - if (!this.have) return; - this.dmg = GameData.avgSkillDamage(this.skill, unit); - }; - - const AttackData = { - "Attack": new ClassData(sdk.skills.Attack, 4), - "FireBolt": new ClassData(sdk.skills.FireBolt), - "ChargedBolt": new ClassData(sdk.skills.ChargedBolt), - "IceBolt": new ClassData(sdk.skills.IceBolt), - "Inferno": new ClassData(sdk.skills.Inferno), - "Telekinesis": new ClassData(sdk.skills.Telekinesis, 20), - "StaticField": new ClassData(sdk.skills.StaticField), - "IceBlast": new ClassData(sdk.skills.IceBlast), - "FrostNova": new ClassData(sdk.skills.FrostNova), - "FireBall": new ClassData(sdk.skills.FireBall), - // Blaze: new ClassData(sdk.skills.Blaze), - "Lightning": new ClassData(sdk.skills.Lightning), - "Nova": new ClassData(sdk.skills.Nova), - "FireWall": new ClassData(sdk.skills.FireWall), - "ChainLightning": new ClassData(sdk.skills.ChainLightning), - "GlacialSpike": new ClassData(sdk.skills.GlacialSpike), - "Meteor": new ClassData(sdk.skills.Meteor), - // ThunderStorm: new ClassData(sdk.skills.ThunderStorm), - "Blizzard": new ClassData(sdk.skills.Blizzard), - "Hydra": new ClassData(sdk.skills.Hydra), - "FrozenOrb": new ClassData(sdk.skills.FrozenOrb), - }; - - /** - * The keys never change so this makes it easier to iterate without calling Object.keys each time - * @type {Array} - */ - const AttackDataKeys = Object.keys(AttackData); - - /** - * Helper function to re-init AttackData - * @param {number} currLvl - * @todo decide when AttackData need to be re-initialized becasue doing it every attack is a waste - */ - const initAttackData = (currLvl = me.charlvl) => { - AttackDataKeys.forEach(sk => { - if (currLvl >= AttackData[sk].reqLvl) { - AttackData[sk].assignValues(); - } - }); - }; - - /** - * Helper function to init damage value for unit - * @param {Monster} unit - */ - const setDamageValues = (unit) => { - AttackDataKeys.forEach(sk => { - if (AttackData[sk].have) { - AttackData[sk].calcDmg(unit); - } - }); - }; - - /** - * Check if this skill is the most damaging - * @param {ClassData} skill - * @returns {boolean} - */ - const isHighestDmg = (skill) => { - for (let key of AttackDataKeys) { - if (AttackData[key].dmg > skill.dmg) { - return false; - } - } - return true; - }; - - /** - * Used to handle times when there isn't a valid skill we can use, to prevent throwing error - */ - const DummyData = new ClassData(-1, -1); - - /** - * @param {Monster} unit - * @param {boolean} force - * @todo keep track of when, what, and who we last casted on to prevent spamming charged skills in a short period of time - */ - ClassAttack.switchCurse = function (unit, force) { - if (CharData.skillData.haveChargedSkill([sdk.skills.SlowMissiles, sdk.skills.LowerResist, sdk.skills.Weaken]) && unit.curseable) { - const gold = me.gold; - const isBoss = unit.isBoss; - const dangerZone = [sdk.areas.ChaosSanctuary, sdk.areas.ThroneofDestruction].includes(me.area); - if (force && checkCollision(me, unit, sdk.collision.Ranged)) { - if (!Attack.getIntoPosition(unit, 35, sdk.collision.Ranged)) return; - } - // If we have slow missles we might as well use it, currently only on Lighting Enchanted mobs as they are dangerous - // Might be worth it to use on souls too TODO: test this idea - if (CharData.skillData.haveChargedSkill(sdk.skills.SlowMissiles) && gold > 500000 && !isBoss - && unit.getEnchant(sdk.enchant.LightningEnchanted) && !unit.getState(sdk.states.SlowMissiles) - && !checkCollision(me, unit, sdk.collision.Ranged)) { - // Cast slow missiles - Attack.castCharges(sdk.skills.SlowMissiles, unit); - } - // Handle Switch casting - if (CharData.skillData.haveChargedSkillOnSwitch(sdk.skills.LowerResist) - && (gold > 500000 || isBoss || dangerZone) - && !unit.getState(sdk.states.LowerResist) - && !checkCollision(me, unit, sdk.collision.Ranged)) { - // Switch cast lower resist - Attack.switchCastCharges(sdk.skills.LowerResist, unit); - } - - if (CharData.skillData.haveChargedSkillOnSwitch(sdk.skills.Weaken) - && (gold > 500000 || isBoss || dangerZone) - && !unit.getState(sdk.states.Weaken) && !unit.getState(sdk.states.LowerResist) - && !checkCollision(me, unit, sdk.collision.Ranged)) { - // Switch cast weaken - Attack.switchCastCharges(sdk.skills.Weaken, unit); - } - } - }; - - /** - * @param {Monster} unit - * @returns {dataObj} - */ - ClassAttack.decideDistanceSkill = function (unit) { - const currLvl = me.charlvl; - let selected = AttackDataKeys - .filter(sk => { - if (currLvl < AttackData[sk].reqLvl || AttackData[sk].range < 20) return false; - AttackData[sk].assignValues(); - if (!AttackData[sk].have) return false; - AttackData[sk].calcDmg(unit); - /** - * For now, no skill delay check. - * Things to consider: - * 1) If the skill we choose is timed and we are in skillDelay, how long is left to wait? - * 2) If not long then what is the damage difference between the skill we choose and the runner up non-timed skill - * 3) If the non-timed skill will do enough damage to kill this monster then use it, or if we have more than 1-2 seconds to wait - * and we don't need to move to cast the non-timed skill. - * 4) Anything else? - */ - return AttackData[sk].dmg > 0 /* && (!AttackData[k].timed || !me.skillDelay) */; - }) - .sort((a, b) => AttackData[b].dmg - AttackData[a].dmg) - .first(); - return typeof AttackData[selected] === "object" ? AttackData[selected] : DummyData; - }; - - /** - * @override - * @param {Monster} unit - * @param {boolean} recheckSkill - * @param {boolean} once - * @returns {AttackResult} - */ - ClassAttack.doAttack = function (unit, recheckSkill = false, once = false) { - Developer.debugging.skills && console.log(sdk.colors.Green + "Test Start-----------------------------------------//"); - // unit became invalidated - if (!unit || !unit.attackable) return Attack.Result.SUCCESS; - - const currLvl = me.charlvl; - const index = (unit.isSpecial || unit.isPlayer) ? 1 : 3; - let gid = unit.gid; - let tick = getTickCount(); - let gold = me.gold; - - if (Config.MercWatch && me.needMerc() && gold > me.mercrevivecost * 3) { - console.debug("mercwatch"); - - if (Town.visitTown()) { - // lost reference to the mob we were attacking - if (!unit || !copyUnit(unit).x || !Game.getMonster(-1, -1, gid) || unit.dead) { - console.debug("Lost reference to unit"); - return Attack.Result.SUCCESS; - } - gold = me.gold; // reset value after town - } - } - - // maybe every couple attacks or just the first one? - Precast.doPrecast(); - - // Handle Charge skill casting - if (index === 1 && me.expansion && !unit.dead) { - ClassAttack.switchCurse(unit); - } - - initAttackData(currLvl); - - if (AttackData.FrostNova.have) { - if (me.mp > AttackData.FrostNova.mana) { - frostNovaCheck() && Skill.cast(sdk.skills.FrostNova, sdk.skills.hand.Right); - let ticktwo = getTickCount(); - // if the nova cause the death of any monsters around us, its worth it - if (GameData.calculateKillableFallensByFrostNova() > 0) { - Developer.debugging.skills && console.log("took " + ((getTickCount() - ticktwo) / 1000) + " seconds to check calculateKillableFallensByFrostNova. frost nova will kill fallens"); - Skill.cast(sdk.skills.FrostNova, sdk.skills.hand.Right); - } - } - } - - if (AttackData.GlacialSpike.have) { - if (me.mp > AttackData.GlacialSpike.mana * 2) { - let shouldSpike = unit && unit.distance < 10 && - getUnits(sdk.unittype.Monster).filter(function (el) { - return getDistance(el, unit) < 4 && slowable(el, true); - }).length > 1; - if (shouldSpike && !Coords_1.isBlockedBetween(me, unit)) { - Developer.debugging.skills && console.log("SPIKE"); - Skill.cast(sdk.skills.GlacialSpike, sdk.skills.hand.Right, unit); - } - } - } - - // We lost track of the mob or killed it - if (unit === undefined || !unit || !unit.attackable) return Attack.Result.SUCCESS; - - // Set damage values - // redo gamedata to be more efficent - setDamageValues(unit); + /** + * Can we slow this monster + * @param {Monster} unit + * @param {boolean} freezeable + * @returns {boolean} + */ + const slowable = function (unit, freezeable = false) { + return (!!unit && unit.attackable // those that we can attack + && Attack.checkResist(unit, "cold") + // those that are not frozen yet and those that can be frozen or not yet chilled + && (freezeable ? !unit.isFrozen && !unit.getStat(sdk.stats.CannotbeFrozen) : !unit.isChilled) + && ![sdk.monsters.Andariel, sdk.monsters.Lord5].includes(unit.classid)); + }; + + const frostNovaCheck = function () { + // don't build whole list - since we are just trying if at least one passes the test + // todo - test to time difference between these two methods + let mob = Game.getMonster(); + if (mob) { + do { + if (mob.distance < 7 && ![sdk.monsters.Andariel].includes(mob.classid) && mob.attackable + && !mob.isChilled && Attack.checkResist(mob, "cold") && !checkCollision(me, mob, Coords_1.Collision.BLOCK_MISSILE)) { + return true; + } + } while (mob.getNext()); + } + return false; + }; + + /** + * @param {Monster} unit + */ + const battleCryCheck = function (unit, force = false) { + // specials and dolls for now, should make dolls much less dangerous with the reduction of their damage + if (Precast.haveCTA > -1 && !unit.dead && (force || unit.isSpecial || unit.isDoll) + && unit.distance < 5 && !unit.getState(sdk.states.BattleCry) && unit.curseable) { + console.debug("BATTLECRY"); + Skill.switchCast(sdk.skills.BattleCry, { oSkill: true }); + } + }; + + /** + * @constructor + * @param {number} skillId + * @param {number} reqLvl + * @param {number} range + */ + function ClassData (skillId = -1, range = 0) { + this.have = false; + this.skill = skillId; + this.range = range ? range : Skill.getRange(skillId); + this.mana = Infinity; + this.dmg = 0; + this.timed = Skill.isTimed(skillId); + this.reqLvl = getBaseStat("skills", skillId, "reqlevel"); + } + + /** + * Initialize data values + * @param {number} [range] + * @returns {void} + */ + ClassData.prototype.assignValues = function (range) { + this.have = Skill.canUse(this.skill); + if (!this.have) return; + this.range = range || Skill.getRange(this.skill); + this.mana = Skill.getManaCost(this.skill); + }; + + /** + * Calculate effective damage for a certain monster unit + * @param {Monster} unit + * @returns {void} + */ + ClassData.prototype.calcDmg = function (unit) { + if (!this.have) return; + this.dmg = GameData.avgSkillDamage(this.skill, unit); + }; + + const AttackData = { + "Attack": new ClassData(sdk.skills.Attack, 4), + "FireBolt": new ClassData(sdk.skills.FireBolt), + "ChargedBolt": new ClassData(sdk.skills.ChargedBolt), + "IceBolt": new ClassData(sdk.skills.IceBolt), + "Inferno": new ClassData(sdk.skills.Inferno), + "Telekinesis": new ClassData(sdk.skills.Telekinesis, 20), + "StaticField": new ClassData(sdk.skills.StaticField), + "IceBlast": new ClassData(sdk.skills.IceBlast), + "FrostNova": new ClassData(sdk.skills.FrostNova), + "FireBall": new ClassData(sdk.skills.FireBall), + // Blaze: new ClassData(sdk.skills.Blaze), + "Lightning": new ClassData(sdk.skills.Lightning), + "Nova": new ClassData(sdk.skills.Nova), + "FireWall": new ClassData(sdk.skills.FireWall), + "ChainLightning": new ClassData(sdk.skills.ChainLightning), + "GlacialSpike": new ClassData(sdk.skills.GlacialSpike), + "Meteor": new ClassData(sdk.skills.Meteor), + // ThunderStorm: new ClassData(sdk.skills.ThunderStorm), + "Blizzard": new ClassData(sdk.skills.Blizzard), + "Hydra": new ClassData(sdk.skills.Hydra), + "FrozenOrb": new ClassData(sdk.skills.FrozenOrb), + }; + + /** + * The keys never change so this makes it easier to iterate without calling Object.keys each time + * @type {Array} + */ + const AttackDataKeys = Object.keys(AttackData); + + /** + * Helper function to re-init AttackData + * @param {number} currLvl + * @todo decide when AttackData need to be re-initialized becasue doing it every attack is a waste + */ + const initAttackData = (currLvl = me.charlvl) => { + AttackDataKeys.forEach(sk => { + if (currLvl >= AttackData[sk].reqLvl) { + AttackData[sk].assignValues(); + } + }); + }; + + /** + * Helper function to init damage value for unit + * @param {Monster} unit + */ + const setDamageValues = (unit) => { + AttackDataKeys.forEach(sk => { + if (AttackData[sk].have) { + AttackData[sk].calcDmg(unit); + } + }); + }; + + /** + * Check if this skill is the most damaging + * @param {ClassData} skill + * @returns {boolean} + */ + const isHighestDmg = (skill) => { + for (let key of AttackDataKeys) { + if (AttackData[key].dmg > skill.dmg) { + return false; + } + } + return true; + }; + + /** + * Used to handle times when there isn't a valid skill we can use, to prevent throwing error + */ + const DummyData = new ClassData(-1, -1); + + /** + * @param {Monster} unit + * @param {boolean} force + * @todo keep track of when, what, and who we last casted on to prevent spamming charged skills in a short period of time + */ + ClassAttack.switchCurse = function (unit, force) { + if (CharData.skillData.haveChargedSkill([sdk.skills.SlowMissiles, sdk.skills.LowerResist, sdk.skills.Weaken]) && unit.curseable) { + const gold = me.gold; + const isBoss = unit.isBoss; + const dangerZone = [sdk.areas.ChaosSanctuary, sdk.areas.ThroneofDestruction].includes(me.area); + if (force && checkCollision(me, unit, sdk.collision.Ranged)) { + if (!Attack.getIntoPosition(unit, 35, sdk.collision.Ranged)) return; + } + // If we have slow missles we might as well use it, currently only on Lighting Enchanted mobs as they are dangerous + // Might be worth it to use on souls too TODO: test this idea + if (CharData.skillData.haveChargedSkill(sdk.skills.SlowMissiles) && gold > 500000 && !isBoss + && unit.getEnchant(sdk.enchant.LightningEnchanted) && !unit.getState(sdk.states.SlowMissiles) + && !checkCollision(me, unit, sdk.collision.Ranged)) { + // Cast slow missiles + Attack.castCharges(sdk.skills.SlowMissiles, unit); + } + // Handle Switch casting + if (CharData.skillData.haveChargedSkillOnSwitch(sdk.skills.LowerResist) + && (gold > 500000 || isBoss || dangerZone) + && !unit.getState(sdk.states.LowerResist) + && !checkCollision(me, unit, sdk.collision.Ranged)) { + // Switch cast lower resist + Attack.switchCastCharges(sdk.skills.LowerResist, unit); + } + + if (CharData.skillData.haveChargedSkillOnSwitch(sdk.skills.Weaken) + && (gold > 500000 || isBoss || dangerZone) + && !unit.getState(sdk.states.Weaken) && !unit.getState(sdk.states.LowerResist) + && !checkCollision(me, unit, sdk.collision.Ranged)) { + // Switch cast weaken + Attack.switchCastCharges(sdk.skills.Weaken, unit); + } + } + }; + + /** + * @param {Monster} unit + * @returns {dataObj} + */ + ClassAttack.decideDistanceSkill = function (unit) { + const currLvl = me.charlvl; + let selected = AttackDataKeys + .filter(sk => { + if (currLvl < AttackData[sk].reqLvl || AttackData[sk].range < 20) return false; + AttackData[sk].assignValues(); + if (!AttackData[sk].have) return false; + AttackData[sk].calcDmg(unit); + /** + * For now, no skill delay check. + * Things to consider: + * 1) If the skill we choose is timed and we are in skillDelay, how long is left to wait? + * 2) If not long then what is the damage difference between the skill we choose and the runner up non-timed skill + * 3) If the non-timed skill will do enough damage to kill this monster then use it, or if we have more than 1-2 seconds to wait + * and we don't need to move to cast the non-timed skill. + * 4) Anything else? + */ + return AttackData[sk].dmg > 0 /* && (!AttackData[k].timed || !me.skillDelay) */; + }) + .sort((a, b) => AttackData[b].dmg - AttackData[a].dmg) + .first(); + return typeof AttackData[selected] === "object" ? AttackData[selected] : DummyData; + }; + + /** + * @override + * @param {Monster} unit + * @param {boolean} recheckSkill + * @param {boolean} once + * @returns {AttackResult} + */ + ClassAttack.doAttack = function (unit, recheckSkill = false, once = false) { + Developer.debugging.skills && console.log(sdk.colors.Green + "Test Start-----------------------------------------//"); + // unit became invalidated + if (!unit || !unit.attackable) return Attack.Result.SUCCESS; + + const currLvl = me.charlvl; + const index = (unit.isSpecial || unit.isPlayer) ? 1 : 3; + let gid = unit.gid; + let tick = getTickCount(); + let gold = me.gold; + + if (Config.MercWatch && me.needMerc() && gold > me.mercrevivecost * 3) { + console.debug("mercwatch"); + + if (Town.visitTown()) { + // lost reference to the mob we were attacking + if (!unit || !copyUnit(unit).x || !Game.getMonster(-1, -1, gid) || unit.dead) { + console.debug("Lost reference to unit"); + return Attack.Result.SUCCESS; + } + gold = me.gold; // reset value after town + } + } + + // maybe every couple attacks or just the first one? + Precast.doPrecast(); + + // Handle Charge skill casting + if (index === 1 && me.expansion && !unit.dead) { + ClassAttack.switchCurse(unit); + } + + initAttackData(currLvl); + + if (AttackData.FrostNova.have) { + if (me.mp > AttackData.FrostNova.mana) { + frostNovaCheck() && Skill.cast(sdk.skills.FrostNova, sdk.skills.hand.Right); + let ticktwo = getTickCount(); + // if the nova cause the death of any monsters around us, its worth it + if (GameData.calculateKillableFallensByFrostNova() > 0) { + Developer.debugging.skills && console.log("took " + ((getTickCount() - ticktwo) / 1000) + " seconds to check calculateKillableFallensByFrostNova. frost nova will kill fallens"); + Skill.cast(sdk.skills.FrostNova, sdk.skills.hand.Right); + } + } + } + + if (AttackData.GlacialSpike.have) { + if (me.mp > AttackData.GlacialSpike.mana * 2) { + let shouldSpike = unit && unit.distance < 10 && + getUnits(sdk.unittype.Monster).filter(function (el) { + return getDistance(el, unit) < 4 && slowable(el, true); + }).length > 1; + if (shouldSpike && !Coords_1.isBlockedBetween(me, unit)) { + Developer.debugging.skills && console.log("SPIKE"); + Skill.cast(sdk.skills.GlacialSpike, sdk.skills.hand.Right, unit); + } + } + } + + // We lost track of the mob or killed it + if (unit === undefined || !unit || !unit.attackable) return Attack.Result.SUCCESS; + + // Set damage values + // redo gamedata to be more efficent + setDamageValues(unit); - // log damage values - // if (Developer.debugging.skills) { - // Object.keys(data).forEach(k => typeof data[k] === "object" && console.log(getSkillById(data[k].skill) + " : " + data[k].dmg)); - // } - - let rebuild = false; - - // If we have enough mana for Static and it will do more damage than our other skills then duh use it - // should this return afterwards since the calulations will now be different? - if (AttackData.StaticField.have && (AttackData.StaticField.mana * 3) < me.mp) { - let closeMobCheck = getUnits(sdk.unittype.Monster) - .filter(unit => !!unit && unit.attackable && unit.distance < AttackData.StaticField.range) - .find(unit => Attack.checkResist(unit, "lightning") && unit.hpPercent > Config.CastStatic); - if (!!closeMobCheck && isHighestDmg(AttackData.StaticField) && !Coords_1.isBlockedBetween(me, closeMobCheck)) { - Developer.debugging.skills && console.log("STATIC"); - // check if we should use battle cry from cta if we have it - battleCryCheck(closeMobCheck); - Skill.cast(sdk.skills.StaticField, sdk.skills.hand.Right, closeMobCheck) && Skill.cast(sdk.skills.StaticField, sdk.skills.hand.Right, closeMobCheck); - rebuild = true; - } - } - - // We lost track of the mob or killed it (recheck after using static) - if (unit === undefined || !unit || !unit.attackable) return Attack.Result.SUCCESS; - - rebuild && setDamageValues(unit); - - /** - * @todo static field is a good skill but if we are currently out of range, check how dangerous it is to tele to spot before choosing that as our skill - */ - let sortedList = AttackDataKeys - .filter(k => AttackData[k].have && me.mp > AttackData[k].mana - && (!AttackData[k].timed || !me.skillDelay) && (AttackData[k].skill !== sdk.skills.StaticField || !recheckSkill)) - .sort((a, b) => AttackData[b].dmg - AttackData[a].dmg); - if (!sortedList.length) return Attack.Result.FAILED; - - // A bit ugly but handle static and charged bolt here - let skillCheck = ( - (AttackData[sortedList[0]].skill === sdk.skills.StaticField && unit.distance > AttackData.StaticField.range && me.inDanger(unit, 15)) - || (AttackData[sortedList[0]].skill === sdk.skills.ChargedBolt && recheckSkill) - ) - ? sortedList.at(1) - : sortedList.at(0); - - /** @type {ClassData} */ - let selectedSkill = typeof AttackData[skillCheck] === "object" ? AttackData[skillCheck] : DummyData; - - switch (selectedSkill.skill) { - case sdk.skills.ChargedBolt: - if (selectedSkill.skill === sdk.skills.ChargedBolt && AttackData.IceBolt.have && slowable(unit)) { - selectedSkill = AttackData.IceBolt; - } - - break; - case sdk.skills.Telekinesis: - // maybe check if we are able to telestomp? - if (!me.normal) { - selectedSkill = DummyData; - } - - break; - case sdk.skills.Attack: - if (!me.normal || (me.charlvl > 6 && !me.checkForMobs({ range: 10, coll: (sdk.collision.BlockWall | sdk.collision.ClosedDoor) }))) { - selectedSkill = DummyData; - } - } - - /** - * @param {Monster} unit - * @returns {AttackResult} - */ - const switchBowAttack = (unit) => { - if (Attack.getIntoPosition(unit, 20, sdk.collision.Ranged)) { - try { - const checkForShamans = unit.isFallen && !me.inArea(sdk.areas.BloodMoor); - for (let i = 0; i < 5 && unit.attackable; i++) { - if (checkForShamans && !once) { - // before we waste time let's see if there is a shaman we should kill - const shaman = getUnits(sdk.unittype.Monster) - .filter(mon => mon.distance < 20 && mon.isShaman && mon.attackable) - .sort((a, b) => a.distance - b.distance).first(); - if (shaman) return ClassAttack.doAttack(shaman, null, true); - } - if (!Attack.useBowOnSwitch(unit, sdk.skills.Attack, i === 5)) return Attack.Result.FAILED; - if (unit.distance < 8 || me.inDanger()) { - if (once) return Attack.Result.FAILED; - let closeMob = getUnits(sdk.unittype.Monster) - .filter(mon => mon.distance < 10 && mon.attackable && mon.gid !== gid) - .sort(Attack.walkingSortMonsters).first(); - if (closeMob) return ClassAttack.doAttack(closeMob, null, true); - } - } - } finally { - me.weaponswitch !== sdk.player.slot.Main && me.switchWeapons(sdk.player.slot.Main); - } - } - return unit.dead ? Attack.Result.SUCCESS : Attack.Result.FAILED; - }; - - if (CharData.skillData.bow.onSwitch - && (index !== 1 || !unit.name.includes(getLocaleString(sdk.locale.text.Ghostly))) - && ([-1, sdk.skills.Attack].includes(selectedSkill.skill) - || selectedSkill.mana > me.mp - || (selectedSkill.mana * 3 > me.mp && [sdk.skills.FireBolt, sdk.skills.ChargedBolt].includes(selectedSkill.skill)))) { - if (switchBowAttack(unit) === Attack.Result.SUCCESS) return Attack.Result.SUCCESS; - } - - if (selectedSkill === sdk.skills.Attack && me.inDanger(unit, 10)) { - // try to stay safer for now, probably should see if there are any easy targets we can pick off - return Attack.Result.CANTATTACK; - } - - let result = ClassAttack.doCast(unit, selectedSkill); - - switch (result) { - case Attack.Result.FAILED: - Developer.debugging.skills && console.log(sdk.colors.Red + "Fail Test End----Time elasped[" + ((getTickCount() - tick) / 1000) + " seconds]----------------------//"); - - return Attack.Result.FAILED; - case Attack.Result.SUCCESS: - Developer.debugging.skills && console.log(sdk.colors.Red + "Sucess Test End----Time elasped[" + ((getTickCount() - tick) / 1000) + " seconds]----------------------//"); - - return Attack.Result.SUCCESS; - case Attack.Result.CANTATTACK: // Try to telestomp - if (Pather.canTeleport() && Attack.checkResist(unit, "physical") && !!me.getMerc() - && Attack.validSpot(unit.x, unit.y) - && (Config.TeleStomp || (!me.hell && (unit.getMobCount(10) < me.maxNearMonsters && unit.isSpecial)))) { - let merc = me.getMerc(); - let haveTK = Skill.canUse(sdk.skills.Telekinesis); - let mercRevive = 0; - - while (unit.attackable) { - if (!unit) return Attack.Result.SUCCESS; - - if (me.needMerc()) { - if (Config.MercWatch && mercRevive < 3) { - Town.visitTown() && (mercRevive++); - } else { - return Attack.Result.CANTATTACK; - } - - (merc === undefined || !merc) && (merc = me.getMerc()); - } - - if (!!merc && getDistance(merc, unit) > 5) { - Pather.moveToUnit(unit); - - let spot = Attack.findSafeSpot(unit, 10, 5, 9); - !!spot && Pather.walkTo(spot.x, spot.y); - } - - if (Attack.checkResist(unit, "lightning") && AttackData.StaticField.have && unit.hpPercent > Config.CastStatic) { - Skill.cast(sdk.skills.StaticField, sdk.skills.hand.Right); - } - - let closeMob = Attack.getNearestMonster({ skipGid: gid }); - !!closeMob ? this.doCast(closeMob, selectedSkill) : haveTK && Skill.cast(sdk.skills.Telekinesis, sdk.skills.hand.Right, unit); - } - - return Attack.Result.SUCCESS; - } - - return Attack.Result.CANTATTACK; - default: - return result; - } - }; - - /** - * @override - * @param {Monster} unit - * @param {ClassData} choosenSkill - * @returns {AttackResult} - */ - ClassAttack.doCast = function (unit, choosenSkill) { - let noMana = false; - let { skill, range, mana, timed } = choosenSkill; - // unit became invalidated - if (!unit || !unit.attackable) return Attack.Result.SUCCESS; - if (!!skill && me.mp < mana) { - return Attack.Result.NEEDMANA; - } - // No valid skills can be found - if (skill < 0) return Attack.Result.CANTATTACK; - - // print damage values - Developer.debugging.skills && choosenSkill.have && console.log(sdk.colors.Yellow + "(Selected Main :: " + getSkillById(skill) + ") DMG: " + choosenSkill.dmg); - - if (![sdk.skills.FrostNova, sdk.skills.Nova, sdk.skills.StaticField].includes(skill)) { - // need like a potential danger check, sometimes while me might not be immeadiate danger because there aren't a whole - // lot of monsters around, we can suddenly be in danger if a ranged monsters hits us or if one of the monsters near us - // does a lot of damage quickly - if (Skill.canUse(sdk.skills.Teleport) && me.mp > Skill.getManaCost(sdk.skills.Teleport) + mana && me.inDanger()) { - //console.log("FINDING NEW SPOT"); - Attack.getIntoPosition(unit, range, 0 + // log damage values + // if (Developer.debugging.skills) { + // Object.keys(data).forEach(k => typeof data[k] === "object" && console.log(getSkillById(data[k].skill) + " : " + data[k].dmg)); + // } + + let rebuild = false; + + // If we have enough mana for Static and it will do more damage than our other skills then duh use it + // should this return afterwards since the calulations will now be different? + if (AttackData.StaticField.have && (AttackData.StaticField.mana * 3) < me.mp) { + let closeMobCheck = getUnits(sdk.unittype.Monster) + .filter(unit => !!unit && unit.attackable && unit.distance < AttackData.StaticField.range) + .find(unit => Attack.checkResist(unit, "lightning") && unit.hpPercent > Config.CastStatic); + if (!!closeMobCheck && isHighestDmg(AttackData.StaticField) && !Coords_1.isBlockedBetween(me, closeMobCheck)) { + Developer.debugging.skills && console.log("STATIC"); + // check if we should use battle cry from cta if we have it + battleCryCheck(closeMobCheck); + Skill.cast(sdk.skills.StaticField, sdk.skills.hand.Right, closeMobCheck) && Skill.cast(sdk.skills.StaticField, sdk.skills.hand.Right, closeMobCheck); + rebuild = true; + } + } + + // We lost track of the mob or killed it (recheck after using static) + if (unit === undefined || !unit || !unit.attackable) return Attack.Result.SUCCESS; + + rebuild && setDamageValues(unit); + + /** + * @todo static field is a good skill but if we are currently out of range, check how dangerous it is to tele to spot before choosing that as our skill + */ + let sortedList = AttackDataKeys + .filter(k => AttackData[k].have && me.mp > AttackData[k].mana + && (!AttackData[k].timed || !me.skillDelay) && (AttackData[k].skill !== sdk.skills.StaticField || !recheckSkill)) + .sort((a, b) => AttackData[b].dmg - AttackData[a].dmg); + if (!sortedList.length) return Attack.Result.FAILED; + + // A bit ugly but handle static and charged bolt here + let skillCheck = ( + (AttackData[sortedList[0]].skill === sdk.skills.StaticField && unit.distance > AttackData.StaticField.range && me.inDanger(unit, 15)) + || (AttackData[sortedList[0]].skill === sdk.skills.ChargedBolt && recheckSkill) + ) + ? sortedList.at(1) + : sortedList.at(0); + + /** @type {ClassData} */ + let selectedSkill = typeof AttackData[skillCheck] === "object" ? AttackData[skillCheck] : DummyData; + + switch (selectedSkill.skill) { + case sdk.skills.ChargedBolt: + if (selectedSkill.skill === sdk.skills.ChargedBolt && AttackData.IceBolt.have && slowable(unit)) { + selectedSkill = AttackData.IceBolt; + } + + break; + case sdk.skills.Telekinesis: + // maybe check if we are able to telestomp? + if (!me.normal) { + selectedSkill = DummyData; + } + + break; + case sdk.skills.Attack: + if (!me.normal || (me.charlvl > 6 && !me.checkForMobs({ range: 10, coll: (sdk.collision.BlockWall | sdk.collision.ClosedDoor) }))) { + selectedSkill = DummyData; + } + } + + /** + * @param {Monster} unit + * @returns {AttackResult} + */ + const switchBowAttack = (unit) => { + if (Attack.getIntoPosition(unit, 20, sdk.collision.Ranged)) { + try { + const checkForShamans = unit.isFallen && !me.inArea(sdk.areas.BloodMoor); + for (let i = 0; i < 5 && unit.attackable; i++) { + if (checkForShamans && !once) { + // before we waste time let's see if there is a shaman we should kill + const shaman = getUnits(sdk.unittype.Monster) + .filter(mon => mon.distance < 20 && mon.isShaman && mon.attackable) + .sort((a, b) => a.distance - b.distance).first(); + if (shaman) return ClassAttack.doAttack(shaman, null, true); + } + if (!Attack.useBowOnSwitch(unit, sdk.skills.Attack, i === 5)) return Attack.Result.FAILED; + if (unit.distance < 8 || me.inDanger()) { + if (once) return Attack.Result.FAILED; + let closeMob = getUnits(sdk.unittype.Monster) + .filter(mon => mon.distance < 10 && mon.attackable && mon.gid !== gid) + .sort(Attack.walkingSortMonsters).first(); + if (closeMob) return ClassAttack.doAttack(closeMob, null, true); + } + } + } finally { + me.weaponswitch !== sdk.player.slot.Main && me.switchWeapons(sdk.player.slot.Main); + } + } + return unit.dead ? Attack.Result.SUCCESS : Attack.Result.FAILED; + }; + + if (CharData.skillData.bow.onSwitch + && (index !== 1 || !unit.name.includes(getLocaleString(sdk.locale.text.Ghostly))) + && ([-1, sdk.skills.Attack].includes(selectedSkill.skill) + || selectedSkill.mana > me.mp + || (selectedSkill.mana * 3 > me.mp && [sdk.skills.FireBolt, sdk.skills.ChargedBolt].includes(selectedSkill.skill)))) { + if (switchBowAttack(unit) === Attack.Result.SUCCESS) return Attack.Result.SUCCESS; + } + + if (selectedSkill === sdk.skills.Attack && me.inDanger(unit, 10)) { + // try to stay safer for now, probably should see if there are any easy targets we can pick off + return Attack.Result.CANTATTACK; + } + + let result = ClassAttack.doCast(unit, selectedSkill); + + switch (result) { + case Attack.Result.FAILED: + Developer.debugging.skills && console.log(sdk.colors.Red + "Fail Test End----Time elasped[" + ((getTickCount() - tick) / 1000) + " seconds]----------------------//"); + + return Attack.Result.FAILED; + case Attack.Result.SUCCESS: + Developer.debugging.skills && console.log(sdk.colors.Red + "Sucess Test End----Time elasped[" + ((getTickCount() - tick) / 1000) + " seconds]----------------------//"); + + return Attack.Result.SUCCESS; + case Attack.Result.CANTATTACK: // Try to telestomp + if (Pather.canTeleport() && Attack.checkResist(unit, "physical") && !!me.getMerc() + && Attack.validSpot(unit.x, unit.y) + && (Config.TeleStomp || (!me.hell && (unit.getMobCount(10) < me.maxNearMonsters && unit.isSpecial)))) { + let merc = me.getMerc(); + let haveTK = Skill.canUse(sdk.skills.Telekinesis); + let mercRevive = 0; + + while (unit.attackable) { + if (!unit) return Attack.Result.SUCCESS; + + if (me.needMerc()) { + if (Config.MercWatch && mercRevive < 3) { + Town.visitTown() && (mercRevive++); + } else { + return Attack.Result.CANTATTACK; + } + + (merc === undefined || !merc) && (merc = me.getMerc()); + } + + if (!!merc && getDistance(merc, unit) > 5) { + Pather.moveToUnit(unit); + + let spot = Attack.findSafeSpot(unit, 10, 5, 9); + !!spot && Pather.walkTo(spot.x, spot.y); + } + + if (Attack.checkResist(unit, "lightning") && AttackData.StaticField.have && unit.hpPercent > Config.CastStatic) { + Skill.cast(sdk.skills.StaticField, sdk.skills.hand.Right); + } + + let closeMob = Attack.getNearestMonster({ skipGid: gid }); + !!closeMob ? this.doCast(closeMob, selectedSkill) : haveTK && Skill.cast(sdk.skills.Telekinesis, sdk.skills.hand.Right, unit); + } + + return Attack.Result.SUCCESS; + } + + return Attack.Result.CANTATTACK; + default: + return result; + } + }; + + /** + * @override + * @param {Monster} unit + * @param {ClassData} choosenSkill + * @returns {AttackResult} + */ + ClassAttack.doCast = function (unit, choosenSkill) { + let noMana = false; + let { skill, range, mana, timed } = choosenSkill; + // unit became invalidated + if (!unit || !unit.attackable) return Attack.Result.SUCCESS; + if (!!skill && me.mp < mana) { + return Attack.Result.NEEDMANA; + } + // No valid skills can be found + if (skill < 0) return Attack.Result.CANTATTACK; + + // print damage values + Developer.debugging.skills && choosenSkill.have && console.log(sdk.colors.Yellow + "(Selected Main :: " + getSkillById(skill) + ") DMG: " + choosenSkill.dmg); + + if (![sdk.skills.FrostNova, sdk.skills.Nova, sdk.skills.StaticField].includes(skill)) { + // need like a potential danger check, sometimes while me might not be immeadiate danger because there aren't a whole + // lot of monsters around, we can suddenly be in danger if a ranged monsters hits us or if one of the monsters near us + // does a lot of damage quickly + if (Skill.canUse(sdk.skills.Teleport) && me.mp > Skill.getManaCost(sdk.skills.Teleport) + mana && me.inDanger()) { + //console.log("FINDING NEW SPOT"); + Attack.getIntoPosition(unit, range, 0 | Coords_1.BlockBits.LineOfSight | Coords_1.BlockBits.Ranged | Coords_1.BlockBits.Casting | Coords_1.BlockBits.ClosedDoor | Coords_1.BlockBits.Objects, false, true); - } else if (me.inDanger()) { - Attack.getIntoPosition(unit, range + 1, Coords_1.Collision.BLOCK_MISSILE, true); - } else if (unit.distance < 3 && range > 4) { - Attack.getIntoPosition(unit, range, Coords_1.Collision.BLOCK_MISSILE, true); - } - } - - if (skill > -1 && (!me.skillDelay || !timed)) { - let ranged = range > 4; - - if (skill === sdk.skills.ChargedBolt && !unit.hasEnchant(sdk.enchant.ManaBurn, sdk.enchant.ColdEnchanted)) { - unit.getMobCount(6, Coords_1.Collision.BLOCK_MISSILE) < 3 && (range = 7); - } - - if (skill === sdk.skills.Attack) { - if (me.hpPercent < 50 && me.mode !== sdk.player.mode.GettingHit && !me.checkForMobs({ range: 12 })) { - console.log("Low health but safe right now, going to delay a bit"); - let tick = getTickCount(); - const howLongToDelay = Config.AttackSkill.some(sk => sk > 1 && Skill.canUse(sk)) ? Time.seconds(2) : Time.seconds(1); - - while (getTickCount() - tick < howLongToDelay) { - if (me.mode === sdk.player.mode.GettingHit) { - console.debug("no longer safe, we are being attacked"); - break; - } else if (me.hpPercent >= 55) { - return 3; - } - - delay(40); - } - } - } - - if (range < 4 && !Attack.validSpot(unit.x, unit.y)) return Attack.Result.FAILED; - - // Only delay if there are no mobs in our immediate area - if (mana > me.mp && !me.checkForMobs({ range: 12 })) { - let tick = getTickCount(); - - while (getTickCount() - tick < 750) { - if (mana < me.mp) { - break; - } else if (me.mode === sdk.player.mode.GettingHit) { - console.debug("no longer safe, we are being attacked"); - return Attack.Result.NEEDMANA; - } - - delay(25); - } - } - - // try to prevent missing when the monster is moving by getting just a bit closer - if ([sdk.skills.FireBolt, sdk.skills.IceBolt].includes(skill)) { - range = 12; - } - - if (unit.distance > range || Coords_1.isBlockedBetween(me, unit)) { - // Allow short-distance walking for melee skills - let walk = (range < 4 || (skill === sdk.skills.ChargedBolt && range === 7)) && unit.distance < 10 && !checkCollision(me, unit, Coords_1.BlockBits.BlockWall); - - if (ranged) { - if (!Attack.getIntoPosition(unit, range, Coords_1.Collision.BLOCK_MISSILE, walk)) return Attack.Result.FAILED; - } else if (!Attack.getIntoPosition(unit, range, Coords_1.BlockBits.Ranged, walk)) { - return Attack.Result.FAILED; - } - } - - if (!unit.dead && !checkCollision(me, unit, Coords_1.BlockBits.Ranged)) { - if (skill === sdk.skills.ChargedBolt) { - let preHealth = unit.hp; - let cRetry = 0; - unit.distance <= 1 && Attack.getIntoPosition(unit, range, Coords_1.Collision.BLOCK_MISSILE, true); - for (let i = 0; i < 3; i++) { - !unit.dead && Skill.cast(skill, Skill.getHand(skill), unit.x, unit.y); - if (!Misc.poll(() => unit.dead || unit.hp < preHealth, 300, 50)) { - cRetry++; - // we still might of missed so pick another coord - if (!Attack.getIntoPosition(unit, (range - cRetry), Coords_1.Collision.BLOCK_MISSILE, true)) return Attack.Result.FAILED; - !unit.dead && Skill.cast(skill, Skill.getHand(skill), unit.x, unit.y); - } else { - break; - } - } - } else if (skill === sdk.skills.StaticField) { - let preHealth = unit.hp; - let sRetry = 0; - for (let i = 0; i < 4; i++) { - if (!unit.dead) { - // if we are already in close then it might be worth it to use battle cry if we have it - battleCryCheck(unit); - Skill.cast(skill, Skill.getHand(skill), unit); - if (!Misc.poll(() => unit.dead || unit.hp < preHealth, 200, 50)) { - sRetry++; - // we still might of missed so pick another coord - if (!Attack.getIntoPosition(unit, (range - sRetry), Coords_1.Collision.BLOCK_MISSILE, true)) return Attack.Result.FAILED; - !unit.dead && Skill.cast(skill, Skill.getHand(skill), unit); - } - - if (AttackData.FrostNova.have && me.mp > AttackData.FrostNova.mana) { - frostNovaCheck() && Skill.cast(sdk.skills.FrostNova, sdk.skills.hand.Right); - } - - if (mana > me.mp || unit.hpPercent < Config.CastStatic) { - break; - } - if (me.inDanger()) { - Attack.deploy(unit, range, 5, 9); - break; - } - } else { - break; - } - } - } else { - let targetPoint = GameData.targetPointForSkill(skill, unit); - - if (unit.attackable) { - if (targetPoint) { - Skill.cast(skill, Skill.getHand(skill), targetPoint.x, targetPoint.y); - } else { - Skill.cast(skill, Skill.getHand(skill), unit); - } - - if ([sdk.skills.FireBolt, sdk.skills.IceBolt].includes(skill)) { - let preHealth = unit.hp; - let missileDelay = GameData.timeTillMissleImpact(skill, unit); - missileDelay > 0 && Misc.poll(() => unit.dead || unit.hp < preHealth, missileDelay, 50); - delay(50); - } - } - } - } - - return Attack.Result.SUCCESS; - } else { - console.debug(choosenSkill); - noMana = true; - } - - for (let i = 0; i < 25; i++) { - if (!me.skillDelay) { - break; - } - if (i % 5 === 0) { - if (me.inDanger()) { - break; - } - } - - delay(40); - } - - return noMana ? Attack.Result.NEEDMANA : Attack.Result.SUCCESS; - }; + } else if (me.inDanger()) { + Attack.getIntoPosition(unit, range + 1, Coords_1.Collision.BLOCK_MISSILE, true); + } else if (unit.distance < 3 && range > 4) { + Attack.getIntoPosition(unit, range, Coords_1.Collision.BLOCK_MISSILE, true); + } + } + + if (skill > -1 && (!me.skillDelay || !timed)) { + let ranged = range > 4; + + if (skill === sdk.skills.ChargedBolt && !unit.hasEnchant(sdk.enchant.ManaBurn, sdk.enchant.ColdEnchanted)) { + unit.getMobCount(6, Coords_1.Collision.BLOCK_MISSILE) < 3 && (range = 7); + } + + if (skill === sdk.skills.Attack) { + if (me.hpPercent < 50 && me.mode !== sdk.player.mode.GettingHit && !me.checkForMobs({ range: 12 })) { + console.log("Low health but safe right now, going to delay a bit"); + let tick = getTickCount(); + const howLongToDelay = Config.AttackSkill.some(sk => sk > 1 && Skill.canUse(sk)) ? Time.seconds(2) : Time.seconds(1); + + while (getTickCount() - tick < howLongToDelay) { + if (me.mode === sdk.player.mode.GettingHit) { + console.debug("no longer safe, we are being attacked"); + break; + } else if (me.hpPercent >= 55) { + return 3; + } + + delay(40); + } + } + } + + if (range < 4 && !Attack.validSpot(unit.x, unit.y)) return Attack.Result.FAILED; + + // Only delay if there are no mobs in our immediate area + if (mana > me.mp && !me.checkForMobs({ range: 12 })) { + let tick = getTickCount(); + + while (getTickCount() - tick < 750) { + if (mana < me.mp) { + break; + } else if (me.mode === sdk.player.mode.GettingHit) { + console.debug("no longer safe, we are being attacked"); + return Attack.Result.NEEDMANA; + } + + delay(25); + } + } + + // try to prevent missing when the monster is moving by getting just a bit closer + if ([sdk.skills.FireBolt, sdk.skills.IceBolt].includes(skill)) { + range = 12; + } + + if (unit.distance > range || Coords_1.isBlockedBetween(me, unit)) { + // Allow short-distance walking for melee skills + let walk = (range < 4 || (skill === sdk.skills.ChargedBolt && range === 7)) && unit.distance < 10 && !checkCollision(me, unit, Coords_1.BlockBits.BlockWall); + + if (ranged) { + if (!Attack.getIntoPosition(unit, range, Coords_1.Collision.BLOCK_MISSILE, walk)) return Attack.Result.FAILED; + } else if (!Attack.getIntoPosition(unit, range, Coords_1.BlockBits.Ranged, walk)) { + return Attack.Result.FAILED; + } + } + + if (!unit.dead && !checkCollision(me, unit, Coords_1.BlockBits.Ranged)) { + if (skill === sdk.skills.ChargedBolt) { + let preHealth = unit.hp; + let cRetry = 0; + unit.distance <= 1 && Attack.getIntoPosition(unit, range, Coords_1.Collision.BLOCK_MISSILE, true); + for (let i = 0; i < 3; i++) { + !unit.dead && Skill.cast(skill, Skill.getHand(skill), unit.x, unit.y); + if (!Misc.poll(() => unit.dead || unit.hp < preHealth, 300, 50)) { + cRetry++; + // we still might of missed so pick another coord + if (!Attack.getIntoPosition(unit, (range - cRetry), Coords_1.Collision.BLOCK_MISSILE, true)) return Attack.Result.FAILED; + !unit.dead && Skill.cast(skill, Skill.getHand(skill), unit.x, unit.y); + } else { + break; + } + } + } else if (skill === sdk.skills.StaticField) { + let preHealth = unit.hp; + let sRetry = 0; + for (let i = 0; i < 4; i++) { + if (!unit.dead) { + // if we are already in close then it might be worth it to use battle cry if we have it + battleCryCheck(unit); + Skill.cast(skill, Skill.getHand(skill), unit); + if (!Misc.poll(() => unit.dead || unit.hp < preHealth, 200, 50)) { + sRetry++; + // we still might of missed so pick another coord + if (!Attack.getIntoPosition(unit, (range - sRetry), Coords_1.Collision.BLOCK_MISSILE, true)) return Attack.Result.FAILED; + !unit.dead && Skill.cast(skill, Skill.getHand(skill), unit); + } + + if (AttackData.FrostNova.have && me.mp > AttackData.FrostNova.mana) { + frostNovaCheck() && Skill.cast(sdk.skills.FrostNova, sdk.skills.hand.Right); + } + + if (mana > me.mp || unit.hpPercent < Config.CastStatic) { + break; + } + if (me.inDanger()) { + Attack.deploy(unit, range, 5, 9); + break; + } + } else { + break; + } + } + } else { + let targetPoint = GameData.targetPointForSkill(skill, unit); + + if (unit.attackable) { + if (targetPoint) { + Skill.cast(skill, Skill.getHand(skill), targetPoint.x, targetPoint.y); + } else { + Skill.cast(skill, Skill.getHand(skill), unit); + } + + if ([sdk.skills.FireBolt, sdk.skills.IceBolt].includes(skill)) { + let preHealth = unit.hp; + let missileDelay = GameData.timeTillMissleImpact(skill, unit); + missileDelay > 0 && Misc.poll(() => unit.dead || unit.hp < preHealth, missileDelay, 50); + delay(50); + } + } + } + } + + return Attack.Result.SUCCESS; + } else { + console.debug(choosenSkill); + noMana = true; + } + + for (let i = 0; i < 25; i++) { + if (!me.skillDelay) { + break; + } + if (i % 5 === 0) { + if (me.inDanger()) { + break; + } + } + + delay(40); + } + + return noMana ? Attack.Result.NEEDMANA : Attack.Result.SUCCESS; + }; })(); diff --git a/libs/SoloPlay/Functions/ConfigOverrides.js b/libs/SoloPlay/Functions/ConfigOverrides.js index f973cff5..f9a0d042 100644 --- a/libs/SoloPlay/Functions/ConfigOverrides.js +++ b/libs/SoloPlay/Functions/ConfigOverrides.js @@ -8,52 +8,52 @@ includeIfNotIncluded("core/Config.js"); Config.init = function (notify) { - const formats = ((className, profile, charname, realm) => ({ - // Class.Profile.js - 1: className + "." + profile + ".js", - // Realm.Class.Charname.js - 2: realm + "." + className + "." + charname + ".js", - // Class.Charname.js - 3: className + "." + charname + ".js", - // Profile.js - 4: profile + ".js", - // Class.js - 5: className + ".js", - }))(sdk.player.class.nameOf(me.classid), me.profile, me.charname, me.realm); - let configFilename = ""; - - for (let i = 1; i <= 5; i++) { - configFilename = formats[i]; - - if (configFilename && FileTools.exists("libs/SoloPlay/Config/" + configFilename)) { - break; - } - } - - try { - if (!include("SoloPlay/Config/" + configFilename)) { - throw new Error(); - } - notify && console.log("ÿc2Loaded: ÿc9SoloPlay/Config/" + configFilename); - } catch (e1) { - console.error("ÿc1" + e1 + "\nÿc0If you are seeing this message you likely did not copy over all the files or are using the wrong kolbot version."); - D2Bot.printToConsole("Please return to the kolbot-SoloPlay main github page and read the readMe. https://github.com/blizzhackers/kolbot-SoloPlay#readme", sdk.colors.D2Bot.Orange); - - throw new Error("Failed to load character config."); - } - - if (Config.Silence && !Config.LocalChat.Enabled) { - // Override the say function with print, so it just gets printed to console - global._say = global.say; - global.say = (what) => console.log("Tryed to say: " + what); - } - - try { - if (Config.AutoBuild.Enabled === true && include("SoloPlay/Functions/AutoBuild.js")) { - AutoBuild.initialize(); - } - } catch (e3) { - console.log("ÿc8Error in libs/SoloPlay/Functions/AutoBuild.js (AutoBuild system is not active!)"); - console.error(e3); - } + const formats = ((className, profile, charname, realm) => ({ + // Class.Profile.js + 1: className + "." + profile + ".js", + // Realm.Class.Charname.js + 2: realm + "." + className + "." + charname + ".js", + // Class.Charname.js + 3: className + "." + charname + ".js", + // Profile.js + 4: profile + ".js", + // Class.js + 5: className + ".js", + }))(sdk.player.class.nameOf(me.classid), me.profile, me.charname, me.realm); + let configFilename = ""; + + for (let i = 1; i <= 5; i++) { + configFilename = formats[i]; + + if (configFilename && FileTools.exists("libs/SoloPlay/Config/" + configFilename)) { + break; + } + } + + try { + if (!include("SoloPlay/Config/" + configFilename)) { + throw new Error(); + } + notify && console.log("ÿc2Loaded: ÿc9SoloPlay/Config/" + configFilename); + } catch (e1) { + console.error("ÿc1" + e1 + "\nÿc0If you are seeing this message you likely did not copy over all the files or are using the wrong kolbot version."); + D2Bot.printToConsole("Please return to the kolbot-SoloPlay main github page and read the readMe. https://github.com/blizzhackers/kolbot-SoloPlay#readme", sdk.colors.D2Bot.Orange); + + throw new Error("Failed to load character config."); + } + + if (Config.Silence && !Config.LocalChat.Enabled) { + // Override the say function with print, so it just gets printed to console + global._say = global.say; + global.say = (what) => console.log("Tryed to say: " + what); + } + + try { + if (Config.AutoBuild.Enabled === true && include("SoloPlay/Functions/AutoBuild.js")) { + AutoBuild.initialize(); + } + } catch (e3) { + console.log("ÿc8Error in libs/SoloPlay/Functions/AutoBuild.js (AutoBuild system is not active!)"); + console.error(e3); + } }; diff --git a/libs/SoloPlay/Functions/CubingOverrides.js b/libs/SoloPlay/Functions/CubingOverrides.js index 3e388052..cea34798 100644 --- a/libs/SoloPlay/Functions/CubingOverrides.js +++ b/libs/SoloPlay/Functions/CubingOverrides.js @@ -13,634 +13,634 @@ Recipe.Socket.HighMagic = 58; Recipe.Socket.Rare = 59; Cubing.buildRecipes = function () { - this.recipes = []; - - for (let i = 0; i < Config.Recipes.length; i += 1) { - if (typeof Config.Recipes[i] !== "object" - || (Config.Recipes[i].length > 2 - && ((Config.Recipes[i][0] !== Recipe.Reroll.Charm && typeof Config.Recipes[i][2] !== "number") || (Config.Recipes[i][0] === Recipe.Reroll.Charm && typeof Config.Recipes[i][2] !== "object"))) - || Config.Recipes[i].length < 1) { - throw new Error("Cubing.buildRecipes: Invalid recipe format."); - } - - switch (Config.Recipes[i][0]) { - case Recipe.Gem: - this.recipes.push({ Ingredients: [Config.Recipes[i][1], Config.Recipes[i][1], Config.Recipes[i][1]], Index: Recipe.Gem, AlwaysEnabled: true }); - - break; - // Crafting Recipes----------------------------------------------------------------------------------------------------------------------------------// - case Recipe.HitPower.Helm: - this.recipes.push({ Ingredients: [Config.Recipes[i][1], sdk.items.runes.Ith, sdk.items.Jewel, sdk.items.gems.Perfect.Sapphire], Level: 84, Index: Recipe.HitPower.Helm }); + this.recipes = []; + + for (let i = 0; i < Config.Recipes.length; i += 1) { + if (typeof Config.Recipes[i] !== "object" + || (Config.Recipes[i].length > 2 + && ((Config.Recipes[i][0] !== Recipe.Reroll.Charm && typeof Config.Recipes[i][2] !== "number") || (Config.Recipes[i][0] === Recipe.Reroll.Charm && typeof Config.Recipes[i][2] !== "object"))) + || Config.Recipes[i].length < 1) { + throw new Error("Cubing.buildRecipes: Invalid recipe format."); + } + + switch (Config.Recipes[i][0]) { + case Recipe.Gem: + this.recipes.push({ Ingredients: [Config.Recipes[i][1], Config.Recipes[i][1], Config.Recipes[i][1]], Index: Recipe.Gem, AlwaysEnabled: true }); + + break; + // Crafting Recipes----------------------------------------------------------------------------------------------------------------------------------// + case Recipe.HitPower.Helm: + this.recipes.push({ Ingredients: [Config.Recipes[i][1], sdk.items.runes.Ith, sdk.items.Jewel, sdk.items.gems.Perfect.Sapphire], Level: 84, Index: Recipe.HitPower.Helm }); - break; - case Recipe.HitPower.Boots: - this.recipes.push({ Ingredients: [Config.Recipes[i][1], sdk.items.runes.Ral, sdk.items.Jewel, sdk.items.gems.Perfect.Sapphire], Level: 71, Index: Recipe.HitPower.Boots }); + break; + case Recipe.HitPower.Boots: + this.recipes.push({ Ingredients: [Config.Recipes[i][1], sdk.items.runes.Ral, sdk.items.Jewel, sdk.items.gems.Perfect.Sapphire], Level: 71, Index: Recipe.HitPower.Boots }); - break; - case Recipe.HitPower.Gloves: - this.recipes.push({ Ingredients: [Config.Recipes[i][1], sdk.items.runes.Ort, sdk.items.Jewel, sdk.items.gems.Perfect.Sapphire], Level: 79, Index: Recipe.HitPower.Gloves }); + break; + case Recipe.HitPower.Gloves: + this.recipes.push({ Ingredients: [Config.Recipes[i][1], sdk.items.runes.Ort, sdk.items.Jewel, sdk.items.gems.Perfect.Sapphire], Level: 79, Index: Recipe.HitPower.Gloves }); - break; - case Recipe.HitPower.Belt: - this.recipes.push({ Ingredients: [Config.Recipes[i][1], sdk.items.runes.Tal, sdk.items.Jewel, sdk.items.gems.Perfect.Sapphire], Level: 71, Index: Recipe.HitPower.Belt }); + break; + case Recipe.HitPower.Belt: + this.recipes.push({ Ingredients: [Config.Recipes[i][1], sdk.items.runes.Tal, sdk.items.Jewel, sdk.items.gems.Perfect.Sapphire], Level: 71, Index: Recipe.HitPower.Belt }); - break; - case Recipe.HitPower.Shield: - this.recipes.push({ Ingredients: [Config.Recipes[i][1], sdk.items.runes.Eth, sdk.items.Jewel, sdk.items.gems.Perfect.Sapphire], Level: 82, Index: Recipe.HitPower.Shield }); + break; + case Recipe.HitPower.Shield: + this.recipes.push({ Ingredients: [Config.Recipes[i][1], sdk.items.runes.Eth, sdk.items.Jewel, sdk.items.gems.Perfect.Sapphire], Level: 82, Index: Recipe.HitPower.Shield }); - break; - case Recipe.HitPower.Body: - this.recipes.push({ Ingredients: [Config.Recipes[i][1], sdk.items.runes.Nef, sdk.items.Jewel, sdk.items.gems.Perfect.Sapphire], Level: 85, Index: Recipe.HitPower.Body }); + break; + case Recipe.HitPower.Body: + this.recipes.push({ Ingredients: [Config.Recipes[i][1], sdk.items.runes.Nef, sdk.items.Jewel, sdk.items.gems.Perfect.Sapphire], Level: 85, Index: Recipe.HitPower.Body }); - break; - case Recipe.HitPower.Amulet: - this.recipes.push({ Ingredients: [sdk.items.Amulet, sdk.items.runes.Thul, sdk.items.Jewel, sdk.items.gems.Perfect.Sapphire], Level: 90, Index: Recipe.HitPower.Amulet }); + break; + case Recipe.HitPower.Amulet: + this.recipes.push({ Ingredients: [sdk.items.Amulet, sdk.items.runes.Thul, sdk.items.Jewel, sdk.items.gems.Perfect.Sapphire], Level: 90, Index: Recipe.HitPower.Amulet }); - break; - case Recipe.HitPower.Ring: - this.recipes.push({ Ingredients: [sdk.items.Ring, sdk.items.runes.Amn, sdk.items.Jewel, sdk.items.gems.Perfect.Sapphire], Level: 77, Index: Recipe.HitPower.Ring }); + break; + case Recipe.HitPower.Ring: + this.recipes.push({ Ingredients: [sdk.items.Ring, sdk.items.runes.Amn, sdk.items.Jewel, sdk.items.gems.Perfect.Sapphire], Level: 77, Index: Recipe.HitPower.Ring }); - break; - case Recipe.HitPower.Weapon: - this.recipes.push({ Ingredients: [Config.Recipes[i][1], sdk.items.runes.Tir, sdk.items.Jewel, sdk.items.gems.Perfect.Sapphire], Level: 85, Index: Recipe.HitPower.Weapon }); + break; + case Recipe.HitPower.Weapon: + this.recipes.push({ Ingredients: [Config.Recipes[i][1], sdk.items.runes.Tir, sdk.items.Jewel, sdk.items.gems.Perfect.Sapphire], Level: 85, Index: Recipe.HitPower.Weapon }); - break; - case Recipe.Blood.Helm: - this.recipes.push({ Ingredients: [Config.Recipes[i][1], sdk.items.runes.Ral, sdk.items.Jewel, sdk.items.gems.Perfect.Ruby], Level: 84, Index: Recipe.Blood.Helm }); + break; + case Recipe.Blood.Helm: + this.recipes.push({ Ingredients: [Config.Recipes[i][1], sdk.items.runes.Ral, sdk.items.Jewel, sdk.items.gems.Perfect.Ruby], Level: 84, Index: Recipe.Blood.Helm }); - break; - case Recipe.Blood.Boots: - this.recipes.push({ Ingredients: [Config.Recipes[i][1], sdk.items.runes.Eth, sdk.items.Jewel, sdk.items.gems.Perfect.Ruby], Level: 71, Index: Recipe.Blood.Boots }); + break; + case Recipe.Blood.Boots: + this.recipes.push({ Ingredients: [Config.Recipes[i][1], sdk.items.runes.Eth, sdk.items.Jewel, sdk.items.gems.Perfect.Ruby], Level: 71, Index: Recipe.Blood.Boots }); - break; - case Recipe.Blood.Gloves: - this.recipes.push({ Ingredients: [Config.Recipes[i][1], sdk.items.runes.Nef, sdk.items.Jewel, sdk.items.gems.Perfect.Ruby], Level: 79, Index: Recipe.Blood.Gloves }); - - break; - case Recipe.Blood.Belt: - this.recipes.push({ Ingredients: [Config.Recipes[i][1], sdk.items.runes.Tal, sdk.items.Jewel, sdk.items.gems.Perfect.Ruby], Level: 71, Index: Recipe.Blood.Belt }); - - break; - case Recipe.Blood.Shield: - this.recipes.push({ Ingredients: [Config.Recipes[i][1], sdk.items.runes.Ith, sdk.items.Jewel, sdk.items.gems.Perfect.Ruby], Level: 82, Index: Recipe.Blood.Shield }); - - break; - case Recipe.Blood.Body: - this.recipes.push({ Ingredients: [Config.Recipes[i][1], sdk.items.runes.Thul, sdk.items.Jewel, sdk.items.gems.Perfect.Ruby], Level: 85, Index: Recipe.Blood.Body }); - - break; - case Recipe.Blood.Amulet: - this.recipes.push({ Ingredients: [sdk.items.Amulet, sdk.items.runes.Amn, sdk.items.Jewel, sdk.items.gems.Perfect.Ruby], Level: 90, Index: Recipe.Blood.Amulet }); - - break; - case Recipe.Blood.Ring: - this.recipes.push({ Ingredients: [sdk.items.Ring, sdk.items.runes.Sol, sdk.items.Jewel, sdk.items.gems.Perfect.Ruby], Level: 77, Index: Recipe.Blood.Ring }); - - break; - case Recipe.Blood.Weapon: - this.recipes.push({ Ingredients: [Config.Recipes[i][1], sdk.items.runes.Ort, sdk.items.Jewel, sdk.items.gems.Perfect.Ruby], Level: 85, Index: Recipe.Blood.Weapon }); - - break; - case Recipe.Caster.Helm: - this.recipes.push({ Ingredients: [Config.Recipes[i][1], sdk.items.runes.Nef, sdk.items.Jewel, sdk.items.gems.Perfect.Amethyst], Level: 84, Index: Recipe.Caster.Helm }); - - break; - case Recipe.Caster.Boots: - this.recipes.push({ Ingredients: [Config.Recipes[i][1], sdk.items.runes.Thul, sdk.items.Jewel, sdk.items.gems.Perfect.Amethyst], Level: 71, Index: Recipe.Caster.Boots }); - - break; - case Recipe.Caster.Gloves: - this.recipes.push({ Ingredients: [Config.Recipes[i][1], sdk.items.runes.Ort, sdk.items.Jewel, sdk.items.gems.Perfect.Amethyst], Level: 79, Index: Recipe.Caster.Gloves }); - - break; - case Recipe.Caster.Belt: - this.recipes.push({ Ingredients: [Config.Recipes[i][1], sdk.items.runes.Ith, sdk.items.Jewel, sdk.items.gems.Perfect.Amethyst], Level: 71, Index: Recipe.Caster.Belt }); - - break; - case Recipe.Caster.Shield: - this.recipes.push({ Ingredients: [Config.Recipes[i][1], sdk.items.runes.Eth, sdk.items.Jewel, sdk.items.gems.Perfect.Amethyst], Level: 82, Index: Recipe.Caster.Shield }); - - break; - case Recipe.Caster.Body: - this.recipes.push({ Ingredients: [Config.Recipes[i][1], sdk.items.runes.Tal, sdk.items.Jewel, sdk.items.gems.Perfect.Amethyst], Level: 85, Index: Recipe.Caster.Body }); - - break; - case Recipe.Caster.Amulet: - this.recipes.push({ Ingredients: [sdk.items.Amulet, sdk.items.runes.Ral, sdk.items.Jewel, sdk.items.gems.Perfect.Amethyst], Level: 90, Index: Recipe.Caster.Amulet }); - - break; - case Recipe.Caster.Ring: - this.recipes.push({ Ingredients: [sdk.items.Ring, sdk.items.runes.Amn, sdk.items.Jewel, sdk.items.gems.Perfect.Amethyst], Level: 77, Index: Recipe.Caster.Ring }); - - break; - case Recipe.Caster.Weapon: - this.recipes.push({ Ingredients: [Config.Recipes[i][1], sdk.items.runes.Tir, sdk.items.Jewel, sdk.items.gems.Perfect.Amethyst], Level: 85, Index: Recipe.Caster.Weapon }); - - break; - case Recipe.Safety.Helm: - this.recipes.push({ Ingredients: [Config.Recipes[i][1], sdk.items.runes.Ith, sdk.items.Jewel, sdk.items.gems.Perfect.Emerald], Level: 84, Index: Recipe.Safety.Helm }); - - break; - case Recipe.Safety.Boots: - this.recipes.push({ Ingredients: [Config.Recipes[i][1], sdk.items.runes.Ort, sdk.items.Jewel, sdk.items.gems.Perfect.Emerald], Level: 71, Index: Recipe.Safety.Boots }); - - break; - case Recipe.Safety.Gloves: - this.recipes.push({ Ingredients: [Config.Recipes[i][1], sdk.items.runes.Ral, sdk.items.Jewel, sdk.items.gems.Perfect.Emerald], Level: 79, Index: Recipe.Safety.Gloves }); - - break; - case Recipe.Safety.Belt: - this.recipes.push({ Ingredients: [Config.Recipes[i][1], sdk.items.runes.Tal, sdk.items.Jewel, sdk.items.gems.Perfect.Emerald], Level: 71, Index: Recipe.Safety.Belt }); - - break; - case Recipe.Safety.Shield: - this.recipes.push({ Ingredients: [Config.Recipes[i][1], sdk.items.runes.Nef, sdk.items.Jewel, sdk.items.gems.Perfect.Emerald], Level: 82, Index: Recipe.Safety.Shield }); - - break; - case Recipe.Safety.Body: - this.recipes.push({ Ingredients: [Config.Recipes[i][1], sdk.items.runes.Eth, sdk.items.Jewel, sdk.items.gems.Perfect.Emerald], Level: 85, Index: Recipe.Safety.Body }); - - break; - case Recipe.Safety.Amulet: - this.recipes.push({ Ingredients: [sdk.items.Amulet, sdk.items.runes.Thul, sdk.items.Jewel, sdk.items.gems.Perfect.Emerald], Level: 90, Index: Recipe.Safety.Amulet }); - - break; - case Recipe.Safety.Ring: - this.recipes.push({ Ingredients: [sdk.items.Ring, sdk.items.runes.Amn, sdk.items.Jewel, sdk.items.gems.Perfect.Emerald], Level: 77, Index: Recipe.Safety.Ring }); - - break; - case Recipe.Safety.Weapon: - this.recipes.push({ Ingredients: [Config.Recipes[i][1], sdk.items.runes.Sol, sdk.items.Jewel, sdk.items.gems.Perfect.Emerald], Level: 85, Index: Recipe.Safety.Weapon }); - - break; - // Upgrading Recipes----------------------------------------------------------------------------------------------------------------------------------// - case Recipe.Unique.Weapon.ToExceptional: - this.recipes.push({ Ingredients: [Config.Recipes[i][1], sdk.items.runes.Ral, sdk.items.runes.Sol, sdk.items.gems.Perfect.Emerald], Index: Recipe.Unique.Weapon.ToExceptional, Ethereal: Config.Recipes[i][2] }); - - break; - case Recipe.Unique.Weapon.ToElite: // Ladder only - if (me.ladder || Developer.addLadderRW) { - this.recipes.push({ Ingredients: [Config.Recipes[i][1], sdk.items.runes.Lum, sdk.items.runes.Pul, sdk.items.gems.Perfect.Emerald], Index: Recipe.Unique.Weapon.ToElite, Ethereal: Config.Recipes[i][2] }); - } - - break; - case Recipe.Unique.Armor.ToExceptional: - this.recipes.push({ Ingredients: [Config.Recipes[i][1], sdk.items.runes.Tal, sdk.items.runes.Shael, sdk.items.gems.Perfect.Diamond], Index: Recipe.Unique.Armor.ToExceptional, Ethereal: Config.Recipes[i][2] }); - - break; - case Recipe.Unique.Armor.ToElite: // Ladder only - if (me.ladder || Developer.addLadderRW) { - this.recipes.push({ Ingredients: [Config.Recipes[i][1], sdk.items.runes.Lem, sdk.items.runes.Ko, sdk.items.gems.Perfect.Diamond], Index: Recipe.Unique.Armor.ToElite, Ethereal: Config.Recipes[i][2] }); - } - - break; - case Recipe.Rare.Weapon.ToExceptional: - this.recipes.push({ Ingredients: [Config.Recipes[i][1], sdk.items.runes.Ort, sdk.items.runes.Amn, sdk.items.gems.Perfect.Sapphire], Index: Recipe.Rare.Weapon.ToExceptional, Ethereal: Config.Recipes[i][2] }); - - break; - case Recipe.Rare.Weapon.ToElite: - this.recipes.push({ Ingredients: [Config.Recipes[i][1], sdk.items.runes.Fal, sdk.items.runes.Um, sdk.items.gems.Perfect.Sapphire], Index: Recipe.Rare.Weapon.ToElite, Ethereal: Config.Recipes[i][2] }); - - break; - case Recipe.Rare.Armor.ToExceptional: - this.recipes.push({ Ingredients: [Config.Recipes[i][1], sdk.items.runes.Ral, sdk.items.runes.Thul, sdk.items.gems.Perfect.Amethyst], Index: Recipe.Rare.Armor.ToExceptional, Ethereal: Config.Recipes[i][2] }); - - break; - case Recipe.Rare.Armor.ToElite: - this.recipes.push({ Ingredients: [Config.Recipes[i][1], sdk.items.runes.Ko, sdk.items.runes.Pul, sdk.items.gems.Perfect.Amethyst], Index: Recipe.Rare.Armor.ToElite, Ethereal: Config.Recipes[i][2] }); - - break; - // Socketing Recipes----------------------------------------------------------------------------------------------------------------------------------// - case Recipe.Socket.Shield: - this.recipes.push({ Ingredients: [Config.Recipes[i][1], sdk.items.runes.Tal, sdk.items.runes.Amn, sdk.items.gems.Perfect.Ruby], Index: Recipe.Socket.Shield, Ethereal: Config.Recipes[i][2] }); - - break; - case Recipe.Socket.Weapon: - this.recipes.push({ Ingredients: [Config.Recipes[i][1], sdk.items.runes.Ral, sdk.items.runes.Amn, sdk.items.gems.Perfect.Amethyst], Index: Recipe.Socket.Weapon, Ethereal: Config.Recipes[i][2] }); - - break; - case Recipe.Socket.Armor: - this.recipes.push({ Ingredients: [Config.Recipes[i][1], sdk.items.runes.Tal, sdk.items.runes.Thul, sdk.items.gems.Perfect.Topaz], Index: Recipe.Socket.Armor, Ethereal: Config.Recipes[i][2] }); - - break; - case Recipe.Socket.Helm: - this.recipes.push({ Ingredients: [Config.Recipes[i][1], sdk.items.runes.Ral, sdk.items.runes.Thul, sdk.items.gems.Perfect.Sapphire], Index: Recipe.Socket.Helm, Ethereal: Config.Recipes[i][2] }); - - break; - case Recipe.Socket.LowMagic: - this.recipes.push({ Ingredients: [Config.Recipes[i][1], "cgem", "cgem", "cgem"], Level: 25, Index: Recipe.Socket.LowMagic }); - - break; - case Recipe.Socket.HighMagic: - this.recipes.push({ Ingredients: [Config.Recipes[i][1], "fgem", "fgem", "fgem"], Level: 30, Index: Recipe.Socket.HighMagic }); - - break; - case Recipe.Socket.Rare: - this.recipes.push({ Ingredients: [Config.Recipes[i][1], sdk.items.Ring, sdk.items.gems.Perfect.Skull, sdk.items.gems.Perfect.Skull, sdk.items.gems.Perfect.Skull], Index: Recipe.Socket.Rare }); - - break; - // Re-rolling Recipes----------------------------------------------------------------------------------------------------------------------------------// - case Recipe.Reroll.Magic: // Hacky solution ftw - this.recipes.push({ Ingredients: [Config.Recipes[i][1], "pgem", "pgem", "pgem"], Level: 91, Index: Recipe.Reroll.Magic }); - - break; - case Recipe.Reroll.Charm: - this.recipes.push({ Ingredients: [Config.Recipes[i][1], "pgem", "pgem", "pgem"], Level: Object.assign({ "cm1": 95, "cm2": 91, "cm3": 91 }, Config.Recipes[i][2]), Index: Recipe.Reroll.Charm }); - - break; - case Recipe.Reroll.Rare: - this.recipes.push({ Ingredients: [Config.Recipes[i][1], sdk.items.gems.Perfect.Skull, sdk.items.gems.Perfect.Skull, sdk.items.gems.Perfect.Skull, sdk.items.gems.Perfect.Skull, sdk.items.gems.Perfect.Skull, sdk.items.gems.Perfect.Skull], Index: Recipe.Reroll.Rare }); - - break; - case Recipe.Reroll.HighRare: - this.recipes.push({ Ingredients: [Config.Recipes[i][1], sdk.items.gems.Perfect.Skull, sdk.items.Ring], Index: Recipe.Reroll.HighRare, Enabled: false }); - - break; - case Recipe.LowToNorm.Weapon: - this.recipes.push({ Ingredients: [Config.Recipes[i][1], sdk.items.runes.Eld, "cgem"], Index: Recipe.LowToNorm.Weapon }); - - break; - case Recipe.LowToNorm.Armor: - this.recipes.push({ Ingredients: [Config.Recipes[i][1], sdk.items.runes.El, "cgem"], Index: Recipe.LowToNorm.Armor }); - - break; - // Rune Recipes----------------------------------------------------------------------------------------------------------------------------------// - case Recipe.Rune: - switch (Config.Recipes[i][1]) { - case sdk.items.runes.El: - case sdk.items.runes.Eld: - case sdk.items.runes.Tir: - case sdk.items.runes.Nef: - case sdk.items.runes.Eth: - case sdk.items.runes.Ith: - case sdk.items.runes.Tal: - case sdk.items.runes.Ral: - case sdk.items.runes.Ort: - this.recipes.push({ Ingredients: [Config.Recipes[i][1], Config.Recipes[i][1], Config.Recipes[i][1]], Index: Recipe.Rune, AlwaysEnabled: true }); - - break; - case sdk.items.runes.Thul: // thul->amn - this.recipes.push({ Ingredients: [sdk.items.runes.Thul, sdk.items.runes.Thul, sdk.items.runes.Thul, sdk.items.gems.Chipped.Topaz], Index: Recipe.Rune }); - - break; - case sdk.items.runes.Amn: // amn->sol - this.recipes.push({ Ingredients: [sdk.items.runes.Amn, sdk.items.runes.Amn, sdk.items.runes.Amn, sdk.items.gems.Chipped.Amethyst], Index: Recipe.Rune }); - - break; - case sdk.items.runes.Sol: // sol->shael - this.recipes.push({ Ingredients: [sdk.items.runes.Sol, sdk.items.runes.Sol, sdk.items.runes.Sol, sdk.items.gems.Chipped.Sapphire], Index: Recipe.Rune }); - - break; - case sdk.items.runes.Shael: // shael->dol - this.recipes.push({ Ingredients: [sdk.items.runes.Shael, sdk.items.runes.Shael, sdk.items.runes.Shael, sdk.items.gems.Chipped.Ruby], Index: Recipe.Rune }); - - break; - case sdk.items.runes.Dol: // dol->hel - if (me.ladder || Developer.addLadderRW) { - this.recipes.push({ Ingredients: [sdk.items.runes.Dol, sdk.items.runes.Dol, sdk.items.runes.Dol, sdk.items.gems.Chipped.Emerald], Index: Recipe.Rune }); - } - - break; - case sdk.items.runes.Hel: // hel->io - if (me.ladder || Developer.addLadderRW) { - this.recipes.push({ Ingredients: [sdk.items.runes.Hel, sdk.items.runes.Hel, sdk.items.runes.Hel, sdk.items.gems.Chipped.Diamond], Index: Recipe.Rune }); - } - - break; - case sdk.items.runes.Io: // io->lum - if (me.ladder || Developer.addLadderRW) { - this.recipes.push({ Ingredients: [sdk.items.runes.Io, sdk.items.runes.Io, sdk.items.runes.Io, sdk.items.gems.Flawed.Topaz], Index: Recipe.Rune }); - } - - break; - case sdk.items.runes.Lum: // lum->ko - if (me.ladder || Developer.addLadderRW) { - this.recipes.push({ Ingredients: [sdk.items.runes.Lum, sdk.items.runes.Lum, sdk.items.runes.Lum, sdk.items.gems.Flawed.Amethyst], Index: Recipe.Rune }); - } - - break; - case sdk.items.runes.Ko: // ko->fal - if (me.ladder || Developer.addLadderRW) { - this.recipes.push({ Ingredients: [sdk.items.runes.Ko, sdk.items.runes.Ko, sdk.items.runes.Ko, sdk.items.gems.Flawed.Sapphire], Index: Recipe.Rune }); - } - - break; - case sdk.items.runes.Fal: // fal->lem - if (me.ladder || Developer.addLadderRW) { - this.recipes.push({ Ingredients: [sdk.items.runes.Fal, sdk.items.runes.Fal, sdk.items.runes.Fal, sdk.items.gems.Flawed.Ruby], Index: Recipe.Rune }); - } - - break; - case sdk.items.runes.Lem: // lem->pul - if (me.ladder || Developer.addLadderRW) { - this.recipes.push({ Ingredients: [sdk.items.runes.Lem, sdk.items.runes.Lem, sdk.items.runes.Lem, sdk.items.gems.Flawed.Emerald], Index: Recipe.Rune }); - } - - break; - case sdk.items.runes.Pul: // pul->um - if (me.ladder || Developer.addLadderRW) { - this.recipes.push({ Ingredients: [sdk.items.runes.Pul, sdk.items.runes.Pul, sdk.items.gems.Flawed.Diamond], Index: Recipe.Rune }); - } - - break; - case sdk.items.runes.Um: // um->mal - if (me.ladder || Developer.addLadderRW) { - this.recipes.push({ Ingredients: [sdk.items.runes.Um, sdk.items.runes.Um, sdk.items.gems.Normal.Topaz], Index: Recipe.Rune }); - } - - break; - case sdk.items.runes.Mal: // mal->ist - if (me.ladder || Developer.addLadderRW) { - this.recipes.push({ Ingredients: [sdk.items.runes.Mal, sdk.items.runes.Mal, sdk.items.gems.Normal.Amethyst], Index: Recipe.Rune }); - } - - break; - case sdk.items.runes.Ist: // ist->gul - if (me.ladder || Developer.addLadderRW) { - this.recipes.push({ Ingredients: [sdk.items.runes.Ist, sdk.items.runes.Ist, sdk.items.gems.Normal.Sapphire], Index: Recipe.Rune }); - } - - break; - case sdk.items.runes.Gul: // gul->vex - if (me.ladder || Developer.addLadderRW) { - this.recipes.push({ Ingredients: [sdk.items.runes.Gul, sdk.items.runes.Gul, sdk.items.gems.Normal.Ruby], Index: Recipe.Rune }); - } - - break; - case sdk.items.runes.Vex: // vex->ohm - if (me.ladder || Developer.addLadderRW) { - this.recipes.push({ Ingredients: [sdk.items.runes.Vex, sdk.items.runes.Vex, sdk.items.gems.Normal.Emerald], Index: Recipe.Rune }); - } - - break; - case sdk.items.runes.Ohm: // ohm->lo - if (me.ladder || Developer.addLadderRW) { - this.recipes.push({ Ingredients: [sdk.items.runes.Ohm, sdk.items.runes.Ohm, sdk.items.gems.Normal.Diamond], Index: Recipe.Rune }); - } - - break; - case sdk.items.runes.Lo: // lo->sur - if (me.ladder || Developer.addLadderRW) { - this.recipes.push({ Ingredients: [sdk.items.runes.Lo, sdk.items.runes.Lo, sdk.items.gems.Flawless.Topaz], Index: Recipe.Rune }); - } - - break; - case sdk.items.runes.Sur: // sur->ber - if (me.ladder || Developer.addLadderRW) { - this.recipes.push({ Ingredients: [sdk.items.runes.Sur, sdk.items.runes.Sur, sdk.items.gems.Flawless.Amethyst], Index: Recipe.Rune }); - } - - break; - case sdk.items.runes.Ber: // ber->jah - if (me.ladder || Developer.addLadderRW) { - this.recipes.push({ Ingredients: [sdk.items.runes.Ber, sdk.items.runes.Ber, sdk.items.gems.Flawless.Sapphire], Index: Recipe.Rune }); - } - - break; - case sdk.items.runes.Jah: // jah->cham - if (me.ladder || Developer.addLadderRW) { - this.recipes.push({ Ingredients: [sdk.items.runes.Jah, sdk.items.runes.Jah, sdk.items.gems.Flawless.Ruby], Index: Recipe.Rune }); - } - - break; - case sdk.items.runes.Cham: // cham->zod - if (me.ladder || Developer.addLadderRW) { - this.recipes.push({ Ingredients: [sdk.items.runes.Cham, sdk.items.runes.Cham, sdk.items.gems.Flawless.Emerald], Index: Recipe.Rune }); - } - - break; - } - - break; - case Recipe.Token: - this.recipes.push({ Ingredients: [sdk.quest.item.TwistedEssenceofSuffering, sdk.quest.item.ChargedEssenceofHatred, sdk.quest.item.BurningEssenceofTerror, sdk.quest.item.FesteringEssenceofDestruction], Index: Recipe.Token, AlwaysEnabled: true }); - - break; - } - } + break; + case Recipe.Blood.Gloves: + this.recipes.push({ Ingredients: [Config.Recipes[i][1], sdk.items.runes.Nef, sdk.items.Jewel, sdk.items.gems.Perfect.Ruby], Level: 79, Index: Recipe.Blood.Gloves }); + + break; + case Recipe.Blood.Belt: + this.recipes.push({ Ingredients: [Config.Recipes[i][1], sdk.items.runes.Tal, sdk.items.Jewel, sdk.items.gems.Perfect.Ruby], Level: 71, Index: Recipe.Blood.Belt }); + + break; + case Recipe.Blood.Shield: + this.recipes.push({ Ingredients: [Config.Recipes[i][1], sdk.items.runes.Ith, sdk.items.Jewel, sdk.items.gems.Perfect.Ruby], Level: 82, Index: Recipe.Blood.Shield }); + + break; + case Recipe.Blood.Body: + this.recipes.push({ Ingredients: [Config.Recipes[i][1], sdk.items.runes.Thul, sdk.items.Jewel, sdk.items.gems.Perfect.Ruby], Level: 85, Index: Recipe.Blood.Body }); + + break; + case Recipe.Blood.Amulet: + this.recipes.push({ Ingredients: [sdk.items.Amulet, sdk.items.runes.Amn, sdk.items.Jewel, sdk.items.gems.Perfect.Ruby], Level: 90, Index: Recipe.Blood.Amulet }); + + break; + case Recipe.Blood.Ring: + this.recipes.push({ Ingredients: [sdk.items.Ring, sdk.items.runes.Sol, sdk.items.Jewel, sdk.items.gems.Perfect.Ruby], Level: 77, Index: Recipe.Blood.Ring }); + + break; + case Recipe.Blood.Weapon: + this.recipes.push({ Ingredients: [Config.Recipes[i][1], sdk.items.runes.Ort, sdk.items.Jewel, sdk.items.gems.Perfect.Ruby], Level: 85, Index: Recipe.Blood.Weapon }); + + break; + case Recipe.Caster.Helm: + this.recipes.push({ Ingredients: [Config.Recipes[i][1], sdk.items.runes.Nef, sdk.items.Jewel, sdk.items.gems.Perfect.Amethyst], Level: 84, Index: Recipe.Caster.Helm }); + + break; + case Recipe.Caster.Boots: + this.recipes.push({ Ingredients: [Config.Recipes[i][1], sdk.items.runes.Thul, sdk.items.Jewel, sdk.items.gems.Perfect.Amethyst], Level: 71, Index: Recipe.Caster.Boots }); + + break; + case Recipe.Caster.Gloves: + this.recipes.push({ Ingredients: [Config.Recipes[i][1], sdk.items.runes.Ort, sdk.items.Jewel, sdk.items.gems.Perfect.Amethyst], Level: 79, Index: Recipe.Caster.Gloves }); + + break; + case Recipe.Caster.Belt: + this.recipes.push({ Ingredients: [Config.Recipes[i][1], sdk.items.runes.Ith, sdk.items.Jewel, sdk.items.gems.Perfect.Amethyst], Level: 71, Index: Recipe.Caster.Belt }); + + break; + case Recipe.Caster.Shield: + this.recipes.push({ Ingredients: [Config.Recipes[i][1], sdk.items.runes.Eth, sdk.items.Jewel, sdk.items.gems.Perfect.Amethyst], Level: 82, Index: Recipe.Caster.Shield }); + + break; + case Recipe.Caster.Body: + this.recipes.push({ Ingredients: [Config.Recipes[i][1], sdk.items.runes.Tal, sdk.items.Jewel, sdk.items.gems.Perfect.Amethyst], Level: 85, Index: Recipe.Caster.Body }); + + break; + case Recipe.Caster.Amulet: + this.recipes.push({ Ingredients: [sdk.items.Amulet, sdk.items.runes.Ral, sdk.items.Jewel, sdk.items.gems.Perfect.Amethyst], Level: 90, Index: Recipe.Caster.Amulet }); + + break; + case Recipe.Caster.Ring: + this.recipes.push({ Ingredients: [sdk.items.Ring, sdk.items.runes.Amn, sdk.items.Jewel, sdk.items.gems.Perfect.Amethyst], Level: 77, Index: Recipe.Caster.Ring }); + + break; + case Recipe.Caster.Weapon: + this.recipes.push({ Ingredients: [Config.Recipes[i][1], sdk.items.runes.Tir, sdk.items.Jewel, sdk.items.gems.Perfect.Amethyst], Level: 85, Index: Recipe.Caster.Weapon }); + + break; + case Recipe.Safety.Helm: + this.recipes.push({ Ingredients: [Config.Recipes[i][1], sdk.items.runes.Ith, sdk.items.Jewel, sdk.items.gems.Perfect.Emerald], Level: 84, Index: Recipe.Safety.Helm }); + + break; + case Recipe.Safety.Boots: + this.recipes.push({ Ingredients: [Config.Recipes[i][1], sdk.items.runes.Ort, sdk.items.Jewel, sdk.items.gems.Perfect.Emerald], Level: 71, Index: Recipe.Safety.Boots }); + + break; + case Recipe.Safety.Gloves: + this.recipes.push({ Ingredients: [Config.Recipes[i][1], sdk.items.runes.Ral, sdk.items.Jewel, sdk.items.gems.Perfect.Emerald], Level: 79, Index: Recipe.Safety.Gloves }); + + break; + case Recipe.Safety.Belt: + this.recipes.push({ Ingredients: [Config.Recipes[i][1], sdk.items.runes.Tal, sdk.items.Jewel, sdk.items.gems.Perfect.Emerald], Level: 71, Index: Recipe.Safety.Belt }); + + break; + case Recipe.Safety.Shield: + this.recipes.push({ Ingredients: [Config.Recipes[i][1], sdk.items.runes.Nef, sdk.items.Jewel, sdk.items.gems.Perfect.Emerald], Level: 82, Index: Recipe.Safety.Shield }); + + break; + case Recipe.Safety.Body: + this.recipes.push({ Ingredients: [Config.Recipes[i][1], sdk.items.runes.Eth, sdk.items.Jewel, sdk.items.gems.Perfect.Emerald], Level: 85, Index: Recipe.Safety.Body }); + + break; + case Recipe.Safety.Amulet: + this.recipes.push({ Ingredients: [sdk.items.Amulet, sdk.items.runes.Thul, sdk.items.Jewel, sdk.items.gems.Perfect.Emerald], Level: 90, Index: Recipe.Safety.Amulet }); + + break; + case Recipe.Safety.Ring: + this.recipes.push({ Ingredients: [sdk.items.Ring, sdk.items.runes.Amn, sdk.items.Jewel, sdk.items.gems.Perfect.Emerald], Level: 77, Index: Recipe.Safety.Ring }); + + break; + case Recipe.Safety.Weapon: + this.recipes.push({ Ingredients: [Config.Recipes[i][1], sdk.items.runes.Sol, sdk.items.Jewel, sdk.items.gems.Perfect.Emerald], Level: 85, Index: Recipe.Safety.Weapon }); + + break; + // Upgrading Recipes----------------------------------------------------------------------------------------------------------------------------------// + case Recipe.Unique.Weapon.ToExceptional: + this.recipes.push({ Ingredients: [Config.Recipes[i][1], sdk.items.runes.Ral, sdk.items.runes.Sol, sdk.items.gems.Perfect.Emerald], Index: Recipe.Unique.Weapon.ToExceptional, Ethereal: Config.Recipes[i][2] }); + + break; + case Recipe.Unique.Weapon.ToElite: // Ladder only + if (me.ladder || Developer.addLadderRW) { + this.recipes.push({ Ingredients: [Config.Recipes[i][1], sdk.items.runes.Lum, sdk.items.runes.Pul, sdk.items.gems.Perfect.Emerald], Index: Recipe.Unique.Weapon.ToElite, Ethereal: Config.Recipes[i][2] }); + } + + break; + case Recipe.Unique.Armor.ToExceptional: + this.recipes.push({ Ingredients: [Config.Recipes[i][1], sdk.items.runes.Tal, sdk.items.runes.Shael, sdk.items.gems.Perfect.Diamond], Index: Recipe.Unique.Armor.ToExceptional, Ethereal: Config.Recipes[i][2] }); + + break; + case Recipe.Unique.Armor.ToElite: // Ladder only + if (me.ladder || Developer.addLadderRW) { + this.recipes.push({ Ingredients: [Config.Recipes[i][1], sdk.items.runes.Lem, sdk.items.runes.Ko, sdk.items.gems.Perfect.Diamond], Index: Recipe.Unique.Armor.ToElite, Ethereal: Config.Recipes[i][2] }); + } + + break; + case Recipe.Rare.Weapon.ToExceptional: + this.recipes.push({ Ingredients: [Config.Recipes[i][1], sdk.items.runes.Ort, sdk.items.runes.Amn, sdk.items.gems.Perfect.Sapphire], Index: Recipe.Rare.Weapon.ToExceptional, Ethereal: Config.Recipes[i][2] }); + + break; + case Recipe.Rare.Weapon.ToElite: + this.recipes.push({ Ingredients: [Config.Recipes[i][1], sdk.items.runes.Fal, sdk.items.runes.Um, sdk.items.gems.Perfect.Sapphire], Index: Recipe.Rare.Weapon.ToElite, Ethereal: Config.Recipes[i][2] }); + + break; + case Recipe.Rare.Armor.ToExceptional: + this.recipes.push({ Ingredients: [Config.Recipes[i][1], sdk.items.runes.Ral, sdk.items.runes.Thul, sdk.items.gems.Perfect.Amethyst], Index: Recipe.Rare.Armor.ToExceptional, Ethereal: Config.Recipes[i][2] }); + + break; + case Recipe.Rare.Armor.ToElite: + this.recipes.push({ Ingredients: [Config.Recipes[i][1], sdk.items.runes.Ko, sdk.items.runes.Pul, sdk.items.gems.Perfect.Amethyst], Index: Recipe.Rare.Armor.ToElite, Ethereal: Config.Recipes[i][2] }); + + break; + // Socketing Recipes----------------------------------------------------------------------------------------------------------------------------------// + case Recipe.Socket.Shield: + this.recipes.push({ Ingredients: [Config.Recipes[i][1], sdk.items.runes.Tal, sdk.items.runes.Amn, sdk.items.gems.Perfect.Ruby], Index: Recipe.Socket.Shield, Ethereal: Config.Recipes[i][2] }); + + break; + case Recipe.Socket.Weapon: + this.recipes.push({ Ingredients: [Config.Recipes[i][1], sdk.items.runes.Ral, sdk.items.runes.Amn, sdk.items.gems.Perfect.Amethyst], Index: Recipe.Socket.Weapon, Ethereal: Config.Recipes[i][2] }); + + break; + case Recipe.Socket.Armor: + this.recipes.push({ Ingredients: [Config.Recipes[i][1], sdk.items.runes.Tal, sdk.items.runes.Thul, sdk.items.gems.Perfect.Topaz], Index: Recipe.Socket.Armor, Ethereal: Config.Recipes[i][2] }); + + break; + case Recipe.Socket.Helm: + this.recipes.push({ Ingredients: [Config.Recipes[i][1], sdk.items.runes.Ral, sdk.items.runes.Thul, sdk.items.gems.Perfect.Sapphire], Index: Recipe.Socket.Helm, Ethereal: Config.Recipes[i][2] }); + + break; + case Recipe.Socket.LowMagic: + this.recipes.push({ Ingredients: [Config.Recipes[i][1], "cgem", "cgem", "cgem"], Level: 25, Index: Recipe.Socket.LowMagic }); + + break; + case Recipe.Socket.HighMagic: + this.recipes.push({ Ingredients: [Config.Recipes[i][1], "fgem", "fgem", "fgem"], Level: 30, Index: Recipe.Socket.HighMagic }); + + break; + case Recipe.Socket.Rare: + this.recipes.push({ Ingredients: [Config.Recipes[i][1], sdk.items.Ring, sdk.items.gems.Perfect.Skull, sdk.items.gems.Perfect.Skull, sdk.items.gems.Perfect.Skull], Index: Recipe.Socket.Rare }); + + break; + // Re-rolling Recipes----------------------------------------------------------------------------------------------------------------------------------// + case Recipe.Reroll.Magic: // Hacky solution ftw + this.recipes.push({ Ingredients: [Config.Recipes[i][1], "pgem", "pgem", "pgem"], Level: 91, Index: Recipe.Reroll.Magic }); + + break; + case Recipe.Reroll.Charm: + this.recipes.push({ Ingredients: [Config.Recipes[i][1], "pgem", "pgem", "pgem"], Level: Object.assign({ "cm1": 95, "cm2": 91, "cm3": 91 }, Config.Recipes[i][2]), Index: Recipe.Reroll.Charm }); + + break; + case Recipe.Reroll.Rare: + this.recipes.push({ Ingredients: [Config.Recipes[i][1], sdk.items.gems.Perfect.Skull, sdk.items.gems.Perfect.Skull, sdk.items.gems.Perfect.Skull, sdk.items.gems.Perfect.Skull, sdk.items.gems.Perfect.Skull, sdk.items.gems.Perfect.Skull], Index: Recipe.Reroll.Rare }); + + break; + case Recipe.Reroll.HighRare: + this.recipes.push({ Ingredients: [Config.Recipes[i][1], sdk.items.gems.Perfect.Skull, sdk.items.Ring], Index: Recipe.Reroll.HighRare, Enabled: false }); + + break; + case Recipe.LowToNorm.Weapon: + this.recipes.push({ Ingredients: [Config.Recipes[i][1], sdk.items.runes.Eld, "cgem"], Index: Recipe.LowToNorm.Weapon }); + + break; + case Recipe.LowToNorm.Armor: + this.recipes.push({ Ingredients: [Config.Recipes[i][1], sdk.items.runes.El, "cgem"], Index: Recipe.LowToNorm.Armor }); + + break; + // Rune Recipes----------------------------------------------------------------------------------------------------------------------------------// + case Recipe.Rune: + switch (Config.Recipes[i][1]) { + case sdk.items.runes.El: + case sdk.items.runes.Eld: + case sdk.items.runes.Tir: + case sdk.items.runes.Nef: + case sdk.items.runes.Eth: + case sdk.items.runes.Ith: + case sdk.items.runes.Tal: + case sdk.items.runes.Ral: + case sdk.items.runes.Ort: + this.recipes.push({ Ingredients: [Config.Recipes[i][1], Config.Recipes[i][1], Config.Recipes[i][1]], Index: Recipe.Rune, AlwaysEnabled: true }); + + break; + case sdk.items.runes.Thul: // thul->amn + this.recipes.push({ Ingredients: [sdk.items.runes.Thul, sdk.items.runes.Thul, sdk.items.runes.Thul, sdk.items.gems.Chipped.Topaz], Index: Recipe.Rune }); + + break; + case sdk.items.runes.Amn: // amn->sol + this.recipes.push({ Ingredients: [sdk.items.runes.Amn, sdk.items.runes.Amn, sdk.items.runes.Amn, sdk.items.gems.Chipped.Amethyst], Index: Recipe.Rune }); + + break; + case sdk.items.runes.Sol: // sol->shael + this.recipes.push({ Ingredients: [sdk.items.runes.Sol, sdk.items.runes.Sol, sdk.items.runes.Sol, sdk.items.gems.Chipped.Sapphire], Index: Recipe.Rune }); + + break; + case sdk.items.runes.Shael: // shael->dol + this.recipes.push({ Ingredients: [sdk.items.runes.Shael, sdk.items.runes.Shael, sdk.items.runes.Shael, sdk.items.gems.Chipped.Ruby], Index: Recipe.Rune }); + + break; + case sdk.items.runes.Dol: // dol->hel + if (me.ladder || Developer.addLadderRW) { + this.recipes.push({ Ingredients: [sdk.items.runes.Dol, sdk.items.runes.Dol, sdk.items.runes.Dol, sdk.items.gems.Chipped.Emerald], Index: Recipe.Rune }); + } + + break; + case sdk.items.runes.Hel: // hel->io + if (me.ladder || Developer.addLadderRW) { + this.recipes.push({ Ingredients: [sdk.items.runes.Hel, sdk.items.runes.Hel, sdk.items.runes.Hel, sdk.items.gems.Chipped.Diamond], Index: Recipe.Rune }); + } + + break; + case sdk.items.runes.Io: // io->lum + if (me.ladder || Developer.addLadderRW) { + this.recipes.push({ Ingredients: [sdk.items.runes.Io, sdk.items.runes.Io, sdk.items.runes.Io, sdk.items.gems.Flawed.Topaz], Index: Recipe.Rune }); + } + + break; + case sdk.items.runes.Lum: // lum->ko + if (me.ladder || Developer.addLadderRW) { + this.recipes.push({ Ingredients: [sdk.items.runes.Lum, sdk.items.runes.Lum, sdk.items.runes.Lum, sdk.items.gems.Flawed.Amethyst], Index: Recipe.Rune }); + } + + break; + case sdk.items.runes.Ko: // ko->fal + if (me.ladder || Developer.addLadderRW) { + this.recipes.push({ Ingredients: [sdk.items.runes.Ko, sdk.items.runes.Ko, sdk.items.runes.Ko, sdk.items.gems.Flawed.Sapphire], Index: Recipe.Rune }); + } + + break; + case sdk.items.runes.Fal: // fal->lem + if (me.ladder || Developer.addLadderRW) { + this.recipes.push({ Ingredients: [sdk.items.runes.Fal, sdk.items.runes.Fal, sdk.items.runes.Fal, sdk.items.gems.Flawed.Ruby], Index: Recipe.Rune }); + } + + break; + case sdk.items.runes.Lem: // lem->pul + if (me.ladder || Developer.addLadderRW) { + this.recipes.push({ Ingredients: [sdk.items.runes.Lem, sdk.items.runes.Lem, sdk.items.runes.Lem, sdk.items.gems.Flawed.Emerald], Index: Recipe.Rune }); + } + + break; + case sdk.items.runes.Pul: // pul->um + if (me.ladder || Developer.addLadderRW) { + this.recipes.push({ Ingredients: [sdk.items.runes.Pul, sdk.items.runes.Pul, sdk.items.gems.Flawed.Diamond], Index: Recipe.Rune }); + } + + break; + case sdk.items.runes.Um: // um->mal + if (me.ladder || Developer.addLadderRW) { + this.recipes.push({ Ingredients: [sdk.items.runes.Um, sdk.items.runes.Um, sdk.items.gems.Normal.Topaz], Index: Recipe.Rune }); + } + + break; + case sdk.items.runes.Mal: // mal->ist + if (me.ladder || Developer.addLadderRW) { + this.recipes.push({ Ingredients: [sdk.items.runes.Mal, sdk.items.runes.Mal, sdk.items.gems.Normal.Amethyst], Index: Recipe.Rune }); + } + + break; + case sdk.items.runes.Ist: // ist->gul + if (me.ladder || Developer.addLadderRW) { + this.recipes.push({ Ingredients: [sdk.items.runes.Ist, sdk.items.runes.Ist, sdk.items.gems.Normal.Sapphire], Index: Recipe.Rune }); + } + + break; + case sdk.items.runes.Gul: // gul->vex + if (me.ladder || Developer.addLadderRW) { + this.recipes.push({ Ingredients: [sdk.items.runes.Gul, sdk.items.runes.Gul, sdk.items.gems.Normal.Ruby], Index: Recipe.Rune }); + } + + break; + case sdk.items.runes.Vex: // vex->ohm + if (me.ladder || Developer.addLadderRW) { + this.recipes.push({ Ingredients: [sdk.items.runes.Vex, sdk.items.runes.Vex, sdk.items.gems.Normal.Emerald], Index: Recipe.Rune }); + } + + break; + case sdk.items.runes.Ohm: // ohm->lo + if (me.ladder || Developer.addLadderRW) { + this.recipes.push({ Ingredients: [sdk.items.runes.Ohm, sdk.items.runes.Ohm, sdk.items.gems.Normal.Diamond], Index: Recipe.Rune }); + } + + break; + case sdk.items.runes.Lo: // lo->sur + if (me.ladder || Developer.addLadderRW) { + this.recipes.push({ Ingredients: [sdk.items.runes.Lo, sdk.items.runes.Lo, sdk.items.gems.Flawless.Topaz], Index: Recipe.Rune }); + } + + break; + case sdk.items.runes.Sur: // sur->ber + if (me.ladder || Developer.addLadderRW) { + this.recipes.push({ Ingredients: [sdk.items.runes.Sur, sdk.items.runes.Sur, sdk.items.gems.Flawless.Amethyst], Index: Recipe.Rune }); + } + + break; + case sdk.items.runes.Ber: // ber->jah + if (me.ladder || Developer.addLadderRW) { + this.recipes.push({ Ingredients: [sdk.items.runes.Ber, sdk.items.runes.Ber, sdk.items.gems.Flawless.Sapphire], Index: Recipe.Rune }); + } + + break; + case sdk.items.runes.Jah: // jah->cham + if (me.ladder || Developer.addLadderRW) { + this.recipes.push({ Ingredients: [sdk.items.runes.Jah, sdk.items.runes.Jah, sdk.items.gems.Flawless.Ruby], Index: Recipe.Rune }); + } + + break; + case sdk.items.runes.Cham: // cham->zod + if (me.ladder || Developer.addLadderRW) { + this.recipes.push({ Ingredients: [sdk.items.runes.Cham, sdk.items.runes.Cham, sdk.items.gems.Flawless.Emerald], Index: Recipe.Rune }); + } + + break; + } + + break; + case Recipe.Token: + this.recipes.push({ Ingredients: [sdk.quest.item.TwistedEssenceofSuffering, sdk.quest.item.ChargedEssenceofHatred, sdk.quest.item.BurningEssenceofTerror, sdk.quest.item.FesteringEssenceofDestruction], Index: Recipe.Token, AlwaysEnabled: true }); + + break; + } + } }; Cubing.buildLists = function () { - CraftingSystem.checkSubrecipes(); - SoloWants.checkSubrecipes(); - - this.validIngredients = []; - this.neededIngredients = []; - let items = me.getItemsEx() - .filter(item => [sdk.items.mode.inStorage, sdk.items.mode.Equipped].includes(item.mode)) - .sort((a, b) => b.ilvl - a.ilvl); - /** - * @param {ItemUnit} item - * @param {*} recipe - */ - const ingredientObj = (item, recipe) => ({ - classid: item.classid, - type: item.itemType, - quality: item.quality, - ilvl: item.ilvl, - gid: item.gid, - recipe: recipe, - }); - - for (let i = 0; i < this.recipes.length; i += 1) { - // Set default Enabled property - true if recipe is always enabled, false otherwise - this.recipes[i].Enabled = this.recipes[i].hasOwnProperty("AlwaysEnabled"); - - IngredientLoop: - for (let j = 0; j < this.recipes[i].Ingredients.length; j += 1) { - for (let k = 0; k < items.length; k += 1) { - if (((this.recipes[i].Ingredients[j] === "pgem" && this.gemList.includes(items[k].classid)) - || (this.recipes[i].Ingredients[j] === "fgem" && [sdk.items.gems.Flawless.Amethyst, sdk.items.gems.Flawless.Topaz, sdk.items.gems.Flawed.Sapphire, sdk.items.gems.Flawless.Emerald, sdk.items.gems.Flawless.Ruby, sdk.items.gems.Flawless.Diamond, sdk.items.gems.Flawless.Skull].includes(items[k].classid)) - || (this.recipes[i].Ingredients[j] === "cgem" && this.chippedGems.includes(items[k].classid)) - || items[k].classid === this.recipes[i].Ingredients[j]) && this.validItem(items[k], this.recipes[i])) { - - // push the item's info into the valid ingredients array. this will be used to find items when checking recipes - this.validIngredients.push(ingredientObj(items[k], Cubing.recipes[i])); - - // Remove from item list to prevent counting the same item more than once - items.splice(k, 1); - - k -= 1; - - // Enable recipes for gem/jewel pickup - // Enable rune recipe after 2 bases are found - if (this.recipes[i].Index !== Recipe.Rune || (this.recipes[i].Index === Recipe.Rune && j >= 1)) { - this.recipes[i].Enabled = true; - } - - continue IngredientLoop; - } - } - - // add the item to needed list - enable pickup - this.neededIngredients.push({ classid: this.recipes[i].Ingredients[j], recipe: this.recipes[i] }); - - // skip flawless gems adding if we don't have the main item (Recipe.Gem and Recipe.Rune for el-ort are always enabled) - if (!this.recipes[i].Enabled) { - break; - } - - // if the recipe is enabled (we have the main item), add flawless gem recipes (if needed) - - if (this.subRecipes.indexOf(sdk.items.gems.Perfect.Amethyst) === -1 && (this.recipes[i].Ingredients[j] === sdk.items.gems.Perfect.Amethyst || (this.recipes[i].Ingredients[j] === "pgem" && this.gemList.indexOf(sdk.items.gems.Perfect.Amethyst) > -1))) { - this.recipes.push({ Ingredients: [sdk.items.gems.Flawless.Amethyst, sdk.items.gems.Flawless.Amethyst, sdk.items.gems.Flawless.Amethyst], Index: Recipe.Gem, AlwaysEnabled: true, MainRecipe: this.recipes[i].Index }); - this.subRecipes.push(sdk.items.gems.Perfect.Amethyst); - } - - // Make flawless amethyst - if (this.subRecipes.indexOf(sdk.items.gems.Flawless.Amethyst) === -1 && (this.recipes[i].Ingredients[j] === sdk.items.gems.Flawless.Amethyst || (this.recipes[i].Ingredients[j] === "fgem" && this.gemList.indexOf(sdk.items.gems.Flawless.Amethyst) > -1))) { - this.recipes.push({ Ingredients: [sdk.items.gems.Normal.Amethyst, sdk.items.gems.Normal.Amethyst, sdk.items.gems.Normal.Amethyst], Index: Recipe.Gem, AlwaysEnabled: true, MainRecipe: this.recipes[i].Index }); - this.subRecipes.push(sdk.items.gems.Flawless.Amethyst); - } - - // Make perf topaz - if (this.subRecipes.indexOf(sdk.items.gems.Perfect.Topaz) === -1 && (this.recipes[i].Ingredients[j] === sdk.items.gems.Perfect.Topaz || (this.recipes[i].Ingredients[j] === "pgem" && this.gemList.indexOf(sdk.items.gems.Perfect.Topaz) > -1))) { - this.recipes.push({ Ingredients: [sdk.items.gems.Flawless.Topaz, sdk.items.gems.Flawless.Topaz, sdk.items.gems.Flawless.Topaz], Index: Recipe.Gem, AlwaysEnabled: true, MainRecipe: this.recipes[i].Index }); - this.subRecipes.push(sdk.items.gems.Perfect.Topaz); - } - - // Make flawless topaz - if (this.subRecipes.indexOf(sdk.items.gems.Flawless.Topaz) === -1 && (this.recipes[i].Ingredients[j] === sdk.items.gems.Flawless.Topaz || (this.recipes[i].Ingredients[j] === "fgem" && this.gemList.indexOf(sdk.items.gems.Flawless.Topaz) > -1))) { - this.recipes.push({ Ingredients: [sdk.items.gems.Normal.Topaz, sdk.items.gems.Normal.Topaz, sdk.items.gems.Normal.Topaz], Index: Recipe.Gem, AlwaysEnabled: true, MainRecipe: this.recipes[i].Index }); - this.subRecipes.push(sdk.items.gems.Flawless.Topaz); - } - - // Make perf sapphire - if (this.subRecipes.indexOf(sdk.items.gems.Perfect.Sapphire) === -1 && (this.recipes[i].Ingredients[j] === sdk.items.gems.Perfect.Sapphire || (this.recipes[i].Ingredients[j] === "pgem" && this.gemList.indexOf(sdk.items.gems.Perfect.Sapphire) > -1))) { - this.recipes.push({ Ingredients: [sdk.items.gems.Flawless.Sapphire, sdk.items.gems.Flawless.Sapphire, sdk.items.gems.Flawless.Sapphire], Index: Recipe.Gem, AlwaysEnabled: true, MainRecipe: this.recipes[i].Index }); - this.subRecipes.push(sdk.items.gems.Perfect.Sapphire); - } - - // Make flawless sapphire - if (this.subRecipes.indexOf(sdk.items.gems.Flawless.Sapphire) === -1 && (this.recipes[i].Ingredients[j] === sdk.items.gems.Flawless.Sapphire || (this.recipes[i].Ingredients[j] === "fgem" && this.gemList.indexOf(sdk.items.gems.Flawless.Sapphire) > -1))) { - this.recipes.push({ Ingredients: [sdk.items.gems.Normal.Sapphire, sdk.items.gems.Normal.Sapphire, sdk.items.gems.Normal.Sapphire], Index: Recipe.Gem, AlwaysEnabled: true, MainRecipe: this.recipes[i].Index }); - this.subRecipes.push(sdk.items.gems.Flawless.Sapphire); - } - - // Make perf emerald - if (this.subRecipes.indexOf(sdk.items.gems.Perfect.Emerald) === -1 && (this.recipes[i].Ingredients[j] === sdk.items.gems.Perfect.Emerald || (this.recipes[i].Ingredients[j] === "pgem" && this.gemList.indexOf(sdk.items.gems.Perfect.Emerald) > -1))) { - this.recipes.push({ Ingredients: [sdk.items.gems.Flawless.Emerald, sdk.items.gems.Flawless.Emerald, sdk.items.gems.Flawless.Emerald], Index: Recipe.Gem, AlwaysEnabled: true, MainRecipe: this.recipes[i].Index }); - this.subRecipes.push(sdk.items.gems.Perfect.Emerald); - } - - // Make flawless emerald - if (this.subRecipes.indexOf(sdk.items.gems.Flawless.Emerald) === -1 && (this.recipes[i].Ingredients[j] === sdk.items.gems.Flawless.Emerald || (this.recipes[i].Ingredients[j] === "fgem" && this.gemList.indexOf(sdk.items.gems.Flawless.Emerald) > -1))) { - this.recipes.push({ Ingredients: [sdk.items.gems.Normal.Emerald, sdk.items.gems.Normal.Emerald, sdk.items.gems.Normal.Emerald], Index: Recipe.Gem, AlwaysEnabled: true, MainRecipe: this.recipes[i].Index }); - this.subRecipes.push(sdk.items.gems.Flawless.Emerald); - } - - // Make perf ruby - if (this.subRecipes.indexOf(sdk.items.gems.Perfect.Ruby) === -1 && (this.recipes[i].Ingredients[j] === sdk.items.gems.Perfect.Ruby || (this.recipes[i].Ingredients[j] === "pgem" && this.gemList.indexOf(sdk.items.gems.Perfect.Ruby) > -1))) { - this.recipes.push({ Ingredients: [sdk.items.gems.Flawless.Ruby, sdk.items.gems.Flawless.Ruby, sdk.items.gems.Flawless.Ruby], Index: Recipe.Gem, AlwaysEnabled: true, MainRecipe: this.recipes[i].Index }); - this.subRecipes.push(sdk.items.gems.Perfect.Ruby); - } - - // Make flawless ruby - if (this.subRecipes.indexOf(sdk.items.gems.Flawless.Ruby) === -1 && (this.recipes[i].Ingredients[j] === sdk.items.gems.Flawless.Ruby || (this.recipes[i].Ingredients[j] === "fgem" && this.gemList.indexOf(sdk.items.gems.Flawless.Ruby) > -1))) { - this.recipes.push({ Ingredients: [sdk.items.gems.Normal.Ruby, sdk.items.gems.Normal.Ruby, sdk.items.gems.Normal.Ruby], Index: Recipe.Gem, AlwaysEnabled: true, MainRecipe: this.recipes[i].Index }); - this.subRecipes.push(sdk.items.gems.Flawless.Ruby); - } - - // Make perf diamond - if (this.subRecipes.indexOf(sdk.items.gems.Perfect.Diamond) === -1 && (this.recipes[i].Ingredients[j] === sdk.items.gems.Perfect.Diamond || (this.recipes[i].Ingredients[j] === "pgem" && this.gemList.indexOf(sdk.items.gems.Perfect.Diamond) > -1))) { - this.recipes.push({ Ingredients: [sdk.items.gems.Flawless.Diamond, sdk.items.gems.Flawless.Diamond, sdk.items.gems.Flawless.Diamond], Index: Recipe.Gem, AlwaysEnabled: true, MainRecipe: this.recipes[i].Index }); - this.subRecipes.push(sdk.items.gems.Perfect.Diamond); - } - - // Make flawless diamond - if (this.subRecipes.indexOf(sdk.items.gems.Flawless.Diamond) === -1 && (this.recipes[i].Ingredients[j] === sdk.items.gems.Flawless.Diamond || (this.recipes[i].Ingredients[j] === "fgem" && this.gemList.indexOf(sdk.items.gems.Flawless.Diamond) > -1))) { - this.recipes.push({ Ingredients: [sdk.items.gems.Normal.Diamond, sdk.items.gems.Normal.Diamond, sdk.items.gems.Normal.Diamond], Index: Recipe.Gem, AlwaysEnabled: true, MainRecipe: this.recipes[i].Index }); - this.subRecipes.push(sdk.items.gems.Flawless.Diamond); - } - - // Make perf skull - if (this.subRecipes.indexOf(sdk.items.gems.Perfect.Skull) === -1 && (this.recipes[i].Ingredients[j] === sdk.items.gems.Perfect.Skull || (this.recipes[i].Ingredients[j] === "pgem" && this.gemList.indexOf(sdk.items.gems.Perfect.Skull) > -1))) { - this.recipes.push({ Ingredients: [sdk.items.gems.Flawless.Skull, sdk.items.gems.Flawless.Skull, sdk.items.gems.Flawless.Skull], Index: Recipe.Gem, AlwaysEnabled: true, MainRecipe: this.recipes[i].Index }); - this.subRecipes.push(sdk.items.gems.Perfect.Skull); - } - - // Make flawless skull - if (this.subRecipes.indexOf(sdk.items.gems.Flawless.Skull) === -1 && (this.recipes[i].Ingredients[j] === sdk.items.gems.Flawless.Skull || (this.recipes[i].Ingredients[j] === "fgem" && this.gemList.indexOf(sdk.items.gems.Flawless.Skull) > -1))) { - this.recipes.push({ Ingredients: [sdk.items.gems.Normal.Skull, sdk.items.gems.Normal.Skull, sdk.items.gems.Normal.Skull], Index: Recipe.Gem, AlwaysEnabled: true, MainRecipe: this.recipes[i].Index }); - this.subRecipes.push(sdk.items.gems.Flawless.Skull); - } - } - } + CraftingSystem.checkSubrecipes(); + SoloWants.checkSubrecipes(); + + this.validIngredients = []; + this.neededIngredients = []; + let items = me.getItemsEx() + .filter(item => [sdk.items.mode.inStorage, sdk.items.mode.Equipped].includes(item.mode)) + .sort((a, b) => b.ilvl - a.ilvl); + /** + * @param {ItemUnit} item + * @param {*} recipe + */ + const ingredientObj = (item, recipe) => ({ + classid: item.classid, + type: item.itemType, + quality: item.quality, + ilvl: item.ilvl, + gid: item.gid, + recipe: recipe, + }); + + for (let i = 0; i < this.recipes.length; i += 1) { + // Set default Enabled property - true if recipe is always enabled, false otherwise + this.recipes[i].Enabled = this.recipes[i].hasOwnProperty("AlwaysEnabled"); + + IngredientLoop: + for (let j = 0; j < this.recipes[i].Ingredients.length; j += 1) { + for (let k = 0; k < items.length; k += 1) { + if (((this.recipes[i].Ingredients[j] === "pgem" && this.gemList.includes(items[k].classid)) + || (this.recipes[i].Ingredients[j] === "fgem" && [sdk.items.gems.Flawless.Amethyst, sdk.items.gems.Flawless.Topaz, sdk.items.gems.Flawed.Sapphire, sdk.items.gems.Flawless.Emerald, sdk.items.gems.Flawless.Ruby, sdk.items.gems.Flawless.Diamond, sdk.items.gems.Flawless.Skull].includes(items[k].classid)) + || (this.recipes[i].Ingredients[j] === "cgem" && this.chippedGems.includes(items[k].classid)) + || items[k].classid === this.recipes[i].Ingredients[j]) && this.validItem(items[k], this.recipes[i])) { + + // push the item's info into the valid ingredients array. this will be used to find items when checking recipes + this.validIngredients.push(ingredientObj(items[k], Cubing.recipes[i])); + + // Remove from item list to prevent counting the same item more than once + items.splice(k, 1); + + k -= 1; + + // Enable recipes for gem/jewel pickup + // Enable rune recipe after 2 bases are found + if (this.recipes[i].Index !== Recipe.Rune || (this.recipes[i].Index === Recipe.Rune && j >= 1)) { + this.recipes[i].Enabled = true; + } + + continue IngredientLoop; + } + } + + // add the item to needed list - enable pickup + this.neededIngredients.push({ classid: this.recipes[i].Ingredients[j], recipe: this.recipes[i] }); + + // skip flawless gems adding if we don't have the main item (Recipe.Gem and Recipe.Rune for el-ort are always enabled) + if (!this.recipes[i].Enabled) { + break; + } + + // if the recipe is enabled (we have the main item), add flawless gem recipes (if needed) + + if (this.subRecipes.indexOf(sdk.items.gems.Perfect.Amethyst) === -1 && (this.recipes[i].Ingredients[j] === sdk.items.gems.Perfect.Amethyst || (this.recipes[i].Ingredients[j] === "pgem" && this.gemList.indexOf(sdk.items.gems.Perfect.Amethyst) > -1))) { + this.recipes.push({ Ingredients: [sdk.items.gems.Flawless.Amethyst, sdk.items.gems.Flawless.Amethyst, sdk.items.gems.Flawless.Amethyst], Index: Recipe.Gem, AlwaysEnabled: true, MainRecipe: this.recipes[i].Index }); + this.subRecipes.push(sdk.items.gems.Perfect.Amethyst); + } + + // Make flawless amethyst + if (this.subRecipes.indexOf(sdk.items.gems.Flawless.Amethyst) === -1 && (this.recipes[i].Ingredients[j] === sdk.items.gems.Flawless.Amethyst || (this.recipes[i].Ingredients[j] === "fgem" && this.gemList.indexOf(sdk.items.gems.Flawless.Amethyst) > -1))) { + this.recipes.push({ Ingredients: [sdk.items.gems.Normal.Amethyst, sdk.items.gems.Normal.Amethyst, sdk.items.gems.Normal.Amethyst], Index: Recipe.Gem, AlwaysEnabled: true, MainRecipe: this.recipes[i].Index }); + this.subRecipes.push(sdk.items.gems.Flawless.Amethyst); + } + + // Make perf topaz + if (this.subRecipes.indexOf(sdk.items.gems.Perfect.Topaz) === -1 && (this.recipes[i].Ingredients[j] === sdk.items.gems.Perfect.Topaz || (this.recipes[i].Ingredients[j] === "pgem" && this.gemList.indexOf(sdk.items.gems.Perfect.Topaz) > -1))) { + this.recipes.push({ Ingredients: [sdk.items.gems.Flawless.Topaz, sdk.items.gems.Flawless.Topaz, sdk.items.gems.Flawless.Topaz], Index: Recipe.Gem, AlwaysEnabled: true, MainRecipe: this.recipes[i].Index }); + this.subRecipes.push(sdk.items.gems.Perfect.Topaz); + } + + // Make flawless topaz + if (this.subRecipes.indexOf(sdk.items.gems.Flawless.Topaz) === -1 && (this.recipes[i].Ingredients[j] === sdk.items.gems.Flawless.Topaz || (this.recipes[i].Ingredients[j] === "fgem" && this.gemList.indexOf(sdk.items.gems.Flawless.Topaz) > -1))) { + this.recipes.push({ Ingredients: [sdk.items.gems.Normal.Topaz, sdk.items.gems.Normal.Topaz, sdk.items.gems.Normal.Topaz], Index: Recipe.Gem, AlwaysEnabled: true, MainRecipe: this.recipes[i].Index }); + this.subRecipes.push(sdk.items.gems.Flawless.Topaz); + } + + // Make perf sapphire + if (this.subRecipes.indexOf(sdk.items.gems.Perfect.Sapphire) === -1 && (this.recipes[i].Ingredients[j] === sdk.items.gems.Perfect.Sapphire || (this.recipes[i].Ingredients[j] === "pgem" && this.gemList.indexOf(sdk.items.gems.Perfect.Sapphire) > -1))) { + this.recipes.push({ Ingredients: [sdk.items.gems.Flawless.Sapphire, sdk.items.gems.Flawless.Sapphire, sdk.items.gems.Flawless.Sapphire], Index: Recipe.Gem, AlwaysEnabled: true, MainRecipe: this.recipes[i].Index }); + this.subRecipes.push(sdk.items.gems.Perfect.Sapphire); + } + + // Make flawless sapphire + if (this.subRecipes.indexOf(sdk.items.gems.Flawless.Sapphire) === -1 && (this.recipes[i].Ingredients[j] === sdk.items.gems.Flawless.Sapphire || (this.recipes[i].Ingredients[j] === "fgem" && this.gemList.indexOf(sdk.items.gems.Flawless.Sapphire) > -1))) { + this.recipes.push({ Ingredients: [sdk.items.gems.Normal.Sapphire, sdk.items.gems.Normal.Sapphire, sdk.items.gems.Normal.Sapphire], Index: Recipe.Gem, AlwaysEnabled: true, MainRecipe: this.recipes[i].Index }); + this.subRecipes.push(sdk.items.gems.Flawless.Sapphire); + } + + // Make perf emerald + if (this.subRecipes.indexOf(sdk.items.gems.Perfect.Emerald) === -1 && (this.recipes[i].Ingredients[j] === sdk.items.gems.Perfect.Emerald || (this.recipes[i].Ingredients[j] === "pgem" && this.gemList.indexOf(sdk.items.gems.Perfect.Emerald) > -1))) { + this.recipes.push({ Ingredients: [sdk.items.gems.Flawless.Emerald, sdk.items.gems.Flawless.Emerald, sdk.items.gems.Flawless.Emerald], Index: Recipe.Gem, AlwaysEnabled: true, MainRecipe: this.recipes[i].Index }); + this.subRecipes.push(sdk.items.gems.Perfect.Emerald); + } + + // Make flawless emerald + if (this.subRecipes.indexOf(sdk.items.gems.Flawless.Emerald) === -1 && (this.recipes[i].Ingredients[j] === sdk.items.gems.Flawless.Emerald || (this.recipes[i].Ingredients[j] === "fgem" && this.gemList.indexOf(sdk.items.gems.Flawless.Emerald) > -1))) { + this.recipes.push({ Ingredients: [sdk.items.gems.Normal.Emerald, sdk.items.gems.Normal.Emerald, sdk.items.gems.Normal.Emerald], Index: Recipe.Gem, AlwaysEnabled: true, MainRecipe: this.recipes[i].Index }); + this.subRecipes.push(sdk.items.gems.Flawless.Emerald); + } + + // Make perf ruby + if (this.subRecipes.indexOf(sdk.items.gems.Perfect.Ruby) === -1 && (this.recipes[i].Ingredients[j] === sdk.items.gems.Perfect.Ruby || (this.recipes[i].Ingredients[j] === "pgem" && this.gemList.indexOf(sdk.items.gems.Perfect.Ruby) > -1))) { + this.recipes.push({ Ingredients: [sdk.items.gems.Flawless.Ruby, sdk.items.gems.Flawless.Ruby, sdk.items.gems.Flawless.Ruby], Index: Recipe.Gem, AlwaysEnabled: true, MainRecipe: this.recipes[i].Index }); + this.subRecipes.push(sdk.items.gems.Perfect.Ruby); + } + + // Make flawless ruby + if (this.subRecipes.indexOf(sdk.items.gems.Flawless.Ruby) === -1 && (this.recipes[i].Ingredients[j] === sdk.items.gems.Flawless.Ruby || (this.recipes[i].Ingredients[j] === "fgem" && this.gemList.indexOf(sdk.items.gems.Flawless.Ruby) > -1))) { + this.recipes.push({ Ingredients: [sdk.items.gems.Normal.Ruby, sdk.items.gems.Normal.Ruby, sdk.items.gems.Normal.Ruby], Index: Recipe.Gem, AlwaysEnabled: true, MainRecipe: this.recipes[i].Index }); + this.subRecipes.push(sdk.items.gems.Flawless.Ruby); + } + + // Make perf diamond + if (this.subRecipes.indexOf(sdk.items.gems.Perfect.Diamond) === -1 && (this.recipes[i].Ingredients[j] === sdk.items.gems.Perfect.Diamond || (this.recipes[i].Ingredients[j] === "pgem" && this.gemList.indexOf(sdk.items.gems.Perfect.Diamond) > -1))) { + this.recipes.push({ Ingredients: [sdk.items.gems.Flawless.Diamond, sdk.items.gems.Flawless.Diamond, sdk.items.gems.Flawless.Diamond], Index: Recipe.Gem, AlwaysEnabled: true, MainRecipe: this.recipes[i].Index }); + this.subRecipes.push(sdk.items.gems.Perfect.Diamond); + } + + // Make flawless diamond + if (this.subRecipes.indexOf(sdk.items.gems.Flawless.Diamond) === -1 && (this.recipes[i].Ingredients[j] === sdk.items.gems.Flawless.Diamond || (this.recipes[i].Ingredients[j] === "fgem" && this.gemList.indexOf(sdk.items.gems.Flawless.Diamond) > -1))) { + this.recipes.push({ Ingredients: [sdk.items.gems.Normal.Diamond, sdk.items.gems.Normal.Diamond, sdk.items.gems.Normal.Diamond], Index: Recipe.Gem, AlwaysEnabled: true, MainRecipe: this.recipes[i].Index }); + this.subRecipes.push(sdk.items.gems.Flawless.Diamond); + } + + // Make perf skull + if (this.subRecipes.indexOf(sdk.items.gems.Perfect.Skull) === -1 && (this.recipes[i].Ingredients[j] === sdk.items.gems.Perfect.Skull || (this.recipes[i].Ingredients[j] === "pgem" && this.gemList.indexOf(sdk.items.gems.Perfect.Skull) > -1))) { + this.recipes.push({ Ingredients: [sdk.items.gems.Flawless.Skull, sdk.items.gems.Flawless.Skull, sdk.items.gems.Flawless.Skull], Index: Recipe.Gem, AlwaysEnabled: true, MainRecipe: this.recipes[i].Index }); + this.subRecipes.push(sdk.items.gems.Perfect.Skull); + } + + // Make flawless skull + if (this.subRecipes.indexOf(sdk.items.gems.Flawless.Skull) === -1 && (this.recipes[i].Ingredients[j] === sdk.items.gems.Flawless.Skull || (this.recipes[i].Ingredients[j] === "fgem" && this.gemList.indexOf(sdk.items.gems.Flawless.Skull) > -1))) { + this.recipes.push({ Ingredients: [sdk.items.gems.Normal.Skull, sdk.items.gems.Normal.Skull, sdk.items.gems.Normal.Skull], Index: Recipe.Gem, AlwaysEnabled: true, MainRecipe: this.recipes[i].Index }); + this.subRecipes.push(sdk.items.gems.Flawless.Skull); + } + } + } }; // Added try again to emptying cube if it fails it will clear inventory then organize it Cubing.emptyCube = function () { - let cube = me.getItem(sdk.items.quest.Cube); - let items = me.findItems(-1, -1, sdk.storage.Cube); - if (!cube || !items) return false; + let cube = me.getItem(sdk.items.quest.Cube); + let items = me.findItems(-1, -1, sdk.storage.Cube); + if (!cube || !items) return false; - while (items.length) { - !getUIFlag(sdk.uiflags.Cube) && Cubing.openCube(); + while (items.length) { + !getUIFlag(sdk.uiflags.Cube) && Cubing.openCube(); - if (!Storage.Stash.MoveTo(items[0]) && !Storage.Inventory.MoveTo(items[0])) { - Town.clearInventory(); - me.sortInventory(); + if (!Storage.Stash.MoveTo(items[0]) && !Storage.Inventory.MoveTo(items[0])) { + Town.clearInventory(); + me.sortInventory(); - if (!Storage.Stash.MoveTo(items[0]) && !Storage.Inventory.MoveTo(items[0])) { - return false; - } - } + if (!Storage.Stash.MoveTo(items[0]) && !Storage.Inventory.MoveTo(items[0])) { + return false; + } + } - items.shift(); - } + items.shift(); + } - return true; + return true; }; /** @param {ItemUnit} unit */ Cubing.checkItem = function (unit) { - if (!Config.Cubing || !unit) return false; - - for (let i = 0; i < this.validIngredients.length; i++) { - // not the same item but the same type of item - if (unit.mode !== sdk.items.mode.Equipped && unit.gid !== this.validIngredients[i].gid - && unit.classid === this.validIngredients[i].classid && unit.quality === this.validIngredients[i].quality) { - // item is better than the one we currently have, so add it to validIngredient array and remove old item - if (unit.ilvl > this.validIngredients[i].ilvl && this.validItem(unit, this.validIngredients[i].recipe)) { - this.validIngredients.push({ classid: unit.classid, quality: unit.quality, ilvl: unit.ilvl, gid: unit.gid, recipe: this.validIngredients[i].recipe }); - this.validIngredients.splice(i, 1); - return true; - } - } - // its an item meant for socketing so lets be sure we have the best base - if (this.validIngredients[i].recipe.Index >= Recipe.Socket.Shield && this.validIngredients[i].recipe.Index <= Recipe.Socket.Helm) { - // not the same item but the same type of item - if (!unit.isEquipped && unit.gid !== this.validIngredients[i].gid && unit.itemType === this.validIngredients[i].type - && unit.quality === this.validIngredients[i].quality) { - // console.debug(this.validIngredients[i], "\n//~~~~//\n", unit, "\n//~~~~~/\n", Item.betterThanStashed(unit, true)); - // item is better than the one we currently have, so add it to validIngredient array and remove old item - if (Item.betterThanStashed(unit, true) && this.validItem(unit, this.validIngredients[i].recipe)) { - this.validIngredients.push({ - classid: unit.classid, - type: unit.itemType, - quality: unit.quality, - ilvl: unit.ilvl, - gid: unit.gid, - recipe: this.validIngredients[i].recipe - }); - this.validIngredients.splice(i, 1); - return true; - } - } - } - } - - if (this.keepItem(unit)) { - return true; - } - - for (let i = 0; i < this.neededIngredients.length; i++) { - if (unit.classid === this.neededIngredients[i].classid && this.validItem(unit, this.neededIngredients[i].recipe)) { - return true; - } - } - - return false; + if (!Config.Cubing || !unit) return false; + + for (let i = 0; i < this.validIngredients.length; i++) { + // not the same item but the same type of item + if (unit.mode !== sdk.items.mode.Equipped && unit.gid !== this.validIngredients[i].gid + && unit.classid === this.validIngredients[i].classid && unit.quality === this.validIngredients[i].quality) { + // item is better than the one we currently have, so add it to validIngredient array and remove old item + if (unit.ilvl > this.validIngredients[i].ilvl && this.validItem(unit, this.validIngredients[i].recipe)) { + this.validIngredients.push({ classid: unit.classid, quality: unit.quality, ilvl: unit.ilvl, gid: unit.gid, recipe: this.validIngredients[i].recipe }); + this.validIngredients.splice(i, 1); + return true; + } + } + // its an item meant for socketing so lets be sure we have the best base + if (this.validIngredients[i].recipe.Index >= Recipe.Socket.Shield && this.validIngredients[i].recipe.Index <= Recipe.Socket.Helm) { + // not the same item but the same type of item + if (!unit.isEquipped && unit.gid !== this.validIngredients[i].gid && unit.itemType === this.validIngredients[i].type + && unit.quality === this.validIngredients[i].quality) { + // console.debug(this.validIngredients[i], "\n//~~~~//\n", unit, "\n//~~~~~/\n", Item.betterThanStashed(unit, true)); + // item is better than the one we currently have, so add it to validIngredient array and remove old item + if (Item.betterThanStashed(unit, true) && this.validItem(unit, this.validIngredients[i].recipe)) { + this.validIngredients.push({ + classid: unit.classid, + type: unit.itemType, + quality: unit.quality, + ilvl: unit.ilvl, + gid: unit.gid, + recipe: this.validIngredients[i].recipe + }); + this.validIngredients.splice(i, 1); + return true; + } + } + } + } + + if (this.keepItem(unit)) { + return true; + } + + for (let i = 0; i < this.neededIngredients.length; i++) { + if (unit.classid === this.neededIngredients[i].classid && this.validItem(unit, this.neededIngredients[i].recipe)) { + return true; + } + } + + return false; }; /** @@ -648,281 +648,281 @@ Cubing.checkItem = function (unit) { * @param {*} recipe */ Cubing.validItem = function (unit, recipe) { - // Excluded items - // Don't use items in locked inventory space - if (unit.isInInventory && Storage.Inventory.IsLocked(unit, Config.Inventory)) return false; - // Don't use items that are wanted by other systems - if (Runewords.validGids.includes(unit.gid) || CraftingSystem.validGids.includes(unit.gid)) return false; - - // Gems and runes - if ((unit.itemType >= sdk.items.type.Amethyst && unit.itemType <= sdk.items.type.Skull) || unit.itemType === sdk.items.type.Rune) { - if (!recipe.Enabled && recipe.Ingredients[0] !== unit.classid && recipe.Ingredients[1] !== unit.classid) { - return false; - } - - return true; - } - - // Token - if (recipe.Index === Recipe.Token) return true; - - // START - let valid = true; - const ntipResult = NTIP.CheckItem(unit); - const ntipNoTierResult = NTIP.CheckItem(unit, NTIP_CheckListNoTier); - - if (recipe.Index >= Recipe.HitPower.Helm && recipe.Index <= Recipe.Safety.Weapon) { - if (Math.floor(me.charlvl / 2) + Math.floor(unit.ilvl / 2) < recipe.Level) { - if (me.charlvl < 50) { - // set it equal to ilvl 31 where 60% chance of 2 affixes and 20% chance each of 3 or 4 affixes - recipe.Level = 31; - } else if (me.charlvl > 50 && me.charlvl < 70) { - // set it equal to ilvl 51 where 80% chance of 3 affixes and 20% chance of 4 affixes - recipe.Level = 51; - } else if (me.charlvl > 70 && me.charlvl < 93) { - // set it equal to ilvl 71 where 100% chance of 4 affixes - recipe.Level = 71; - } - } - // Junk jewels (NOT matching a pickit entry) - if (unit.itemType === sdk.items.type.Jewel) { - if (recipe.Enabled && ntipResult === Pickit.Result.UNWANTED) return true; - // Main item, NOT matching a pickit entry - } else if (unit.magic && Math.floor(me.charlvl / 2) + Math.floor(unit.ilvl / 2) >= recipe.Level - && NTIP.CheckItem(unit, NTIP_CheckListNoTier) === Pickit.Result.UNWANTED) { - return true; - } - - return false; - } else if (recipe.Index >= Recipe.Unique.Weapon.ToExceptional && recipe.Index <= Recipe.Unique.Armor.ToElite) { - // If item is equipped, ensure we can use the upgraded version - if (unit.isEquipped) { - if (me.charlvl < unit.upgradedLvlReq || me.trueStr < unit.upgradedStrReq || me.trueDex < unit.upgradedDexReq) { - return false; - } - } - // Unique item matching pickit entry - if (unit.unique && ntipResult === Pickit.Result.WANTED) { - // check items name (prevents upgrading lavagout when we want to be upgrading magefist for the second time) - if (recipe.Name !== undefined) { - valid = !!unit.fname.toLowerCase().includes(recipe.Name.toLowerCase()); - if (valid) { - // check to see if we are using this already and if so compare base stats to see if this one would be better - // ignore things that get re-rolled like defense or min/max dmg just focus on base stats like enhanced defense/damage - let equipped = me.getItemsEx(-1, sdk.storage.Equipped).find(item => item.fname.toLowerCase().includes(recipe.Name.toLowerCase())); - if (equipped) { - switch (recipe.Name.toLowerCase()) { - case "magefist": - // compare enhanced defense - keep "equal to" because base defense gets re-rolled so it might turn out better - valid = (unit.getStat(sdk.stats.ArmorPercent) >= equipped.getStat(sdk.stats.ArmorPercent)); - break; - } - } - } - } - switch (recipe.Ethereal) { - case Roll.All: - case undefined: - return valid && ntipResult === Pickit.Result.WANTED; - case Roll.Eth: - return valid && unit.ethereal && ntipResult === Pickit.Result.WANTED; - case Roll.NonEth: - return valid && !unit.ethereal && ntipResult === Pickit.Result.WANTED; - } - } - - return false; - } else if (recipe.Index >= Recipe.Rare.Weapon.ToExceptional && recipe.Index <= Recipe.Rare.Armor.ToElite) { - // If item is equipped, ensure we can use the upgraded version - if (unit.isEquipped) { - if (me.charlvl < unit.upgradedLvlReq || me.trueStr < unit.upgradedStrReq || me.trueDex < unit.upgradedDexReq) { - return false; - } - } - // Rare item matching pickit entry - if (unit.rare && ntipResult === Pickit.Result.WANTED) { - switch (recipe.Ethereal) { - case Roll.All: - case undefined: - return ntipResult === Pickit.Result.WANTED; - case Roll.Eth: - return unit.ethereal && ntipResult === Pickit.Result.WANTED; - case Roll.NonEth: - return !unit.ethereal && ntipResult === Pickit.Result.WANTED; - } - } - - return false; - } else if (recipe.Index >= Recipe.Socket.Shield && recipe.Index <= Recipe.Socket.Helm) { - // Normal item matching pickit entry, no sockets - if (unit.normal && unit.sockets === 0) { - if (Pickit.Result.WANTED === ntipResult - && [sdk.items.type.Wand, sdk.items.type.VoodooHeads, sdk.items.type.AuricShields, sdk.items.type.PrimalHelm, sdk.items.type.Pelt].includes(unit.itemType)) { - if (!Item.betterThanStashed(unit) || !Item.betterBaseThanWearing(unit)) return false; - } - switch (recipe.Ethereal) { - case Roll.All: - case undefined: - return ntipResult === Pickit.Result.WANTED; - case Roll.Eth: - return unit.ethereal && ntipResult === Pickit.Result.WANTED; - case Roll.NonEth: - return !unit.ethereal && ntipResult === Pickit.Result.WANTED; - } - } - - return false; - } else if (recipe.Index === Recipe.Reroll.Magic) { - if (unit.magic && unit.ilvl >= recipe.Level) { - if (ntipResult === Pickit.Result.UNWANTED) return true; - // should allow for charms that aren't immeaditly wanted by equip and not nip wanted - if (unit.isCharm && !CharmEquip.check(unit) && ntipNoTierResult === Pickit.Result.UNWANTED) return true; - return true; - } - - return false; - } else if (recipe.Index === Recipe.Reroll.Charm) { - if (unit.isCharm && unit.magic && (ntipResult === Pickit.Result.UNWANTED || (!CharmEquip.check(unit) && ntipNoTierResult === Pickit.Result.UNWANTED))) { - switch (unit.itemType) { - case sdk.items.type.SmallCharm: - if (unit.ilvl >= recipe.Level[unit.code].ilvl) { - return true; - } - break; - case sdk.items.type.LargeCharm: - if (unit.ilvl >= recipe.Level.cm2.ilvl) { - return true; - } - break; - case sdk.items.type.GrandCharm: - if (unit.ilvl >= recipe.Level.cm2.ilvl) { - return true; - } - break; - } - } - - return false; - } else if (recipe.Index === Recipe.Reroll.Rare) { - if (unit.rare && ntipResult === Pickit.Result.UNWANTED) { - return true; - } - - return false; - } else if (recipe.Index === Recipe.Reroll.HighRare) { - if (recipe.Ingredients[0] === unit.classid && unit.rare && ntipResult === Pickit.Result.UNWANTED) { - recipe.Enabled = true; - - return true; - } - - if (recipe.Enabled && recipe.Ingredients[2] === unit.classid && unit.itemType === sdk.items.type.Ring - && unit.getStat(sdk.stats.MaxManaPercent) && !Storage.Inventory.IsLocked(unit, Config.Inventory)) { - return true; - } - - return false; - } else if (recipe.Index === Recipe.LowToNorm.Armor || recipe.Index === Recipe.LowToNorm.Weapon) { - if (unit.lowquality && ntipResult === Pickit.Result.UNWANTED) { - return true; - } - } - - return false; + // Excluded items + // Don't use items in locked inventory space + if (unit.isInInventory && Storage.Inventory.IsLocked(unit, Config.Inventory)) return false; + // Don't use items that are wanted by other systems + if (Runewords.validGids.includes(unit.gid) || CraftingSystem.validGids.includes(unit.gid)) return false; + + // Gems and runes + if ((unit.itemType >= sdk.items.type.Amethyst && unit.itemType <= sdk.items.type.Skull) || unit.itemType === sdk.items.type.Rune) { + if (!recipe.Enabled && recipe.Ingredients[0] !== unit.classid && recipe.Ingredients[1] !== unit.classid) { + return false; + } + + return true; + } + + // Token + if (recipe.Index === Recipe.Token) return true; + + // START + let valid = true; + const ntipResult = NTIP.CheckItem(unit); + const ntipNoTierResult = NTIP.CheckItem(unit, NTIP_CheckListNoTier); + + if (recipe.Index >= Recipe.HitPower.Helm && recipe.Index <= Recipe.Safety.Weapon) { + if (Math.floor(me.charlvl / 2) + Math.floor(unit.ilvl / 2) < recipe.Level) { + if (me.charlvl < 50) { + // set it equal to ilvl 31 where 60% chance of 2 affixes and 20% chance each of 3 or 4 affixes + recipe.Level = 31; + } else if (me.charlvl > 50 && me.charlvl < 70) { + // set it equal to ilvl 51 where 80% chance of 3 affixes and 20% chance of 4 affixes + recipe.Level = 51; + } else if (me.charlvl > 70 && me.charlvl < 93) { + // set it equal to ilvl 71 where 100% chance of 4 affixes + recipe.Level = 71; + } + } + // Junk jewels (NOT matching a pickit entry) + if (unit.itemType === sdk.items.type.Jewel) { + if (recipe.Enabled && ntipResult === Pickit.Result.UNWANTED) return true; + // Main item, NOT matching a pickit entry + } else if (unit.magic && Math.floor(me.charlvl / 2) + Math.floor(unit.ilvl / 2) >= recipe.Level + && NTIP.CheckItem(unit, NTIP_CheckListNoTier) === Pickit.Result.UNWANTED) { + return true; + } + + return false; + } else if (recipe.Index >= Recipe.Unique.Weapon.ToExceptional && recipe.Index <= Recipe.Unique.Armor.ToElite) { + // If item is equipped, ensure we can use the upgraded version + if (unit.isEquipped) { + if (me.charlvl < unit.upgradedLvlReq || me.trueStr < unit.upgradedStrReq || me.trueDex < unit.upgradedDexReq) { + return false; + } + } + // Unique item matching pickit entry + if (unit.unique && ntipResult === Pickit.Result.WANTED) { + // check items name (prevents upgrading lavagout when we want to be upgrading magefist for the second time) + if (recipe.Name !== undefined) { + valid = !!unit.fname.toLowerCase().includes(recipe.Name.toLowerCase()); + if (valid) { + // check to see if we are using this already and if so compare base stats to see if this one would be better + // ignore things that get re-rolled like defense or min/max dmg just focus on base stats like enhanced defense/damage + let equipped = me.getItemsEx(-1, sdk.storage.Equipped).find(item => item.fname.toLowerCase().includes(recipe.Name.toLowerCase())); + if (equipped) { + switch (recipe.Name.toLowerCase()) { + case "magefist": + // compare enhanced defense - keep "equal to" because base defense gets re-rolled so it might turn out better + valid = (unit.getStat(sdk.stats.ArmorPercent) >= equipped.getStat(sdk.stats.ArmorPercent)); + break; + } + } + } + } + switch (recipe.Ethereal) { + case Roll.All: + case undefined: + return valid && ntipResult === Pickit.Result.WANTED; + case Roll.Eth: + return valid && unit.ethereal && ntipResult === Pickit.Result.WANTED; + case Roll.NonEth: + return valid && !unit.ethereal && ntipResult === Pickit.Result.WANTED; + } + } + + return false; + } else if (recipe.Index >= Recipe.Rare.Weapon.ToExceptional && recipe.Index <= Recipe.Rare.Armor.ToElite) { + // If item is equipped, ensure we can use the upgraded version + if (unit.isEquipped) { + if (me.charlvl < unit.upgradedLvlReq || me.trueStr < unit.upgradedStrReq || me.trueDex < unit.upgradedDexReq) { + return false; + } + } + // Rare item matching pickit entry + if (unit.rare && ntipResult === Pickit.Result.WANTED) { + switch (recipe.Ethereal) { + case Roll.All: + case undefined: + return ntipResult === Pickit.Result.WANTED; + case Roll.Eth: + return unit.ethereal && ntipResult === Pickit.Result.WANTED; + case Roll.NonEth: + return !unit.ethereal && ntipResult === Pickit.Result.WANTED; + } + } + + return false; + } else if (recipe.Index >= Recipe.Socket.Shield && recipe.Index <= Recipe.Socket.Helm) { + // Normal item matching pickit entry, no sockets + if (unit.normal && unit.sockets === 0) { + if (Pickit.Result.WANTED === ntipResult + && [sdk.items.type.Wand, sdk.items.type.VoodooHeads, sdk.items.type.AuricShields, sdk.items.type.PrimalHelm, sdk.items.type.Pelt].includes(unit.itemType)) { + if (!Item.betterThanStashed(unit) || !Item.betterBaseThanWearing(unit)) return false; + } + switch (recipe.Ethereal) { + case Roll.All: + case undefined: + return ntipResult === Pickit.Result.WANTED; + case Roll.Eth: + return unit.ethereal && ntipResult === Pickit.Result.WANTED; + case Roll.NonEth: + return !unit.ethereal && ntipResult === Pickit.Result.WANTED; + } + } + + return false; + } else if (recipe.Index === Recipe.Reroll.Magic) { + if (unit.magic && unit.ilvl >= recipe.Level) { + if (ntipResult === Pickit.Result.UNWANTED) return true; + // should allow for charms that aren't immeaditly wanted by equip and not nip wanted + if (unit.isCharm && !CharmEquip.check(unit) && ntipNoTierResult === Pickit.Result.UNWANTED) return true; + return true; + } + + return false; + } else if (recipe.Index === Recipe.Reroll.Charm) { + if (unit.isCharm && unit.magic && (ntipResult === Pickit.Result.UNWANTED || (!CharmEquip.check(unit) && ntipNoTierResult === Pickit.Result.UNWANTED))) { + switch (unit.itemType) { + case sdk.items.type.SmallCharm: + if (unit.ilvl >= recipe.Level[unit.code].ilvl) { + return true; + } + break; + case sdk.items.type.LargeCharm: + if (unit.ilvl >= recipe.Level.cm2.ilvl) { + return true; + } + break; + case sdk.items.type.GrandCharm: + if (unit.ilvl >= recipe.Level.cm2.ilvl) { + return true; + } + break; + } + } + + return false; + } else if (recipe.Index === Recipe.Reroll.Rare) { + if (unit.rare && ntipResult === Pickit.Result.UNWANTED) { + return true; + } + + return false; + } else if (recipe.Index === Recipe.Reroll.HighRare) { + if (recipe.Ingredients[0] === unit.classid && unit.rare && ntipResult === Pickit.Result.UNWANTED) { + recipe.Enabled = true; + + return true; + } + + if (recipe.Enabled && recipe.Ingredients[2] === unit.classid && unit.itemType === sdk.items.type.Ring + && unit.getStat(sdk.stats.MaxManaPercent) && !Storage.Inventory.IsLocked(unit, Config.Inventory)) { + return true; + } + + return false; + } else if (recipe.Index === Recipe.LowToNorm.Armor || recipe.Index === Recipe.LowToNorm.Weapon) { + if (unit.lowquality && ntipResult === Pickit.Result.UNWANTED) { + return true; + } + } + + return false; }; Cubing.doCubing = function () { - if (!Config.Cubing || !me.getItem(sdk.items.quest.Cube)) return false; + if (!Config.Cubing || !me.getItem(sdk.items.quest.Cube)) return false; - let wasEquipped = false; + let wasEquipped = false; - this.update(); - // Randomize the recipe array to prevent recipe blocking (multiple caster items etc.) - let tempArray = this.recipes.slice().shuffle(); + this.update(); + // Randomize the recipe array to prevent recipe blocking (multiple caster items etc.) + let tempArray = this.recipes.slice().shuffle(); - for (let i = 0; i < tempArray.length; i++) { - let string = "Transmuting: "; - let items = this.checkRecipe(tempArray[i]); + for (let i = 0; i < tempArray.length; i++) { + let string = "Transmuting: "; + let items = this.checkRecipe(tempArray[i]); - if (items) { - // If cube isn't open, attempt to open stash (the function returns true if stash is already open) - if ((!getUIFlag(sdk.uiflags.Cube) && !Town.openStash()) || !this.emptyCube()) return false; + if (items) { + // If cube isn't open, attempt to open stash (the function returns true if stash is already open) + if ((!getUIFlag(sdk.uiflags.Cube) && !Town.openStash()) || !this.emptyCube()) return false; - this.cursorCheck(); + this.cursorCheck(); - i = -1; + i = -1; - while (items.length) { - string += (items[0].name.trim() + (items.length > 1 ? " + " : "")); - items[0].isEquipped && (wasEquipped = true); - if (!Storage.Cube.MoveTo(items[0])) return false; - items.shift(); - } + while (items.length) { + string += (items[0].name.trim() + (items.length > 1 ? " + " : "")); + items[0].isEquipped && (wasEquipped = true); + if (!Storage.Cube.MoveTo(items[0])) return false; + items.shift(); + } - if (!this.openCube()) return false; + if (!this.openCube()) return false; - transmute(); - delay(700 + me.ping); - console.log("ÿc4Cubing: " + string); - Config.ShowCubingInfo && D2Bot.printToConsole(string, sdk.colors.D2Bot.Green); + transmute(); + delay(700 + me.ping); + console.log("ÿc4Cubing: " + string); + Config.ShowCubingInfo && D2Bot.printToConsole(string, sdk.colors.D2Bot.Green); - this.update(); + this.update(); - items = me.findItems(-1, -1, sdk.storage.Cube); + items = me.findItems(-1, -1, sdk.storage.Cube); - if (items) { - for (let j = 0; j < items.length; j++) { - let result = Pickit.checkItem(items[j]); + if (items) { + for (let j = 0; j < items.length; j++) { + let result = Pickit.checkItem(items[j]); - switch (result.result) { - case Pickit.Result.UNWANTED: - // keep if item is worth selling - if (items[j].getItemCost(sdk.items.cost.ToSell) / (items[j].sizex * items[j].sizey) >= (me.normal ? 50 : me.nightmare ? 500 : 1000)) { - if (Storage.Inventory.CanFit(items[j])) { - Storage.Inventory.MoveTo(items[j]); - } else { - Item.logger("Dropped", items[j], "doCubing"); - items[j].drop(); - } - } + switch (result.result) { + case Pickit.Result.UNWANTED: + // keep if item is worth selling + if (items[j].getItemCost(sdk.items.cost.ToSell) / (items[j].sizex * items[j].sizey) >= (me.normal ? 50 : me.nightmare ? 500 : 1000)) { + if (Storage.Inventory.CanFit(items[j])) { + Storage.Inventory.MoveTo(items[j]); + } else { + Item.logger("Dropped", items[j], "doCubing"); + items[j].drop(); + } + } - Developer.debugging.crafting && Item.logItem("Crafted but didn't want", items[j]); + Developer.debugging.crafting && Item.logItem("Crafted but didn't want", items[j]); - break; - case Pickit.Result.WANTED: - case Pickit.Result.SOLOWANTS: - Item.logger("Cubing Kept", items[j]); - Item.logItem("Cubing Kept", items[j], result.line); + break; + case Pickit.Result.WANTED: + case Pickit.Result.SOLOWANTS: + Item.logger("Cubing Kept", items[j]); + Item.logItem("Cubing Kept", items[j], result.line); - break; - case Pickit.Result.CRAFTING: // Crafting System - CraftingSystem.update(items[j]); + break; + case Pickit.Result.CRAFTING: // Crafting System + CraftingSystem.update(items[j]); - break; - case Pickit.Result.SOLOSYSTEM: // SoloWants System - SoloWants.update(items[j]); + break; + case Pickit.Result.SOLOSYSTEM: // SoloWants System + SoloWants.update(items[j]); - break; - } - } - } + break; + } + } + } - if (!this.emptyCube()) { - break; - } - } - } + if (!this.emptyCube()) { + break; + } + } + } - if (getUIFlag(sdk.uiflags.Cube) || getUIFlag(sdk.uiflags.Stash)) { - delay(1000); + if (getUIFlag(sdk.uiflags.Cube) || getUIFlag(sdk.uiflags.Stash)) { + delay(1000); - while (getUIFlag(sdk.uiflags.Cube) || getUIFlag(sdk.uiflags.Stash)) { - me.cancel(); - delay(300); - } - } + while (getUIFlag(sdk.uiflags.Cube) || getUIFlag(sdk.uiflags.Stash)) { + me.cancel(); + delay(300); + } + } - wasEquipped && Item.autoEquip(); + wasEquipped && Item.autoEquip(); - return true; + return true; }; diff --git a/libs/SoloPlay/Functions/DynamicTiers.js b/libs/SoloPlay/Functions/DynamicTiers.js index a3fd8489..c7545f79 100644 --- a/libs/SoloPlay/Functions/DynamicTiers.js +++ b/libs/SoloPlay/Functions/DynamicTiers.js @@ -12,636 +12,636 @@ */ (function () { - /** - * @param {ItemUnit} item - */ - const sumElementalDmg = function (item) { - if (!item) return 0; - let fire = item.getStatEx(sdk.stats.FireMinDamage) + item.getStatEx(sdk.stats.FireMaxDamage); - let light = item.getStatEx(sdk.stats.LightMinDamage) + item.getStatEx(sdk.stats.LightMaxDamage); - let magic = item.getStatEx(sdk.stats.MagicMinDamage) + item.getStatEx(sdk.stats.MagicMaxDamage); - let cold = item.getStatEx(sdk.stats.ColdMinDamage) + item.getStatEx(sdk.stats.ColdMaxDamage); - let poison = (item.getStatEx(sdk.stats.PoisonMinDamage) * 125 / 256); // PSN damage adjusted for damage per frame (125/256) - return (fire + light + magic + cold + poison); - }; - - /** - * @param {ItemUnit} item - */ - const mercscore = function (item) { - const mercWeights = { - IAS: 3.5, - MINDMG: 3, // min damage - MAXDMG: 3, // max damage - SECMINDMG: 3, // secondary min damage - SECMAXDMG: 3, // secondary max damage - ELEDMG: 2, // elemental damage - AR: 0.1, // attack rating - CB: 3, // crushing blow - OW: 3, // open wounds - LL: 8, //lifeleach - // CTC on attack - CTCOAAMP: 5, - CTCOADECREP: 10, - // CTC on striking - CTCOSAMP: 3, - CTCOSDECREP: 8, - // regen - HPREGEN: 2, - FHR: 3, // faster hit recovery - DEF: 0.05, // defense - HP: 2, - STR: 1.5, - DEX: 1.5, - ALL: 60, // + all skills - FR: 2, // fire resist - LR: 2, // lightning resist - CR: 1.5, // cold resist - PR: 1, // poison resist - ABS: 2.7, // absorb damage (fire light magic cold) - DR: 2, // Damage resist - MR: 3, // Magic damage resist - }; - - let mercRating = 1; - // start - item.prefixnum === sdk.locale.items.Treachery && (mercRating += item.getStatEx(sdk.stats.SkillWhenStruck, 2) * 1000); // fade - mercRating += item.getStatEx(sdk.stats.SkillOnAura, sdk.skills.Conviction) * 1000; // conviction aura - mercRating += item.getStatEx(sdk.stats.SkillOnAura, sdk.skills.Meditation) * 100; // meditation aura - mercRating += item.getStatEx(sdk.stats.AllSkills) * mercWeights.ALL; // add all skills - mercRating += item.getStatEx(sdk.stats.IAS) * mercWeights.IAS; // add IAS - mercRating += item.getStatEx(sdk.stats.ToHit) * mercWeights.AR; // add AR - mercRating += item.getStatEx(sdk.stats.CrushingBlow) * mercWeights.CB; // add crushing blow - mercRating += item.getStatEx(sdk.stats.OpenWounds) * mercWeights.OW; // add open wounds - mercRating += item.getStatEx(sdk.stats.LifeLeech) * mercWeights.LL; // add LL - mercRating += item.getStatEx(sdk.stats.HpRegen) * mercWeights.HPREGEN; // add hp regeneration - mercRating += item.getStatEx(sdk.stats.FHR) * mercWeights.FHR; // add faster hit recovery - mercRating += item.getStatEx(sdk.stats.Defense) * mercWeights.DEF; // add Defense - mercRating += item.getStatEx(sdk.stats.Strength) * mercWeights.STR; // add STR - mercRating += item.getStatEx(sdk.stats.Dexterity) * mercWeights.DEX; // add DEX - mercRating += item.getStatEx(sdk.stats.FireResist) * mercWeights.FR; // add FR - mercRating += item.getStatEx(sdk.stats.ColdResist) * mercWeights.CR; // add CR - mercRating += item.getStatEx(sdk.stats.LightResist) * mercWeights.LR; // add LR - mercRating += item.getStatEx(sdk.stats.PoisonResist) * mercWeights.PR; // add PR - mercRating += (item.getStatEx(sdk.stats.Vitality) + item.getStatEx(sdk.stats.MaxHp) + (item.getStatEx(sdk.stats.PerLevelHp) / 2048 * me.charlvl)) * mercWeights.HP; // add HP - mercRating += sumElementalDmg(item) * mercWeights.ELEDMG; // add elemental damage - mercRating += (item.getStatEx(sdk.stats.AbsorbFirePercent) + item.getStatEx(sdk.stats.AbsorbLightPercent) + item.getStatEx(sdk.stats.AbsorbMagicPercent) + item.getStatEx(sdk.stats.AbsorbColdPercent)) * mercWeights.ABS; // add absorb damage - mercRating += item.getStatEx(sdk.stats.NormalDamageReduction) * mercWeights.DR; // add integer damage resist - mercRating += item.getStatEx(sdk.stats.DamageResist) * mercWeights.DR * 2; // add damage resist % - mercRating += item.getStatEx(sdk.stats.MagicDamageReduction) * mercWeights.MR; // add integer magic damage resist - mercRating += item.getStatEx(sdk.stats.MagicResist) * mercWeights.MR * 2; // add magic damage resist % - - switch (me.data.merc.classid) { - case sdk.mercs.Rogue: - case sdk.mercs.IronWolf: - mercRating += item.getStatEx(sdk.stats.MinDamage) * mercWeights.MINDMG; // add MIN damage - mercRating += item.getStatEx(sdk.stats.MaxDamage) * mercWeights.MAXDMG; // add MAX damage - - break; - case sdk.mercs.A5Barb: - if ([item.getStatEx(sdk.stats.SecondaryMinDamage), item.getStatEx(sdk.stats.SecondaryMaxDamage)].includes(0)) { - mercRating += item.getStatEx(sdk.stats.MinDamage) * mercWeights.MINDMG; // add MIN damage - mercRating += item.getStatEx(sdk.stats.MaxDamage) * mercWeights.MAXDMG; // add MAX damage - - break; - } - // eslint-disable-next-line no-fallthrough - case sdk.mercs.Guard: - default: - mercRating += item.getStatEx(sdk.stats.SecondaryMinDamage) * mercWeights.SECMINDMG; - mercRating += item.getStatEx(sdk.stats.SecondaryMaxDamage) * mercWeights.SECMAXDMG; - - break; - } - - if (!me.sorceress && !me.necromancer && !me.assassin) { - mercRating += item.getStatEx(sdk.stats.SkillOnAttack, 4238) * mercWeights.CTCOAAMP; // add CTC amplify damage on attack - mercRating += item.getStatEx(sdk.stats.SkillOnAttack, 4225) * mercWeights.CTCOAAMP; // add CTC amplify damage on attack (magic items) - mercRating += item.getStatEx(sdk.stats.SkillOnAttack, 5583) * mercWeights.CTCOADECREP; // add CTC decrepify on attack - mercRating += item.getStatEx(sdk.stats.SkillOnAttack, 5631) * mercWeights.CTCOADECREP; // add CTC decrepify on attack (magic items) - mercRating += item.getStatEx(sdk.stats.SkillOnHit, 4238) * mercWeights.CTCOSAMP; // add CTC amplify damage on strikng - mercRating += item.getStatEx(sdk.stats.SkillOnHit, 4225) * mercWeights.CTCOSAMP; // add CTC amplify damage on strikng (magic items) - mercRating += item.getStatEx(sdk.stats.SkillOnHit, 5583) * mercWeights.CTCOSDECREP; // add CTC decrepify on strikng - mercRating += item.getStatEx(sdk.stats.SkillOnHit, 5631) * mercWeights.CTCOSDECREP; // add CTC decrepify on strikng (magic items) - } - - if (item.isBaseType && !item.isRuneword) { - for (let runeword of Config.Runewords) { - let [sockets, baseCID] = [runeword[0].sockets, runeword[1]]; - if (item.classid === baseCID && item.sockets === sockets && !item.getItemsEx().length) return -1; - } - } - - return mercRating; - }; - - /** - * @param {ItemUnit} item - * @param {number} [skillId] - * @param {object} [buildInfo] - */ - const chargeditemscore = function (item, skillId, buildInfo) { - if (!item) return 0; - - let tier = 0; - !buildInfo && (buildInfo = Check.currentBuild()); - - /** - * @constructor - * @param {{ skill: number, level: number, charges: number, maxcharges: number}} obj - */ - function ChargedItem (obj = {}) { - this.skill = obj.skill; - this.level = obj.level; - this.charges = obj.charges; - this.maxcharges = obj.maxcharges; - } - const _chargedWeights = new Map([ - [sdk.skills.Teleport, (Pather.canTeleport() ? 0 : 5)], - [sdk.skills.Enchant, (buildInfo.caster ? 0 : 10)], - [sdk.skills.InnerSight, (me.amazon || buildInfo.caster ? 0 : 10)], - [sdk.skills.SlowMissiles, (me.amazon ? 0 : 10)], - ]); - - let stats = item.getStat(-2); - let chargedItems = []; - - if (stats.hasOwnProperty(sdk.stats.ChargedSkill)) { - if (stats[sdk.stats.ChargedSkill] instanceof Array) { - for (let i = 0; i < stats[sdk.stats.ChargedSkill].length; i++) { - if (stats[sdk.stats.ChargedSkill][i] !== undefined) { - chargedItems.push(new ChargedItem(stats[sdk.stats.ChargedSkill][i])); - } - } - } else { - chargedItems.push(new ChargedItem(stats[sdk.stats.ChargedSkill])); - } - } - - chargedItems = chargedItems - .filter((v, i, a) => a.findIndex(el => ["skill", "level"].every(k => el[k] === v[k])) === i); - - if (skillId > 0) { - chargedItems - .filter(check => check.skill === skillId) - .forEach(el => tier += el.level * 5); - } else { - chargedItems.forEach(function (el) { - if (el.skill === sdk.skills.Teleport) { - tier += el.maxcharges * 2; - } else if (_chargedWeights.has(el.skill)) { - tier += el.level * _chargedWeights.get(el.skill); - } - }); - } - - return tier; - }; - - const _tierWeights = (function () { - const hc = me.hardcore; - const buildInfo = Check.currentBuild(); - /** @type {Map} */ - const res = new Map([ - [sdk.stats.FireResist, hc ? 5 : 3], - [sdk.stats.LightningResist, hc ? 5 : 3], - [sdk.stats.ColdResist, hc ? 3 : 1.5], - [sdk.stats.PoisonResist, hc ? 5 : 1], - [sdk.stats.MaxFireResist, 5], - [sdk.stats.MaxLightResist, 5], - [sdk.stats.MaxColdResist, 3], - [sdk.stats.MaxPoisonResist, 3], - [sdk.stats.AbsorbFire, hc ? 2 : 1], - [sdk.stats.AbsorbFirePercent, hc ? 3 : 1.5], - [sdk.stats.AbsorbLight, hc ? 2 : 1], - [sdk.stats.AbsorbLightPercent, hc ? 3 : 1.5], - [sdk.stats.AbsorbCold, hc ? 1 : 0.5], - [sdk.stats.AbsorbColdPercent, hc ? 1.5 : 0.75], - [sdk.stats.AbsorbMagic, hc ? 2 : 1], - [sdk.stats.AbsorbMagicPercent, hc ? 3 : 1.5], - [sdk.stats.NormalDamageReduction, hc ? 1 : 0.5], - [sdk.stats.MagicDamageReduction, hc ? 1 : 0.5], - [sdk.stats.DamageResist, hc ? 5 : 2], - [sdk.stats.MagicResist, hc ? 6 : 3], - ]); - /** @type {Map} */ - const gen = new Map([ - [sdk.stats.CannotbeFrozen, buildInfo.caster ? 25 : 100], - [sdk.stats.FRW, 1], - [sdk.stats.FHR, 3], - [sdk.stats.FBR, 1], - [sdk.stats.ToBlock, 1], - [sdk.stats.IAS, buildInfo.caster && !me.assassin ? 0 : 4], - [sdk.stats.FCR, buildInfo.caster ? me.assassin ? 2 : 5 : 0.5], - [sdk.stats.Defense, 0.05], - [sdk.stats.MagicBonus, 1], - [sdk.stats.GoldBonus, 0.5], - [sdk.stats.Vitality, 1], - [sdk.stats.MaxHp, 1], - [sdk.stats.PerLevelHp, 1], - [sdk.stats.HpRegen, 2], - [sdk.stats.Energy, 1], - [sdk.stats.MaxMana, 1], - [sdk.stats.PerLevelMana, 1], - [sdk.stats.ManaRecovery, buildInfo.caster ? 2.5 : 1], - [sdk.stats.Strength, 1], - [sdk.stats.Dexterity, me.amazon ? 3 : 1], - [sdk.stats.ReplenishQuantity, me.amazon ? 50 : 0], - [sdk.stats.ToHit, 0.2], - [sdk.stats.CrushingBlow, 4], - [sdk.stats.OpenWounds, 1], - [sdk.stats.DeadlyStrike, 1.5], - [sdk.stats.LifeLeech, 4], - [sdk.stats.ManaLeech, 2], - [sdk.stats.DemonDamagePercent, 0.5], - [sdk.stats.UndeadDamagePercent, 0.5], - [sdk.stats.ReplenishDurability, 15], - [sdk.stats.IgnoreTargetDefense, 50], - [sdk.stats.MinDamage, 3], - [sdk.stats.MaxDamage, 3], - [sdk.stats.SecondaryMinDamage, 2], - [sdk.stats.SecondaryMaxDamage, 2], - ]); - /** @type {Map} */ - const skill = new Map([ - [sdk.stats.AllSkills, 200], - [sdk.stats.AddClassSkills, 175], - [sdk.stats.AddSkillTab, 125], - ]); - /** @type {Map} */ - const charms = new Map([ - [sdk.stats.AllSkills, 180], - [sdk.stats.AddClassSkills, 175], - [sdk.stats.AddSkillTab, 300], - [sdk.stats.FireResist, 3], - [sdk.stats.LightningResist, 5], - [sdk.stats.ColdResist, 2], - [sdk.stats.PoisonResist, 1], - [sdk.stats.FRW, 1], - [sdk.stats.FHR, (me.barbarian ? 4 : 2)], - [sdk.stats.Defense, 0.05], - [sdk.stats.MagicBonus, 2], - [sdk.stats.GoldBonus, 0.5], - [sdk.stats.MaxHp, 1.75], - [sdk.stats.PerLevelHp, 1], - [sdk.stats.HpRegen, 2], - [sdk.stats.MaxMana, 1], - [sdk.stats.PerLevelMana, 1], - [sdk.stats.Strength, 1], - [sdk.stats.Dexterity, me.amazon ? 3 : 1], - [sdk.stats.Vitality, 1], - [sdk.stats.Energy, 0.8], - ]); - - /** @type {Map} */ - const ctc = new Map([ - [sdk.stats.SkillWhenStruck, 2], - [sdk.stats.SkillOnAttack, 2], - [sdk.stats.SkillOnStrike, 1], - [sdk.skills.Nova, 2], - [sdk.skills.FrostNova, 4], - [sdk.skills.IceBlast, 4], - [sdk.skills.ChargedBolt, 4], - [sdk.skills.StaticField, 5], - [sdk.skills.GlacialSpike, 6], - [sdk.skills.ChainLightning, 6], - [sdk.skills.Blizzard, 4], - [sdk.skills.FrozenOrb, 8], - [sdk.skills.Hydra, 4], - [sdk.skills.AmplifyDamage, 5], - [sdk.skills.Decrepify, 10], - [sdk.skills.LifeTap, 10], - [sdk.skills.BoneArmor, 10], - [sdk.skills.BoneSpear, 8], - [sdk.skills.BoneSpirit, 8], - [sdk.skills.PoisonNova, 10], - [sdk.skills.Taunt, 5], - [sdk.skills.Howl, 5], - [sdk.skills.CycloneArmor, 10], - [sdk.skills.Twister, 5], - [sdk.skills.Fade, 10], - [sdk.skills.Venom, 8], - ]); - - return { - res: res, - gen: gen, - skill: skill, - charms: charms, - ctc: ctc, - }; - })(); - - /** - * @param {ItemUnit} item - * @param {number} [bodyloc] - * @todo Breakpoint scoring similar to how res is scored - */ - const tierscore = function (item, tier, bodyloc) { - if (item.questItem) return -1; - const itembodyloc = Item.getBodyLoc(item); - if (!itembodyloc.length) return -1; - bodyloc = bodyloc || itembodyloc.last(); - - if (item.isBaseType && !item.isRuneword && me.charlvl > 10) { - for (let runeword of Config.Runewords) { - let [sockets, baseCID] = [runeword[0].sockets, runeword[1]]; - if (item.classid === baseCID && item.sockets === sockets && !item.getItemsEx().length) return -1; - } - } - - const buildInfo = Check.currentBuild(); - const canTele = Pather.canTeleport(); - // const eqItem = me.equipped.get(bodyloc); - const eqItem = me.getEquippedItem(bodyloc); - - const generalScore = () => { - let generalRating = 0; - - // get item cbf stat from olditem equipped on body location - if (!canTele && item.getStatEx(sdk.stats.CannotbeFrozen)) { - // check if we have cbf but make sure its not from the item we are trying to un-equip - if (!me.getStat(sdk.stats.CannotbeFrozen)) { - // Cannot be frozen is very important for Melee chars - generalRating += _tierWeights.gen.get(sdk.stats.CannotbeFrozen); - } - } - - // belt slots - if (item.itemType === sdk.items.type.Belt) { - const beltSizes = { lbl: 2, vbl: 2, mbl: 3, tbl: 3 }; - const beltSize = beltSizes[item.code] || 4; - // if our current belt-size is better, don't down-grade even if the other stats on the new item are better, not worth the town visits - generalRating += (Storage.BeltSize() > beltSize ? -50 : (beltSize * 4 * 2)); - } - - // pierce/mastery's not sure how I want to weight this so for now just its base value - buildInfo.usefulStats.forEach(stat => generalRating += item.getStatEx(stat)); - - // start generalRating - !item.isRuneword && (generalRating += (item.sockets * 10)); // priortize sockets - generalRating += ((item.getStatEx(sdk.stats.PerLevelHp) / 2048 * me.charlvl)) * _tierWeights.gen.get(sdk.stats.PerLevelHp); - generalRating += ((item.getStatEx(sdk.stats.PerLevelMana) / 2048 * me.charlvl)) * _tierWeights.gen.get(sdk.stats.PerLevelMana); - - return [ - sdk.stats.FHR, sdk.stats.FRW, sdk.stats.FBR, sdk.stats.FCR, sdk.stats.ToBlock, - sdk.stats.MagicBonus, sdk.stats.GoldBonus, sdk.stats.Defense, sdk.stats.ManaRecovery, - sdk.stats.Strength, sdk.stats.Dexterity, sdk.stats.Vitality, sdk.stats.Energy, - sdk.stats.MaxHp, sdk.stats.MaxMana, sdk.stats.ReplenishQuantity, sdk.stats.HpRegen, - ].reduce((acc, stat) => acc + item.getStatEx(stat) * _tierWeights.gen.get(stat), generalRating); - }; - - const resistScore = () => { - let resistRating = 0; - - // get new item stats - let [newitemFR, newitemCR, newitemLR, newitemPR] = [ - item.getStatEx(sdk.stats.FireResist), - item.getStatEx(sdk.stats.ColdResist), - item.getStatEx(sdk.stats.LightResist), - item.getStatEx(sdk.stats.PoisonResist) - ]; - // only enter next block if we have a new item with resists - if (newitemFR || newitemCR || newitemLR || newitemPR) { - const maxRes = me.hell ? 80 : (80 + me.getResPenalty(me.diff + 1) - me.getResPenalty(me.diff)); - // get item resists stats from olditem equipped on body location - let [olditemFR, olditemCR, olditemLR, olditemPR] = [0, 0, 0, 0]; - if (eqItem) { - // equipped resists - [olditemFR, olditemCR, olditemLR, olditemPR] = [ - eqItem.getStatEx(sdk.stats.FireResist), eqItem.getStatEx(sdk.stats.ColdResist), - eqItem.getStatEx(sdk.stats.LightResist), eqItem.getStatEx(sdk.stats.PoisonResist) - ]; - } - // subtract olditem resists from current total resists - const [baseFR, baseCR, baseLR, basePR] = [ - me.fireRes - olditemFR, - me.coldRes - olditemCR, - me.lightRes - olditemLR, - me.poisonRes - olditemPR, - ]; - // if baseRes < max resists give score value upto max resists reached - const [FRlimit, CRlimit, LRlimit, PRlimit] = [ - Math.max(maxRes - baseFR, 0), - Math.max(maxRes - baseCR, 0), - Math.max(maxRes - baseLR, 0), - Math.max(maxRes - basePR, 0) - ]; - // newitemRes upto reslimit - let [effectiveFR, effectiveCR, effectiveLR, effectivePR] = [ - Math.min(newitemFR, FRlimit), - Math.min(newitemCR, CRlimit), - Math.min(newitemLR, LRlimit), - Math.min(newitemPR, PRlimit) - ]; - // sum resistRatings - resistRating += effectiveFR * _tierWeights.res.get(sdk.stats.FireResist); - resistRating += effectiveCR * _tierWeights.res.get(sdk.stats.ColdResist); - resistRating += effectiveLR * _tierWeights.res.get(sdk.stats.LightResist); - resistRating += effectivePR * _tierWeights.res.get(sdk.stats.PoisonResist); - } - - return ([ - sdk.stats.MaxFireResist, sdk.stats.MaxLightResist, sdk.stats.MaxColdResist, sdk.stats.MaxPoisonResist, - sdk.stats.AbsorbFire, sdk.stats.AbsorbLight, sdk.stats.AbsorbMagic, sdk.stats.AbsorbCold, - sdk.stats.AbsorbFirePercent, sdk.stats.AbsorbLightPercent, sdk.stats.AbsorbMagicPercent, sdk.stats.AbsorbColdPercent, - sdk.stats.NormalDamageReduction, sdk.stats.DamageResist, sdk.stats.MagicDamageReduction, sdk.stats.MagicResist - ].reduce((acc, stat) => acc + item.getStatEx(stat) * _tierWeights.res.get(stat), resistRating)); - }; - - const buildScore = () => { - // dirty fix maybe? - if (me.barbarian && SetUp.currentBuild !== "Immortalwhirl" && item.strictlyTwoHanded) { - return 0; - } - // Melee Specific - if (!buildInfo.caster - || Config.AttackSkill.includes(sdk.skills.Attack) - || Config.LowManaSkill.includes(sdk.skills.Attack) - || ([sdk.items.type.Bow, sdk.items.type.AmazonBow, sdk.items.type.Crossbow].includes(item.itemType) && CharData.skillData.bow.onSwitch)) { - let meleeRating = 0; - const eleDmgWeight = 0.5; - const eleDmgModifer = [sdk.items.type.Ring, sdk.items.type.Amulet].includes(item.itemType) ? 2 : 1; - - meleeRating += ((item.getStatEx(sdk.stats.MaxDamage) + item.getStatEx(sdk.stats.MinDamage)) / 2) * 3; - meleeRating += sumElementalDmg(item) * (eleDmgWeight / eleDmgModifer); // add elemental damage - meleeRating += item.getStatEx(sdk.stats.SkillOnAura, sdk.skills.Sanctuary) * 25; // sanctuary aura - - [ - sdk.stats.ReplenishDurability, sdk.stats.IgnoreTargetDefense, sdk.stats.ToHit, sdk.stats.CrushingBlow, - sdk.stats.OpenWounds, sdk.stats.DeadlyStrike, sdk.stats.LifeLeech, sdk.stats.ManaLeech, - sdk.stats.DemonDamagePercent, sdk.stats.UndeadDamagePercent, - ].reduce((acc, stat) => acc + item.getStatEx(stat) * _tierWeights.gen.get(stat), meleeRating); - buildInfo.caster && (meleeRating /= 2); - - return meleeRating; - } - - return 0; - }; - - const skillsScore = () => { - let skillsRating = [ - [sdk.stats.AllSkills, -1], [sdk.stats.AddClassSkills, me.classid], [sdk.stats.AddSkillTab, buildInfo.tabSkills], - ].reduce((acc, [stat, subId]) => acc + item.getStatEx(stat, subId) * _tierWeights.skill.get(stat), 0); - (!buildInfo.caster && item.getItemType() === "Weapon") && (skillsRating /= 4); - const _misc = { wanted: 40, useful: 35 }; - - let selectedWeights = [_misc.wanted, _misc.useful]; - let selectedSkills = [buildInfo.wantedSkills, buildInfo.usefulSkills]; - - for (let i = 0; i < selectedWeights.length; i++) { - for (let j = 0; j < selectedSkills.length; j++) { - for (let k = 0; k < selectedSkills[j].length; k++) { - skillsRating += item.getStatEx(sdk.stats.SingleSkill, selectedSkills[j][k]) * selectedWeights[i]; - } - } - } - - // Spirit Fix for barb - (item.prefixnum === sdk.locale.items.Spirit && !buildInfo.caster) && (skillsRating -= 400); - - return skillsRating; - }; - - const ctcScore = () => { - // chance to cast doesn't exist in classic - if (me.classic) return 0; - - let ctcRating = 0; - let ctcItems = []; - const stats = item.getStat(-2); - const ctcSkillObj = (ctcType, skill, level) => ({ ctcType: ctcType, skill: skill, level: level }); - const meleeCheck = !buildInfo.caster; - /** - * @param {number} type - */ - const buildList = function (type) { - let skill, level; - if (stats.hasOwnProperty(type)) { - if (stats[type] instanceof Array) { - for (let i = 0; i < stats[type].length; i++) { - if (stats[type][i] !== undefined) { - ({ skill, level } = stats[type][i]); - ctcItems.push(ctcSkillObj(type, skill, level)); - } - } - } else { - ({ skill, level } = stats[type]); - ctcItems.push(ctcSkillObj(type, skill, level)); - } - } - }; - - buildList(sdk.stats.SkillWhenStruck); - - if (meleeCheck) { - buildList(sdk.stats.SkillOnAttack); - buildList(sdk.stats.SkillOnStrike); - } else { - _tierWeights.ctc.set(sdk.skills.Venom, 0); - if (me.charlvl > 50) { - _tierWeights.ctc.set(sdk.skills.ChargedBolt, 2); - } - } - if (!ctcItems.length) return 0; - - ctcItems - .filter((v, i, a) => a.findIndex(el => ["ctcType", "skill"].every(k => el[k] === v[k])) === i) - .forEach(el => { - if (!_tierWeights.ctc.has(el.skill)) return; - ctcRating += (el.level * _tierWeights.ctc.get(el.skill) * _tierWeights.ctc.get(el.ctcType)); - }); - - return ctcRating; - }; - - tier === undefined && (tier = 1); // set to 1 for native autoequip to use items. - tier += generalScore(); - tier += resistScore(); - tier += buildScore(); - tier += skillsScore(); - tier += ctcScore(); - tier += chargeditemscore(item, -1, buildInfo); - - if (tier > 1 && tier < NTIP.MAX_TIER && NTIP.CheckItem(item, NTIP.FinalGear.list) === Pickit.Result.WANTED) { - // console.debug(item.prettyPrint + "~~~" + tier); - tier += NTIP.MAX_TIER; - } - - return Math.max(1, tier); - }; - - /** - * @param {ItemUnit} item - */ - const secondaryscore = function (item) { - let tier = 0; - - Check.finalBuild().precastSkills - .forEach(skill => tier += item.getStatEx(sdk.stats.SingleSkill, skill) * 50); - - tier += item.getStatEx(sdk.stats.FCR) * 5; // add FCR - tier += item.getStatEx(sdk.stats.FHR) * 3; // add faster hit recovery - - return [ - [sdk.stats.AllSkills, -1], - [sdk.stats.AddClassSkills, me.classid], - [sdk.stats.AddSkillTab, Check.finalBuild().tabSkills], - ].reduce((acc, [stat, subId]) => acc + item.getStatEx(stat, subId) * _tierWeights.skill.get(stat), tier); - }; - - /** - * @param {ItemUnit} item - */ - const charmscore = function (item) { - if (me.data.charmGids.includes(item.gid)) return 1000; - // depending on invo space it might be worth it early on to keep 1 or 2 non-skiller grandcharms - @todo test that out - if (!item.unique && item.classid === sdk.items.GrandCharm && !me.getSkillTabs().some(s => item.getStatEx(sdk.stats.AddSkillTab, s))) return -1; - const buildInfo = Check.currentBuild(); - - let charmRating = 1; - - // Gheeds, Torch, annhi - we know possible attributes so don't waste resources checking for all the others - if (item.unique) { - charmRating += item.getStatEx(sdk.stats.Strength); // handle +all atrributes - charmRating += item.getStatEx(sdk.stats.AllRes); - if (item.isAnni) { - charmRating += item.getStatEx(sdk.stats.AllSkills) * _tierWeights.charms.get(sdk.stats.AllSkills); - charmRating += item.getStatEx(sdk.stats.AddExperience); - } else if (item.isGheeds) { - charmRating += item.getStatEx(sdk.stats.GoldBonus); - charmRating += item.getStatEx(sdk.stats.ReducedPrices) * 1.5; - charmRating += item.getStatEx(sdk.stats.MagicBonus) * _tierWeights.charms.get(sdk.stats.MagicBonus); - } else { - charmRating += item.getStatEx(sdk.stats.AddClassSkills, me.classid) * _tierWeights.charms.get(sdk.stats.AddClassSkills); - } - } else { - charmRating += item.getStatEx(sdk.stats.AddSkillTab, buildInfo.tabSkills) * _tierWeights.charms.get(sdk.stats.AddSkillTab); - charmRating += ((item.getStatEx(sdk.stats.PerLevelHp) / 2048 * me.charlvl)) * _tierWeights.charms.get(sdk.stats.PerLevelHp); - charmRating += ((item.getStatEx(sdk.stats.PerLevelMana) / 2048 * me.charlvl)) * _tierWeights.charms.get(sdk.stats.PerLevelMana); - - if (!buildInfo.caster) { - charmRating += item.getStatEx(sdk.stats.MinDamage) * 3; // add MIN damage - charmRating += item.getStatEx(sdk.stats.MaxDamage) * 3; // add MAX damage - charmRating += sumElementalDmg(item); // add elemental damage - charmRating += item.getStatEx(sdk.stats.ToHit) * 0.5; // add AR - } - - return [ - sdk.stats.MaxHp, sdk.stats.MaxMana, - sdk.stats.FireResist, sdk.stats.LightResist, sdk.stats.ColdResist, sdk.stats.PoisonResist, - sdk.stats.FHR, sdk.stats.FRW, sdk.stats.MagicBonus, sdk.stats.GoldBonus, sdk.stats.Defense, - sdk.stats.Strength, sdk.stats.Dexterity, sdk.stats.Vitality, sdk.stats.Energy, - ].reduce((acc, stat) => acc + item.getStatEx(stat) * _tierWeights.charms.get(stat), charmRating); - } - return charmRating; - }; - - // export to global scope - global.mercscore = mercscore; - global.chargeditemscore = chargeditemscore; - global.tierscore = tierscore; - global.secondaryscore = secondaryscore; - global.charmscore = charmscore; + /** + * @param {ItemUnit} item + */ + const sumElementalDmg = function (item) { + if (!item) return 0; + let fire = item.getStatEx(sdk.stats.FireMinDamage) + item.getStatEx(sdk.stats.FireMaxDamage); + let light = item.getStatEx(sdk.stats.LightMinDamage) + item.getStatEx(sdk.stats.LightMaxDamage); + let magic = item.getStatEx(sdk.stats.MagicMinDamage) + item.getStatEx(sdk.stats.MagicMaxDamage); + let cold = item.getStatEx(sdk.stats.ColdMinDamage) + item.getStatEx(sdk.stats.ColdMaxDamage); + let poison = (item.getStatEx(sdk.stats.PoisonMinDamage) * 125 / 256); // PSN damage adjusted for damage per frame (125/256) + return (fire + light + magic + cold + poison); + }; + + /** + * @param {ItemUnit} item + */ + const mercscore = function (item) { + const mercWeights = { + IAS: 3.5, + MINDMG: 3, // min damage + MAXDMG: 3, // max damage + SECMINDMG: 3, // secondary min damage + SECMAXDMG: 3, // secondary max damage + ELEDMG: 2, // elemental damage + AR: 0.1, // attack rating + CB: 3, // crushing blow + OW: 3, // open wounds + LL: 8, //lifeleach + // CTC on attack + CTCOAAMP: 5, + CTCOADECREP: 10, + // CTC on striking + CTCOSAMP: 3, + CTCOSDECREP: 8, + // regen + HPREGEN: 2, + FHR: 3, // faster hit recovery + DEF: 0.05, // defense + HP: 2, + STR: 1.5, + DEX: 1.5, + ALL: 60, // + all skills + FR: 2, // fire resist + LR: 2, // lightning resist + CR: 1.5, // cold resist + PR: 1, // poison resist + ABS: 2.7, // absorb damage (fire light magic cold) + DR: 2, // Damage resist + MR: 3, // Magic damage resist + }; + + let mercRating = 1; + // start + item.prefixnum === sdk.locale.items.Treachery && (mercRating += item.getStatEx(sdk.stats.SkillWhenStruck, 2) * 1000); // fade + mercRating += item.getStatEx(sdk.stats.SkillOnAura, sdk.skills.Conviction) * 1000; // conviction aura + mercRating += item.getStatEx(sdk.stats.SkillOnAura, sdk.skills.Meditation) * 100; // meditation aura + mercRating += item.getStatEx(sdk.stats.AllSkills) * mercWeights.ALL; // add all skills + mercRating += item.getStatEx(sdk.stats.IAS) * mercWeights.IAS; // add IAS + mercRating += item.getStatEx(sdk.stats.ToHit) * mercWeights.AR; // add AR + mercRating += item.getStatEx(sdk.stats.CrushingBlow) * mercWeights.CB; // add crushing blow + mercRating += item.getStatEx(sdk.stats.OpenWounds) * mercWeights.OW; // add open wounds + mercRating += item.getStatEx(sdk.stats.LifeLeech) * mercWeights.LL; // add LL + mercRating += item.getStatEx(sdk.stats.HpRegen) * mercWeights.HPREGEN; // add hp regeneration + mercRating += item.getStatEx(sdk.stats.FHR) * mercWeights.FHR; // add faster hit recovery + mercRating += item.getStatEx(sdk.stats.Defense) * mercWeights.DEF; // add Defense + mercRating += item.getStatEx(sdk.stats.Strength) * mercWeights.STR; // add STR + mercRating += item.getStatEx(sdk.stats.Dexterity) * mercWeights.DEX; // add DEX + mercRating += item.getStatEx(sdk.stats.FireResist) * mercWeights.FR; // add FR + mercRating += item.getStatEx(sdk.stats.ColdResist) * mercWeights.CR; // add CR + mercRating += item.getStatEx(sdk.stats.LightResist) * mercWeights.LR; // add LR + mercRating += item.getStatEx(sdk.stats.PoisonResist) * mercWeights.PR; // add PR + mercRating += (item.getStatEx(sdk.stats.Vitality) + item.getStatEx(sdk.stats.MaxHp) + (item.getStatEx(sdk.stats.PerLevelHp) / 2048 * me.charlvl)) * mercWeights.HP; // add HP + mercRating += sumElementalDmg(item) * mercWeights.ELEDMG; // add elemental damage + mercRating += (item.getStatEx(sdk.stats.AbsorbFirePercent) + item.getStatEx(sdk.stats.AbsorbLightPercent) + item.getStatEx(sdk.stats.AbsorbMagicPercent) + item.getStatEx(sdk.stats.AbsorbColdPercent)) * mercWeights.ABS; // add absorb damage + mercRating += item.getStatEx(sdk.stats.NormalDamageReduction) * mercWeights.DR; // add integer damage resist + mercRating += item.getStatEx(sdk.stats.DamageResist) * mercWeights.DR * 2; // add damage resist % + mercRating += item.getStatEx(sdk.stats.MagicDamageReduction) * mercWeights.MR; // add integer magic damage resist + mercRating += item.getStatEx(sdk.stats.MagicResist) * mercWeights.MR * 2; // add magic damage resist % + + switch (me.data.merc.classid) { + case sdk.mercs.Rogue: + case sdk.mercs.IronWolf: + mercRating += item.getStatEx(sdk.stats.MinDamage) * mercWeights.MINDMG; // add MIN damage + mercRating += item.getStatEx(sdk.stats.MaxDamage) * mercWeights.MAXDMG; // add MAX damage + + break; + case sdk.mercs.A5Barb: + if ([item.getStatEx(sdk.stats.SecondaryMinDamage), item.getStatEx(sdk.stats.SecondaryMaxDamage)].includes(0)) { + mercRating += item.getStatEx(sdk.stats.MinDamage) * mercWeights.MINDMG; // add MIN damage + mercRating += item.getStatEx(sdk.stats.MaxDamage) * mercWeights.MAXDMG; // add MAX damage + + break; + } + // eslint-disable-next-line no-fallthrough + case sdk.mercs.Guard: + default: + mercRating += item.getStatEx(sdk.stats.SecondaryMinDamage) * mercWeights.SECMINDMG; + mercRating += item.getStatEx(sdk.stats.SecondaryMaxDamage) * mercWeights.SECMAXDMG; + + break; + } + + if (!me.sorceress && !me.necromancer && !me.assassin) { + mercRating += item.getStatEx(sdk.stats.SkillOnAttack, 4238) * mercWeights.CTCOAAMP; // add CTC amplify damage on attack + mercRating += item.getStatEx(sdk.stats.SkillOnAttack, 4225) * mercWeights.CTCOAAMP; // add CTC amplify damage on attack (magic items) + mercRating += item.getStatEx(sdk.stats.SkillOnAttack, 5583) * mercWeights.CTCOADECREP; // add CTC decrepify on attack + mercRating += item.getStatEx(sdk.stats.SkillOnAttack, 5631) * mercWeights.CTCOADECREP; // add CTC decrepify on attack (magic items) + mercRating += item.getStatEx(sdk.stats.SkillOnHit, 4238) * mercWeights.CTCOSAMP; // add CTC amplify damage on strikng + mercRating += item.getStatEx(sdk.stats.SkillOnHit, 4225) * mercWeights.CTCOSAMP; // add CTC amplify damage on strikng (magic items) + mercRating += item.getStatEx(sdk.stats.SkillOnHit, 5583) * mercWeights.CTCOSDECREP; // add CTC decrepify on strikng + mercRating += item.getStatEx(sdk.stats.SkillOnHit, 5631) * mercWeights.CTCOSDECREP; // add CTC decrepify on strikng (magic items) + } + + if (item.isBaseType && !item.isRuneword) { + for (let runeword of Config.Runewords) { + let [sockets, baseCID] = [runeword[0].sockets, runeword[1]]; + if (item.classid === baseCID && item.sockets === sockets && !item.getItemsEx().length) return -1; + } + } + + return mercRating; + }; + + /** + * @param {ItemUnit} item + * @param {number} [skillId] + * @param {object} [buildInfo] + */ + const chargeditemscore = function (item, skillId, buildInfo) { + if (!item) return 0; + + let tier = 0; + !buildInfo && (buildInfo = Check.currentBuild()); + + /** + * @constructor + * @param {{ skill: number, level: number, charges: number, maxcharges: number}} obj + */ + function ChargedItem (obj = {}) { + this.skill = obj.skill; + this.level = obj.level; + this.charges = obj.charges; + this.maxcharges = obj.maxcharges; + } + const _chargedWeights = new Map([ + [sdk.skills.Teleport, (Pather.canTeleport() ? 0 : 5)], + [sdk.skills.Enchant, (buildInfo.caster ? 0 : 10)], + [sdk.skills.InnerSight, (me.amazon || buildInfo.caster ? 0 : 10)], + [sdk.skills.SlowMissiles, (me.amazon ? 0 : 10)], + ]); + + let stats = item.getStat(-2); + let chargedItems = []; + + if (stats.hasOwnProperty(sdk.stats.ChargedSkill)) { + if (stats[sdk.stats.ChargedSkill] instanceof Array) { + for (let i = 0; i < stats[sdk.stats.ChargedSkill].length; i++) { + if (stats[sdk.stats.ChargedSkill][i] !== undefined) { + chargedItems.push(new ChargedItem(stats[sdk.stats.ChargedSkill][i])); + } + } + } else { + chargedItems.push(new ChargedItem(stats[sdk.stats.ChargedSkill])); + } + } + + chargedItems = chargedItems + .filter((v, i, a) => a.findIndex(el => ["skill", "level"].every(k => el[k] === v[k])) === i); + + if (skillId > 0) { + chargedItems + .filter(check => check.skill === skillId) + .forEach(el => tier += el.level * 5); + } else { + chargedItems.forEach(function (el) { + if (el.skill === sdk.skills.Teleport) { + tier += el.maxcharges * 2; + } else if (_chargedWeights.has(el.skill)) { + tier += el.level * _chargedWeights.get(el.skill); + } + }); + } + + return tier; + }; + + const _tierWeights = (function () { + const hc = me.hardcore; + const buildInfo = Check.currentBuild(); + /** @type {Map} */ + const res = new Map([ + [sdk.stats.FireResist, hc ? 5 : 3], + [sdk.stats.LightningResist, hc ? 5 : 3], + [sdk.stats.ColdResist, hc ? 3 : 1.5], + [sdk.stats.PoisonResist, hc ? 5 : 1], + [sdk.stats.MaxFireResist, 5], + [sdk.stats.MaxLightResist, 5], + [sdk.stats.MaxColdResist, 3], + [sdk.stats.MaxPoisonResist, 3], + [sdk.stats.AbsorbFire, hc ? 2 : 1], + [sdk.stats.AbsorbFirePercent, hc ? 3 : 1.5], + [sdk.stats.AbsorbLight, hc ? 2 : 1], + [sdk.stats.AbsorbLightPercent, hc ? 3 : 1.5], + [sdk.stats.AbsorbCold, hc ? 1 : 0.5], + [sdk.stats.AbsorbColdPercent, hc ? 1.5 : 0.75], + [sdk.stats.AbsorbMagic, hc ? 2 : 1], + [sdk.stats.AbsorbMagicPercent, hc ? 3 : 1.5], + [sdk.stats.NormalDamageReduction, hc ? 1 : 0.5], + [sdk.stats.MagicDamageReduction, hc ? 1 : 0.5], + [sdk.stats.DamageResist, hc ? 5 : 2], + [sdk.stats.MagicResist, hc ? 6 : 3], + ]); + /** @type {Map} */ + const gen = new Map([ + [sdk.stats.CannotbeFrozen, buildInfo.caster ? 25 : 100], + [sdk.stats.FRW, 1], + [sdk.stats.FHR, 3], + [sdk.stats.FBR, 1], + [sdk.stats.ToBlock, 1], + [sdk.stats.IAS, buildInfo.caster && !me.assassin ? 0 : 4], + [sdk.stats.FCR, buildInfo.caster ? me.assassin ? 2 : 5 : 0.5], + [sdk.stats.Defense, 0.05], + [sdk.stats.MagicBonus, 1], + [sdk.stats.GoldBonus, 0.5], + [sdk.stats.Vitality, 1], + [sdk.stats.MaxHp, 1], + [sdk.stats.PerLevelHp, 1], + [sdk.stats.HpRegen, 2], + [sdk.stats.Energy, 1], + [sdk.stats.MaxMana, 1], + [sdk.stats.PerLevelMana, 1], + [sdk.stats.ManaRecovery, buildInfo.caster ? 2.5 : 1], + [sdk.stats.Strength, 1], + [sdk.stats.Dexterity, me.amazon ? 3 : 1], + [sdk.stats.ReplenishQuantity, me.amazon ? 50 : 0], + [sdk.stats.ToHit, 0.2], + [sdk.stats.CrushingBlow, 4], + [sdk.stats.OpenWounds, 1], + [sdk.stats.DeadlyStrike, 1.5], + [sdk.stats.LifeLeech, 4], + [sdk.stats.ManaLeech, 2], + [sdk.stats.DemonDamagePercent, 0.5], + [sdk.stats.UndeadDamagePercent, 0.5], + [sdk.stats.ReplenishDurability, 15], + [sdk.stats.IgnoreTargetDefense, 50], + [sdk.stats.MinDamage, 3], + [sdk.stats.MaxDamage, 3], + [sdk.stats.SecondaryMinDamage, 2], + [sdk.stats.SecondaryMaxDamage, 2], + ]); + /** @type {Map} */ + const skill = new Map([ + [sdk.stats.AllSkills, 200], + [sdk.stats.AddClassSkills, 175], + [sdk.stats.AddSkillTab, 125], + ]); + /** @type {Map} */ + const charms = new Map([ + [sdk.stats.AllSkills, 180], + [sdk.stats.AddClassSkills, 175], + [sdk.stats.AddSkillTab, 300], + [sdk.stats.FireResist, 3], + [sdk.stats.LightningResist, 5], + [sdk.stats.ColdResist, 2], + [sdk.stats.PoisonResist, 1], + [sdk.stats.FRW, 1], + [sdk.stats.FHR, (me.barbarian ? 4 : 2)], + [sdk.stats.Defense, 0.05], + [sdk.stats.MagicBonus, 2], + [sdk.stats.GoldBonus, 0.5], + [sdk.stats.MaxHp, 1.75], + [sdk.stats.PerLevelHp, 1], + [sdk.stats.HpRegen, 2], + [sdk.stats.MaxMana, 1], + [sdk.stats.PerLevelMana, 1], + [sdk.stats.Strength, 1], + [sdk.stats.Dexterity, me.amazon ? 3 : 1], + [sdk.stats.Vitality, 1], + [sdk.stats.Energy, 0.8], + ]); + + /** @type {Map} */ + const ctc = new Map([ + [sdk.stats.SkillWhenStruck, 2], + [sdk.stats.SkillOnAttack, 2], + [sdk.stats.SkillOnStrike, 1], + [sdk.skills.Nova, 2], + [sdk.skills.FrostNova, 4], + [sdk.skills.IceBlast, 4], + [sdk.skills.ChargedBolt, 4], + [sdk.skills.StaticField, 5], + [sdk.skills.GlacialSpike, 6], + [sdk.skills.ChainLightning, 6], + [sdk.skills.Blizzard, 4], + [sdk.skills.FrozenOrb, 8], + [sdk.skills.Hydra, 4], + [sdk.skills.AmplifyDamage, 5], + [sdk.skills.Decrepify, 10], + [sdk.skills.LifeTap, 10], + [sdk.skills.BoneArmor, 10], + [sdk.skills.BoneSpear, 8], + [sdk.skills.BoneSpirit, 8], + [sdk.skills.PoisonNova, 10], + [sdk.skills.Taunt, 5], + [sdk.skills.Howl, 5], + [sdk.skills.CycloneArmor, 10], + [sdk.skills.Twister, 5], + [sdk.skills.Fade, 10], + [sdk.skills.Venom, 8], + ]); + + return { + res: res, + gen: gen, + skill: skill, + charms: charms, + ctc: ctc, + }; + })(); + + /** + * @param {ItemUnit} item + * @param {number} [bodyloc] + * @todo Breakpoint scoring similar to how res is scored + */ + const tierscore = function (item, tier, bodyloc) { + if (item.questItem) return -1; + const itembodyloc = Item.getBodyLoc(item); + if (!itembodyloc.length) return -1; + bodyloc = bodyloc || itembodyloc.last(); + + if (item.isBaseType && !item.isRuneword && me.charlvl > 10) { + for (let runeword of Config.Runewords) { + let [sockets, baseCID] = [runeword[0].sockets, runeword[1]]; + if (item.classid === baseCID && item.sockets === sockets && !item.getItemsEx().length) return -1; + } + } + + const buildInfo = Check.currentBuild(); + const canTele = Pather.canTeleport(); + // const eqItem = me.equipped.get(bodyloc); + const eqItem = me.getEquippedItem(bodyloc); + + const generalScore = () => { + let generalRating = 0; + + // get item cbf stat from olditem equipped on body location + if (!canTele && item.getStatEx(sdk.stats.CannotbeFrozen)) { + // check if we have cbf but make sure its not from the item we are trying to un-equip + if (!me.getStat(sdk.stats.CannotbeFrozen)) { + // Cannot be frozen is very important for Melee chars + generalRating += _tierWeights.gen.get(sdk.stats.CannotbeFrozen); + } + } + + // belt slots + if (item.itemType === sdk.items.type.Belt) { + const beltSizes = { lbl: 2, vbl: 2, mbl: 3, tbl: 3 }; + const beltSize = beltSizes[item.code] || 4; + // if our current belt-size is better, don't down-grade even if the other stats on the new item are better, not worth the town visits + generalRating += (Storage.BeltSize() > beltSize ? -50 : (beltSize * 4 * 2)); + } + + // pierce/mastery's not sure how I want to weight this so for now just its base value + buildInfo.usefulStats.forEach(stat => generalRating += item.getStatEx(stat)); + + // start generalRating + !item.isRuneword && (generalRating += (item.sockets * 10)); // priortize sockets + generalRating += ((item.getStatEx(sdk.stats.PerLevelHp) / 2048 * me.charlvl)) * _tierWeights.gen.get(sdk.stats.PerLevelHp); + generalRating += ((item.getStatEx(sdk.stats.PerLevelMana) / 2048 * me.charlvl)) * _tierWeights.gen.get(sdk.stats.PerLevelMana); + + return [ + sdk.stats.FHR, sdk.stats.FRW, sdk.stats.FBR, sdk.stats.FCR, sdk.stats.ToBlock, + sdk.stats.MagicBonus, sdk.stats.GoldBonus, sdk.stats.Defense, sdk.stats.ManaRecovery, + sdk.stats.Strength, sdk.stats.Dexterity, sdk.stats.Vitality, sdk.stats.Energy, + sdk.stats.MaxHp, sdk.stats.MaxMana, sdk.stats.ReplenishQuantity, sdk.stats.HpRegen, + ].reduce((acc, stat) => acc + item.getStatEx(stat) * _tierWeights.gen.get(stat), generalRating); + }; + + const resistScore = () => { + let resistRating = 0; + + // get new item stats + let [newitemFR, newitemCR, newitemLR, newitemPR] = [ + item.getStatEx(sdk.stats.FireResist), + item.getStatEx(sdk.stats.ColdResist), + item.getStatEx(sdk.stats.LightResist), + item.getStatEx(sdk.stats.PoisonResist) + ]; + // only enter next block if we have a new item with resists + if (newitemFR || newitemCR || newitemLR || newitemPR) { + const maxRes = me.hell ? 80 : (80 + me.getResPenalty(me.diff + 1) - me.getResPenalty(me.diff)); + // get item resists stats from olditem equipped on body location + let [olditemFR, olditemCR, olditemLR, olditemPR] = [0, 0, 0, 0]; + if (eqItem) { + // equipped resists + [olditemFR, olditemCR, olditemLR, olditemPR] = [ + eqItem.getStatEx(sdk.stats.FireResist), eqItem.getStatEx(sdk.stats.ColdResist), + eqItem.getStatEx(sdk.stats.LightResist), eqItem.getStatEx(sdk.stats.PoisonResist) + ]; + } + // subtract olditem resists from current total resists + const [baseFR, baseCR, baseLR, basePR] = [ + me.fireRes - olditemFR, + me.coldRes - olditemCR, + me.lightRes - olditemLR, + me.poisonRes - olditemPR, + ]; + // if baseRes < max resists give score value upto max resists reached + const [FRlimit, CRlimit, LRlimit, PRlimit] = [ + Math.max(maxRes - baseFR, 0), + Math.max(maxRes - baseCR, 0), + Math.max(maxRes - baseLR, 0), + Math.max(maxRes - basePR, 0) + ]; + // newitemRes upto reslimit + let [effectiveFR, effectiveCR, effectiveLR, effectivePR] = [ + Math.min(newitemFR, FRlimit), + Math.min(newitemCR, CRlimit), + Math.min(newitemLR, LRlimit), + Math.min(newitemPR, PRlimit) + ]; + // sum resistRatings + resistRating += effectiveFR * _tierWeights.res.get(sdk.stats.FireResist); + resistRating += effectiveCR * _tierWeights.res.get(sdk.stats.ColdResist); + resistRating += effectiveLR * _tierWeights.res.get(sdk.stats.LightResist); + resistRating += effectivePR * _tierWeights.res.get(sdk.stats.PoisonResist); + } + + return ([ + sdk.stats.MaxFireResist, sdk.stats.MaxLightResist, sdk.stats.MaxColdResist, sdk.stats.MaxPoisonResist, + sdk.stats.AbsorbFire, sdk.stats.AbsorbLight, sdk.stats.AbsorbMagic, sdk.stats.AbsorbCold, + sdk.stats.AbsorbFirePercent, sdk.stats.AbsorbLightPercent, sdk.stats.AbsorbMagicPercent, sdk.stats.AbsorbColdPercent, + sdk.stats.NormalDamageReduction, sdk.stats.DamageResist, sdk.stats.MagicDamageReduction, sdk.stats.MagicResist + ].reduce((acc, stat) => acc + item.getStatEx(stat) * _tierWeights.res.get(stat), resistRating)); + }; + + const buildScore = () => { + // dirty fix maybe? + if (me.barbarian && SetUp.currentBuild !== "Immortalwhirl" && item.strictlyTwoHanded) { + return 0; + } + // Melee Specific + if (!buildInfo.caster + || Config.AttackSkill.includes(sdk.skills.Attack) + || Config.LowManaSkill.includes(sdk.skills.Attack) + || ([sdk.items.type.Bow, sdk.items.type.AmazonBow, sdk.items.type.Crossbow].includes(item.itemType) && CharData.skillData.bow.onSwitch)) { + let meleeRating = 0; + const eleDmgWeight = 0.5; + const eleDmgModifer = [sdk.items.type.Ring, sdk.items.type.Amulet].includes(item.itemType) ? 2 : 1; + + meleeRating += ((item.getStatEx(sdk.stats.MaxDamage) + item.getStatEx(sdk.stats.MinDamage)) / 2) * 3; + meleeRating += sumElementalDmg(item) * (eleDmgWeight / eleDmgModifer); // add elemental damage + meleeRating += item.getStatEx(sdk.stats.SkillOnAura, sdk.skills.Sanctuary) * 25; // sanctuary aura + + [ + sdk.stats.ReplenishDurability, sdk.stats.IgnoreTargetDefense, sdk.stats.ToHit, sdk.stats.CrushingBlow, + sdk.stats.OpenWounds, sdk.stats.DeadlyStrike, sdk.stats.LifeLeech, sdk.stats.ManaLeech, + sdk.stats.DemonDamagePercent, sdk.stats.UndeadDamagePercent, + ].reduce((acc, stat) => acc + item.getStatEx(stat) * _tierWeights.gen.get(stat), meleeRating); + buildInfo.caster && (meleeRating /= 2); + + return meleeRating; + } + + return 0; + }; + + const skillsScore = () => { + let skillsRating = [ + [sdk.stats.AllSkills, -1], [sdk.stats.AddClassSkills, me.classid], [sdk.stats.AddSkillTab, buildInfo.tabSkills], + ].reduce((acc, [stat, subId]) => acc + item.getStatEx(stat, subId) * _tierWeights.skill.get(stat), 0); + (!buildInfo.caster && item.getItemType() === "Weapon") && (skillsRating /= 4); + const _misc = { wanted: 40, useful: 35 }; + + let selectedWeights = [_misc.wanted, _misc.useful]; + let selectedSkills = [buildInfo.wantedSkills, buildInfo.usefulSkills]; + + for (let i = 0; i < selectedWeights.length; i++) { + for (let j = 0; j < selectedSkills.length; j++) { + for (let k = 0; k < selectedSkills[j].length; k++) { + skillsRating += item.getStatEx(sdk.stats.SingleSkill, selectedSkills[j][k]) * selectedWeights[i]; + } + } + } + + // Spirit Fix for barb + (item.prefixnum === sdk.locale.items.Spirit && !buildInfo.caster) && (skillsRating -= 400); + + return skillsRating; + }; + + const ctcScore = () => { + // chance to cast doesn't exist in classic + if (me.classic) return 0; + + let ctcRating = 0; + let ctcItems = []; + const stats = item.getStat(-2); + const ctcSkillObj = (ctcType, skill, level) => ({ ctcType: ctcType, skill: skill, level: level }); + const meleeCheck = !buildInfo.caster; + /** + * @param {number} type + */ + const buildList = function (type) { + let skill, level; + if (stats.hasOwnProperty(type)) { + if (stats[type] instanceof Array) { + for (let i = 0; i < stats[type].length; i++) { + if (stats[type][i] !== undefined) { + ({ skill, level } = stats[type][i]); + ctcItems.push(ctcSkillObj(type, skill, level)); + } + } + } else { + ({ skill, level } = stats[type]); + ctcItems.push(ctcSkillObj(type, skill, level)); + } + } + }; + + buildList(sdk.stats.SkillWhenStruck); + + if (meleeCheck) { + buildList(sdk.stats.SkillOnAttack); + buildList(sdk.stats.SkillOnStrike); + } else { + _tierWeights.ctc.set(sdk.skills.Venom, 0); + if (me.charlvl > 50) { + _tierWeights.ctc.set(sdk.skills.ChargedBolt, 2); + } + } + if (!ctcItems.length) return 0; + + ctcItems + .filter((v, i, a) => a.findIndex(el => ["ctcType", "skill"].every(k => el[k] === v[k])) === i) + .forEach(el => { + if (!_tierWeights.ctc.has(el.skill)) return; + ctcRating += (el.level * _tierWeights.ctc.get(el.skill) * _tierWeights.ctc.get(el.ctcType)); + }); + + return ctcRating; + }; + + tier === undefined && (tier = 1); // set to 1 for native autoequip to use items. + tier += generalScore(); + tier += resistScore(); + tier += buildScore(); + tier += skillsScore(); + tier += ctcScore(); + tier += chargeditemscore(item, -1, buildInfo); + + if (tier > 1 && tier < NTIP.MAX_TIER && NTIP.CheckItem(item, NTIP.FinalGear.list) === Pickit.Result.WANTED) { + // console.debug(item.prettyPrint + "~~~" + tier); + tier += NTIP.MAX_TIER; + } + + return Math.max(1, tier); + }; + + /** + * @param {ItemUnit} item + */ + const secondaryscore = function (item) { + let tier = 0; + + Check.finalBuild().precastSkills + .forEach(skill => tier += item.getStatEx(sdk.stats.SingleSkill, skill) * 50); + + tier += item.getStatEx(sdk.stats.FCR) * 5; // add FCR + tier += item.getStatEx(sdk.stats.FHR) * 3; // add faster hit recovery + + return [ + [sdk.stats.AllSkills, -1], + [sdk.stats.AddClassSkills, me.classid], + [sdk.stats.AddSkillTab, Check.finalBuild().tabSkills], + ].reduce((acc, [stat, subId]) => acc + item.getStatEx(stat, subId) * _tierWeights.skill.get(stat), tier); + }; + + /** + * @param {ItemUnit} item + */ + const charmscore = function (item) { + if (me.data.charmGids.includes(item.gid)) return 1000; + // depending on invo space it might be worth it early on to keep 1 or 2 non-skiller grandcharms - @todo test that out + if (!item.unique && item.classid === sdk.items.GrandCharm && !me.getSkillTabs().some(s => item.getStatEx(sdk.stats.AddSkillTab, s))) return -1; + const buildInfo = Check.currentBuild(); + + let charmRating = 1; + + // Gheeds, Torch, annhi - we know possible attributes so don't waste resources checking for all the others + if (item.unique) { + charmRating += item.getStatEx(sdk.stats.Strength); // handle +all atrributes + charmRating += item.getStatEx(sdk.stats.AllRes); + if (item.isAnni) { + charmRating += item.getStatEx(sdk.stats.AllSkills) * _tierWeights.charms.get(sdk.stats.AllSkills); + charmRating += item.getStatEx(sdk.stats.AddExperience); + } else if (item.isGheeds) { + charmRating += item.getStatEx(sdk.stats.GoldBonus); + charmRating += item.getStatEx(sdk.stats.ReducedPrices) * 1.5; + charmRating += item.getStatEx(sdk.stats.MagicBonus) * _tierWeights.charms.get(sdk.stats.MagicBonus); + } else { + charmRating += item.getStatEx(sdk.stats.AddClassSkills, me.classid) * _tierWeights.charms.get(sdk.stats.AddClassSkills); + } + } else { + charmRating += item.getStatEx(sdk.stats.AddSkillTab, buildInfo.tabSkills) * _tierWeights.charms.get(sdk.stats.AddSkillTab); + charmRating += ((item.getStatEx(sdk.stats.PerLevelHp) / 2048 * me.charlvl)) * _tierWeights.charms.get(sdk.stats.PerLevelHp); + charmRating += ((item.getStatEx(sdk.stats.PerLevelMana) / 2048 * me.charlvl)) * _tierWeights.charms.get(sdk.stats.PerLevelMana); + + if (!buildInfo.caster) { + charmRating += item.getStatEx(sdk.stats.MinDamage) * 3; // add MIN damage + charmRating += item.getStatEx(sdk.stats.MaxDamage) * 3; // add MAX damage + charmRating += sumElementalDmg(item); // add elemental damage + charmRating += item.getStatEx(sdk.stats.ToHit) * 0.5; // add AR + } + + return [ + sdk.stats.MaxHp, sdk.stats.MaxMana, + sdk.stats.FireResist, sdk.stats.LightResist, sdk.stats.ColdResist, sdk.stats.PoisonResist, + sdk.stats.FHR, sdk.stats.FRW, sdk.stats.MagicBonus, sdk.stats.GoldBonus, sdk.stats.Defense, + sdk.stats.Strength, sdk.stats.Dexterity, sdk.stats.Vitality, sdk.stats.Energy, + ].reduce((acc, stat) => acc + item.getStatEx(stat) * _tierWeights.charms.get(stat), charmRating); + } + return charmRating; + }; + + // export to global scope + global.mercscore = mercscore; + global.chargeditemscore = chargeditemscore; + global.tierscore = tierscore; + global.secondaryscore = secondaryscore; + global.charmscore = charmscore; })(); diff --git a/libs/SoloPlay/Functions/Globals.js b/libs/SoloPlay/Functions/Globals.js index c508931c..a274e158 100644 --- a/libs/SoloPlay/Functions/Globals.js +++ b/libs/SoloPlay/Functions/Globals.js @@ -33,1033 +33,1033 @@ const MYCLASSNAME = sdk.player.class.nameOf(me.classid).toLowerCase(); includeIfNotIncluded("SoloPlay/BuildFiles/" + MYCLASSNAME + "/" + MYCLASSNAME + ".js"); function myPrint (str = "", toConsole = false, color = 0) { - console.log("ÿc8Kolbot-SoloPlayÿc0: " + str); - me.overhead(str); - - if (toConsole && typeof color === "string") { - color = color.capitalize(true); - color = !!sdk.colors.D2Bot[color] ? sdk.colors.D2Bot[color] : 0; - } - toConsole && D2Bot.printToConsole("Kolbot-SoloPlay :: " + str, color); + console.log("ÿc8Kolbot-SoloPlayÿc0: " + str); + me.overhead(str); + + if (toConsole && typeof color === "string") { + color = color.capitalize(true); + color = !!sdk.colors.D2Bot[color] ? sdk.colors.D2Bot[color] : 0; + } + toConsole && D2Bot.printToConsole("Kolbot-SoloPlay :: " + str, color); } // general settings const SetUp = { - mercEnabled: true, - _buildTemplate: "", - - init: function () { - // ensure finalBuild is properly formatted - let checkBuildTemplate = () => { - let build = (["Bumper", "Socketmule", "Imbuemule"].includes(SetUp.finalBuild) - ? ["Javazon", "Cold", "Bone", "Hammerdin", "Whirlwind", "Wind", "Trapsin"][me.classid] - : SetUp.finalBuild) + "Build"; - return ("libs/SoloPlay/BuildFiles/" + MYCLASSNAME + "/" + MYCLASSNAME + "." + build + ".js").toLowerCase(); - }; - SetUp._buildTemplate = checkBuildTemplate(); - - if (!FileTools.exists(SetUp._buildTemplate)) { - let errors = []; - /** @type {string[]} */ - let possibleBuilds = dopen("libs/SoloPlay/BuildFiles/" + MYCLASSNAME + "/") - .getFiles() - .filter(file => file.includes("Build")) - .map(file => file.substring(file.indexOf(".") + 1, file.indexOf("Build"))); - - // try to see if we can correct the finalBuild - for (let build of possibleBuilds) { - let match = me.data.finalBuild.match(build, "gi"); - - if (match) { - console.log(match); - let old = me.data.finalBuild; - me.data.finalBuild = match[0].trim().capitalize(true); - errors.push( - "~Info tag :: " + old + " was incorrect, I have attempted to remedy this." - + " If it is still giving you an error please re-read the documentation. \n" - + "New InfoTag/finalBuild :: " + SetUp.finalBuild - ); - - break; - } - } - - if (errors.length) { - D2Bot.printToConsole("Kolbot-SoloPlay Final Build Error :: \n" + errors.join("\n"), sdk.colors.D2Bot.Red); - SetUp._buildTemplate = checkBuildTemplate(); // check again - if (!FileTools.exists(SetUp._buildTemplate)) { - console.error( - "ÿc8Kolbot-SoloPlayÿc0: Failed to find finalBuild template." - + " Please check that you have actually entered it in correctly," - + " and that you have the build in to BuildFiles folder." - + " Here is what you currently have: " + SetUp.finalBuild); - throw new Error("finalBuild(): Failed to find template: " + SetUp._buildTemplate); - } - D2Bot.setProfile(null, null, null, null, null, SetUp.finalBuild); - CharData.updateData("me", "finalBuild", SetUp.finalBuild); - } - } - - if (!me.data.initialized) { - me.data.startTime = me.gamestarttime; - me.data.level = me.charlvl; - me.data.classid = me.classid; - me.data.charName = me.name; - me.data.strength = me.rawStrength; - me.data.dexterity = me.rawDexterity; - - if (me.expansion) { - me.data.charms = Check.finalBuild().finalCharms; - } - - me.data.initialized = true; - CharData.updateData("me", me.data); - } - - let temp = copyObj(me.data); - - if (me.data.currentBuild !== CharInfo.getActiveBuild()) { - me.data.currentBuild = CharInfo.getActiveBuild(); - } - - let currDiffStr = sdk.difficulty.nameOf(me.diff).toLowerCase(); - - if (sdk.difficulty.Difficulties.indexOf(me.data.highestDifficulty) < sdk.difficulty.Difficulties.indexOf(sdk.difficulty.nameOf(me.diff))) { - me.data.highestDifficulty = sdk.difficulty.nameOf(me.diff); - } - - if (me.smith && me.data[currDiffStr].imbueUsed === false) { - me.data[currDiffStr].imbueUsed = true; - } - - if (me.respec && me.data[currDiffStr].respecUsed === false) { - me.data[currDiffStr].respecUsed = true; - } - - me.data.level !== me.charlvl && (me.data.level = me.charlvl); - me.data.strength !== me.rawStrength && (me.data.strength = me.rawStrength); - me.data.dexterity !== me.rawDexterity && (me.data.dexterity = me.rawDexterity); - - // expansion check - let [cUpdate, mUpdate] = [false, false]; - - if (me.expansion) { - if (!me.data.merc.gear) { - me.data.merc.gear = []; - mUpdate = true; - } - - // merc check - if (me.getMercEx()) { - // TODO: figure out how to ensure we are already using the right merc to prevent re-hiring - // can't do an aura check as merc auras are bugged, only useful info from getUnit is the classid - let merc = me.getMercEx(); - let mercItems = merc.getItemsEx(); - let preLength = me.data.merc.gear.length; - let check = me.data.merc.gear.filter(i => mercItems.some(item => item.prefixnum === i)); - - if (check !== preLength) { - mUpdate = true; - me.data.merc.gear = check; - } - - let mercInfo = Mercenary.getMercInfo(merc); - mercInfo.classid !== me.data.merc.classid && (me.data.merc.classid = mercInfo.classid); - mercInfo.act !== me.data.merc.act && (me.data.merc.act = mercInfo.act); - mercInfo.difficulty !== me.data.merc.difficulty && (me.data.merc.difficulty = mercInfo.difficulty); - - if (merc.classid !== sdk.mercs.Guard) { - try { - if (mercInfo.skillName !== me.data.merc.skillName) { - me.data.merc.skillName = mercInfo.skillName; - me.data.merc.skill = MercData.findByName(me.data.merc.skillName, me.data.merc.act).skill; - } - } catch (e) { - // - } - } - - // if (merc.classid === sdk.mercs.Guard && !Mercenary.checkMercSkill(me.data.merc.type)) { - // // go back, need to make sure this works properly. - // // only "go back" if we are past the difficulty we need to be in to hire merc. Ex. In hell but want holy freeze merc - // // only if we have enough gold on hand to hire said merc - // // return to our orignal difficulty afterwards - // } - } - - // charm check - if (!me.data.charms || !Object.keys(me.data.charms).length) { - me.data.charms = Check.finalBuild().finalCharms; - cUpdate = true; - } - - if (!me.data.charmGids || me.data.charmGids.length > 0) { - me.data.charmGids = []; - cUpdate = true; - } - - const finalCharmKeys = Object.keys(me.data.charms); - // gids change from game to game so reset our list - for (let i = 0; i < finalCharmKeys.length; i++) { - let cKey = finalCharmKeys[i]; - if (me.data.charms[cKey].have.length) { - me.data.charms[cKey].have = []; - cUpdate = true; - } - } - - if (!!me.shenk && me.data[currDiffStr].socketUsed === false) { - me.data[currDiffStr].socketUsed = true; - } - - if (mUpdate) { - CharData.updateData("merc", me.data); - } - } - - let changed = Misc.recursiveSearch(me.data, temp); - - if (Object.keys(changed).length > 0 || cUpdate) { - CharData.updateData("me", me.data); - } - }, - - // Should this be moved elsewhere? Currently have to include Globals then call this to include rest of overrides - // which in doing so would include globals anyway but does this always need to be included first? - // really need a centralized way to make sure all files use/have the custom functions and all threads stay updated without having to - // scriptBroadcast all the time - include: function () { - let files = dopen("libs/SoloPlay/Functions/").getFiles(); - if (!files.length) throw new Error("Failed to find my files"); - if (!files.includes("Globals.js")) { - console.warn("Incorrect Files?", files); - // something went wrong? - while (!files.includes("Globals.js")) { - files = dopen("libs/SoloPlay/Functions/").getFiles(); - delay(50); - } - } - Array.isArray(files) && files - .filter(file => file.endsWith(".js")) - .sort(a => a.startsWith("PrototypeOverrides.js") ? 0 : 1) // Dirty fix to load new prototypes first - .forEach(function (x) { - if (!isIncluded("SoloPlay/Functions/" + x)) { - if (!include("SoloPlay/Functions/" + x)) { - throw new Error("Failed to include " + "SoloPlay/Functions/" + x); - } - } - }); - }, - - // Storage Settings - sortSettings: { - ItemsSortedFromLeft: [], // default: everything not in Config.ItemsSortedFromRight - ItemsSortedFromRight: [ - // (NOTE: default pickit is fastest if the left side is open) - sdk.items.SmallCharm, sdk.items.LargeCharm, sdk.items.GrandCharm, // sort charms from the right - sdk.items.TomeofIdentify, sdk.items.TomeofTownPortal, sdk.items.Key, // sort tomes and keys to the right - // sort all inventory potions from the right - sdk.items.RejuvenationPotion, sdk.items.FullRejuvenationPotion, - sdk.items.MinorHealingPotion, sdk.items.LightHealingPotion, sdk.items.HealingPotion, sdk.items.GreaterHealingPotion, sdk.items.SuperHealingPotion, - sdk.items.MinorManaPotion, sdk.items.LightManaPotion, sdk.items.ManaPotion, sdk.items.GreaterManaPotion, sdk.items.SuperManaPotion - ], - PrioritySorting: true, - ItemsSortedFromLeftPriority: [/*605, 604, 603, 519, 518*/], // (NOTE: the earlier in the index, the further to the Left) - ItemsSortedFromRightPriority: [ - // (NOTE: the earlier in the index, the further to the Right) - // sort charms from the right, GC > LC > SC - sdk.items.GrandCharm, sdk.items.LargeCharm, sdk.items.SmallCharm, - sdk.items.TomeofIdentify, sdk.items.TomeofTownPortal, sdk.items.Key - ], - }, - - currentBuild: this.currentBuild, - finalBuild: this.finalBuild, - - // setter for Developer option to stop a profile once it reaches a certain level - stopAtLevel: (function () { - if (!Developer.stopAtLevel.enabled) return false; - let level = Developer.stopAtLevel.profiles.find(prof => String.isEqual(prof[0], me.profile)) || false; - return level ? level[1] : false; - })(), - - // pulls respec requirments from final build file - finalRespec: function () { - let respec = Check.finalBuild().respec() ? me.charlvl : 100; - - if (respec === me.charlvl && me.charlvl < 60) { - showConsole(); - console.log("ÿc8Kolbot-SoloPlayÿc0: Bot has respecTwo items but is too low a level to respec."); - console.log("ÿc8Kolbot-SoloPlayÿc0: This only happens with user intervention. Remove the items you gave the bot until at least level 60"); - respec = 100; - } - - return respec; - }, - - autoBuild: function () { - let build = me.currentBuild; - if (!build) throw new Error("Failed to include template: " + SetUp._buildTemplate); - - /* AutoStat configuration. */ - Config.AutoStat.Enabled = true; - Config.AutoStat.Save = 0; - Config.AutoStat.BlockChance = me.paladin ? 75 : 57; - Config.AutoStat.UseBulk = true; - Config.AutoStat.Build = JSON.parse(JSON.stringify(build.stats)); - - /* AutoSkill configuration. */ - Config.AutoSkill.Enabled = true; - Config.AutoSkill.Save = 0; - Config.AutoSkill.Build = JSON.parse(JSON.stringify(build.skills)); - - /* AutoBuild configuration. */ - Config.AutoBuild.Enabled = true; - Config.AutoBuild.Verbose = false; - Config.AutoBuild.DebugMode = false; - Config.AutoBuild.Template = SetUp.currentBuild; - - return true; - }, - - makeNext: function () { - includeIfNotIncluded("SoloPlay/Tools/Tracker.js"); - let gameObj, printTotalTime = Developer.logPerformance; - printTotalTime && (gameObj = Tracker.readObj(Tracker.GTPath)); - - // log info - myPrint(this.finalBuild + " goal reached. On to the next."); - D2Bot.printToConsole("Kolbot-SoloPlay: " + this.finalBuild + " goal reached" + (printTotalTime ? " (" + (Time.format(gameObj.Total + Time.elapsed(gameObj.LastSave))) + "). " : ". ") + "Making next...", sdk.colors.D2Bot.Gold); - - D2Bot.setProfile(null, null, require("../Tools/NameGen")()); - CharData.delete(true); - delay(250); - D2Bot.restart(); - }, - - belt: function () { - let beltSlots = Math.max(1, Storage.BeltSize() - 1); - Config.BeltColumn.forEach(function (col, index) { - Config.MinColumn[index] = col.toLowerCase() !== "rv" ? beltSlots : 0; - }); - }, - - buffers: function () { - const isCaster = Check.currentBuild().caster; - const beltModifer = 4 - Storage.BeltSize(); - const mpFactor = isCaster ? 80 : 50; - Config.MPBuffer = Math.floor(mpFactor / Math.sqrt(me.mpmax)) + (beltModifer * 2); - !me.data.merc.gear.includes(sdk.locale.items.Insight) && (Config.MPBuffer += 2); - const hpFactor = isCaster ? 65 : 80; - Config.HPBuffer = Math.floor(hpFactor / Math.sqrt(me.hpmax)) + (beltModifer * 2); - }, - - bowQuiver: function () { - NTIP.resetRuntimeList(); - if (CharData.skillData.bow.onSwitch) { - if ([sdk.items.type.Bow, sdk.items.type.AmazonBow].includes(CharData.skillData.bow.bowType)) { - NTIP.addToRuntime("[type] == bowquiver # # [maxquantity] == 1"); - } else if (CharData.skillData.bow.bowType === sdk.items.type.Crossbow) { - NTIP.addToRuntime("[type] == crossbowquiver # # [maxquantity] == 1"); - } else if (me.charlvl < 10) { - NTIP.addToRuntime("[type] == bowquiver # # [maxquantity] == 1"); - } - } - }, - - imbueItems: function () { - if (SetUp.finalBuild === "Imbuemule") return []; - let temp = []; - for (let imbueItem of Config.imbueables) { - try { - if (imbueItem.condition()) { - temp.push("[name] == " + imbueItem.name + " && [quality] >= normal && [quality] <= superior && [flag] != ethereal # [Sockets] == 0 # [maxquantity] == 1"); - } - } catch (e) { - console.log(e); - } - } - return temp; - }, - - config: function () { - me.equipped.init(); - // just initializes the data - Check.currentBuild(); - Check.finalBuild(); - - Config.socketables = []; - Config.AutoEquip = true; - - if (me.ladder > 0 || Developer.addLadderRW) { - // Runewords.ladderOverride = true; - Config.LadderOveride = true; - } - - // common items - NTIP.buildList([ - "([type] == helm || [type] == circlet) && ([quality] >= magic || [flag] == runeword) && [flag] != ethereal # [itemchargedskill] >= 0 # [tier] == tierscore(item)", - // Belt - "[type] == belt && [quality] >= magic && [flag] != ethereal # [itemchargedskill] >= 0 # [tier] == tierscore(item)", - "me.normal && [type] == belt && [quality] >= lowquality && [flag] != ethereal # [itemchargedskill] >= 0 # [tier] == tierscore(item)", - // Boots - "[type] == boots && [quality] >= normal && [flag] != ethereal # [itemchargedskill] >= 0 # [tier] == tierscore(item)", - // Armor - "[type] == armor && ([quality] >= magic || [flag] == runeword) && [flag] != ethereal # [itemchargedskill] >= 0 # [tier] == tierscore(item)", - // Gloves - "[type] == gloves && [quality] >= normal && [flag] != ethereal # [itemchargedskill] >= 0 # [tier] == tierscore(item)", - // Amulet - "[type] == amulet && [quality] >= magic # [itemchargedskill] >= 0 # [tier] == tierscore(item)", - // Rings - "[type] == ring && [quality] >= magic # [itemchargedskill] >= 0 # [tier] == tierscore(item)", - // non runeword white items - "([type] == armor) && [quality] >= normal && [flag] != ethereal # [itemchargedskill] >= 0 && [sockets] == 1 # [tier] == tierscore(item)", - "([type] == helm || [type] == circlet) && [quality] >= normal && [flag] != ethereal # [itemchargedskill] >= 0 && ([sockets] == 1 || [sockets] == 3) # [tier] == tierscore(item)", - ]); - - if (me.expansion) { - if (Storage.Stash === undefined) { - Storage.Init(); - } - // sometimes it seems hard to find skillers, if we have the room lets try to cube some - if (Storage.Stash.UsedSpacePercent() < 60 && CharmEquip.grandCharm().keep.length < CharData.charms.get("grand").count().max) { - Config.Recipes.push([Recipe.Reroll.Magic, "Grand Charm"]); - } - // switch bow - only for zon/sorc/pal/necro classes right now - if (!me.barbarian && !me.assassin && !me.druid) { - NTIP.addLine("([type] == bow || [type] == crossbow) && [quality] >= normal # [itemchargedskill] >= 0 # [secondarytier] == tierscore(item)"); - } - const expansionExtras = [ - // Special Charms - "[name] == smallcharm && [quality] == unique # [itemallskills] == 1 # [charmtier] == 100000", - "[name] == largecharm && [quality] == unique # [itemaddclassskills] == 3 # [charmtier] == 100000", - "[name] == grandcharm && [quality] == unique # [itemmagicbonus] >= 30 || [itemgoldbonus] >= 150 # [charmtier] == 100000", - // Merc - "([type] == circlet || [type] == helm) && ([quality] >= magic || [flag] == runeword) # [itemchargedskill] >= 0 # [Merctier] == mercscore(item)", - "[type] == armor && ([quality] >= magic || [flag] == runeword) # [itemchargedskill] >= 0 # [Merctier] == mercscore(item)", - // Rogue - "me.mercid === 271 && [type] == bow && ([quality] >= magic || [flag] == runeword) # [itemchargedskill] >= 0 # [Merctier] == mercscore(item)", - // A2 Guard - "me.mercid === 338 && ([type] == polearm || [type] == spear) && ([quality] >= magic || [flag] == runeword) # [itemchargedskill] >= 0 # [Merctier] == mercscore(item)", - ]; - NTIP.buildList(expansionExtras); - this.bowQuiver(); - } - - /* General configuration. */ - Config.MinGameTime = 400; - Config.MaxGameTime = 7200; - Config.MiniShopBot = true; - Config.PacketShopping = true; - Config.TownCheck = true; - Config.LogExperience = false; - Config.PingQuit = [{ Ping: 600, Duration: 10 }]; - Config.Silence = true; - Config.OpenChests.Enabled = true; - Config.LowGold = me.normal ? 25000 : me.nightmare ? 50000 : 100000; - Config.PrimarySlot = 0; - Config.PacketCasting = 1; - Config.WaypointMenu = true; - Config.Cubing = !!me.getItem(sdk.items.quest.Cube); - Config.MakeRunewords = true; - - /* Chicken configuration. */ - Config.LifeChicken = me.hardcore ? 45 : 10; - Config.ManaChicken = 0; - Config.MercChicken = 0; - Config.TownHP = me.hardcore ? 0 : 35; - Config.TownMP = 0; - - /* Potions configuration. */ - Config.UseHP = me.hardcore ? 90 : 80; - Config.UseRejuvHP = me.hardcore ? 65 : 50; - Config.UseMP = me.hardcore ? 75 : 65; - Config.UseMercHP = 75; - - /* Belt configuration. */ - Config.BeltColumn = ["hp", "mp", "mp", "rv"]; - SetUp.belt(); - - /* Gambling configuration. */ - Config.Gamble = true; - Config.GambleGoldStart = 1250000; - Config.GambleGoldStop = 750000; - - /* AutoMule configuration. */ - Config.AutoMule.Trigger = []; - Config.AutoMule.Force = []; - Config.AutoMule.Exclude = [ - "[name] >= Elrune && [name] <= Lemrune", - ]; - - /* Shrine scan configuration. */ - if (Check.currentBuild().caster) { - Config.ScanShrines = [ - sdk.shrines.Refilling, sdk.shrines.Health, sdk.shrines.Mana, sdk.shrines.Gem, sdk.shrines.Monster, sdk.shrines.HealthExchange, - sdk.shrines.ManaExchange, sdk.shrines.Experience, sdk.shrines.Armor, sdk.shrines.ResistFire, sdk.shrines.ResistCold, - sdk.shrines.ResistLightning, sdk.shrines.ResistPoison, sdk.shrines.Skill, sdk.shrines.ManaRecharge, sdk.shrines.Stamina - ]; - } else { - Config.ScanShrines = [ - sdk.shrines.Refilling, sdk.shrines.Health, sdk.shrines.Mana, sdk.shrines.Gem, sdk.shrines.Monster, sdk.shrines.HealthExchange, - sdk.shrines.ManaExchange, sdk.shrines.Experience, sdk.shrines.Combat, sdk.shrines.Skill, sdk.shrines.Armor, sdk.shrines.ResistFire, - sdk.shrines.ResistCold, sdk.shrines.ResistLightning, sdk.shrines.ResistPoison, sdk.shrines.ManaRecharge, sdk.shrines.Stamina - ]; - } - - /* General logging. */ - Config.ItemInfo = false; - Config.LogKeys = false; - Config.LogOrgans = false; - Config.LogMiddleRunes = true; - Config.LogHighRunes = true; - Config.ShowCubingInfo = true; - - /* DClone. */ - Config.StopOnDClone = !!me.expansion; - Config.SoJWaitTime = 5; // Time in minutes to wait for another SoJ sale before leaving game. 0 = disabled - Config.KillDclone = !!me.expansion; - Config.DCloneQuit = false; - - /* Town configuration. */ - Config.HealHP = 99; - Config.HealMP = 99; - Config.HealStatus = true; - Config.UseMerc = me.expansion; - Config.MercWatch = SetUp.mercwatch; - Config.StashGold = me.charlvl * 100; - Config.ClearInvOnStart = false; - - /* Inventory buffers and lock configuration. */ - Config.HPBuffer = 0; - Config.MPBuffer = 0; - Config.RejuvBuffer = 4; - Config.Inventory[0] = [1, 1, 1, 1, 1, 1, 1, 1, 1, 1]; - Config.Inventory[1] = [1, 1, 1, 1, 1, 1, 1, 1, 1, 1]; - Config.Inventory[2] = [1, 1, 1, 1, 1, 1, 1, 1, 1, 1]; - Config.Inventory[3] = [1, 1, 1, 1, 1, 1, 1, 1, 1, 1]; - - Config.SkipId.push(sdk.monsters.FireTower); - - /* FastMod configuration. */ - Config.FCR = 0; - Config.FHR = 0; - Config.FBR = 0; - Config.IAS = 0; - - SetUp.autoBuild(); - } + mercEnabled: true, + _buildTemplate: "", + + init: function () { + // ensure finalBuild is properly formatted + let checkBuildTemplate = () => { + let build = (["Bumper", "Socketmule", "Imbuemule"].includes(SetUp.finalBuild) + ? ["Javazon", "Cold", "Bone", "Hammerdin", "Whirlwind", "Wind", "Trapsin"][me.classid] + : SetUp.finalBuild) + "Build"; + return ("libs/SoloPlay/BuildFiles/" + MYCLASSNAME + "/" + MYCLASSNAME + "." + build + ".js").toLowerCase(); + }; + SetUp._buildTemplate = checkBuildTemplate(); + + if (!FileTools.exists(SetUp._buildTemplate)) { + let errors = []; + /** @type {string[]} */ + let possibleBuilds = dopen("libs/SoloPlay/BuildFiles/" + MYCLASSNAME + "/") + .getFiles() + .filter(file => file.includes("Build")) + .map(file => file.substring(file.indexOf(".") + 1, file.indexOf("Build"))); + + // try to see if we can correct the finalBuild + for (let build of possibleBuilds) { + let match = me.data.finalBuild.match(build, "gi"); + + if (match) { + console.log(match); + let old = me.data.finalBuild; + me.data.finalBuild = match[0].trim().capitalize(true); + errors.push( + "~Info tag :: " + old + " was incorrect, I have attempted to remedy this." + + " If it is still giving you an error please re-read the documentation. \n" + + "New InfoTag/finalBuild :: " + SetUp.finalBuild + ); + + break; + } + } + + if (errors.length) { + D2Bot.printToConsole("Kolbot-SoloPlay Final Build Error :: \n" + errors.join("\n"), sdk.colors.D2Bot.Red); + SetUp._buildTemplate = checkBuildTemplate(); // check again + if (!FileTools.exists(SetUp._buildTemplate)) { + console.error( + "ÿc8Kolbot-SoloPlayÿc0: Failed to find finalBuild template." + + " Please check that you have actually entered it in correctly," + + " and that you have the build in to BuildFiles folder." + + " Here is what you currently have: " + SetUp.finalBuild); + throw new Error("finalBuild(): Failed to find template: " + SetUp._buildTemplate); + } + D2Bot.setProfile(null, null, null, null, null, SetUp.finalBuild); + CharData.updateData("me", "finalBuild", SetUp.finalBuild); + } + } + + if (!me.data.initialized) { + me.data.startTime = me.gamestarttime; + me.data.level = me.charlvl; + me.data.classid = me.classid; + me.data.charName = me.name; + me.data.strength = me.rawStrength; + me.data.dexterity = me.rawDexterity; + + if (me.expansion) { + me.data.charms = Check.finalBuild().finalCharms; + } + + me.data.initialized = true; + CharData.updateData("me", me.data); + } + + let temp = copyObj(me.data); + + if (me.data.currentBuild !== CharInfo.getActiveBuild()) { + me.data.currentBuild = CharInfo.getActiveBuild(); + } + + let currDiffStr = sdk.difficulty.nameOf(me.diff).toLowerCase(); + + if (sdk.difficulty.Difficulties.indexOf(me.data.highestDifficulty) < sdk.difficulty.Difficulties.indexOf(sdk.difficulty.nameOf(me.diff))) { + me.data.highestDifficulty = sdk.difficulty.nameOf(me.diff); + } + + if (me.smith && me.data[currDiffStr].imbueUsed === false) { + me.data[currDiffStr].imbueUsed = true; + } + + if (me.respec && me.data[currDiffStr].respecUsed === false) { + me.data[currDiffStr].respecUsed = true; + } + + me.data.level !== me.charlvl && (me.data.level = me.charlvl); + me.data.strength !== me.rawStrength && (me.data.strength = me.rawStrength); + me.data.dexterity !== me.rawDexterity && (me.data.dexterity = me.rawDexterity); + + // expansion check + let [cUpdate, mUpdate] = [false, false]; + + if (me.expansion) { + if (!me.data.merc.gear) { + me.data.merc.gear = []; + mUpdate = true; + } + + // merc check + if (me.getMercEx()) { + // TODO: figure out how to ensure we are already using the right merc to prevent re-hiring + // can't do an aura check as merc auras are bugged, only useful info from getUnit is the classid + let merc = me.getMercEx(); + let mercItems = merc.getItemsEx(); + let preLength = me.data.merc.gear.length; + let check = me.data.merc.gear.filter(i => mercItems.some(item => item.prefixnum === i)); + + if (check !== preLength) { + mUpdate = true; + me.data.merc.gear = check; + } + + let mercInfo = Mercenary.getMercInfo(merc); + mercInfo.classid !== me.data.merc.classid && (me.data.merc.classid = mercInfo.classid); + mercInfo.act !== me.data.merc.act && (me.data.merc.act = mercInfo.act); + mercInfo.difficulty !== me.data.merc.difficulty && (me.data.merc.difficulty = mercInfo.difficulty); + + if (merc.classid !== sdk.mercs.Guard) { + try { + if (mercInfo.skillName !== me.data.merc.skillName) { + me.data.merc.skillName = mercInfo.skillName; + me.data.merc.skill = MercData.findByName(me.data.merc.skillName, me.data.merc.act).skill; + } + } catch (e) { + // + } + } + + // if (merc.classid === sdk.mercs.Guard && !Mercenary.checkMercSkill(me.data.merc.type)) { + // // go back, need to make sure this works properly. + // // only "go back" if we are past the difficulty we need to be in to hire merc. Ex. In hell but want holy freeze merc + // // only if we have enough gold on hand to hire said merc + // // return to our orignal difficulty afterwards + // } + } + + // charm check + if (!me.data.charms || !Object.keys(me.data.charms).length) { + me.data.charms = Check.finalBuild().finalCharms; + cUpdate = true; + } + + if (!me.data.charmGids || me.data.charmGids.length > 0) { + me.data.charmGids = []; + cUpdate = true; + } + + const finalCharmKeys = Object.keys(me.data.charms); + // gids change from game to game so reset our list + for (let i = 0; i < finalCharmKeys.length; i++) { + let cKey = finalCharmKeys[i]; + if (me.data.charms[cKey].have.length) { + me.data.charms[cKey].have = []; + cUpdate = true; + } + } + + if (!!me.shenk && me.data[currDiffStr].socketUsed === false) { + me.data[currDiffStr].socketUsed = true; + } + + if (mUpdate) { + CharData.updateData("merc", me.data); + } + } + + let changed = Misc.recursiveSearch(me.data, temp); + + if (Object.keys(changed).length > 0 || cUpdate) { + CharData.updateData("me", me.data); + } + }, + + // Should this be moved elsewhere? Currently have to include Globals then call this to include rest of overrides + // which in doing so would include globals anyway but does this always need to be included first? + // really need a centralized way to make sure all files use/have the custom functions and all threads stay updated without having to + // scriptBroadcast all the time + include: function () { + let files = dopen("libs/SoloPlay/Functions/").getFiles(); + if (!files.length) throw new Error("Failed to find my files"); + if (!files.includes("Globals.js")) { + console.warn("Incorrect Files?", files); + // something went wrong? + while (!files.includes("Globals.js")) { + files = dopen("libs/SoloPlay/Functions/").getFiles(); + delay(50); + } + } + Array.isArray(files) && files + .filter(file => file.endsWith(".js")) + .sort(a => a.startsWith("PrototypeOverrides.js") ? 0 : 1) // Dirty fix to load new prototypes first + .forEach(function (x) { + if (!isIncluded("SoloPlay/Functions/" + x)) { + if (!include("SoloPlay/Functions/" + x)) { + throw new Error("Failed to include " + "SoloPlay/Functions/" + x); + } + } + }); + }, + + // Storage Settings + sortSettings: { + ItemsSortedFromLeft: [], // default: everything not in Config.ItemsSortedFromRight + ItemsSortedFromRight: [ + // (NOTE: default pickit is fastest if the left side is open) + sdk.items.SmallCharm, sdk.items.LargeCharm, sdk.items.GrandCharm, // sort charms from the right + sdk.items.TomeofIdentify, sdk.items.TomeofTownPortal, sdk.items.Key, // sort tomes and keys to the right + // sort all inventory potions from the right + sdk.items.RejuvenationPotion, sdk.items.FullRejuvenationPotion, + sdk.items.MinorHealingPotion, sdk.items.LightHealingPotion, sdk.items.HealingPotion, sdk.items.GreaterHealingPotion, sdk.items.SuperHealingPotion, + sdk.items.MinorManaPotion, sdk.items.LightManaPotion, sdk.items.ManaPotion, sdk.items.GreaterManaPotion, sdk.items.SuperManaPotion + ], + PrioritySorting: true, + ItemsSortedFromLeftPriority: [/*605, 604, 603, 519, 518*/], // (NOTE: the earlier in the index, the further to the Left) + ItemsSortedFromRightPriority: [ + // (NOTE: the earlier in the index, the further to the Right) + // sort charms from the right, GC > LC > SC + sdk.items.GrandCharm, sdk.items.LargeCharm, sdk.items.SmallCharm, + sdk.items.TomeofIdentify, sdk.items.TomeofTownPortal, sdk.items.Key + ], + }, + + currentBuild: this.currentBuild, + finalBuild: this.finalBuild, + + // setter for Developer option to stop a profile once it reaches a certain level + stopAtLevel: (function () { + if (!Developer.stopAtLevel.enabled) return false; + let level = Developer.stopAtLevel.profiles.find(prof => String.isEqual(prof[0], me.profile)) || false; + return level ? level[1] : false; + })(), + + // pulls respec requirments from final build file + finalRespec: function () { + let respec = Check.finalBuild().respec() ? me.charlvl : 100; + + if (respec === me.charlvl && me.charlvl < 60) { + showConsole(); + console.log("ÿc8Kolbot-SoloPlayÿc0: Bot has respecTwo items but is too low a level to respec."); + console.log("ÿc8Kolbot-SoloPlayÿc0: This only happens with user intervention. Remove the items you gave the bot until at least level 60"); + respec = 100; + } + + return respec; + }, + + autoBuild: function () { + let build = me.currentBuild; + if (!build) throw new Error("Failed to include template: " + SetUp._buildTemplate); + + /* AutoStat configuration. */ + Config.AutoStat.Enabled = true; + Config.AutoStat.Save = 0; + Config.AutoStat.BlockChance = me.paladin ? 75 : 57; + Config.AutoStat.UseBulk = true; + Config.AutoStat.Build = JSON.parse(JSON.stringify(build.stats)); + + /* AutoSkill configuration. */ + Config.AutoSkill.Enabled = true; + Config.AutoSkill.Save = 0; + Config.AutoSkill.Build = JSON.parse(JSON.stringify(build.skills)); + + /* AutoBuild configuration. */ + Config.AutoBuild.Enabled = true; + Config.AutoBuild.Verbose = false; + Config.AutoBuild.DebugMode = false; + Config.AutoBuild.Template = SetUp.currentBuild; + + return true; + }, + + makeNext: function () { + includeIfNotIncluded("SoloPlay/Tools/Tracker.js"); + let gameObj, printTotalTime = Developer.logPerformance; + printTotalTime && (gameObj = Tracker.readObj(Tracker.GTPath)); + + // log info + myPrint(this.finalBuild + " goal reached. On to the next."); + D2Bot.printToConsole("Kolbot-SoloPlay: " + this.finalBuild + " goal reached" + (printTotalTime ? " (" + (Time.format(gameObj.Total + Time.elapsed(gameObj.LastSave))) + "). " : ". ") + "Making next...", sdk.colors.D2Bot.Gold); + + D2Bot.setProfile(null, null, require("../Tools/NameGen")()); + CharData.delete(true); + delay(250); + D2Bot.restart(); + }, + + belt: function () { + let beltSlots = Math.max(1, Storage.BeltSize() - 1); + Config.BeltColumn.forEach(function (col, index) { + Config.MinColumn[index] = col.toLowerCase() !== "rv" ? beltSlots : 0; + }); + }, + + buffers: function () { + const isCaster = Check.currentBuild().caster; + const beltModifer = 4 - Storage.BeltSize(); + const mpFactor = isCaster ? 80 : 50; + Config.MPBuffer = Math.floor(mpFactor / Math.sqrt(me.mpmax)) + (beltModifer * 2); + !me.data.merc.gear.includes(sdk.locale.items.Insight) && (Config.MPBuffer += 2); + const hpFactor = isCaster ? 65 : 80; + Config.HPBuffer = Math.floor(hpFactor / Math.sqrt(me.hpmax)) + (beltModifer * 2); + }, + + bowQuiver: function () { + NTIP.resetRuntimeList(); + if (CharData.skillData.bow.onSwitch) { + if ([sdk.items.type.Bow, sdk.items.type.AmazonBow].includes(CharData.skillData.bow.bowType)) { + NTIP.addToRuntime("[type] == bowquiver # # [maxquantity] == 1"); + } else if (CharData.skillData.bow.bowType === sdk.items.type.Crossbow) { + NTIP.addToRuntime("[type] == crossbowquiver # # [maxquantity] == 1"); + } else if (me.charlvl < 10) { + NTIP.addToRuntime("[type] == bowquiver # # [maxquantity] == 1"); + } + } + }, + + imbueItems: function () { + if (SetUp.finalBuild === "Imbuemule") return []; + let temp = []; + for (let imbueItem of Config.imbueables) { + try { + if (imbueItem.condition()) { + temp.push("[name] == " + imbueItem.name + " && [quality] >= normal && [quality] <= superior && [flag] != ethereal # [Sockets] == 0 # [maxquantity] == 1"); + } + } catch (e) { + console.log(e); + } + } + return temp; + }, + + config: function () { + me.equipped.init(); + // just initializes the data + Check.currentBuild(); + Check.finalBuild(); + + Config.socketables = []; + Config.AutoEquip = true; + + if (me.ladder > 0 || Developer.addLadderRW) { + // Runewords.ladderOverride = true; + Config.LadderOveride = true; + } + + // common items + NTIP.buildList([ + "([type] == helm || [type] == circlet) && ([quality] >= magic || [flag] == runeword) && [flag] != ethereal # [itemchargedskill] >= 0 # [tier] == tierscore(item)", + // Belt + "[type] == belt && [quality] >= magic && [flag] != ethereal # [itemchargedskill] >= 0 # [tier] == tierscore(item)", + "me.normal && [type] == belt && [quality] >= lowquality && [flag] != ethereal # [itemchargedskill] >= 0 # [tier] == tierscore(item)", + // Boots + "[type] == boots && [quality] >= normal && [flag] != ethereal # [itemchargedskill] >= 0 # [tier] == tierscore(item)", + // Armor + "[type] == armor && ([quality] >= magic || [flag] == runeword) && [flag] != ethereal # [itemchargedskill] >= 0 # [tier] == tierscore(item)", + // Gloves + "[type] == gloves && [quality] >= normal && [flag] != ethereal # [itemchargedskill] >= 0 # [tier] == tierscore(item)", + // Amulet + "[type] == amulet && [quality] >= magic # [itemchargedskill] >= 0 # [tier] == tierscore(item)", + // Rings + "[type] == ring && [quality] >= magic # [itemchargedskill] >= 0 # [tier] == tierscore(item)", + // non runeword white items + "([type] == armor) && [quality] >= normal && [flag] != ethereal # [itemchargedskill] >= 0 && [sockets] == 1 # [tier] == tierscore(item)", + "([type] == helm || [type] == circlet) && [quality] >= normal && [flag] != ethereal # [itemchargedskill] >= 0 && ([sockets] == 1 || [sockets] == 3) # [tier] == tierscore(item)", + ]); + + if (me.expansion) { + if (Storage.Stash === undefined) { + Storage.Init(); + } + // sometimes it seems hard to find skillers, if we have the room lets try to cube some + if (Storage.Stash.UsedSpacePercent() < 60 && CharmEquip.grandCharm().keep.length < CharData.charms.get("grand").count().max) { + Config.Recipes.push([Recipe.Reroll.Magic, "Grand Charm"]); + } + // switch bow - only for zon/sorc/pal/necro classes right now + if (!me.barbarian && !me.assassin && !me.druid) { + NTIP.addLine("([type] == bow || [type] == crossbow) && [quality] >= normal # [itemchargedskill] >= 0 # [secondarytier] == tierscore(item)"); + } + const expansionExtras = [ + // Special Charms + "[name] == smallcharm && [quality] == unique # [itemallskills] == 1 # [charmtier] == 100000", + "[name] == largecharm && [quality] == unique # [itemaddclassskills] == 3 # [charmtier] == 100000", + "[name] == grandcharm && [quality] == unique # [itemmagicbonus] >= 30 || [itemgoldbonus] >= 150 # [charmtier] == 100000", + // Merc + "([type] == circlet || [type] == helm) && ([quality] >= magic || [flag] == runeword) # [itemchargedskill] >= 0 # [Merctier] == mercscore(item)", + "[type] == armor && ([quality] >= magic || [flag] == runeword) # [itemchargedskill] >= 0 # [Merctier] == mercscore(item)", + // Rogue + "me.mercid === 271 && [type] == bow && ([quality] >= magic || [flag] == runeword) # [itemchargedskill] >= 0 # [Merctier] == mercscore(item)", + // A2 Guard + "me.mercid === 338 && ([type] == polearm || [type] == spear) && ([quality] >= magic || [flag] == runeword) # [itemchargedskill] >= 0 # [Merctier] == mercscore(item)", + ]; + NTIP.buildList(expansionExtras); + this.bowQuiver(); + } + + /* General configuration. */ + Config.MinGameTime = 400; + Config.MaxGameTime = 7200; + Config.MiniShopBot = true; + Config.PacketShopping = true; + Config.TownCheck = true; + Config.LogExperience = false; + Config.PingQuit = [{ Ping: 600, Duration: 10 }]; + Config.Silence = true; + Config.OpenChests.Enabled = true; + Config.LowGold = me.normal ? 25000 : me.nightmare ? 50000 : 100000; + Config.PrimarySlot = 0; + Config.PacketCasting = 1; + Config.WaypointMenu = true; + Config.Cubing = !!me.getItem(sdk.items.quest.Cube); + Config.MakeRunewords = true; + + /* Chicken configuration. */ + Config.LifeChicken = me.hardcore ? 45 : 10; + Config.ManaChicken = 0; + Config.MercChicken = 0; + Config.TownHP = me.hardcore ? 0 : 35; + Config.TownMP = 0; + + /* Potions configuration. */ + Config.UseHP = me.hardcore ? 90 : 80; + Config.UseRejuvHP = me.hardcore ? 65 : 50; + Config.UseMP = me.hardcore ? 75 : 65; + Config.UseMercHP = 75; + + /* Belt configuration. */ + Config.BeltColumn = ["hp", "mp", "mp", "rv"]; + SetUp.belt(); + + /* Gambling configuration. */ + Config.Gamble = true; + Config.GambleGoldStart = 1250000; + Config.GambleGoldStop = 750000; + + /* AutoMule configuration. */ + Config.AutoMule.Trigger = []; + Config.AutoMule.Force = []; + Config.AutoMule.Exclude = [ + "[name] >= Elrune && [name] <= Lemrune", + ]; + + /* Shrine scan configuration. */ + if (Check.currentBuild().caster) { + Config.ScanShrines = [ + sdk.shrines.Refilling, sdk.shrines.Health, sdk.shrines.Mana, sdk.shrines.Gem, sdk.shrines.Monster, sdk.shrines.HealthExchange, + sdk.shrines.ManaExchange, sdk.shrines.Experience, sdk.shrines.Armor, sdk.shrines.ResistFire, sdk.shrines.ResistCold, + sdk.shrines.ResistLightning, sdk.shrines.ResistPoison, sdk.shrines.Skill, sdk.shrines.ManaRecharge, sdk.shrines.Stamina + ]; + } else { + Config.ScanShrines = [ + sdk.shrines.Refilling, sdk.shrines.Health, sdk.shrines.Mana, sdk.shrines.Gem, sdk.shrines.Monster, sdk.shrines.HealthExchange, + sdk.shrines.ManaExchange, sdk.shrines.Experience, sdk.shrines.Combat, sdk.shrines.Skill, sdk.shrines.Armor, sdk.shrines.ResistFire, + sdk.shrines.ResistCold, sdk.shrines.ResistLightning, sdk.shrines.ResistPoison, sdk.shrines.ManaRecharge, sdk.shrines.Stamina + ]; + } + + /* General logging. */ + Config.ItemInfo = false; + Config.LogKeys = false; + Config.LogOrgans = false; + Config.LogMiddleRunes = true; + Config.LogHighRunes = true; + Config.ShowCubingInfo = true; + + /* DClone. */ + Config.StopOnDClone = !!me.expansion; + Config.SoJWaitTime = 5; // Time in minutes to wait for another SoJ sale before leaving game. 0 = disabled + Config.KillDclone = !!me.expansion; + Config.DCloneQuit = false; + + /* Town configuration. */ + Config.HealHP = 99; + Config.HealMP = 99; + Config.HealStatus = true; + Config.UseMerc = me.expansion; + Config.MercWatch = SetUp.mercwatch; + Config.StashGold = me.charlvl * 100; + Config.ClearInvOnStart = false; + + /* Inventory buffers and lock configuration. */ + Config.HPBuffer = 0; + Config.MPBuffer = 0; + Config.RejuvBuffer = 4; + Config.Inventory[0] = [1, 1, 1, 1, 1, 1, 1, 1, 1, 1]; + Config.Inventory[1] = [1, 1, 1, 1, 1, 1, 1, 1, 1, 1]; + Config.Inventory[2] = [1, 1, 1, 1, 1, 1, 1, 1, 1, 1]; + Config.Inventory[3] = [1, 1, 1, 1, 1, 1, 1, 1, 1, 1]; + + Config.SkipId.push(sdk.monsters.FireTower); + + /* FastMod configuration. */ + Config.FCR = 0; + Config.FHR = 0; + Config.FBR = 0; + Config.IAS = 0; + + SetUp.autoBuild(); + } }; Object.defineProperties(SetUp, { - currentBuild: { - get: function () { - return me.data.currentBuild; - }, - }, - finalBuild: { - get: function () { - return me.data.finalBuild; - }, - }, - mercwatch: { - get: function () { - const myGold = me.gold; - const cLvl = me.charlvl; - let lowGold = Math.min(Math.floor(500 + (cLvl * 150 * Math.sqrt(cLvl - 1))), 250000); - return (SetUp.mercEnabled && (myGold > lowGold) && (myGold > me.mercrevivecost)); - } - }, + currentBuild: { + get: function () { + return me.data.currentBuild; + }, + }, + finalBuild: { + get: function () { + return me.data.finalBuild; + }, + }, + mercwatch: { + get: function () { + const myGold = me.gold; + const cLvl = me.charlvl; + let lowGold = Math.min(Math.floor(500 + (cLvl * 150 * Math.sqrt(cLvl - 1))), 250000); + return (SetUp.mercEnabled && (myGold > lowGold) && (myGold > me.mercrevivecost)); + } + }, }); // misc const goToDifficulty = function (diff = undefined, reason = "") { - try { - if (diff === undefined) throw new Error("diff is undefined"); - - let diffString; - switch (typeof diff) { - case "string": - diff = diff.capitalize(true); - if (!sdk.difficulty.Difficulties.includes(diff)) throw new Error("difficulty doesn't exist" + diff); - if (sdk.difficulty.Difficulties.indexOf(diff) === me.diff) throw new Error("already in this difficulty" + diff); - diffString = diff; - - break; - case "number": - if (diff === me.diff || diff < 0) throw new Error("invalid diff" + diff); - diffString = sdk.difficulty.nameOf(diff); - - break; - default: - throw new Error("?"); - } - - CharData.updateData("me", "setDifficulty", diffString); - myPrint("Going to " + diffString + " " + reason, true); - delay(1000); - if (CharData.getStats().setDifficulty !== diffString) { - throw new Error("Failed to set difficulty"); - } - scriptBroadcast("quit"); - - while (me.ingame) { - delay(3); - } - } catch (e) { - console.debug(e.message ? e.message : e); - return false; - } - - return true; + try { + if (diff === undefined) throw new Error("diff is undefined"); + + let diffString; + switch (typeof diff) { + case "string": + diff = diff.capitalize(true); + if (!sdk.difficulty.Difficulties.includes(diff)) throw new Error("difficulty doesn't exist" + diff); + if (sdk.difficulty.Difficulties.indexOf(diff) === me.diff) throw new Error("already in this difficulty" + diff); + diffString = diff; + + break; + case "number": + if (diff === me.diff || diff < 0) throw new Error("invalid diff" + diff); + diffString = sdk.difficulty.nameOf(diff); + + break; + default: + throw new Error("?"); + } + + CharData.updateData("me", "setDifficulty", diffString); + myPrint("Going to " + diffString + " " + reason, true); + delay(1000); + if (CharData.getStats().setDifficulty !== diffString) { + throw new Error("Failed to set difficulty"); + } + scriptBroadcast("quit"); + + while (me.ingame) { + delay(3); + } + } catch (e) { + console.debug(e.message ? e.message : e); + return false; + } + + return true; }; // General Game functions const Check = { - lowGold: false, - - gold: function () { - let gold = me.gold; - let goldLimit = [25000, 50000, 100000][me.diff]; - - if ((me.normal && !me.accessToAct(2)) || gold >= goldLimit) { - return true; - } - - me.overhead("low gold"); - - return false; - }, - - brokeAf: function (announce = true) { - let gold = me.gold; - let lowGold = Math.min(Math.floor(500 + (me.charlvl * 100 * Math.sqrt(me.charlvl - 1))), 250000); - - switch (true) { - case (me.charlvl < 15): - case (me.normal && !me.accessToAct(2)): - case (gold >= lowGold): - case (me.charlvl >= 15 && gold > Math.floor(lowGold / 2) && gold > me.getRepairCost()): - return false; - } - - if (announce) { - myPrint("very low gold. My Gold: " + gold); - NTIP.addLine("[name] == gold # [gold] >= 1"); - } - - return true; - }, - - broken: function () { - let gold = me.gold; - - // Almost broken but not quite - if (((me.equipped.get(sdk.body.RightArm).durability <= 30 && me.equipped.get(sdk.body.RightArm).durability > 0) - || (me.equipped.get(sdk.body.LeftArm).durability <= 30 && me.equipped.get(sdk.body.LeftArm).durability > 0) - && !me.getMerc() && me.charlvl >= 15 && !me.normal && !me.nightmare && gold < 1000)) { - return 1; - } - - // Broken - if ((me.equipped.get(sdk.body.RightArm).durability === 0 || me.equipped.get(sdk.body.LeftArm).durability === 0) && me.charlvl >= 15 && !me.normal && gold < 1000) { - return 2; - } - - return 0; - }, - - brokeCheck: function () { - Town.doChores(); - - let myGold = me.gold; - let repairCost = me.getRepairCost(); - let items = (me.getItemsForRepair(100, false) || []); - let meleeChar = !Check.currentBuild().caster; - let msg = ""; - let diff = -1; - - switch (true) { - case myGold > repairCost: - return false; - case me.normal: - case !meleeChar && me.nightmare: - Check.lowGold = myGold < repairCost; - return false; - case meleeChar && !me.normal: - // check how broke we are - only for melee chars since casters don't care about weapons - let wep = items.filter(i => i.isEquipped && i.bodylocation === sdk.body.RightArm).first(); - if (!!wep && meleeChar && wep.durabilityPercent === 0) { - // we are really broke - go back to normal - msg = " We are broken - lets get some easy gold in normal."; - diff = sdk.difficulty.Normal; - } - - break; - case !meleeChar && me.hell: - msg = " We are pretty broke, lets run some easy stuff in nightmare for gold"; - diff = sdk.difficulty.Nightmare; - - break; - } - - if (diff > -1) { - console.debug("My gold: " + myGold + ", Repair cost: " + repairCost); - goToDifficulty(diff, msg + (" My gold: " + myGold + ", Repair cost: " + repairCost)); - - return true; - } - - return false; - }, - - resistance: function () { - let resPenalty = me.getResPenalty(me.diff + 1); - let [frRes, lrRes, crRes, prRes] = [(me.realFR - resPenalty), (me.realLR - resPenalty), (me.realCR - resPenalty), (me.realPR - resPenalty)]; - - return { - Status: ((frRes > 0) && (lrRes > 0) && (crRes > 0)), - FR: frRes, - CR: crRes, - LR: lrRes, - PR: prRes, - }; - }, - - nextDifficulty: function (announce = true) { - let currDiff = me.diff; - if (currDiff === sdk.difficulty.Hell) return false; - if (["Bumper", "Socketmule"].includes(SetUp.finalBuild)) return false; - if (me.charlvl < CharInfo.levelCap) return false; - if (!me.diffCompleted) return false; - let nextDiff = null; - let res = this.resistance(); - let lvlReq = !!(!this.broken()); - let [str, color] = ["", sdk.colors.D2Bot.Black]; - - if (lvlReq) { - if (res.Status) { - nextDiff = currDiff + 1; - [str, color] = ["next difficulty requirements met. Starting: " + sdk.difficulty.nameOf(nextDiff), sdk.colors.D2Bot.Blue]; - } else { - if (me.charlvl >= CharInfo.levelCap + (!me.normal ? 5 : 2)) { - nextDiff = currDiff + 1; - str = "Over leveled. Starting: " + sdk.difficulty.nameOf(nextDiff); - } else { - announce && myPrint( - sdk.difficulty.nameOf(currDiff + 1) - + " requirements not met. Negative resistance. FR: " + res.FR + " | CR: " + res.CR + " | LR: " + res.LR - ); - } - } - } - - if (!nextDiff) return false; - if (announce && str) { - D2Bot.printToConsole("Kolbot-SoloPlay: " + str, color); - } - - return sdk.difficulty.nameOf(nextDiff); - }, - - runes: function () { - if (me.classic) return false; - let needRunes = true; - - switch (me.diff) { - case sdk.difficulty.Normal: - // Have runes or stealth and ancients pledge - if (me.haveRunes([sdk.items.runes.Tal, sdk.items.runes.Eth]) || me.checkItem({ name: sdk.locale.items.Stealth }).have) { - needRunes = false; - } - - break; - case sdk.difficulty.Nightmare: - if ((me.haveRunes([sdk.items.runes.Tal, sdk.items.runes.Thul, sdk.items.runes.Ort, sdk.items.runes.Amn]) && Check.currentBuild().caster) - || (!me.paladin && me.checkItem({ name: sdk.locale.items.Spirit, itemtype: sdk.items.type.Sword }).have) - || (me.paladin && me.haveAll([{ name: sdk.locale.items.Spirit, itemtype: sdk.items.type.Sword }, { name: sdk.locale.items.Spirit, itemtype: sdk.items.type.AuricShields }])) - || (me.necromancer && me.checkItem({ name: sdk.locale.items.White }).have - && (me.checkItem({ name: sdk.locale.items.Rhyme, itemtype: sdk.items.type.VoodooHeads }).have || me.equipped.get(sdk.body.LeftArm).tier > 800)) - || (me.barbarian && (me.checkItem({ name: sdk.locale.items.Lawbringer }).have || me.baal))) { - needRunes = false; - } - - break; - case sdk.difficulty.Hell: - if (!me.baal || (me.sorceress && !["Blova", "Lightning"].includes(SetUp.currentBuild))) { - needRunes = false; - } - - break; - } - - return needRunes; - }, - - // todo: need to finish up adding locale string ids to sdk so I can remove this in favor of better me.checkItem prototype - haveItem: function (type, flag, iName) { - let [isClassID, itemCHECK, typeCHECK] = [false, false, false]; - - flag && typeof flag === "string" && (flag = flag.capitalize(true)); - typeof iName === "string" && (iName = iName.toLowerCase()); - - let items = me.getItemsEx() - .filter(function (item) { - return !item.questItem && (flag === "Runeword" ? item.isRuneword : item.quality === sdk.items.quality[flag]); - }); - - switch (typeof type) { - case "string": - typeof type === "string" && (type = type.toLowerCase()); - if (type !== "dontcare" && !NTIPAliasType[type] && !NTIPAliasClassID[type]) return false; - if (type === "dontcare") { - typeCHECK = true; // we don't care about type - break; - } - - // check if item is a classid but with hacky fix for items like belt which is a type and classid...sigh - isClassID = !!NTIPAliasClassID[type] && !NTIPAliasType[type]; - type = isClassID ? NTIPAliasClassID[type] : NTIPAliasType[type]; - - break; - case "number": - if (!Object.values(sdk.items.type).includes(type) && !Object.values(sdk.items).includes(type)) return false; - // check if item is a classid but with hacky fix for items like belt which is a type and classid...sigh - isClassID = Object.values(sdk.items).includes(type) && !Object.values(sdk.items.type).includes(type); - - break; - } - - // filter out non-matching item types/classids - if (typeof type === "number") { - items = items.filter(function (item) { - return (isClassID ? item.classid === type : item.itemType === type); - }); - } - - for (let i = 0; i < items.length; i++) { - switch (flag) { - case "Set": - case "Unique": - case "Crafted": - itemCHECK = !!(items[i].quality === sdk.items.quality[flag]) && (iName ? items[i].fname.toLowerCase().includes(iName) : true); - break; - case "Runeword": - itemCHECK = !!(items[i].isRuneword) && (iName ? items[i].fname.toLowerCase().includes(iName) : true); - break; - } - - // don't waste time if first condition wasn't met - if (itemCHECK && typeof type === "number") { - typeCHECK = isClassID ? items[i].classid === type : items[i].itemType === type; - } - - if (itemCHECK && typeCHECK) { - return true; - } - } - - return false; - }, - - itemSockables: function (type, quality, iName) { - quality && typeof quality === "string" && (quality = sdk.items.quality[quality.capitalize(true)]); - typeof iName === "string" && (iName = iName.toLowerCase()); - let [isClassID, itemCHECK, typeCHECK] = [false, false, false]; - - switch (typeof type) { - case "string": - typeof type === "string" && (type = type.toLowerCase()); - if (!NTIPAliasType[type] && !NTIPAliasClassID[type]) return false; - isClassID = !!NTIPAliasClassID[type]; - type = isClassID ? NTIPAliasClassID[type] : NTIPAliasType[type]; - - break; - case "number": - if (!Object.values(sdk.items.type).includes(type) && !Object.values(sdk.items).includes(type)) return false; - isClassID = Object.values(sdk.items).includes(type); - - break; - } - - let socketableCHECK = isClassID ? Config.socketables.find(({ classid }) => type === classid) : false; - let items = me.getItemsEx() - .filter(function (item) { - return item.quality === quality && !item.questItem && !item.isRuneword && (isClassID ? item.classid === type : item.itemType === type) && getBaseStat("items", item.classid, "gemsockets") > 0; - }); - - for (let i = 0; i < items.length; i++) { - itemCHECK = !!(items[i].quality === quality) && (iName ? items[i].fname.toLowerCase().includes(iName) : true); - - // don't waste time if first condition wasn't met - itemCHECK && (typeCHECK = isClassID ? items[i].classid === type : items[i].itemType === type); - - if (itemCHECK && typeCHECK) { - if (!socketableCHECK && items[i].getItemsEx().length === 0) { - return true; - } else if (socketableCHECK) { - SoloWants.addToList(items[i]); - - return true; - } - } - } - - return false; - }, - - haveBase: function (type = undefined, sockets = undefined) { - if (!type || !sockets) return false; - let isClassID = false; - - switch (typeof type) { - case "string": - typeof type === "string" && (type = type.toLowerCase()); - if (!NTIPAliasType[type] && !NTIPAliasClassID[type]) return false; - isClassID = !!NTIPAliasClassID[type]; - type = isClassID ? NTIPAliasClassID[type] : NTIPAliasType[type]; - - break; - case "number": - if (!Object.values(sdk.items.type).includes(type) && !Object.values(sdk.items).includes(type)) return false; - isClassID = Object.values(sdk.items).includes(type); - - break; - } - - let items = me.getItemsEx() - .filter(item => item.isBaseType && item.isInStorage && (isClassID ? item.classid === type : item.itemType === type)); - - for (let i = 0; i < items.length; i++) { - if (items[i].sockets === sockets && (isClassID ? items[i].classid === type : items[i].itemType === type)) { - return true; - } - } - - return false; - }, - - getMaxValue: function (buildInfo, stat) { - if (!buildInfo || !buildInfo.stats || stat === undefined) return 0; - let highest = 0; - const shorthandStr = [sdk.stats.Strength, "s", "str", "strength"]; - const shorthandDex = [sdk.stats.Dexterity, "d", "dex", "dexterity"]; - const statToCheck = shorthandStr.includes(stat) ? "str" : shorthandDex.includes(stat) ? "dex" : ""; - - buildInfo.stats.forEach(s => { - switch (true) { - case (shorthandStr.includes(s[0]) && statToCheck === "str"): - case (shorthandDex.includes(s[0]) && statToCheck === "dex"): - if (typeof s[1] === "number" && s[1] > highest) { - highest = s[1]; - } - - break; - default: - break; - } - }); - - return highest; - }, - - // repetitive code - FIX THIS - currentBuild: function () { - let build = me.currentBuild; - - if (!build) throw new Error("currentBuild(): Failed to include template: " + SetUp._buildTemplate); - - return { - caster: build.caster, - tabSkills: build.skillstab, - wantedSkills: build.wantedskills, - usefulSkills: build.usefulskills, - precastSkills: build.hasOwnProperty("precastSkills") ? build.precastSkills : [], - usefulStats: build.hasOwnProperty("usefulStats") ? build.usefulStats : [], - wantedMerc: build.hasOwnProperty("wantedMerc") ? build.wantedMerc : null, - finalCharms: build.hasOwnProperty("charms") ? (build.charms || {}) : {}, - maxStr: Check.getMaxValue(build, "strength"), - maxDex: Check.getMaxValue(build, "dexterity"), - respec: build.hasOwnProperty("respec") ? build.respec : () => {}, - active: build.active, - }; - }, - - // repetitive code - FIX THIS - finalBuild: function () { - let finalBuild = me.finalBuild; - - if (!finalBuild) throw new Error("finalBuild(): Failed to include template: " + SetUp._buildTemplate); - - return { - caster: finalBuild.caster, - tabSkills: finalBuild.skillstab, - wantedSkills: finalBuild.wantedskills, - usefulSkills: finalBuild.usefulskills, - precastSkills: finalBuild.precastSkills, - usefulStats: (!!finalBuild.usefulStats ? finalBuild.usefulStats : []), - wantedMerc: finalBuild.wantedMerc, - finalCharms: (finalBuild.charms || {}), - maxStr: Check.getMaxValue(finalBuild, "strength"), - maxDex: Check.getMaxValue(finalBuild, "dexterity"), - respec: finalBuild.respec, - active: finalBuild.active, - }; - }, - - checkSpecialCase: function () { - const questCompleted = (id) => !!Misc.checkQuest(id, sdk.quest.states.ReqComplete); - let goalReached = false, goal = ""; - - switch (true) { - case SetUp.finalBuild === "Bumper" && me.charlvl >= 40: - case (SetUp.finalBuild === "Socketmule" && questCompleted(sdk.quest.id.SiegeOnHarrogath)): - case (SetUp.finalBuild === "Imbuemule" && questCompleted(sdk.quest.id.ToolsoftheTrade) && me.charlvl >= Developer.imbueStopLevel): - goal = SetUp.finalBuild; - goalReached = true; - - break; - case SetUp.stopAtLevel && me.charlvl >= SetUp.stopAtLevel: - goal = "Level: " + SetUp.stopAtLevel; - goalReached = true; - - break; - case sdk.difficulty.Difficulties.indexOf(sdk.difficulty.nameOf(me.diff)) < sdk.difficulty.Difficulties.indexOf(me.data.highestDifficulty): - // TODO: fill this out, if we go back to normal from hell I want to be able to do whatever it was imbue/socket/respec then return to our orignal difficulty - // as it is right now if we go back it would take 2 games to get back to hell - // but this needs a check to ensure that one of the above reasons are why we went back in case we had gone back because low gold in which case we need to stay in the game - break; - default: - break; - } - - if (goalReached) { - const gameObj = Developer.logPerformance ? Tracker.readObj(Tracker.GTPath) : null; - - switch (true) { - case (SetUp.finalBuild === "Bumper" && Developer.fillAccount.bumpers): - case (SetUp.finalBuild === "Socketmule" && Developer.fillAccount.socketMules): - SetUp.makeNext(); - - break; - default: - D2Bot.printToConsole("Kolbot-SoloPlay " + goal + " goal reached." + (gameObj ? " (" + (Time.format(gameObj.Total + Time.elapsed(gameObj.LastSave))) + ")" : ""), sdk.colors.D2Bot.Gold); - Developer.logPerformance && Tracker.update(); - D2Bot.stop(); - } - } - }, - - // TODO: enable this for other items, i.e maybe don't socket tal helm in hell but instead go back and use nightmare so then we can use hell socket on tal armor? - usePreviousSocketQuest: function () { - if (me.classic) return; - if (!Check.resistance().Status) { - if (me.weaponswitch === 0 && me.equipped.get(sdk.body.LeftArm).fname.includes("Lidless Wall") && !me.equipped.get(sdk.body.LeftArm).socketed) { - if (!me.normal) { - if (!me.data.normal.socketUsed) goToDifficulty(sdk.difficulty.Normal, " to use socket quest"); - if (me.hell && !me.data.nightmare.socketUsed) goToDifficulty(sdk.difficulty.Nightmare, " to use socket quest"); - } - } - } - }, + lowGold: false, + + gold: function () { + let gold = me.gold; + let goldLimit = [25000, 50000, 100000][me.diff]; + + if ((me.normal && !me.accessToAct(2)) || gold >= goldLimit) { + return true; + } + + me.overhead("low gold"); + + return false; + }, + + brokeAf: function (announce = true) { + let gold = me.gold; + let lowGold = Math.min(Math.floor(500 + (me.charlvl * 100 * Math.sqrt(me.charlvl - 1))), 250000); + + switch (true) { + case (me.charlvl < 15): + case (me.normal && !me.accessToAct(2)): + case (gold >= lowGold): + case (me.charlvl >= 15 && gold > Math.floor(lowGold / 2) && gold > me.getRepairCost()): + return false; + } + + if (announce) { + myPrint("very low gold. My Gold: " + gold); + NTIP.addLine("[name] == gold # [gold] >= 1"); + } + + return true; + }, + + broken: function () { + let gold = me.gold; + + // Almost broken but not quite + if (((me.equipped.get(sdk.body.RightArm).durability <= 30 && me.equipped.get(sdk.body.RightArm).durability > 0) + || (me.equipped.get(sdk.body.LeftArm).durability <= 30 && me.equipped.get(sdk.body.LeftArm).durability > 0) + && !me.getMerc() && me.charlvl >= 15 && !me.normal && !me.nightmare && gold < 1000)) { + return 1; + } + + // Broken + if ((me.equipped.get(sdk.body.RightArm).durability === 0 || me.equipped.get(sdk.body.LeftArm).durability === 0) && me.charlvl >= 15 && !me.normal && gold < 1000) { + return 2; + } + + return 0; + }, + + brokeCheck: function () { + Town.doChores(); + + let myGold = me.gold; + let repairCost = me.getRepairCost(); + let items = (me.getItemsForRepair(100, false) || []); + let meleeChar = !Check.currentBuild().caster; + let msg = ""; + let diff = -1; + + switch (true) { + case myGold > repairCost: + return false; + case me.normal: + case !meleeChar && me.nightmare: + Check.lowGold = myGold < repairCost; + return false; + case meleeChar && !me.normal: + // check how broke we are - only for melee chars since casters don't care about weapons + let wep = items.filter(i => i.isEquipped && i.bodylocation === sdk.body.RightArm).first(); + if (!!wep && meleeChar && wep.durabilityPercent === 0) { + // we are really broke - go back to normal + msg = " We are broken - lets get some easy gold in normal."; + diff = sdk.difficulty.Normal; + } + + break; + case !meleeChar && me.hell: + msg = " We are pretty broke, lets run some easy stuff in nightmare for gold"; + diff = sdk.difficulty.Nightmare; + + break; + } + + if (diff > -1) { + console.debug("My gold: " + myGold + ", Repair cost: " + repairCost); + goToDifficulty(diff, msg + (" My gold: " + myGold + ", Repair cost: " + repairCost)); + + return true; + } + + return false; + }, + + resistance: function () { + let resPenalty = me.getResPenalty(me.diff + 1); + let [frRes, lrRes, crRes, prRes] = [(me.realFR - resPenalty), (me.realLR - resPenalty), (me.realCR - resPenalty), (me.realPR - resPenalty)]; + + return { + Status: ((frRes > 0) && (lrRes > 0) && (crRes > 0)), + FR: frRes, + CR: crRes, + LR: lrRes, + PR: prRes, + }; + }, + + nextDifficulty: function (announce = true) { + let currDiff = me.diff; + if (currDiff === sdk.difficulty.Hell) return false; + if (["Bumper", "Socketmule"].includes(SetUp.finalBuild)) return false; + if (me.charlvl < CharInfo.levelCap) return false; + if (!me.diffCompleted) return false; + let nextDiff = null; + let res = this.resistance(); + let lvlReq = !!(!this.broken()); + let [str, color] = ["", sdk.colors.D2Bot.Black]; + + if (lvlReq) { + if (res.Status) { + nextDiff = currDiff + 1; + [str, color] = ["next difficulty requirements met. Starting: " + sdk.difficulty.nameOf(nextDiff), sdk.colors.D2Bot.Blue]; + } else { + if (me.charlvl >= CharInfo.levelCap + (!me.normal ? 5 : 2)) { + nextDiff = currDiff + 1; + str = "Over leveled. Starting: " + sdk.difficulty.nameOf(nextDiff); + } else { + announce && myPrint( + sdk.difficulty.nameOf(currDiff + 1) + + " requirements not met. Negative resistance. FR: " + res.FR + " | CR: " + res.CR + " | LR: " + res.LR + ); + } + } + } + + if (!nextDiff) return false; + if (announce && str) { + D2Bot.printToConsole("Kolbot-SoloPlay: " + str, color); + } + + return sdk.difficulty.nameOf(nextDiff); + }, + + runes: function () { + if (me.classic) return false; + let needRunes = true; + + switch (me.diff) { + case sdk.difficulty.Normal: + // Have runes or stealth and ancients pledge + if (me.haveRunes([sdk.items.runes.Tal, sdk.items.runes.Eth]) || me.checkItem({ name: sdk.locale.items.Stealth }).have) { + needRunes = false; + } + + break; + case sdk.difficulty.Nightmare: + if ((me.haveRunes([sdk.items.runes.Tal, sdk.items.runes.Thul, sdk.items.runes.Ort, sdk.items.runes.Amn]) && Check.currentBuild().caster) + || (!me.paladin && me.checkItem({ name: sdk.locale.items.Spirit, itemtype: sdk.items.type.Sword }).have) + || (me.paladin && me.haveAll([{ name: sdk.locale.items.Spirit, itemtype: sdk.items.type.Sword }, { name: sdk.locale.items.Spirit, itemtype: sdk.items.type.AuricShields }])) + || (me.necromancer && me.checkItem({ name: sdk.locale.items.White }).have + && (me.checkItem({ name: sdk.locale.items.Rhyme, itemtype: sdk.items.type.VoodooHeads }).have || me.equipped.get(sdk.body.LeftArm).tier > 800)) + || (me.barbarian && (me.checkItem({ name: sdk.locale.items.Lawbringer }).have || me.baal))) { + needRunes = false; + } + + break; + case sdk.difficulty.Hell: + if (!me.baal || (me.sorceress && !["Blova", "Lightning"].includes(SetUp.currentBuild))) { + needRunes = false; + } + + break; + } + + return needRunes; + }, + + // todo: need to finish up adding locale string ids to sdk so I can remove this in favor of better me.checkItem prototype + haveItem: function (type, flag, iName) { + let [isClassID, itemCHECK, typeCHECK] = [false, false, false]; + + flag && typeof flag === "string" && (flag = flag.capitalize(true)); + typeof iName === "string" && (iName = iName.toLowerCase()); + + let items = me.getItemsEx() + .filter(function (item) { + return !item.questItem && (flag === "Runeword" ? item.isRuneword : item.quality === sdk.items.quality[flag]); + }); + + switch (typeof type) { + case "string": + typeof type === "string" && (type = type.toLowerCase()); + if (type !== "dontcare" && !NTIPAliasType[type] && !NTIPAliasClassID[type]) return false; + if (type === "dontcare") { + typeCHECK = true; // we don't care about type + break; + } + + // check if item is a classid but with hacky fix for items like belt which is a type and classid...sigh + isClassID = !!NTIPAliasClassID[type] && !NTIPAliasType[type]; + type = isClassID ? NTIPAliasClassID[type] : NTIPAliasType[type]; + + break; + case "number": + if (!Object.values(sdk.items.type).includes(type) && !Object.values(sdk.items).includes(type)) return false; + // check if item is a classid but with hacky fix for items like belt which is a type and classid...sigh + isClassID = Object.values(sdk.items).includes(type) && !Object.values(sdk.items.type).includes(type); + + break; + } + + // filter out non-matching item types/classids + if (typeof type === "number") { + items = items.filter(function (item) { + return (isClassID ? item.classid === type : item.itemType === type); + }); + } + + for (let i = 0; i < items.length; i++) { + switch (flag) { + case "Set": + case "Unique": + case "Crafted": + itemCHECK = !!(items[i].quality === sdk.items.quality[flag]) && (iName ? items[i].fname.toLowerCase().includes(iName) : true); + break; + case "Runeword": + itemCHECK = !!(items[i].isRuneword) && (iName ? items[i].fname.toLowerCase().includes(iName) : true); + break; + } + + // don't waste time if first condition wasn't met + if (itemCHECK && typeof type === "number") { + typeCHECK = isClassID ? items[i].classid === type : items[i].itemType === type; + } + + if (itemCHECK && typeCHECK) { + return true; + } + } + + return false; + }, + + itemSockables: function (type, quality, iName) { + quality && typeof quality === "string" && (quality = sdk.items.quality[quality.capitalize(true)]); + typeof iName === "string" && (iName = iName.toLowerCase()); + let [isClassID, itemCHECK, typeCHECK] = [false, false, false]; + + switch (typeof type) { + case "string": + typeof type === "string" && (type = type.toLowerCase()); + if (!NTIPAliasType[type] && !NTIPAliasClassID[type]) return false; + isClassID = !!NTIPAliasClassID[type]; + type = isClassID ? NTIPAliasClassID[type] : NTIPAliasType[type]; + + break; + case "number": + if (!Object.values(sdk.items.type).includes(type) && !Object.values(sdk.items).includes(type)) return false; + isClassID = Object.values(sdk.items).includes(type); + + break; + } + + let socketableCHECK = isClassID ? Config.socketables.find(({ classid }) => type === classid) : false; + let items = me.getItemsEx() + .filter(function (item) { + return item.quality === quality && !item.questItem && !item.isRuneword && (isClassID ? item.classid === type : item.itemType === type) && getBaseStat("items", item.classid, "gemsockets") > 0; + }); + + for (let i = 0; i < items.length; i++) { + itemCHECK = !!(items[i].quality === quality) && (iName ? items[i].fname.toLowerCase().includes(iName) : true); + + // don't waste time if first condition wasn't met + itemCHECK && (typeCHECK = isClassID ? items[i].classid === type : items[i].itemType === type); + + if (itemCHECK && typeCHECK) { + if (!socketableCHECK && items[i].getItemsEx().length === 0) { + return true; + } else if (socketableCHECK) { + SoloWants.addToList(items[i]); + + return true; + } + } + } + + return false; + }, + + haveBase: function (type = undefined, sockets = undefined) { + if (!type || !sockets) return false; + let isClassID = false; + + switch (typeof type) { + case "string": + typeof type === "string" && (type = type.toLowerCase()); + if (!NTIPAliasType[type] && !NTIPAliasClassID[type]) return false; + isClassID = !!NTIPAliasClassID[type]; + type = isClassID ? NTIPAliasClassID[type] : NTIPAliasType[type]; + + break; + case "number": + if (!Object.values(sdk.items.type).includes(type) && !Object.values(sdk.items).includes(type)) return false; + isClassID = Object.values(sdk.items).includes(type); + + break; + } + + let items = me.getItemsEx() + .filter(item => item.isBaseType && item.isInStorage && (isClassID ? item.classid === type : item.itemType === type)); + + for (let i = 0; i < items.length; i++) { + if (items[i].sockets === sockets && (isClassID ? items[i].classid === type : items[i].itemType === type)) { + return true; + } + } + + return false; + }, + + getMaxValue: function (buildInfo, stat) { + if (!buildInfo || !buildInfo.stats || stat === undefined) return 0; + let highest = 0; + const shorthandStr = [sdk.stats.Strength, "s", "str", "strength"]; + const shorthandDex = [sdk.stats.Dexterity, "d", "dex", "dexterity"]; + const statToCheck = shorthandStr.includes(stat) ? "str" : shorthandDex.includes(stat) ? "dex" : ""; + + buildInfo.stats.forEach(s => { + switch (true) { + case (shorthandStr.includes(s[0]) && statToCheck === "str"): + case (shorthandDex.includes(s[0]) && statToCheck === "dex"): + if (typeof s[1] === "number" && s[1] > highest) { + highest = s[1]; + } + + break; + default: + break; + } + }); + + return highest; + }, + + // repetitive code - FIX THIS + currentBuild: function () { + let build = me.currentBuild; + + if (!build) throw new Error("currentBuild(): Failed to include template: " + SetUp._buildTemplate); + + return { + caster: build.caster, + tabSkills: build.skillstab, + wantedSkills: build.wantedskills, + usefulSkills: build.usefulskills, + precastSkills: build.hasOwnProperty("precastSkills") ? build.precastSkills : [], + usefulStats: build.hasOwnProperty("usefulStats") ? build.usefulStats : [], + wantedMerc: build.hasOwnProperty("wantedMerc") ? build.wantedMerc : null, + finalCharms: build.hasOwnProperty("charms") ? (build.charms || {}) : {}, + maxStr: Check.getMaxValue(build, "strength"), + maxDex: Check.getMaxValue(build, "dexterity"), + respec: build.hasOwnProperty("respec") ? build.respec : () => {}, + active: build.active, + }; + }, + + // repetitive code - FIX THIS + finalBuild: function () { + let finalBuild = me.finalBuild; + + if (!finalBuild) throw new Error("finalBuild(): Failed to include template: " + SetUp._buildTemplate); + + return { + caster: finalBuild.caster, + tabSkills: finalBuild.skillstab, + wantedSkills: finalBuild.wantedskills, + usefulSkills: finalBuild.usefulskills, + precastSkills: finalBuild.precastSkills, + usefulStats: (!!finalBuild.usefulStats ? finalBuild.usefulStats : []), + wantedMerc: finalBuild.wantedMerc, + finalCharms: (finalBuild.charms || {}), + maxStr: Check.getMaxValue(finalBuild, "strength"), + maxDex: Check.getMaxValue(finalBuild, "dexterity"), + respec: finalBuild.respec, + active: finalBuild.active, + }; + }, + + checkSpecialCase: function () { + const questCompleted = (id) => !!Misc.checkQuest(id, sdk.quest.states.ReqComplete); + let goalReached = false, goal = ""; + + switch (true) { + case SetUp.finalBuild === "Bumper" && me.charlvl >= 40: + case (SetUp.finalBuild === "Socketmule" && questCompleted(sdk.quest.id.SiegeOnHarrogath)): + case (SetUp.finalBuild === "Imbuemule" && questCompleted(sdk.quest.id.ToolsoftheTrade) && me.charlvl >= Developer.imbueStopLevel): + goal = SetUp.finalBuild; + goalReached = true; + + break; + case SetUp.stopAtLevel && me.charlvl >= SetUp.stopAtLevel: + goal = "Level: " + SetUp.stopAtLevel; + goalReached = true; + + break; + case sdk.difficulty.Difficulties.indexOf(sdk.difficulty.nameOf(me.diff)) < sdk.difficulty.Difficulties.indexOf(me.data.highestDifficulty): + // TODO: fill this out, if we go back to normal from hell I want to be able to do whatever it was imbue/socket/respec then return to our orignal difficulty + // as it is right now if we go back it would take 2 games to get back to hell + // but this needs a check to ensure that one of the above reasons are why we went back in case we had gone back because low gold in which case we need to stay in the game + break; + default: + break; + } + + if (goalReached) { + const gameObj = Developer.logPerformance ? Tracker.readObj(Tracker.GTPath) : null; + + switch (true) { + case (SetUp.finalBuild === "Bumper" && Developer.fillAccount.bumpers): + case (SetUp.finalBuild === "Socketmule" && Developer.fillAccount.socketMules): + SetUp.makeNext(); + + break; + default: + D2Bot.printToConsole("Kolbot-SoloPlay " + goal + " goal reached." + (gameObj ? " (" + (Time.format(gameObj.Total + Time.elapsed(gameObj.LastSave))) + ")" : ""), sdk.colors.D2Bot.Gold); + Developer.logPerformance && Tracker.update(); + D2Bot.stop(); + } + } + }, + + // TODO: enable this for other items, i.e maybe don't socket tal helm in hell but instead go back and use nightmare so then we can use hell socket on tal armor? + usePreviousSocketQuest: function () { + if (me.classic) return; + if (!Check.resistance().Status) { + if (me.weaponswitch === 0 && me.equipped.get(sdk.body.LeftArm).fname.includes("Lidless Wall") && !me.equipped.get(sdk.body.LeftArm).socketed) { + if (!me.normal) { + if (!me.data.normal.socketUsed) goToDifficulty(sdk.difficulty.Normal, " to use socket quest"); + if (me.hell && !me.data.nightmare.socketUsed) goToDifficulty(sdk.difficulty.Nightmare, " to use socket quest"); + } + } + } + }, }; diff --git a/libs/SoloPlay/Functions/ItemOverrides.js b/libs/SoloPlay/Functions/ItemOverrides.js index 8fd86b7f..2a2120c7 100644 --- a/libs/SoloPlay/Functions/ItemOverrides.js +++ b/libs/SoloPlay/Functions/ItemOverrides.js @@ -10,16 +10,16 @@ includeIfNotIncluded("core/Item.js"); includeIfNotIncluded("SoloPlay/Functions/ItemPrototypes.js"); Item.weaponTypes = [ - sdk.items.type.Scepter, sdk.items.type.Wand, sdk.items.type.Staff, sdk.items.type.Bow, sdk.items.type.Axe, sdk.items.type.Club, - sdk.items.type.Sword, sdk.items.type.Hammer, sdk.items.type.Knife, sdk.items.type.Spear, sdk.items.type.Polearm, sdk.items.type.Crossbow, - sdk.items.type.Mace, sdk.items.type.ThrowingKnife, sdk.items.type.ThrowingAxe, sdk.items.type.Javelin, sdk.items.type.Orb, sdk.items.type.AmazonBow, - sdk.items.type.AmazonSpear, sdk.items.type.AmazonJavelin, sdk.items.type.MissilePotion + sdk.items.type.Scepter, sdk.items.type.Wand, sdk.items.type.Staff, sdk.items.type.Bow, sdk.items.type.Axe, sdk.items.type.Club, + sdk.items.type.Sword, sdk.items.type.Hammer, sdk.items.type.Knife, sdk.items.type.Spear, sdk.items.type.Polearm, sdk.items.type.Crossbow, + sdk.items.type.Mace, sdk.items.type.ThrowingKnife, sdk.items.type.ThrowingAxe, sdk.items.type.Javelin, sdk.items.type.Orb, sdk.items.type.AmazonBow, + sdk.items.type.AmazonSpear, sdk.items.type.AmazonJavelin, sdk.items.type.MissilePotion ]; Item.shieldTypes = [ - sdk.items.type.Shield, sdk.items.type.AuricShields, sdk.items.type.VoodooHeads, sdk.items.type.BowQuiver, sdk.items.type.CrossbowQuiver + sdk.items.type.Shield, sdk.items.type.AuricShields, sdk.items.type.VoodooHeads, sdk.items.type.BowQuiver, sdk.items.type.CrossbowQuiver ]; Item.helmTypes = [ - sdk.items.type.Helm, sdk.items.type.PrimalHelm, sdk.items.type.Circlet, sdk.items.type.Pelt + sdk.items.type.Helm, sdk.items.type.PrimalHelm, sdk.items.type.Circlet, sdk.items.type.Pelt ]; /** @@ -27,89 +27,89 @@ Item.helmTypes = [ * @param {boolean} [skipSameItem] */ Item.getQuantityOwned = function (item, skipSameItem = false) { - if (!item) return 0; - - return me.getItemsEx() - .filter(check => - check.itemType === item.itemType - && (!skipSameItem || check.gid !== item.gid) - && check.classid === item.classid - && check.quality === item.quality - && check.sockets === item.sockets - && check.isInStorage - ).length; + if (!item) return 0; + + return me.getItemsEx() + .filter(check => + check.itemType === item.itemType + && (!skipSameItem || check.gid !== item.gid) + && check.classid === item.classid + && check.quality === item.quality + && check.sockets === item.sockets + && check.isInStorage + ).length; }; /** * @param {ItemUnit} item */ Item.hasDependancy = function (item) { - switch (item.itemType) { - case sdk.items.type.Bow: - case sdk.items.type.AmazonBow: - return sdk.items.Arrows; - case sdk.items.type.Crossbow: - return sdk.items.Bolts; - default: - return false; - } + switch (item.itemType) { + case sdk.items.type.Bow: + case sdk.items.type.AmazonBow: + return sdk.items.Arrows; + case sdk.items.type.Crossbow: + return sdk.items.Bolts; + default: + return false; + } }; /** * @param {ItemUnit} item */ Item.identify = function (item) { - if (item.identified) return true; - let idTool = me.getIdTool(); - - if (idTool) { - item.isInStash && Town.openStash(); - return Town.identifyItem(item, idTool); - } - return false; + if (item.identified) return true; + let idTool = me.getIdTool(); + + if (idTool) { + item.isInStash && Town.openStash(); + return Town.identifyItem(item, idTool); + } + return false; }; /** * @param {ItemUnit} item */ Item.getBodyLoc = function (item) { - if (!item || item.isInsertable) return []; - if (Item.shieldTypes.includes(item.itemType)) return [sdk.body.LeftArm]; - if (Item.helmTypes.includes(item.itemType)) return [sdk.body.Head]; - if (Item.weaponTypes.includes(item.itemType)) { - return me.barbarian && item.twoHanded && !item.strictlyTwoHanded - ? [sdk.body.RightArm, sdk.body.LeftArm] - : [sdk.body.RightArm]; - } - - switch (item.itemType) { - case sdk.items.type.Armor: - return [sdk.body.Armor]; - case sdk.items.type.Ring: - return [sdk.body.RingRight, sdk.body.RingLeft]; - case sdk.items.type.Amulet: - return [sdk.body.Neck]; - case sdk.items.type.Boots: - return [sdk.body.Feet]; - case sdk.items.type.Gloves: - return [sdk.body.Gloves]; - case sdk.items.type.Belt: - return [sdk.body.Belt]; - case sdk.items.type.AssassinClaw: - case sdk.items.type.HandtoHand: - return !Check.currentBuild().caster && me.assassin - ? [sdk.body.RightArm, sdk.body.LeftArm] : [sdk.body.RightArm]; - } - - return []; + if (!item || item.isInsertable) return []; + if (Item.shieldTypes.includes(item.itemType)) return [sdk.body.LeftArm]; + if (Item.helmTypes.includes(item.itemType)) return [sdk.body.Head]; + if (Item.weaponTypes.includes(item.itemType)) { + return me.barbarian && item.twoHanded && !item.strictlyTwoHanded + ? [sdk.body.RightArm, sdk.body.LeftArm] + : [sdk.body.RightArm]; + } + + switch (item.itemType) { + case sdk.items.type.Armor: + return [sdk.body.Armor]; + case sdk.items.type.Ring: + return [sdk.body.RingRight, sdk.body.RingLeft]; + case sdk.items.type.Amulet: + return [sdk.body.Neck]; + case sdk.items.type.Boots: + return [sdk.body.Feet]; + case sdk.items.type.Gloves: + return [sdk.body.Gloves]; + case sdk.items.type.Belt: + return [sdk.body.Belt]; + case sdk.items.type.AssassinClaw: + case sdk.items.type.HandtoHand: + return !Check.currentBuild().caster && me.assassin + ? [sdk.body.RightArm, sdk.body.LeftArm] : [sdk.body.RightArm]; + } + + return []; }; /** * @param {ItemUnit} item */ Item.canEquip = function (item) { - if (!item || item.type !== sdk.unittype.Item || !item.identified) return false; - return me.charlvl >= item.getStat(sdk.stats.LevelReq) && me.trueStr >= item.strreq && me.trueDex >= item.dexreq; + if (!item || item.type !== sdk.unittype.Item || !item.identified) return false; + return me.charlvl >= item.getStat(sdk.stats.LevelReq) && me.trueStr >= item.strreq && me.trueDex >= item.dexreq; }; /** @@ -117,215 +117,215 @@ Item.canEquip = function (item) { * @param {boolean} basicCheck */ Item.autoEquipCheck = function (item, basicCheck = false) { - if (!Config.AutoEquip) return true; - - let tier = NTIP.GetTier(item); - if (tier <= 0) return false; - let bodyLoc = this.getBodyLoc(item); - - for (let loc of bodyLoc) { - const equippedItem = me.equipped.get(loc); - - // rings are special - if (item.isInStorage && item.itemType === sdk.items.type.Ring) { - // have to pass in the specific location - tier = tierscore(item, 1, loc); - - if (tier > equippedItem.tierScore) { - return true; - } - } else if (tier > equippedItem.tier && (basicCheck ? true : this.canEquip(item) || !item.identified)) { - if (Item.canEquip(item)) { - if (item.twoHanded && !me.barbarian) { - if (tier < me.equipped.get(sdk.body.RightArm).tier + me.equipped.get(sdk.body.LeftArm).tier) return false; - } - - if (!me.barbarian && loc === sdk.body.LeftArm && me.equipped.get(loc).tier === -1) { - if (me.equipped.get(sdk.body.RightArm).twoHanded && tier < me.equipped.get(sdk.body.RightArm).tier) return false; - } - - return true; - } else { - /** - * @param {ItemUnit} item - * @returns {boolean} - */ - const checkForBetterItem = (item) => { - let betterItem = me.getItemsEx() - .filter(el => el.isInStorage && el.gid !== item.gid && el.identified && Item.getBodyLoc(el).includes(loc)) - .sort((a, b) => NTIP.GetTier(b) - NTIP.GetTier(a)) - .find(el => NTIP.GetTier(el) > tier); - return !!betterItem; - }; - // keep wanted final gear items - if (NTIP.CheckItem(item, NTIP.FinalGear.list) === Pickit.Result.WANTED) { - // don't horde items we can't equip - return !checkForBetterItem(item); - } - - let [lvlReq, strReq, dexReq] = [item.getStat(sdk.stats.LevelReq), item.strreq, item.dexreq]; - - // todo - bit hacky, better way would be to track what stats are going to be allocated next - if ((lvlReq - me.charlvl > 5) || (strReq - me.trueStr > 10) || (dexReq - me.trueDex > 10)) { - return false; - } - - // if we can't equip it, but it's a good item, keep it as long as we have space for it - // lets double check that this is the highest tied'd item of this type in our storage - let betterItem = checkForBetterItem(item); - if (!betterItem) return true; - - return Storage.Stash.CanFit(item) && Storage.Stash.UsedSpacePercent() < 65; - } - } - } - - return false; + if (!Config.AutoEquip) return true; + + let tier = NTIP.GetTier(item); + if (tier <= 0) return false; + let bodyLoc = this.getBodyLoc(item); + + for (let loc of bodyLoc) { + const equippedItem = me.equipped.get(loc); + + // rings are special + if (item.isInStorage && item.itemType === sdk.items.type.Ring) { + // have to pass in the specific location + tier = tierscore(item, 1, loc); + + if (tier > equippedItem.tierScore) { + return true; + } + } else if (tier > equippedItem.tier && (basicCheck ? true : this.canEquip(item) || !item.identified)) { + if (Item.canEquip(item)) { + if (item.twoHanded && !me.barbarian) { + if (tier < me.equipped.get(sdk.body.RightArm).tier + me.equipped.get(sdk.body.LeftArm).tier) return false; + } + + if (!me.barbarian && loc === sdk.body.LeftArm && me.equipped.get(loc).tier === -1) { + if (me.equipped.get(sdk.body.RightArm).twoHanded && tier < me.equipped.get(sdk.body.RightArm).tier) return false; + } + + return true; + } else { + /** + * @param {ItemUnit} item + * @returns {boolean} + */ + const checkForBetterItem = (item) => { + let betterItem = me.getItemsEx() + .filter(el => el.isInStorage && el.gid !== item.gid && el.identified && Item.getBodyLoc(el).includes(loc)) + .sort((a, b) => NTIP.GetTier(b) - NTIP.GetTier(a)) + .find(el => NTIP.GetTier(el) > tier); + return !!betterItem; + }; + // keep wanted final gear items + if (NTIP.CheckItem(item, NTIP.FinalGear.list) === Pickit.Result.WANTED) { + // don't horde items we can't equip + return !checkForBetterItem(item); + } + + let [lvlReq, strReq, dexReq] = [item.getStat(sdk.stats.LevelReq), item.strreq, item.dexreq]; + + // todo - bit hacky, better way would be to track what stats are going to be allocated next + if ((lvlReq - me.charlvl > 5) || (strReq - me.trueStr > 10) || (dexReq - me.trueDex > 10)) { + return false; + } + + // if we can't equip it, but it's a good item, keep it as long as we have space for it + // lets double check that this is the highest tied'd item of this type in our storage + let betterItem = checkForBetterItem(item); + if (!betterItem) return true; + + return Storage.Stash.CanFit(item) && Storage.Stash.UsedSpacePercent() < 65; + } + } + } + + return false; }; /** * @param {string} task */ Item.autoEquip = function (task = "") { - if (!Config.AutoEquip) return true; - task = task + "AutoEquip"; - console.log("ÿc8Kolbot-SoloPlayÿc0: Entering " + task); - - const noStash = (!me.inTown || task !== "AutoEquip"); - let tick = getTickCount(); - let items = me.getItemsEx() - .filter(function (item) { - if (!item.isInStorage) return false; - if (noStash && !item.isInInventory) return false; - let tier = NTIP.GetTier(item); - return (item.identified ? tier > 0 : tier !== 0); - }); - // couldn't find my items - if (!items.length) return false; - - me.switchWeapons(sdk.player.slot.Main); - - const sortEq = (a, b) => { - let [prioA, prioB] = [Item.canEquip(a), Item.canEquip(b)]; - if (prioA && prioB) return NTIP.GetTier(b) - NTIP.GetTier(a); - if (prioA) return -1; - if (prioB) return 1; - return 0; - }; - - /** - * @param {ItemUnit} item - * @param {number} bodyLoc - * @param {number} tier - */ - const runEquip = (item, bodyLoc, tier) => { - let gid = item.gid; - let prettyName = item.prettyPrint; - console.debug(prettyName + " tier: " + tier); - - if (this.equip(item, bodyLoc)) { - console.log( - "ÿc9" + task + "ÿc0 :: \n" - + "ÿc9 - Equippedÿc0: " + prettyName + "\n" - + "ÿc9 - Tierÿc0: " + tier - ); - // item that can have sockets - if (item.getItemType()) { - SoloWants.addToList(item); - SoloWants.ensureList(); - } - Developer.debugging.autoEquip && Item.logItem(task, me.getItem(-1, -1, gid)); - Developer.logEquipped && MuleLogger.logEquippedItems(); - me.equipped.set(bodyLoc, item); - } else if (!noStash && item.lvlreq > me.charlvl && !item.isInStash) { - if (Storage.Stash.CanFit(item)) { - console.log( - "ÿc9" + task + "ÿc0 :: \n" - + "- " + prettyName + " Item req is too high (" + item.lvlreq + ") for my level (" + me.charlvl + ") \n" - + "- Stash for now as its better than what I currently have. Tier: " + tier - ); - Storage.Stash.MoveTo(item); - } - } else if (me.getItem(-1, -1, gid)) { - // Make sure we didn't lose it during roll back - return false; - } - - return true; - }; - - // stash'd unid check - let unids = items.filter(item => !item.identified && item.isInStash); - if (unids.length && NPCAction.fillTome(sdk.items.TomeofIdentify, true)) { - unids.forEach(item => Item.identify(item)); - } - - // ring check - sometimes a higher tier ring ends up on the wrong finger causing a rollback loop - if (me.equipped.get(sdk.body.RingLeft).tierScore > me.equipped.get(sdk.body.RingRight).tierScore) { - console.log("ÿc9" + task + "ÿc0 :: Swapping rings, higher tier ring is on the wrong finger"); - clickItemAndWait(sdk.clicktypes.click.item.Left, sdk.body.RingLeft); - delay(200); - me.itemoncursor && clickItemAndWait(sdk.clicktypes.click.item.Left, sdk.body.RingRight); - delay(200); - me.itemoncursor && clickItemAndWait(sdk.clicktypes.click.item.Left, sdk.body.RingLeft); - me.equipped.init(); - } - - !getUIFlag(sdk.uiflags.Shop) && me.cancel(); - - while (items.length > 0) { - items.sort(sortEq); - const item = items.shift(); - if (!item.isInStorage) continue; - let tier = NTIP.GetTier(item); - if (tier <= 0) continue; - let bodyLoc = this.getBodyLoc(item); - - for (let loc of bodyLoc) { - const equippedItem = me.equipped.get(loc); - if (equippedItem.classid === sdk.items.quest.KhalimsWill) continue; - // rings are special - if (item.itemType === sdk.items.type.Ring) { - Item.identify(item); - // have to pass in the specific location - tier = tierscore(item, 1, loc); - - if (tier > equippedItem.tierScore) { - if (!runEquip(item, loc, tier)) { - continue; - } - } - } else { - if (tier > equippedItem.tier) { - console.debug("EquippedItem :: " + equippedItem.prettyPrint + " |ÿc0 Tier: " + equippedItem.tier); - Item.identify(item); - - if (item.twoHanded && !me.barbarian) { - if (tier < me.equipped.get(sdk.body.RightArm).tier + me.equipped.get(sdk.body.LeftArm).tier) { - continue; - } - console.log("ÿc9" + task + "ÿc0 :: TwoHandedWep better than sum tier of currently equipped main + shield hand : " + item.fname + " Tier: " + tier); - } - - if (!me.barbarian && loc === sdk.body.LeftArm && equippedItem.tier === -1 && me.equipped.get(sdk.body.RightArm).twoHanded) { - if (tier < me.equipped.get(sdk.body.RightArm).tier) { - continue; - } - console.log("ÿc9" + task + "ÿc0 :: TwoHandedWep not as good as what we want to equip on our shield hand : " + item.fname + " Tier: " + tier); - } - - if (!runEquip(item, loc, tier)) { - continue; - } - - break; - } - } - } - } - - console.log("ÿc8Kolbot-SoloPlayÿc0: Exiting ÿc9" + task + "ÿc0. Time elapsed: " + Time.format(getTickCount() - tick)); - return true; + if (!Config.AutoEquip) return true; + task = task + "AutoEquip"; + console.log("ÿc8Kolbot-SoloPlayÿc0: Entering " + task); + + const noStash = (!me.inTown || task !== "AutoEquip"); + let tick = getTickCount(); + let items = me.getItemsEx() + .filter(function (item) { + if (!item.isInStorage) return false; + if (noStash && !item.isInInventory) return false; + let tier = NTIP.GetTier(item); + return (item.identified ? tier > 0 : tier !== 0); + }); + // couldn't find my items + if (!items.length) return false; + + me.switchWeapons(sdk.player.slot.Main); + + const sortEq = (a, b) => { + let [prioA, prioB] = [Item.canEquip(a), Item.canEquip(b)]; + if (prioA && prioB) return NTIP.GetTier(b) - NTIP.GetTier(a); + if (prioA) return -1; + if (prioB) return 1; + return 0; + }; + + /** + * @param {ItemUnit} item + * @param {number} bodyLoc + * @param {number} tier + */ + const runEquip = (item, bodyLoc, tier) => { + let gid = item.gid; + let prettyName = item.prettyPrint; + console.debug(prettyName + " tier: " + tier); + + if (this.equip(item, bodyLoc)) { + console.log( + "ÿc9" + task + "ÿc0 :: \n" + + "ÿc9 - Equippedÿc0: " + prettyName + "\n" + + "ÿc9 - Tierÿc0: " + tier + ); + // item that can have sockets + if (item.getItemType()) { + SoloWants.addToList(item); + SoloWants.ensureList(); + } + Developer.debugging.autoEquip && Item.logItem(task, me.getItem(-1, -1, gid)); + Developer.logEquipped && MuleLogger.logEquippedItems(); + me.equipped.set(bodyLoc, item); + } else if (!noStash && item.lvlreq > me.charlvl && !item.isInStash) { + if (Storage.Stash.CanFit(item)) { + console.log( + "ÿc9" + task + "ÿc0 :: \n" + + "- " + prettyName + " Item req is too high (" + item.lvlreq + ") for my level (" + me.charlvl + ") \n" + + "- Stash for now as its better than what I currently have. Tier: " + tier + ); + Storage.Stash.MoveTo(item); + } + } else if (me.getItem(-1, -1, gid)) { + // Make sure we didn't lose it during roll back + return false; + } + + return true; + }; + + // stash'd unid check + let unids = items.filter(item => !item.identified && item.isInStash); + if (unids.length && NPCAction.fillTome(sdk.items.TomeofIdentify, true)) { + unids.forEach(item => Item.identify(item)); + } + + // ring check - sometimes a higher tier ring ends up on the wrong finger causing a rollback loop + if (me.equipped.get(sdk.body.RingLeft).tierScore > me.equipped.get(sdk.body.RingRight).tierScore) { + console.log("ÿc9" + task + "ÿc0 :: Swapping rings, higher tier ring is on the wrong finger"); + clickItemAndWait(sdk.clicktypes.click.item.Left, sdk.body.RingLeft); + delay(200); + me.itemoncursor && clickItemAndWait(sdk.clicktypes.click.item.Left, sdk.body.RingRight); + delay(200); + me.itemoncursor && clickItemAndWait(sdk.clicktypes.click.item.Left, sdk.body.RingLeft); + me.equipped.init(); + } + + !getUIFlag(sdk.uiflags.Shop) && me.cancel(); + + while (items.length > 0) { + items.sort(sortEq); + const item = items.shift(); + if (!item.isInStorage) continue; + let tier = NTIP.GetTier(item); + if (tier <= 0) continue; + let bodyLoc = this.getBodyLoc(item); + + for (let loc of bodyLoc) { + const equippedItem = me.equipped.get(loc); + if (equippedItem.classid === sdk.items.quest.KhalimsWill) continue; + // rings are special + if (item.itemType === sdk.items.type.Ring) { + Item.identify(item); + // have to pass in the specific location + tier = tierscore(item, 1, loc); + + if (tier > equippedItem.tierScore) { + if (!runEquip(item, loc, tier)) { + continue; + } + } + } else { + if (tier > equippedItem.tier) { + console.debug("EquippedItem :: " + equippedItem.prettyPrint + " |ÿc0 Tier: " + equippedItem.tier); + Item.identify(item); + + if (item.twoHanded && !me.barbarian) { + if (tier < me.equipped.get(sdk.body.RightArm).tier + me.equipped.get(sdk.body.LeftArm).tier) { + continue; + } + console.log("ÿc9" + task + "ÿc0 :: TwoHandedWep better than sum tier of currently equipped main + shield hand : " + item.fname + " Tier: " + tier); + } + + if (!me.barbarian && loc === sdk.body.LeftArm && equippedItem.tier === -1 && me.equipped.get(sdk.body.RightArm).twoHanded) { + if (tier < me.equipped.get(sdk.body.RightArm).tier) { + continue; + } + console.log("ÿc9" + task + "ÿc0 :: TwoHandedWep not as good as what we want to equip on our shield hand : " + item.fname + " Tier: " + tier); + } + + if (!runEquip(item, loc, tier)) { + continue; + } + + break; + } + } + } + } + + console.log("ÿc8Kolbot-SoloPlayÿc0: Exiting ÿc9" + task + "ÿc0. Time elapsed: " + Time.format(getTickCount() - tick)); + return true; }; /** @@ -333,118 +333,118 @@ Item.autoEquip = function (task = "") { * @param {number} bodyLoc */ Item.equip = function (item, bodyLoc) { - // can't equip - @todo handle if it's one of our final items and we can equip it given the stats of our other items - if (!this.canEquip(item)) return false; - - // Already equipped in the right slot - if (item.mode === sdk.items.mode.Equipped && item.bodylocation === bodyLoc) return true; - // failed to open stash - if (item.isInStash && !Town.openStash()) return false; - // failed to open cube - if (item.isInCube && !Cubing.openCube()) return false; - - let currentEquipped = me.getItemsEx() - .filter(item => item.isEquipped && item.bodylocation === bodyLoc) - .first(); - if (currentEquipped) { - console.debug( - "ÿc9Equipÿc0 ::\n" - + "ÿc9 - Equippingÿc0: " + item.prettyPrint + "\n" - + "ÿc9 - Tierÿc0: " + NTIP.GetTier(item) + "\n" - + "ÿc9 - Currently Equippedÿc0: " + currentEquipped.prettyPrint + "\n" - + "ÿc9 - Tierÿc0: " + NTIP.GetTier(currentEquipped) + "\n" - + "ÿc9 - BodyLocÿc0: " + bodyLoc - ); - } - let rolledBack = false; - // todo: sometimes rings get bugged with the higher tier ring ending up on the wrong finger, if this happens swap them - - for (let i = 0; i < 3; i += 1) { - if (item.toCursor()) { - clickItemAndWait(sdk.clicktypes.click.item.Left, bodyLoc); - - if (item.bodylocation === bodyLoc) { - if (getCursorType() === 3) { - let cursorItem = Game.getCursorUnit(); - - if (cursorItem) { - // rollback check - let justEquipped = me.equipped.get(bodyLoc); - let checkScore = 0; - switch (cursorItem.itemType) { - case sdk.items.type.Ring: - checkScore = tierscore(cursorItem, 1, bodyLoc); - if (checkScore > justEquipped.tierScore) { - console.debug("ROLLING BACK TO OLD ITEM BECAUSE IT WAS BETTER"); - console.debug("OldItem: " + checkScore + " Just Equipped Item: " + me.equipped.get(bodyLoc).tierScore); - clickItemAndWait(sdk.clicktypes.click.item.Left, bodyLoc); - cursorItem = Game.getCursorUnit(); - rolledBack = true; - } - - break; - default: - checkScore = NTIP.GetTier(cursorItem); - if (checkScore > justEquipped.tier && !item.questItem && !justEquipped.isRuneword/*Wierd bug with runewords that it'll fail to get correct item desc so don't attempt rollback*/) { - console.debug("ROLLING BACK TO OLD ITEM BECAUSE IT WAS BETTER"); - console.debug("OldItem: " + checkScore + " Just Equipped Item: " + me.equipped.get(bodyLoc).tier); - clickItemAndWait(sdk.clicktypes.click.item.Left, bodyLoc); - cursorItem = Game.getCursorUnit(); - rolledBack = true; - } - - break; - } - - if (cursorItem && !cursorItem.shouldKeep()) { - console.debug("ÿc9Equipÿc0 :: Dropping " + cursorItem.prettyPrint); - cursorItem.drop(); - } - } - } - - return rolledBack ? false : true; - } - } - } - - return false; + // can't equip - @todo handle if it's one of our final items and we can equip it given the stats of our other items + if (!this.canEquip(item)) return false; + + // Already equipped in the right slot + if (item.mode === sdk.items.mode.Equipped && item.bodylocation === bodyLoc) return true; + // failed to open stash + if (item.isInStash && !Town.openStash()) return false; + // failed to open cube + if (item.isInCube && !Cubing.openCube()) return false; + + let currentEquipped = me.getItemsEx() + .filter(item => item.isEquipped && item.bodylocation === bodyLoc) + .first(); + if (currentEquipped) { + console.debug( + "ÿc9Equipÿc0 ::\n" + + "ÿc9 - Equippingÿc0: " + item.prettyPrint + "\n" + + "ÿc9 - Tierÿc0: " + NTIP.GetTier(item) + "\n" + + "ÿc9 - Currently Equippedÿc0: " + currentEquipped.prettyPrint + "\n" + + "ÿc9 - Tierÿc0: " + NTIP.GetTier(currentEquipped) + "\n" + + "ÿc9 - BodyLocÿc0: " + bodyLoc + ); + } + let rolledBack = false; + // todo: sometimes rings get bugged with the higher tier ring ending up on the wrong finger, if this happens swap them + + for (let i = 0; i < 3; i += 1) { + if (item.toCursor()) { + clickItemAndWait(sdk.clicktypes.click.item.Left, bodyLoc); + + if (item.bodylocation === bodyLoc) { + if (getCursorType() === 3) { + let cursorItem = Game.getCursorUnit(); + + if (cursorItem) { + // rollback check + let justEquipped = me.equipped.get(bodyLoc); + let checkScore = 0; + switch (cursorItem.itemType) { + case sdk.items.type.Ring: + checkScore = tierscore(cursorItem, 1, bodyLoc); + if (checkScore > justEquipped.tierScore) { + console.debug("ROLLING BACK TO OLD ITEM BECAUSE IT WAS BETTER"); + console.debug("OldItem: " + checkScore + " Just Equipped Item: " + me.equipped.get(bodyLoc).tierScore); + clickItemAndWait(sdk.clicktypes.click.item.Left, bodyLoc); + cursorItem = Game.getCursorUnit(); + rolledBack = true; + } + + break; + default: + checkScore = NTIP.GetTier(cursorItem); + if (checkScore > justEquipped.tier && !item.questItem && !justEquipped.isRuneword/*Wierd bug with runewords that it'll fail to get correct item desc so don't attempt rollback*/) { + console.debug("ROLLING BACK TO OLD ITEM BECAUSE IT WAS BETTER"); + console.debug("OldItem: " + checkScore + " Just Equipped Item: " + me.equipped.get(bodyLoc).tier); + clickItemAndWait(sdk.clicktypes.click.item.Left, bodyLoc); + cursorItem = Game.getCursorUnit(); + rolledBack = true; + } + + break; + } + + if (cursorItem && !cursorItem.shouldKeep()) { + console.debug("ÿc9Equipÿc0 :: Dropping " + cursorItem.prettyPrint); + cursorItem.drop(); + } + } + } + + return rolledBack ? false : true; + } + } + } + + return false; }; Item.removeItem = function (bodyLoc = -1, item = undefined) { - let removable = item && typeof item === "object" - ? item - : me.getEquippedItem(bodyLoc); - !me.inTown && Town.goToTown(); - !getUIFlag(sdk.uiflags.Stash) && Town.openStash(); - - if (removable) { - removable.isOnSwap && me.weaponswitch !== 1 && me.switchWeapons(1); - removable.toCursor(); - let cursorItem = Game.getCursorUnit(); - - if (cursorItem) { - // only keep wanted items - if ([Pickit.Result.WANTED, Pickit.Result.SOLOWANTS].includes(Pickit.checkItem(cursorItem).result) || AutoEquip.wanted(cursorItem)) { - if (Storage.Inventory.CanFit(cursorItem)) { - Storage.Inventory.MoveTo(cursorItem); - } else if (Storage.Stash.CanFit(cursorItem)) { - Storage.Stash.MoveTo(cursorItem); - } else if (Storage.Cube.CanFit(cursorItem)) { - Storage.Cube.MoveTo(cursorItem); - } - } else { - D2Bot.printToConsole("Dropped " + cursorItem.prettyPrint + " during un-equip process", sdk.colors.D2Bot.Red); - cursorItem.drop(); - } - } - - me.weaponswitch === 1 && me.switchWeapons(0); - - return true; - } - - return false; + let removable = item && typeof item === "object" + ? item + : me.getEquippedItem(bodyLoc); + !me.inTown && Town.goToTown(); + !getUIFlag(sdk.uiflags.Stash) && Town.openStash(); + + if (removable) { + removable.isOnSwap && me.weaponswitch !== 1 && me.switchWeapons(1); + removable.toCursor(); + let cursorItem = Game.getCursorUnit(); + + if (cursorItem) { + // only keep wanted items + if ([Pickit.Result.WANTED, Pickit.Result.SOLOWANTS].includes(Pickit.checkItem(cursorItem).result) || AutoEquip.wanted(cursorItem)) { + if (Storage.Inventory.CanFit(cursorItem)) { + Storage.Inventory.MoveTo(cursorItem); + } else if (Storage.Stash.CanFit(cursorItem)) { + Storage.Stash.MoveTo(cursorItem); + } else if (Storage.Cube.CanFit(cursorItem)) { + Storage.Cube.MoveTo(cursorItem); + } + } else { + D2Bot.printToConsole("Dropped " + cursorItem.prettyPrint + " during un-equip process", sdk.colors.D2Bot.Red); + cursorItem.drop(); + } + } + + me.weaponswitch === 1 && me.switchWeapons(0); + + return true; + } + + return false; }; /** @@ -456,18 +456,18 @@ Item.hasSecondaryTier = (item) => Config.AutoEquip && me.expansion && NTIP.GetSe * @param {ItemUnit} item */ Item.getSecondaryBodyLoc = function (item) { - if (Item.shieldTypes.includes(item.itemType)) return [sdk.body.LeftArmSecondary]; - if ([sdk.items.type.HandtoHand, sdk.items.type.AssassinClaw].includes(item.itemType)) { - return !Check.currentBuild().caster && me.assassin - ? [sdk.body.RightArmSecondary, sdk.body.LeftArmSecondary] - : [sdk.body.RightArmSecondary]; - } - if (Item.weaponTypes.includes(item.itemType)) { - return me.barbarian && item.twoHanded && !item.strictlyTwoHanded - ? [sdk.body.RightArmSecondary, sdk.body.LeftArmSecondary] - : [sdk.body.RightArmSecondary]; - } - return []; + if (Item.shieldTypes.includes(item.itemType)) return [sdk.body.LeftArmSecondary]; + if ([sdk.items.type.HandtoHand, sdk.items.type.AssassinClaw].includes(item.itemType)) { + return !Check.currentBuild().caster && me.assassin + ? [sdk.body.RightArmSecondary, sdk.body.LeftArmSecondary] + : [sdk.body.RightArmSecondary]; + } + if (Item.weaponTypes.includes(item.itemType)) { + return me.barbarian && item.twoHanded && !item.strictlyTwoHanded + ? [sdk.body.RightArmSecondary, sdk.body.LeftArmSecondary] + : [sdk.body.RightArmSecondary]; + } + return []; }; /** @@ -475,127 +475,127 @@ Item.getSecondaryBodyLoc = function (item) { * @param {11 | 12} bodyLoc */ Item.secondaryEquip = function (item, bodyLoc) { - if (!this.canEquip(item) && me.expansion) return false; - // Already equipped in the right slot - if (item.mode === sdk.items.mode.Equipped && item.bodylocation === bodyLoc) return true; - if (item.isInStash && !Town.openStash()) return false; - let equipped = false; - - me.switchWeapons(1); // Switch weapons - - try { - for (let i = 0; i < 3; i += 1) { - if (item.toCursor()) { - clickItemAndWait(sdk.clicktypes.click.item.Left, bodyLoc - 7); - - if (item.bodylocation === bodyLoc - 7) { - equipped = true; - [sdk.items.Arrows, sdk.items.Bolts].includes(item.classid) && CharData.skillData.bow.setArrowInfo(item); - [sdk.items.type.Bow, sdk.items.type.AmazonBow, sdk.items.type.Crossbow].includes(item.itemType) && CharData.skillData.bow.setBowInfo(item); - - if (getCursorType() === 3) { - let cursorItem = Game.getCursorUnit(); - !!cursorItem && !cursorItem.shouldKeep() && cursorItem.drop(); - } - - if (Item.hasDependancy(item) && me.needRepair() && me.inTown) { - NPCAction.repair(true); - } - - return true; - } - } - } - } finally { - // Switch back to primary - me.weaponswitch !== 0 && me.switchWeapons(0); - } - - return equipped; + if (!this.canEquip(item) && me.expansion) return false; + // Already equipped in the right slot + if (item.mode === sdk.items.mode.Equipped && item.bodylocation === bodyLoc) return true; + if (item.isInStash && !Town.openStash()) return false; + let equipped = false; + + me.switchWeapons(1); // Switch weapons + + try { + for (let i = 0; i < 3; i += 1) { + if (item.toCursor()) { + clickItemAndWait(sdk.clicktypes.click.item.Left, bodyLoc - 7); + + if (item.bodylocation === bodyLoc - 7) { + equipped = true; + [sdk.items.Arrows, sdk.items.Bolts].includes(item.classid) && CharData.skillData.bow.setArrowInfo(item); + [sdk.items.type.Bow, sdk.items.type.AmazonBow, sdk.items.type.Crossbow].includes(item.itemType) && CharData.skillData.bow.setBowInfo(item); + + if (getCursorType() === 3) { + let cursorItem = Game.getCursorUnit(); + !!cursorItem && !cursorItem.shouldKeep() && cursorItem.drop(); + } + + if (Item.hasDependancy(item) && me.needRepair() && me.inTown) { + NPCAction.repair(true); + } + + return true; + } + } + } + } finally { + // Switch back to primary + me.weaponswitch !== 0 && me.switchWeapons(0); + } + + return equipped; }; /** * @param {ItemUnit} item */ Item.autoEquipCheckSecondary = function (item) { - if (!Config.AutoEquip) return true; - if (me.classic) return false; + if (!Config.AutoEquip) return true; + if (me.classic) return false; - let tier = NTIP.GetSecondaryTier(item); - if (tier <= 0) return false; - let bodyLoc = Item.getSecondaryBodyLoc(item); + let tier = NTIP.GetSecondaryTier(item); + if (tier <= 0) return false; + let bodyLoc = Item.getSecondaryBodyLoc(item); - for (let loc of bodyLoc) { - if (tier > me.equipped.get(loc).secondaryTier && (Item.canEquip(item) || !item.identified)) { - return true; - } - } + for (let loc of bodyLoc) { + if (tier > me.equipped.get(loc).secondaryTier && (Item.canEquip(item) || !item.identified)) { + return true; + } + } - return false; + return false; }; /** * @param {string} task */ Item.autoEquipSecondary = function (task = "") { - if (!Config.AutoEquip || me.classic) return true; - - task = task + "Secondary AutoEquip"; - console.log("ÿc8Kolbot-SoloPlayÿc0: Entering " + task); - - const noStash = (!me.inTown || task !== "Secondary AutoEquip"); - let tick = getTickCount(); - let items = me.getItemsEx() - .filter(function (item) { - if (!item.isInStorage) return false; - if (noStash && !item.isInInventory) return false; - let tier = NTIP.GetSecondaryTier(item); - return (item.identified ? tier > 0 : tier !== 0); - }); - - if (!items) return false; - - const sortEq = (a, b) => { - if (Item.canEquip(a)) return -1; - if (Item.canEquip(b)) return 1; - return 0; - }; - - me.cancel(); - - while (items.length > 0) { - items.sort(sortEq); - const item = items.shift(); - if (!item.isInStorage) continue; - const tier = NTIP.GetSecondaryTier(item); - if (tier <= 0) continue; - let bodyLoc = Item.getSecondaryBodyLoc(item); - - for (let loc of bodyLoc) { - const equippedItem = me.equipped.get(loc); - // should never happen - but just in case - if (equippedItem.classid === sdk.items.quest.KhalimsWill) continue; - if (tier > equippedItem.secondaryTier) { - Item.identify(item); - - let gid = item.gid; - let prettyName = item.prettyPrint; - console.debug(prettyName + " tier: " + tier); - - if (this.secondaryEquip(item, loc)) { - console.log("ÿc9SecondaryEquipÿc0 :: Equipped: " + prettyName + " SecondaryTier: " + tier); - Developer.debugging.autoEquip && Item.logItem("Equipped switch", me.getItem(-1, -1, gid)); - Developer.logEquipped && MuleLogger.logEquippedItems(); - me.equipped.set(loc, item); - } - - break; - } - } - } - - console.log("ÿc8Kolbot-SoloPlayÿc0: Exiting secondary auto equip. Time elapsed: " + Time.format(getTickCount() - tick)); - return true; + if (!Config.AutoEquip || me.classic) return true; + + task = task + "Secondary AutoEquip"; + console.log("ÿc8Kolbot-SoloPlayÿc0: Entering " + task); + + const noStash = (!me.inTown || task !== "Secondary AutoEquip"); + let tick = getTickCount(); + let items = me.getItemsEx() + .filter(function (item) { + if (!item.isInStorage) return false; + if (noStash && !item.isInInventory) return false; + let tier = NTIP.GetSecondaryTier(item); + return (item.identified ? tier > 0 : tier !== 0); + }); + + if (!items) return false; + + const sortEq = (a, b) => { + if (Item.canEquip(a)) return -1; + if (Item.canEquip(b)) return 1; + return 0; + }; + + me.cancel(); + + while (items.length > 0) { + items.sort(sortEq); + const item = items.shift(); + if (!item.isInStorage) continue; + const tier = NTIP.GetSecondaryTier(item); + if (tier <= 0) continue; + let bodyLoc = Item.getSecondaryBodyLoc(item); + + for (let loc of bodyLoc) { + const equippedItem = me.equipped.get(loc); + // should never happen - but just in case + if (equippedItem.classid === sdk.items.quest.KhalimsWill) continue; + if (tier > equippedItem.secondaryTier) { + Item.identify(item); + + let gid = item.gid; + let prettyName = item.prettyPrint; + console.debug(prettyName + " tier: " + tier); + + if (this.secondaryEquip(item, loc)) { + console.log("ÿc9SecondaryEquipÿc0 :: Equipped: " + prettyName + " SecondaryTier: " + tier); + Developer.debugging.autoEquip && Item.logItem("Equipped switch", me.getItem(-1, -1, gid)); + Developer.logEquipped && MuleLogger.logEquippedItems(); + me.equipped.set(loc, item); + } + + break; + } + } + } + + console.log("ÿc8Kolbot-SoloPlayÿc0: Exiting secondary auto equip. Time elapsed: " + Time.format(getTickCount() - tick)); + return true; }; /** @@ -609,21 +609,21 @@ Item.hasMercTier = (item) => Config.AutoEquip && me.expansion && NTIP.GetMercTie * @todo re-work using char data so we can shop/keep items if merc is dead *but* we have enough to revive him and buy the item and enough space */ Item.canEquipMerc = function (item, bodyLoc) { - if (item.type !== sdk.unittype.Item || me.classic) return false; - let mercenary = me.getMercEx(); + if (item.type !== sdk.unittype.Item || me.classic) return false; + let mercenary = me.getMercEx(); - // dont have merc or he is dead or unidentifed item - if (!mercenary || !item.identified) return false; - let curr = Item.getMercEquipped(bodyLoc); + // dont have merc or he is dead or unidentifed item + if (!mercenary || !item.identified) return false; + let curr = Item.getMercEquipped(bodyLoc); - // Higher requirements - if (item.getStat(sdk.stats.LevelReq) > mercenary.getStat(sdk.stats.Level) - || item.dexreq > mercenary.getStat(sdk.stats.Dexterity) - curr.dex - || item.strreq > mercenary.getStat(sdk.stats.Strength) - curr.str) { - return false; - } + // Higher requirements + if (item.getStat(sdk.stats.LevelReq) > mercenary.getStat(sdk.stats.Level) + || item.dexreq > mercenary.getStat(sdk.stats.Dexterity) - curr.dex + || item.strreq > mercenary.getStat(sdk.stats.Strength) - curr.str) { + return false; + } - return true; + return true; }; /** @@ -631,74 +631,74 @@ Item.canEquipMerc = function (item, bodyLoc) { * @param {number} bodyLoc */ Item.equipMerc = function (item, bodyLoc) { - let mercenary = me.getMercEx(); - - // dont have merc or he is dead or higher requirements - if (!mercenary || !Item.canEquipMerc(item, bodyLoc)) return false; - // Already equipped in the right slot - if (item.mode === sdk.items.mode.Equipped && item.bodylocation === bodyLoc) return true; - if (item.isInStash && !Town.openStash()) return false; - - for (let i = 0; i < 3; i += 1) { - if (item.toCursor()) { - if (clickItem(sdk.clicktypes.click.item.Mercenary, bodyLoc)) { - delay(500 + me.ping * 2); - Developer.debugging.autoEquip && Item.logItem("Merc Equipped", mercenary.getItem(item.classid)); - } - - let check = mercenary.getItem(item.classid); - - if (check && check.bodylocation === bodyLoc) { - if (check.runeword) { - // just track runewords for now - me.data.merc.gear.push(check.prefixnum); - CharData.updateData("merc", me.data); - } - - if (getCursorType() === 3) { - let cursorItem = Game.getCursorUnit(); - !!cursorItem && !cursorItem.shouldKeep() && cursorItem.drop(); - } - - Developer.logEquipped && MuleLogger.logEquippedItems(); - - return true; - } - } - } - - return false; + let mercenary = me.getMercEx(); + + // dont have merc or he is dead or higher requirements + if (!mercenary || !Item.canEquipMerc(item, bodyLoc)) return false; + // Already equipped in the right slot + if (item.mode === sdk.items.mode.Equipped && item.bodylocation === bodyLoc) return true; + if (item.isInStash && !Town.openStash()) return false; + + for (let i = 0; i < 3; i += 1) { + if (item.toCursor()) { + if (clickItem(sdk.clicktypes.click.item.Mercenary, bodyLoc)) { + delay(500 + me.ping * 2); + Developer.debugging.autoEquip && Item.logItem("Merc Equipped", mercenary.getItem(item.classid)); + } + + let check = mercenary.getItem(item.classid); + + if (check && check.bodylocation === bodyLoc) { + if (check.runeword) { + // just track runewords for now + me.data.merc.gear.push(check.prefixnum); + CharData.updateData("merc", me.data); + } + + if (getCursorType() === 3) { + let cursorItem = Game.getCursorUnit(); + !!cursorItem && !cursorItem.shouldKeep() && cursorItem.drop(); + } + + Developer.logEquipped && MuleLogger.logEquippedItems(); + + return true; + } + } + } + + return false; }; Item.getMercEquipped = function (bodyLoc = -1) { - let mercenary = me.getMercEx(); - - if (mercenary) { - let item = mercenary.getItemsEx() - .filter((item) => item.isEquipped && item.bodylocation === bodyLoc) - .first(); - - if (item) { - return { - classid: item.classid, - prefixnum: item.prefixnum, - tier: NTIP.GetMercTier(item), - name: item.fname, - str: item.getStatEx(sdk.stats.Strength), - dex: item.getStatEx(sdk.stats.Dexterity) - }; - } - } - - // Don't have anything equipped in there - return { - classid: -1, - prefixnum: -1, - tier: -1, - name: "none", - str: 0, - dex: 0 - }; + let mercenary = me.getMercEx(); + + if (mercenary) { + let item = mercenary.getItemsEx() + .filter((item) => item.isEquipped && item.bodylocation === bodyLoc) + .first(); + + if (item) { + return { + classid: item.classid, + prefixnum: item.prefixnum, + tier: NTIP.GetMercTier(item), + name: item.fname, + str: item.getStatEx(sdk.stats.Strength), + dex: item.getStatEx(sdk.stats.Dexterity) + }; + } + } + + // Don't have anything equipped in there + return { + classid: -1, + prefixnum: -1, + tier: -1, + name: "none", + str: 0, + dex: 0 + }; }; /** @@ -706,37 +706,37 @@ Item.getMercEquipped = function (bodyLoc = -1) { * @returns {number[]} */ Item.getBodyLocMerc = function (item) { - let _mercId = me.data.merc.classid; - - switch (item.itemType) { - case sdk.items.type.Shield: - return (_mercId === sdk.mercs.IronWolf - ? [sdk.body.LeftArm] - : []); - case sdk.items.type.Armor: - return [sdk.body.Armor]; - case sdk.items.type.Helm: - case sdk.items.type.Circlet: - return [sdk.body.Head]; - case sdk.items.type.PrimalHelm: - return (_mercId === sdk.mercs.A5Barb - ? [sdk.body.Head] - : []); - case sdk.items.type.Bow: - return (_mercId === sdk.mercs.Rogue - ? [sdk.body.RightArm] - : []); - case sdk.items.type.Spear: - case sdk.items.type.Polearm: - return (_mercId === sdk.mercs.Guard - ? [sdk.body.RightArm] - : []); - case sdk.items.type.Sword: - return ([sdk.mercs.IronWolf, sdk.mercs.A5Barb].includes(_mercId) - ? sdk.body.RightArm - : []); - } - return []; + let _mercId = me.data.merc.classid; + + switch (item.itemType) { + case sdk.items.type.Shield: + return (_mercId === sdk.mercs.IronWolf + ? [sdk.body.LeftArm] + : []); + case sdk.items.type.Armor: + return [sdk.body.Armor]; + case sdk.items.type.Helm: + case sdk.items.type.Circlet: + return [sdk.body.Head]; + case sdk.items.type.PrimalHelm: + return (_mercId === sdk.mercs.A5Barb + ? [sdk.body.Head] + : []); + case sdk.items.type.Bow: + return (_mercId === sdk.mercs.Rogue + ? [sdk.body.RightArm] + : []); + case sdk.items.type.Spear: + case sdk.items.type.Polearm: + return (_mercId === sdk.mercs.Guard + ? [sdk.body.RightArm] + : []); + case sdk.items.type.Sword: + return ([sdk.mercs.IronWolf, sdk.mercs.A5Barb].includes(_mercId) + ? sdk.body.RightArm + : []); + } + return []; }; /** @@ -744,221 +744,221 @@ Item.getBodyLocMerc = function (item) { * @param {boolean} basicCheck */ Item.autoEquipCheckMerc = function (item, basicCheck = false) { - if (!Config.AutoEquip) return true; - if (Config.AutoEquip && !me.getMercEx()) return false; - - let tier = NTIP.GetMercTier(item); - if (tier <= 0) return false; - let bodyLoc = Item.getBodyLocMerc(item); - - for (let loc of bodyLoc) { - let oldTier = Item.getMercEquipped(loc).tier; - - if (tier > oldTier) { - if (Item.canEquipMerc(item) || !item.identified) { - return true; - } else if (basicCheck) { - // keep wanted final gear items - if (NTIP.CheckItem(item, NTIP.FinalGear.list) === Pickit.Result.WANTED) { - return true; - } - - return false; - } - } - } - - return false; + if (!Config.AutoEquip) return true; + if (Config.AutoEquip && !me.getMercEx()) return false; + + let tier = NTIP.GetMercTier(item); + if (tier <= 0) return false; + let bodyLoc = Item.getBodyLocMerc(item); + + for (let loc of bodyLoc) { + let oldTier = Item.getMercEquipped(loc).tier; + + if (tier > oldTier) { + if (Item.canEquipMerc(item) || !item.identified) { + return true; + } else if (basicCheck) { + // keep wanted final gear items + if (NTIP.CheckItem(item, NTIP.FinalGear.list) === Pickit.Result.WANTED) { + return true; + } + + return false; + } + } + } + + return false; }; Item.autoEquipMerc = function () { - if (!Config.AutoEquip || !me.getMercEx()) return true; - - let items = me.getItemsEx() - .filter(function (item) { - if (!item.isInStorage) return false; - let tier = NTIP.GetMercTier(item); - return (item.identified ? tier > 0 : tier !== 0); - }); - if (!items.length) return false; - - function sortEq (a, b) { - let [prioA, prioB] = [Item.canEquip(a), Item.canEquip(b)]; - if (prioA && prioB) return NTIP.GetMercTier(b) - NTIP.GetMercTier(a); - if (prioA) return -1; - if (prioB) return 1; - return 0; - } - - me.cancel(); - - while (items.length > 0) { - items.sort(sortEq); - const item = items.shift(); - const tier = NTIP.GetMercTier(item); - if (tier <= 0) continue; - const bodyLoc = Item.getBodyLocMerc(item); - const name = item.name; - - for (let loc of bodyLoc) { - if ([sdk.storage.Inventory, sdk.storage.Stash].includes(item.location) && tier > Item.getMercEquipped(loc).tier) { - Item.identify(item); - - console.log("Merc " + name); - this.equipMerc(item, loc) && console.log("ÿc9MercEquipÿc0 :: Equipped: " + name + " MercTier: " + tier); - - let cursorItem = Game.getCursorUnit(); - - if (cursorItem) { - cursorItem.drop(); - Developer.debugging.autoEquip && Item.logItem("Merc Dropped", cursorItem); - } - - break; - } - } - } - - return true; + if (!Config.AutoEquip || !me.getMercEx()) return true; + + let items = me.getItemsEx() + .filter(function (item) { + if (!item.isInStorage) return false; + let tier = NTIP.GetMercTier(item); + return (item.identified ? tier > 0 : tier !== 0); + }); + if (!items.length) return false; + + function sortEq (a, b) { + let [prioA, prioB] = [Item.canEquip(a), Item.canEquip(b)]; + if (prioA && prioB) return NTIP.GetMercTier(b) - NTIP.GetMercTier(a); + if (prioA) return -1; + if (prioB) return 1; + return 0; + } + + me.cancel(); + + while (items.length > 0) { + items.sort(sortEq); + const item = items.shift(); + const tier = NTIP.GetMercTier(item); + if (tier <= 0) continue; + const bodyLoc = Item.getBodyLocMerc(item); + const name = item.name; + + for (let loc of bodyLoc) { + if ([sdk.storage.Inventory, sdk.storage.Stash].includes(item.location) && tier > Item.getMercEquipped(loc).tier) { + Item.identify(item); + + console.log("Merc " + name); + this.equipMerc(item, loc) && console.log("ÿc9MercEquipÿc0 :: Equipped: " + name + " MercTier: " + tier); + + let cursorItem = Game.getCursorUnit(); + + if (cursorItem) { + cursorItem.drop(); + Developer.debugging.autoEquip && Item.logItem("Merc Dropped", cursorItem); + } + + break; + } + } + } + + return true; }; Item.removeItemsMerc = function () { - let mercenary = me.getMercEx(); - if (!mercenary) return true; - // Sort items so we try to keep the highest tier'd items in case space in our invo is limited - let items = mercenary.getItemsEx().sort((a, b) => NTIP.GetMercTier(b) - NTIP.GetMercTier(a)); - - if (items) { - for (let i = 0; i < items.length; i++) { - clickItem(sdk.clicktypes.click.item.Mercenary, items[i].bodylocation); - delay(500 + me.ping * 2); - - let cursorItem = Game.getCursorUnit(); - - if (cursorItem) { - if (Storage.Inventory.CanFit(cursorItem)) { - Storage.Inventory.MoveTo(cursorItem); - } else { - cursorItem.drop(); - } - } - } - } - - return !!mercenary.getItem(); + let mercenary = me.getMercEx(); + if (!mercenary) return true; + // Sort items so we try to keep the highest tier'd items in case space in our invo is limited + let items = mercenary.getItemsEx().sort((a, b) => NTIP.GetMercTier(b) - NTIP.GetMercTier(a)); + + if (items) { + for (let i = 0; i < items.length; i++) { + clickItem(sdk.clicktypes.click.item.Mercenary, items[i].bodylocation); + delay(500 + me.ping * 2); + + let cursorItem = Game.getCursorUnit(); + + if (cursorItem) { + if (Storage.Inventory.CanFit(cursorItem)) { + Storage.Inventory.MoveTo(cursorItem); + } else { + cursorItem.drop(); + } + } + } + } + + return !!mercenary.getItem(); }; // Log kept item stats in the manager. Item.logItem = function (action, unit, keptLine, force) { - if (!this.useItemLog || unit === undefined || !unit || !unit.fname) return false; - if (!Config.LogKeys && ["pk1", "pk2", "pk3"].includes(unit.code)) return false; - if (!Config.LogOrgans && ["dhn", "bey", "mbr"].includes(unit.code)) return false; - if (!Config.LogLowRunes && ["r01", "r02", "r03", "r04", "r05", "r06", "r07", "r08", "r09", "r10", "r11", "r12", "r13", "r14"].includes(unit.code)) return false; - if (!Config.LogMiddleRunes && ["r15", "r16", "r17", "r18", "r19", "r20", "r21", "r22", "r23"].includes(unit.code)) return false; - if (!Config.LogHighRunes && ["r24", "r25", "r26", "r27", "r28", "r29", "r30", "r31", "r32", "r33"].includes(unit.code)) return false; - if (!Config.LogLowGems && ["gcv", "gcy", "gcb", "gcg", "gcr", "gcw", "skc", "gfv", "gfy", "gfb", "gfg", "gfr", "gfw", "skf", "gsv", "gsy", "gsb", "gsg", "gsr", "gsw", "sku"].includes(unit.code)) return false; - if (!Config.LogHighGems && ["gzv", "gly", "glb", "glg", "glr", "glw", "skl", "gpv", "gpy", "gpb", "gpg", "gpr", "gpw", "skz"].includes(unit.code)) return false; - - for (let i = 0; i < Config.SkipLogging.length; i++) { - if (Config.SkipLogging[i] === unit.classid || Config.SkipLogging[i] === unit.code) return false; - } - - let lastArea; - let name = unit.fname.split("\n").reverse().join(" ").replace(/ÿc[0-9!"+<:;.*]|\/|\\/g, "").trim(); - let desc = (this.getItemDesc(unit) || ""); - let color = (unit.getColor() || -1); - - if (action.match("kept", "i")) { - lastArea = DataFile.getStats().lastArea; - lastArea && (desc += ("\n\\xffc0Area: " + lastArea)); - } - - const mercCheck = action.match("Merc"); - const hasTier = AutoEquip.hasTier(unit); - const charmCheck = (unit.isCharm && CharmEquip.check(unit)); - const nTResult = NTIP.CheckItem(unit, NTIP_CheckListNoTier) === 1; - - if (!action.match("kept", "i") && !action.match("Shopped") && hasTier) { - if (!mercCheck) { - NTIP.GetCharmTier(unit) > 0 && (desc += ("\n\\xffc0Autoequip charm tier: " + NTIP.GetCharmTier(unit))); - NTIP.GetTier(unit) > 0 && (desc += ("\n\\xffc0Autoequip char tier: " + NTIP.GetTier(unit))); - } else { - desc += ("\n\\xffc0Autoequip merc tier: " + NTIP.GetMercTier(unit)); - } - } - - // should stop logging items unless we wish to see them or it's part of normal pickit - if (!nTResult && !force) { - switch (true) { - case (unit.questItem || unit.isBaseType): - case (!unit.isCharm && hasTier && !Developer.debugging.autoEquip): - case (charmCheck && !Developer.debugging.smallCharm && unit.classid === sdk.items.SmallCharm): - case (charmCheck && !Developer.debugging.largeCharm && unit.classid === sdk.items.LargeCharm): - case (charmCheck && !Developer.debugging.grandCharm && unit.classid === sdk.items.GrandCharm): - return true; - default: - break; - } - } - - let code = this.getItemCode(unit); - let sock = unit.getItem(); - - if (sock) { - do { - if (sock.itemType === sdk.items.type.Jewel) { - desc += "\n\n"; - desc += this.getItemDesc(sock); - } - } while (sock.getNext()); - } - - keptLine && (desc += ("\n\\xffc0Line: " + keptLine)); - desc += "$" + (unit.getFlag(sdk.items.flags.Ethereal) ? ":eth" : ""); - - let itemObj = { - title: action + " " + name, - description: desc, - image: code, - textColor: unit.quality, - itemColor: color, - header: "", - sockets: this.getItemSockets(unit) - }; - - D2Bot.printToItemLog(itemObj); - - return true; + if (!this.useItemLog || unit === undefined || !unit || !unit.fname) return false; + if (!Config.LogKeys && ["pk1", "pk2", "pk3"].includes(unit.code)) return false; + if (!Config.LogOrgans && ["dhn", "bey", "mbr"].includes(unit.code)) return false; + if (!Config.LogLowRunes && ["r01", "r02", "r03", "r04", "r05", "r06", "r07", "r08", "r09", "r10", "r11", "r12", "r13", "r14"].includes(unit.code)) return false; + if (!Config.LogMiddleRunes && ["r15", "r16", "r17", "r18", "r19", "r20", "r21", "r22", "r23"].includes(unit.code)) return false; + if (!Config.LogHighRunes && ["r24", "r25", "r26", "r27", "r28", "r29", "r30", "r31", "r32", "r33"].includes(unit.code)) return false; + if (!Config.LogLowGems && ["gcv", "gcy", "gcb", "gcg", "gcr", "gcw", "skc", "gfv", "gfy", "gfb", "gfg", "gfr", "gfw", "skf", "gsv", "gsy", "gsb", "gsg", "gsr", "gsw", "sku"].includes(unit.code)) return false; + if (!Config.LogHighGems && ["gzv", "gly", "glb", "glg", "glr", "glw", "skl", "gpv", "gpy", "gpb", "gpg", "gpr", "gpw", "skz"].includes(unit.code)) return false; + + for (let i = 0; i < Config.SkipLogging.length; i++) { + if (Config.SkipLogging[i] === unit.classid || Config.SkipLogging[i] === unit.code) return false; + } + + let lastArea; + let name = unit.fname.split("\n").reverse().join(" ").replace(/ÿc[0-9!"+<:;.*]|\/|\\/g, "").trim(); + let desc = (this.getItemDesc(unit) || ""); + let color = (unit.getColor() || -1); + + if (action.match("kept", "i")) { + lastArea = DataFile.getStats().lastArea; + lastArea && (desc += ("\n\\xffc0Area: " + lastArea)); + } + + const mercCheck = action.match("Merc"); + const hasTier = AutoEquip.hasTier(unit); + const charmCheck = (unit.isCharm && CharmEquip.check(unit)); + const nTResult = NTIP.CheckItem(unit, NTIP_CheckListNoTier) === 1; + + if (!action.match("kept", "i") && !action.match("Shopped") && hasTier) { + if (!mercCheck) { + NTIP.GetCharmTier(unit) > 0 && (desc += ("\n\\xffc0Autoequip charm tier: " + NTIP.GetCharmTier(unit))); + NTIP.GetTier(unit) > 0 && (desc += ("\n\\xffc0Autoequip char tier: " + NTIP.GetTier(unit))); + } else { + desc += ("\n\\xffc0Autoequip merc tier: " + NTIP.GetMercTier(unit)); + } + } + + // should stop logging items unless we wish to see them or it's part of normal pickit + if (!nTResult && !force) { + switch (true) { + case (unit.questItem || unit.isBaseType): + case (!unit.isCharm && hasTier && !Developer.debugging.autoEquip): + case (charmCheck && !Developer.debugging.smallCharm && unit.classid === sdk.items.SmallCharm): + case (charmCheck && !Developer.debugging.largeCharm && unit.classid === sdk.items.LargeCharm): + case (charmCheck && !Developer.debugging.grandCharm && unit.classid === sdk.items.GrandCharm): + return true; + default: + break; + } + } + + let code = this.getItemCode(unit); + let sock = unit.getItem(); + + if (sock) { + do { + if (sock.itemType === sdk.items.type.Jewel) { + desc += "\n\n"; + desc += this.getItemDesc(sock); + } + } while (sock.getNext()); + } + + keptLine && (desc += ("\n\\xffc0Line: " + keptLine)); + desc += "$" + (unit.getFlag(sdk.items.flags.Ethereal) ? ":eth" : ""); + + let itemObj = { + title: action + " " + name, + description: desc, + image: code, + textColor: unit.quality, + itemColor: color, + header: "", + sockets: this.getItemSockets(unit) + }; + + D2Bot.printToItemLog(itemObj); + + return true; }; const AutoEquip = { - /** - * @param {ItemUnit} item - */ - hasTier: function (item) { - if (me.classic) return Item.hasTier(item); - if (item.isCharm) { - return CharmEquip.hasCharmTier(item); - } - return Item.hasMercTier(item) || Item.hasTier(item) || Item.hasSecondaryTier(item); - }, - - /** - * @param {ItemUnit} item - */ - wanted: function (item) { - if (me.classic) return Item.autoEquipCheck(item, true); - if (item.isCharm) { - return CharmEquip.check(item); - } - return Item.autoEquipCheckMerc(item, true) || Item.autoEquipCheck(item, true) || Item.autoEquipCheckSecondary(item); - }, - - run: function () { - console.time("AutoEquip"); - Item.autoEquip(); - Item.autoEquipSecondary(); - CharmEquip.run(); - console.timeEnd("AutoEquip"); - } + /** + * @param {ItemUnit} item + */ + hasTier: function (item) { + if (me.classic) return Item.hasTier(item); + if (item.isCharm) { + return CharmEquip.hasCharmTier(item); + } + return Item.hasMercTier(item) || Item.hasTier(item) || Item.hasSecondaryTier(item); + }, + + /** + * @param {ItemUnit} item + */ + wanted: function (item) { + if (me.classic) return Item.autoEquipCheck(item, true); + if (item.isCharm) { + return CharmEquip.check(item); + } + return Item.autoEquipCheckMerc(item, true) || Item.autoEquipCheck(item, true) || Item.autoEquipCheckSecondary(item); + }, + + run: function () { + console.time("AutoEquip"); + Item.autoEquip(); + Item.autoEquipSecondary(); + CharmEquip.run(); + console.timeEnd("AutoEquip"); + } }; diff --git a/libs/SoloPlay/Functions/ItemPrototypes.js b/libs/SoloPlay/Functions/ItemPrototypes.js index 3b28e1fb..5b8bdc81 100644 --- a/libs/SoloPlay/Functions/ItemPrototypes.js +++ b/libs/SoloPlay/Functions/ItemPrototypes.js @@ -10,337 +10,337 @@ includeIfNotIncluded("SoloPlay/Functions/PrototypeOverrides.js"); Unit.prototype.getQuantityOwned = function (skipSameItem = false) { - if (this === undefined || this.type !== sdk.unittype.Item) return 0; - - return me.getItemsEx() - .filter(check => - check.itemType === this.itemType - && (!skipSameItem || check.gid !== this.gid) - && check.classid === this.classid - && check.quality === this.quality - && check.sockets === this.sockets - && check.isInStorage - ).length; + if (this === undefined || this.type !== sdk.unittype.Item) return 0; + + return me.getItemsEx() + .filter(check => + check.itemType === this.itemType + && (!skipSameItem || check.gid !== this.gid) + && check.classid === this.classid + && check.quality === this.quality + && check.sockets === this.sockets + && check.isInStorage + ).length; }; Unit.prototype.hasTier = function () { - if (this === undefined || this.type !== sdk.unittype.Item) return false; - return Config.AutoEquip && NTIP.GetTier(this) > 0; + if (this === undefined || this.type !== sdk.unittype.Item) return false; + return Config.AutoEquip && NTIP.GetTier(this) > 0; }; Unit.prototype.hasSecondaryTier = function () { - if (this === undefined || this.type !== sdk.unittype.Item) return false; - return Config.AutoEquip && NTIP.GetSecondaryTier(this) > 0 && me.expansion; + if (this === undefined || this.type !== sdk.unittype.Item) return false; + return Config.AutoEquip && NTIP.GetSecondaryTier(this) > 0 && me.expansion; }; Unit.prototype.hasMercTier = function () { - if (this === undefined || this.type !== sdk.unittype.Item) return false; - return Config.AutoEquip && NTIP.GetMercTier(this) > 0 && me.expansion; + if (this === undefined || this.type !== sdk.unittype.Item) return false; + return Config.AutoEquip && NTIP.GetMercTier(this) > 0 && me.expansion; }; Unit.prototype.bodyLocation = function () { - if (this === undefined || this.type !== sdk.unittype.Item) return []; - - let bodyLoc = (() => { - switch (true) { - case Item.shieldTypes.includes(this.itemType): - return sdk.body.LeftArm; - case this.itemType === sdk.items.type.Armor: - return sdk.body.Armor; - case this.itemType === sdk.items.type.Ring: - return [sdk.body.RingRight, sdk.body.RingLeft]; - case this.itemType === sdk.items.type.Amulet: - return sdk.body.Neck; - case this.itemType === sdk.items.type.Boots: - return sdk.body.Feet; - case this.itemType === sdk.items.type.Gloves: - return sdk.body.Gloves; - case this.itemType === sdk.items.type.Belt: - return sdk.body.Belt; - case Item.helmTypes.includes(this.itemType): - return sdk.body.Head; - case Item.weaponTypes.includes(this.itemType): - return me.barbarian ? [sdk.body.RightArm, sdk.body.LeftArm] : sdk.body.RightArm; - case [sdk.items.type.HandtoHand, sdk.items.type.AssassinClaw].includes(this.itemType): - return !Check.currentBuild().caster && me.assassin ? [sdk.body.RightArm, sdk.body.LeftArm] : sdk.body.RightArm; - default: - return false; - } - })(); - - return Array.isArray(bodyLoc) ? bodyLoc : [bodyLoc]; + if (this === undefined || this.type !== sdk.unittype.Item) return []; + + let bodyLoc = (() => { + switch (true) { + case Item.shieldTypes.includes(this.itemType): + return sdk.body.LeftArm; + case this.itemType === sdk.items.type.Armor: + return sdk.body.Armor; + case this.itemType === sdk.items.type.Ring: + return [sdk.body.RingRight, sdk.body.RingLeft]; + case this.itemType === sdk.items.type.Amulet: + return sdk.body.Neck; + case this.itemType === sdk.items.type.Boots: + return sdk.body.Feet; + case this.itemType === sdk.items.type.Gloves: + return sdk.body.Gloves; + case this.itemType === sdk.items.type.Belt: + return sdk.body.Belt; + case Item.helmTypes.includes(this.itemType): + return sdk.body.Head; + case Item.weaponTypes.includes(this.itemType): + return me.barbarian ? [sdk.body.RightArm, sdk.body.LeftArm] : sdk.body.RightArm; + case [sdk.items.type.HandtoHand, sdk.items.type.AssassinClaw].includes(this.itemType): + return !Check.currentBuild().caster && me.assassin ? [sdk.body.RightArm, sdk.body.LeftArm] : sdk.body.RightArm; + default: + return false; + } + })(); + + return Array.isArray(bodyLoc) ? bodyLoc : [bodyLoc]; }; Unit.prototype.secondaryBodyLocation = function () { - if (this === undefined || this.type !== sdk.unittype.Item) return []; - - let bodyLoc = (() => { - switch (true) { - case Item.shieldTypes.includes(this.itemType): - return sdk.body.LeftArmSecondary; - case Item.weaponTypes.includes(this.itemType): - return me.barbarian ? [sdk.body.RightArmSecondary, sdk.body.LeftArmSecondary] : sdk.body.RightArmSecondary; - case [sdk.items.type.HandtoHand, sdk.items.type.AssassinClaw].includes(this.itemType): - return !Check.currentBuild().caster && me.assassin ? [sdk.body.RightArmSecondary, sdk.body.LeftArmSecondary] : sdk.body.RightArmSecondary; - default: - return false; - } - })(); - - return Array.isArray(bodyLoc) ? bodyLoc : [bodyLoc]; + if (this === undefined || this.type !== sdk.unittype.Item) return []; + + let bodyLoc = (() => { + switch (true) { + case Item.shieldTypes.includes(this.itemType): + return sdk.body.LeftArmSecondary; + case Item.weaponTypes.includes(this.itemType): + return me.barbarian ? [sdk.body.RightArmSecondary, sdk.body.LeftArmSecondary] : sdk.body.RightArmSecondary; + case [sdk.items.type.HandtoHand, sdk.items.type.AssassinClaw].includes(this.itemType): + return !Check.currentBuild().caster && me.assassin ? [sdk.body.RightArmSecondary, sdk.body.LeftArmSecondary] : sdk.body.RightArmSecondary; + default: + return false; + } + })(); + + return Array.isArray(bodyLoc) ? bodyLoc : [bodyLoc]; }; Unit.prototype.mercBodyLocation = function () { - if (this === undefined || this.type !== sdk.unittype.Item) return []; - - let mercenary = me.getMercEx(); - if (!mercenary) return []; - - let bodyLoc = (() => { - switch (this.itemType) { - case sdk.items.type.Shield: - return (mercenary.classid === sdk.mercs.IronWolf ? sdk.body.LeftArm : []); - case sdk.items.type.Armor: - return sdk.body.Armor; - case sdk.items.type.Helm: - case sdk.items.type.Circlet: - return sdk.body.Head; - case sdk.items.type.PrimalHelm: - return (mercenary.classid === sdk.mercs.A5Barb ? sdk.body.Head : []); - case sdk.items.type.Bow: - return (mercenary.classid === sdk.mercs.Rogue ? sdk.body.RightArm : []); - case sdk.items.type.Spear: - case sdk.items.type.Polearm: - return (mercenary.classid === sdk.mercs.Guard ? sdk.body.RightArm : []); - case sdk.items.type.Sword: - return ([sdk.mercs.IronWolf, sdk.mercs.A5Barb].includes(mercenary.classid) ? sdk.body.RightArm : []); - default: - return false; - } - })(); - - return Array.isArray(bodyLoc) ? bodyLoc : [bodyLoc]; + if (this === undefined || this.type !== sdk.unittype.Item) return []; + + let mercenary = me.getMercEx(); + if (!mercenary) return []; + + let bodyLoc = (() => { + switch (this.itemType) { + case sdk.items.type.Shield: + return (mercenary.classid === sdk.mercs.IronWolf ? sdk.body.LeftArm : []); + case sdk.items.type.Armor: + return sdk.body.Armor; + case sdk.items.type.Helm: + case sdk.items.type.Circlet: + return sdk.body.Head; + case sdk.items.type.PrimalHelm: + return (mercenary.classid === sdk.mercs.A5Barb ? sdk.body.Head : []); + case sdk.items.type.Bow: + return (mercenary.classid === sdk.mercs.Rogue ? sdk.body.RightArm : []); + case sdk.items.type.Spear: + case sdk.items.type.Polearm: + return (mercenary.classid === sdk.mercs.Guard ? sdk.body.RightArm : []); + case sdk.items.type.Sword: + return ([sdk.mercs.IronWolf, sdk.mercs.A5Barb].includes(mercenary.classid) ? sdk.body.RightArm : []); + default: + return false; + } + })(); + + return Array.isArray(bodyLoc) ? bodyLoc : [bodyLoc]; }; Unit.prototype.canEquip = function () { - if (this === undefined || this.type !== sdk.unittype.Item || !this.identified) return false; - return me.charlvl >= this.getStat(sdk.stats.LevelReq) && me.trueStr >= this.strreq && me.trueDex >= this.dexreq; + if (this === undefined || this.type !== sdk.unittype.Item || !this.identified) return false; + return me.charlvl >= this.getStat(sdk.stats.LevelReq) && me.trueStr >= this.strreq && me.trueDex >= this.dexreq; }; Unit.prototype.canEquipMerc = function (bodyLoc = -1) { - if (this === undefined || this.type !== sdk.unittype.Item || !this.identified || me.classic) return false; - let mercenary = me.getMercEx(); + if (this === undefined || this.type !== sdk.unittype.Item || !this.identified || me.classic) return false; + let mercenary = me.getMercEx(); - // dont have merc or he is dead - if (!mercenary) return false; - let curr = Item.getMercEquipped(bodyLoc); + // dont have merc or he is dead + if (!mercenary) return false; + let curr = Item.getMercEquipped(bodyLoc); - // Higher requirements - if (this.getStat(sdk.stats.LevelReq) > mercenary.getStat(sdk.stats.Level) - || this.dexreq > mercenary.getStat(sdk.stats.Dexterity) - curr.dex - || this.strreq > mercenary.getStat(sdk.stats.Strength) - curr.str) { - return false; - } + // Higher requirements + if (this.getStat(sdk.stats.LevelReq) > mercenary.getStat(sdk.stats.Level) + || this.dexreq > mercenary.getStat(sdk.stats.Dexterity) - curr.dex + || this.strreq > mercenary.getStat(sdk.stats.Strength) - curr.str) { + return false; + } - return true; + return true; }; Unit.prototype.autoEquipCheck = function (checkCanEquip = true) { - if (!Config.AutoEquip) return true; - if (this === undefined || this.type !== sdk.unittype.Item) return false; + if (!Config.AutoEquip) return true; + if (this === undefined || this.type !== sdk.unittype.Item) return false; - let tier = NTIP.GetTier(this); - let bodyLoc = this.bodyLocation(); + let tier = NTIP.GetTier(this); + let bodyLoc = this.bodyLocation(); - if (tier > 0 && bodyLoc.length) { - for (let i = 0; i < bodyLoc.length; i += 1) { - if (tier > me.equipped.get(bodyLoc[i]).tier && ((checkCanEquip ? this.canEquip() : true) || !this.identified)) { - if (this.twoHanded && !me.barbarian) { - if (tier < me.equipped.get(sdk.body.RightArm).tier + me.equipped.get(sdk.body.LeftArm).tier) return false; - } + if (tier > 0 && bodyLoc.length) { + for (let i = 0; i < bodyLoc.length; i += 1) { + if (tier > me.equipped.get(bodyLoc[i]).tier && ((checkCanEquip ? this.canEquip() : true) || !this.identified)) { + if (this.twoHanded && !me.barbarian) { + if (tier < me.equipped.get(sdk.body.RightArm).tier + me.equipped.get(sdk.body.LeftArm).tier) return false; + } - if (!me.barbarian && bodyLoc[i] === 5 && me.equipped.get(bodyLoc[i]).tier === -1) { - if (me.equipped.get(sdk.body.RightArm).twoHanded && tier < me.equipped.get(sdk.body.RightArm).tier) return false; - } + if (!me.barbarian && bodyLoc[i] === 5 && me.equipped.get(bodyLoc[i]).tier === -1) { + if (me.equipped.get(sdk.body.RightArm).twoHanded && tier < me.equipped.get(sdk.body.RightArm).tier) return false; + } - return true; - } - } - } + return true; + } + } + } - return false; + return false; }; Unit.prototype.autoEquipCheckSecondary = function (checkCanEquip = true) { - if (!Config.AutoEquip) return true; - if (this === undefined || this.type !== sdk.unittype.Item || me.classic) return false; + if (!Config.AutoEquip) return true; + if (this === undefined || this.type !== sdk.unittype.Item || me.classic) return false; - let tier = NTIP.GetSecondaryTier(this); - let bodyLoc = this.secondaryBodyLocation(); + let tier = NTIP.GetSecondaryTier(this); + let bodyLoc = this.secondaryBodyLocation(); - if (tier > 0 && bodyLoc.length) { - for (let i = 0; i < bodyLoc.length; i += 1) { - if (tier > me.equipped.get(bodyLoc[i]).secondaryTier && ((checkCanEquip ? this.canEquip() : true) || !this.identified)) { - if (this.twoHanded && !me.barbarian) { - if (tier < me.equipped.get(sdk.body.RightArmSecondary).secondaryTier + me.equipped.get(sdk.body.LeftArmSecondary).secondaryTier) return false; - } + if (tier > 0 && bodyLoc.length) { + for (let i = 0; i < bodyLoc.length; i += 1) { + if (tier > me.equipped.get(bodyLoc[i]).secondaryTier && ((checkCanEquip ? this.canEquip() : true) || !this.identified)) { + if (this.twoHanded && !me.barbarian) { + if (tier < me.equipped.get(sdk.body.RightArmSecondary).secondaryTier + me.equipped.get(sdk.body.LeftArmSecondary).secondaryTier) return false; + } - if (!me.barbarian && bodyLoc[i] === 12 && me.equipped.get(bodyLoc[i]).secondaryTier === -1) { - if (me.equipped.get(sdk.body.RightArmSecondary).twoHanded && tier < me.equipped.get(sdk.body.RightArmSecondary).secondaryTier) return false; - } + if (!me.barbarian && bodyLoc[i] === 12 && me.equipped.get(bodyLoc[i]).secondaryTier === -1) { + if (me.equipped.get(sdk.body.RightArmSecondary).twoHanded && tier < me.equipped.get(sdk.body.RightArmSecondary).secondaryTier) return false; + } - return true; - } - } - } + return true; + } + } + } - return false; + return false; }; Unit.prototype.autoEquipCheckMerc = function (checkCanEquip = true) { - if (!Config.AutoEquip) return true; - if (this === undefined || this.type !== sdk.unittype.Item || me.classic || !me.getMercEx()) return false; + if (!Config.AutoEquip) return true; + if (this === undefined || this.type !== sdk.unittype.Item || me.classic || !me.getMercEx()) return false; - let tier = NTIP.GetMercTier(this); - let bodyLoc = this.mercBodyLocation(); + let tier = NTIP.GetMercTier(this); + let bodyLoc = this.mercBodyLocation(); - if (tier > 0 && bodyLoc.length) { - for (let i = 0; i < bodyLoc.length; i += 1) { - let oldTier = Item.getMercEquipped(bodyLoc[i]).tier; + if (tier > 0 && bodyLoc.length) { + for (let i = 0; i < bodyLoc.length; i += 1) { + let oldTier = Item.getMercEquipped(bodyLoc[i]).tier; - if (tier > oldTier && ((checkCanEquip ? this.canEquipMerc() : true) || !this.identified)) { - return true; - } - } - } + if (tier > oldTier && ((checkCanEquip ? this.canEquipMerc() : true) || !this.identified)) { + return true; + } + } + } - return false; + return false; }; Unit.prototype.shouldKeep = function () { - if (this === undefined || this.type !== sdk.unittype.Item) return false; - - if (Pickit.checkItem(this).result === 1 - // only keep wanted items or cubing items (in rare cases where weapon being used is also a cubing wanted item) - || (this.unique && Pickit.checkItem(this).result === 2) - // or keep if item is worth selling - || (this.getItemCost(sdk.items.cost.ToSell) / (this.sizex * this.sizey) >= (!me.inTown && me.charlvl < 12 ? 5 : me.normal ? 50 : me.nightmare ? 500 : 1000))) { - if ((Storage.Inventory.CanFit(this) && Storage.Inventory.MoveTo(this))) { - !AutoEquip.wanted(this) && NTIP.CheckItem(this, NTIP_CheckListNoTier) === Pickit.Result.UNWANTED && Town.sell.push(this); - return true; - } - } - - return false; + if (this === undefined || this.type !== sdk.unittype.Item) return false; + + if (Pickit.checkItem(this).result === 1 + // only keep wanted items or cubing items (in rare cases where weapon being used is also a cubing wanted item) + || (this.unique && Pickit.checkItem(this).result === 2) + // or keep if item is worth selling + || (this.getItemCost(sdk.items.cost.ToSell) / (this.sizex * this.sizey) >= (!me.inTown && me.charlvl < 12 ? 5 : me.normal ? 50 : me.nightmare ? 500 : 1000))) { + if ((Storage.Inventory.CanFit(this) && Storage.Inventory.MoveTo(this))) { + !AutoEquip.wanted(this) && NTIP.CheckItem(this, NTIP_CheckListNoTier) === Pickit.Result.UNWANTED && Town.sell.push(this); + return true; + } + } + + return false; }; Unit.prototype.equipItem = function (bodyLoc = -1) { - // can't equip - if (this === undefined || this.type !== sdk.unittype.Item || !this.canEquip()) return false; - bodyLoc === -1 && (bodyLoc = this.bodyLocation().first()); - // Already equipped in the right slot - if (this.mode === sdk.items.mode.Equipped && this.bodylocation === bodyLoc) return true; - // failed to open stash - if (this.isInStash && !Town.openStash()) return false; - // failed to open cube - if (this.isInCube && !Cubing.openCube()) return false; - - let rolledBack = false; - // todo: sometimes rings get bugged with the higher tier ring ending up on the wrong finger, if this happens swap them - - for (let i = 0; i < 3; i += 1) { - if (this.toCursor()) { - clickItemAndWait(sdk.clicktypes.click.item.Left, bodyLoc); - - if (this.bodylocation === bodyLoc) { - if (getCursorType() === 3) { - let cursorItem = Game.getCursorUnit(); - - if (cursorItem) { - // rollback check - let justEquipped = me.equipped.get(bodyLoc); - if (NTIP.GetTier(cursorItem) > justEquipped.tier && !this.questItem && !justEquipped.isRuneword/*Wierd bug with runewords that it'll fail to get correct item desc so don't attempt rollback*/) { - console.debug("ROLLING BACK TO OLD ITEM BECAUSE IT WAS BETTER"); - console.debug("OldItem: " + NTIP.GetTier(cursorItem) + " Just Equipped Item: " + me.equipped.get(bodyLoc).tier); - clickItemAndWait(sdk.clicktypes.click.item.Left, bodyLoc); - cursorItem = Game.getCursorUnit(); - rolledBack = true; - } - - !this.shouldKeep() && this.drop(); - } - } - - return rolledBack ? false : true; - } - } - } - - return false; + // can't equip + if (this === undefined || this.type !== sdk.unittype.Item || !this.canEquip()) return false; + bodyLoc === -1 && (bodyLoc = this.bodyLocation().first()); + // Already equipped in the right slot + if (this.mode === sdk.items.mode.Equipped && this.bodylocation === bodyLoc) return true; + // failed to open stash + if (this.isInStash && !Town.openStash()) return false; + // failed to open cube + if (this.isInCube && !Cubing.openCube()) return false; + + let rolledBack = false; + // todo: sometimes rings get bugged with the higher tier ring ending up on the wrong finger, if this happens swap them + + for (let i = 0; i < 3; i += 1) { + if (this.toCursor()) { + clickItemAndWait(sdk.clicktypes.click.item.Left, bodyLoc); + + if (this.bodylocation === bodyLoc) { + if (getCursorType() === 3) { + let cursorItem = Game.getCursorUnit(); + + if (cursorItem) { + // rollback check + let justEquipped = me.equipped.get(bodyLoc); + if (NTIP.GetTier(cursorItem) > justEquipped.tier && !this.questItem && !justEquipped.isRuneword/*Wierd bug with runewords that it'll fail to get correct item desc so don't attempt rollback*/) { + console.debug("ROLLING BACK TO OLD ITEM BECAUSE IT WAS BETTER"); + console.debug("OldItem: " + NTIP.GetTier(cursorItem) + " Just Equipped Item: " + me.equipped.get(bodyLoc).tier); + clickItemAndWait(sdk.clicktypes.click.item.Left, bodyLoc); + cursorItem = Game.getCursorUnit(); + rolledBack = true; + } + + !this.shouldKeep() && this.drop(); + } + } + + return rolledBack ? false : true; + } + } + } + + return false; }; Unit.prototype.secondaryEquip = function (bodyLoc = -1) { - // can't equip - if (this === undefined || this.type !== sdk.unittype.Item || (!this.canEquip() && me.expansion)) return false; - bodyLoc === -1 && (bodyLoc = this.secondaryBodyLocation().first()); - // Already equipped in the right slot - if (this.mode === sdk.items.mode.Equipped && this.bodylocation === bodyLoc) return true; - // failed to open stash - if (this.isInStash && !Town.openStash()) return false; - // failed to open cube - if (this.isInCube && !Cubing.openCube()) return false; - - me.switchWeapons(1); - - for (let i = 0; i < 3; i += 1) { - if (this.toCursor()) { - clickItemAndWait(sdk.clicktypes.click.item.Left, bodyLoc - 7); - - if (this.bodylocation === bodyLoc - 7) { - if (getCursorType() === 3) { - let cursorItem = Game.getCursorUnit(); - !!cursorItem && !this.shouldKeep() && this.drop(); - } - - me.switchWeapons(0); - return true; - } - } - } - - me.weaponswitch !== 0 && me.switchWeapons(0); - - return false; + // can't equip + if (this === undefined || this.type !== sdk.unittype.Item || (!this.canEquip() && me.expansion)) return false; + bodyLoc === -1 && (bodyLoc = this.secondaryBodyLocation().first()); + // Already equipped in the right slot + if (this.mode === sdk.items.mode.Equipped && this.bodylocation === bodyLoc) return true; + // failed to open stash + if (this.isInStash && !Town.openStash()) return false; + // failed to open cube + if (this.isInCube && !Cubing.openCube()) return false; + + me.switchWeapons(1); + + for (let i = 0; i < 3; i += 1) { + if (this.toCursor()) { + clickItemAndWait(sdk.clicktypes.click.item.Left, bodyLoc - 7); + + if (this.bodylocation === bodyLoc - 7) { + if (getCursorType() === 3) { + let cursorItem = Game.getCursorUnit(); + !!cursorItem && !this.shouldKeep() && this.drop(); + } + + me.switchWeapons(0); + return true; + } + } + } + + me.weaponswitch !== 0 && me.switchWeapons(0); + + return false; }; Unit.prototype.equipMerc = function (bodyLoc = -1) { - // can't equip - if (this === undefined || this.type !== sdk.unittype.Item || !this.canEquipMerc()) return false; - bodyLoc === -1 && (bodyLoc = this.mercBodyLocation().first()); - // Already equipped in the right slot - if (this.mode === sdk.items.mode.Equipped && this.bodylocation === bodyLoc) return true; - // failed to open stash - if (this.isInStash && !Town.openStash()) return false; - // failed to open cube - if (this.isInCube && !Cubing.openCube()) return false; - - for (let i = 0; i < 3; i += 1) { - if (this.toCursor()) { - clickItemAndWait(sdk.clicktypes.click.item.Mercenary, bodyLoc); - - if (this.bodylocation === bodyLoc) { - if (getCursorType() === 3) { - let cursorItem = Game.getCursorUnit(); - !!cursorItem && !this.shouldKeep() && this.drop(); - } - - Developer.debugging.autoEquip && Item.logItem("Merc Equipped", me.getMerc().getItem(this.classid)); - Developer.logEquipped && MuleLogger.logEquippedItems(); - - return true; - } - } - } - - return false; + // can't equip + if (this === undefined || this.type !== sdk.unittype.Item || !this.canEquipMerc()) return false; + bodyLoc === -1 && (bodyLoc = this.mercBodyLocation().first()); + // Already equipped in the right slot + if (this.mode === sdk.items.mode.Equipped && this.bodylocation === bodyLoc) return true; + // failed to open stash + if (this.isInStash && !Town.openStash()) return false; + // failed to open cube + if (this.isInCube && !Cubing.openCube()) return false; + + for (let i = 0; i < 3; i += 1) { + if (this.toCursor()) { + clickItemAndWait(sdk.clicktypes.click.item.Mercenary, bodyLoc); + + if (this.bodylocation === bodyLoc) { + if (getCursorType() === 3) { + let cursorItem = Game.getCursorUnit(); + !!cursorItem && !this.shouldKeep() && this.drop(); + } + + Developer.debugging.autoEquip && Item.logItem("Merc Equipped", me.getMerc().getItem(this.classid)); + Developer.logEquipped && MuleLogger.logEquippedItems(); + + return true; + } + } + } + + return false; }; diff --git a/libs/SoloPlay/Functions/ItemUtilities.js b/libs/SoloPlay/Functions/ItemUtilities.js index 6760568e..78055ed7 100644 --- a/libs/SoloPlay/Functions/ItemUtilities.js +++ b/libs/SoloPlay/Functions/ItemUtilities.js @@ -7,545 +7,545 @@ includeIfNotIncluded("core/Item.js"); (function () { - /** - * @param {ItemUnit} item - * @param {*} buildInfo - * @returns {number} - * @todo clean this up (sigh) - 8/10/22 - update refactored alot, still think more can be done though - */ - const baseSkillsScore = (item, buildInfo) => { - buildInfo === undefined && (buildInfo = Check.currentBuild()); - let generalScore = 0; - let selectedWeights = [30, 20]; - let selectedSkills = [buildInfo.wantedSkills, buildInfo.usefulSkills]; - generalScore += item.getStatEx(sdk.stats.AddClassSkills, me.classid) * 200; // + class skills - generalScore += item.getStatEx(sdk.stats.AddSkillTab, buildInfo.tabSkills) * 100; // + TAB skills - todo handle array of tab skills - - for (let i = 0; i < selectedWeights.length; i++) { - for (let j = 0; j < selectedSkills.length; j++) { - for (let k = 0; k < selectedSkills[j].length; k++) { - generalScore += item.getStatEx(107, selectedSkills[j][k]) * selectedWeights[i]; - } - } - } - return generalScore; - }; - - /** - * @param {ItemUnit} base - * @param {boolean} verbose - * @returns {boolean} - */ - Item.betterBaseThanWearing = function (base, verbose = Developer.debugging.baseCheck) { - if (!base || !base.isBaseType) return false; - - let name = ""; - let itemsResists, baseResists, itemsTotalDmg, baseDmg, itemsDefense, baseDefense; - let baseSkillsTier, equippedSkillsTier; - let result = false, preSocketCheck = false; - let bodyLoc = Item.getBodyLoc(base); - - const checkNoSockets = (item) => [ - sdk.locale.items.AncientsPledge, sdk.locale.items.Exile, sdk.locale.items.Lore, sdk.locale.items.White, sdk.locale.items.Rhyme - ].includes(item.prefixnum) || (item.prefixnum === sdk.locale.items.Spirit && item.getItemType() === "Shield"); - const getRes = (item) => item.getStatEx(sdk.stats.FireResist) + item.getStatEx(sdk.stats.LightResist) + item.getStatEx(sdk.stats.ColdResist) + item.getStatEx(sdk.stats.PoisonResist); - const getDmg = (item) => item.getStatEx(sdk.stats.MinDamage) + item.getStatEx(sdk.stats.MaxDamage); - const getRealDmg = (item, maxED = 0, minOffset = 0, maxOffset = 0) => { - let ED = item.getStatEx(sdk.stats.EnhancedDamage); - ED > maxED && (ED = maxED); - let itemsMinDmg = Math.ceil(((item.getStatEx(sdk.stats.MinDamage) - minOffset) / ((ED + 100) / 100))); - let itemsMaxDmg = Math.ceil(((item.getStatEx(sdk.stats.MaxDamage) - maxOffset) / ((ED + 100) / 100))); - return (itemsMinDmg + itemsMaxDmg); - }; - const getDef = (item) => item.getStatEx(sdk.stats.Defense); - const getRealDef = (item, maxEDef) => { - let ED = item.getStatEx(sdk.stats.ArmorPercent); - ED > maxEDef && (ED = maxEDef); - return (Math.ceil((item.getStatEx(sdk.stats.Defense) / ((ED + 100) / 100)))); - }; - const resCheck = (baseResists, itemsResists) => { - verbose && console.log("ÿc9BetterThanWearingCheckÿc0 :: RW(" + name + ") BaseResists: " + baseResists + " EquippedItem: " + itemsResists); - return (baseResists > itemsResists); - }; - const defCheck = (itemsDefense, baseDefense) => { - verbose && console.log("ÿc9BetterThanWearingCheckÿc0 :: RW(" + name + ") BaseDefense: " + baseDefense + " EquippedItem: " + itemsDefense); - return (baseDefense > itemsDefense); - }; - const dmgCheck = (itemsTotalDmg, baseDmg) => { - verbose && console.log("ÿc9BetterThanWearingCheckÿc0 :: RW(" + name + ") BaseDamage: " + baseDmg + " EquippedItem: " + itemsTotalDmg); - return (baseDmg > itemsTotalDmg); - }; - - // @todo - betterThanMercUsing check for now just keep merc items - if ([sdk.items.type.Polearm, sdk.items.type.Spear].includes(base.itemType) || ([sdk.items.type.Armor].includes(base.itemType) && base.ethereal)) { - let merc = me.getMercEx(); - if (merc) { - bodyLoc = Item.getBodyLocMerc(base); - let eqItem = merc.getItemsEx().filter(i => i.isEquipped && bodyLoc.includes(i.bodylocation)); - if (!eqItem || !eqItem.runeword || NTIP.GetMercTier(eqItem) >= NTIP.MAX_TIER) return true; - name = getLocaleString(eqItem.prefixnum); - // todo logic checking before this to ensure we aren't keeping extra merc stuff - if (base.sockets === 0) return true; - switch (equippedItem.prefixnum) { - case sdk.locale.items.Insight: - [itemsTotalDmg, baseDmg] = [getRealDmg(equippedItem, 260), getDmg(base)]; - if (baseDmg !== itemsTotalDmg && dmgCheck(itemsTotalDmg, baseDmg)) return true; - - break; - case sdk.locale.items.Infinity: - [itemsTotalDmg, baseDmg] = [getRealDmg(equippedItem, 325), getDmg(base)]; - if (baseDmg !== itemsTotalDmg && dmgCheck(itemsTotalDmg, baseDmg)) return true; - - break; - case sdk.locale.items.Treachery: - [itemsDefense, baseDefense] = [getRealDef(equippedItem), getDef(base)]; - if (baseDefense !== itemsDefense && defCheck(itemsDefense, baseDefense)) return true; - - break; - default: - return true; - } - return false; - } - } - // Can't use so its worse then what we already have - if ((Check.finalBuild().maxStr < base.strreq || Check.finalBuild().maxDex < base.dexreq)) { - console.log("ÿc9BetterThanWearingCheckÿc0 :: " + base.name + " has to high stat requirments strReq: " + base.strreq + " dexReq " + base.dexreq); - return false; - } - // don't toss pb base crescent moon/HoJ/Grief - if (base.classid === sdk.items.PhaseBlade && [3, 4, 5].includes(base.sockets)) return true; - - let items = me.getItemsEx().filter(i => i.isEquipped && bodyLoc.includes(i.bodylocation)); - - for (let i = 0; i < bodyLoc.length; i++) { - let equippedItem = items.find(item => item.bodylocation === bodyLoc[i]); - if (!equippedItem || !equippedItem.runeword || NTIP.GetTier(equippedItem) >= NTIP.MAX_TIER) { - if (i === 0 && bodyLoc.length > 1) continue; - return true; - } - name = getLocaleString(equippedItem.prefixnum); - - preSocketCheck = checkNoSockets(equippedItem); - if (base.sockets === 0 && !preSocketCheck) return true; - - if (base.sockets === equippedItem.sockets || preSocketCheck) { - switch (equippedItem.prefixnum) { - case sdk.locale.items.AncientsPledge: - if (me.paladin) { - [itemsResists, baseResists] = [(getRes(equippedItem) - 187), getRes(base)]; - if (baseResists !== itemsResists && resCheck(baseResists, itemsResists)) return true; - } else { - [itemsDefense, baseDefense] = [getRealDef(equippedItem, 50), getDef(base)]; - if (baseDefense !== itemsDefense && defCheck(itemsDefense, baseDefense)) return true; - } - - break; - case sdk.locale.items.Black: - [itemsTotalDmg, baseDmg] = [getRealDmg(equippedItem, 120), getDmg(base)]; - if (baseDmg !== itemsTotalDmg && dmgCheck(itemsTotalDmg, baseDmg)) return true; - - break; - case sdk.locale.items.CrescentMoon: - [itemsTotalDmg, baseDmg] = [getRealDmg(equippedItem, 220), getDmg(base)]; - if (baseDmg !== itemsTotalDmg && dmgCheck(itemsTotalDmg, baseDmg)) return true; - - break; - case sdk.locale.items.Exile: - [itemsResists, baseResists] = [getRes(equippedItem), getRes(base)]; - if (baseResists !== itemsResists && resCheck(baseResists, itemsResists)) return true; - - break; - case sdk.locale.items.Honor: - [itemsTotalDmg, baseDmg] = [getRealDmg(equippedItem, 160, 9, 9), getDmg(base)]; - if (baseDmg !== itemsTotalDmg && dmgCheck(itemsTotalDmg, baseDmg)) return true; - - break; - case sdk.locale.items.KingsGrace: - [itemsTotalDmg, baseDmg] = [getRealDmg(equippedItem, 100), getDmg(base)]; - if (baseDmg !== itemsTotalDmg && dmgCheck(itemsTotalDmg, baseDmg)) return true; - - break; - case sdk.locale.items.Lawbringer: - [itemsTotalDmg, baseDmg] = [getRealDmg(equippedItem), getDmg(base)]; - if (baseDmg !== itemsTotalDmg && dmgCheck(itemsTotalDmg, baseDmg)) return true; - - break; - case sdk.locale.items.Lore: - [itemsDefense, baseDefense] = [getRealDef(equippedItem), getDef(base)]; - - if (me.barbarian || me.druid) { - // (PrimalHelms and Pelts) - [equippedSkillsTier, baseSkillsTier] = [baseSkillsScore(equippedItem), baseSkillsScore(base)]; - - verbose && console.log("ÿc9BetterThanWearingCheckÿc0 :: RW(Lore) EquippedSkillsTier: " + equippedSkillsTier + " BaseSkillsTier: " + baseSkillsTier); - if (equippedSkillsTier !== baseSkillsTier) { - // Might need to add some type of std deviation, having the skills is probably better but maybe not if in hell with a 50 defense helm - return (baseSkillsTier > equippedSkillsTier); - } else if (baseDefense !== itemsDefense) { - verbose && console.log("ÿc9BetterThanWearingCheckÿc0 :: RW(Lore) BaseDefense: " + baseDefense + " EquippedItem: " + itemsDefense); - return (baseDefense > itemsDefense); - } - } else { - if (baseDefense !== itemsDefense && defCheck(itemsDefense, baseDefense)) return true; - } - - break; - case sdk.locale.items.Malice: - [itemsTotalDmg, baseDmg] = [getRealDmg(equippedItem, 33, 9), getDmg(base)]; - - if (me.paladin) { - // Paladin TODO: See if its worth it to calculate the added damage skills would add - [equippedSkillsTier, baseSkillsTier] = [baseSkillsScore(equippedItem), baseSkillsScore(base)]; - } - - if (baseDmg !== itemsTotalDmg && dmgCheck(itemsTotalDmg, baseDmg)) return true; - - break; - case sdk.locale.items.Rhyme: - if (me.necromancer) { - [equippedSkillsTier, baseSkillsTier] = [baseSkillsScore(equippedItem), baseSkillsScore(base)]; - - if (equippedSkillsTier !== baseSkillsTier) { - verbose && console.log("ÿc9BetterThanWearingCheckÿc0 :: RW(Rhyme) EquippedSkillsTier: " + equippedSkillsTier + " BaseSkillsTier: " + baseSkillsTier); - // Might need to add some type of std deviation, having the skills is probably better but maybe not if in hell with a 50 defense shield - if (baseSkillsTier > equippedSkillsTier) return true; - } else if (equippedSkillsTier === baseSkillsTier) { - [itemsDefense, baseDefense] = [getRealDef(equippedItem), getDef(base)]; - if (baseDefense !== itemsDefense && defCheck(itemsDefense, baseDefense)) return true; - } - } else if (me.paladin) { - [itemsResists, baseResists] = [(getRes(equippedItem) - 100), getRes(base)]; - if (baseResists !== itemsResists && resCheck(baseResists, itemsResists)) return true; - } - - break; - case sdk.locale.items.Rift: - [itemsTotalDmg, baseDmg] = [getRealDmg(equippedItem), getDmg(base)]; - if (baseDmg !== itemsTotalDmg && dmgCheck(itemsTotalDmg, baseDmg)) return true; - - break; - case sdk.locale.items.Spirit: - if (!me.paladin || bodyLoc[i] !== sdk.body.LeftArm || base.getItemType() !== "Shield") return true; - - [itemsResists, baseResists] = [(getRes(equippedItem) - 115), getRes(base)]; - if (baseResists !== itemsResists && resCheck(baseResists, itemsResists)) return true; - - break; - case sdk.locale.items.Steel: - [itemsTotalDmg, baseDmg] = [getRealDmg(equippedItem, 20, 3, 3), getDmg(base)]; - if (baseDmg !== itemsTotalDmg && dmgCheck(itemsTotalDmg, baseDmg)) return true; - - break; - case sdk.locale.items.Strength: - [itemsTotalDmg, baseDmg] = [getRealDmg(equippedItem, 35, 9, 9), getDmg(base)]; - if (baseDmg !== itemsTotalDmg && dmgCheck(itemsTotalDmg, baseDmg)) return true; - - break; - case sdk.locale.items.White: - if (me.necromancer) { - [equippedSkillsTier, baseSkillsTier] = [(baseSkillsScore(equippedItem) - 550), baseSkillsScore(base)]; - - if (equippedSkillsTier !== baseSkillsTier) { - verbose && console.log("ÿc9BetterThanWearingCheckÿc0 :: RW(White) EquippedSkillsTier: " + equippedSkillsTier + " BaseSkillsTier: " + baseSkillsTier); - if (baseSkillsTier > equippedSkillsTier) return true; - } - } - - break; - case sdk.locale.items.Stone: - [itemsDefense, baseDefense] = [getRealDef(equippedItem, 290), getDef(base)]; - if (baseDefense !== itemsDefense && defCheck(itemsDefense, baseDefense)) return true; - - break; - case sdk.locale.items.Smoke: - [itemsDefense, baseDefense] = [getRealDef(equippedItem, 75), getDef(base)]; - if (baseDefense !== itemsDefense && defCheck(itemsDefense, baseDefense)) return true; - - break; - case sdk.locale.items.Prudence: - [itemsDefense, baseDefense] = [getRealDef(equippedItem, 170), getDef(base)]; - if (baseDefense !== itemsDefense && defCheck(itemsDefense, baseDefense)) return true; - - break; - case sdk.locale.items.Gloom: - [itemsDefense, baseDefense] = [getRealDef(equippedItem, 260), getDef(base)]; - if (baseDefense !== itemsDefense && defCheck(itemsDefense, baseDefense)) return true; - - break; - case sdk.locale.items.Fortitude: - [itemsDefense, baseDefense] = [getRealDef(equippedItem, 200), getDef(base)]; - if (baseDefense !== itemsDefense && defCheck(itemsDefense, baseDefense)) return true; - - break; - case sdk.locale.items.Enlightenment: - [itemsDefense, baseDefense] = [getRealDef(equippedItem, 30), getDef(base)]; - if (baseDefense !== itemsDefense && defCheck(itemsDefense, baseDefense)) return true; - - break; - case sdk.locale.items.Duress: - [itemsDefense, baseDefense] = [getRealDef(equippedItem, 200), getDef(base)]; - if (baseDefense !== itemsDefense && defCheck(itemsDefense, baseDefense)) return true; - - break; - case sdk.locale.items.ChainsofHonor: - [itemsDefense, baseDefense] = [getRealDef(equippedItem, 70), getDef(base)]; - if (baseDefense !== itemsDefense && defCheck(itemsDefense, baseDefense)) return true; - - break; - case sdk.locale.items.Bramble: - case sdk.locale.items.Bone: - case sdk.locale.items.Dragon: - case sdk.locale.items.Enigma: - case sdk.locale.items.Lionheart: - case sdk.locale.items.Myth: - case sdk.locale.items.Peace: - case sdk.locale.items.Principle: - case sdk.locale.items.Rain: - case sdk.locale.items.Stealth: - case sdk.locale.items.Treachery: - case sdk.locale.items.Wealth: - [itemsDefense, baseDefense] = [getRealDef(equippedItem), getDef(base)]; - if (baseDefense !== itemsDefense && defCheck(itemsDefense, baseDefense)) return true; - - break; - default: - // Runeword base isn't in the list, keep the base - return true; - } - } - } - - return result; - }; - - /** - * @param {ItemUnit} base - * @param {boolean} verbose - */ - Item.betterThanStashed = function (base, verbose) { - if (!base || base.quality > sdk.items.quality.Superior || base.isRuneword) return false; - if (base.sockets === 0 && getBaseStat("items", base.classid, "gemsockets") <= 1) return false; - if (base.sockets === 1) return false; - verbose === undefined && (verbose = Developer.debugging.baseCheck); - - const defenseScore = (item) => ({ - def: item.getStatEx(sdk.stats.Defense), - eDef: item.getStatEx(sdk.stats.ArmorPercent) - }); - - const dmgScore = (item) => ({ - dmg: Math.round((item.getStatEx(sdk.stats.MinDamagePercent) + item.getStatEx(sdk.stats.MaxDamagePercent)) / 2), - twoHandDmg: Math.round((item.getStatEx(sdk.stats.SecondaryMinDamage) + item.getStatEx(sdk.stats.SecondaryMaxDamage)) / 2), - eDmg: item.getStatEx(sdk.stats.EnhancedDamage) - }); - - const generalScore = (item) => { - const buildInfo = Check.currentBuild(); - let generalScore = baseSkillsScore(item, buildInfo); - - if (generalScore === 0) { - // should take into account other skills that would be helpful for the current build - me.paladin && (generalScore += item.getStatEx(sdk.stats.FireResist) * 2); - generalScore += item.getStatEx(sdk.stats.Defense) * 0.5; - - if (!buildInfo.caster) { - let dmg = dmgScore(item); - generalScore += (dmg.dmg + dmg.eDmg); - } - } - - return generalScore; - }; - - /** - * @todo - better comparison for socketed items to unsocketed items - * - should compare items with same sockets - * - should compare some items (wands, voodooheads, primalhelms, pelts) with no sockets to ones with sockets - * - should be able to compare regular items to class items and take into account the max amount of sockets an item can have - * - */ - function getItemToCompare (itemtypes = [], eth = null, sort = (a, b) => a - b) { - return me.getItemsEx(-1, sdk.items.mode.inStorage) - .filter(item => - itemtypes.includes(item.itemType) - && ((item.sockets === base.sockets) || (item.sockets > base.sockets)) - && (eth === null || item.ethereal === eth)) - .sort(sort) - .last(); - } - - /** - * @param {ItemUnit} a - * @param {ItemUnit} b - */ - const defenseSort = (a, b) => { - let [aDef, bDef] = [a.getStatEx(sdk.stats.Defense), b.getStatEx(sdk.stats.Defense)]; - if (aDef !== bDef) return aDef - bDef; - return a.getStatEx(sdk.stats.ArmorPercent) - b.getStatEx(sdk.stats.ArmorPercent); - }; - - /** - * @param {ItemUnit} a - * @param {ItemUnit} b - */ - const generalScoreSort = (a, b) => { - let [aScore, bScore] = [generalScore(a), generalScore(b)]; - if (aScore !== bScore) return aScore - bScore; - let [aSoc, bSoc] = [a.sockets, b.sockets]; - if (aSoc !== bSoc) return aSoc - bSoc; - if (a.getItemType() !== "Weapon" && aScore === bScore) { - let [aDef, bDef] = [a.getStatEx(sdk.stats.Defense), b.getStatEx(sdk.stats.Defense)]; - if (aDef !== bDef) return aDef - bDef; - if (aDef === bDef) a.getStatEx(sdk.stats.ArmorPercent) - b.getStatEx(sdk.stats.ArmorPercent); - } - return a.sockets - b.sockets; - }; - - /** - * @param {ItemUnit} a - * @param {ItemUnit} b - */ - const twoHandDmgSort = (a, b) => { - let [aDmg, bDmg] = [dmgScore(a), dmgScore(b)]; - if (aDmg.twoHandDmg !== bDmg.twoHandDmg) return aDmg.twoHandDmg - bDmg.twoHandDmg; - return aDmg.eDmg - bDmg.eDmg; - }; - - const defenseScoreCheck = (base, itemToCheck) => { - let [defScoreBase, defScoreItem] = [defenseScore(base), defenseScore(itemToCheck)]; - verbose && console.log("ÿc9betterThanStashedÿc0 :: BaseScore: ", defScoreBase, " itemToCheckScore: ", defScoreItem); - if (defScoreBase.def > defScoreItem.def || (defScoreBase.def === defScoreItem.def && (defScoreBase.eDef > defScoreItem.eDef || base.ilvl > itemToCheck.ilvl))) { - return true; - } - return false; - }; - - const damageScoreCheck = (base, itemToCheck) => { - let [gScoreBase, gScoreCheck] = [generalScore(base), generalScore(itemToCheck)]; - verbose && console.log("ÿc9betterThanStashedÿc0 :: BaseScore: " + generalScore(base) + " itemToCheckScore: " + generalScore(itemToCheck)); - switch (true) { - case (gScoreBase > gScoreCheck || (gScoreBase === gScoreCheck && base.ilvl > itemToCheck.ilvl)): - case (me.barbarian && (gScoreBase === gScoreCheck && Item.getQuantityOwned(base) < 2)): - case (me.assassin && !Check.currentBuild().caster && (gScoreBase === gScoreCheck && Item.getQuantityOwned(base) < 2)): - return true; - } - return false; - }; - - const generalScoreCheck = (base, itemToCheck) => { - let [gScoreBase, gScoreCheck] = [generalScore(base), generalScore(itemToCheck)]; - verbose && console.log("ÿc9betterThanStashedÿc0 :: BaseScore: " + generalScore(base) + " itemToCheckScore: " + generalScore(itemToCheck)); - if (gScoreBase > gScoreCheck) return true; - if (base.getItemType() === "Shield" && gScoreBase === gScoreCheck) return defenseScoreCheck(base, itemToCheck); - return false; - }; - - let checkItem; - - switch (base.itemType) { - case sdk.items.type.Shield: - case sdk.items.type.AuricShields: - case sdk.items.type.VoodooHeads: - if (me.paladin || me.necromancer) { - let iType = [sdk.items.type.Shield]; - me.necromancer ? iType.push(sdk.items.type.VoodooHeads) : iType.push(sdk.items.type.AuricShields); - - checkItem = getItemToCompare(iType, false, generalScoreSort); - if (checkItem === undefined || checkItem.gid === base.gid) return true; - - return (base.isInStorage ? generalScoreCheck(base, checkItem) : false); - } - - if (base.ethereal || base.sockets === 0) return false; - checkItem = getItemToCompare([sdk.items.type.Shield], false, defenseSort); - if (checkItem === undefined || checkItem.gid === base.gid) return true; - - return (base.isInStorage ? defenseScoreCheck(base, checkItem) : false); - case sdk.items.type.Armor: - if (base.ethereal || base.sockets === 0) return false; - checkItem = getItemToCompare([sdk.items.type.Armor], false, defenseSort); - if (checkItem === undefined || checkItem.gid === base.gid) return true; - - return (base.isInStorage ? defenseScoreCheck(base, checkItem) : false); - case sdk.items.type.Helm: - case sdk.items.type.PrimalHelm: - case sdk.items.type.Circlet: - case sdk.items.type.Pelt: - if (me.barbarian || me.druid) { - let iType = [sdk.items.type.Helm, sdk.items.type.Circlet]; - me.druid ? iType.push(sdk.items.type.Pelt) : iType.push(sdk.items.type.PrimalHelm); - - checkItem = getItemToCompare(iType, false, generalScoreSort); - if (checkItem === undefined || checkItem.gid === base.gid) return true; - - return (base.isInStorage ? generalScoreCheck(base, checkItem) : false); - } - - if (base.ethereal || base.sockets === 0) return false; - checkItem = getItemToCompare([sdk.items.type.Helm, sdk.items.type.Circlet], false, defenseSort); - if (checkItem === undefined || checkItem.gid === base.gid) return true; - - return (base.isInStorage ? defenseScoreCheck(base, checkItem) : false); - case sdk.items.type.Wand: - if (!me.necromancer) return false; - - checkItem = getItemToCompare([sdk.items.type.Wand], null, generalScoreSort); - if (checkItem === undefined || checkItem.gid === base.gid) return true; - return (base.isInStorage ? generalScoreCheck(base, checkItem) : false); - case sdk.items.type.Scepter: - case sdk.items.type.Staff: - case sdk.items.type.Bow: - case sdk.items.type.Axe: - case sdk.items.type.Club: - case sdk.items.type.Sword: - case sdk.items.type.Hammer: - case sdk.items.type.Knife: - case sdk.items.type.Spear: - case sdk.items.type.Crossbow: - case sdk.items.type.Mace: - case sdk.items.type.Orb: - case sdk.items.type.AmazonBow: - case sdk.items.type.AmazonSpear: - // don't toss grief base - // Can't use so it's worse then what we already have - // todo - need better solution to know what the max stats are for our current build and wanted final build - // update - 8/8/2022 - checks final build stat requirements but still need a better check - // don't keep an item if we are only going to be able to use it when we get to our final build but also sometimes like paladin making grief - // we need the item to get to our final build but won't actually be able to use it till then so we can't just use max current build str/dex - if ((Check.finalBuild().maxStr < base.strreq || Check.finalBuild().maxDex < base.dexreq)) return false; - // need better solution for comparison based on what runeword can be made in a base type - // should allow comparing multiple item types given they are all for the same runeword - checkItem = getItemToCompare([base.itemType], false, generalScoreSort); - if (checkItem === undefined || checkItem.gid === base.gid) return true; - - return (base.isInStorage ? damageScoreCheck(base, checkItem) : false); - case sdk.items.type.HandtoHand: - case sdk.items.type.AssassinClaw: - if (!me.assassin) return false; - - checkItem = getItemToCompare([sdk.items.type.HandtoHand, sdk.items.type.AssassinClaw], false, generalScoreSort); - if (checkItem === undefined || checkItem.gid === base.gid) return true; - - return (base.isInStorage ? damageScoreCheck(base, checkItem) : false); - case sdk.items.type.Polearm: - checkItem = getItemToCompare([sdk.items.type.Polearm], null, twoHandDmgSort); - if (checkItem === undefined || checkItem.gid === base.gid) return true; - - if (base.isInStorage && base.sockets > 0) { - let [baseDmg, checkItemDmg] = [dmgScore(base), dmgScore(checkItem)]; - switch (true) { - case (baseDmg.twoHandDmg > checkItemDmg.twoHandDmg): - case ((baseDmg.twoHandDmg === checkItemDmg.twoHandDmg) && (baseDmg.eDmg > checkItemDmg.eDmg)): - case ((baseDmg.twoHandDmg === checkItemDmg.twoHandDmg) && (baseDmg.eDmg === checkItemDmg.eDmg) && base.ilvl > checkItem.ilvl): - verbose && console.log("ÿc9betterThanStashedÿc0 :: BaseScore: ", baseDmg, " itemToCheckScore: ", checkItemDmg); - return true; - } - } - - break; - } - - return false; - }; + /** + * @param {ItemUnit} item + * @param {*} buildInfo + * @returns {number} + * @todo clean this up (sigh) - 8/10/22 - update refactored alot, still think more can be done though + */ + const baseSkillsScore = (item, buildInfo) => { + buildInfo === undefined && (buildInfo = Check.currentBuild()); + let generalScore = 0; + let selectedWeights = [30, 20]; + let selectedSkills = [buildInfo.wantedSkills, buildInfo.usefulSkills]; + generalScore += item.getStatEx(sdk.stats.AddClassSkills, me.classid) * 200; // + class skills + generalScore += item.getStatEx(sdk.stats.AddSkillTab, buildInfo.tabSkills) * 100; // + TAB skills - todo handle array of tab skills + + for (let i = 0; i < selectedWeights.length; i++) { + for (let j = 0; j < selectedSkills.length; j++) { + for (let k = 0; k < selectedSkills[j].length; k++) { + generalScore += item.getStatEx(107, selectedSkills[j][k]) * selectedWeights[i]; + } + } + } + return generalScore; + }; + + /** + * @param {ItemUnit} base + * @param {boolean} verbose + * @returns {boolean} + */ + Item.betterBaseThanWearing = function (base, verbose = Developer.debugging.baseCheck) { + if (!base || !base.isBaseType) return false; + + let name = ""; + let itemsResists, baseResists, itemsTotalDmg, baseDmg, itemsDefense, baseDefense; + let baseSkillsTier, equippedSkillsTier; + let result = false, preSocketCheck = false; + let bodyLoc = Item.getBodyLoc(base); + + const checkNoSockets = (item) => [ + sdk.locale.items.AncientsPledge, sdk.locale.items.Exile, sdk.locale.items.Lore, sdk.locale.items.White, sdk.locale.items.Rhyme + ].includes(item.prefixnum) || (item.prefixnum === sdk.locale.items.Spirit && item.getItemType() === "Shield"); + const getRes = (item) => item.getStatEx(sdk.stats.FireResist) + item.getStatEx(sdk.stats.LightResist) + item.getStatEx(sdk.stats.ColdResist) + item.getStatEx(sdk.stats.PoisonResist); + const getDmg = (item) => item.getStatEx(sdk.stats.MinDamage) + item.getStatEx(sdk.stats.MaxDamage); + const getRealDmg = (item, maxED = 0, minOffset = 0, maxOffset = 0) => { + let ED = item.getStatEx(sdk.stats.EnhancedDamage); + ED > maxED && (ED = maxED); + let itemsMinDmg = Math.ceil(((item.getStatEx(sdk.stats.MinDamage) - minOffset) / ((ED + 100) / 100))); + let itemsMaxDmg = Math.ceil(((item.getStatEx(sdk.stats.MaxDamage) - maxOffset) / ((ED + 100) / 100))); + return (itemsMinDmg + itemsMaxDmg); + }; + const getDef = (item) => item.getStatEx(sdk.stats.Defense); + const getRealDef = (item, maxEDef) => { + let ED = item.getStatEx(sdk.stats.ArmorPercent); + ED > maxEDef && (ED = maxEDef); + return (Math.ceil((item.getStatEx(sdk.stats.Defense) / ((ED + 100) / 100)))); + }; + const resCheck = (baseResists, itemsResists) => { + verbose && console.log("ÿc9BetterThanWearingCheckÿc0 :: RW(" + name + ") BaseResists: " + baseResists + " EquippedItem: " + itemsResists); + return (baseResists > itemsResists); + }; + const defCheck = (itemsDefense, baseDefense) => { + verbose && console.log("ÿc9BetterThanWearingCheckÿc0 :: RW(" + name + ") BaseDefense: " + baseDefense + " EquippedItem: " + itemsDefense); + return (baseDefense > itemsDefense); + }; + const dmgCheck = (itemsTotalDmg, baseDmg) => { + verbose && console.log("ÿc9BetterThanWearingCheckÿc0 :: RW(" + name + ") BaseDamage: " + baseDmg + " EquippedItem: " + itemsTotalDmg); + return (baseDmg > itemsTotalDmg); + }; + + // @todo - betterThanMercUsing check for now just keep merc items + if ([sdk.items.type.Polearm, sdk.items.type.Spear].includes(base.itemType) || ([sdk.items.type.Armor].includes(base.itemType) && base.ethereal)) { + let merc = me.getMercEx(); + if (merc) { + bodyLoc = Item.getBodyLocMerc(base); + let eqItem = merc.getItemsEx().filter(i => i.isEquipped && bodyLoc.includes(i.bodylocation)); + if (!eqItem || !eqItem.runeword || NTIP.GetMercTier(eqItem) >= NTIP.MAX_TIER) return true; + name = getLocaleString(eqItem.prefixnum); + // todo logic checking before this to ensure we aren't keeping extra merc stuff + if (base.sockets === 0) return true; + switch (equippedItem.prefixnum) { + case sdk.locale.items.Insight: + [itemsTotalDmg, baseDmg] = [getRealDmg(equippedItem, 260), getDmg(base)]; + if (baseDmg !== itemsTotalDmg && dmgCheck(itemsTotalDmg, baseDmg)) return true; + + break; + case sdk.locale.items.Infinity: + [itemsTotalDmg, baseDmg] = [getRealDmg(equippedItem, 325), getDmg(base)]; + if (baseDmg !== itemsTotalDmg && dmgCheck(itemsTotalDmg, baseDmg)) return true; + + break; + case sdk.locale.items.Treachery: + [itemsDefense, baseDefense] = [getRealDef(equippedItem), getDef(base)]; + if (baseDefense !== itemsDefense && defCheck(itemsDefense, baseDefense)) return true; + + break; + default: + return true; + } + return false; + } + } + // Can't use so its worse then what we already have + if ((Check.finalBuild().maxStr < base.strreq || Check.finalBuild().maxDex < base.dexreq)) { + console.log("ÿc9BetterThanWearingCheckÿc0 :: " + base.name + " has to high stat requirments strReq: " + base.strreq + " dexReq " + base.dexreq); + return false; + } + // don't toss pb base crescent moon/HoJ/Grief + if (base.classid === sdk.items.PhaseBlade && [3, 4, 5].includes(base.sockets)) return true; + + let items = me.getItemsEx().filter(i => i.isEquipped && bodyLoc.includes(i.bodylocation)); + + for (let i = 0; i < bodyLoc.length; i++) { + let equippedItem = items.find(item => item.bodylocation === bodyLoc[i]); + if (!equippedItem || !equippedItem.runeword || NTIP.GetTier(equippedItem) >= NTIP.MAX_TIER) { + if (i === 0 && bodyLoc.length > 1) continue; + return true; + } + name = getLocaleString(equippedItem.prefixnum); + + preSocketCheck = checkNoSockets(equippedItem); + if (base.sockets === 0 && !preSocketCheck) return true; + + if (base.sockets === equippedItem.sockets || preSocketCheck) { + switch (equippedItem.prefixnum) { + case sdk.locale.items.AncientsPledge: + if (me.paladin) { + [itemsResists, baseResists] = [(getRes(equippedItem) - 187), getRes(base)]; + if (baseResists !== itemsResists && resCheck(baseResists, itemsResists)) return true; + } else { + [itemsDefense, baseDefense] = [getRealDef(equippedItem, 50), getDef(base)]; + if (baseDefense !== itemsDefense && defCheck(itemsDefense, baseDefense)) return true; + } + + break; + case sdk.locale.items.Black: + [itemsTotalDmg, baseDmg] = [getRealDmg(equippedItem, 120), getDmg(base)]; + if (baseDmg !== itemsTotalDmg && dmgCheck(itemsTotalDmg, baseDmg)) return true; + + break; + case sdk.locale.items.CrescentMoon: + [itemsTotalDmg, baseDmg] = [getRealDmg(equippedItem, 220), getDmg(base)]; + if (baseDmg !== itemsTotalDmg && dmgCheck(itemsTotalDmg, baseDmg)) return true; + + break; + case sdk.locale.items.Exile: + [itemsResists, baseResists] = [getRes(equippedItem), getRes(base)]; + if (baseResists !== itemsResists && resCheck(baseResists, itemsResists)) return true; + + break; + case sdk.locale.items.Honor: + [itemsTotalDmg, baseDmg] = [getRealDmg(equippedItem, 160, 9, 9), getDmg(base)]; + if (baseDmg !== itemsTotalDmg && dmgCheck(itemsTotalDmg, baseDmg)) return true; + + break; + case sdk.locale.items.KingsGrace: + [itemsTotalDmg, baseDmg] = [getRealDmg(equippedItem, 100), getDmg(base)]; + if (baseDmg !== itemsTotalDmg && dmgCheck(itemsTotalDmg, baseDmg)) return true; + + break; + case sdk.locale.items.Lawbringer: + [itemsTotalDmg, baseDmg] = [getRealDmg(equippedItem), getDmg(base)]; + if (baseDmg !== itemsTotalDmg && dmgCheck(itemsTotalDmg, baseDmg)) return true; + + break; + case sdk.locale.items.Lore: + [itemsDefense, baseDefense] = [getRealDef(equippedItem), getDef(base)]; + + if (me.barbarian || me.druid) { + // (PrimalHelms and Pelts) + [equippedSkillsTier, baseSkillsTier] = [baseSkillsScore(equippedItem), baseSkillsScore(base)]; + + verbose && console.log("ÿc9BetterThanWearingCheckÿc0 :: RW(Lore) EquippedSkillsTier: " + equippedSkillsTier + " BaseSkillsTier: " + baseSkillsTier); + if (equippedSkillsTier !== baseSkillsTier) { + // Might need to add some type of std deviation, having the skills is probably better but maybe not if in hell with a 50 defense helm + return (baseSkillsTier > equippedSkillsTier); + } else if (baseDefense !== itemsDefense) { + verbose && console.log("ÿc9BetterThanWearingCheckÿc0 :: RW(Lore) BaseDefense: " + baseDefense + " EquippedItem: " + itemsDefense); + return (baseDefense > itemsDefense); + } + } else { + if (baseDefense !== itemsDefense && defCheck(itemsDefense, baseDefense)) return true; + } + + break; + case sdk.locale.items.Malice: + [itemsTotalDmg, baseDmg] = [getRealDmg(equippedItem, 33, 9), getDmg(base)]; + + if (me.paladin) { + // Paladin TODO: See if its worth it to calculate the added damage skills would add + [equippedSkillsTier, baseSkillsTier] = [baseSkillsScore(equippedItem), baseSkillsScore(base)]; + } + + if (baseDmg !== itemsTotalDmg && dmgCheck(itemsTotalDmg, baseDmg)) return true; + + break; + case sdk.locale.items.Rhyme: + if (me.necromancer) { + [equippedSkillsTier, baseSkillsTier] = [baseSkillsScore(equippedItem), baseSkillsScore(base)]; + + if (equippedSkillsTier !== baseSkillsTier) { + verbose && console.log("ÿc9BetterThanWearingCheckÿc0 :: RW(Rhyme) EquippedSkillsTier: " + equippedSkillsTier + " BaseSkillsTier: " + baseSkillsTier); + // Might need to add some type of std deviation, having the skills is probably better but maybe not if in hell with a 50 defense shield + if (baseSkillsTier > equippedSkillsTier) return true; + } else if (equippedSkillsTier === baseSkillsTier) { + [itemsDefense, baseDefense] = [getRealDef(equippedItem), getDef(base)]; + if (baseDefense !== itemsDefense && defCheck(itemsDefense, baseDefense)) return true; + } + } else if (me.paladin) { + [itemsResists, baseResists] = [(getRes(equippedItem) - 100), getRes(base)]; + if (baseResists !== itemsResists && resCheck(baseResists, itemsResists)) return true; + } + + break; + case sdk.locale.items.Rift: + [itemsTotalDmg, baseDmg] = [getRealDmg(equippedItem), getDmg(base)]; + if (baseDmg !== itemsTotalDmg && dmgCheck(itemsTotalDmg, baseDmg)) return true; + + break; + case sdk.locale.items.Spirit: + if (!me.paladin || bodyLoc[i] !== sdk.body.LeftArm || base.getItemType() !== "Shield") return true; + + [itemsResists, baseResists] = [(getRes(equippedItem) - 115), getRes(base)]; + if (baseResists !== itemsResists && resCheck(baseResists, itemsResists)) return true; + + break; + case sdk.locale.items.Steel: + [itemsTotalDmg, baseDmg] = [getRealDmg(equippedItem, 20, 3, 3), getDmg(base)]; + if (baseDmg !== itemsTotalDmg && dmgCheck(itemsTotalDmg, baseDmg)) return true; + + break; + case sdk.locale.items.Strength: + [itemsTotalDmg, baseDmg] = [getRealDmg(equippedItem, 35, 9, 9), getDmg(base)]; + if (baseDmg !== itemsTotalDmg && dmgCheck(itemsTotalDmg, baseDmg)) return true; + + break; + case sdk.locale.items.White: + if (me.necromancer) { + [equippedSkillsTier, baseSkillsTier] = [(baseSkillsScore(equippedItem) - 550), baseSkillsScore(base)]; + + if (equippedSkillsTier !== baseSkillsTier) { + verbose && console.log("ÿc9BetterThanWearingCheckÿc0 :: RW(White) EquippedSkillsTier: " + equippedSkillsTier + " BaseSkillsTier: " + baseSkillsTier); + if (baseSkillsTier > equippedSkillsTier) return true; + } + } + + break; + case sdk.locale.items.Stone: + [itemsDefense, baseDefense] = [getRealDef(equippedItem, 290), getDef(base)]; + if (baseDefense !== itemsDefense && defCheck(itemsDefense, baseDefense)) return true; + + break; + case sdk.locale.items.Smoke: + [itemsDefense, baseDefense] = [getRealDef(equippedItem, 75), getDef(base)]; + if (baseDefense !== itemsDefense && defCheck(itemsDefense, baseDefense)) return true; + + break; + case sdk.locale.items.Prudence: + [itemsDefense, baseDefense] = [getRealDef(equippedItem, 170), getDef(base)]; + if (baseDefense !== itemsDefense && defCheck(itemsDefense, baseDefense)) return true; + + break; + case sdk.locale.items.Gloom: + [itemsDefense, baseDefense] = [getRealDef(equippedItem, 260), getDef(base)]; + if (baseDefense !== itemsDefense && defCheck(itemsDefense, baseDefense)) return true; + + break; + case sdk.locale.items.Fortitude: + [itemsDefense, baseDefense] = [getRealDef(equippedItem, 200), getDef(base)]; + if (baseDefense !== itemsDefense && defCheck(itemsDefense, baseDefense)) return true; + + break; + case sdk.locale.items.Enlightenment: + [itemsDefense, baseDefense] = [getRealDef(equippedItem, 30), getDef(base)]; + if (baseDefense !== itemsDefense && defCheck(itemsDefense, baseDefense)) return true; + + break; + case sdk.locale.items.Duress: + [itemsDefense, baseDefense] = [getRealDef(equippedItem, 200), getDef(base)]; + if (baseDefense !== itemsDefense && defCheck(itemsDefense, baseDefense)) return true; + + break; + case sdk.locale.items.ChainsofHonor: + [itemsDefense, baseDefense] = [getRealDef(equippedItem, 70), getDef(base)]; + if (baseDefense !== itemsDefense && defCheck(itemsDefense, baseDefense)) return true; + + break; + case sdk.locale.items.Bramble: + case sdk.locale.items.Bone: + case sdk.locale.items.Dragon: + case sdk.locale.items.Enigma: + case sdk.locale.items.Lionheart: + case sdk.locale.items.Myth: + case sdk.locale.items.Peace: + case sdk.locale.items.Principle: + case sdk.locale.items.Rain: + case sdk.locale.items.Stealth: + case sdk.locale.items.Treachery: + case sdk.locale.items.Wealth: + [itemsDefense, baseDefense] = [getRealDef(equippedItem), getDef(base)]; + if (baseDefense !== itemsDefense && defCheck(itemsDefense, baseDefense)) return true; + + break; + default: + // Runeword base isn't in the list, keep the base + return true; + } + } + } + + return result; + }; + + /** + * @param {ItemUnit} base + * @param {boolean} verbose + */ + Item.betterThanStashed = function (base, verbose) { + if (!base || base.quality > sdk.items.quality.Superior || base.isRuneword) return false; + if (base.sockets === 0 && getBaseStat("items", base.classid, "gemsockets") <= 1) return false; + if (base.sockets === 1) return false; + verbose === undefined && (verbose = Developer.debugging.baseCheck); + + const defenseScore = (item) => ({ + def: item.getStatEx(sdk.stats.Defense), + eDef: item.getStatEx(sdk.stats.ArmorPercent) + }); + + const dmgScore = (item) => ({ + dmg: Math.round((item.getStatEx(sdk.stats.MinDamagePercent) + item.getStatEx(sdk.stats.MaxDamagePercent)) / 2), + twoHandDmg: Math.round((item.getStatEx(sdk.stats.SecondaryMinDamage) + item.getStatEx(sdk.stats.SecondaryMaxDamage)) / 2), + eDmg: item.getStatEx(sdk.stats.EnhancedDamage) + }); + + const generalScore = (item) => { + const buildInfo = Check.currentBuild(); + let generalScore = baseSkillsScore(item, buildInfo); + + if (generalScore === 0) { + // should take into account other skills that would be helpful for the current build + me.paladin && (generalScore += item.getStatEx(sdk.stats.FireResist) * 2); + generalScore += item.getStatEx(sdk.stats.Defense) * 0.5; + + if (!buildInfo.caster) { + let dmg = dmgScore(item); + generalScore += (dmg.dmg + dmg.eDmg); + } + } + + return generalScore; + }; + + /** + * @todo - better comparison for socketed items to unsocketed items + * - should compare items with same sockets + * - should compare some items (wands, voodooheads, primalhelms, pelts) with no sockets to ones with sockets + * - should be able to compare regular items to class items and take into account the max amount of sockets an item can have + * + */ + function getItemToCompare (itemtypes = [], eth = null, sort = (a, b) => a - b) { + return me.getItemsEx(-1, sdk.items.mode.inStorage) + .filter(item => + itemtypes.includes(item.itemType) + && ((item.sockets === base.sockets) || (item.sockets > base.sockets)) + && (eth === null || item.ethereal === eth)) + .sort(sort) + .last(); + } + + /** + * @param {ItemUnit} a + * @param {ItemUnit} b + */ + const defenseSort = (a, b) => { + let [aDef, bDef] = [a.getStatEx(sdk.stats.Defense), b.getStatEx(sdk.stats.Defense)]; + if (aDef !== bDef) return aDef - bDef; + return a.getStatEx(sdk.stats.ArmorPercent) - b.getStatEx(sdk.stats.ArmorPercent); + }; + + /** + * @param {ItemUnit} a + * @param {ItemUnit} b + */ + const generalScoreSort = (a, b) => { + let [aScore, bScore] = [generalScore(a), generalScore(b)]; + if (aScore !== bScore) return aScore - bScore; + let [aSoc, bSoc] = [a.sockets, b.sockets]; + if (aSoc !== bSoc) return aSoc - bSoc; + if (a.getItemType() !== "Weapon" && aScore === bScore) { + let [aDef, bDef] = [a.getStatEx(sdk.stats.Defense), b.getStatEx(sdk.stats.Defense)]; + if (aDef !== bDef) return aDef - bDef; + if (aDef === bDef) a.getStatEx(sdk.stats.ArmorPercent) - b.getStatEx(sdk.stats.ArmorPercent); + } + return a.sockets - b.sockets; + }; + + /** + * @param {ItemUnit} a + * @param {ItemUnit} b + */ + const twoHandDmgSort = (a, b) => { + let [aDmg, bDmg] = [dmgScore(a), dmgScore(b)]; + if (aDmg.twoHandDmg !== bDmg.twoHandDmg) return aDmg.twoHandDmg - bDmg.twoHandDmg; + return aDmg.eDmg - bDmg.eDmg; + }; + + const defenseScoreCheck = (base, itemToCheck) => { + let [defScoreBase, defScoreItem] = [defenseScore(base), defenseScore(itemToCheck)]; + verbose && console.log("ÿc9betterThanStashedÿc0 :: BaseScore: ", defScoreBase, " itemToCheckScore: ", defScoreItem); + if (defScoreBase.def > defScoreItem.def || (defScoreBase.def === defScoreItem.def && (defScoreBase.eDef > defScoreItem.eDef || base.ilvl > itemToCheck.ilvl))) { + return true; + } + return false; + }; + + const damageScoreCheck = (base, itemToCheck) => { + let [gScoreBase, gScoreCheck] = [generalScore(base), generalScore(itemToCheck)]; + verbose && console.log("ÿc9betterThanStashedÿc0 :: BaseScore: " + generalScore(base) + " itemToCheckScore: " + generalScore(itemToCheck)); + switch (true) { + case (gScoreBase > gScoreCheck || (gScoreBase === gScoreCheck && base.ilvl > itemToCheck.ilvl)): + case (me.barbarian && (gScoreBase === gScoreCheck && Item.getQuantityOwned(base) < 2)): + case (me.assassin && !Check.currentBuild().caster && (gScoreBase === gScoreCheck && Item.getQuantityOwned(base) < 2)): + return true; + } + return false; + }; + + const generalScoreCheck = (base, itemToCheck) => { + let [gScoreBase, gScoreCheck] = [generalScore(base), generalScore(itemToCheck)]; + verbose && console.log("ÿc9betterThanStashedÿc0 :: BaseScore: " + generalScore(base) + " itemToCheckScore: " + generalScore(itemToCheck)); + if (gScoreBase > gScoreCheck) return true; + if (base.getItemType() === "Shield" && gScoreBase === gScoreCheck) return defenseScoreCheck(base, itemToCheck); + return false; + }; + + let checkItem; + + switch (base.itemType) { + case sdk.items.type.Shield: + case sdk.items.type.AuricShields: + case sdk.items.type.VoodooHeads: + if (me.paladin || me.necromancer) { + let iType = [sdk.items.type.Shield]; + me.necromancer ? iType.push(sdk.items.type.VoodooHeads) : iType.push(sdk.items.type.AuricShields); + + checkItem = getItemToCompare(iType, false, generalScoreSort); + if (checkItem === undefined || checkItem.gid === base.gid) return true; + + return (base.isInStorage ? generalScoreCheck(base, checkItem) : false); + } + + if (base.ethereal || base.sockets === 0) return false; + checkItem = getItemToCompare([sdk.items.type.Shield], false, defenseSort); + if (checkItem === undefined || checkItem.gid === base.gid) return true; + + return (base.isInStorage ? defenseScoreCheck(base, checkItem) : false); + case sdk.items.type.Armor: + if (base.ethereal || base.sockets === 0) return false; + checkItem = getItemToCompare([sdk.items.type.Armor], false, defenseSort); + if (checkItem === undefined || checkItem.gid === base.gid) return true; + + return (base.isInStorage ? defenseScoreCheck(base, checkItem) : false); + case sdk.items.type.Helm: + case sdk.items.type.PrimalHelm: + case sdk.items.type.Circlet: + case sdk.items.type.Pelt: + if (me.barbarian || me.druid) { + let iType = [sdk.items.type.Helm, sdk.items.type.Circlet]; + me.druid ? iType.push(sdk.items.type.Pelt) : iType.push(sdk.items.type.PrimalHelm); + + checkItem = getItemToCompare(iType, false, generalScoreSort); + if (checkItem === undefined || checkItem.gid === base.gid) return true; + + return (base.isInStorage ? generalScoreCheck(base, checkItem) : false); + } + + if (base.ethereal || base.sockets === 0) return false; + checkItem = getItemToCompare([sdk.items.type.Helm, sdk.items.type.Circlet], false, defenseSort); + if (checkItem === undefined || checkItem.gid === base.gid) return true; + + return (base.isInStorage ? defenseScoreCheck(base, checkItem) : false); + case sdk.items.type.Wand: + if (!me.necromancer) return false; + + checkItem = getItemToCompare([sdk.items.type.Wand], null, generalScoreSort); + if (checkItem === undefined || checkItem.gid === base.gid) return true; + return (base.isInStorage ? generalScoreCheck(base, checkItem) : false); + case sdk.items.type.Scepter: + case sdk.items.type.Staff: + case sdk.items.type.Bow: + case sdk.items.type.Axe: + case sdk.items.type.Club: + case sdk.items.type.Sword: + case sdk.items.type.Hammer: + case sdk.items.type.Knife: + case sdk.items.type.Spear: + case sdk.items.type.Crossbow: + case sdk.items.type.Mace: + case sdk.items.type.Orb: + case sdk.items.type.AmazonBow: + case sdk.items.type.AmazonSpear: + // don't toss grief base + // Can't use so it's worse then what we already have + // todo - need better solution to know what the max stats are for our current build and wanted final build + // update - 8/8/2022 - checks final build stat requirements but still need a better check + // don't keep an item if we are only going to be able to use it when we get to our final build but also sometimes like paladin making grief + // we need the item to get to our final build but won't actually be able to use it till then so we can't just use max current build str/dex + if ((Check.finalBuild().maxStr < base.strreq || Check.finalBuild().maxDex < base.dexreq)) return false; + // need better solution for comparison based on what runeword can be made in a base type + // should allow comparing multiple item types given they are all for the same runeword + checkItem = getItemToCompare([base.itemType], false, generalScoreSort); + if (checkItem === undefined || checkItem.gid === base.gid) return true; + + return (base.isInStorage ? damageScoreCheck(base, checkItem) : false); + case sdk.items.type.HandtoHand: + case sdk.items.type.AssassinClaw: + if (!me.assassin) return false; + + checkItem = getItemToCompare([sdk.items.type.HandtoHand, sdk.items.type.AssassinClaw], false, generalScoreSort); + if (checkItem === undefined || checkItem.gid === base.gid) return true; + + return (base.isInStorage ? damageScoreCheck(base, checkItem) : false); + case sdk.items.type.Polearm: + checkItem = getItemToCompare([sdk.items.type.Polearm], null, twoHandDmgSort); + if (checkItem === undefined || checkItem.gid === base.gid) return true; + + if (base.isInStorage && base.sockets > 0) { + let [baseDmg, checkItemDmg] = [dmgScore(base), dmgScore(checkItem)]; + switch (true) { + case (baseDmg.twoHandDmg > checkItemDmg.twoHandDmg): + case ((baseDmg.twoHandDmg === checkItemDmg.twoHandDmg) && (baseDmg.eDmg > checkItemDmg.eDmg)): + case ((baseDmg.twoHandDmg === checkItemDmg.twoHandDmg) && (baseDmg.eDmg === checkItemDmg.eDmg) && base.ilvl > checkItem.ilvl): + verbose && console.log("ÿc9betterThanStashedÿc0 :: BaseScore: ", baseDmg, " itemToCheckScore: ", checkItemDmg); + return true; + } + } + + break; + } + + return false; + }; })(); diff --git a/libs/SoloPlay/Functions/LoaderOverrides.js b/libs/SoloPlay/Functions/LoaderOverrides.js index 66a4f133..d6e07b3d 100644 --- a/libs/SoloPlay/Functions/LoaderOverrides.js +++ b/libs/SoloPlay/Functions/LoaderOverrides.js @@ -9,169 +9,169 @@ includeIfNotIncluded("core/Loader.js"); Loader.getScripts = function () { - let fileList = dopen("libs/SoloPlay/Scripts").getFiles(); + let fileList = dopen("libs/SoloPlay/Scripts").getFiles(); - for (let i = 0; i < fileList.length; i += 1) { - if (fileList[i].indexOf(".js") > -1) { - this.fileList.push(fileList[i].substring(0, fileList[i].indexOf(".js"))); - } - } + for (let i = 0; i < fileList.length; i += 1) { + if (fileList[i].indexOf(".js") > -1) { + this.fileList.push(fileList[i].substring(0, fileList[i].indexOf(".js"))); + } + } }; Loader.scriptName = function (offset = 0) { - let index = this.scriptIndex + offset; + let index = this.scriptIndex + offset; - if (index >= 0 && index < SoloIndex.scripts.length) { - return SoloIndex.scripts[index]; - } + if (index >= 0 && index < SoloIndex.scripts.length) { + return SoloIndex.scripts[index]; + } - return "SoloPlay"; + return "SoloPlay"; }; /** * @deprecated Loader.run is used instead */ Loader.loadScripts = function () { - return Loader.run(); + return Loader.run(); }; Loader.run = function () { - let updatedDifficulty = Check.nextDifficulty(); - if (updatedDifficulty) { - CharData.updateData("me", "setDifficulty", updatedDifficulty); - !me.realm && Messaging.sendToScript("D2BotSoloPlay.dbj", "diffChange"); - } - - for (this.scriptIndex = 0; this.scriptIndex < SoloIndex.scripts.length; this.scriptIndex++) { - !me.inTown && Town.goToTown(); - Check.checkSpecialCase(); - const scriptName = SoloIndex.scripts[this.scriptIndex]; - - if (SoloIndex.index[scriptName] !== undefined && SoloIndex.index[scriptName].shouldRun()) { - let j; - let tick; - let currentExp; - - try { - includeIfNotIncluded("SoloPlay/Scripts/" + scriptName + ".js"); - - tick = getTickCount(); - currentExp = me.getStat(sdk.stats.Experience); - Messaging.sendToScript("libs/SoloPlay/Threads/ToolsThread.js", JSON.stringify({ currScript: scriptName })); - DataFile.updateStats("lastScript", scriptName); - - for (j = 0; j < 5; j += 1) { - if (global[scriptName]()) { - break; - } - } - - (j === 5) && myPrint("script " + scriptName + " failed."); - } catch (e) { - console.warn("ÿc8Kolbot-SoloPlayÿc0: ", (typeof e === "object" ? e.message : e)); - console.error(e); - } finally { - SoloIndex.doneList.push(scriptName); - // skip logging if we didn't actually finish it - !SoloIndex.retryList.includes(scriptName) && Developer.logPerformance && Tracker.script(tick, scriptName, currentExp); - console.log("ÿc8Kolbot-SoloPlayÿc0: Old maxgametime: " + Time.format(me.maxgametime)); - me.maxgametime += (getTickCount() - tick); - console.log("ÿc8Kolbot-SoloPlayÿc0: New maxgametime: " + Time.format(me.maxgametime)); - console.log("ÿc8Kolbot-SoloPlayÿc0 :: ÿc8" + scriptName + "ÿc0 - ÿc7Duration: ÿc0" + Time.format(getTickCount() - tick)); - - // remove script function from function scope, so it can be cleared by GC - if (this.scriptIndex < SoloIndex.scripts.length) { - delete global[scriptName]; - } - } - - if (me.sorceress && me.hell && scriptName === "bloodraven" && me.charlvl < 68) { - console.debug("End-run, we are not ready to keep pushing yet"); - - break; - } - - if (me.dead) { - // not sure how we got here but we are dead, why did toolsthread not quit lets check it - let tThread = getScript("libs/SoloPlay/Threads/ToolsThread.js"); - if (!tThread || !tThread.running) { - // well that explains why, toolsthread seems to have crashed lets restart it so we quit properly - load("libs/SoloPlay/Threads/ToolsThread.js"); - } - } - } - } - - // Re-check to see if after this run we now meet difficulty requirments - if (!updatedDifficulty) { - updatedDifficulty = Check.nextDifficulty(false); - if (updatedDifficulty) { - CharData.updateData("me", "setDifficulty", updatedDifficulty); - !me.realm && Messaging.sendToScript("D2BotSoloPlay.dbj", "diffChange"); - } - } - - return true; + let updatedDifficulty = Check.nextDifficulty(); + if (updatedDifficulty) { + CharData.updateData("me", "setDifficulty", updatedDifficulty); + !me.realm && Messaging.sendToScript("D2BotSoloPlay.dbj", "diffChange"); + } + + for (this.scriptIndex = 0; this.scriptIndex < SoloIndex.scripts.length; this.scriptIndex++) { + !me.inTown && Town.goToTown(); + Check.checkSpecialCase(); + const scriptName = SoloIndex.scripts[this.scriptIndex]; + + if (SoloIndex.index[scriptName] !== undefined && SoloIndex.index[scriptName].shouldRun()) { + let j; + let tick; + let currentExp; + + try { + includeIfNotIncluded("SoloPlay/Scripts/" + scriptName + ".js"); + + tick = getTickCount(); + currentExp = me.getStat(sdk.stats.Experience); + Messaging.sendToScript("libs/SoloPlay/Threads/ToolsThread.js", JSON.stringify({ currScript: scriptName })); + DataFile.updateStats("lastScript", scriptName); + + for (j = 0; j < 5; j += 1) { + if (global[scriptName]()) { + break; + } + } + + (j === 5) && myPrint("script " + scriptName + " failed."); + } catch (e) { + console.warn("ÿc8Kolbot-SoloPlayÿc0: ", (typeof e === "object" ? e.message : e)); + console.error(e); + } finally { + SoloIndex.doneList.push(scriptName); + // skip logging if we didn't actually finish it + !SoloIndex.retryList.includes(scriptName) && Developer.logPerformance && Tracker.script(tick, scriptName, currentExp); + console.log("ÿc8Kolbot-SoloPlayÿc0: Old maxgametime: " + Time.format(me.maxgametime)); + me.maxgametime += (getTickCount() - tick); + console.log("ÿc8Kolbot-SoloPlayÿc0: New maxgametime: " + Time.format(me.maxgametime)); + console.log("ÿc8Kolbot-SoloPlayÿc0 :: ÿc8" + scriptName + "ÿc0 - ÿc7Duration: ÿc0" + Time.format(getTickCount() - tick)); + + // remove script function from function scope, so it can be cleared by GC + if (this.scriptIndex < SoloIndex.scripts.length) { + delete global[scriptName]; + } + } + + if (me.sorceress && me.hell && scriptName === "bloodraven" && me.charlvl < 68) { + console.debug("End-run, we are not ready to keep pushing yet"); + + break; + } + + if (me.dead) { + // not sure how we got here but we are dead, why did toolsthread not quit lets check it + let tThread = getScript("libs/SoloPlay/Threads/ToolsThread.js"); + if (!tThread || !tThread.running) { + // well that explains why, toolsthread seems to have crashed lets restart it so we quit properly + load("libs/SoloPlay/Threads/ToolsThread.js"); + } + } + } + } + + // Re-check to see if after this run we now meet difficulty requirments + if (!updatedDifficulty) { + updatedDifficulty = Check.nextDifficulty(false); + if (updatedDifficulty) { + CharData.updateData("me", "setDifficulty", updatedDifficulty); + !me.realm && Messaging.sendToScript("D2BotSoloPlay.dbj", "diffChange"); + } + } + + return true; }; Loader.runScript = function (script, configOverride) { - let tick; - let currentExp; - let failed = false; - let reconfiguration, unmodifiedConfig = {}; - let mainScript = this.scriptName(); - - function buildScriptMsg () { - let str = "ÿc9" + mainScript + " ÿc0:: "; - - if (Loader.tempList.length && Loader.tempList[0] !== mainScript) { - Loader.tempList.forEach(s => str += "ÿc9" + s + " ÿc0:: "); - } - - return str; - } - - this.copy(Config, unmodifiedConfig); - - if (includeIfNotIncluded("SoloPlay/Scripts/" + script + ".js")) { - try { - if (typeof (global[script]) !== "function") throw new Error("Invalid script function name"); - if (this.skipTown.includes(script) || Town.goToTown()) { - let mainScriptStr = (mainScript !== script ? buildScriptMsg() : ""); - this.tempList.push(script); - console.log(mainScriptStr + "ÿc2Starting script: ÿc9" + script); - Messaging.sendToScript("libs/SoloPlay/Threads/ToolsThread.js", JSON.stringify({ currScript: script })); - DataFile.updateStats("lastScript", script); - - if (typeof configOverride === "function") { - reconfiguration = true; - configOverride(); - } - - tick = getTickCount(); - currentExp = me.getStat(sdk.stats.Experience); - - if (global[script]()) { - console.log(mainScriptStr + "ÿc7" + script + " :: ÿc0Complete ÿc0- ÿc7Duration: ÿc0" + (Time.format(getTickCount() - tick))); - } - } - } catch (e) { - failed = true; - console.warn("ÿc8Kolbot-SoloPlayÿc0: " + (e.message ? e.message : e)); - } finally { - SoloIndex.doneList.push(script); - Developer.logPerformance && Tracker.script(tick, script, currentExp); - delete global[script]; - this.tempList.pop(); - - if (reconfiguration) { - console.log("ÿc2Reverting back unmodified config properties."); - this.copy(unmodifiedConfig, Config); - } - } - } else { - console.warn("Failed to include: " + script); - } - - return !failed; + let tick; + let currentExp; + let failed = false; + let reconfiguration, unmodifiedConfig = {}; + let mainScript = this.scriptName(); + + function buildScriptMsg () { + let str = "ÿc9" + mainScript + " ÿc0:: "; + + if (Loader.tempList.length && Loader.tempList[0] !== mainScript) { + Loader.tempList.forEach(s => str += "ÿc9" + s + " ÿc0:: "); + } + + return str; + } + + this.copy(Config, unmodifiedConfig); + + if (includeIfNotIncluded("SoloPlay/Scripts/" + script + ".js")) { + try { + if (typeof (global[script]) !== "function") throw new Error("Invalid script function name"); + if (this.skipTown.includes(script) || Town.goToTown()) { + let mainScriptStr = (mainScript !== script ? buildScriptMsg() : ""); + this.tempList.push(script); + console.log(mainScriptStr + "ÿc2Starting script: ÿc9" + script); + Messaging.sendToScript("libs/SoloPlay/Threads/ToolsThread.js", JSON.stringify({ currScript: script })); + DataFile.updateStats("lastScript", script); + + if (typeof configOverride === "function") { + reconfiguration = true; + configOverride(); + } + + tick = getTickCount(); + currentExp = me.getStat(sdk.stats.Experience); + + if (global[script]()) { + console.log(mainScriptStr + "ÿc7" + script + " :: ÿc0Complete ÿc0- ÿc7Duration: ÿc0" + (Time.format(getTickCount() - tick))); + } + } + } catch (e) { + failed = true; + console.warn("ÿc8Kolbot-SoloPlayÿc0: " + (e.message ? e.message : e)); + } finally { + SoloIndex.doneList.push(script); + Developer.logPerformance && Tracker.script(tick, script, currentExp); + delete global[script]; + this.tempList.pop(); + + if (reconfiguration) { + console.log("ÿc2Reverting back unmodified config properties."); + this.copy(unmodifiedConfig, Config); + } + } + } else { + console.warn("Failed to include: " + script); + } + + return !failed; }; diff --git a/libs/SoloPlay/Functions/Me.js b/libs/SoloPlay/Functions/Me.js index de2486d9..af6d1de7 100644 --- a/libs/SoloPlay/Functions/Me.js +++ b/libs/SoloPlay/Functions/Me.js @@ -11,457 +11,457 @@ includeIfNotIncluded("core/Prototypes.js"); * @description me prototypes for soloplay with checks to ensure forwards compatibility */ if (!me.hasOwnProperty("maxNearMonsters")) { - Object.defineProperty(me, "maxNearMonsters", { - get: function () { - return Math.floor((4 * (1 / me.hpmax * me.hp)) + 1); - } - }); + Object.defineProperty(me, "maxNearMonsters", { + get: function () { + return Math.floor((4 * (1 / me.hpmax * me.hp)) + 1); + } + }); } if (!me.hasOwnProperty("dualWielding")) { - Object.defineProperty(me, "dualWielding", { - get: function () { - // only classes that can duel wield - if (!me.assassin && !me.barbarian) return false; - let items = me.getItemsEx().filter((item) => item.isEquipped && item.isOnMain); - return !!items.length && items.length >= 2 && items.every((item) => !item.isShield && !getBaseStat("items", item.classid, "block")); - } - }); + Object.defineProperty(me, "dualWielding", { + get: function () { + // only classes that can duel wield + if (!me.assassin && !me.barbarian) return false; + let items = me.getItemsEx().filter((item) => item.isEquipped && item.isOnMain); + return !!items.length && items.length >= 2 && items.every((item) => !item.isShield && !getBaseStat("items", item.classid, "block")); + } + }); } if (!me.hasOwnProperty("realFR")) { - Object.defineProperty(me, "realFR", { - get: function () { - return me.getStat(sdk.stats.FireResist); - } - }); + Object.defineProperty(me, "realFR", { + get: function () { + return me.getStat(sdk.stats.FireResist); + } + }); } if (!me.hasOwnProperty("realCR")) { - Object.defineProperty(me, "realCR", { - get: function () { - return me.getStat(sdk.stats.ColdResist); - } - }); + Object.defineProperty(me, "realCR", { + get: function () { + return me.getStat(sdk.stats.ColdResist); + } + }); } if (!me.hasOwnProperty("realLR")) { - Object.defineProperty(me, "realLR", { - get: function () { - return me.getStat(sdk.stats.LightResist); - } - }); + Object.defineProperty(me, "realLR", { + get: function () { + return me.getStat(sdk.stats.LightResist); + } + }); } if (!me.hasOwnProperty("realPR")) { - Object.defineProperty(me, "realPR", { - get: function () { - return me.getStat(sdk.stats.PoisonResist); - } - }); + Object.defineProperty(me, "realPR", { + get: function () { + return me.getStat(sdk.stats.PoisonResist); + } + }); } if (!me.hasOwnProperty("FR")) { - Object.defineProperty(me, "FR", { - get: function () { - return Math.min(75 + this.getStat(sdk.stats.MaxFireResist), me.realFR - me.resPenalty); - } - }); + Object.defineProperty(me, "FR", { + get: function () { + return Math.min(75 + this.getStat(sdk.stats.MaxFireResist), me.realFR - me.resPenalty); + } + }); } if (!me.hasOwnProperty("CR")) { - Object.defineProperty(me, "CR", { - get: function () { - return Math.min(75 + this.getStat(sdk.stats.MaxColdResist), me.realCR - me.resPenalty); - } - }); + Object.defineProperty(me, "CR", { + get: function () { + return Math.min(75 + this.getStat(sdk.stats.MaxColdResist), me.realCR - me.resPenalty); + } + }); } if (!me.hasOwnProperty("LR")) { - Object.defineProperty(me, "LR", { - get: function () { - return Math.min(75 + this.getStat(sdk.stats.MaxLightResist), me.realLR - me.resPenalty); - } - }); + Object.defineProperty(me, "LR", { + get: function () { + return Math.min(75 + this.getStat(sdk.stats.MaxLightResist), me.realLR - me.resPenalty); + } + }); } if (!me.hasOwnProperty("PR")) { - Object.defineProperty(me, "PR", { - get: function () { - return Math.min(75 + this.getStat(sdk.stats.MaxPoisonResist), me.realPR - me.resPenalty); - } - }); + Object.defineProperty(me, "PR", { + get: function () { + return Math.min(75 + this.getStat(sdk.stats.MaxPoisonResist), me.realPR - me.resPenalty); + } + }); } if (!me.hasOwnProperty("className")) { - Object.defineProperty(me, "className", { - get: function () { - return sdk.player.class.nameOf(me.classid); - } - }); + Object.defineProperty(me, "className", { + get: function () { + return sdk.player.class.nameOf(me.classid); + } + }); } /** * Soloplay specific but might as well keep with the format */ if (!me.hasOwnProperty("onFinalBuild")) { - Object.defineProperty(me, "onFinalBuild", { - get: function () { - return me.data.currentBuild === me.data.finalBuild; - } - }); + Object.defineProperty(me, "onFinalBuild", { + get: function () { + return me.data.currentBuild === me.data.finalBuild; + } + }); } if (!me.hasOwnProperty("mercid")) { - Object.defineProperty(me, "mercid", { - get: function () { - return me.data.merc.classid || (() => { - let merc = me.getMercEx(); - if (!merc) return 0; - me.data.merc.classid = merc.classid; - return merc.classid; - })(); - } - }); + Object.defineProperty(me, "mercid", { + get: function () { + return me.data.merc.classid || (() => { + let merc = me.getMercEx(); + if (!merc) return 0; + me.data.merc.classid = merc.classid; + return merc.classid; + })(); + } + }); } if (!me.hasOwnProperty("trueStr")) { - Object.defineProperty(me, "trueStr", { - get: function () { - return me.data.strength || (() => { - let str = me.rawStrength; - me.data.strength = str; - return str; - })(); - } - }); + Object.defineProperty(me, "trueStr", { + get: function () { + return me.data.strength || (() => { + let str = me.rawStrength; + me.data.strength = str; + return str; + })(); + } + }); } if (!me.hasOwnProperty("trueDex")) { - Object.defineProperty(me, "trueDex", { - get: function () { - return me.data.dexterity || (() => { - let dex = me.rawDexterity; - me.data.dexterity = dex; - return dex; - })(); - } - }); + Object.defineProperty(me, "trueDex", { + get: function () { + return me.data.dexterity || (() => { + let dex = me.rawDexterity; + me.data.dexterity = dex; + return dex; + })(); + } + }); } if (!me.hasOwnProperty("finalBuild")) { - let _finalBuild = null; - - Object.defineProperty(me, "finalBuild", { - get: function () { - if (_finalBuild) return _finalBuild; - let className = me.className.toLowerCase(); - let build = (["Bumper", "Socketmule", "Imbuemule"].includes(SetUp.finalBuild) - ? ["Javazon", "Cold", "Bone", "Hammerdin", "Whirlwind", "Wind", "Trapsin"][me.classid] - : SetUp.finalBuild) + "Build"; - _finalBuild = require("../BuildFiles/" + className + "/" + className + "." + build); - return _finalBuild; - }, - set: function (v) { - if (v.hasOwnProperty("AutoBuildTemplate")) { - // Object.assign(this.finalBuild, v); - _finalBuild = v; - } - }, - }); + let _finalBuild = null; + + Object.defineProperty(me, "finalBuild", { + get: function () { + if (_finalBuild) return _finalBuild; + let className = me.className.toLowerCase(); + let build = (["Bumper", "Socketmule", "Imbuemule"].includes(SetUp.finalBuild) + ? ["Javazon", "Cold", "Bone", "Hammerdin", "Whirlwind", "Wind", "Trapsin"][me.classid] + : SetUp.finalBuild) + "Build"; + _finalBuild = require("../BuildFiles/" + className + "/" + className + "." + build); + return _finalBuild; + }, + set: function (v) { + if (v.hasOwnProperty("AutoBuildTemplate")) { + // Object.assign(this.finalBuild, v); + _finalBuild = v; + } + }, + }); } if (!me.hasOwnProperty("currentBuild")) { - let _currentBuild = null; - - Object.defineProperty(me, "currentBuild", { - get: function () { - if (_currentBuild) return _currentBuild; - let className = me.className.toLowerCase(); - let build = SetUp.currentBuild + "Build"; - _currentBuild = require("../BuildFiles/" + className + "/" + className + "." + build); - return _currentBuild; - }, - set: function (v) { - if (v.hasOwnProperty("AutoBuildTemplate")) { - // Object.assign(this.currentBuild, v); - _currentBuild = v; - } - }, - }); + let _currentBuild = null; + + Object.defineProperty(me, "currentBuild", { + get: function () { + if (_currentBuild) return _currentBuild; + let className = me.className.toLowerCase(); + let build = SetUp.currentBuild + "Build"; + _currentBuild = require("../BuildFiles/" + className + "/" + className + "." + build); + return _currentBuild; + }, + set: function (v) { + if (v.hasOwnProperty("AutoBuildTemplate")) { + // Object.assign(this.currentBuild, v); + _currentBuild = v; + } + }, + }); } if (!me.hasOwnProperty("data")) { - let _data = null; - - Object.defineProperty(me, "data", { - get: function () { - if (_data) return _data; - _data = CharData.getStats(); - // handle if it was an old data file - if (!_data.hasOwnProperty("startTime")) { - let oldData = copyObj(_data); - _data = CharData.create(); - Object.assign(_data, oldData.me); - Object.assign(_data.merc, oldData.merc); - if (oldData.merc.hasOwnProperty("type")) { - _data.merc.skillName = oldData.merc.type; - _data.merc.skill = MercData.findByName(_data.merc.skillName, _data.merc.act).skill; - } - } - return _data; - }, - set: function (v) { - if (v.hasOwnProperty("startTime")) { - _data = v; - } - }, - }); - - Object.defineProperty(me, "update", { - value: function () { - let obj = JSON.stringify(copyObj(me.data)); - let myThread = getScript(true).name; - CharData.threads.forEach(function (script) { - let curr = getScript(script); - if (curr && myThread !== curr.name) { - curr.send("data--" + obj); - } - }); - }, - }); + let _data = null; + + Object.defineProperty(me, "data", { + get: function () { + if (_data) return _data; + _data = CharData.getStats(); + // handle if it was an old data file + if (!_data.hasOwnProperty("startTime")) { + let oldData = copyObj(_data); + _data = CharData.create(); + Object.assign(_data, oldData.me); + Object.assign(_data.merc, oldData.merc); + if (oldData.merc.hasOwnProperty("type")) { + _data.merc.skillName = oldData.merc.type; + _data.merc.skill = MercData.findByName(_data.merc.skillName, _data.merc.act).skill; + } + } + return _data; + }, + set: function (v) { + if (v.hasOwnProperty("startTime")) { + _data = v; + } + }, + }); + + Object.defineProperty(me, "update", { + value: function () { + let obj = JSON.stringify(copyObj(me.data)); + let myThread = getScript(true).name; + CharData.threads.forEach(function (script) { + let curr = getScript(script); + if (curr && myThread !== curr.name) { + curr.send("data--" + obj); + } + }); + }, + }); } if (!me.hasOwnProperty("equipped")) { - me.equipped = (function () { - const defaultsMap = new Map([ - ["gid", -1], - ["classid", -1], - ["name", ""], - ["code", ""], - ["fname", ""], - ["quality", -1], - ["ethereal", false], - ["itemType", -1], - ["strreq", -1], - ["dexreq", -1], - ["lvlreq", -1], - ["sockets", -1], - ["prefixnum", -1], - ["suffixnum", -1], - ]); - - /** - * @constructor - * @param {ItemUnit} item - */ - function EquippedItem (item) { - /** @type {ItemUnit} */ - this._item = (item instanceof Unit) - ? copyUnit(item) - : null; - this._gid = item ? item.gid : -1; - this._bodylocation = item ? item.bodylocation : -1; - - return new Proxy(this, { - get: function (target, prop) { - if (prop in target) { - return target[prop]; - } - - if (target._item && prop in target._item) { - return target._item[prop]; - } - - return defaultsMap.get(prop); - } - }); - } - - Object.defineProperties(EquippedItem.prototype, { - location: { - /** @this EquippedItem */ - get: function () { - return this._item ? this._item.bodylocation : -1; - }, - }, - durability: { - /** @this EquippedItem */ - get: function () { - return this._item ? this._item.durabilityPercent : -1; - }, - }, - tier: { - /** @this EquippedItem */ - get: function () { - return this._item ? NTIP.GetTier(this._item) : -1; - }, - }, - tierScore: { - /** @this EquippedItem */ - get: function () { - return this._item ? tierscore(this._item, 1, this._bodylocation) : -1; - }, - }, - secondaryTier: { - /** @this EquippedItem */ - get: function () { - return this._item ? NTIP.GetSecondaryTier(this._item) : -1; - }, - }, - socketed: { - /** @this EquippedItem */ - get: function () { - return this._item ? this._item.getItemsEx().length > 0 : false; - }, - }, - }); - - EquippedItem.prototype.twoHandedCheck = function (strict = false) { - return this._item - ? strict - ? this._item.strictlyTwoHanded - : this._item.twoHanded - : false; - }; - - EquippedItem.prototype.getStat = function (stat, subid) { - return this._item ? this._item.getStat(stat, subid) : -1; - }; - - EquippedItem.prototype.getStatEx = function (stat, subid) { - return this._item ? this._item.getStatEx(stat, subid) : -1; - }; - - /** @type {Map} */ - const bodyMap = new Map(); - bodyMap.set(sdk.body.Head, new EquippedItem()); - bodyMap.set(sdk.body.Neck, new EquippedItem()); - bodyMap.set(sdk.body.Armor, new EquippedItem()); - bodyMap.set(sdk.body.RightArm, new EquippedItem()); - bodyMap.set(sdk.body.LeftArm, new EquippedItem()); - bodyMap.set(sdk.body.Gloves, new EquippedItem()); - bodyMap.set(sdk.body.RingRight, new EquippedItem()); - bodyMap.set(sdk.body.RingLeft, new EquippedItem()); - bodyMap.set(sdk.body.Belt, new EquippedItem()); - bodyMap.set(sdk.body.Feet, new EquippedItem()); - bodyMap.set(sdk.body.RightArmSecondary, new EquippedItem()); - bodyMap.set(sdk.body.LeftArmSecondary, new EquippedItem()); - - // const _dummy = new EquippedItem(); - - return { - /** - * @param {number} bodylocation - * @returns {EquippedItem} - */ - get: function (bodylocation) { - // if (!bodyMap.has(bodylocation)) return _dummy; - let item = bodyMap.get(bodylocation); - if (item._gid === -1 || (item._gid !== item.gid || (item._bodylocation !== item.location && me.weaponswitch !== sdk.player.slot.Secondary))) { - // item has changed - find the new item - let newItem = me.getItemsEx() - .filter((el) => el.isEquipped && el.bodylocation === bodylocation) - .first(); - bodyMap.set(bodylocation, new EquippedItem(newItem)); - } - return bodyMap.get(bodylocation); - }, - /** - * @param {number} bodylocation - * @returns {boolean} - */ - has: function (bodylocation) { - return bodyMap.has(bodylocation); - }, - /** - * @param {number} bodylocation - * @param {ItemUnit} item - */ - set: function (bodylocation, item) { - if (bodyMap.has(bodylocation) && item instanceof Unit) { - bodyMap.set(bodylocation, new EquippedItem(item)); - } - }, - /** - * @description Initializes the equipped item map with the items currently equipped - */ - init: function () { - me.getItemsEx() - .filter(item => item.isEquipped) - .forEach(item => bodyMap.set(item.bodylocation, new EquippedItem(item))); - }, - }; - })(); + me.equipped = (function () { + const defaultsMap = new Map([ + ["gid", -1], + ["classid", -1], + ["name", ""], + ["code", ""], + ["fname", ""], + ["quality", -1], + ["ethereal", false], + ["itemType", -1], + ["strreq", -1], + ["dexreq", -1], + ["lvlreq", -1], + ["sockets", -1], + ["prefixnum", -1], + ["suffixnum", -1], + ]); + + /** + * @constructor + * @param {ItemUnit} item + */ + function EquippedItem (item) { + /** @type {ItemUnit} */ + this._item = (item instanceof Unit) + ? copyUnit(item) + : null; + this._gid = item ? item.gid : -1; + this._bodylocation = item ? item.bodylocation : -1; + + return new Proxy(this, { + get: function (target, prop) { + if (prop in target) { + return target[prop]; + } + + if (target._item && prop in target._item) { + return target._item[prop]; + } + + return defaultsMap.get(prop); + } + }); + } + + Object.defineProperties(EquippedItem.prototype, { + location: { + /** @this EquippedItem */ + get: function () { + return this._item ? this._item.bodylocation : -1; + }, + }, + durability: { + /** @this EquippedItem */ + get: function () { + return this._item ? this._item.durabilityPercent : -1; + }, + }, + tier: { + /** @this EquippedItem */ + get: function () { + return this._item ? NTIP.GetTier(this._item) : -1; + }, + }, + tierScore: { + /** @this EquippedItem */ + get: function () { + return this._item ? tierscore(this._item, 1, this._bodylocation) : -1; + }, + }, + secondaryTier: { + /** @this EquippedItem */ + get: function () { + return this._item ? NTIP.GetSecondaryTier(this._item) : -1; + }, + }, + socketed: { + /** @this EquippedItem */ + get: function () { + return this._item ? this._item.getItemsEx().length > 0 : false; + }, + }, + }); + + EquippedItem.prototype.twoHandedCheck = function (strict = false) { + return this._item + ? strict + ? this._item.strictlyTwoHanded + : this._item.twoHanded + : false; + }; + + EquippedItem.prototype.getStat = function (stat, subid) { + return this._item ? this._item.getStat(stat, subid) : -1; + }; + + EquippedItem.prototype.getStatEx = function (stat, subid) { + return this._item ? this._item.getStatEx(stat, subid) : -1; + }; + + /** @type {Map} */ + const bodyMap = new Map(); + bodyMap.set(sdk.body.Head, new EquippedItem()); + bodyMap.set(sdk.body.Neck, new EquippedItem()); + bodyMap.set(sdk.body.Armor, new EquippedItem()); + bodyMap.set(sdk.body.RightArm, new EquippedItem()); + bodyMap.set(sdk.body.LeftArm, new EquippedItem()); + bodyMap.set(sdk.body.Gloves, new EquippedItem()); + bodyMap.set(sdk.body.RingRight, new EquippedItem()); + bodyMap.set(sdk.body.RingLeft, new EquippedItem()); + bodyMap.set(sdk.body.Belt, new EquippedItem()); + bodyMap.set(sdk.body.Feet, new EquippedItem()); + bodyMap.set(sdk.body.RightArmSecondary, new EquippedItem()); + bodyMap.set(sdk.body.LeftArmSecondary, new EquippedItem()); + + // const _dummy = new EquippedItem(); + + return { + /** + * @param {number} bodylocation + * @returns {EquippedItem} + */ + get: function (bodylocation) { + // if (!bodyMap.has(bodylocation)) return _dummy; + let item = bodyMap.get(bodylocation); + if (item._gid === -1 || (item._gid !== item.gid || (item._bodylocation !== item.location && me.weaponswitch !== sdk.player.slot.Secondary))) { + // item has changed - find the new item + let newItem = me.getItemsEx() + .filter((el) => el.isEquipped && el.bodylocation === bodylocation) + .first(); + bodyMap.set(bodylocation, new EquippedItem(newItem)); + } + return bodyMap.get(bodylocation); + }, + /** + * @param {number} bodylocation + * @returns {boolean} + */ + has: function (bodylocation) { + return bodyMap.has(bodylocation); + }, + /** + * @param {number} bodylocation + * @param {ItemUnit} item + */ + set: function (bodylocation, item) { + if (bodyMap.has(bodylocation) && item instanceof Unit) { + bodyMap.set(bodylocation, new EquippedItem(item)); + } + }, + /** + * @description Initializes the equipped item map with the items currently equipped + */ + init: function () { + me.getItemsEx() + .filter(item => item.isEquipped) + .forEach(item => bodyMap.set(item.bodylocation, new EquippedItem(item))); + }, + }; + })(); } /** @returns {boolean} */ me.canTpToTown = function () { - // can't tp if dead - or not currently enabled to - if (me.dead || SoloEvents.townChicken.disabled) return false; - const myArea = me.area; - let badAreas = [ - sdk.areas.RogueEncampment, sdk.areas.LutGholein, sdk.areas.KurastDocktown, - sdk.areas.PandemoniumFortress, sdk.areas.Harrogath, sdk.areas.ArreatSummit, sdk.areas.UberTristram - ]; - // can't tp from town or Uber Trist, and shouldn't tp from arreat summit - if (badAreas.includes(myArea)) return false; - // If we made it this far, we can only tp if we even have a tp - return !!me.getTpTool(); + // can't tp if dead - or not currently enabled to + if (me.dead || SoloEvents.townChicken.disabled) return false; + const myArea = me.area; + let badAreas = [ + sdk.areas.RogueEncampment, sdk.areas.LutGholein, sdk.areas.KurastDocktown, + sdk.areas.PandemoniumFortress, sdk.areas.Harrogath, sdk.areas.ArreatSummit, sdk.areas.UberTristram + ]; + // can't tp from town or Uber Trist, and shouldn't tp from arreat summit + if (badAreas.includes(myArea)) return false; + // If we made it this far, we can only tp if we even have a tp + return !!me.getTpTool(); }; me.getMercEx = function () { - if (!Config.UseMerc || me.classic || me.mercrevivecost) return null; - let merc = Misc.poll(() => me.getMerc(), 250, 50); + if (!Config.UseMerc || me.classic || me.mercrevivecost) return null; + let merc = Misc.poll(() => me.getMerc(), 250, 50); - return !!merc && !merc.dead ? merc : null; + return !!merc && !merc.dead ? merc : null; }; me.getEquippedItem = function (bodyLoc) { - if (!bodyLoc) return null; - let equippedItem = me.getItemsEx().filter(i => i.isEquipped && i.bodylocation === bodyLoc); - if (!equippedItem.length) return null; - return equippedItem.first(); + if (!bodyLoc) return null; + let equippedItem = me.getItemsEx().filter(i => i.isEquipped && i.bodylocation === bodyLoc); + if (!equippedItem.length) return null; + return equippedItem.first(); }; /** * @param {number} bodyLoc */ me.getWeaponQuantityPercent = function (bodyLoc) { - if (!bodyLoc) return 0; - let weapon = me.getEquippedItem(bodyLoc); - if (!weapon) return 0; - return weapon.quantityPercent; + if (!bodyLoc) return 0; + let weapon = me.getEquippedItem(bodyLoc); + if (!weapon) return 0; + return weapon.quantityPercent; }; me.getSkillTabs = function (classid = me.classid) { - return [ - [sdk.skills.tabs.BowandCrossbow, sdk.skills.tabs.PassiveandMagic, sdk.skills.tabs.JavelinandSpear], - [sdk.skills.tabs.Fire, sdk.skills.tabs.Lightning, sdk.skills.tabs.Cold], - [sdk.skills.tabs.Curses, sdk.skills.tabs.PoisonandBone, sdk.skills.tabs.NecroSummoning], - [sdk.skills.tabs.PalaCombat, sdk.skills.tabs.Offensive, sdk.skills.tabs.Defensive], - [sdk.skills.tabs.BarbCombat, sdk.skills.tabs.Masteries, sdk.skills.tabs.Warcries], - [sdk.skills.tabs.DruidSummon, sdk.skills.tabs.ShapeShifting, sdk.skills.tabs.Elemental], - [sdk.skills.tabs.Traps, sdk.skills.tabs.ShadowDisciplines, sdk.skills.tabs.MartialArts] - ][classid]; + return [ + [sdk.skills.tabs.BowandCrossbow, sdk.skills.tabs.PassiveandMagic, sdk.skills.tabs.JavelinandSpear], + [sdk.skills.tabs.Fire, sdk.skills.tabs.Lightning, sdk.skills.tabs.Cold], + [sdk.skills.tabs.Curses, sdk.skills.tabs.PoisonandBone, sdk.skills.tabs.NecroSummoning], + [sdk.skills.tabs.PalaCombat, sdk.skills.tabs.Offensive, sdk.skills.tabs.Defensive], + [sdk.skills.tabs.BarbCombat, sdk.skills.tabs.Masteries, sdk.skills.tabs.Warcries], + [sdk.skills.tabs.DruidSummon, sdk.skills.tabs.ShapeShifting, sdk.skills.tabs.Elemental], + [sdk.skills.tabs.Traps, sdk.skills.tabs.ShadowDisciplines, sdk.skills.tabs.MartialArts] + ][classid]; }; // @todo better determination of what actually constitutes being in danger // need check for ranged mobs so we can stick and move to avoid missiles me.inDanger = function (checkLoc, range) { - let count = 0; - let _this = typeof checkLoc !== "undefined" && checkLoc.hasOwnProperty("x") ? checkLoc : me; - range === undefined && (range = 10); - let nearUnits = getUnits(sdk.unittype.Monster).filter((mon) => mon && mon.attackable && getDistance(_this, mon) < 10); - nearUnits.forEach(u => u.isSpecial - ? [sdk.states.Fanaticism, sdk.states.Conviction].some(state => u.getState(state)) - ? (count += 3) - : (count += 2) - : (count += 1)); - if (count > me.maxNearMonsters) return true; - let dangerClose = nearUnits - .find(mon => [sdk.enchant.ManaBurn, sdk.enchant.LightningEnchanted, sdk.enchant.FireEnchanted].some(chant => mon.getEnchant(chant))); - return dangerClose; + let count = 0; + let _this = typeof checkLoc !== "undefined" && checkLoc.hasOwnProperty("x") ? checkLoc : me; + range === undefined && (range = 10); + let nearUnits = getUnits(sdk.unittype.Monster).filter((mon) => mon && mon.attackable && getDistance(_this, mon) < 10); + nearUnits.forEach(u => u.isSpecial + ? [sdk.states.Fanaticism, sdk.states.Conviction].some(state => u.getState(state)) + ? (count += 3) + : (count += 2) + : (count += 1)); + if (count > me.maxNearMonsters) return true; + let dangerClose = nearUnits + .find(mon => [sdk.enchant.ManaBurn, sdk.enchant.LightningEnchanted, sdk.enchant.FireEnchanted].some(chant => mon.getEnchant(chant))); + return dangerClose; }; /** @@ -473,387 +473,387 @@ me.inDanger = function (checkLoc, range) { me.checkSkill = (skillId = 0, subId = 0) => !!me.getSkill(skillId, subId); me.cleanUpInvoPotions = function (beltSize) { - beltSize === undefined && (beltSize = Storage.BeltSize()); - const beltMax = (beltSize * 4); - /** - * belt 4x4 locations - * 12 13 14 15 - * 8 9 10 11 - * 4 5 6 7 - * 0 1 2 3 - */ - const beltCapRef = [(0 + beltMax), (1 + beltMax), (2 + beltMax), (3 + beltMax)]; - // check if we have empty belt slots - let needCleanup = Storage.Belt.checkColumns(beltSize).some(slot => slot > 0); - - if (needCleanup) { - const potsInInventory = me.getItemsEx() - .filter((p) => p.isInInventory && [sdk.items.type.HealingPotion, sdk.items.type.ManaPotion, sdk.items.type.RejuvPotion].includes(p.itemType)) - .sort((a, b) => a.itemType - b.itemType); - - potsInInventory.length > 0 && console.debug("We have potions in our invo, put them in belt before we perform townchicken check"); - // Start interating over all the pots we have in our inventory - beltSize > 1 && potsInInventory.forEach(function (p) { - let moved = false; - // get free space in each slot of our belt - let freeSpace = Storage.Belt.checkColumns(beltSize); - for (let i = 0; i < 4 && !moved; i += 1) { - // checking that current potion matches what we want in our belt - if (freeSpace[i] > 0 && p.code && p.code.startsWith(Config.BeltColumn[i])) { - // Pick up the potion and put it in belt if the column is empty, and we don't have any other columns empty - // prevents shift-clicking potion into wrong column - if (freeSpace[i] === beltSize || freeSpace.some((spot) => spot === beltSize)) { - let x = freeSpace[i] === beltSize ? i : (beltCapRef[i] - (freeSpace[i] * 4)); - Packet.placeInBelt(p, x); - } else { - clickItemAndWait(sdk.clicktypes.click.item.ShiftLeft, p.x, p.y, p.location); - } - Misc.poll(() => !me.itemoncursor, 300, 30); - moved = Storage.Belt.checkColumns(beltSize)[i] === freeSpace[i] - 1; - } - Cubing.cursorCheck(); - } - }); - } - - return true; + beltSize === undefined && (beltSize = Storage.BeltSize()); + const beltMax = (beltSize * 4); + /** + * belt 4x4 locations + * 12 13 14 15 + * 8 9 10 11 + * 4 5 6 7 + * 0 1 2 3 + */ + const beltCapRef = [(0 + beltMax), (1 + beltMax), (2 + beltMax), (3 + beltMax)]; + // check if we have empty belt slots + let needCleanup = Storage.Belt.checkColumns(beltSize).some(slot => slot > 0); + + if (needCleanup) { + const potsInInventory = me.getItemsEx() + .filter((p) => p.isInInventory && [sdk.items.type.HealingPotion, sdk.items.type.ManaPotion, sdk.items.type.RejuvPotion].includes(p.itemType)) + .sort((a, b) => a.itemType - b.itemType); + + potsInInventory.length > 0 && console.debug("We have potions in our invo, put them in belt before we perform townchicken check"); + // Start interating over all the pots we have in our inventory + beltSize > 1 && potsInInventory.forEach(function (p) { + let moved = false; + // get free space in each slot of our belt + let freeSpace = Storage.Belt.checkColumns(beltSize); + for (let i = 0; i < 4 && !moved; i += 1) { + // checking that current potion matches what we want in our belt + if (freeSpace[i] > 0 && p.code && p.code.startsWith(Config.BeltColumn[i])) { + // Pick up the potion and put it in belt if the column is empty, and we don't have any other columns empty + // prevents shift-clicking potion into wrong column + if (freeSpace[i] === beltSize || freeSpace.some((spot) => spot === beltSize)) { + let x = freeSpace[i] === beltSize ? i : (beltCapRef[i] - (freeSpace[i] * 4)); + Packet.placeInBelt(p, x); + } else { + clickItemAndWait(sdk.clicktypes.click.item.ShiftLeft, p.x, p.y, p.location); + } + Misc.poll(() => !me.itemoncursor, 300, 30); + moved = Storage.Belt.checkColumns(beltSize)[i] === freeSpace[i] - 1; + } + Cubing.cursorCheck(); + } + }); + } + + return true; }; me.cleanUpScrolls = function (tome, scrollId) { - if (!tome || !scrollId) return 0; - - let cleanedUp = 0; - let myScrolls = me.getItemsEx() - .filter(el => el.isInInventory && el.classid === scrollId); - - if (myScrolls.length) { - try { - // If we are at an npc already, open the window otherwise moving potions around fails - if (getUIFlag(sdk.uiflags.NPCMenu) && !getUIFlag(sdk.uiflags.Shop)) { - console.info(null, "Opening npc menu to clean up scrolls"); - Misc.useMenu(sdk.menu.Trade) || me.cancelUIFlags(); - } - - myScrolls.forEach(el => { - if (tome && tome.getStat(sdk.stats.Quantity) < 20) { - let currQuantity = tome.getStat(sdk.stats.Quantity); - if (el.toCursor()) { - new PacketBuilder().byte(sdk.packets.send.ScrollToMe).dword(el.gid).dword(tome.gid).send(); - Misc.poll(() => !me.itemoncursor, 100, 25); - - if (tome.getStat(sdk.stats.Quantity) > currQuantity) { - console.info(null, "Placed scroll in tome"); - cleanedUp++; - } - } else { - console.warn("failed to place scroll in tome"); - } - } - }); - } catch (e) { - console.error(e); - me.cancelUIFlags(); - } - } - - return cleanedUp; + if (!tome || !scrollId) return 0; + + let cleanedUp = 0; + let myScrolls = me.getItemsEx() + .filter(el => el.isInInventory && el.classid === scrollId); + + if (myScrolls.length) { + try { + // If we are at an npc already, open the window otherwise moving potions around fails + if (getUIFlag(sdk.uiflags.NPCMenu) && !getUIFlag(sdk.uiflags.Shop)) { + console.info(null, "Opening npc menu to clean up scrolls"); + Misc.useMenu(sdk.menu.Trade) || me.cancelUIFlags(); + } + + myScrolls.forEach(el => { + if (tome && tome.getStat(sdk.stats.Quantity) < 20) { + let currQuantity = tome.getStat(sdk.stats.Quantity); + if (el.toCursor()) { + new PacketBuilder().byte(sdk.packets.send.ScrollToMe).dword(el.gid).dword(tome.gid).send(); + Misc.poll(() => !me.itemoncursor, 100, 25); + + if (tome.getStat(sdk.stats.Quantity) > currQuantity) { + console.info(null, "Placed scroll in tome"); + cleanedUp++; + } + } else { + console.warn("failed to place scroll in tome"); + } + } + }); + } catch (e) { + console.error(e); + me.cancelUIFlags(); + } + } + + return cleanedUp; }; me.needPotions = function () { - // we aren't using MinColumn if none of the values are set - if (!Config.MinColumn.some(el => el > 0)) return false; - // no hp pots or mp pots in Config.BeltColumn (who uses only rejuv pots?) - if (!Config.BeltColumn.some(el => ["hp", "mp"].includes(el))) return false; - - // Start - if (me.charlvl > 2 && me.gold > 1000) { - let pots = { hp: [], mp: [], }; - const beltSize = Storage.BeltSize(); - - // only run this bit if we aren't wearing a belt for now - beltSize === 1 && me.cleanUpInvoPotions(beltSize); - // now check what's in our belt - me.getItemsEx(-1, sdk.items.mode.inBelt) - .filter(p => [sdk.items.type.HealingPotion, sdk.items.type.ManaPotion].includes(p.itemType) && p.x < 4) - .forEach(p => { - if (p.itemType === sdk.items.type.HealingPotion) { - pots.hp.push(copyUnit(p)); - } else if (p.itemType === sdk.items.type.ManaPotion) { - pots.mp.push(copyUnit(p)); - } - }); - - // quick check - if ((Config.BeltColumn.includes("hp") && !pots.hp.length) - || (Config.BeltColumn.includes("mp") && !pots.mp.length)) { - return true; - } - - // should we check the actual amount in the column? - // For now just keeping the way it was and checking if a column is empty - for (let i = 0; i < 4; i += 1) { - if (Config.MinColumn[i] <= 0) { - continue; - } - - switch (Config.BeltColumn[i]) { - case "hp": - if (!pots.hp.some(p => p.x === i)) { - console.debug("Column: " + (i + 1) + " needs hp pots"); - return true; - } - break; - case "mp": - if (!pots.mp.some(p => p.x === i)) { - console.debug("Column: " + (i + 1) + " needs mp pots"); - return true; - } - break; - } - } - } - - return false; + // we aren't using MinColumn if none of the values are set + if (!Config.MinColumn.some(el => el > 0)) return false; + // no hp pots or mp pots in Config.BeltColumn (who uses only rejuv pots?) + if (!Config.BeltColumn.some(el => ["hp", "mp"].includes(el))) return false; + + // Start + if (me.charlvl > 2 && me.gold > 1000) { + let pots = { hp: [], mp: [], }; + const beltSize = Storage.BeltSize(); + + // only run this bit if we aren't wearing a belt for now + beltSize === 1 && me.cleanUpInvoPotions(beltSize); + // now check what's in our belt + me.getItemsEx(-1, sdk.items.mode.inBelt) + .filter(p => [sdk.items.type.HealingPotion, sdk.items.type.ManaPotion].includes(p.itemType) && p.x < 4) + .forEach(p => { + if (p.itemType === sdk.items.type.HealingPotion) { + pots.hp.push(copyUnit(p)); + } else if (p.itemType === sdk.items.type.ManaPotion) { + pots.mp.push(copyUnit(p)); + } + }); + + // quick check + if ((Config.BeltColumn.includes("hp") && !pots.hp.length) + || (Config.BeltColumn.includes("mp") && !pots.mp.length)) { + return true; + } + + // should we check the actual amount in the column? + // For now just keeping the way it was and checking if a column is empty + for (let i = 0; i < 4; i += 1) { + if (Config.MinColumn[i] <= 0) { + continue; + } + + switch (Config.BeltColumn[i]) { + case "hp": + if (!pots.hp.some(p => p.x === i)) { + console.debug("Column: " + (i + 1) + " needs hp pots"); + return true; + } + break; + case "mp": + if (!pots.mp.some(p => p.x === i)) { + console.debug("Column: " + (i + 1) + " needs mp pots"); + return true; + } + break; + } + } + } + + return false; }; me.clearBelt = function () { - let item = me.getItem(-1, sdk.items.mode.inBelt); - let clearList = []; - - if (item) { - do { - switch (item.itemType) { - case sdk.items.type.HealingPotion: - if (Config.BeltColumn[item.x % 4] !== "hp") { - clearList.push(copyUnit(item)); - } - - break; - case sdk.items.type.ManaPotion: - if (Config.BeltColumn[item.x % 4] !== "mp") { - clearList.push(copyUnit(item)); - } - - break; - case sdk.items.type.RejuvPotion: - if (Config.BeltColumn[item.x % 4] !== "rv") { - clearList.push(copyUnit(item)); - } - - break; - case sdk.items.type.StaminaPotion: - case sdk.items.type.AntidotePotion: - case sdk.items.type.ThawingPotion: - clearList.push(copyUnit(item)); - } - } while (item.getNext()); - - while (clearList.length > 0) { - let pot = clearList.shift(); - (Storage.Inventory.CanFit(pot) && Storage.Inventory.MoveTo(pot)) || pot.interact(); - delay(200); - } - } - - return true; + let item = me.getItem(-1, sdk.items.mode.inBelt); + let clearList = []; + + if (item) { + do { + switch (item.itemType) { + case sdk.items.type.HealingPotion: + if (Config.BeltColumn[item.x % 4] !== "hp") { + clearList.push(copyUnit(item)); + } + + break; + case sdk.items.type.ManaPotion: + if (Config.BeltColumn[item.x % 4] !== "mp") { + clearList.push(copyUnit(item)); + } + + break; + case sdk.items.type.RejuvPotion: + if (Config.BeltColumn[item.x % 4] !== "rv") { + clearList.push(copyUnit(item)); + } + + break; + case sdk.items.type.StaminaPotion: + case sdk.items.type.AntidotePotion: + case sdk.items.type.ThawingPotion: + clearList.push(copyUnit(item)); + } + } while (item.getNext()); + + while (clearList.length > 0) { + let pot = clearList.shift(); + (Storage.Inventory.CanFit(pot) && Storage.Inventory.MoveTo(pot)) || pot.interact(); + delay(200); + } + } + + return true; }; me.getIdTool = function () { - let items = me.getItemsEx().filter((i) => i.isInInventory && [sdk.items.ScrollofIdentify, sdk.items.TomeofIdentify].includes(i.classid)); - let scroll = items.find((i) => i.isInInventory && i.classid === sdk.items.ScrollofIdentify); - if (scroll) return scroll; - let tome = items.find((i) => i.isInInventory && i.classid === sdk.items.TomeofIdentify); - if (tome && tome.getStat(sdk.stats.Quantity) > 0) return tome; + let items = me.getItemsEx().filter((i) => i.isInInventory && [sdk.items.ScrollofIdentify, sdk.items.TomeofIdentify].includes(i.classid)); + let scroll = items.find((i) => i.isInInventory && i.classid === sdk.items.ScrollofIdentify); + if (scroll) return scroll; + let tome = items.find((i) => i.isInInventory && i.classid === sdk.items.TomeofIdentify); + if (tome && tome.getStat(sdk.stats.Quantity) > 0) return tome; - return null; + return null; }; me.getTpTool = function () { - let items = me.getItemsEx(-1, sdk.items.mode.inStorage).filter((i) => i.isInInventory && [sdk.items.ScrollofTownPortal, sdk.items.TomeofTownPortal].includes(i.classid)); - if (!items.length) return null; - let tome = items.find((i) => i.classid === sdk.items.TomeofTownPortal && i.getStat(sdk.stats.Quantity) > 0); - if (tome) return tome; - let scroll = items.find((i) => i.classid === sdk.items.ScrollofTownPortal); - if (scroll) return scroll; - return null; + let items = me.getItemsEx(-1, sdk.items.mode.inStorage).filter((i) => i.isInInventory && [sdk.items.ScrollofTownPortal, sdk.items.TomeofTownPortal].includes(i.classid)); + if (!items.length) return null; + let tome = items.find((i) => i.classid === sdk.items.TomeofTownPortal && i.getStat(sdk.stats.Quantity) > 0); + if (tome) return tome; + let scroll = items.find((i) => i.classid === sdk.items.ScrollofTownPortal); + if (scroll) return scroll; + return null; }; me.getUnids = function () { - let list = []; - let item = me.getItem(-1, sdk.items.mode.inStorage); + let list = []; + let item = me.getItem(-1, sdk.items.mode.inStorage); - if (!item) return []; + if (!item) return []; - do { - if (item.isInInventory && !item.identified) { - list.push(copyUnit(item)); - } - } while (item.getNext()); + do { + if (item.isInInventory && !item.identified) { + list.push(copyUnit(item)); + } + } while (item.getNext()); - return list; + return list; }; me.fieldID = function () { - let list = me.getUnids(); - if (!list) return false; - - while (list.length > 0) { - let idTool = me.getIdTool(); - if (!idTool) return false; - - let item = list.shift(); - let result = Pickit.checkItem(item); - // Force ID for unid items matching autoEquip/cubing criteria - Town.needForceID(item) && (result.result = -1); - - // unid item that should be identified - if (result.result === Pickit.Result.UNID) { - Town.identifyItem(item, idTool, Config.FieldID.PacketID); - delay(50); - result = Pickit.checkItem(item); - } - Town.itemResult(item, result, "Field", false); - } - - delay(200); - me.cancel(); - - return true; + let list = me.getUnids(); + if (!list) return false; + + while (list.length > 0) { + let idTool = me.getIdTool(); + if (!idTool) return false; + + let item = list.shift(); + let result = Pickit.checkItem(item); + // Force ID for unid items matching autoEquip/cubing criteria + Town.needForceID(item) && (result.result = -1); + + // unid item that should be identified + if (result.result === Pickit.Result.UNID) { + Town.identifyItem(item, idTool, Config.FieldID.PacketID); + delay(50); + result = Pickit.checkItem(item); + } + Town.itemResult(item, result, "Field", false); + } + + delay(200); + me.cancel(); + + return true; }; me.getWeaponQuantity = function (weaponLoc = sdk.body.RightArm) { - let currItem = me.getItemsEx(-1, sdk.items.mode.Equipped).filter(i => i.bodylocation === weaponLoc).first(); - return !!currItem ? currItem.getStat(sdk.stats.Quantity) : 0; + let currItem = me.getItemsEx(-1, sdk.items.mode.Equipped).filter(i => i.bodylocation === weaponLoc).first(); + return !!currItem ? currItem.getStat(sdk.stats.Quantity) : 0; }; me.getItemsForRepair = function (repairPercent, chargedItems) { - const lowLevelCheck = me.charlvl < 5; - // lower the required percent as we are a low level - (lowLevelCheck && repairPercent > 30) && (repairPercent = 15); - let itemList = []; - let item = me.getItem(-1, sdk.items.mode.Equipped); - - if (item) { - do { - if (lowLevelCheck && !item.isOnMain && !item.isOnSwap) continue; - // Skip ethereal items - if (!item.ethereal) { - // Skip indestructible items - if (!item.getStat(sdk.stats.Indestructible)) { - switch (item.itemType) { - // Quantity check - case sdk.items.type.ThrowingKnife: - case sdk.items.type.ThrowingAxe: - case sdk.items.type.Javelin: - case sdk.items.type.AmazonJavelin: - let quantity = item.getStat(sdk.stats.Quantity); - - // Stat 254 = increased stack size - if (typeof quantity === "number" && quantity * 100 / (getBaseStat("items", item.classid, "maxstack") + item.getStat(sdk.stats.ExtraStack)) <= repairPercent) { - itemList.push(copyUnit(item)); - } - - break; - default: - // Durability check - if (item.durabilityPercent <= repairPercent) { - itemList.push(copyUnit(item)); - } - - break; - } - } - - if (chargedItems) { - // Charged item check - let charge = item.getStat(-2)[sdk.stats.ChargedSkill]; - - if (typeof (charge) === "object") { - if (charge instanceof Array) { - for (let i = 0; i < charge.length; i += 1) { - if (charge[i] !== undefined && charge[i].hasOwnProperty("charges") && charge[i].charges * 100 / charge[i].maxcharges <= repairPercent) { - itemList.push(copyUnit(item)); - } - } - } else if (charge.charges * 100 / charge.maxcharges <= repairPercent) { - itemList.push(copyUnit(item)); - } - } - } - } - } while (item.getNext()); - } - - return itemList; + const lowLevelCheck = me.charlvl < 5; + // lower the required percent as we are a low level + (lowLevelCheck && repairPercent > 30) && (repairPercent = 15); + let itemList = []; + let item = me.getItem(-1, sdk.items.mode.Equipped); + + if (item) { + do { + if (lowLevelCheck && !item.isOnMain && !item.isOnSwap) continue; + // Skip ethereal items + if (!item.ethereal) { + // Skip indestructible items + if (!item.getStat(sdk.stats.Indestructible)) { + switch (item.itemType) { + // Quantity check + case sdk.items.type.ThrowingKnife: + case sdk.items.type.ThrowingAxe: + case sdk.items.type.Javelin: + case sdk.items.type.AmazonJavelin: + let quantity = item.getStat(sdk.stats.Quantity); + + // Stat 254 = increased stack size + if (typeof quantity === "number" && quantity * 100 / (getBaseStat("items", item.classid, "maxstack") + item.getStat(sdk.stats.ExtraStack)) <= repairPercent) { + itemList.push(copyUnit(item)); + } + + break; + default: + // Durability check + if (item.durabilityPercent <= repairPercent) { + itemList.push(copyUnit(item)); + } + + break; + } + } + + if (chargedItems) { + // Charged item check + let charge = item.getStat(-2)[sdk.stats.ChargedSkill]; + + if (typeof (charge) === "object") { + if (charge instanceof Array) { + for (let i = 0; i < charge.length; i += 1) { + if (charge[i] !== undefined && charge[i].hasOwnProperty("charges") && charge[i].charges * 100 / charge[i].maxcharges <= repairPercent) { + itemList.push(copyUnit(item)); + } + } + } else if (charge.charges * 100 / charge.maxcharges <= repairPercent) { + itemList.push(copyUnit(item)); + } + } + } + } + } while (item.getNext()); + } + + return itemList; }; me.needRepair = function () { - let repairAction = []; - let bowCheck = Attack.usingBow(); - let switchBowCheck = CharData.skillData.bow.onSwitch; - let canAfford = me.gold >= me.getRepairCost(); - !bowCheck && switchBowCheck && (bowCheck = (() => { - switch (CharData.skillData.bow.bowType) { - case sdk.items.type.Bow: - case sdk.items.type.AmazonBow: - return "bow"; - case sdk.items.type.Crossbow: - return "crossbow"; - default: - return ""; - } - })()); - - if (bowCheck) { - let [quiver, inventoryQuiver] = (() => { - switch (bowCheck) { - case "crossbow": - return [me.getItem("cqv", sdk.items.mode.Equipped), me.getItem("cqv", sdk.items.mode.inStorage)]; - case "bow": - default: - return [me.getItem("aqv", sdk.items.mode.Equipped), me.getItem("aqv", sdk.items.mode.inStorage)]; - } - })(); - - // Out of arrows/bolts - if (!quiver) { - inventoryQuiver ? switchBowCheck ? Item.secondaryEquip(inventoryQuiver, sdk.body.LeftArmSecondary) : Item.equip(inventoryQuiver, 5) : repairAction.push("buyQuiver") && repairAction.push("buyQuiver"); - } else { - let quantity = quiver.getStat(sdk.stats.Quantity); - - if (typeof quantity === "number" && quantity * 100 / getBaseStat("items", quiver.classid, "maxstack") <= Config.RepairPercent) { - inventoryQuiver ? switchBowCheck ? Item.secondaryEquip(inventoryQuiver, sdk.body.LeftArmSecondary) : Item.equip(inventoryQuiver, 5) : repairAction.push("buyQuiver") && repairAction.push("buyQuiver"); - } - } - } - - // Repair durability/quantity/charges - if (canAfford && this.getItemsForRepair(Config.RepairPercent, true).length > 0) { - repairAction.push("repair"); - } - - return repairAction; + let repairAction = []; + let bowCheck = Attack.usingBow(); + let switchBowCheck = CharData.skillData.bow.onSwitch; + let canAfford = me.gold >= me.getRepairCost(); + !bowCheck && switchBowCheck && (bowCheck = (() => { + switch (CharData.skillData.bow.bowType) { + case sdk.items.type.Bow: + case sdk.items.type.AmazonBow: + return "bow"; + case sdk.items.type.Crossbow: + return "crossbow"; + default: + return ""; + } + })()); + + if (bowCheck) { + let [quiver, inventoryQuiver] = (() => { + switch (bowCheck) { + case "crossbow": + return [me.getItem("cqv", sdk.items.mode.Equipped), me.getItem("cqv", sdk.items.mode.inStorage)]; + case "bow": + default: + return [me.getItem("aqv", sdk.items.mode.Equipped), me.getItem("aqv", sdk.items.mode.inStorage)]; + } + })(); + + // Out of arrows/bolts + if (!quiver) { + inventoryQuiver ? switchBowCheck ? Item.secondaryEquip(inventoryQuiver, sdk.body.LeftArmSecondary) : Item.equip(inventoryQuiver, 5) : repairAction.push("buyQuiver") && repairAction.push("buyQuiver"); + } else { + let quantity = quiver.getStat(sdk.stats.Quantity); + + if (typeof quantity === "number" && quantity * 100 / getBaseStat("items", quiver.classid, "maxstack") <= Config.RepairPercent) { + inventoryQuiver ? switchBowCheck ? Item.secondaryEquip(inventoryQuiver, sdk.body.LeftArmSecondary) : Item.equip(inventoryQuiver, 5) : repairAction.push("buyQuiver") && repairAction.push("buyQuiver"); + } + } + } + + // Repair durability/quantity/charges + if (canAfford && this.getItemsForRepair(Config.RepairPercent, true).length > 0) { + repairAction.push("repair"); + } + + return repairAction; }; me.needMerc = function () { - if (me.classic || !Config.UseMerc || me.gold < me.mercrevivecost || me.mercrevivecost === 0) return false; + if (me.classic || !Config.UseMerc || me.gold < me.mercrevivecost || me.mercrevivecost === 0) return false; - Misc.poll(() => me.gameReady, 1000, 100); - // me.getMerc() might return null if called right after taking a portal, that's why there's retry attempts - for (let i = 0; i < 3; i += 1) { - let merc = me.getMercEx(); - if (!!merc && !merc.dead) return false; + Misc.poll(() => me.gameReady, 1000, 100); + // me.getMerc() might return null if called right after taking a portal, that's why there's retry attempts + for (let i = 0; i < 3; i += 1) { + let merc = me.getMercEx(); + if (!!merc && !merc.dead) return false; - delay(100); - } + delay(100); + } - // In case we never had a merc and Config.UseMerc is still set to true for some odd reason - return true; + // In case we never had a merc and Config.UseMerc is still set to true for some odd reason + return true; }; me.sortInventory = function () { - return Storage.Inventory.SortItems( - SetUp.sortSettings.ItemsSortedFromLeft, - SetUp.sortSettings.ItemsSortedFromRight - ); + return Storage.Inventory.SortItems( + SetUp.sortSettings.ItemsSortedFromLeft, + SetUp.sortSettings.ItemsSortedFromRight + ); }; diff --git a/libs/SoloPlay/Functions/Mercenary.js b/libs/SoloPlay/Functions/Mercenary.js index 8a3a78b4..3118c847 100644 --- a/libs/SoloPlay/Functions/Mercenary.js +++ b/libs/SoloPlay/Functions/Mercenary.js @@ -7,315 +7,315 @@ */ const MercData = new function MercData () { - /** - * @constructor Merc - * @param {number} classid - * @param {number} skill - * @param {number} act - * @param {number} [difficulty] - */ - function Merc (classid, skill, act, difficulty) { - this.classid = classid; - this.skill = skill; - this.skillName = getSkillById(skill); - this.act = act; - this.difficulty = difficulty || sdk.difficulty.Normal; - } - - // Act 1 - this[sdk.skills.FireArrow] = new Merc(sdk.mercs.Rogue, sdk.skills.FireArrow, 1); - this[sdk.skills.ColdArrow] = new Merc(sdk.mercs.Rogue, sdk.skills.ColdArrow, 1); - - // Act 2 - this[sdk.skills.Prayer] = new Merc(sdk.mercs.Guard, sdk.skills.Prayer, 2, sdk.difficulty.Normal); - this[sdk.skills.BlessedAim] = new Merc(sdk.mercs.Guard, sdk.skills.BlessedAim, 2, sdk.difficulty.Normal); - this[sdk.skills.Defiance] = new Merc(sdk.mercs.Guard, sdk.skills.Defiance, 2, sdk.difficulty.Normal); - - this[sdk.skills.HolyFreeze] = new Merc(sdk.mercs.Guard, sdk.skills.HolyFreeze, 2, sdk.difficulty.Nightmare); - this[sdk.skills.Might] = new Merc(sdk.mercs.Guard, sdk.skills.Might, 2, sdk.difficulty.Nightmare); - this[sdk.skills.Thorns] = new Merc(sdk.mercs.Guard, sdk.skills.Thorns, 2, sdk.difficulty.Nightmare); - - // Act 3 - this[sdk.skills.IceBlast] = new Merc(sdk.mercs.IronWolf, sdk.skills.IceBlast, 3, sdk.difficulty.Normal); - this[sdk.skills.FireBall] = new Merc(sdk.mercs.IronWolf, sdk.skills.FireBall, 3, sdk.difficulty.Normal); - this[sdk.skills.Lightning] = new Merc(sdk.mercs.IronWolf, sdk.skills.Lightning, 3, sdk.difficulty.Normal); - - // Act 5 - this[sdk.skills.Bash] = new Merc(sdk.mercs.A5Barb, sdk.skills.Bash, 5, sdk.difficulty.Normal); - - /** @type {Map} */ - this.actMap = new Map(); - this.actMap.set(sdk.mercs.Rogue, 1); - this.actMap.set(1, [this[sdk.skills.FireArrow], this[sdk.skills.ColdArrow]]); - - this.actMap.set(sdk.mercs.Guard, 2); - this.actMap.set(2, [ - this[sdk.skills.Prayer], this[sdk.skills.BlessedAim], - this[sdk.skills.Defiance], this[sdk.skills.HolyFreeze], - this[sdk.skills.Might], this[sdk.skills.Thorns] - ]); - - this.actMap.set(sdk.mercs.IronWolf, 3); - this.actMap.set(3, [this[sdk.skills.IceBlast], this[sdk.skills.FireBall], this[sdk.skills.Lightning]]); - - this.actMap.set(sdk.mercs.A5Barb, 5); - this.actMap.set(5, [this[sdk.skills.Bash]]); - - this.findByName = function (name, act) { - let merc = this.actMap.get(act) - .find(m => m.skillName === name); - return merc; - }; + /** + * @constructor Merc + * @param {number} classid + * @param {number} skill + * @param {number} act + * @param {number} [difficulty] + */ + function Merc (classid, skill, act, difficulty) { + this.classid = classid; + this.skill = skill; + this.skillName = getSkillById(skill); + this.act = act; + this.difficulty = difficulty || sdk.difficulty.Normal; + } + + // Act 1 + this[sdk.skills.FireArrow] = new Merc(sdk.mercs.Rogue, sdk.skills.FireArrow, 1); + this[sdk.skills.ColdArrow] = new Merc(sdk.mercs.Rogue, sdk.skills.ColdArrow, 1); + + // Act 2 + this[sdk.skills.Prayer] = new Merc(sdk.mercs.Guard, sdk.skills.Prayer, 2, sdk.difficulty.Normal); + this[sdk.skills.BlessedAim] = new Merc(sdk.mercs.Guard, sdk.skills.BlessedAim, 2, sdk.difficulty.Normal); + this[sdk.skills.Defiance] = new Merc(sdk.mercs.Guard, sdk.skills.Defiance, 2, sdk.difficulty.Normal); + + this[sdk.skills.HolyFreeze] = new Merc(sdk.mercs.Guard, sdk.skills.HolyFreeze, 2, sdk.difficulty.Nightmare); + this[sdk.skills.Might] = new Merc(sdk.mercs.Guard, sdk.skills.Might, 2, sdk.difficulty.Nightmare); + this[sdk.skills.Thorns] = new Merc(sdk.mercs.Guard, sdk.skills.Thorns, 2, sdk.difficulty.Nightmare); + + // Act 3 + this[sdk.skills.IceBlast] = new Merc(sdk.mercs.IronWolf, sdk.skills.IceBlast, 3, sdk.difficulty.Normal); + this[sdk.skills.FireBall] = new Merc(sdk.mercs.IronWolf, sdk.skills.FireBall, 3, sdk.difficulty.Normal); + this[sdk.skills.Lightning] = new Merc(sdk.mercs.IronWolf, sdk.skills.Lightning, 3, sdk.difficulty.Normal); + + // Act 5 + this[sdk.skills.Bash] = new Merc(sdk.mercs.A5Barb, sdk.skills.Bash, 5, sdk.difficulty.Normal); + + /** @type {Map} */ + this.actMap = new Map(); + this.actMap.set(sdk.mercs.Rogue, 1); + this.actMap.set(1, [this[sdk.skills.FireArrow], this[sdk.skills.ColdArrow]]); + + this.actMap.set(sdk.mercs.Guard, 2); + this.actMap.set(2, [ + this[sdk.skills.Prayer], this[sdk.skills.BlessedAim], + this[sdk.skills.Defiance], this[sdk.skills.HolyFreeze], + this[sdk.skills.Might], this[sdk.skills.Thorns] + ]); + + this.actMap.set(sdk.mercs.IronWolf, 3); + this.actMap.set(3, [this[sdk.skills.IceBlast], this[sdk.skills.FireBall], this[sdk.skills.Lightning]]); + + this.actMap.set(sdk.mercs.A5Barb, 5); + this.actMap.set(5, [this[sdk.skills.Bash]]); + + this.findByName = function (name, act) { + let merc = this.actMap.get(act) + .find(m => m.skillName === name); + return merc; + }; }; const Mercenary = { - minCost: -1, - timeout: 0, - - /** - * only a2 mercs for now, need to test others to see if ModifierListSkill returns their skill - * @param {MercUnit} merc - * @returns {string} - */ - getMercSkill: function (merc) { - !merc && (merc = Misc.poll(() => me.getMerc(), 1000, 50)); - if (!merc) return false; - let mercSkill = (() => { - switch (merc.classid) { - case sdk.mercs.Rogue: - return [sdk.skills.FireArrow, sdk.skills.ColdArrow].find(s => merc.getSkill(s, sdk.skills.subindex.HardPoints)); - case sdk.mercs.Guard: - let checkStat = merc.getStat(sdk.stats.ModifierListSkill); - // if ([sdk.skills.Meditation, sdk.skills.Conviction, sdk.skills.Concentration, sdk.skills.HolyFire].includes(checkStat)) { - // return [sdk.skills.Prayer, sdk.skills.BlessedAim, sdk.skills.Defiance].find(s => merc.getSkill(s, sdk.skills.subindex.HardPoints)); - // } - if (![sdk.skills.Prayer, sdk.skills.BlessedAim, sdk.skills.Defiance, sdk.skills.HolyFreeze, sdk.skills.Might, sdk.skills.Thorns].includes(checkStat)) { - // check items for aura granting one then subtract it's skillId - merc.getItemsEx().forEach(item => { - if (!item.unique && !item.runeword) return false; - switch (true) { - case (item.getStat(sdk.stats.SkillOnAura, sdk.skills.Meditation)): - return (checkStat -= sdk.skills.Meditation); - case (item.getStat(sdk.stats.SkillOnAura, sdk.skills.Conviction)): - return (checkStat -= sdk.skills.Conviction); - case (item.getStat(sdk.stats.SkillOnAura, sdk.skills.Concentration)): - return (checkStat -= sdk.skills.Concentration); - case (item.getStat(sdk.stats.SkillOnAura, sdk.skills.HolyFreeze)): - return (checkStat -= sdk.skills.HolyFreeze); - case (item.getStat(sdk.stats.SkillOnAura, sdk.skills.HolyFire)): - return (checkStat -= sdk.skills.HolyFire); - case (item.getStat(sdk.stats.SkillOnAura, sdk.skills.HolyShock)): - return (checkStat -= sdk.skills.HolyShock); - } - return true; - }); - } - return checkStat >= sdk.skills.Might ? checkStat : 0; - case sdk.mercs.IronWolf: - return [sdk.skills.IceBlast, sdk.skills.FireBall, sdk.skills.Lightning].find(s => merc.getSkill(s, sdk.skills.subindex.HardPoints)); - case sdk.mercs.A5Barb: - return sdk.skills.Bash; - default: - return 0; - } - })(); - - return mercSkill ? getSkillById(mercSkill) : ""; - }, - - /** - * @param {MercUnit} merc - * @returns {number} - */ - getMercDifficulty: function (merc) { - !merc && (merc = Misc.poll(() => me.getMerc(), 1000, 50)); - if (!merc) return false; - if (merc.classid !== sdk.mercs.Guard) return sdk.difficulty.Normal; - - let mercSkill = merc.getStat(sdk.stats.ModifierListSkill); - - switch (mercSkill) { - case sdk.skills.Thorns: - case sdk.skills.HolyFreeze: - case sdk.skills.Might: - return sdk.difficulty.Nightmare; - default: - return sdk.difficulty.Normal; - } - }, - - /** - * @param {MercUnit} merc - * @returns {number} - */ - getMercAct: function (merc) { - !merc && (merc = Misc.poll(() => me.getMerc(), 1000, 50)); - if (!merc) return 0; - return MercData.actMap.get(merc.classid) || 0; - }, - - /** - * @param {MercUnit} merc - */ - getMercInfo: function (merc) { - !merc && (merc = Misc.poll(() => me.getMerc(), 1000, 50)); - if (!merc) return { classid: 0, act: 0, difficulty: 0, type: "" }; - return { - classid: merc.classid, - act: this.getMercAct(merc), - difficulty: this.getMercDifficulty(merc), - skillName: this.getMercSkill(merc) - }; - }, - - /** - * - * @param {{ classid: number, act: number, skill: number, skillName: string, difficulty: number }} wanted - * @param {MercUnit} merc - * @returns {boolean} - */ - checkMercSkill: function (wanted, merc) { - merc = !!merc ? merc : me.getMerc(); - if (!merc) return false; - let mercSkill = merc.getStat(sdk.stats.ModifierListSkill); - - if (merc.classid === sdk.mercs.Guard) { - return mercSkill === wanted.skill; - } else { - return merc.getSkill(wanted.skill, sdk.skills.subindex.HardPoints) > 0; - } - }, - - // only supports act 2 mercs for now - hireMerc: function () { - if (me.classic) return true; - if (Mercenary.timeout && getTickCount() < Mercenary.timeout) return true; - let _a; - let { wantedMerc } = Check.finalBuild(); - let mercAct = (!me.accessToAct(2) && me.normal ? 1 : wantedMerc.act); - let tmpAuraName = "Defiance"; - let currMerc = me.data.merc; - - // don't hire if using correct a1 merc, or passed merc hire difficulty - // we've already gotten the correct a1 merc or haven't yet completed Bloodraven - // we are not in the correct difficulty to hire our wanted merc - // we don't have access to the act of our wanted merc - // we've already hired our wanted merc - // we aren't in our wanted mercs difficulty but we have already hired the correct temp a2 merc - // we've gone back a difficulty - (with using the data file it shouldn't get here but still handle it just in case) - // we don't have enough spare gold to buy a1 merc - // we don't have enough gold to hire our wanted merc - switch (true) { - case mercAct === 1 && (currMerc.skillName === "Cold Arrow" || !Misc.checkQuest(sdk.quest.id.SistersBurialGrounds, sdk.quest.states.Completed)): - case currMerc.skillName === wantedMerc.skillName: - case me.diff > wantedMerc.difficulty: - case me.diff === wantedMerc.difficulty && !me.accessToAct(wantedMerc.act): - case me.diff !== wantedMerc.difficulty && currMerc.skillName === "Defiance": - case (me.charlvl > CharInfo.levelCap + 10 && Mercenary.checkMercSkill(wantedMerc)): - case me.gold < Math.round((((me.charlvl - 1) * (me.charlvl - 1)) / 2) * 7.5): - case this.minCost > 0 && me.gold < this.minCost: - return true; - } - - // lets check what our current actually merc is - /** @type {MercUnit} */ - let checkMyMerc = Misc.poll(() => me.getMerc(), 50, 500); - - const wantedSkill = (mercAct === 1 - ? "Fire Arrow" === wantedMerc.skillName - ? wantedMerc.skillName - : "Cold Arrow" - : me.normal - ? tmpAuraName - : wantedMerc.skillName - ); - - if (checkMyMerc && Mercenary.checkMercSkill(wantedMerc, checkMyMerc)) { - // we have our wanted merc, data file was probably erased so lets re-update it - me.data.merc.act = Mercenary.getMercAct(checkMyMerc); - me.data.merc.classid = checkMyMerc.classid; - me.data.merc.difficulty = Mercenary.getMercDifficulty(checkMyMerc); - me.data.merc.skillName = wantedMerc.skillName; - me.data.merc.skill = MercData.findByName(me.data.merc.skillName, me.act).skill; - CharData.updateData("merc", me.data) && me.update(); - - return true; - } else if (!!checkMyMerc && checkMyMerc.classid === sdk.mercs.Guard) { - let checkSkill = checkMyMerc.getStat(sdk.stats.ModifierListSkill); - // aura isn't active so we can't check it - if (!checkSkill) return true; - // or we might have multiple aura's going - if ([sdk.skills.Meditation, sdk.skills.Conviction, sdk.skills.Concentration].includes(checkSkill)) return true; - if (checkSkill > sdk.skills.Conviction) return true; - } - - let MercLib_1 = require("../Modules/MercLib"); - try { - Town.goToTown(mercAct); - myPrint("ÿc9Mercenaryÿc0 :: getting merc"); - Town.move(Town.tasks[me.act - 1].Merc); - me.sortInventory(); - Item.removeItemsMerc(); // strip temp merc gear - delay(500 + me.ping); - - addEventListener("gamepacket", MercLib_1.mercPacket); - Town.initNPC("Merc", "getMerc"); - - delay(500); - - if (!MercLib_1.default.length) throw new Error("No mercs found"); - - let wantedMerc = MercLib_1.default - .filter((merc) => merc.skills.some((skill) => (skill === null || skill === void 0 ? void 0 : skill.name) === wantedSkill)) - .sort((a, b) => b.level - a.level) - .first(); - if (!wantedMerc) throw new Error("No merc found with skill " + wantedSkill); - if (wantedMerc.cost > me.gold) { - Mercenary.minCost = wantedMerc.cost; - throw new Error("Too expensive " + wantedMerc.cost); - } - - let oldGid_1 = (_a = me.getMercEx()) === null || _a === void 0 ? void 0 : _a.gid; - console.log("ÿc9Mercenaryÿc0 :: Found a merc to hire " + JSON.stringify(wantedMerc)); - - (wantedMerc === null || wantedMerc === void 0) - ? void 0 - : wantedMerc.hire(); - let newMerc = Misc.poll(function () { - let merc = me.getMerc(); - if (!merc) return false; - if (oldGid_1 && oldGid_1 === merc.gid) return false; - return merc; - }); - - console.log("Hired a merc?"); - if (newMerc) { - console.log("Yep"); - me.data.merc.act = me.act; - me.data.merc.classid = newMerc.classid; - me.data.merc.difficulty = me.diff; - me.data.merc.skillName = wantedMerc.skills.find(sk => sk.name === wantedSkill).name; - me.data.merc.skill = MercData.findByName(me.data.merc.skillName, me.act).skill; - CharData.updateData("merc", me.data) && me.update(); - console.log("ÿc9Mercenaryÿc0 :: " + me.data.merc.skillName + " merc hired."); - } - me.cancelUIFlags(); - while (getInteractedNPC()) { - delay(me.ping || 5); - me.cancel(); - } - } catch (e) { - console.error(e); - Mercenary.timeout = getTickCount() + Time.minutes(3); - } finally { - removeEventListener("gamepacket", MercLib_1.mercPacket); - } - - Item.autoEquipMerc(); - Pickit.pickItems(); // safetycheck for merc items on ground - Item.autoEquipMerc(); - - return true; - }, + minCost: -1, + timeout: 0, + + /** + * only a2 mercs for now, need to test others to see if ModifierListSkill returns their skill + * @param {MercUnit} merc + * @returns {string} + */ + getMercSkill: function (merc) { + !merc && (merc = Misc.poll(() => me.getMerc(), 1000, 50)); + if (!merc) return false; + let mercSkill = (() => { + switch (merc.classid) { + case sdk.mercs.Rogue: + return [sdk.skills.FireArrow, sdk.skills.ColdArrow].find(s => merc.getSkill(s, sdk.skills.subindex.HardPoints)); + case sdk.mercs.Guard: + let checkStat = merc.getStat(sdk.stats.ModifierListSkill); + // if ([sdk.skills.Meditation, sdk.skills.Conviction, sdk.skills.Concentration, sdk.skills.HolyFire].includes(checkStat)) { + // return [sdk.skills.Prayer, sdk.skills.BlessedAim, sdk.skills.Defiance].find(s => merc.getSkill(s, sdk.skills.subindex.HardPoints)); + // } + if (![sdk.skills.Prayer, sdk.skills.BlessedAim, sdk.skills.Defiance, sdk.skills.HolyFreeze, sdk.skills.Might, sdk.skills.Thorns].includes(checkStat)) { + // check items for aura granting one then subtract it's skillId + merc.getItemsEx().forEach(item => { + if (!item.unique && !item.runeword) return false; + switch (true) { + case (item.getStat(sdk.stats.SkillOnAura, sdk.skills.Meditation)): + return (checkStat -= sdk.skills.Meditation); + case (item.getStat(sdk.stats.SkillOnAura, sdk.skills.Conviction)): + return (checkStat -= sdk.skills.Conviction); + case (item.getStat(sdk.stats.SkillOnAura, sdk.skills.Concentration)): + return (checkStat -= sdk.skills.Concentration); + case (item.getStat(sdk.stats.SkillOnAura, sdk.skills.HolyFreeze)): + return (checkStat -= sdk.skills.HolyFreeze); + case (item.getStat(sdk.stats.SkillOnAura, sdk.skills.HolyFire)): + return (checkStat -= sdk.skills.HolyFire); + case (item.getStat(sdk.stats.SkillOnAura, sdk.skills.HolyShock)): + return (checkStat -= sdk.skills.HolyShock); + } + return true; + }); + } + return checkStat >= sdk.skills.Might ? checkStat : 0; + case sdk.mercs.IronWolf: + return [sdk.skills.IceBlast, sdk.skills.FireBall, sdk.skills.Lightning].find(s => merc.getSkill(s, sdk.skills.subindex.HardPoints)); + case sdk.mercs.A5Barb: + return sdk.skills.Bash; + default: + return 0; + } + })(); + + return mercSkill ? getSkillById(mercSkill) : ""; + }, + + /** + * @param {MercUnit} merc + * @returns {number} + */ + getMercDifficulty: function (merc) { + !merc && (merc = Misc.poll(() => me.getMerc(), 1000, 50)); + if (!merc) return false; + if (merc.classid !== sdk.mercs.Guard) return sdk.difficulty.Normal; + + let mercSkill = merc.getStat(sdk.stats.ModifierListSkill); + + switch (mercSkill) { + case sdk.skills.Thorns: + case sdk.skills.HolyFreeze: + case sdk.skills.Might: + return sdk.difficulty.Nightmare; + default: + return sdk.difficulty.Normal; + } + }, + + /** + * @param {MercUnit} merc + * @returns {number} + */ + getMercAct: function (merc) { + !merc && (merc = Misc.poll(() => me.getMerc(), 1000, 50)); + if (!merc) return 0; + return MercData.actMap.get(merc.classid) || 0; + }, + + /** + * @param {MercUnit} merc + */ + getMercInfo: function (merc) { + !merc && (merc = Misc.poll(() => me.getMerc(), 1000, 50)); + if (!merc) return { classid: 0, act: 0, difficulty: 0, type: "" }; + return { + classid: merc.classid, + act: this.getMercAct(merc), + difficulty: this.getMercDifficulty(merc), + skillName: this.getMercSkill(merc) + }; + }, + + /** + * + * @param {{ classid: number, act: number, skill: number, skillName: string, difficulty: number }} wanted + * @param {MercUnit} merc + * @returns {boolean} + */ + checkMercSkill: function (wanted, merc) { + merc = !!merc ? merc : me.getMerc(); + if (!merc) return false; + let mercSkill = merc.getStat(sdk.stats.ModifierListSkill); + + if (merc.classid === sdk.mercs.Guard) { + return mercSkill === wanted.skill; + } else { + return merc.getSkill(wanted.skill, sdk.skills.subindex.HardPoints) > 0; + } + }, + + // only supports act 2 mercs for now + hireMerc: function () { + if (me.classic) return true; + if (Mercenary.timeout && getTickCount() < Mercenary.timeout) return true; + let _a; + let { wantedMerc } = Check.finalBuild(); + let mercAct = (!me.accessToAct(2) && me.normal ? 1 : wantedMerc.act); + let tmpAuraName = "Defiance"; + let currMerc = me.data.merc; + + // don't hire if using correct a1 merc, or passed merc hire difficulty + // we've already gotten the correct a1 merc or haven't yet completed Bloodraven + // we are not in the correct difficulty to hire our wanted merc + // we don't have access to the act of our wanted merc + // we've already hired our wanted merc + // we aren't in our wanted mercs difficulty but we have already hired the correct temp a2 merc + // we've gone back a difficulty - (with using the data file it shouldn't get here but still handle it just in case) + // we don't have enough spare gold to buy a1 merc + // we don't have enough gold to hire our wanted merc + switch (true) { + case mercAct === 1 && (currMerc.skillName === "Cold Arrow" || !Misc.checkQuest(sdk.quest.id.SistersBurialGrounds, sdk.quest.states.Completed)): + case currMerc.skillName === wantedMerc.skillName: + case me.diff > wantedMerc.difficulty: + case me.diff === wantedMerc.difficulty && !me.accessToAct(wantedMerc.act): + case me.diff !== wantedMerc.difficulty && currMerc.skillName === "Defiance": + case (me.charlvl > CharInfo.levelCap + 10 && Mercenary.checkMercSkill(wantedMerc)): + case me.gold < Math.round((((me.charlvl - 1) * (me.charlvl - 1)) / 2) * 7.5): + case this.minCost > 0 && me.gold < this.minCost: + return true; + } + + // lets check what our current actually merc is + /** @type {MercUnit} */ + let checkMyMerc = Misc.poll(() => me.getMerc(), 50, 500); + + const wantedSkill = (mercAct === 1 + ? "Fire Arrow" === wantedMerc.skillName + ? wantedMerc.skillName + : "Cold Arrow" + : me.normal + ? tmpAuraName + : wantedMerc.skillName + ); + + if (checkMyMerc && Mercenary.checkMercSkill(wantedMerc, checkMyMerc)) { + // we have our wanted merc, data file was probably erased so lets re-update it + me.data.merc.act = Mercenary.getMercAct(checkMyMerc); + me.data.merc.classid = checkMyMerc.classid; + me.data.merc.difficulty = Mercenary.getMercDifficulty(checkMyMerc); + me.data.merc.skillName = wantedMerc.skillName; + me.data.merc.skill = MercData.findByName(me.data.merc.skillName, me.act).skill; + CharData.updateData("merc", me.data) && me.update(); + + return true; + } else if (!!checkMyMerc && checkMyMerc.classid === sdk.mercs.Guard) { + let checkSkill = checkMyMerc.getStat(sdk.stats.ModifierListSkill); + // aura isn't active so we can't check it + if (!checkSkill) return true; + // or we might have multiple aura's going + if ([sdk.skills.Meditation, sdk.skills.Conviction, sdk.skills.Concentration].includes(checkSkill)) return true; + if (checkSkill > sdk.skills.Conviction) return true; + } + + let MercLib_1 = require("../Modules/MercLib"); + try { + Town.goToTown(mercAct); + myPrint("ÿc9Mercenaryÿc0 :: getting merc"); + Town.move(Town.tasks[me.act - 1].Merc); + me.sortInventory(); + Item.removeItemsMerc(); // strip temp merc gear + delay(500 + me.ping); + + addEventListener("gamepacket", MercLib_1.mercPacket); + Town.initNPC("Merc", "getMerc"); + + delay(500); + + if (!MercLib_1.default.length) throw new Error("No mercs found"); + + let wantedMerc = MercLib_1.default + .filter((merc) => merc.skills.some((skill) => (skill === null || skill === void 0 ? void 0 : skill.name) === wantedSkill)) + .sort((a, b) => b.level - a.level) + .first(); + if (!wantedMerc) throw new Error("No merc found with skill " + wantedSkill); + if (wantedMerc.cost > me.gold) { + Mercenary.minCost = wantedMerc.cost; + throw new Error("Too expensive " + wantedMerc.cost); + } + + let oldGid_1 = (_a = me.getMercEx()) === null || _a === void 0 ? void 0 : _a.gid; + console.log("ÿc9Mercenaryÿc0 :: Found a merc to hire " + JSON.stringify(wantedMerc)); + + (wantedMerc === null || wantedMerc === void 0) + ? void 0 + : wantedMerc.hire(); + let newMerc = Misc.poll(function () { + let merc = me.getMerc(); + if (!merc) return false; + if (oldGid_1 && oldGid_1 === merc.gid) return false; + return merc; + }); + + console.log("Hired a merc?"); + if (newMerc) { + console.log("Yep"); + me.data.merc.act = me.act; + me.data.merc.classid = newMerc.classid; + me.data.merc.difficulty = me.diff; + me.data.merc.skillName = wantedMerc.skills.find(sk => sk.name === wantedSkill).name; + me.data.merc.skill = MercData.findByName(me.data.merc.skillName, me.act).skill; + CharData.updateData("merc", me.data) && me.update(); + console.log("ÿc9Mercenaryÿc0 :: " + me.data.merc.skillName + " merc hired."); + } + me.cancelUIFlags(); + while (getInteractedNPC()) { + delay(me.ping || 5); + me.cancel(); + } + } catch (e) { + console.error(e); + Mercenary.timeout = getTickCount() + Time.minutes(3); + } finally { + removeEventListener("gamepacket", MercLib_1.mercPacket); + } + + Item.autoEquipMerc(); + Pickit.pickItems(); // safetycheck for merc items on ground + Item.autoEquipMerc(); + + return true; + }, }; diff --git a/libs/SoloPlay/Functions/MiscOverrides.js b/libs/SoloPlay/Functions/MiscOverrides.js index d0a81237..e73a7ef9 100644 --- a/libs/SoloPlay/Functions/MiscOverrides.js +++ b/libs/SoloPlay/Functions/MiscOverrides.js @@ -11,9 +11,9 @@ includeIfNotIncluded("core/Misc.js"); Misc.openChestsEnabled = true; Misc.screenshotErrors = true; Misc.presetChestIds = [ - 5, 6, 87, 104, 105, 106, 107, 143, 140, 141, 144, 146, 147, 148, 176, 177, 181, 183, 198, 240, 241, - 242, 243, 329, 330, 331, 332, 333, 334, 335, 336, 354, 355, 356, 371, 387, 389, 390, 391, 397, 405, - 406, 407, 413, 420, 424, 425, 430, 431, 432, 433, 454, 455, 501, 502, 504, 505, 580, 581 + 5, 6, 87, 104, 105, 106, 107, 143, 140, 141, 144, 146, 147, 148, 176, 177, 181, 183, 198, 240, 241, + 242, 243, 329, 330, 331, 332, 333, 334, 335, 336, 354, 355, 356, 371, 387, 389, 390, 391, 397, 405, + 406, 407, 413, 420, 424, 425, 430, 431, 432, 433, 454, 455, 501, 502, 504, 505, 580, 581 ]; /** @@ -24,40 +24,40 @@ Misc.presetChestIds = [ * @returns {boolean} */ Misc.openChestsInArea = function (area, chestIds = [], sort = undefined) { - !area && (area = me.area); - area !== me.area && Pather.journeyTo(area); - - let presetUnits = Game.getPresetObjects(area); - if (!presetUnits) return false; - - !chestIds.length && (chestIds = Misc.presetChestIds.slice(0)); - - let coords = []; - - while (presetUnits.length > 0) { - if (chestIds.includes(presetUnits[0].id)) { - coords.push({ - x: presetUnits[0].roomx * 5 + presetUnits[0].x, - y: presetUnits[0].roomy * 5 + presetUnits[0].y - }); - } - - presetUnits.shift(); - } - - while (coords.length) { - coords.sort(sort ? sort : Sort.units); - Pather.moveToUnit(coords[0], 1, 2); - this.openChests(20); - - for (let i = 0; i < coords.length; i += 1) { - if (getDistance(coords[i].x, coords[i].y, coords[0].x, coords[0].y) < 20) { - coords.shift(); - } - } - } - - return true; + !area && (area = me.area); + area !== me.area && Pather.journeyTo(area); + + let presetUnits = Game.getPresetObjects(area); + if (!presetUnits) return false; + + !chestIds.length && (chestIds = Misc.presetChestIds.slice(0)); + + let coords = []; + + while (presetUnits.length > 0) { + if (chestIds.includes(presetUnits[0].id)) { + coords.push({ + x: presetUnits[0].roomx * 5 + presetUnits[0].x, + y: presetUnits[0].roomy * 5 + presetUnits[0].y + }); + } + + presetUnits.shift(); + } + + while (coords.length) { + coords.sort(sort ? sort : Sort.units); + Pather.moveToUnit(coords[0], 1, 2); + this.openChests(20); + + for (let i = 0; i < coords.length; i += 1) { + if (getDistance(coords[i].x, coords[i].y, coords[0].x, coords[0].y) < 20) { + coords.shift(); + } + } + } + + return true; }; /** @@ -66,43 +66,43 @@ Misc.openChestsInArea = function (area, chestIds = [], sort = undefined) { * @returns {boolean} If we opened the chest */ Misc.openChest = function (unit) { - typeof unit === "number" && (unit = Game.getObject(unit)); - - // Skip invalid/open and Countess chests - if (!unit || unit.x === 12526 || unit.x === 12565 || unit.mode) return false; - // locked chest, no keys - if (!me.assassin && unit.islocked && !me.findItem(sdk.items.Key, sdk.items.mode.inStorage, sdk.storage.Inventory)) return false; - - let specialChest = sdk.quest.chests.includes(unit.classid); - - for (let i = 0; i < 7; i++) { - // don't use tk if we are right next to it - let useTK = (unit.distance > 5 && Skill.useTK(unit) && i < 3); - let useDodge = Pather.useTeleport() && Skill.useTK(unit); - if (useTK) { - unit.distance > 18 && Attack.getIntoPosition(unit, 18, sdk.collision.WallOrRanged, false, true); - if (!Skill.cast(sdk.skills.Telekinesis, sdk.skills.hand.Right, unit)) { - console.debug("Failed to tk: attempt: " + i); - continue; - } - } else { - if (useDodge && me.inDanger()) { - if (Attack.getIntoPosition(unit, 18, sdk.collision.WallOrRanged, false, true)) continue; - } - [(unit.x + 1), (unit.y + 2)].distance > 5 && Pather.moveTo(unit.x + 1, unit.y + 2, 3); - (specialChest || i > 2) ? Misc.click(0, 0, unit) : Packet.entityInteract(unit); - } - - if (Misc.poll(() => !unit || unit.mode, 1000, 50)) { - return true; - } - Packet.flash(me.gid); - } - - // Click to stop walking in case we got stuck - !me.idle && Misc.click(0, 0, me.x, me.y); - - return false; + typeof unit === "number" && (unit = Game.getObject(unit)); + + // Skip invalid/open and Countess chests + if (!unit || unit.x === 12526 || unit.x === 12565 || unit.mode) return false; + // locked chest, no keys + if (!me.assassin && unit.islocked && !me.findItem(sdk.items.Key, sdk.items.mode.inStorage, sdk.storage.Inventory)) return false; + + let specialChest = sdk.quest.chests.includes(unit.classid); + + for (let i = 0; i < 7; i++) { + // don't use tk if we are right next to it + let useTK = (unit.distance > 5 && Skill.useTK(unit) && i < 3); + let useDodge = Pather.useTeleport() && Skill.useTK(unit); + if (useTK) { + unit.distance > 18 && Attack.getIntoPosition(unit, 18, sdk.collision.WallOrRanged, false, true); + if (!Skill.cast(sdk.skills.Telekinesis, sdk.skills.hand.Right, unit)) { + console.debug("Failed to tk: attempt: " + i); + continue; + } + } else { + if (useDodge && me.inDanger()) { + if (Attack.getIntoPosition(unit, 18, sdk.collision.WallOrRanged, false, true)) continue; + } + [(unit.x + 1), (unit.y + 2)].distance > 5 && Pather.moveTo(unit.x + 1, unit.y + 2, 3); + (specialChest || i > 2) ? Misc.click(0, 0, unit) : Packet.entityInteract(unit); + } + + if (Misc.poll(() => !unit || unit.mode, 1000, 50)) { + return true; + } + Packet.flash(me.gid); + } + + // Click to stop walking in case we got stuck + !me.idle && Misc.click(0, 0, me.x, me.y); + + return false; }; /** @@ -111,48 +111,48 @@ Misc.openChest = function (unit) { * @todo Take path parameter to we can open the chests in an order that brings us closer to our destination */ Misc.openChests = function (range = 15) { - if (!Misc.openChestsEnabled) return false; - const containers = [ - "chest", "loose rock", "hidden stash", "loose boulder", "corpseonstick", "casket", "armorstand", "weaponrack", - "holeanim", "roguecorpse", "corpse", "tomb2", "tomb3", "chest3", - "skeleton", "guardcorpse", "sarcophagus", "object2", "cocoon", "hollow log", "hungskeleton", - "bonechest", "woodchestl", "woodchestr", - "burialchestr", "burialchestl", "chestl", "chestr", "groundtomb", "tomb3l", "tomb1l", - "deadperson", "deadperson2", "groundtombl", "casket" - ]; - - if (Config.OpenChests.Types.some((el) => el.toLowerCase() === "all")) { - containers.push( - "barrel", "ratnest", "goo pile", "largeurn", "urn", "jug", "basket", "stash", - "pillar", "skullpile", "skull pile", "jar3", "jar2", "jar1", "barrel wilderness", - "explodingchest", "icecavejar1", "icecavejar2", "icecavejar3", - "icecavejar4", "evilurn" - ); - } - - me.baal && containers.push("evilurn"); - - let unitList = getUnits(sdk.unittype.Object) - .filter(c => c.name && c.mode === sdk.objects.mode.Inactive && c.distance <= range && containers.includes(c.name.toLowerCase())); - - while (unitList.length > 0) { - unitList.sort(Sort.units); - let unit = unitList.shift(); - - if (unit) { - // check mob count at chest - think I need a new prototype for faster checking - // allow specifying an amount and return true/false, rather than building the whole list then deciding what amount is too much - // possibly also specify a danger modifier - 3 champions around a chest is much more dangerous than 3 fallens - // also think we need to take into account mob count arround us, we shouldn't open chests when we are surrounded and in the process of clearing - // that needs a handler as well though, if we aren't clearing and are just pathing (tele char) opening a chest and moving on is fine - } - - if (unit && (Pather.useTeleport() || !checkCollision(me, unit, sdk.collision.WallOrRanged)) && this.openChest(unit)) { - Pickit.pickItems(); - } - } - - return true; + if (!Misc.openChestsEnabled) return false; + const containers = [ + "chest", "loose rock", "hidden stash", "loose boulder", "corpseonstick", "casket", "armorstand", "weaponrack", + "holeanim", "roguecorpse", "corpse", "tomb2", "tomb3", "chest3", + "skeleton", "guardcorpse", "sarcophagus", "object2", "cocoon", "hollow log", "hungskeleton", + "bonechest", "woodchestl", "woodchestr", + "burialchestr", "burialchestl", "chestl", "chestr", "groundtomb", "tomb3l", "tomb1l", + "deadperson", "deadperson2", "groundtombl", "casket" + ]; + + if (Config.OpenChests.Types.some((el) => el.toLowerCase() === "all")) { + containers.push( + "barrel", "ratnest", "goo pile", "largeurn", "urn", "jug", "basket", "stash", + "pillar", "skullpile", "skull pile", "jar3", "jar2", "jar1", "barrel wilderness", + "explodingchest", "icecavejar1", "icecavejar2", "icecavejar3", + "icecavejar4", "evilurn" + ); + } + + me.baal && containers.push("evilurn"); + + let unitList = getUnits(sdk.unittype.Object) + .filter(c => c.name && c.mode === sdk.objects.mode.Inactive && c.distance <= range && containers.includes(c.name.toLowerCase())); + + while (unitList.length > 0) { + unitList.sort(Sort.units); + let unit = unitList.shift(); + + if (unit) { + // check mob count at chest - think I need a new prototype for faster checking + // allow specifying an amount and return true/false, rather than building the whole list then deciding what amount is too much + // possibly also specify a danger modifier - 3 champions around a chest is much more dangerous than 3 fallens + // also think we need to take into account mob count arround us, we shouldn't open chests when we are surrounded and in the process of clearing + // that needs a handler as well though, if we aren't clearing and are just pathing (tele char) opening a chest and moving on is fine + } + + if (unit && (Pather.useTeleport() || !checkCollision(me, unit, sdk.collision.WallOrRanged)) && this.openChest(unit)) { + Pickit.pickItems(); + } + } + + return true; }; /** @@ -160,49 +160,49 @@ Misc.openChests = function (range = 15) { * @returns {boolean} */ Misc.getWell = function (unit) { - if (!unit || unit.mode === sdk.objects.mode.Active) return false; - - for (let i = 0; i < 3; i++) { - if (Skill.useTK(unit) && i < 2) { - unit.distance > 21 && Pather.moveNearUnit(unit, 20); - checkCollision(me, unit, sdk.collision.Ranged) && Attack.getIntoPosition(unit, 20, sdk.collision.Ranged); - Skill.cast(sdk.skills.Telekinesis, sdk.skills.hand.Right, unit); - } else { - if (unit.distance < 4 || Pather.moveToUnit(unit, 3, 0)) { - Misc.click(0, 0, unit); - } - } - - if (Misc.poll(() => unit.mode, 1000, 50)) return true; - Packet.flash(me.gid); - } - - return false; + if (!unit || unit.mode === sdk.objects.mode.Active) return false; + + for (let i = 0; i < 3; i++) { + if (Skill.useTK(unit) && i < 2) { + unit.distance > 21 && Pather.moveNearUnit(unit, 20); + checkCollision(me, unit, sdk.collision.Ranged) && Attack.getIntoPosition(unit, 20, sdk.collision.Ranged); + Skill.cast(sdk.skills.Telekinesis, sdk.skills.hand.Right, unit); + } else { + if (unit.distance < 4 || Pather.moveToUnit(unit, 3, 0)) { + Misc.click(0, 0, unit); + } + } + + if (Misc.poll(() => unit.mode, 1000, 50)) return true; + Packet.flash(me.gid); + } + + return false; }; Misc.useWell = function (range = 15) { - // I'm in perfect health, don't need this shit - if (me.hpPercent >= 95 && me.mpPercent >= 95 && me.staminaPercent >= 50 - && [sdk.states.Frozen, sdk.states.Poison, sdk.states.AmplifyDamage, sdk.states.Decrepify].every((states) => !me.getState(states))) { - return true; - } + // I'm in perfect health, don't need this shit + if (me.hpPercent >= 95 && me.mpPercent >= 95 && me.staminaPercent >= 50 + && [sdk.states.Frozen, sdk.states.Poison, sdk.states.AmplifyDamage, sdk.states.Decrepify].every((states) => !me.getState(states))) { + return true; + } - Pather.canTeleport() && me.hpPercent < 60 && (range = 25); + Pather.canTeleport() && me.hpPercent < 60 && (range = 25); - let unitList = getUnits(sdk.unittype.Object, "well").filter(function (well) { - return well.distance < range && well.mode !== sdk.objects.mode.Active; - }); + let unitList = getUnits(sdk.unittype.Object, "well").filter(function (well) { + return well.distance < range && well.mode !== sdk.objects.mode.Active; + }); - while (unitList.length > 0) { - unitList.sort(Sort.units); - let unit = unitList.shift(); + while (unitList.length > 0) { + unitList.sort(Sort.units); + let unit = unitList.shift(); - if (unit && (Pather.useTeleport() || !checkCollision(me, unit, sdk.collision.WallOrRanged))) { - this.getWell(unit); - } - } + if (unit && (Pather.useTeleport() || !checkCollision(me, unit, sdk.collision.WallOrRanged))) { + this.getWell(unit); + } + } - return true; + return true; }; const shrineMap = new Map(); @@ -230,98 +230,98 @@ shrineMap.set(sdk.shrines.Exploding, 0); shrineMap.set(sdk.shrines.Poison, 0); Misc.scanShrines = function (range, ignore = []) { - if (!Config.ScanShrines.length) return false; - - !range && (range = Pather.useTeleport() ? 25 : 15); - !Array.isArray(ignore) && (ignore = [ignore]); - - /** @type {ObjectUnit[]} */ - let shrineList = []; - - const rangeCheck = (shrineType) => { - switch (true) { - case shrineType === sdk.shrines.Refilling && (me.hpPercent < 50 || me.mpPercent < 50 || me.staminaPercent < 50): - case shrineType === sdk.shrines.Mana && me.mpPercent < 50: - case shrineType === sdk.shrines.ManaRecharge && me.mpPercent < 50 && me.charlvl < 20: - case [sdk.shrines.Skill, sdk.shrines.Experience].includes(shrineType): - return 30; - case [sdk.shrines.Poison, sdk.shrines.Exploding].includes(shrineType): - return 15; - } - return range; - }; - - // add exploding/poision shrines - if (me.normal) { - Config.ScanShrines.indexOf(sdk.shrines.Poison) === -1 && Config.ScanShrines.push(sdk.shrines.Poison); - Config.ScanShrines.indexOf(sdk.shrines.Exploding) === -1 && Config.ScanShrines.push(sdk.shrines.Exploding); - } - - // Initiate shrine states - if (!this.shrineStates) { - this.shrineStates = []; - let i = 0; - for (let shrine of Config.ScanShrines) { - if (shrine > 0) { - this.shrineStates[i] = shrineMap.get(shrine); - i++; - } - } - } - - /** - * @todo - We should build a list of shrines by their preset values when we scan the area - */ - - let shrine = Game.getObject(); - - /** - * Fix for a3/a5 shrines - */ - if (shrine) { - let index = -1; - - // Build a list of nearby shrines - do { - if (shrine.name.toLowerCase().includes("shrine") && shrineMap.has(shrine.objtype) - && shrine.mode === sdk.objects.mode.Inactive && !ignore.includes(shrine.objtype) - && getDistance(me.x, me.y, shrine.x, shrine.y) <= rangeCheck(shrine.objtype)) { - shrineList.push(copyUnit(shrine)); - } - } while (shrine.getNext()); - - if (!shrineList.length) return false; - - // Check if we have a shrine state, store its index if yes - for (let i = 0; i < this.shrineStates.length; i += 1) { - if (me.getState(this.shrineStates[i])) { - index = i; - - break; - } - } - - for (let i = 0; i < Config.ScanShrines.length; i += 1) { - for (let shrine of shrineList) { - // Get the shrine if we have no active state or to refresh current state or if the shrine has no state - // Don't override shrine state with a lesser priority shrine - // todo - check to make sure we can actually get the shrine for ones without states - // can't grab a health shrine if we are in perfect health, can't grab mana shrine if our mana is maxed - if (index === -1 || i <= index || this.shrineStates[i] === 0) { - if (shrine.objtype === Config.ScanShrines[i] && (Pather.useTeleport() || !checkCollision(me, shrine, sdk.collision.WallOrRanged))) { - this.getShrine(shrine); - - // Gem shrine - pick gem - if (Config.ScanShrines[i] === sdk.shrines.Gem) { - Pickit.pickItems(); - } - } - } - } - } - } - - return true; + if (!Config.ScanShrines.length) return false; + + !range && (range = Pather.useTeleport() ? 25 : 15); + !Array.isArray(ignore) && (ignore = [ignore]); + + /** @type {ObjectUnit[]} */ + let shrineList = []; + + const rangeCheck = (shrineType) => { + switch (true) { + case shrineType === sdk.shrines.Refilling && (me.hpPercent < 50 || me.mpPercent < 50 || me.staminaPercent < 50): + case shrineType === sdk.shrines.Mana && me.mpPercent < 50: + case shrineType === sdk.shrines.ManaRecharge && me.mpPercent < 50 && me.charlvl < 20: + case [sdk.shrines.Skill, sdk.shrines.Experience].includes(shrineType): + return 30; + case [sdk.shrines.Poison, sdk.shrines.Exploding].includes(shrineType): + return 15; + } + return range; + }; + + // add exploding/poision shrines + if (me.normal) { + Config.ScanShrines.indexOf(sdk.shrines.Poison) === -1 && Config.ScanShrines.push(sdk.shrines.Poison); + Config.ScanShrines.indexOf(sdk.shrines.Exploding) === -1 && Config.ScanShrines.push(sdk.shrines.Exploding); + } + + // Initiate shrine states + if (!this.shrineStates) { + this.shrineStates = []; + let i = 0; + for (let shrine of Config.ScanShrines) { + if (shrine > 0) { + this.shrineStates[i] = shrineMap.get(shrine); + i++; + } + } + } + + /** + * @todo - We should build a list of shrines by their preset values when we scan the area + */ + + let shrine = Game.getObject(); + + /** + * Fix for a3/a5 shrines + */ + if (shrine) { + let index = -1; + + // Build a list of nearby shrines + do { + if (shrine.name.toLowerCase().includes("shrine") && shrineMap.has(shrine.objtype) + && shrine.mode === sdk.objects.mode.Inactive && !ignore.includes(shrine.objtype) + && getDistance(me.x, me.y, shrine.x, shrine.y) <= rangeCheck(shrine.objtype)) { + shrineList.push(copyUnit(shrine)); + } + } while (shrine.getNext()); + + if (!shrineList.length) return false; + + // Check if we have a shrine state, store its index if yes + for (let i = 0; i < this.shrineStates.length; i += 1) { + if (me.getState(this.shrineStates[i])) { + index = i; + + break; + } + } + + for (let i = 0; i < Config.ScanShrines.length; i += 1) { + for (let shrine of shrineList) { + // Get the shrine if we have no active state or to refresh current state or if the shrine has no state + // Don't override shrine state with a lesser priority shrine + // todo - check to make sure we can actually get the shrine for ones without states + // can't grab a health shrine if we are in perfect health, can't grab mana shrine if our mana is maxed + if (index === -1 || i <= index || this.shrineStates[i] === 0) { + if (shrine.objtype === Config.ScanShrines[i] && (Pather.useTeleport() || !checkCollision(me, shrine, sdk.collision.WallOrRanged))) { + this.getShrine(shrine); + + // Gem shrine - pick gem + if (Config.ScanShrines[i] === sdk.shrines.Gem) { + Pickit.pickItems(); + } + } + } + } + } + } + + return true; }; Misc.presetShrineIds = [2, 81, 83]; @@ -336,84 +336,84 @@ Misc.presetShrineIds = [2, 81, 83]; * of getUnit and can see the shrine type so we know whether to continue moving to it or not. */ Misc.getShrinesInArea = function (area, type, use) { - let shrineLocs = []; - let result = false; - let unit = Game.getPresetObjects(area); - - if (unit) { - for (let i = 0; i < unit.length; i += 1) { - if (Misc.presetShrineIds.includes(unit[i].id)) { - Misc.presetShrineIds.push([unit[i].roomx * 5 + unit[i].x, unit[i].roomy * 5 + unit[i].y]); - } - } - } - - try { - NodeAction.shrinesToIgnore.push(type); - - while (shrineLocs.length > 0) { - shrineLocs.sort(Sort.points); - let coords = shrineLocs.shift(); - - Pather.moveToEx(coords[0], coords[1], { minDist: Skill.haveTK ? 20 : 5, callback: () => { - let shrine = Game.getObject("shrine"); - // for now until I write a proper isShrineWanted check, get close enough that nodeaction checks it - return !!shrine && shrine.x === coords[0] && shrine.y === coords[1] && shrine.distance <= 15; - } }); - - let shrine = Game.getObject("shrine"); - - if (shrine) { - do { - if (shrine.objtype === type && shrine.mode === sdk.objects.mode.Inactive) { - (!Skill.haveTK || !use) && Pather.moveTo(shrine.x - 2, shrine.y - 2); - - if (!use || this.getShrine(shrine)) { - result = true; - - if (type === sdk.shrines.Gem) { - Pickit.pickItems(); - } - return true; - } - - if (use && type >= sdk.shrines.Armor && type <= sdk.shrines.Experience && me.getState(type + 122)) { - return true; - } - } - } while (shrine.getNext()); - } - } - } finally { - NodeAction.shrinesToIgnore.remove(type); - } - - return result; + let shrineLocs = []; + let result = false; + let unit = Game.getPresetObjects(area); + + if (unit) { + for (let i = 0; i < unit.length; i += 1) { + if (Misc.presetShrineIds.includes(unit[i].id)) { + Misc.presetShrineIds.push([unit[i].roomx * 5 + unit[i].x, unit[i].roomy * 5 + unit[i].y]); + } + } + } + + try { + NodeAction.shrinesToIgnore.push(type); + + while (shrineLocs.length > 0) { + shrineLocs.sort(Sort.points); + let coords = shrineLocs.shift(); + + Pather.moveToEx(coords[0], coords[1], { minDist: Skill.haveTK ? 20 : 5, callback: () => { + let shrine = Game.getObject("shrine"); + // for now until I write a proper isShrineWanted check, get close enough that nodeaction checks it + return !!shrine && shrine.x === coords[0] && shrine.y === coords[1] && shrine.distance <= 15; + } }); + + let shrine = Game.getObject("shrine"); + + if (shrine) { + do { + if (shrine.objtype === type && shrine.mode === sdk.objects.mode.Inactive) { + (!Skill.haveTK || !use) && Pather.moveTo(shrine.x - 2, shrine.y - 2); + + if (!use || this.getShrine(shrine)) { + result = true; + + if (type === sdk.shrines.Gem) { + Pickit.pickItems(); + } + return true; + } + + if (use && type >= sdk.shrines.Armor && type <= sdk.shrines.Experience && me.getState(type + 122)) { + return true; + } + } + } while (shrine.getNext()); + } + } + } finally { + NodeAction.shrinesToIgnore.remove(type); + } + + return result; }; Misc.getExpShrine = function (shrineLocs = []) { - if (me.getState(sdk.states.ShrineExperience)) return true; + if (me.getState(sdk.states.ShrineExperience)) return true; - for (let get = 0; get < shrineLocs.length; get++) { - me.overhead("Looking for xp shrine"); + for (let get = 0; get < shrineLocs.length; get++) { + me.overhead("Looking for xp shrine"); - if (shrineLocs[get] === sdk.areas.BloodMoor) { - Pather.journeyTo(shrineLocs[get]); - } else { - Pather.checkWP(shrineLocs[get], true) ? Pather.useWaypoint(shrineLocs[get]) : Pather.getWP(shrineLocs[get]); - } + if (shrineLocs[get] === sdk.areas.BloodMoor) { + Pather.journeyTo(shrineLocs[get]); + } else { + Pather.checkWP(shrineLocs[get], true) ? Pather.useWaypoint(shrineLocs[get]) : Pather.getWP(shrineLocs[get]); + } - Precast.doPrecast(true); - Misc.getShrinesInArea(shrineLocs[get], sdk.shrines.Experience, true); + Precast.doPrecast(true); + Misc.getShrinesInArea(shrineLocs[get], sdk.shrines.Experience, true); - if (me.getState(sdk.states.ShrineExperience)) { - break; - } + if (me.getState(sdk.states.ShrineExperience)) { + break; + } - !me.inTown && Town.goToTown(); - } + !me.inTown && Town.goToTown(); + } - return true; + return true; }; /** @@ -421,81 +421,81 @@ Misc.getExpShrine = function (shrineLocs = []) { * @returns {boolean} */ Misc.unsocketItem = function (item) { - if (me.classic || !me.getItem(sdk.items.quest.Cube) || !item) return false; - // Item doesn't have anything socketed - if (item.getItemsEx().length === 0) return true; - - let hel = me.getItem(sdk.items.runes.Hel, sdk.items.mode.inStorage); - if (!hel) return false; - - let scroll = Runewords.getScroll(); - let bodyLoc; - let { classid, quality } = item; - item.isEquipped && (bodyLoc = item.bodylocation); - - // failed to get scroll or open stash most likely means we're stuck somewhere in town, so it's better to return false - if (!scroll || !Town.openStash() || !Cubing.emptyCube()) return false; - - try { - // failed to move any of the items to the cube - if (!Storage.Cube.MoveTo(item) || !Storage.Cube.MoveTo(hel) || !Storage.Cube.MoveTo(scroll)) throw "Failed to move items to cube"; - - // probably only happens on server crash - if (!Cubing.openCube()) throw "Failed to open cube"; - - myPrint("ÿc4Removing sockets from: ÿc0" + item.fname.split("\n").reverse().join(" ").replace(/ÿc[0-9!"+<;.*]/, "")); - transmute(); - delay(500); - // unsocketing an item causes loss of reference, so re-find our item - item = me.findItem(classid, -1, sdk.storage.Cube); - !!item && bodyLoc && item.equip(bodyLoc); - - // can't pull the item out = no space = fail - if (!Cubing.emptyCube()) throw "Failed to empty cube"; - } catch (e) { - console.debug(e); - } finally { - // lost the item, so relocate it - !item && (item = me.findItem(classid, -1, -1, quality)); - // In case error was thrown before hitting above re-equip statement - bodyLoc && !item.isEquipped && item.equip(bodyLoc); - // No bodyloc so move back to stash - !bodyLoc && !item.isInStash && Storage.Stash.MoveTo(item); - getUIFlag(sdk.uiflags.Cube) && me.cancel(); - } - - return item.getItemsEx().length === 0; + if (me.classic || !me.getItem(sdk.items.quest.Cube) || !item) return false; + // Item doesn't have anything socketed + if (item.getItemsEx().length === 0) return true; + + let hel = me.getItem(sdk.items.runes.Hel, sdk.items.mode.inStorage); + if (!hel) return false; + + let scroll = Runewords.getScroll(); + let bodyLoc; + let { classid, quality } = item; + item.isEquipped && (bodyLoc = item.bodylocation); + + // failed to get scroll or open stash most likely means we're stuck somewhere in town, so it's better to return false + if (!scroll || !Town.openStash() || !Cubing.emptyCube()) return false; + + try { + // failed to move any of the items to the cube + if (!Storage.Cube.MoveTo(item) || !Storage.Cube.MoveTo(hel) || !Storage.Cube.MoveTo(scroll)) throw "Failed to move items to cube"; + + // probably only happens on server crash + if (!Cubing.openCube()) throw "Failed to open cube"; + + myPrint("ÿc4Removing sockets from: ÿc0" + item.fname.split("\n").reverse().join(" ").replace(/ÿc[0-9!"+<;.*]/, "")); + transmute(); + delay(500); + // unsocketing an item causes loss of reference, so re-find our item + item = me.findItem(classid, -1, sdk.storage.Cube); + !!item && bodyLoc && item.equip(bodyLoc); + + // can't pull the item out = no space = fail + if (!Cubing.emptyCube()) throw "Failed to empty cube"; + } catch (e) { + console.debug(e); + } finally { + // lost the item, so relocate it + !item && (item = me.findItem(classid, -1, -1, quality)); + // In case error was thrown before hitting above re-equip statement + bodyLoc && !item.isEquipped && item.equip(bodyLoc); + // No bodyloc so move back to stash + !bodyLoc && !item.isInStash && Storage.Stash.MoveTo(item); + getUIFlag(sdk.uiflags.Cube) && me.cancel(); + } + + return item.getItemsEx().length === 0; }; Misc.checkItemsForSocketing = function () { - if (me.classic || !me.getQuest(sdk.quest.id.SiegeOnHarrogath, sdk.quest.states.ReqComplete)) return false; + if (me.classic || !me.getQuest(sdk.quest.id.SiegeOnHarrogath, sdk.quest.states.ReqComplete)) return false; - let items = me.getItemsEx() - .filter(item => item.sockets === 0 && getBaseStat("items", item.classid, "gemsockets") > 0) - .sort((a, b) => NTIP.GetTier(b) - NTIP.GetTier(a)); + let items = me.getItemsEx() + .filter(item => item.sockets === 0 && getBaseStat("items", item.classid, "gemsockets") > 0) + .sort((a, b) => NTIP.GetTier(b) - NTIP.GetTier(a)); - for (let i = 0; i < items.length; i++) { - let curr = Config.socketables.find(({ classid }) => items[i].classid === classid); - if (curr && curr.condition(items[i]) && curr.useSocketQuest) { - return items[i]; - } - } + for (let i = 0; i < items.length; i++) { + let curr = Config.socketables.find(({ classid }) => items[i].classid === classid); + if (curr && curr.condition(items[i]) && curr.useSocketQuest) { + return items[i]; + } + } - return false; + return false; }; Misc.checkItemsForImbueing = function () { - if (!me.getQuest(sdk.quest.id.ToolsoftheTrade, sdk.quest.states.ReqComplete)) return false; + if (!me.getQuest(sdk.quest.id.ToolsoftheTrade, sdk.quest.states.ReqComplete)) return false; - let items = me.getItemsEx().filter(item => item.sockets === 0 && (item.normal || item.superior)); + let items = me.getItemsEx().filter(item => item.sockets === 0 && (item.normal || item.superior)); - for (let i = 0; i < items.length; i++) { - if (Config.imbueables.some(item => item.name === items[i].classid && Item.canEquip(items[i]))) { - return items[i]; - } - } + for (let i = 0; i < items.length; i++) { + if (Config.imbueables.some(item => item.name === items[i].classid && Item.canEquip(items[i]))) { + return items[i]; + } + } - return false; + return false; }; /** @@ -504,57 +504,57 @@ Misc.checkItemsForImbueing = function () { * @returns {boolean} */ Misc.addSocketablesToItem = function (item, runes = []) { - if (!item || item.sockets === 0) return false; - let preSockets = item.getItemsEx().length; - let original = preSockets; - let bodyLoc; + if (!item || item.sockets === 0) return false; + let preSockets = item.getItemsEx().length; + let original = preSockets; + let bodyLoc; - if (item.isEquipped) { - bodyLoc = item.bodylocation; + if (item.isEquipped) { + bodyLoc = item.bodylocation; - if (!Storage.Inventory.CanFit(item)) { - me.sortInventory(); + if (!Storage.Inventory.CanFit(item)) { + me.sortInventory(); - if (!Storage.Inventory.CanFit(item) && !Storage.Inventory.MoveTo(item)) { - console.log("ÿc8AddSocketableToItemÿc0 :: No space to get item back"); - return false; - } - } else { - if (!Storage.Inventory.MoveTo(item)) return false; - } - } + if (!Storage.Inventory.CanFit(item) && !Storage.Inventory.MoveTo(item)) { + console.log("ÿc8AddSocketableToItemÿc0 :: No space to get item back"); + return false; + } + } else { + if (!Storage.Inventory.MoveTo(item)) return false; + } + } - if (!Town.openStash()) return false; + if (!Town.openStash()) return false; - for (let i = 0; i < runes.length; i++) { - let rune = runes[i]; - if (!rune.toCursor()) return false; + for (let i = 0; i < runes.length; i++) { + let rune = runes[i]; + if (!rune.toCursor()) return false; - for (let i = 0; i < 3; i += 1) { - sendPacket(1, sdk.packets.send.InsertSocketItem, 4, rune.gid, 4, item.gid); - let tick = getTickCount(); + for (let i = 0; i < 3; i += 1) { + sendPacket(1, sdk.packets.send.InsertSocketItem, 4, rune.gid, 4, item.gid); + let tick = getTickCount(); - while (getTickCount() - tick < 2000) { - if (!me.itemoncursor) { - delay(300); + while (getTickCount() - tick < 2000) { + if (!me.itemoncursor) { + delay(300); - break; - } + break; + } - delay(10); - } + delay(10); + } - if (item.getItemsEx().length > preSockets) { - D2Bot.printToConsole("Added socketable: " + rune.fname + " to " + item.fname, sdk.colors.D2Bot.Gold); - Item.logItem("Added " + rune.name + " to: ", item, null, true); - preSockets++; - } - } - } + if (item.getItemsEx().length > preSockets) { + D2Bot.printToConsole("Added socketable: " + rune.fname + " to " + item.fname, sdk.colors.D2Bot.Gold); + Item.logItem("Added " + rune.name + " to: ", item, null, true); + preSockets++; + } + } + } - bodyLoc && Item.equip(item, bodyLoc); + bodyLoc && Item.equip(item, bodyLoc); - return item.getItemsEx().length > original; + return item.getItemsEx().length > original; }; /** @@ -563,232 +563,232 @@ Misc.addSocketablesToItem = function (item, runes = []) { * @returns {boolean} */ Misc.getSocketables = function (item, itemInfo) { - if (!item) return false; - itemInfo === undefined && (itemInfo = {}); - - let itemtype, gemType, runeType; - let [multiple, temp] = [[], []]; - let itemSocketInfo = item.getItemsEx(); - let preSockets = itemSocketInfo.length; - let allowTemp = (itemInfo.hasOwnProperty("temp") && itemInfo.temp.length > 0 - && (preSockets === 0 || preSockets > 0 && itemSocketInfo.some(el => !itemInfo.socketWith.includes(el.classid)))); - let sockets = item.sockets; - let openSockets = sockets - preSockets; - let { classid, quality } = item; - let socketables = me.getItemsEx().filter(item => item.isInsertable); - - if (!socketables || (!allowTemp && openSockets === 0)) return false; - - function highestGemAvailable (gem, checkList = []) { - if (!gem) return false; - - // filter out all items that aren't the gem type we are looking for - // then sort the highest classid (better gems first) - let myItems = me.getItemsEx() - .filter(item => item.itemType === gem.itemType) - .sort((a, b) => b.classid - a.classid); - - for (let i = 0; i < myItems.length; i++) { - if (!checkList.includes(myItems[i])) return true; - } - - return false; - } - - if (!itemInfo.hasOwnProperty("socketWith") || (itemInfo.hasOwnProperty("socketWith") && itemInfo.socketWith.length === 0)) { - itemtype = item.getItemType(); - if (!itemtype) return false; - gemType = ["Helmet", "Armor"].includes(itemtype) - ? "Ruby" : itemtype === "Shield" - ? "Diamond" : itemtype === "Weapon" && !Check.currentBuild().caster - ? "Skull" : ""; - - // Tir rune in normal, Io rune otherwise and Shael's if assassin - !gemType && (runeType = me.normal ? "Tir" : me.assassin ? "Shael" : "Io"); - - // TODO: Use Jewels - // would need to score them and way to compare to runes/gems by what itemtype we are looking at - // then keep upgrading until we actually are ready to insert in the item - } - - for (let i = 0; i < socketables.length; i++) { - if (itemInfo.hasOwnProperty("socketWith") && itemInfo.socketWith.length > 0) { - // In case we are trying to use different runes, check if item already has current rune inserted - // or if its already in the muliple list. If it is, remove that socketables classid from the list of wanted classids - if (itemInfo.socketWith.length > 1 - && (itemSocketInfo.some(el => el.classid === socketables[i].classid) || multiple.some(el => el.classid === socketables[i].classid))) { - itemInfo.socketWith.remove(socketables[i].classid); - } - - if (itemInfo.socketWith.includes(socketables[i].classid) && !multiple.includes(socketables[i])) { - if (multiple.length < sockets) { - multiple.push(socketables[i]); - } - } - - if (allowTemp && itemInfo.temp.includes(socketables[i].classid) && !temp.includes(socketables[i])) { - if (temp.length < sockets) { - temp.push(socketables[i]); - } - } - } else { - // If itemtype was matched with a gemType - if (gemType) { - // current item matches wanted gemType - if (socketables[i].itemType === sdk.items.type[gemType]) { - // is the highest gem of that type - if (highestGemAvailable(socketables[i], multiple)) { - if (multiple.length < sockets) { - multiple.push(socketables[i]); - } - } - } - } else if (runeType) { - if (socketables[i].classid === sdk.items.runes[runeType] && !multiple.includes(socketables[i])) { - if (multiple.length < sockets) { - multiple.push(socketables[i]); - } - } - } - } - - if (multiple.length === sockets) { - break; - } - } - - if (allowTemp) { - // we have all our wanted socketables - if (multiple.length === sockets) { - // Failed to remove temp socketables - if (!Misc.unsocketItem(item)) return false; - // relocate our item as unsocketing it causes loss of reference - item = me.findItem(classid, -1, -1, quality); - openSockets = sockets; - } else { - if (temp.length > 0) { - // use temp socketables - multiple = temp.slice(0); - } else if (item.getItemsEx().some((el) => itemInfo.temp.includes(el.classid))) { - return false; - } - } - } - - if (multiple.length > 0) { - multiple.length > openSockets && (multiple.length = openSockets); - if (openSockets === 0) return false; - // check to ensure I am a high enough level to use wanted socketables - for (let i = 0; i < multiple.length; i++) { - if (me.charlvl < multiple[i].lvlreq) { - console.log("ÿc8Kolbot-SoloPlayÿc0: Not high enough level for " + multiple[i].fname); - return false; - } - } - - if (Misc.addSocketablesToItem(item, multiple)) { - delay(250 + me.ping); - } else { - console.log("ÿc8Kolbot-SoloPlayÿc0: Failed to add socketable to " + item.fname); - } - - return item.getItemsEx().length === sockets || item.getItemsEx().length > preSockets; - } - - return false; + if (!item) return false; + itemInfo === undefined && (itemInfo = {}); + + let itemtype, gemType, runeType; + let [multiple, temp] = [[], []]; + let itemSocketInfo = item.getItemsEx(); + let preSockets = itemSocketInfo.length; + let allowTemp = (itemInfo.hasOwnProperty("temp") && itemInfo.temp.length > 0 + && (preSockets === 0 || preSockets > 0 && itemSocketInfo.some(el => !itemInfo.socketWith.includes(el.classid)))); + let sockets = item.sockets; + let openSockets = sockets - preSockets; + let { classid, quality } = item; + let socketables = me.getItemsEx().filter(item => item.isInsertable); + + if (!socketables || (!allowTemp && openSockets === 0)) return false; + + function highestGemAvailable (gem, checkList = []) { + if (!gem) return false; + + // filter out all items that aren't the gem type we are looking for + // then sort the highest classid (better gems first) + let myItems = me.getItemsEx() + .filter(item => item.itemType === gem.itemType) + .sort((a, b) => b.classid - a.classid); + + for (let i = 0; i < myItems.length; i++) { + if (!checkList.includes(myItems[i])) return true; + } + + return false; + } + + if (!itemInfo.hasOwnProperty("socketWith") || (itemInfo.hasOwnProperty("socketWith") && itemInfo.socketWith.length === 0)) { + itemtype = item.getItemType(); + if (!itemtype) return false; + gemType = ["Helmet", "Armor"].includes(itemtype) + ? "Ruby" : itemtype === "Shield" + ? "Diamond" : itemtype === "Weapon" && !Check.currentBuild().caster + ? "Skull" : ""; + + // Tir rune in normal, Io rune otherwise and Shael's if assassin + !gemType && (runeType = me.normal ? "Tir" : me.assassin ? "Shael" : "Io"); + + // TODO: Use Jewels + // would need to score them and way to compare to runes/gems by what itemtype we are looking at + // then keep upgrading until we actually are ready to insert in the item + } + + for (let i = 0; i < socketables.length; i++) { + if (itemInfo.hasOwnProperty("socketWith") && itemInfo.socketWith.length > 0) { + // In case we are trying to use different runes, check if item already has current rune inserted + // or if its already in the muliple list. If it is, remove that socketables classid from the list of wanted classids + if (itemInfo.socketWith.length > 1 + && (itemSocketInfo.some(el => el.classid === socketables[i].classid) || multiple.some(el => el.classid === socketables[i].classid))) { + itemInfo.socketWith.remove(socketables[i].classid); + } + + if (itemInfo.socketWith.includes(socketables[i].classid) && !multiple.includes(socketables[i])) { + if (multiple.length < sockets) { + multiple.push(socketables[i]); + } + } + + if (allowTemp && itemInfo.temp.includes(socketables[i].classid) && !temp.includes(socketables[i])) { + if (temp.length < sockets) { + temp.push(socketables[i]); + } + } + } else { + // If itemtype was matched with a gemType + if (gemType) { + // current item matches wanted gemType + if (socketables[i].itemType === sdk.items.type[gemType]) { + // is the highest gem of that type + if (highestGemAvailable(socketables[i], multiple)) { + if (multiple.length < sockets) { + multiple.push(socketables[i]); + } + } + } + } else if (runeType) { + if (socketables[i].classid === sdk.items.runes[runeType] && !multiple.includes(socketables[i])) { + if (multiple.length < sockets) { + multiple.push(socketables[i]); + } + } + } + } + + if (multiple.length === sockets) { + break; + } + } + + if (allowTemp) { + // we have all our wanted socketables + if (multiple.length === sockets) { + // Failed to remove temp socketables + if (!Misc.unsocketItem(item)) return false; + // relocate our item as unsocketing it causes loss of reference + item = me.findItem(classid, -1, -1, quality); + openSockets = sockets; + } else { + if (temp.length > 0) { + // use temp socketables + multiple = temp.slice(0); + } else if (item.getItemsEx().some((el) => itemInfo.temp.includes(el.classid))) { + return false; + } + } + } + + if (multiple.length > 0) { + multiple.length > openSockets && (multiple.length = openSockets); + if (openSockets === 0) return false; + // check to ensure I am a high enough level to use wanted socketables + for (let i = 0; i < multiple.length; i++) { + if (me.charlvl < multiple[i].lvlreq) { + console.log("ÿc8Kolbot-SoloPlayÿc0: Not high enough level for " + multiple[i].fname); + return false; + } + } + + if (Misc.addSocketablesToItem(item, multiple)) { + delay(250 + me.ping); + } else { + console.log("ÿc8Kolbot-SoloPlayÿc0: Failed to add socketable to " + item.fname); + } + + return item.getItemsEx().length === sockets || item.getItemsEx().length > preSockets; + } + + return false; }; Misc.checkSocketables = function () { - let items = me.getItemsEx() - .filter(item => item.sockets > 0 && AutoEquip.hasTier(item) - && (item.quality >= sdk.items.quality.Magic || ((item.normal || item.superior) && item.isEquipped))) - .sort((a, b) => NTIP.GetTier(b) - NTIP.GetTier(a)); - - if (!items) return; - - for (let i = 0; i < items.length; i++) { - let sockets = items[i].sockets; - - switch (items[i].quality) { - case sdk.items.quality.Normal: - case sdk.items.quality.Superior: - case sdk.items.quality.Magic: - case sdk.items.quality.Rare: - case sdk.items.quality.Crafted: - // no need to check anything else if already socketed - if (items[i].getItemsEx().length === sockets) { - continue; - } - // Any magic, rare, or crafted item with open sockets - if (items[i].isEquipped && [sdk.body.Head, sdk.body.Armor, sdk.body.RightArm, sdk.body.LeftArm].includes(items[i].bodylocation)) { - Misc.getSocketables(items[i]); - } - - break; - case sdk.items.quality.Set: - case sdk.items.quality.Unique: - { - let curr = Config.socketables.find(({ classid }) => items[i].classid === classid); - - // item is already socketed and we don't use temp socketables on this item - if ((!curr || (curr && !curr.temp)) && items[i].getItemsEx().length === sockets) { - continue; - } - - if (curr && curr.condition(items[i])) { - Misc.getSocketables(items[i], curr); - } else if (items[i].isEquipped) { - Misc.getSocketables(items[i]); - } - } - - break; - default: - break; - } - } + let items = me.getItemsEx() + .filter(item => item.sockets > 0 && AutoEquip.hasTier(item) + && (item.quality >= sdk.items.quality.Magic || ((item.normal || item.superior) && item.isEquipped))) + .sort((a, b) => NTIP.GetTier(b) - NTIP.GetTier(a)); + + if (!items) return; + + for (let i = 0; i < items.length; i++) { + let sockets = items[i].sockets; + + switch (items[i].quality) { + case sdk.items.quality.Normal: + case sdk.items.quality.Superior: + case sdk.items.quality.Magic: + case sdk.items.quality.Rare: + case sdk.items.quality.Crafted: + // no need to check anything else if already socketed + if (items[i].getItemsEx().length === sockets) { + continue; + } + // Any magic, rare, or crafted item with open sockets + if (items[i].isEquipped && [sdk.body.Head, sdk.body.Armor, sdk.body.RightArm, sdk.body.LeftArm].includes(items[i].bodylocation)) { + Misc.getSocketables(items[i]); + } + + break; + case sdk.items.quality.Set: + case sdk.items.quality.Unique: + { + let curr = Config.socketables.find(({ classid }) => items[i].classid === classid); + + // item is already socketed and we don't use temp socketables on this item + if ((!curr || (curr && !curr.temp)) && items[i].getItemsEx().length === sockets) { + continue; + } + + if (curr && curr.condition(items[i])) { + Misc.getSocketables(items[i], curr); + } else if (items[i].isEquipped) { + Misc.getSocketables(items[i]); + } + } + + break; + default: + break; + } + } }; Misc.updateRecursively = function (oldObj, newObj, path) { - if (path === void 0) { path = []; } - Object.keys(newObj).forEach(function (key) { - if (typeof newObj[key] === "function") return; // skip - if (typeof newObj[key] !== "object") { - if (!oldObj.hasOwnProperty(key) || oldObj[key] !== newObj[key]) { - oldObj[key] = newObj[key]; - } - } else if (Array.isArray(newObj[key]) && !newObj[key].some(k => typeof k === "object")) { - // copy array (shallow copy) - if (oldObj[key] === undefined || !oldObj[key].equals(newObj[key])) { - oldObj[key] = newObj[key].slice(0); - } - } else { - if (typeof oldObj[key] !== "object") { - oldObj[key] = {}; - } - path.push(key); - Misc.updateRecursively(oldObj[key], newObj[key], path); - } - }); + if (path === void 0) { path = []; } + Object.keys(newObj).forEach(function (key) { + if (typeof newObj[key] === "function") return; // skip + if (typeof newObj[key] !== "object") { + if (!oldObj.hasOwnProperty(key) || oldObj[key] !== newObj[key]) { + oldObj[key] = newObj[key]; + } + } else if (Array.isArray(newObj[key]) && !newObj[key].some(k => typeof k === "object")) { + // copy array (shallow copy) + if (oldObj[key] === undefined || !oldObj[key].equals(newObj[key])) { + oldObj[key] = newObj[key].slice(0); + } + } else { + if (typeof oldObj[key] !== "object") { + oldObj[key] = {}; + } + path.push(key); + Misc.updateRecursively(oldObj[key], newObj[key], path); + } + }); }; Misc.recursiveSearch = function (o, n, changed) { - if (changed === void 0) { changed = {}; } - Object.keys(n).forEach(function (key) { - if (typeof n[key] === "function") return; // skip - if (typeof n[key] !== "object") { - if (!o.hasOwnProperty(key) || o[key] !== n[key]) { - changed[key] = n[key]; - } - } else { - if (typeof changed[key] !== "object" || !changed[key]) { - changed[key] = {}; - } - Misc.recursiveSearch((o === null || o === void 0 ? void 0 : o[key]) || {}, (n === null || n === void 0 ? void 0 : n[key]) || {}, changed[key]); - if (!Object.keys(changed[key]).length) { - delete changed[key]; - } - } - }); - return changed; + if (changed === void 0) { changed = {}; } + Object.keys(n).forEach(function (key) { + if (typeof n[key] === "function") return; // skip + if (typeof n[key] !== "object") { + if (!o.hasOwnProperty(key) || o[key] !== n[key]) { + changed[key] = n[key]; + } + } else { + if (typeof changed[key] !== "object" || !changed[key]) { + changed[key] = {}; + } + Misc.recursiveSearch((o === null || o === void 0 ? void 0 : o[key]) || {}, (n === null || n === void 0 ? void 0 : n[key]) || {}, changed[key]); + if (!Object.keys(changed[key]).length) { + delete changed[key]; + } + } + }); + return changed; }; diff --git a/libs/SoloPlay/Functions/MuleloggerOverrides.js b/libs/SoloPlay/Functions/MuleloggerOverrides.js index ddf03dbd..17f2dbed 100644 --- a/libs/SoloPlay/Functions/MuleloggerOverrides.js +++ b/libs/SoloPlay/Functions/MuleloggerOverrides.js @@ -11,128 +11,128 @@ includeIfNotIncluded("SoloPlay/Functions/MiscOverrides.js"); // Added type parameter and logging tier value under picture on char viewer tab MuleLogger.logItem = function (unit, logIlvl, type = "Player") { - if (!isIncluded("core/misc.js")) { - include("core/misc.js"); - include("core/util.js"); - } - - logIlvl === undefined && (logIlvl = this.LogItemLevel); - - let header = ""; - let name = unit.itemType + "_" + unit.fname.split("\n").reverse().join(" ").replace(/(y|ÿ)c[0-9!"+<:;.*]|\/|\\/g, "").trim(); - let desc = (Item.getItemDesc(unit, logIlvl) || ""); - let color = (unit.getColor() || -1); - let code = Item.getItemCode(unit); - - if (NTIP.GetMercTier(unit) > 0 || NTIP.GetTier(unit) > 0 || NTIP.GetCharmTier(unit) > 0 || NTIP.GetSecondaryTier(unit) > 0) { - if (unit.mode === sdk.items.mode.inStorage && type === "Player") { - if (unit.isCharm) { - desc += ("\n\\xffc0Autoequip charm tier: " + NTIP.GetCharmTier(unit)); - } else { - desc += ("\n\\xffc0Autoequip tier: " + NTIP.GetTier(unit)); - - if (NTIP.GetSecondaryTier(unit) > 0) { - desc += ("\n\\xffc0Autoequip Secondary tier: " + NTIP.GetSecondaryTier(unit)); - } - } - } else if (unit.mode === sdk.items.mode.inStorage && type === "Merc") { - desc += ("\n\\xffc0Autoequip merctier: " + NTIP.GetMercTier(unit)); - } - } - - let sock = unit.getItems(); - - if (sock) { - for (let i = 0; i < sock.length; i += 1) { - if (sock[i].itemType === sdk.items.type.Jewel) { - desc += "\n\n"; - desc += Item.getItemDesc(sock[i]); - } - } - } - - desc += "$" + unit.gid + ":" + unit.classid + ":" + unit.location + ":" + unit.x + ":" + unit.y + (unit.getFlag(sdk.items.flags.Ethereal) ? ":eth" : ""); - - return { - itemColor: color, - image: code, - title: name, - description: desc, - header: header, - sockets: Item.getItemSockets(unit) - }; + if (!isIncluded("core/misc.js")) { + include("core/misc.js"); + include("core/util.js"); + } + + logIlvl === undefined && (logIlvl = this.LogItemLevel); + + let header = ""; + let name = unit.itemType + "_" + unit.fname.split("\n").reverse().join(" ").replace(/(y|ÿ)c[0-9!"+<:;.*]|\/|\\/g, "").trim(); + let desc = (Item.getItemDesc(unit, logIlvl) || ""); + let color = (unit.getColor() || -1); + let code = Item.getItemCode(unit); + + if (NTIP.GetMercTier(unit) > 0 || NTIP.GetTier(unit) > 0 || NTIP.GetCharmTier(unit) > 0 || NTIP.GetSecondaryTier(unit) > 0) { + if (unit.mode === sdk.items.mode.inStorage && type === "Player") { + if (unit.isCharm) { + desc += ("\n\\xffc0Autoequip charm tier: " + NTIP.GetCharmTier(unit)); + } else { + desc += ("\n\\xffc0Autoequip tier: " + NTIP.GetTier(unit)); + + if (NTIP.GetSecondaryTier(unit) > 0) { + desc += ("\n\\xffc0Autoequip Secondary tier: " + NTIP.GetSecondaryTier(unit)); + } + } + } else if (unit.mode === sdk.items.mode.inStorage && type === "Merc") { + desc += ("\n\\xffc0Autoequip merctier: " + NTIP.GetMercTier(unit)); + } + } + + let sock = unit.getItems(); + + if (sock) { + for (let i = 0; i < sock.length; i += 1) { + if (sock[i].itemType === sdk.items.type.Jewel) { + desc += "\n\n"; + desc += Item.getItemDesc(sock[i]); + } + } + } + + desc += "$" + unit.gid + ":" + unit.classid + ":" + unit.location + ":" + unit.x + ":" + unit.y + (unit.getFlag(sdk.items.flags.Ethereal) ? ":eth" : ""); + + return { + itemColor: color, + image: code, + title: name, + description: desc, + header: header, + sockets: Item.getItemSockets(unit) + }; }; MuleLogger.logEquippedItems = function () { - while (!me.gameReady) { - delay(100); - } - - let folder, string, parsedItem; - let realm = me.realm || "Single Player"; - let finalString = ""; - let items = me.getItemsEx().filter(item => item.isEquipped || item.isEquippedCharm || (item.isInStorage && item.itemType === sdk.items.type.Rune)); - if (!items || !items.length) return; - items.sort((a, b) => b.itemType - a.itemType); - - if (!FileTools.exists("mules/" + realm)) { - folder = dopen("mules"); - folder.create(realm); - } - - if (!FileTools.exists("mules/" + realm + "/" + "Kolbot-SoloPlay")) { - folder = dopen("mules/" + realm); - folder.create("Kolbot-SoloPlay"); - } - - if (!FileTools.exists("mules/" + realm + "/" + "Kolbot-SoloPlay/" + me.account)) { - folder = dopen("mules/" + realm + "/Kolbot-SoloPlay"); - folder.create(me.account); - } - - for (let i = 0; i < items.length; i += 1) { - parsedItem = this.logItem(items[i], true, "Player"); - // Always put name on Char Viewer items - !parsedItem.header && (parsedItem.header = (me.account || "Single Player") + " / " + me.name); - // Remove itemtype_ prefix from the name - parsedItem.title = parsedItem.title.substr(parsedItem.title.indexOf("_") + 1); - - switch (items[i].mode) { - case sdk.items.mode.inStorage: - parsedItem.title += ((items[i].isInInventory && items[i].isEquippedCharm) ? " (equipped charm)" : " (in stash)"); - - break; - case sdk.items.mode.Equipped: - parsedItem.title += (items[i].isOnSwap ? " (secondary equipped)" : " (equipped)"); - - break; - } - - string = JSON.stringify(parsedItem); - finalString += (string + "\n"); - } - - if (Config.UseMerc) { - let merc = me.getMercEx(); - - if (merc) { - items = merc.getItemsEx(); - - for (let i = 0; i < items.length; i += 1) { - parsedItem = this.logItem(items[i], true, "Merc"); - parsedItem.title += " (merc)"; - - string = JSON.stringify(parsedItem); - finalString += (string + "\n"); - } - } - - } - - let charClass = ["amazon-", "sorceress-", "necromancer-", "paladin-", "barbarian-", "druid-", "assassin-"][me.classid]; - - // hccl = hardcore classic ladder - // scnl = softcore expan nonladder - FileTools.writeText("mules/" + realm + "/" + "Kolbot-SoloPlay/" + me.account + "/" + charClass + "-" + me.profile + "-" + me.name + "." + ( me.playertype ? "hc" : "sc" ) + (me.classic ? "c" : "" ) + ( me.ladder > 0 ? "l" : "nl" ) + ".txt", finalString); - console.log("Item logging done."); + while (!me.gameReady) { + delay(100); + } + + let folder, string, parsedItem; + let realm = me.realm || "Single Player"; + let finalString = ""; + let items = me.getItemsEx().filter(item => item.isEquipped || item.isEquippedCharm || (item.isInStorage && item.itemType === sdk.items.type.Rune)); + if (!items || !items.length) return; + items.sort((a, b) => b.itemType - a.itemType); + + if (!FileTools.exists("mules/" + realm)) { + folder = dopen("mules"); + folder.create(realm); + } + + if (!FileTools.exists("mules/" + realm + "/" + "Kolbot-SoloPlay")) { + folder = dopen("mules/" + realm); + folder.create("Kolbot-SoloPlay"); + } + + if (!FileTools.exists("mules/" + realm + "/" + "Kolbot-SoloPlay/" + me.account)) { + folder = dopen("mules/" + realm + "/Kolbot-SoloPlay"); + folder.create(me.account); + } + + for (let i = 0; i < items.length; i += 1) { + parsedItem = this.logItem(items[i], true, "Player"); + // Always put name on Char Viewer items + !parsedItem.header && (parsedItem.header = (me.account || "Single Player") + " / " + me.name); + // Remove itemtype_ prefix from the name + parsedItem.title = parsedItem.title.substr(parsedItem.title.indexOf("_") + 1); + + switch (items[i].mode) { + case sdk.items.mode.inStorage: + parsedItem.title += ((items[i].isInInventory && items[i].isEquippedCharm) ? " (equipped charm)" : " (in stash)"); + + break; + case sdk.items.mode.Equipped: + parsedItem.title += (items[i].isOnSwap ? " (secondary equipped)" : " (equipped)"); + + break; + } + + string = JSON.stringify(parsedItem); + finalString += (string + "\n"); + } + + if (Config.UseMerc) { + let merc = me.getMercEx(); + + if (merc) { + items = merc.getItemsEx(); + + for (let i = 0; i < items.length; i += 1) { + parsedItem = this.logItem(items[i], true, "Merc"); + parsedItem.title += " (merc)"; + + string = JSON.stringify(parsedItem); + finalString += (string + "\n"); + } + } + + } + + let charClass = ["amazon-", "sorceress-", "necromancer-", "paladin-", "barbarian-", "druid-", "assassin-"][me.classid]; + + // hccl = hardcore classic ladder + // scnl = softcore expan nonladder + FileTools.writeText("mules/" + realm + "/" + "Kolbot-SoloPlay/" + me.account + "/" + charClass + "-" + me.profile + "-" + me.name + "." + ( me.playertype ? "hc" : "sc" ) + (me.classic ? "c" : "" ) + ( me.ladder > 0 ? "l" : "nl" ) + ".txt", finalString); + console.log("Item logging done."); }; diff --git a/libs/SoloPlay/Functions/NPCAction.js b/libs/SoloPlay/Functions/NPCAction.js index 92fdda9d..86b1a92e 100644 --- a/libs/SoloPlay/Functions/NPCAction.js +++ b/libs/SoloPlay/Functions/NPCAction.js @@ -8,700 +8,700 @@ // ugly but should handle scope issues if I decide to add this to the core in which case I can come back and remove this // but won't get immeadiate issues of trying to redefine a const (function (NPCAction) { - /** - * Easier shopping done at a specific npc - * @param {string} npcName - NPC.NameOfNPC - * @returns {boolean} - */ - NPCAction.shopAt = function (npcName) { - if (!me.inTown) return false; - - // check for invaid npcs to shop at - if ([NPC.Kashya, NPC.Warriv, NPC.Meshif, NPC.Atma, NPC.Greiz, NPC.Tyrael, NPC.Qual_Kehk, NPC.Cain].includes(npcName.toLowerCase())) { - console.warn(npcName + " is an invalid npc to shop at"); - - return false; - } - - // keep track of where we start from - const origAct = me.act; - const npcAct = NPC.getAct(npcName); - if (!npcAct.length) return false; - - try { - if (!npcAct.includes(origAct)) { - Town.goToTown(npcAct[0]); - } - - Town.move(npcName); - let npc = Game.getNPC(npcName); - - if (!npc && Town.move(npcName)) { - npc = Game.getNPC(npcName); - } - - if (!getUIFlag(sdk.uiflags.Shop) && !npc.startTrade("Shop")) { - throw new Error("Failed to shop at " + npc.name); - } - - return Town.shopItems(); - } catch (e) { - console.error(e); - - return false; - } finally { - me.act !== origAct && Town.goToTown(origAct); - } - }; - - NPCAction.buyPotions = function () { - if (me.gold < 450 || !me.getItem(sdk.items.TomeofTownPortal)) return false; - - me.clearBelt(); - const buffer = { hp: 0, mp: 0 }; - const beltSize = Storage.BeltSize(); - let [needPots, needBuffer, specialCheck] = [false, true, false]; - let col = Storage.Belt.checkColumns(beltSize); - - const getNeededBuffer = () => { - [buffer.hp, buffer.mp] = [0, 0]; - me.getItemsEx().filter(function (p) { - return p.isInInventory && [sdk.items.type.HealingPotion, sdk.items.type.ManaPotion].includes(p.itemType); - }).forEach(function (p) { - switch (p.itemType) { - case sdk.items.type.HealingPotion: - return (buffer.hp++); - case sdk.items.type.ManaPotion: - return (buffer.mp++); - } - return false; - }); - }; - - // HP/MP Buffer - (Config.HPBuffer > 0 || Config.MPBuffer > 0) && getNeededBuffer(); - - // Check if we need to buy potions based on Config.MinColumn - if (Config.BeltColumn.some((c, i) => ["hp", "mp"].includes(c) && col[i] > (beltSize - Math.min(Config.MinColumn[i], beltSize)))) { - needPots = true; - } - - // Check if we need any potions for buffers - if (buffer.mp < Config.MPBuffer || buffer.hp < Config.HPBuffer) { - if (Config.BeltColumn.some((c, i) => col[i] >= beltSize && (!needPots || c === "rv"))) { - specialCheck = true; - } - } - - // We have enough potions in inventory - (buffer.mp >= Config.MPBuffer && buffer.hp >= Config.HPBuffer) && (needBuffer = false); - - // No columns to fill - if (!needPots && !needBuffer) return true; - // todo: buy the cheaper potions if we are low on gold or don't need the higher ones i.e have low mana/health pool - // why buy potion that heals 225 (greater mana) if we only have sub 100 mana - let [wantedHpPot, wantedMpPot] = [5, 5]; - // only do this if we are low on gold in the first place - if (me.normal && me.gold < Config.LowGold) { - const mpPotsEffects = PotData.getMpPots().map(el => el.effect[me.classid]); - const hpPotsEffects = PotData.getHpPots().map(el => el.effect[me.classid]); - - wantedHpPot = (hpPotsEffects.findIndex(eff => me.hpmax / 2 < eff) + 1 || hpPotsEffects.length - 1); - wantedMpPot = (mpPotsEffects.findIndex(eff => me.mpmax / 2 < eff) + 1 || mpPotsEffects.length - 1); - console.debug("Wanted hpPot: " + wantedHpPot + " Wanted mpPot: " + wantedMpPot); - } - - if (me.normal && me.highestAct >= 4) { - let pAct = Math.max(wantedHpPot, wantedMpPot); - pAct >= 4 ? me.act < 4 && Town.goToTown(4) : pAct > me.act && Town.goToTown(pAct); - } - - let npc = Town.initNPC("Shop", "buyPotions"); - if (!npc) return false; - - // special check, sometimes our rejuv slot is empty but we do still need buffer. Check if we can buy something to slot there - if (specialCheck && Config.BeltColumn.some((c, i) => c === "rv" && col[i] >= beltSize)) { - let pots = [sdk.items.ThawingPotion, sdk.items.AntidotePotion, sdk.items.StaminaPotion]; - Config.BeltColumn.forEach((c, i) => { - if (c === "rv" && col[i] >= beltSize && pots.length) { - let usePot = pots[0]; - let pot = npc.getItem(usePot); - if (pot) { - Storage.Inventory.CanFit(pot) && Packet.buyItem(pot, false); - pot = me.getItemsEx(usePot, sdk.items.mode.inStorage).filter(i => i.isInInventory).first(); - !!pot && Packet.placeInBelt(pot, i); - pots.shift(); - } else { - needBuffer = false; // we weren't able to find any pots to buy - } - } - }); - } - - for (let i = 0; i < 4; i += 1) { - if (col[i] > 0) { - const useShift = Town.shiftCheck(col, beltSize); - const wantedPot = Config.BeltColumn[i] === "hp" ? wantedHpPot : wantedMpPot; - let pot = Town.getPotion(npc, Config.BeltColumn[i], wantedPot); - - if (pot) { - // print("ÿc2column ÿc0" + i + "ÿc2 needs ÿc0" + col[i] + " ÿc2potions"); - // Shift+buy will trigger if there's no empty columns or if only the current column is empty - if (useShift) { - pot.buy(true); - } else { - for (let j = 0; j < col[i]; j += 1) { - pot.buy(false); - } - } - } - } - - col = Storage.Belt.checkColumns(beltSize); // Re-initialize columns (needed because 1 shift-buy can fill multiple columns) - } - - // re-check - !needBuffer && (Config.HPBuffer > 0 || Config.MPBuffer > 0) && getNeededBuffer(); - - const buyHPBuffers = () => { - if (needBuffer && buffer.hp < Config.HPBuffer) { - for (let i = 0; i < Config.HPBuffer - buffer.hp; i += 1) { - let pot = Town.getPotion(npc, "hp", wantedHpPot); - !!pot && Storage.Inventory.CanFit(pot) && pot.buy(false); - } - } - return true; - }; - const buyMPBuffers = () => { - if (needBuffer && buffer.mp < Config.MPBuffer) { - for (let i = 0; i < Config.MPBuffer - buffer.mp; i += 1) { - let pot = Town.getPotion(npc, "mp", wantedMpPot); - !!pot && Storage.Inventory.CanFit(pot) && pot.buy(false); - } - } - return true; - }; - // priortize mana pots if caster - Check.currentBuild().caster ? buyMPBuffers() && buyHPBuffers() : buyHPBuffers() && buyMPBuffers(); - - // keep cold/pois res high with potions - if (me.gold > 50000 && npc.getItem(sdk.items.ThawingPotion)) { - CharData.pots.get("thawing").need() && Town.buyPots(12, "thawing", true); - CharData.pots.get("antidote").need() && Town.buyPots(12, "antidote", true); - } - - return true; - }; - - /** - * @param {number} classid - * @param {boolean} force - * @returns {boolean} - */ - NPCAction.fillTome = function (classid, force = false) { - const scrollId = (classid === sdk.items.TomeofTownPortal ? sdk.items.ScrollofTownPortal : sdk.items.ScrollofIdentify); - const have = Town.checkScrolls(classid, force); - let myTome = me.getTome(classid); - let invoScrolls = 0; - - if (have > 0 && have < 20) { - // lets see if we have scrolls to cleanup - invoScrolls = me.cleanUpScrolls(myTome, scrollId); - } - - if (have + invoScrolls >= (me.charlvl < 12 ? 5 : 13)) return true; - if (me.gold < 450) return false; - - let npc = Town.initNPC("Shop", "fillTome"); - if (!npc) return false; - - delay(500); - - - if (!myTome) { - let tome = npc.getItem(classid); - - try { - if (tome) { - Storage.Inventory.CanFit(tome) && tome.buy(); - } else { - // couldn't buy tome, lets see if we can just buy a single scroll - if (me.getItem(scrollId)) return true; - let scroll = npc.getItem(scrollId); - if (!scroll || !Storage.Inventory.CanFit(scroll)) return false; - scroll.buy(); - - return true; - } - } catch (e) { - console.error(e); - - return false; - } - } - - let scroll = npc.getItem(scrollId); - if (!scroll) return false; - if (!myTome && !(myTome = me.getTome(classid))) return false; - - // place scrolls in tome if we have any now that we know we have a tome (possibly just bought one) - me.cleanUpScrolls(myTome, scrollId); - - try { - if (me.gold < 5000) { - myTome = me.getTome(classid); - - if (myTome) { - while (myTome.getStat(sdk.stats.Quantity) < 5 && me.gold > 500) { - scroll = npc.getItem(scrollId); - scroll && Packet.buyScroll(scroll, myTome, false); - delay(50); - } - } - } else { - scroll.buy(true); - } - } catch (e2) { - console.error(e2); - - return false; - } - - return true; - }; - - NPCAction.cainID = function (force = false) { - if ((!Config.CainID.Enable && !force) || !Misc.checkQuest(sdk.quest.id.TheSearchForCain, sdk.quest.states.Completed)) return false; - - let npc = getInteractedNPC(); - - // Check if we're already in a shop. It would be pointless to go to Cain if so. - if (npc && npc.name.toLowerCase() === Town.tasks[me.act - 1].Shop) return false; - // Check if we may use Cain - minimum gold - if (me.gold < Config.CainID.MinGold && !force) return false; - - me.cancel(); - - let unids = me.getUnids(); - - if (unids.length) { - // Check if we may use Cain - number of unid items - if (unids.length < Config.CainID.MinUnids && !force) return false; - - let cain = Town.initNPC("CainID", "cainID"); - if (!cain) return false; - - me.cancelUIFlags(); - - while (unids.length) { - const item = unids.shift(); - const { result, line } = Pickit.checkItem(item); - - switch (result) { - case Pickit.Result.TRASH: - Town.sell.push(item); - - break; - case Pickit.Result.WANTED: - case Pickit.Result.SOLOWANTS: - Item.logger("Kept", item); - Item.logItem("Kept", item, line); - - break; - case Pickit.Result.CUBING: - Item.logger("Kept", item, "Cubing-Town"); - Cubing.update(); - - break; - case Pickit.Result.RUNEWORD: - Item.logger("Kept", item, "Runewords-Town"); - Runewords.update(item.classid, item.gid); - - break; - case Pickit.Result.CRAFTING: - Item.logger("Kept", item, "CraftSys-Town"); - CraftingSystem.update(item); - - break; - case Pickit.Result.SOLOSYSTEM: - Item.logger("Kept", item, "SoloWants-Town"); - SoloWants.update(item); - - break; - case Pickit.Result.UNID: - default: - break; - } - } - } - - return true; - }; - - // todo - allow earlier shopping, mainly to get a belt - NPCAction.shopItems = function (force = false) { - if (!Config.MiniShopBot) return true; - if (!me.getTome(sdk.items.TomeofTownPortal)) return false; - - // todo - better gold scaling - // let goldLimit = [10000, 20000, 30000][me.diff]; - const startingGold = me.gold; - let goldLimit = Math.floor(startingGold * 0.60); - let itemTypes = []; - let lowLevelShop = false; - - if (me.charlvl < 6 && startingGold > 200) { - Storage.BeltSize() === 1 && itemTypes.push(sdk.items.type.Belt); - !CharData.skillData.bow.onSwitch && itemTypes.push(sdk.items.type.Bow, sdk.items.type.Crossbow); - if (!itemTypes.length) return true; - lowLevelShop = true; - } - - let npc = getInteractedNPC(); - if (!npc || !npc.itemcount) { - // for now we only do force shop on low level - if ((force || lowLevelShop) && itemTypes.length) { - console.debug("Attempt force shopping"); - Town.initNPC("Repair", "shopItems"); - npc = getInteractedNPC(); - if (!npc || !npc.itemcount) return false; - } else { - return false; - } - } - - if (getTickCount() - Town.lastShopped.tick < Time.seconds(3) && Town.lastShopped.who === npc.name) return false; - let items = npc.getItemsEx() - .filter((item) => !Town.ignoreType(item.itemType) - && (itemTypes.length === 0 || itemTypes.includes(item.itemType)) - && (NTIP.CheckItem(item) !== Pickit.Result.UNWANTED) - && (startingGold - item.getItemCost(sdk.items.cost.ToBuy) > goldLimit)) - .sort((a, b) => { - let priorityA = itemTypes.includes(a.itemType); - let priorityB = itemTypes.includes(b.itemType); - if (priorityA && priorityB) return NTIP.GetTier(b) - NTIP.GetTier(a); - if (priorityA) return 1; - if (priorityB) return -1; - return NTIP.GetTier(b) - NTIP.GetTier(a); - }); - if (!items.length) return false; - - const checkedItems = items.length; - - console.time("shopItems"); - - let bought = 0; - const haveMerc = !!me.getMercEx(); - console.info(true, "ÿc4MiniShopBotÿc0: Scanning " + npc.itemcount + " items."); - - /** - * @param {ItemUnit} item - * @param {string} action - * @param {{ result: PickitResult, line: string }} result - * @param {number | string} tierInfo - */ - const shopReport = (item, action, result, tierInfo) => { - action === undefined && (action = ""); - tierInfo === undefined && (tierInfo = ""); - console.log("ÿc8Kolbot-SoloPlayÿc0: " + action + (tierInfo ? " " + tierInfo : "")); - Item.logger(action, item); - Developer.debugging.autoEquip && Item.logItem("Shopped " + action, item, result.line !== undefined ? result.line : "null"); - }; - - /** - * Buy dependancy item if it's needed - * @param {ItemUnit} item - */ - const checkDependancy = (item) => { - let check = Item.hasDependancy(item); - if (check) { - let el = npc.getItem(check); - !!el && el.buy(); - } - }; - - for (let i = 0; i < items.length; i++) { - const item = items[i]; - const myGold = me.gold; - const itemCost = item.getItemCost(sdk.items.cost.ToBuy); - if (myGold < itemCost) continue; - const { result, line } = Pickit.checkItem(item); - - // no tier'ed items - if (!lowLevelShop && result === Pickit.Result.SOLOWANTS && NTIP.CheckItem(item, NTIP.SoloCheckListNoTier, true).result !== Pickit.Result.UNWANTED) { - try { - if (Storage.Inventory.CanFit(item) && myGold >= itemCost && (myGold - itemCost > goldLimit)) { - if (item.isBaseType) { - if (Item.betterThanStashed(item) && Item.betterBaseThanWearing(item, Developer.debugging.baseCheck)) { - shopReport(item, "better base", line); - item.buy() && bought++; - } - } else { - shopReport(item, "NoTier", line); - item.buy() && bought++; - } - } - } catch (e) { - console.error(e); - } - } else if (result === Pickit.Result.SOLOWANTS && AutoEquip.wanted(item)) { - // tier'ed items - try { - if (Storage.Inventory.CanFit(item) && myGold >= itemCost && (myGold - itemCost > goldLimit)) { - let [mainTier] = [NTIP.GetTier(item)]; - - // we want this to be at least a 5% increase in the tier value - if (Item.hasTier(item) && Item.autoEquipCheck(item) && ((Item.getEquippedItem(item.bodyLocation().first()).tier - mainTier) / (mainTier * 100)) > 5) { - shopReport(item, "AutoEquip", line, (item.prettyPrint + " Tier: " + NTIP.GetTier(item))); - item.buy() && bought++; - Item.autoEquip("InShop"); - checkDependancy(item); - } else if (Item.hasSecondaryTier(item) && Item.autoEquipCheckSecondary(item)) { - shopReport(item, "AutoEquip Switch Shopped", line, (item.prettyPrint + " SecondaryTier: " + NTIP.GetSecondaryTier(item))); - item.buy() && bought++; - Item.autoEquip("InShop"); - checkDependancy(item); - } - } - } catch (e) { - console.error(e); - } - } - - delay(2); - } - - // merc tier'ed items - if (haveMerc && !lowLevelShop) { - items = npc.getItemsEx() - .filter((item) => !Town.ignoreType(item.itemType) && NTIP.GetMercTier(item) > 0) - .sort((a, b) => NTIP.GetMercTier(b) - NTIP.GetMercTier(a)) - .forEach(item => { - const myGold = me.gold; - const itemCost = item.getItemCost(sdk.items.cost.ToBuy); - if (myGold < itemCost) return; - const result = Pickit.checkItem(item); - - if (result.result === Pickit.Result.SOLOWANTS && AutoEquip.wanted(item)) { - try { - if (Storage.Inventory.CanFit(item) && myGold >= itemCost && (myGold - itemCost > goldLimit)) { - if (Item.hasMercTier(item) && Item.autoEquipCheckMerc(item)) { - shopReport(item, "AutoEquipMerc", result.line, (item.fname + " Tier: " + NTIP.GetMercTier(item))); - item.buy() && bought++; - } - } - } catch (e) { - console.error(e); - } - } - - delay(2); - }); - } - - Town.lastShopped.tick = getTickCount(); - Town.lastShopped.who = npc.name; - - console.info(false, "Evaluated " + checkedItems + " items. Bought " + bought + " items. Spent " + (startingGold - me.gold) + " gold", "shopItems"); - - return true; - }; - - NPCAction.gamble = function () { - if (!Town.needGamble() || Config.GambleItems.length === 0) return true; - - let list = []; - - if (Town.gambleIds.length === 0) { - // change text to classid - for (let i = 0; i < Config.GambleItems.length; i += 1) { - if (isNaN(Config.GambleItems[i])) { - if (NTIPAliasClassID.hasOwnProperty(Config.GambleItems[i].replace(/\s+/g, "").toLowerCase())) { - Town.gambleIds.push(NTIPAliasClassID[Config.GambleItems[i].replace(/\s+/g, "").toLowerCase()]); - } else { - Misc.errorReport("ÿc1Invalid gamble entry:ÿc0 " + Config.GambleItems[i]); - } - } else { - Town.gambleIds.push(Config.GambleItems[i]); - } - } - } - - if (Town.gambleIds.length === 0) return true; - - // avoid Alkor - me.act === 3 && Town.goToTown(me.accessToAct(4) ? 4 : 2); - - let npc = Town.initNPC("Gamble", "gamble"); - if (!npc) return false; - - let items = me.findItems(-1, sdk.items.mode.inStorage, sdk.storage.Inventory); - - while (items && items.length > 0) { - list.push(items.shift().gid); - } - - while (me.gold >= Config.GambleGoldStop) { - !getInteractedNPC() && npc.startTrade("Gamble"); - - let item = npc.getItem(); - items = []; - - if (item) { - do { - Town.gambleIds.includes(item.classid) && items.push(copyUnit(item)); - } while (item.getNext()); - - for (let i = 0; i < items.length; i += 1) { - if (!Storage.Inventory.CanFit(items[i])) return false; - - items[i].buy(false, true); - - let newItem = Town.getGambledItem(list); - - if (newItem) { - let result = Pickit.checkItem(newItem); - - switch (result.result) { - case Pickit.Result.WANTED: - Item.logger("Gambled", newItem); - Item.logItem("Gambled", newItem, result.line); - list.push(newItem.gid); - - break; - case Pickit.Result.CUBING: - list.push(newItem.gid); - Cubing.update(); - - break; - case Pickit.Result.CRAFTING: - CraftingSystem.update(newItem); - - break; - default: - Item.logger("Sold", newItem, "Gambling"); - newItem.sell(); - - if (!Config.PacketShopping) { - delay(500); - } - - break; - } - } - } - } + /** + * Easier shopping done at a specific npc + * @param {string} npcName - NPC.NameOfNPC + * @returns {boolean} + */ + NPCAction.shopAt = function (npcName) { + if (!me.inTown) return false; + + // check for invaid npcs to shop at + if ([NPC.Kashya, NPC.Warriv, NPC.Meshif, NPC.Atma, NPC.Greiz, NPC.Tyrael, NPC.Qual_Kehk, NPC.Cain].includes(npcName.toLowerCase())) { + console.warn(npcName + " is an invalid npc to shop at"); + + return false; + } + + // keep track of where we start from + const origAct = me.act; + const npcAct = NPC.getAct(npcName); + if (!npcAct.length) return false; + + try { + if (!npcAct.includes(origAct)) { + Town.goToTown(npcAct[0]); + } + + Town.move(npcName); + let npc = Game.getNPC(npcName); + + if (!npc && Town.move(npcName)) { + npc = Game.getNPC(npcName); + } + + if (!getUIFlag(sdk.uiflags.Shop) && !npc.startTrade("Shop")) { + throw new Error("Failed to shop at " + npc.name); + } + + return Town.shopItems(); + } catch (e) { + console.error(e); + + return false; + } finally { + me.act !== origAct && Town.goToTown(origAct); + } + }; + + NPCAction.buyPotions = function () { + if (me.gold < 450 || !me.getItem(sdk.items.TomeofTownPortal)) return false; + + me.clearBelt(); + const buffer = { hp: 0, mp: 0 }; + const beltSize = Storage.BeltSize(); + let [needPots, needBuffer, specialCheck] = [false, true, false]; + let col = Storage.Belt.checkColumns(beltSize); + + const getNeededBuffer = () => { + [buffer.hp, buffer.mp] = [0, 0]; + me.getItemsEx().filter(function (p) { + return p.isInInventory && [sdk.items.type.HealingPotion, sdk.items.type.ManaPotion].includes(p.itemType); + }).forEach(function (p) { + switch (p.itemType) { + case sdk.items.type.HealingPotion: + return (buffer.hp++); + case sdk.items.type.ManaPotion: + return (buffer.mp++); + } + return false; + }); + }; + + // HP/MP Buffer + (Config.HPBuffer > 0 || Config.MPBuffer > 0) && getNeededBuffer(); + + // Check if we need to buy potions based on Config.MinColumn + if (Config.BeltColumn.some((c, i) => ["hp", "mp"].includes(c) && col[i] > (beltSize - Math.min(Config.MinColumn[i], beltSize)))) { + needPots = true; + } + + // Check if we need any potions for buffers + if (buffer.mp < Config.MPBuffer || buffer.hp < Config.HPBuffer) { + if (Config.BeltColumn.some((c, i) => col[i] >= beltSize && (!needPots || c === "rv"))) { + specialCheck = true; + } + } + + // We have enough potions in inventory + (buffer.mp >= Config.MPBuffer && buffer.hp >= Config.HPBuffer) && (needBuffer = false); + + // No columns to fill + if (!needPots && !needBuffer) return true; + // todo: buy the cheaper potions if we are low on gold or don't need the higher ones i.e have low mana/health pool + // why buy potion that heals 225 (greater mana) if we only have sub 100 mana + let [wantedHpPot, wantedMpPot] = [5, 5]; + // only do this if we are low on gold in the first place + if (me.normal && me.gold < Config.LowGold) { + const mpPotsEffects = PotData.getMpPots().map(el => el.effect[me.classid]); + const hpPotsEffects = PotData.getHpPots().map(el => el.effect[me.classid]); + + wantedHpPot = (hpPotsEffects.findIndex(eff => me.hpmax / 2 < eff) + 1 || hpPotsEffects.length - 1); + wantedMpPot = (mpPotsEffects.findIndex(eff => me.mpmax / 2 < eff) + 1 || mpPotsEffects.length - 1); + console.debug("Wanted hpPot: " + wantedHpPot + " Wanted mpPot: " + wantedMpPot); + } + + if (me.normal && me.highestAct >= 4) { + let pAct = Math.max(wantedHpPot, wantedMpPot); + pAct >= 4 ? me.act < 4 && Town.goToTown(4) : pAct > me.act && Town.goToTown(pAct); + } + + let npc = Town.initNPC("Shop", "buyPotions"); + if (!npc) return false; + + // special check, sometimes our rejuv slot is empty but we do still need buffer. Check if we can buy something to slot there + if (specialCheck && Config.BeltColumn.some((c, i) => c === "rv" && col[i] >= beltSize)) { + let pots = [sdk.items.ThawingPotion, sdk.items.AntidotePotion, sdk.items.StaminaPotion]; + Config.BeltColumn.forEach((c, i) => { + if (c === "rv" && col[i] >= beltSize && pots.length) { + let usePot = pots[0]; + let pot = npc.getItem(usePot); + if (pot) { + Storage.Inventory.CanFit(pot) && Packet.buyItem(pot, false); + pot = me.getItemsEx(usePot, sdk.items.mode.inStorage).filter(i => i.isInInventory).first(); + !!pot && Packet.placeInBelt(pot, i); + pots.shift(); + } else { + needBuffer = false; // we weren't able to find any pots to buy + } + } + }); + } + + for (let i = 0; i < 4; i += 1) { + if (col[i] > 0) { + const useShift = Town.shiftCheck(col, beltSize); + const wantedPot = Config.BeltColumn[i] === "hp" ? wantedHpPot : wantedMpPot; + let pot = Town.getPotion(npc, Config.BeltColumn[i], wantedPot); + + if (pot) { + // print("ÿc2column ÿc0" + i + "ÿc2 needs ÿc0" + col[i] + " ÿc2potions"); + // Shift+buy will trigger if there's no empty columns or if only the current column is empty + if (useShift) { + pot.buy(true); + } else { + for (let j = 0; j < col[i]; j += 1) { + pot.buy(false); + } + } + } + } + + col = Storage.Belt.checkColumns(beltSize); // Re-initialize columns (needed because 1 shift-buy can fill multiple columns) + } + + // re-check + !needBuffer && (Config.HPBuffer > 0 || Config.MPBuffer > 0) && getNeededBuffer(); + + const buyHPBuffers = () => { + if (needBuffer && buffer.hp < Config.HPBuffer) { + for (let i = 0; i < Config.HPBuffer - buffer.hp; i += 1) { + let pot = Town.getPotion(npc, "hp", wantedHpPot); + !!pot && Storage.Inventory.CanFit(pot) && pot.buy(false); + } + } + return true; + }; + const buyMPBuffers = () => { + if (needBuffer && buffer.mp < Config.MPBuffer) { + for (let i = 0; i < Config.MPBuffer - buffer.mp; i += 1) { + let pot = Town.getPotion(npc, "mp", wantedMpPot); + !!pot && Storage.Inventory.CanFit(pot) && pot.buy(false); + } + } + return true; + }; + // priortize mana pots if caster + Check.currentBuild().caster ? buyMPBuffers() && buyHPBuffers() : buyHPBuffers() && buyMPBuffers(); + + // keep cold/pois res high with potions + if (me.gold > 50000 && npc.getItem(sdk.items.ThawingPotion)) { + CharData.pots.get("thawing").need() && Town.buyPots(12, "thawing", true); + CharData.pots.get("antidote").need() && Town.buyPots(12, "antidote", true); + } + + return true; + }; + + /** + * @param {number} classid + * @param {boolean} force + * @returns {boolean} + */ + NPCAction.fillTome = function (classid, force = false) { + const scrollId = (classid === sdk.items.TomeofTownPortal ? sdk.items.ScrollofTownPortal : sdk.items.ScrollofIdentify); + const have = Town.checkScrolls(classid, force); + let myTome = me.getTome(classid); + let invoScrolls = 0; + + if (have > 0 && have < 20) { + // lets see if we have scrolls to cleanup + invoScrolls = me.cleanUpScrolls(myTome, scrollId); + } + + if (have + invoScrolls >= (me.charlvl < 12 ? 5 : 13)) return true; + if (me.gold < 450) return false; + + let npc = Town.initNPC("Shop", "fillTome"); + if (!npc) return false; + + delay(500); + + + if (!myTome) { + let tome = npc.getItem(classid); + + try { + if (tome) { + Storage.Inventory.CanFit(tome) && tome.buy(); + } else { + // couldn't buy tome, lets see if we can just buy a single scroll + if (me.getItem(scrollId)) return true; + let scroll = npc.getItem(scrollId); + if (!scroll || !Storage.Inventory.CanFit(scroll)) return false; + scroll.buy(); + + return true; + } + } catch (e) { + console.error(e); + + return false; + } + } + + let scroll = npc.getItem(scrollId); + if (!scroll) return false; + if (!myTome && !(myTome = me.getTome(classid))) return false; + + // place scrolls in tome if we have any now that we know we have a tome (possibly just bought one) + me.cleanUpScrolls(myTome, scrollId); + + try { + if (me.gold < 5000) { + myTome = me.getTome(classid); + + if (myTome) { + while (myTome.getStat(sdk.stats.Quantity) < 5 && me.gold > 500) { + scroll = npc.getItem(scrollId); + scroll && Packet.buyScroll(scroll, myTome, false); + delay(50); + } + } + } else { + scroll.buy(true); + } + } catch (e2) { + console.error(e2); + + return false; + } + + return true; + }; + + NPCAction.cainID = function (force = false) { + if ((!Config.CainID.Enable && !force) || !Misc.checkQuest(sdk.quest.id.TheSearchForCain, sdk.quest.states.Completed)) return false; + + let npc = getInteractedNPC(); + + // Check if we're already in a shop. It would be pointless to go to Cain if so. + if (npc && npc.name.toLowerCase() === Town.tasks[me.act - 1].Shop) return false; + // Check if we may use Cain - minimum gold + if (me.gold < Config.CainID.MinGold && !force) return false; + + me.cancel(); + + let unids = me.getUnids(); + + if (unids.length) { + // Check if we may use Cain - number of unid items + if (unids.length < Config.CainID.MinUnids && !force) return false; + + let cain = Town.initNPC("CainID", "cainID"); + if (!cain) return false; + + me.cancelUIFlags(); + + while (unids.length) { + const item = unids.shift(); + const { result, line } = Pickit.checkItem(item); + + switch (result) { + case Pickit.Result.TRASH: + Town.sell.push(item); + + break; + case Pickit.Result.WANTED: + case Pickit.Result.SOLOWANTS: + Item.logger("Kept", item); + Item.logItem("Kept", item, line); + + break; + case Pickit.Result.CUBING: + Item.logger("Kept", item, "Cubing-Town"); + Cubing.update(); + + break; + case Pickit.Result.RUNEWORD: + Item.logger("Kept", item, "Runewords-Town"); + Runewords.update(item.classid, item.gid); + + break; + case Pickit.Result.CRAFTING: + Item.logger("Kept", item, "CraftSys-Town"); + CraftingSystem.update(item); + + break; + case Pickit.Result.SOLOSYSTEM: + Item.logger("Kept", item, "SoloWants-Town"); + SoloWants.update(item); + + break; + case Pickit.Result.UNID: + default: + break; + } + } + } + + return true; + }; + + // todo - allow earlier shopping, mainly to get a belt + NPCAction.shopItems = function (force = false) { + if (!Config.MiniShopBot) return true; + if (!me.getTome(sdk.items.TomeofTownPortal)) return false; + + // todo - better gold scaling + // let goldLimit = [10000, 20000, 30000][me.diff]; + const startingGold = me.gold; + let goldLimit = Math.floor(startingGold * 0.60); + let itemTypes = []; + let lowLevelShop = false; + + if (me.charlvl < 6 && startingGold > 200) { + Storage.BeltSize() === 1 && itemTypes.push(sdk.items.type.Belt); + !CharData.skillData.bow.onSwitch && itemTypes.push(sdk.items.type.Bow, sdk.items.type.Crossbow); + if (!itemTypes.length) return true; + lowLevelShop = true; + } + + let npc = getInteractedNPC(); + if (!npc || !npc.itemcount) { + // for now we only do force shop on low level + if ((force || lowLevelShop) && itemTypes.length) { + console.debug("Attempt force shopping"); + Town.initNPC("Repair", "shopItems"); + npc = getInteractedNPC(); + if (!npc || !npc.itemcount) return false; + } else { + return false; + } + } + + if (getTickCount() - Town.lastShopped.tick < Time.seconds(3) && Town.lastShopped.who === npc.name) return false; + let items = npc.getItemsEx() + .filter((item) => !Town.ignoreType(item.itemType) + && (itemTypes.length === 0 || itemTypes.includes(item.itemType)) + && (NTIP.CheckItem(item) !== Pickit.Result.UNWANTED) + && (startingGold - item.getItemCost(sdk.items.cost.ToBuy) > goldLimit)) + .sort((a, b) => { + let priorityA = itemTypes.includes(a.itemType); + let priorityB = itemTypes.includes(b.itemType); + if (priorityA && priorityB) return NTIP.GetTier(b) - NTIP.GetTier(a); + if (priorityA) return 1; + if (priorityB) return -1; + return NTIP.GetTier(b) - NTIP.GetTier(a); + }); + if (!items.length) return false; + + const checkedItems = items.length; + + console.time("shopItems"); + + let bought = 0; + const haveMerc = !!me.getMercEx(); + console.info(true, "ÿc4MiniShopBotÿc0: Scanning " + npc.itemcount + " items."); + + /** + * @param {ItemUnit} item + * @param {string} action + * @param {{ result: PickitResult, line: string }} result + * @param {number | string} tierInfo + */ + const shopReport = (item, action, result, tierInfo) => { + action === undefined && (action = ""); + tierInfo === undefined && (tierInfo = ""); + console.log("ÿc8Kolbot-SoloPlayÿc0: " + action + (tierInfo ? " " + tierInfo : "")); + Item.logger(action, item); + Developer.debugging.autoEquip && Item.logItem("Shopped " + action, item, result.line !== undefined ? result.line : "null"); + }; + + /** + * Buy dependancy item if it's needed + * @param {ItemUnit} item + */ + const checkDependancy = (item) => { + let check = Item.hasDependancy(item); + if (check) { + let el = npc.getItem(check); + !!el && el.buy(); + } + }; + + for (let i = 0; i < items.length; i++) { + const item = items[i]; + const myGold = me.gold; + const itemCost = item.getItemCost(sdk.items.cost.ToBuy); + if (myGold < itemCost) continue; + const { result, line } = Pickit.checkItem(item); + + // no tier'ed items + if (!lowLevelShop && result === Pickit.Result.SOLOWANTS && NTIP.CheckItem(item, NTIP.SoloCheckListNoTier, true).result !== Pickit.Result.UNWANTED) { + try { + if (Storage.Inventory.CanFit(item) && myGold >= itemCost && (myGold - itemCost > goldLimit)) { + if (item.isBaseType) { + if (Item.betterThanStashed(item) && Item.betterBaseThanWearing(item, Developer.debugging.baseCheck)) { + shopReport(item, "better base", line); + item.buy() && bought++; + } + } else { + shopReport(item, "NoTier", line); + item.buy() && bought++; + } + } + } catch (e) { + console.error(e); + } + } else if (result === Pickit.Result.SOLOWANTS && AutoEquip.wanted(item)) { + // tier'ed items + try { + if (Storage.Inventory.CanFit(item) && myGold >= itemCost && (myGold - itemCost > goldLimit)) { + let [mainTier] = [NTIP.GetTier(item)]; + + // we want this to be at least a 5% increase in the tier value + if (Item.hasTier(item) && Item.autoEquipCheck(item) && ((Item.getEquippedItem(item.bodyLocation().first()).tier - mainTier) / (mainTier * 100)) > 5) { + shopReport(item, "AutoEquip", line, (item.prettyPrint + " Tier: " + NTIP.GetTier(item))); + item.buy() && bought++; + Item.autoEquip("InShop"); + checkDependancy(item); + } else if (Item.hasSecondaryTier(item) && Item.autoEquipCheckSecondary(item)) { + shopReport(item, "AutoEquip Switch Shopped", line, (item.prettyPrint + " SecondaryTier: " + NTIP.GetSecondaryTier(item))); + item.buy() && bought++; + Item.autoEquip("InShop"); + checkDependancy(item); + } + } + } catch (e) { + console.error(e); + } + } + + delay(2); + } + + // merc tier'ed items + if (haveMerc && !lowLevelShop) { + items = npc.getItemsEx() + .filter((item) => !Town.ignoreType(item.itemType) && NTIP.GetMercTier(item) > 0) + .sort((a, b) => NTIP.GetMercTier(b) - NTIP.GetMercTier(a)) + .forEach(item => { + const myGold = me.gold; + const itemCost = item.getItemCost(sdk.items.cost.ToBuy); + if (myGold < itemCost) return; + const result = Pickit.checkItem(item); + + if (result.result === Pickit.Result.SOLOWANTS && AutoEquip.wanted(item)) { + try { + if (Storage.Inventory.CanFit(item) && myGold >= itemCost && (myGold - itemCost > goldLimit)) { + if (Item.hasMercTier(item) && Item.autoEquipCheckMerc(item)) { + shopReport(item, "AutoEquipMerc", result.line, (item.fname + " Tier: " + NTIP.GetMercTier(item))); + item.buy() && bought++; + } + } + } catch (e) { + console.error(e); + } + } + + delay(2); + }); + } + + Town.lastShopped.tick = getTickCount(); + Town.lastShopped.who = npc.name; + + console.info(false, "Evaluated " + checkedItems + " items. Bought " + bought + " items. Spent " + (startingGold - me.gold) + " gold", "shopItems"); + + return true; + }; + + NPCAction.gamble = function () { + if (!Town.needGamble() || Config.GambleItems.length === 0) return true; + + let list = []; + + if (Town.gambleIds.length === 0) { + // change text to classid + for (let i = 0; i < Config.GambleItems.length; i += 1) { + if (isNaN(Config.GambleItems[i])) { + if (NTIPAliasClassID.hasOwnProperty(Config.GambleItems[i].replace(/\s+/g, "").toLowerCase())) { + Town.gambleIds.push(NTIPAliasClassID[Config.GambleItems[i].replace(/\s+/g, "").toLowerCase()]); + } else { + Misc.errorReport("ÿc1Invalid gamble entry:ÿc0 " + Config.GambleItems[i]); + } + } else { + Town.gambleIds.push(Config.GambleItems[i]); + } + } + } + + if (Town.gambleIds.length === 0) return true; + + // avoid Alkor + me.act === 3 && Town.goToTown(me.accessToAct(4) ? 4 : 2); + + let npc = Town.initNPC("Gamble", "gamble"); + if (!npc) return false; + + let items = me.findItems(-1, sdk.items.mode.inStorage, sdk.storage.Inventory); + + while (items && items.length > 0) { + list.push(items.shift().gid); + } + + while (me.gold >= Config.GambleGoldStop) { + !getInteractedNPC() && npc.startTrade("Gamble"); + + let item = npc.getItem(); + items = []; + + if (item) { + do { + Town.gambleIds.includes(item.classid) && items.push(copyUnit(item)); + } while (item.getNext()); + + for (let i = 0; i < items.length; i += 1) { + if (!Storage.Inventory.CanFit(items[i])) return false; + + items[i].buy(false, true); + + let newItem = Town.getGambledItem(list); + + if (newItem) { + let result = Pickit.checkItem(newItem); + + switch (result.result) { + case Pickit.Result.WANTED: + Item.logger("Gambled", newItem); + Item.logItem("Gambled", newItem, result.line); + list.push(newItem.gid); + + break; + case Pickit.Result.CUBING: + list.push(newItem.gid); + Cubing.update(); + + break; + case Pickit.Result.CRAFTING: + CraftingSystem.update(newItem); + + break; + default: + Item.logger("Sold", newItem, "Gambling"); + newItem.sell(); + + if (!Config.PacketShopping) { + delay(500); + } + + break; + } + } + } + } - me.cancel(); - } + me.cancel(); + } - return true; - }; + return true; + }; - NPCAction.repair = function (force = false) { - if (Town.cubeRepair()) return true; + NPCAction.repair = function (force = false) { + if (Town.cubeRepair()) return true; - let npc; - let repairAction = Town.needRepair(); - force && repairAction.indexOf("repair") === -1 && repairAction.push("repair"); - if (!repairAction || !repairAction.length) return false; - - for (let i = 0; i < repairAction.length; i += 1) { - switch (repairAction[i]) { - case "repair": - me.act === 3 && Town.goToTown(me.accessToAct(4) ? 4 : 2); - npc = Town.initNPC("Repair", "repair"); - if (!npc) return false; - me.repair(); - - break; - case "buyQuiver": - let bowCheck = me.getItemsEx() - .filter(el => el.isEquipped && [sdk.items.type.Bow, sdk.items.type.Crossbow, sdk.items.type.AmazonBow].includes(el.itemType)) - .first(); - - if (bowCheck) { - let quiverType = bowCheck.itemType === sdk.items.type.Crossbow ? sdk.items.Bolts : sdk.items.Arrows; - let onSwitch = bowCheck.isOnSwap; - onSwitch && me.switchWeapons(sdk.player.slot.Secondary); + let npc; + let repairAction = Town.needRepair(); + force && repairAction.indexOf("repair") === -1 && repairAction.push("repair"); + if (!repairAction || !repairAction.length) return false; + + for (let i = 0; i < repairAction.length; i += 1) { + switch (repairAction[i]) { + case "repair": + me.act === 3 && Town.goToTown(me.accessToAct(4) ? 4 : 2); + npc = Town.initNPC("Repair", "repair"); + if (!npc) return false; + me.repair(); + + break; + case "buyQuiver": + let bowCheck = me.getItemsEx() + .filter(el => el.isEquipped && [sdk.items.type.Bow, sdk.items.type.Crossbow, sdk.items.type.AmazonBow].includes(el.itemType)) + .first(); + + if (bowCheck) { + let quiverType = bowCheck.itemType === sdk.items.type.Crossbow ? sdk.items.Bolts : sdk.items.Arrows; + let onSwitch = bowCheck.isOnSwap; + onSwitch && me.switchWeapons(sdk.player.slot.Secondary); - npc = Town.initNPC("Repair", "buyQuiver"); - if (!npc) return false; + npc = Town.initNPC("Repair", "buyQuiver"); + if (!npc) return false; - let myQuiver = me.getItem(quiver, sdk.items.mode.Equipped); - !!myQuiver && myQuiver.sell(); + let myQuiver = me.getItem(quiver, sdk.items.mode.Equipped); + !!myQuiver && myQuiver.sell(); - let quiver = npc.getItem(quiverType); - !!quiver && quiver.buy(); - onSwitch && me.switchWeapons(sdk.player.slot.Main); - } - - break; - } - } - - NPCAction.shopItems(); - - return true; - }; - - NPCAction.reviveMerc = function () { - if (!me.needMerc()) return true; - let preArea = me.area; - - // avoid Aheara - me.act === 3 && Town.goToTown(me.accessToAct(4) ? 4 : 2); - - let npc = Town.initNPC("Merc", "reviveMerc"); - if (!npc) return false; - - MainLoop: - for (let i = 0; i < 3; i += 1) { - let dialog = getDialogLines(); - if (!dialog) continue; - - for (let lines = 0; lines < dialog.length; lines += 1) { - if (dialog[lines].text.match(":", "gi")) { - dialog[lines].handler(); - delay(Math.max(750, me.ping * 2)); - } - - // "You do not have enough gold for that." - if (dialog[lines].text.match(getLocaleString(sdk.locale.dialog.youDoNotHaveEnoughGoldForThat), "gi")) { - dialog.find(line => !line.text.match(getLocaleString(sdk.locale.dialog.youDoNotHaveEnoughGoldForThat), "gi")).handler(); - delay(Math.max(750, me.ping * 2)); - me.cancelUIFlags(); - console.error("Could not revive merc: My current gold: " + me.gold + ", current mercrevivecost: " + me.mercrevivecost); - return false; - } - } - - let tick = getTickCount(); - - while (getTickCount() - tick < 2000) { - if (me.getMercEx()) { - delay(Math.max(750, me.ping * 2)); - - break MainLoop; - } - - delay(200); - } - } - - Attack.checkInfinity(); - - if (me.getMercEx()) { - // Cast BO on merc so he doesn't just die again. Only do this is you are a barb or actually have a cta. Otherwise its just a waste of time. - if (Config.MercWatch && Precast.needOutOfTownCast()) { - console.log("MercWatch precast"); - Precast.doRandomPrecast(true, preArea); - } - - return true; - } - - return false; - }; + let quiver = npc.getItem(quiverType); + !!quiver && quiver.buy(); + onSwitch && me.switchWeapons(sdk.player.slot.Main); + } + + break; + } + } + + NPCAction.shopItems(); + + return true; + }; + + NPCAction.reviveMerc = function () { + if (!me.needMerc()) return true; + let preArea = me.area; + + // avoid Aheara + me.act === 3 && Town.goToTown(me.accessToAct(4) ? 4 : 2); + + let npc = Town.initNPC("Merc", "reviveMerc"); + if (!npc) return false; + + MainLoop: + for (let i = 0; i < 3; i += 1) { + let dialog = getDialogLines(); + if (!dialog) continue; + + for (let lines = 0; lines < dialog.length; lines += 1) { + if (dialog[lines].text.match(":", "gi")) { + dialog[lines].handler(); + delay(Math.max(750, me.ping * 2)); + } + + // "You do not have enough gold for that." + if (dialog[lines].text.match(getLocaleString(sdk.locale.dialog.youDoNotHaveEnoughGoldForThat), "gi")) { + dialog.find(line => !line.text.match(getLocaleString(sdk.locale.dialog.youDoNotHaveEnoughGoldForThat), "gi")).handler(); + delay(Math.max(750, me.ping * 2)); + me.cancelUIFlags(); + console.error("Could not revive merc: My current gold: " + me.gold + ", current mercrevivecost: " + me.mercrevivecost); + return false; + } + } + + let tick = getTickCount(); + + while (getTickCount() - tick < 2000) { + if (me.getMercEx()) { + delay(Math.max(750, me.ping * 2)); + + break MainLoop; + } + + delay(200); + } + } + + Attack.checkInfinity(); + + if (me.getMercEx()) { + // Cast BO on merc so he doesn't just die again. Only do this is you are a barb or actually have a cta. Otherwise its just a waste of time. + if (Config.MercWatch && Precast.needOutOfTownCast()) { + console.log("MercWatch precast"); + Precast.doRandomPrecast(true, preArea); + } + + return true; + } + + return false; + }; })(global.NPCAction = global.NPCAction || {}); diff --git a/libs/SoloPlay/Functions/NTIPOverrides.js b/libs/SoloPlay/Functions/NTIPOverrides.js index 814e4379..29af9a31 100644 --- a/libs/SoloPlay/Functions/NTIPOverrides.js +++ b/libs/SoloPlay/Functions/NTIPOverrides.js @@ -25,8 +25,8 @@ NTIP.SoloCheckList = []; NTIP.SoloCheckListNoTier = []; NTIP.SoloStringArray = []; NTIP.FinalGear = { - list: [], - strArray: [], + list: [], + strArray: [], }; /** @@ -34,52 +34,52 @@ NTIP.FinalGear = { * @returns {(item: ItemUnit) => number} */ NTIP.generateTierFunc = function (tierType) { - return function (item) { - let tier = -1; - - const updateTier = (wanted) => { - const tmpTier = wanted[tierType](item); - - if (tier < tmpTier) { - tier = tmpTier; - } - }; - - // Go through ALL lines that describe the item - for (let i = 0; i < NTIP.SoloCheckList.length; i += 1) { - if (NTIP.SoloCheckList[i].length !== 3) { - continue; - } - - const [type, stat, wanted] = NTIP.SoloCheckList[i]; - - // If the line doesnt have a tier of this type, we dont need to call it - if (typeof wanted === "object" && wanted && typeof wanted[tierType] === "function") { - try { - if (typeof type === "function") { - if (type(item)) { - if (typeof stat === "function") { - if (stat(item)) { - updateTier(wanted); - } - } else { - updateTier(wanted); - } - } - } else if (typeof stat === "function") { - if (stat(item)) { - updateTier(wanted); - } - } - } catch (e) { - const info = NTIP.SoloStringArray[i]; - Misc.errorReport("ÿc1Pickit Tier (" + tierType + ") error! Line # ÿc2" + info.line + " ÿc1Entry: ÿc0" + info.string + " (" + info.file + ") Error message: " + e.message + " Trigger item: " + item.fname.split("\n").reverse().join(" ")); - } - } - } - - return tier; - }; + return function (item) { + let tier = -1; + + const updateTier = (wanted) => { + const tmpTier = wanted[tierType](item); + + if (tier < tmpTier) { + tier = tmpTier; + } + }; + + // Go through ALL lines that describe the item + for (let i = 0; i < NTIP.SoloCheckList.length; i += 1) { + if (NTIP.SoloCheckList[i].length !== 3) { + continue; + } + + const [type, stat, wanted] = NTIP.SoloCheckList[i]; + + // If the line doesnt have a tier of this type, we dont need to call it + if (typeof wanted === "object" && wanted && typeof wanted[tierType] === "function") { + try { + if (typeof type === "function") { + if (type(item)) { + if (typeof stat === "function") { + if (stat(item)) { + updateTier(wanted); + } + } else { + updateTier(wanted); + } + } + } else if (typeof stat === "function") { + if (stat(item)) { + updateTier(wanted); + } + } + } catch (e) { + const info = NTIP.SoloStringArray[i]; + Misc.errorReport("ÿc1Pickit Tier (" + tierType + ") error! Line # ÿc2" + info.line + " ÿc1Entry: ÿc0" + info.string + " (" + info.file + ") Error message: " + e.message + " Trigger item: " + item.fname.split("\n").reverse().join(" ")); + } + } + } + + return tier; + }; }; /** @@ -111,26 +111,26 @@ NTIP.GetCharmTier = NTIP.generateTierFunc("Charmtier"); NTIP.GetSecondaryTier = NTIP.generateTierFunc("Secondarytier"); NTIP.addLine = function (itemString) { - const info = { - line: NTIP.SoloCheckList.length + 1, - file: "Kolbot-SoloPlay", - string: itemString - }; - - const line = NTIP.ParseLineInt(itemString, info); - - if (line) { - if (!itemString.toLowerCase().includes("tier")) { - NTIP.SoloCheckListNoTier.push(line); - } else { - NTIP.SoloCheckListNoTier.push([false, false]); - } - - NTIP.SoloCheckList.push(line); - NTIP.SoloStringArray.push(info); - } - - return true; + const info = { + line: NTIP.SoloCheckList.length + 1, + file: "Kolbot-SoloPlay", + string: itemString + }; + + const line = NTIP.ParseLineInt(itemString, info); + + if (line) { + if (!itemString.toLowerCase().includes("tier")) { + NTIP.SoloCheckListNoTier.push(line); + } else { + NTIP.SoloCheckListNoTier.push([false, false]); + } + + NTIP.SoloCheckList.push(line); + NTIP.SoloStringArray.push(info); + } + + return true; }; /** @@ -138,723 +138,723 @@ NTIP.addLine = function (itemString) { * @returns {boolean} */ NTIP.buildFinalGear = function (arr) { - for (let i = 0; i < arr.length; i++) { - const info = { - line: NTIP.FinalGear.list.length + 1, - file: "Kolbot-SoloPlay", - string: arr[i] - }; - - /** @type {string} */ - const line = NTIP.ParseLineInt(arr[i], info); - - if (line) { - let lineCheck = arr[i].toLowerCase(); - - switch (true) { - case !lineCheck.includes("tier"): - case lineCheck.includes("merctier"): - case lineCheck.includes("secondarytier"): - case lineCheck.includes("charmtier"): - continue; - } - - NTIP.FinalGear.list.push(line); - NTIP.FinalGear.strArray.push(info); - } - } - - return true; + for (let i = 0; i < arr.length; i++) { + const info = { + line: NTIP.FinalGear.list.length + 1, + file: "Kolbot-SoloPlay", + string: arr[i] + }; + + /** @type {string} */ + const line = NTIP.ParseLineInt(arr[i], info); + + if (line) { + let lineCheck = arr[i].toLowerCase(); + + switch (true) { + case !lineCheck.includes("tier"): + case lineCheck.includes("merctier"): + case lineCheck.includes("secondarytier"): + case lineCheck.includes("charmtier"): + continue; + } + + NTIP.FinalGear.list.push(line); + NTIP.FinalGear.strArray.push(info); + } + } + + return true; }; // currently just using for quiver's but if that changes need to figure out way to seperate out sections // so things can be deleted without affecting the entire list NTIP.addToRuntime = function (itemString) { - const info = { - line: NTIP.RuntimeCheckList.length + 1, - file: "Kolbot-SoloPlay-Runtime", - string: itemString - }; + const info = { + line: NTIP.RuntimeCheckList.length + 1, + file: "Kolbot-SoloPlay-Runtime", + string: itemString + }; - const line = NTIP.ParseLineInt(itemString, info); + const line = NTIP.ParseLineInt(itemString, info); - if (line) { - NTIP.RuntimeCheckList.push(line); - NTIP.RuntimeStringArray.push(info); - } + if (line) { + NTIP.RuntimeCheckList.push(line); + NTIP.RuntimeStringArray.push(info); + } - return true; + return true; }; NTIP.resetRuntimeList = () => { - NTIP.RuntimeCheckList.length = 0; - NTIP.RuntimeStringArray.length = 0; + NTIP.RuntimeCheckList.length = 0; + NTIP.RuntimeStringArray.length = 0; }; NTIP.buildList = function (...arraystoloop) { - for (let arr of arraystoloop) { - if (Array.isArray(arr)) { - for (let i = 0; i < arr.length; i++) { - NTIP.addLine(arr[i]); - } - } - } - - return true; + for (let arr of arraystoloop) { + if (Array.isArray(arr)) { + for (let i = 0; i < arr.length; i++) { + NTIP.addLine(arr[i]); + } + } + } + + return true; }; NTIP.hasStats = function (item, entryList = [], verbose = false) { - let hasStat = false, line = "", stats; - const list = entryList.length ? entryList : NTIP.SoloCheckList; - const stringArr = entryList.length ? stringArray : NTIP.SoloStringArray; - - for (let i = 0; i < list.length; i++) { - try { - // eslint-disable-next-line no-unused-vars - let [type, stat, wanted] = list[i]; - - if (typeof type === "function") { - if (type(item)) { - if (typeof stat === "function") { - if (stat(item)) { - hasStat = true; - stats = stat; - line = stringArr[i].file + " #" + stringArr[i].line + " " + stringArr[i].string; - - break; - } - } else { - hasStat = false; - - break; - } - } - } - } catch (e) { - console.log(e); - hasStat = false; - - break; - } - } - - if (hasStat && verbose) { - console.debug(stats); - console.debug(line); - } - - return hasStat; + let hasStat = false, line = "", stats; + const list = entryList.length ? entryList : NTIP.SoloCheckList; + const stringArr = entryList.length ? stringArray : NTIP.SoloStringArray; + + for (let i = 0; i < list.length; i++) { + try { + // eslint-disable-next-line no-unused-vars + let [type, stat, wanted] = list[i]; + + if (typeof type === "function") { + if (type(item)) { + if (typeof stat === "function") { + if (stat(item)) { + hasStat = true; + stats = stat; + line = stringArr[i].file + " #" + stringArr[i].line + " " + stringArr[i].string; + + break; + } + } else { + hasStat = false; + + break; + } + } + } + } catch (e) { + console.log(e); + hasStat = false; + + break; + } + } + + if (hasStat && verbose) { + console.debug(stats); + console.debug(line); + } + + return hasStat; }; // this method for charms needs work NTIP.getInvoQuantity = function (item, entryList = []) { - const list = entryList.length ? entryList : NTIP.SoloCheckList; - - for (let i = 0; i < list.length; i++) { - try { - const [type, stat, wanted] = list[i]; - - if (typeof type === "function") { - if (type(item)) { - if (typeof stat === "function") { - if (stat(item)) { - if (wanted && wanted.InvoQuantity && !isNaN(wanted.InvoQuantity)) { - return wanted.InvoQuantity; - } - } - } else { - if (wanted && wanted.InvoQuantity && !isNaN(wanted.InvoQuantity)) { - return wanted.InvoQuantity; - } - } - } - } else if (typeof stat === "function") { - if (stat(item)) { - if (wanted && wanted.InvoQuantity && !isNaN(wanted.InvoQuantity)) { - return wanted.InvoQuantity; - } - } - } - } catch (e) { - return -1; - } - } - - return -1; + const list = entryList.length ? entryList : NTIP.SoloCheckList; + + for (let i = 0; i < list.length; i++) { + try { + const [type, stat, wanted] = list[i]; + + if (typeof type === "function") { + if (type(item)) { + if (typeof stat === "function") { + if (stat(item)) { + if (wanted && wanted.InvoQuantity && !isNaN(wanted.InvoQuantity)) { + return wanted.InvoQuantity; + } + } + } else { + if (wanted && wanted.InvoQuantity && !isNaN(wanted.InvoQuantity)) { + return wanted.InvoQuantity; + } + } + } + } else if (typeof stat === "function") { + if (stat(item)) { + if (wanted && wanted.InvoQuantity && !isNaN(wanted.InvoQuantity)) { + return wanted.InvoQuantity; + } + } + } + } catch (e) { + return -1; + } + } + + return -1; }; NTIP.getMaxQuantity = function (item, entryList = []) { - const list = entryList.length ? entryList : NTIP.SoloCheckList; - - for (let i = 0; i < list.length; i++) { - try { - let [type, stat, wanted] = list[i]; - - if (typeof type === "function") { - if (type(item)) { - if (typeof stat === "function") { - if (stat(item)) { - if (wanted && wanted.MaxQuantity && !isNaN(wanted.MaxQuantity)) { - return wanted.MaxQuantity; - } - } - } else { - if (wanted && wanted.MaxQuantity && !isNaN(wanted.MaxQuantity)) { - return wanted.MaxQuantity; - } - } - } - } else if (typeof stat === "function") { - if (stat(item)) { - if (wanted && wanted.MaxQuantity && !isNaN(wanted.MaxQuantity)) { - return wanted.MaxQuantity; - } - } - } - } catch (e) { - return -1; - } - } - - return -1; + const list = entryList.length ? entryList : NTIP.SoloCheckList; + + for (let i = 0; i < list.length; i++) { + try { + let [type, stat, wanted] = list[i]; + + if (typeof type === "function") { + if (type(item)) { + if (typeof stat === "function") { + if (stat(item)) { + if (wanted && wanted.MaxQuantity && !isNaN(wanted.MaxQuantity)) { + return wanted.MaxQuantity; + } + } + } else { + if (wanted && wanted.MaxQuantity && !isNaN(wanted.MaxQuantity)) { + return wanted.MaxQuantity; + } + } + } + } else if (typeof stat === "function") { + if (stat(item)) { + if (wanted && wanted.MaxQuantity && !isNaN(wanted.MaxQuantity)) { + return wanted.MaxQuantity; + } + } + } + } catch (e) { + return -1; + } + } + + return -1; }; NTIP.CheckItem = function (item, entryList, verbose = false) { - let rval = {}; - let result = 0; - const identified = item.getFlag(sdk.items.flags.Identified); - - /** - * - * @param {any[]} list - * @param {string[]} stringArr - * @returns - */ - const iterateList = (list, stringArr) => { - let i, num; - - for (i = 0; i < list.length; i++) { - try { - // Get the values in separated variables (its faster) - const [type, stat, wanted] = list[i]; - - if (typeof type === "function") { - if (type(item)) { - if (typeof stat === "function") { - if (stat(item)) { - if (wanted && wanted.MaxQuantity && !isNaN(wanted.MaxQuantity)) { - num = NTIP.CheckQuantityOwned(type, stat); - - if (num < wanted.MaxQuantity) { - result = 1; - - break; - } else { - // attempt at inv fix for maxquantity - if (item.getParent() && item.getParent().name === me.name && item.mode === sdk.items.mode.inStorage && num === wanted.MaxQuantity) { - result = 1; - - break; - } - } - } else { - result = 1; - - break; - } - } else if (!identified && result === 0 || !identified && result === 1) { - result = -1; - - if (verbose && stringArr[i] !== undefined) { - rval.line = stringArr[i].file + " #" + stringArr[i].line; - } - } - } else { - if (wanted && wanted.MaxQuantity && !isNaN(wanted.MaxQuantity)) { - num = NTIP.CheckQuantityOwned(type, null); - - if (num < wanted.MaxQuantity) { - result = 1; - - break; - } else { - // attempt at inv fix for maxquantity - if (item.getParent() && item.getParent().name === me.name && item.mode === sdk.items.mode.inStorage && num === wanted.MaxQuantity) { - result = 1; - - break; - } - } - } else { - result = 1; - - break; - } - } - } - } else if (typeof stat === "function") { - if (stat(item)) { - if (wanted && wanted.MaxQuantity && !isNaN(wanted.MaxQuantity)) { - num = NTIP.CheckQuantityOwned(null, stat); - - if (num < wanted.MaxQuantity) { - result = 1; - - break; - } else { - // attempt at inv fix for maxquantity - if (item.getParent() && item.getParent().name === me.name && item.mode === sdk.items.mode.inStorage && num === wanted.MaxQuantity) { - result = 1; - - break; - } - } - } else { - result = 1; - - break; - } - } else if (!identified && result === 0 || !identified && result === 1) { - result = -1; - - if (verbose && stringArr[i] !== undefined) { - rval.line = stringArr[i].file + " #" + stringArr[i].line; - } - } - } - } catch (pickError) { - showConsole(); - - if (!entryList) { - Misc.errorReport("ÿc1Pickit error! Line # ÿc2" + stringArr[i].line + " ÿc1Entry: ÿc0" + stringArr[i].string + " (" + stringArr[i].file + ") Error message: " + pickError.message + " Trigger item: " + item.fname.split("\n").reverse().join(" ")); - - list.splice(i, 1); // Remove the element from the list - } else { - Misc.errorReport("ÿc1Pickit error in runeword config!"); - } - - result = 0; - } - } - - if (verbose) { - rval.result = result; - rval.line = (() => { - if (stringArr[i] === undefined) return null; - return result === 1 ? stringArr[i].file + " #" + stringArr[i].line : null; - })(); - - if (!identified && result === 1) { - rval.result = -1; - } - - return rval; - } - - return result; - }; - - const listOfLists = [ - [NTIP.SoloCheckList, NTIP.SoloStringArray], - [NTIP_CheckList, stringArray], - [NTIP.RuntimeCheckList, NTIP.RuntimeStringArray] - ]; - if (Array.isArray(entryList)) return iterateList(entryList, stringArray); - - for (let i = 0; i < listOfLists.length; i++) { - iterateList(listOfLists[i][0], listOfLists[i][1]); - if ((verbose && rval.result !== 0) || (!verbose && result !== 0)) { - break; - } - } - - return verbose ? rval : result; + let rval = {}; + let result = 0; + const identified = item.getFlag(sdk.items.flags.Identified); + + /** + * + * @param {any[]} list + * @param {string[]} stringArr + * @returns + */ + const iterateList = (list, stringArr) => { + let i, num; + + for (i = 0; i < list.length; i++) { + try { + // Get the values in separated variables (its faster) + const [type, stat, wanted] = list[i]; + + if (typeof type === "function") { + if (type(item)) { + if (typeof stat === "function") { + if (stat(item)) { + if (wanted && wanted.MaxQuantity && !isNaN(wanted.MaxQuantity)) { + num = NTIP.CheckQuantityOwned(type, stat); + + if (num < wanted.MaxQuantity) { + result = 1; + + break; + } else { + // attempt at inv fix for maxquantity + if (item.getParent() && item.getParent().name === me.name && item.mode === sdk.items.mode.inStorage && num === wanted.MaxQuantity) { + result = 1; + + break; + } + } + } else { + result = 1; + + break; + } + } else if (!identified && result === 0 || !identified && result === 1) { + result = -1; + + if (verbose && stringArr[i] !== undefined) { + rval.line = stringArr[i].file + " #" + stringArr[i].line; + } + } + } else { + if (wanted && wanted.MaxQuantity && !isNaN(wanted.MaxQuantity)) { + num = NTIP.CheckQuantityOwned(type, null); + + if (num < wanted.MaxQuantity) { + result = 1; + + break; + } else { + // attempt at inv fix for maxquantity + if (item.getParent() && item.getParent().name === me.name && item.mode === sdk.items.mode.inStorage && num === wanted.MaxQuantity) { + result = 1; + + break; + } + } + } else { + result = 1; + + break; + } + } + } + } else if (typeof stat === "function") { + if (stat(item)) { + if (wanted && wanted.MaxQuantity && !isNaN(wanted.MaxQuantity)) { + num = NTIP.CheckQuantityOwned(null, stat); + + if (num < wanted.MaxQuantity) { + result = 1; + + break; + } else { + // attempt at inv fix for maxquantity + if (item.getParent() && item.getParent().name === me.name && item.mode === sdk.items.mode.inStorage && num === wanted.MaxQuantity) { + result = 1; + + break; + } + } + } else { + result = 1; + + break; + } + } else if (!identified && result === 0 || !identified && result === 1) { + result = -1; + + if (verbose && stringArr[i] !== undefined) { + rval.line = stringArr[i].file + " #" + stringArr[i].line; + } + } + } + } catch (pickError) { + showConsole(); + + if (!entryList) { + Misc.errorReport("ÿc1Pickit error! Line # ÿc2" + stringArr[i].line + " ÿc1Entry: ÿc0" + stringArr[i].string + " (" + stringArr[i].file + ") Error message: " + pickError.message + " Trigger item: " + item.fname.split("\n").reverse().join(" ")); + + list.splice(i, 1); // Remove the element from the list + } else { + Misc.errorReport("ÿc1Pickit error in runeword config!"); + } + + result = 0; + } + } + + if (verbose) { + rval.result = result; + rval.line = (() => { + if (stringArr[i] === undefined) return null; + return result === 1 ? stringArr[i].file + " #" + stringArr[i].line : null; + })(); + + if (!identified && result === 1) { + rval.result = -1; + } + + return rval; + } + + return result; + }; + + const listOfLists = [ + [NTIP.SoloCheckList, NTIP.SoloStringArray], + [NTIP_CheckList, stringArray], + [NTIP.RuntimeCheckList, NTIP.RuntimeStringArray] + ]; + if (Array.isArray(entryList)) return iterateList(entryList, stringArray); + + for (let i = 0; i < listOfLists.length; i++) { + iterateList(listOfLists[i][0], listOfLists[i][1]); + if ((verbose && rval.result !== 0) || (!verbose && result !== 0)) { + break; + } + } + + return verbose ? rval : result; }; NTIP.OpenFile = function (filepath, notify) { - if (!FileTools.exists(filepath)) { - if (notify) { - Misc.errorReport("ÿc1NIP file doesn't exist: ÿc0" + filepath); - } + if (!FileTools.exists(filepath)) { + if (notify) { + Misc.errorReport("ÿc1NIP file doesn't exist: ÿc0" + filepath); + } - return false; - } + return false; + } - let nipfile, tick = getTickCount(), entries = 0; - let filename = filepath.substring(filepath.lastIndexOf("/") + 1, filepath.length); + let nipfile, tick = getTickCount(), entries = 0; + let filename = filepath.substring(filepath.lastIndexOf("/") + 1, filepath.length); - try { - nipfile = File.open(filepath, 0); - } catch (fileError) { - notify && Misc.errorReport("ÿc1Failed to load NIP: ÿc0" + filename); - } + try { + nipfile = File.open(filepath, 0); + } catch (fileError) { + notify && Misc.errorReport("ÿc1Failed to load NIP: ÿc0" + filename); + } - if (!nipfile) return false; + if (!nipfile) return false; - let lines = nipfile.readAllLines(); - nipfile.close(); + let lines = nipfile.readAllLines(); + nipfile.close(); - for (let i = 0; i < lines.length; i += 1) { - const info = { - line: i + 1, - file: filename, - string: lines[i] - }; + for (let i = 0; i < lines.length; i += 1) { + const info = { + line: i + 1, + file: filename, + string: lines[i] + }; - let line = NTIP.ParseLineInt(lines[i], info); + let line = NTIP.ParseLineInt(lines[i], info); - if (line) { - entries += 1; - NTIP_CheckList.push(line); + if (line) { + entries += 1; + NTIP_CheckList.push(line); - if (!lines[i].toLowerCase().match("tier")) { - NTIP_CheckListNoTier.push(line); - } else { - NTIP_CheckListNoTier.push([false, false]); - } + if (!lines[i].toLowerCase().match("tier")) { + NTIP_CheckListNoTier.push(line); + } else { + NTIP_CheckListNoTier.push([false, false]); + } - stringArray.push(info); - } - } + stringArray.push(info); + } + } - if (notify) { - console.log("ÿc4Loaded NIP: ÿc2" + filename + "ÿc4. Lines: ÿc2" + lines.length + "ÿc4. Valid entries: ÿc2" + entries + ". ÿc4Time: ÿc2" + (getTickCount() - tick) + " ms"); - } + if (notify) { + console.log("ÿc4Loaded NIP: ÿc2" + filename + "ÿc4. Lines: ÿc2" + lines.length + "ÿc4. Valid entries: ÿc2" + entries + ". ÿc4Time: ÿc2" + (getTickCount() - tick) + " ms"); + } - return true; + return true; }; NTIP.ParseLineInt = function (input, info) { - let i, property, p_start, p_end, p_section, p_keyword, p_result, value; + let i, property, p_start, p_end, p_section, p_keyword, p_result, value; - p_end = input.indexOf("//"); + p_end = input.indexOf("//"); - if (p_end !== -1) { - input = input.substring(0, p_end); - } + if (p_end !== -1) { + input = input.substring(0, p_end); + } - input = input.replace(/\s+/g, "").toLowerCase(); + input = input.replace(/\s+/g, "").toLowerCase(); - if (input.length < 5) { - return null; - } + if (input.length < 5) { + return null; + } - p_result = input.split("#"); + p_result = input.split("#"); - if (p_result[0] && p_result[0].length > 4) { - p_section = p_result[0].split("["); + if (p_result[0] && p_result[0].length > 4) { + p_section = p_result[0].split("["); - p_result[0] = p_section[0]; + p_result[0] = p_section[0]; - for (i = 1; i < p_section.length; i += 1) { - p_end = p_section[i].indexOf("]") + 1; - property = p_section[i].substring(0, p_end - 1); + for (i = 1; i < p_section.length; i += 1) { + p_end = p_section[i].indexOf("]") + 1; + property = p_section[i].substring(0, p_end - 1); - switch (property) { - case "wsm": - case "weaponspeed": - p_result[0] += 'getBaseStat("items", item.classid, "speed")'; + switch (property) { + case "wsm": + case "weaponspeed": + p_result[0] += 'getBaseStat("items", item.classid, "speed")'; - break; - case "minimumsockets": - p_result[0] += 'getBaseStat("items", item.classid, "gemsockets")'; + break; + case "minimumsockets": + p_result[0] += 'getBaseStat("items", item.classid, "gemsockets")'; - break; - case "strreq": - p_result[0] += "item.strreq"; + break; + case "strreq": + p_result[0] += "item.strreq"; - break; - case "dexreq": - p_result[0] += "item.dexreq"; + break; + case "dexreq": + p_result[0] += "item.dexreq"; - break; - case "2handed": - p_result[0] += 'getBaseStat("items", item.classid, "2handed")'; + break; + case "2handed": + p_result[0] += 'getBaseStat("items", item.classid, "2handed")'; - break; - case "color": - p_result[0] += "item.getColor()"; + break; + case "color": + p_result[0] += "item.getColor()"; - break; - case "type": - p_result[0] += "item.itemType"; + break; + case "type": + p_result[0] += "item.itemType"; - break; - case "name": - p_result[0] += "item.classid"; + break; + case "name": + p_result[0] += "item.classid"; - break; - case "class": - p_result[0] += "item.itemclass"; + break; + case "class": + p_result[0] += "item.itemclass"; - break; - case "quality": - p_result[0] += "item.quality"; + break; + case "quality": + p_result[0] += "item.quality"; - break; - case "flag": - if (p_section[i][p_end] === "!") { - p_result[0] += "!item.getFlag("; - } else { - p_result[0] += "item.getFlag("; - } + break; + case "flag": + if (p_section[i][p_end] === "!") { + p_result[0] += "!item.getFlag("; + } else { + p_result[0] += "item.getFlag("; + } - p_end += 2; + p_end += 2; - break; - case "level": - p_result[0] += "item.ilvl"; + break; + case "level": + p_result[0] += "item.ilvl"; - break; - case "prefix": - if (p_section[i][p_end] === "!") { - p_result[0] += "!item.getPrefix("; - } else { - p_result[0] += "item.getPrefix("; - } + break; + case "prefix": + if (p_section[i][p_end] === "!") { + p_result[0] += "!item.getPrefix("; + } else { + p_result[0] += "item.getPrefix("; + } - p_end += 2; + p_end += 2; - break; - case "suffix": - if (p_section[i][p_end] === "!") { - p_result[0] += "!item.getSuffix("; - } else { - p_result[0] += "item.getSuffix("; - } + break; + case "suffix": + if (p_section[i][p_end] === "!") { + p_result[0] += "!item.getSuffix("; + } else { + p_result[0] += "item.getSuffix("; + } - p_end += 2; + p_end += 2; - break; - case "europe": - case "uswest": - case "useast": - case "asia": - p_result[0] += '("' + me.realm.toLowerCase() + '"==="' + property.toLowerCase() + '")'; + break; + case "europe": + case "uswest": + case "useast": + case "asia": + p_result[0] += '("' + me.realm.toLowerCase() + '"==="' + property.toLowerCase() + '")'; - break; - case "ladder": - p_result[0] += "me.ladder"; + break; + case "ladder": + p_result[0] += "me.ladder"; - break; - case "hardcore": - p_result[0] += "(!!me.playertype)"; + break; + case "hardcore": + p_result[0] += "(!!me.playertype)"; - break; - case "classic": - p_result[0] += "(!me.gametype)"; + break; + case "classic": + p_result[0] += "(!me.gametype)"; - break; - case "distance": - p_result[0] += "(item.onGroundOrDropping && item.distance || Infinity)"; + break; + case "distance": + p_result[0] += "(item.onGroundOrDropping && item.distance || Infinity)"; - break; - default: - Misc.errorReport("Unknown property: " + property + " File: " + info.file + " Line: " + info.line); - - return false; - } + break; + default: + Misc.errorReport("Unknown property: " + property + " File: " + info.file + " Line: " + info.line); + + return false; + } - for (p_start = p_end; p_end < p_section[i].length; p_end += 1) { - if (!NTIP.IsSyntaxInt(p_section[i][p_end])) { - break; - } - } - - p_result[0] += p_section[i].substring(p_start, p_end); + for (p_start = p_end; p_end < p_section[i].length; p_end += 1) { + if (!NTIP.IsSyntaxInt(p_section[i][p_end])) { + break; + } + } + + p_result[0] += p_section[i].substring(p_start, p_end); - if (p_section[i].substring(p_start, p_end) === "=") { - Misc.errorReport("Unexpected = at line " + info.line + " in " + info.file); - - return false; - } - - for (p_start = p_end; p_end < p_section[i].length; p_end += 1) { - if (NTIP.IsSyntaxInt(p_section[i][p_end])) { - break; - } - } + if (p_section[i].substring(p_start, p_end) === "=") { + Misc.errorReport("Unexpected = at line " + info.line + " in " + info.file); + + return false; + } + + for (p_start = p_end; p_end < p_section[i].length; p_end += 1) { + if (NTIP.IsSyntaxInt(p_section[i][p_end])) { + break; + } + } - p_keyword = p_section[i].substring(p_start, p_end); - - if (isNaN(p_keyword)) { - switch (property) { - case "color": - if (NTIPAliasColor[p_keyword] === undefined) { - Misc.errorReport("Unknown color: " + p_keyword + " File: " + info.file + " Line: " + info.line); - - return false; - } - - p_result[0] += NTIPAliasColor[p_keyword]; - - break; - case "type": - if (NTIPAliasType[p_keyword] === undefined) { - Misc.errorReport("Unknown type: " + p_keyword + " File: " + info.file + " Line: " + info.line); - - return false; - } + p_keyword = p_section[i].substring(p_start, p_end); + + if (isNaN(p_keyword)) { + switch (property) { + case "color": + if (NTIPAliasColor[p_keyword] === undefined) { + Misc.errorReport("Unknown color: " + p_keyword + " File: " + info.file + " Line: " + info.line); + + return false; + } + + p_result[0] += NTIPAliasColor[p_keyword]; + + break; + case "type": + if (NTIPAliasType[p_keyword] === undefined) { + Misc.errorReport("Unknown type: " + p_keyword + " File: " + info.file + " Line: " + info.line); + + return false; + } - p_result[0] += NTIPAliasType[p_keyword]; + p_result[0] += NTIPAliasType[p_keyword]; - break; - case "name": - if (NTIPAliasClassID[p_keyword] === undefined) { - Misc.errorReport("Unknown name: " + p_keyword + " File: " + info.file + " Line: " + info.line); - - return false; - } - - p_result[0] += NTIPAliasClassID[p_keyword]; + break; + case "name": + if (NTIPAliasClassID[p_keyword] === undefined) { + Misc.errorReport("Unknown name: " + p_keyword + " File: " + info.file + " Line: " + info.line); + + return false; + } + + p_result[0] += NTIPAliasClassID[p_keyword]; - break; - case "class": - if (NTIPAliasClass[p_keyword] === undefined) { - Misc.errorReport("Unknown class: " + p_keyword + " File: " + info.file + " Line: " + info.line); + break; + case "class": + if (NTIPAliasClass[p_keyword] === undefined) { + Misc.errorReport("Unknown class: " + p_keyword + " File: " + info.file + " Line: " + info.line); - return false; - } + return false; + } - p_result[0] += NTIPAliasClass[p_keyword]; + p_result[0] += NTIPAliasClass[p_keyword]; - break; - case "quality": - if (NTIPAliasQuality[p_keyword] === undefined) { - Misc.errorReport("Unknown quality: " + p_keyword + " File: " + info.file + " Line: " + info.line); + break; + case "quality": + if (NTIPAliasQuality[p_keyword] === undefined) { + Misc.errorReport("Unknown quality: " + p_keyword + " File: " + info.file + " Line: " + info.line); - return false; - } - - p_result[0] += NTIPAliasQuality[p_keyword]; - - break; - case "flag": - if (NTIPAliasFlag[p_keyword] === undefined) { - Misc.errorReport("Unknown flag: " + p_keyword + " File: " + info.file + " Line: " + info.line); - - return false; - } + return false; + } + + p_result[0] += NTIPAliasQuality[p_keyword]; + + break; + case "flag": + if (NTIPAliasFlag[p_keyword] === undefined) { + Misc.errorReport("Unknown flag: " + p_keyword + " File: " + info.file + " Line: " + info.line); + + return false; + } - p_result[0] += NTIPAliasFlag[p_keyword] + ")"; - - break; - case "prefix": - case "suffix": - p_result[0] += "\"" + p_keyword + "\")"; - - break; - } - } else { - if (property === "flag" || property === "prefix" || property === "suffix") { - p_result[0] += p_keyword + ")"; - } else { - p_result[0] += p_keyword; - } - } - - p_result[0] += p_section[i].substring(p_end); - } - } else { - p_result[0] = ""; - } - - if (p_result[1] && p_result[1].length > 4) { - p_section = p_result[1].split("["); - p_result[1] = p_section[0]; - - for (i = 1; i < p_section.length; i += 1) { - p_end = p_section[i].indexOf("]"); - p_keyword = p_section[i].substring(0, p_end); - - if (isNaN(p_keyword)) { - if (NTIPAliasStat[p_keyword] === undefined) { - Misc.errorReport("Unknown stat: " + p_keyword + " File: " + info.file + " Line: " + info.line); - - return false; - } - - p_result[1] += "item.getStatEx(" + NTIPAliasStat[p_keyword] + ")"; - } else { - p_result[1] += "item.getStatEx(" + p_keyword + ")"; - } - - p_result[1] += p_section[i].substring(p_end + 1); - } - } else { - p_result[1] = ""; - } - - if (p_result[2] && p_result[2].length > 0) { - p_section = p_result[2].split("["); - p_result[2] = {}; - - for (i = 1; i < p_section.length; i += 1) { - p_end = p_section[i].indexOf("]"); - p_keyword = p_section[i].substring(0, p_end); - - let keyword = p_keyword.toLowerCase(); - - switch (keyword) { - // Charm equip specific - case "invoquantity": - let quantity = Number(p_section[i].split("==")[1].match(/\d+/g)); - - if (!isNaN(quantity)) { - p_result[2].InvoQuantity = quantity; - } - - break; - case "finalcharm": - let check = Boolean(p_section[i].split("==")[1].match(/(\b(?!split\b)[^ $]+\b)/g)); - - if (!isNaN(check)) { - p_result[2].FinalCharm = check; - } - - break; - case "maxquantity": - value = Number(p_section[i].split("==")[1].match(/\d+/g)); - - if (!isNaN(value)) { - p_result[2].MaxQuantity = value; - } - - break; - case "tier": - case "secondarytier": - case "charmtier": - case "merctier": - try { - p_result[2][keyword.charAt(0).toUpperCase() + keyword.slice(1)] = (new Function("return function(item) { return " + p_section[i].split("==")[1] + ";}")).call(null); // generate function out of - } catch (e) { - Misc.errorReport("ÿc1Pickit Tier (" + keyword + ") error! Line # ÿc2" + info.line + " ÿc1Entry: ÿc0" + info.string + " (" + info.file + ") Error message: " + e.message); - } - - break; - default: - Misc.errorReport("Unknown 3rd part keyword: " + p_keyword.toLowerCase() + " File: " + info.file + " Line: " + info.line); - - return false; - } - } - } - - // Compile the line, to 1) remove the eval lines, and 2) increase the speed - for (let i = 0; i < 2; i++) { - if (p_result[i].length) { - try { - p_result[i] = (new Function("return function(item) { return " + p_result[i] + ";}")).call(null); // generate function out of - } catch (e) { - Misc.errorReport("ÿc1Pickit error! Line # ÿc2" + info.line + " ÿc1Entry: ÿc0" + info.string + " (" + info.file + ") Error message: " + e.message); - - return null ; // failed load this line so return false - } - } else { - p_result[i] = undefined; - } - - } - - return p_result; + p_result[0] += NTIPAliasFlag[p_keyword] + ")"; + + break; + case "prefix": + case "suffix": + p_result[0] += "\"" + p_keyword + "\")"; + + break; + } + } else { + if (property === "flag" || property === "prefix" || property === "suffix") { + p_result[0] += p_keyword + ")"; + } else { + p_result[0] += p_keyword; + } + } + + p_result[0] += p_section[i].substring(p_end); + } + } else { + p_result[0] = ""; + } + + if (p_result[1] && p_result[1].length > 4) { + p_section = p_result[1].split("["); + p_result[1] = p_section[0]; + + for (i = 1; i < p_section.length; i += 1) { + p_end = p_section[i].indexOf("]"); + p_keyword = p_section[i].substring(0, p_end); + + if (isNaN(p_keyword)) { + if (NTIPAliasStat[p_keyword] === undefined) { + Misc.errorReport("Unknown stat: " + p_keyword + " File: " + info.file + " Line: " + info.line); + + return false; + } + + p_result[1] += "item.getStatEx(" + NTIPAliasStat[p_keyword] + ")"; + } else { + p_result[1] += "item.getStatEx(" + p_keyword + ")"; + } + + p_result[1] += p_section[i].substring(p_end + 1); + } + } else { + p_result[1] = ""; + } + + if (p_result[2] && p_result[2].length > 0) { + p_section = p_result[2].split("["); + p_result[2] = {}; + + for (i = 1; i < p_section.length; i += 1) { + p_end = p_section[i].indexOf("]"); + p_keyword = p_section[i].substring(0, p_end); + + let keyword = p_keyword.toLowerCase(); + + switch (keyword) { + // Charm equip specific + case "invoquantity": + let quantity = Number(p_section[i].split("==")[1].match(/\d+/g)); + + if (!isNaN(quantity)) { + p_result[2].InvoQuantity = quantity; + } + + break; + case "finalcharm": + let check = Boolean(p_section[i].split("==")[1].match(/(\b(?!split\b)[^ $]+\b)/g)); + + if (!isNaN(check)) { + p_result[2].FinalCharm = check; + } + + break; + case "maxquantity": + value = Number(p_section[i].split("==")[1].match(/\d+/g)); + + if (!isNaN(value)) { + p_result[2].MaxQuantity = value; + } + + break; + case "tier": + case "secondarytier": + case "charmtier": + case "merctier": + try { + p_result[2][keyword.charAt(0).toUpperCase() + keyword.slice(1)] = (new Function("return function(item) { return " + p_section[i].split("==")[1] + ";}")).call(null); // generate function out of + } catch (e) { + Misc.errorReport("ÿc1Pickit Tier (" + keyword + ") error! Line # ÿc2" + info.line + " ÿc1Entry: ÿc0" + info.string + " (" + info.file + ") Error message: " + e.message); + } + + break; + default: + Misc.errorReport("Unknown 3rd part keyword: " + p_keyword.toLowerCase() + " File: " + info.file + " Line: " + info.line); + + return false; + } + } + } + + // Compile the line, to 1) remove the eval lines, and 2) increase the speed + for (let i = 0; i < 2; i++) { + if (p_result[i].length) { + try { + p_result[i] = (new Function("return function(item) { return " + p_result[i] + ";}")).call(null); // generate function out of + } catch (e) { + Misc.errorReport("ÿc1Pickit error! Line # ÿc2" + info.line + " ÿc1Entry: ÿc0" + info.string + " (" + info.file + ") Error message: " + e.message); + + return null ; // failed load this line so return false + } + } else { + p_result[i] = undefined; + } + + } + + return p_result; }; diff --git a/libs/SoloPlay/Functions/PatherOverrides.js b/libs/SoloPlay/Functions/PatherOverrides.js index 2ec5632a..c236dfb5 100644 --- a/libs/SoloPlay/Functions/PatherOverrides.js +++ b/libs/SoloPlay/Functions/PatherOverrides.js @@ -11,9 +11,9 @@ includeIfNotIncluded("core/Pather.js"); Developer.debugging.pathing && (PathDebug.enableHooks = true); Pather.inAnnoyingArea = function (currArea, includeArcane = false) { - const areas = [sdk.areas.MaggotLairLvl1, sdk.areas.MaggotLairLvl2, sdk.areas.MaggotLairLvl3]; - includeArcane && areas.push(sdk.areas.ArcaneSanctuary); - return areas.includes(currArea); + const areas = [sdk.areas.MaggotLairLvl1, sdk.areas.MaggotLairLvl2, sdk.areas.MaggotLairLvl3]; + includeArcane && areas.push(sdk.areas.ArcaneSanctuary); + return areas.includes(currArea); }; /** @@ -29,103 +29,103 @@ Pather.inAnnoyingArea = function (currArea, includeArcane = false) { * - use effort level calculations to control clearing */ NodeAction.killMonsters = function (arg = {}) { - if (Attack.stopClear || (arg.hasOwnProperty("allowClearing") && !arg.allowClearing)) return; - - const myArea = me.area; - // I don't think this is even needed anymore, pretty sure I fixed wall hugging. todo - check it - const pallyAnnoyingAreas = [ - sdk.areas.DenofEvil, sdk.areas.CaveLvl1, sdk.areas.UndergroundPassageLvl1, sdk.areas.HoleLvl1, sdk.areas.PitLvl1, sdk.areas.CaveLvl2, - sdk.areas.UndergroundPassageLvl2, sdk.areas.PitLvl2, sdk.areas.HoleLvl2, sdk.areas.DisusedFane, sdk.areas.RuinedTemple, - sdk.areas.ForgottenReliquary, sdk.areas.ForgottenTemple, sdk.areas.RuinedFane, sdk.areas.DisusedReliquary - ]; - const summonerAreas = [ - sdk.areas.DenofEvil, sdk.areas.ColdPlains, sdk.areas.StonyField, sdk.areas.Tristram, sdk.areas.DarkWood, sdk.areas.BlackMarsh, - sdk.areas.OuterCloister, sdk.areas.Barracks, sdk.areas.Cathedral, sdk.areas.CatacombsLvl4, sdk.areas.HallsoftheDeadLvl1, sdk.areas.HallsoftheDeadLvl2, - sdk.areas.HallsoftheDeadLvl3, sdk.areas.ValleyofSnakes, sdk.areas.ClawViperTempleLvl1, sdk.areas.TalRashasTomb1, sdk.areas.TalRashasTomb2, - sdk.areas.TalRashasTomb3, sdk.areas.TalRashasTomb4, sdk.areas.TalRashasTomb5, sdk.areas.TalRashasTomb6, sdk.areas.TalRashasTomb7 - ]; - // sanityCheck from isid0re - added paladin specific areas - theBGuy - a mess.. sigh - if (Pather.inAnnoyingArea(myArea, true) || (me.paladin && pallyAnnoyingAreas.includes(myArea))) { - arg.range = 7; - } - - /** - * @todo: - * - we don't need this if we have a lightning chain based skill, e.g light sorc, light zon - * - better monster sorting. If we are low level priortize killing easy targets like zombies/quill rats while ignoring fallens unless they are in our path - * - ignore dolls when walking unless absolutely necessary because we are blocked - */ - if (!arg.canTele && arg.clearPath !== false) { - let monList = []; - if (me.inArea(sdk.areas.BloodMoor)) { - monList = getUnits(sdk.unittype.Monster) - .filter(mon => mon.attackable && mon.distance < 30 && !mon.isFallen - && !checkCollision(me, mon, (sdk.collision.BlockWall | sdk.collision.LineOfSight | sdk.collision.Ranged))); - monList.length > 0 && Attack.clearList(monList); - } - - if (summonerAreas.includes(myArea)) { - monList = getUnits(sdk.unittype.Monster) - .filter(mon => mon.attackable && mon.distance < 30 && (mon.isUnraveler || mon.isShaman) - && !checkCollision(me, mon, (sdk.collision.BlockWall | sdk.collision.LineOfSight | sdk.collision.Ranged))); - monList.length > 0 && Attack.clearList(monList); - } - - if ([sdk.areas.StonyField, sdk.areas.BlackMarsh, sdk.areas.FarOasis].includes(me.area)) { - // monster nest's are good exp - monList = getUnits(sdk.unittype.Monster).filter(mon => mon.attackable && mon.distance < 35 && mon.isMonsterNest); - monList.length > 0 && Attack.clearList(monList); - } - } - - if (arg.clearPath !== false) { - Attack.clear(arg.range, arg.specType); - } + if (Attack.stopClear || (arg.hasOwnProperty("allowClearing") && !arg.allowClearing)) return; + + const myArea = me.area; + // I don't think this is even needed anymore, pretty sure I fixed wall hugging. todo - check it + const pallyAnnoyingAreas = [ + sdk.areas.DenofEvil, sdk.areas.CaveLvl1, sdk.areas.UndergroundPassageLvl1, sdk.areas.HoleLvl1, sdk.areas.PitLvl1, sdk.areas.CaveLvl2, + sdk.areas.UndergroundPassageLvl2, sdk.areas.PitLvl2, sdk.areas.HoleLvl2, sdk.areas.DisusedFane, sdk.areas.RuinedTemple, + sdk.areas.ForgottenReliquary, sdk.areas.ForgottenTemple, sdk.areas.RuinedFane, sdk.areas.DisusedReliquary + ]; + const summonerAreas = [ + sdk.areas.DenofEvil, sdk.areas.ColdPlains, sdk.areas.StonyField, sdk.areas.Tristram, sdk.areas.DarkWood, sdk.areas.BlackMarsh, + sdk.areas.OuterCloister, sdk.areas.Barracks, sdk.areas.Cathedral, sdk.areas.CatacombsLvl4, sdk.areas.HallsoftheDeadLvl1, sdk.areas.HallsoftheDeadLvl2, + sdk.areas.HallsoftheDeadLvl3, sdk.areas.ValleyofSnakes, sdk.areas.ClawViperTempleLvl1, sdk.areas.TalRashasTomb1, sdk.areas.TalRashasTomb2, + sdk.areas.TalRashasTomb3, sdk.areas.TalRashasTomb4, sdk.areas.TalRashasTomb5, sdk.areas.TalRashasTomb6, sdk.areas.TalRashasTomb7 + ]; + // sanityCheck from isid0re - added paladin specific areas - theBGuy - a mess.. sigh + if (Pather.inAnnoyingArea(myArea, true) || (me.paladin && pallyAnnoyingAreas.includes(myArea))) { + arg.range = 7; + } + + /** + * @todo: + * - we don't need this if we have a lightning chain based skill, e.g light sorc, light zon + * - better monster sorting. If we are low level priortize killing easy targets like zombies/quill rats while ignoring fallens unless they are in our path + * - ignore dolls when walking unless absolutely necessary because we are blocked + */ + if (!arg.canTele && arg.clearPath !== false) { + let monList = []; + if (me.inArea(sdk.areas.BloodMoor)) { + monList = getUnits(sdk.unittype.Monster) + .filter(mon => mon.attackable && mon.distance < 30 && !mon.isFallen + && !checkCollision(me, mon, (sdk.collision.BlockWall | sdk.collision.LineOfSight | sdk.collision.Ranged))); + monList.length > 0 && Attack.clearList(monList); + } + + if (summonerAreas.includes(myArea)) { + monList = getUnits(sdk.unittype.Monster) + .filter(mon => mon.attackable && mon.distance < 30 && (mon.isUnraveler || mon.isShaman) + && !checkCollision(me, mon, (sdk.collision.BlockWall | sdk.collision.LineOfSight | sdk.collision.Ranged))); + monList.length > 0 && Attack.clearList(monList); + } + + if ([sdk.areas.StonyField, sdk.areas.BlackMarsh, sdk.areas.FarOasis].includes(me.area)) { + // monster nest's are good exp + monList = getUnits(sdk.unittype.Monster).filter(mon => mon.attackable && mon.distance < 35 && mon.isMonsterNest); + monList.length > 0 && Attack.clearList(monList); + } + } + + if (arg.clearPath !== false) { + Attack.clear(arg.range, arg.specType); + } }; NodeAction.popChests = function () { - const range = Pather.useTeleport() ? 25 : 15; - Config.OpenChests.Enabled && Misc.openChests(range); - Misc.useWell(range); + const range = Pather.useTeleport() ? 25 : 15; + Config.OpenChests.Enabled && Misc.openChests(range); + Misc.useWell(range); }; NodeAction.pickItems = function (arg = {}) { - if (arg.hasOwnProperty("allowPicking") && !arg.allowPicking) return; - - let item = Game.getItem(); - - if (item) { - const maxDist = Skill.haveTK ? 15 : 5; - const regPickRange = Pather.canTeleport() ? Config.PickRange : 8; - const maxRange = Math.max(maxDist, regPickRange); - const totalList = [].concat(Pickit.essentialList, Pickit.pickList); - const filterJunk = (item) => !!item && item.onGroundOrDropping; - - do { - if (item.onGroundOrDropping) { - const itemDist = getDistance(me, item); - if (itemDist > maxRange) continue; - if (totalList.some(el => el.gid === item.gid)) continue; - if (Pickit.essentials.includes(item.itemType)) { - if (itemDist <= maxDist && (item.itemType !== sdk.items.type.Gold || itemDist < 5) - && Pickit.checkItem(item).result && Pickit.canPick(item) && Pickit.canFit(item)) { - Pickit.essentialList.push(copyUnit(item)); - } - } else if (itemDist <= regPickRange && item.itemType === sdk.items.type.Key) { - if (Pickit.canPick(item) && Pickit.checkItem(item).result) { - Pickit.pickList.push(copyUnit(item)); - } - } else if (itemDist <= regPickRange && Pickit.checkItem(item).result) { - Pickit.pickList.push(copyUnit(item)); - } - } - } while (item.getNext()); - - Pickit.essentialList.length > 0 && (Pickit.essentialList = Pickit.essentialList.filter(filterJunk)); - Pickit.pickList.length > 0 && (Pickit.pickList = Pickit.pickList.filter(filterJunk)); - Pickit.essentialList.length > 0 && Pickit.essessntialsPick(false, false); - Pickit.pickList.length > 0 && Pickit.pickItems(regPickRange); - } + if (arg.hasOwnProperty("allowPicking") && !arg.allowPicking) return; + + let item = Game.getItem(); + + if (item) { + const maxDist = Skill.haveTK ? 15 : 5; + const regPickRange = Pather.canTeleport() ? Config.PickRange : 8; + const maxRange = Math.max(maxDist, regPickRange); + const totalList = [].concat(Pickit.essentialList, Pickit.pickList); + const filterJunk = (item) => !!item && item.onGroundOrDropping; + + do { + if (item.onGroundOrDropping) { + const itemDist = getDistance(me, item); + if (itemDist > maxRange) continue; + if (totalList.some(el => el.gid === item.gid)) continue; + if (Pickit.essentials.includes(item.itemType)) { + if (itemDist <= maxDist && (item.itemType !== sdk.items.type.Gold || itemDist < 5) + && Pickit.checkItem(item).result && Pickit.canPick(item) && Pickit.canFit(item)) { + Pickit.essentialList.push(copyUnit(item)); + } + } else if (itemDist <= regPickRange && item.itemType === sdk.items.type.Key) { + if (Pickit.canPick(item) && Pickit.checkItem(item).result) { + Pickit.pickList.push(copyUnit(item)); + } + } else if (itemDist <= regPickRange && Pickit.checkItem(item).result) { + Pickit.pickList.push(copyUnit(item)); + } + } + } while (item.getNext()); + + Pickit.essentialList.length > 0 && (Pickit.essentialList = Pickit.essentialList.filter(filterJunk)); + Pickit.pickList.length > 0 && (Pickit.pickList = Pickit.pickList.filter(filterJunk)); + Pickit.essentialList.length > 0 && Pickit.essessntialsPick(false, false); + Pickit.pickList.length > 0 && Pickit.pickItems(regPickRange); + } }; // todo - fast shrineing, if we are right next to a shrine then grab it even with mobs around @@ -135,211 +135,211 @@ Pather.forceWalk = false; Pather.forceRun = false; { - let coords = function () { - if (Array.isArray(this) && this.length > 1) return [this[0], this[1]]; - - if (typeof this.x !== "undefined" && typeof this.y !== "undefined") { - return this instanceof PresetUnit && [this.roomx * 5 + this.x, this.roomy * 5 + this.y] || [this.x, this.y]; - } - - return [undefined, undefined]; - }; - - Object.defineProperty(Object.prototype, "mobCount", { - writable: true, - enumerable: false, - configurable: true, - value: function (givenSettings = {}) { - let [x, y] = coords.apply(this); - const settings = Object.assign({}, { - range: 5, - coll: (sdk.collision.BlockWall | sdk.collision.ClosedDoor | sdk.collision.LineOfSight | sdk.collision.BlockMissile), - type: 0, - ignoreClassids: [], - }, givenSettings); - return getUnits(sdk.unittype.Monster) - .filter(function (mon) { - return mon.attackable && getDistance(x, y, mon.x, mon.y) < settings.range - && (!settings.type || (settings.type & mon.spectype)) - && (settings.ignoreClassids.indexOf(mon.classid) === -1) - && !CollMap.checkColl({ x: x, y: y }, mon, settings.coll, 1); - }).length; - } - }); + let coords = function () { + if (Array.isArray(this) && this.length > 1) return [this[0], this[1]]; + + if (typeof this.x !== "undefined" && typeof this.y !== "undefined") { + return this instanceof PresetUnit && [this.roomx * 5 + this.x, this.roomy * 5 + this.y] || [this.x, this.y]; + } + + return [undefined, undefined]; + }; + + Object.defineProperty(Object.prototype, "mobCount", { + writable: true, + enumerable: false, + configurable: true, + value: function (givenSettings = {}) { + let [x, y] = coords.apply(this); + const settings = Object.assign({}, { + range: 5, + coll: (sdk.collision.BlockWall | sdk.collision.ClosedDoor | sdk.collision.LineOfSight | sdk.collision.BlockMissile), + type: 0, + ignoreClassids: [], + }, givenSettings); + return getUnits(sdk.unittype.Monster) + .filter(function (mon) { + return mon.attackable && getDistance(x, y, mon.x, mon.y) < settings.range + && (!settings.type || (settings.type & mon.spectype)) + && (settings.ignoreClassids.indexOf(mon.classid) === -1) + && !CollMap.checkColl({ x: x, y: y }, mon, settings.coll, 1); + }).length; + } + }); } Pather.checkForTeleCharges = function () { - this.haveTeleCharges = Attack.getItemCharges(sdk.skills.Teleport); + this.haveTeleCharges = Attack.getItemCharges(sdk.skills.Teleport); }; Pather.canUseTeleCharges = function () { - if (me.classic || me.inTown || me.shapeshifted) return false; - // Charges are costly so make sure we have enough gold to handle repairs unless we are in maggot lair since thats a pita and worth the gold spent - if (me.gold < 500000 && !Pather.inAnnoyingArea(me.area)) return false; + if (me.classic || me.inTown || me.shapeshifted) return false; + // Charges are costly so make sure we have enough gold to handle repairs unless we are in maggot lair since thats a pita and worth the gold spent + if (me.gold < 500000 && !Pather.inAnnoyingArea(me.area)) return false; - return this.haveTeleCharges; + return this.haveTeleCharges; }; Pather.teleportTo = function (x, y, maxRange = 5) { - Developer.debugging.pathing && console.log("Mob Count at next node: " + [x, y].mobCount()); - - for (let i = 0; i < 3; i += 1) { - Skill.setSkill(sdk.skills.Teleport, sdk.skills.hand.Right) && Packet.castSkill(sdk.skills.hand.Right, x, y); - let tick = getTickCount(); - let pingDelay = i === 0 ? 250 : me.getPingDelay(); - - while (getTickCount() - tick < Math.max(500, pingDelay * 2 + 200)) { - if (getDistance(me.x, me.y, x, y) < maxRange) { - return true; - } - - delay(10); - } - } - - return false; + Developer.debugging.pathing && console.log("Mob Count at next node: " + [x, y].mobCount()); + + for (let i = 0; i < 3; i += 1) { + Skill.setSkill(sdk.skills.Teleport, sdk.skills.hand.Right) && Packet.castSkill(sdk.skills.hand.Right, x, y); + let tick = getTickCount(); + let pingDelay = i === 0 ? 250 : me.getPingDelay(); + + while (getTickCount() - tick < Math.max(500, pingDelay * 2 + 200)) { + if (getDistance(me.x, me.y, x, y) < maxRange) { + return true; + } + + delay(10); + } + } + + return false; }; Pather.teleUsingCharges = function (x, y, maxRange = 5) { - let orgSlot = me.weaponswitch; + let orgSlot = me.weaponswitch; - for (let i = 0; i < 3; i++) { - me.castChargedSkill(sdk.skills.Teleport, x, y); - let tick = getTickCount(); + for (let i = 0; i < 3; i++) { + me.castChargedSkill(sdk.skills.Teleport, x, y); + let tick = getTickCount(); - while (getTickCount() - tick < Math.max(500, me.ping * 2 + 200)) { - if (getDistance(me.x, me.y, x, y) < maxRange) { - me.weaponswitch !== orgSlot && me.switchWeapons(orgSlot); - return true; - } + while (getTickCount() - tick < Math.max(500, me.ping * 2 + 200)) { + if (getDistance(me.x, me.y, x, y) < maxRange) { + me.weaponswitch !== orgSlot && me.switchWeapons(orgSlot); + return true; + } - delay(10); - } - } + delay(10); + } + } - if (me.gold > me.getRepairCost() * 3 && me.canTpToTown()) { - console.debug("Tele-Charge repair"); - Town.visitTown(true); - } else { - this.haveTeleCharges = false; - } + if (me.gold > me.getRepairCost() * 3 && me.canTpToTown()) { + console.debug("Tele-Charge repair"); + Town.visitTown(true); + } else { + this.haveTeleCharges = false; + } - me.weaponswitch !== orgSlot && me.switchWeapons(orgSlot); + me.weaponswitch !== orgSlot && me.switchWeapons(orgSlot); - return false; + return false; }; Pather.checkWP = function (area = 0, keepMenuOpen = false) { - while (!me.gameReady) { - delay(40); - } - - // only do this if we haven't initialzed our wp data - if (!getWaypoint(Pather.wpAreas.indexOf(area)) && !Pather.initialized) { - me.inTown && !getUIFlag(sdk.uiflags.Waypoint) && Town.move("waypoint"); - - for (let i = 0; i < 15; i++) { - let wp = Game.getObject("waypoint"); - let useTK = (Skill.useTK(wp) && i < 5); - let pingDelay = me.getPingDelay(); - - if (wp && wp.area === me.area) { - if (useTK) { - wp.distance > 21 && Pather.moveNearUnit(wp, 20); - Skill.cast(sdk.skills.Telekinesis, sdk.skills.hand.Right, wp); - } else { - wp.distance > 7 && this.moveToUnit(wp); - Misc.click(0, 0, wp); - } - - let tick = getTickCount(); - - while (getTickCount() - tick < Math.max(Math.round((i + 1) * 1000 / (i / 5 + 1)), (1 + pingDelay * 2))) { - if (getUIFlag(sdk.uiflags.Waypoint)) { - delay(500 + pingDelay); - break; - } - - delay(50 + pingDelay); - } - } else { - me.inTown && Town.move("waypoint"); - } - - if (getUIFlag(sdk.uiflags.Waypoint)) { - !keepMenuOpen && me.cancel(); - Pather.initialized = true; - break; - } - } - // go ahead and close out of wp menu if we don't have the wp - !getWaypoint(Pather.wpAreas.indexOf(area)) && getUIFlag(sdk.uiflags.Waypoint) && me.cancel(); - } - - return getWaypoint(Pather.wpAreas.indexOf(area)); + while (!me.gameReady) { + delay(40); + } + + // only do this if we haven't initialzed our wp data + if (!getWaypoint(Pather.wpAreas.indexOf(area)) && !Pather.initialized) { + me.inTown && !getUIFlag(sdk.uiflags.Waypoint) && Town.move("waypoint"); + + for (let i = 0; i < 15; i++) { + let wp = Game.getObject("waypoint"); + let useTK = (Skill.useTK(wp) && i < 5); + let pingDelay = me.getPingDelay(); + + if (wp && wp.area === me.area) { + if (useTK) { + wp.distance > 21 && Pather.moveNearUnit(wp, 20); + Skill.cast(sdk.skills.Telekinesis, sdk.skills.hand.Right, wp); + } else { + wp.distance > 7 && this.moveToUnit(wp); + Misc.click(0, 0, wp); + } + + let tick = getTickCount(); + + while (getTickCount() - tick < Math.max(Math.round((i + 1) * 1000 / (i / 5 + 1)), (1 + pingDelay * 2))) { + if (getUIFlag(sdk.uiflags.Waypoint)) { + delay(500 + pingDelay); + break; + } + + delay(50 + pingDelay); + } + } else { + me.inTown && Town.move("waypoint"); + } + + if (getUIFlag(sdk.uiflags.Waypoint)) { + !keepMenuOpen && me.cancel(); + Pather.initialized = true; + break; + } + } + // go ahead and close out of wp menu if we don't have the wp + !getWaypoint(Pather.wpAreas.indexOf(area)) && getUIFlag(sdk.uiflags.Waypoint) && me.cancel(); + } + + return getWaypoint(Pather.wpAreas.indexOf(area)); }; Pather.changeAct = function () { - let act = me.act + 1; - let [npc, loc] = (() => { - switch (act) { - case 2: - return ["Warriv", sdk.areas.LutGholein]; - case 3: - return ["Meshif", sdk.areas.KurastDocktown]; - case 5: - return ["Tyrael", sdk.areas.Harrogath]; - default: - return ["", 0]; - } - })(); - if (!npc) return false; - - !me.inTown && Town.goToTown(); - let npcUnit = Town.npcInteract(npc); - let timeout = getTickCount() + 3000; - let pingDelay = me.getPingDelay(); - - if (!npcUnit) { - while (!npcUnit && timeout < getTickCount()) { - Town.move(NPC[npc]); - Packet.flash(me.gid, pingDelay); - delay(pingDelay * 2 + 100); - npcUnit = Game.getNPC(npc); - } - } - - if (npcUnit) { - for (let i = 0; i < 5; i++) { - sendPacket(1, 56, 4, 0, 4, npcUnit.gid, 4, loc); - delay(500 + pingDelay); - - if (me.act === act) { - break; - } - } - } else { - myPrint("Failed to move to " + npc); - } - - while (!me.gameReady) { - delay(100); - } - - return me.act === act; + let act = me.act + 1; + let [npc, loc] = (() => { + switch (act) { + case 2: + return ["Warriv", sdk.areas.LutGholein]; + case 3: + return ["Meshif", sdk.areas.KurastDocktown]; + case 5: + return ["Tyrael", sdk.areas.Harrogath]; + default: + return ["", 0]; + } + })(); + if (!npc) return false; + + !me.inTown && Town.goToTown(); + let npcUnit = Town.npcInteract(npc); + let timeout = getTickCount() + 3000; + let pingDelay = me.getPingDelay(); + + if (!npcUnit) { + while (!npcUnit && timeout < getTickCount()) { + Town.move(NPC[npc]); + Packet.flash(me.gid, pingDelay); + delay(pingDelay * 2 + 100); + npcUnit = Game.getNPC(npc); + } + } + + if (npcUnit) { + for (let i = 0; i < 5; i++) { + sendPacket(1, 56, 4, 0, 4, npcUnit.gid, 4, loc); + delay(500 + pingDelay); + + if (me.act === act) { + break; + } + } + } else { + myPrint("Failed to move to " + npc); + } + + while (!me.gameReady) { + delay(100); + } + + return me.act === act; }; Pather.clearUIFlags = function () { - while (!me.gameReady) { - delay(3); - } - - for (let i = 0; i < Pather.cancelFlags.length; i++) { - if (getUIFlag(Pather.cancelFlags[i]) && me.cancel()) { - delay(250); - i = 0; // Reset - } - } + while (!me.gameReady) { + delay(3); + } + + for (let i = 0; i < Pather.cancelFlags.length; i++) { + if (getUIFlag(Pather.cancelFlags[i]) && me.cancel()) { + delay(250); + i = 0; // Reset + } + } }; /** @@ -354,537 +354,537 @@ Pather.currentWalkingPath = []; * @returns {boolean} */ Pather.move = function (target, givenSettings = {}) { - // Abort if dead - if (me.dead) return false; - /** - * assign settings - * @type {pathSettings} - */ - const settings = Object.assign({}, { - clearSettings: { - }, - allowNodeActions: true, - allowTeleport: true, - allowClearing: true, - allowTown: true, - allowPicking: true, - minDist: 3, - retry: 5, - pop: false, - returnSpotOnError: true, - callback: null, - }, givenSettings); - // assign clear settings becasue object.assign was removing the default properties of settings.clearSettings - const clearSettings = Object.assign({ - canTele: false, - clearPath: false, - range: typeof Config.ClearPath.Range === "number" ? Config.ClearPath.Range : 10, - specType: typeof Config.ClearPath.Spectype === "number" ? Config.ClearPath.Spectype : 0, - sort: Attack.sortMonsters, - }, settings.clearSettings); - // set settings.clearSettings equal to the now properly asssigned clearSettings - settings.clearSettings = clearSettings; - !settings.allowClearing && (settings.clearSettings.allowClearing = false); - !settings.allowPicking && (settings.clearSettings.allowPicking = false); - - (target instanceof PresetUnit) && (target = target.realCoords()); - - if (settings.minDist > 3) { - target = Pather.spotOnDistance(target, settings.minDist, { returnSpotOnError: settings.returnSpotOnError, reductionType: (me.inTown ? 0 : 2) }); - } - - let fail = 0; - let node = { x: target.x, y: target.y }; - const leaped = { - at: 0, - /** @type {PathNode} */ - from: { x: null, y: null } - }; - const whirled = { - at: 0, - /** @type {PathNode} */ - from: { x: null, y: null } - }; - const cleared = { - at: 0, - /** @type {PathNode} */ - where: { x: null, y: null } - }; - let [invalidCheck] = [false]; - - Pather.clearUIFlags(); - - if (typeof target.x !== "number" || typeof target.y !== "number") return false; - if (getDistance(me, target) < 2 && !CollMap.checkColl(me, target, Coords_1.Collision.BLOCK_MISSILE, 5)) return true; - - const useTeleport = settings.allowTeleport && (getDistance(me, target) > 15 || me.diff || me.act > 3) && Pather.useTeleport(); - settings.clearSettings.canTele = useTeleport; - const useChargedTele = settings.allowTeleport && Pather.canUseTeleCharges(); - const usingTele = (useTeleport || useChargedTele); - const tpMana = Skill.getManaCost(sdk.skills.Teleport); - const annoyingArea = Pather.inAnnoyingArea(me.area); - let path = getPath(me.area, target.x, target.y, me.x, me.y, usingTele ? 1 : 0, usingTele ? (annoyingArea ? 30 : Pather.teleDistance) : Pather.walkDistance); - if (!path) throw new Error("move: Failed to generate path."); - - // need to work on a better force clearing method but for now just have all walkers clear unless we specifically are forcing them not to (like while repositioning) - settings.allowClearing && !settings.clearSettings.clearPath && !useTeleport && (settings.clearSettings.clearPath = true); - - if (settings.retry <= 3 && target.distance > useTeleport ? 120 : 60) { - settings.retry = 10; - } - - // for now only do this for teleporters - if (useTeleport && !me.normal) { - /** @type {Array} */ - let areaImmunities = GameData.areaImmunities(me.area); - if (areaImmunities.length) { - let mySkElems = Config.AttackSkill.filter(sk => sk > 0).map(sk => Attack.getSkillElement(sk)); - // this area has monsters that are immune to our elements. This is a basic check for now - // a better way would probably be per list built to check the ratio of immunes to non? - if (mySkElems.length && mySkElems.every(elem => areaImmunities.includes(elem))) { - settings.clearSettings.clearPath = false; - } - } else if (AreaData[me.area].hasMonsterType(sdk.monsters.type.UndeadFetish)) { - settings.clearSettings.clearPath = false; - } - } - - path.reverse(); - settings.pop && path.pop(); - PathDebug.drawPath(path); - useTeleport && Config.TeleSwitch && path.length > 5 && me.switchWeapons(Attack.getPrimarySlot() ^ 1); - - while (path.length > 0) { - // Abort if dead - if (me.dead) return false; - // main path - Pather.recursion && (Pather.currentWalkingPath = path); - Pather.clearUIFlags(); - - node = path.shift(); - - if (typeof settings.callback === "function" && settings.callback()) { - // console.debug("Callback function passed. Ending path."); - useTeleport && Config.TeleSwitch && me.switchWeapons(Attack.getPrimarySlot() ^ 1); - PathDebug.removeHooks(); - return true; - } - - if (getDistance(me, node) > 2) { - // Make life in Maggot Lair easier - fail >= 3 && fail % 3 === 0 && !Attack.validSpot(node.x, node.y) && (invalidCheck = true); - // Make life in Maggot Lair easier - should this include arcane as well? - if (annoyingArea || invalidCheck) { - let adjustedNode = Pather.getNearestWalkable(node.x, node.y, 15, 3, sdk.collision.BlockWalk); - - if (adjustedNode) { - [node.x, node.y] = [adjustedNode[0], adjustedNode[1]]; - invalidCheck && (invalidCheck = false); - } - - annoyingArea && ([settings.clearSettings.clearPath, settings.clearSettings.range] = [true, 5]); - settings.retry <= 3 && !useTeleport && (settings.retry = 15); - } - - if (useTeleport && tpMana <= me.mp - ? Pather.teleportTo(node.x, node.y) - : useChargedTele && (getDistance(me, node) >= 15 || me.inArea(sdk.areas.ThroneofDestruction)) - ? Pather.teleUsingCharges(node.x, node.y) - : Pather.walkTo(node.x, node.y, (fail > 0 || me.inTown) ? 2 : 4)) { - if (settings.allowNodeActions && !me.inTown) { - if (Pather.recursion) { - try { - Pather.recursion = false; - /** - * @todo We need to pass our path in so we can fix the recursion issues of running forward on our path only to return to the old node and continue - * we should instead perform the actions in a way that moves us forward on our path ensuring we haven't skipped anything in the process as well - * for long paths maybe generate a coordinate list of shrines/chests and have action hooks for them - */ - NodeAction.go(settings.clearSettings); - // need to determine if its worth going back to our orignal node (items maybe?) - // vs our current proximity to our next node - // need to export our main path so other functions that cause us to move can see it - if (node.distance > 5) { - const lastNode = Pather.currentWalkingPath.last(); - // lets try and find the nearest node that brings us close to our goal - /** @type {PathNode} */ - let nearestNode = Pather.currentWalkingPath.length > 0 && Pather.currentWalkingPath - .filter(el => !!el && el.x !== node.x && el.y !== node.y) - .sort((a, b) => { - if (a.distance < b.distance && getDistance(a, lastNode) < getDistance(b, lastNode)) return -1; - if (a.distance > b.distance && getDistance(a, lastNode) > getDistance(b, lastNode)) return 1; - return a.distance - b.distance; - }) - .find(pNode => pNode.distance > 5); - - if (node.distance < 40) { - let goBack = false; - let foundNode = false; - // lets see if it's worth walking back to old node - Pickit.checkSpotForItems(node, true) && (goBack = true); - // @todo check shrines/chests in proximity to old node vs next node - // let otherObjects = getUnits(sdk.unittype.Object).filter(el => getDistance()); - if (goBack) { - // console.debug("Going back to old node. Distance: " + node.distance); - } else if (nearestNode && nearestNode.distance > 5 && node.distance > 5 && Math.percentDifference(node.distance, nearestNode.distance) > 5/* && 100 / node.distance * nearestNode.distance < 95 */) { - // console.debug("Moving to next node. Distance: " + nearestNode.distance); - let newIndex = path.findIndex(node => nearestNode.x === node.x && nearestNode.y === node.y); - if (newIndex > -1) { - // console.debug("Found new path index: " + newIndex + " of currentPathLen: " + path.length); - path = path.slice(newIndex); - node = path.shift(); - foundNode = true; - // console.debug("New path length: " + path.length); - } else { - // console.debug("Couldn't find new path index"); - } - } - - if (node.distance > 5) { - // !foundNode && console.debug("Path Recursion :: Returning to position " + node.x + "/" + node.y + " distance: " + node.distance); - Pather.move(node, settings); - } - } else { - Pather.move(node, settings); - } - } - } finally { - Pather.recursion = true; - } - } - } - } else { - if (!me.inTown) { - if (!useTeleport && settings.allowClearing) { - let tempRange = (annoyingArea ? 5 : 10); - // allowed to clear so lets see if any mobs are around us - if (me.checkForMobs({ range: tempRange, coll: sdk.collision.BlockWalk })) { - // there are at least some, but lets only continue to next iteration if we actually killed something - if (Attack.clear(tempRange, null, null, null, settings.allowPicking) === Attack.Result.SUCCESS) { - // console.debug("Cleared Node"); - continue; - } - } - } - if (!useTeleport && (Pather.openDoors(node.x, node.y) || Pather.kickBarrels(node.x, node.y))) { - continue; - } - - if (fail > 0 && (!useTeleport || tpMana > me.mp)) { - // if we are allowed to clear - if (settings.allowClearing) { - // Don't go berserk on longer paths - also check that there are even mobs blocking us - if (cleared.at === 0 || getTickCount() - cleared.at > Time.seconds(3) && cleared.where.distance > 5 && me.checkForMobs({ range: 10 })) { - // only set that we cleared if we actually killed at least 1 mob - if (Attack.clear(10, null, null, null, settings.allowPicking)) { - // console.debug("Cleared Node"); - cleared.at = getTickCount(); - [cleared.where.x, cleared.where.y] = [node.x, node.y]; - } - } - } - - // Leap can be helpful on long paths but make sure we don't spam it - if (Skill.canUse(sdk.skills.LeapAttack)) { - // we can use leapAttack, now lets see if we should - either haven't used it yet or it's been long enough since last time - if (leaped.at === 0 || getTickCount() - leaped.at > Time.seconds(3) || leaped.from.distance > 5 || me.checkForMobs({ range: 6 })) { - // alright now if we have actually casted it set the values so we know - if (Skill.cast(sdk.skills.LeapAttack, sdk.skills.hand.Right, node.x, node.y)) { - leaped.at = getTickCount(); - [leaped.from.x, leaped.from.y] = [node.x, node.y]; - } - } - } - - /** - * whirlwind can be useful as well, implement it. - * Things to consider: - * 1) Can we cast whirlwind on the node? Is it blocked by something other than monsters. - * 2) If we can't cast on that node, is there another node between us and it that would work? - */ - if (Skill.canUse(sdk.skills.Whirlwind)) { - // we can use whirlwind, now lets see if we should - either haven't used it yet or it's been long enough since last time - if (whirled.at === 0 || getTickCount() - whirled.at > Time.seconds(3) || whirled.from.distance > 5 || me.checkForMobs({ range: 6 })) { - // alright now if we have actually casted it set the values so we know - if (Skill.cast(sdk.skills.Whirlwind, sdk.skills.hand.Right, node.x, node.y)) { - whirled.at = getTickCount(); - [whirled.from.x, whirled.from.y] = [node.x, node.y]; - } - } - } - } - } - - // Reduce node distance in new path - path = getPath(me.area, target.x, target.y, me.x, me.y, useTeleport ? 1 : 0, useTeleport ? rand(25, 35) : rand(10, 15)); - if (!path) throw new Error("moveTo: Failed to generate path."); - - path.reverse(); - PathDebug.drawPath(path); - settings.pop && path.pop(); - - if (fail > 0) { - console.debug("move retry " + fail); - Packet.flash(me.gid); - - if (fail >= settings.retry) { - console.log("Failed move: Retry = " + settings.retry); - break; - } - } - if (fail > 100) { - // why? - console.debug(settings); - throw new Error("Retry limit excessivly exceeded"); - } - fail++; - } - } - - /** - * @todo handle passing in a callback function - */ - - delay(5); - } - - useTeleport && Config.TeleSwitch && me.switchWeapons(Attack.getPrimarySlot() ^ 1); - PathDebug.removeHooks(); - - return getDistance(me, node.x, node.y) < 5; + // Abort if dead + if (me.dead) return false; + /** + * assign settings + * @type {pathSettings} + */ + const settings = Object.assign({}, { + clearSettings: { + }, + allowNodeActions: true, + allowTeleport: true, + allowClearing: true, + allowTown: true, + allowPicking: true, + minDist: 3, + retry: 5, + pop: false, + returnSpotOnError: true, + callback: null, + }, givenSettings); + // assign clear settings becasue object.assign was removing the default properties of settings.clearSettings + const clearSettings = Object.assign({ + canTele: false, + clearPath: false, + range: typeof Config.ClearPath.Range === "number" ? Config.ClearPath.Range : 10, + specType: typeof Config.ClearPath.Spectype === "number" ? Config.ClearPath.Spectype : 0, + sort: Attack.sortMonsters, + }, settings.clearSettings); + // set settings.clearSettings equal to the now properly asssigned clearSettings + settings.clearSettings = clearSettings; + !settings.allowClearing && (settings.clearSettings.allowClearing = false); + !settings.allowPicking && (settings.clearSettings.allowPicking = false); + + (target instanceof PresetUnit) && (target = target.realCoords()); + + if (settings.minDist > 3) { + target = Pather.spotOnDistance(target, settings.minDist, { returnSpotOnError: settings.returnSpotOnError, reductionType: (me.inTown ? 0 : 2) }); + } + + let fail = 0; + let node = { x: target.x, y: target.y }; + const leaped = { + at: 0, + /** @type {PathNode} */ + from: { x: null, y: null } + }; + const whirled = { + at: 0, + /** @type {PathNode} */ + from: { x: null, y: null } + }; + const cleared = { + at: 0, + /** @type {PathNode} */ + where: { x: null, y: null } + }; + let [invalidCheck] = [false]; + + Pather.clearUIFlags(); + + if (typeof target.x !== "number" || typeof target.y !== "number") return false; + if (getDistance(me, target) < 2 && !CollMap.checkColl(me, target, Coords_1.Collision.BLOCK_MISSILE, 5)) return true; + + const useTeleport = settings.allowTeleport && (getDistance(me, target) > 15 || me.diff || me.act > 3) && Pather.useTeleport(); + settings.clearSettings.canTele = useTeleport; + const useChargedTele = settings.allowTeleport && Pather.canUseTeleCharges(); + const usingTele = (useTeleport || useChargedTele); + const tpMana = Skill.getManaCost(sdk.skills.Teleport); + const annoyingArea = Pather.inAnnoyingArea(me.area); + let path = getPath(me.area, target.x, target.y, me.x, me.y, usingTele ? 1 : 0, usingTele ? (annoyingArea ? 30 : Pather.teleDistance) : Pather.walkDistance); + if (!path) throw new Error("move: Failed to generate path."); + + // need to work on a better force clearing method but for now just have all walkers clear unless we specifically are forcing them not to (like while repositioning) + settings.allowClearing && !settings.clearSettings.clearPath && !useTeleport && (settings.clearSettings.clearPath = true); + + if (settings.retry <= 3 && target.distance > useTeleport ? 120 : 60) { + settings.retry = 10; + } + + // for now only do this for teleporters + if (useTeleport && !me.normal) { + /** @type {Array} */ + let areaImmunities = GameData.areaImmunities(me.area); + if (areaImmunities.length) { + let mySkElems = Config.AttackSkill.filter(sk => sk > 0).map(sk => Attack.getSkillElement(sk)); + // this area has monsters that are immune to our elements. This is a basic check for now + // a better way would probably be per list built to check the ratio of immunes to non? + if (mySkElems.length && mySkElems.every(elem => areaImmunities.includes(elem))) { + settings.clearSettings.clearPath = false; + } + } else if (AreaData[me.area].hasMonsterType(sdk.monsters.type.UndeadFetish)) { + settings.clearSettings.clearPath = false; + } + } + + path.reverse(); + settings.pop && path.pop(); + PathDebug.drawPath(path); + useTeleport && Config.TeleSwitch && path.length > 5 && me.switchWeapons(Attack.getPrimarySlot() ^ 1); + + while (path.length > 0) { + // Abort if dead + if (me.dead) return false; + // main path + Pather.recursion && (Pather.currentWalkingPath = path); + Pather.clearUIFlags(); + + node = path.shift(); + + if (typeof settings.callback === "function" && settings.callback()) { + // console.debug("Callback function passed. Ending path."); + useTeleport && Config.TeleSwitch && me.switchWeapons(Attack.getPrimarySlot() ^ 1); + PathDebug.removeHooks(); + return true; + } + + if (getDistance(me, node) > 2) { + // Make life in Maggot Lair easier + fail >= 3 && fail % 3 === 0 && !Attack.validSpot(node.x, node.y) && (invalidCheck = true); + // Make life in Maggot Lair easier - should this include arcane as well? + if (annoyingArea || invalidCheck) { + let adjustedNode = Pather.getNearestWalkable(node.x, node.y, 15, 3, sdk.collision.BlockWalk); + + if (adjustedNode) { + [node.x, node.y] = [adjustedNode[0], adjustedNode[1]]; + invalidCheck && (invalidCheck = false); + } + + annoyingArea && ([settings.clearSettings.clearPath, settings.clearSettings.range] = [true, 5]); + settings.retry <= 3 && !useTeleport && (settings.retry = 15); + } + + if (useTeleport && tpMana <= me.mp + ? Pather.teleportTo(node.x, node.y) + : useChargedTele && (getDistance(me, node) >= 15 || me.inArea(sdk.areas.ThroneofDestruction)) + ? Pather.teleUsingCharges(node.x, node.y) + : Pather.walkTo(node.x, node.y, (fail > 0 || me.inTown) ? 2 : 4)) { + if (settings.allowNodeActions && !me.inTown) { + if (Pather.recursion) { + try { + Pather.recursion = false; + /** + * @todo We need to pass our path in so we can fix the recursion issues of running forward on our path only to return to the old node and continue + * we should instead perform the actions in a way that moves us forward on our path ensuring we haven't skipped anything in the process as well + * for long paths maybe generate a coordinate list of shrines/chests and have action hooks for them + */ + NodeAction.go(settings.clearSettings); + // need to determine if its worth going back to our orignal node (items maybe?) + // vs our current proximity to our next node + // need to export our main path so other functions that cause us to move can see it + if (node.distance > 5) { + const lastNode = Pather.currentWalkingPath.last(); + // lets try and find the nearest node that brings us close to our goal + /** @type {PathNode} */ + let nearestNode = Pather.currentWalkingPath.length > 0 && Pather.currentWalkingPath + .filter(el => !!el && el.x !== node.x && el.y !== node.y) + .sort((a, b) => { + if (a.distance < b.distance && getDistance(a, lastNode) < getDistance(b, lastNode)) return -1; + if (a.distance > b.distance && getDistance(a, lastNode) > getDistance(b, lastNode)) return 1; + return a.distance - b.distance; + }) + .find(pNode => pNode.distance > 5); + + if (node.distance < 40) { + let goBack = false; + let foundNode = false; + // lets see if it's worth walking back to old node + Pickit.checkSpotForItems(node, true) && (goBack = true); + // @todo check shrines/chests in proximity to old node vs next node + // let otherObjects = getUnits(sdk.unittype.Object).filter(el => getDistance()); + if (goBack) { + // console.debug("Going back to old node. Distance: " + node.distance); + } else if (nearestNode && nearestNode.distance > 5 && node.distance > 5 && Math.percentDifference(node.distance, nearestNode.distance) > 5/* && 100 / node.distance * nearestNode.distance < 95 */) { + // console.debug("Moving to next node. Distance: " + nearestNode.distance); + let newIndex = path.findIndex(node => nearestNode.x === node.x && nearestNode.y === node.y); + if (newIndex > -1) { + // console.debug("Found new path index: " + newIndex + " of currentPathLen: " + path.length); + path = path.slice(newIndex); + node = path.shift(); + foundNode = true; + // console.debug("New path length: " + path.length); + } else { + // console.debug("Couldn't find new path index"); + } + } + + if (node.distance > 5) { + // !foundNode && console.debug("Path Recursion :: Returning to position " + node.x + "/" + node.y + " distance: " + node.distance); + Pather.move(node, settings); + } + } else { + Pather.move(node, settings); + } + } + } finally { + Pather.recursion = true; + } + } + } + } else { + if (!me.inTown) { + if (!useTeleport && settings.allowClearing) { + let tempRange = (annoyingArea ? 5 : 10); + // allowed to clear so lets see if any mobs are around us + if (me.checkForMobs({ range: tempRange, coll: sdk.collision.BlockWalk })) { + // there are at least some, but lets only continue to next iteration if we actually killed something + if (Attack.clear(tempRange, null, null, null, settings.allowPicking) === Attack.Result.SUCCESS) { + // console.debug("Cleared Node"); + continue; + } + } + } + if (!useTeleport && (Pather.openDoors(node.x, node.y) || Pather.kickBarrels(node.x, node.y))) { + continue; + } + + if (fail > 0 && (!useTeleport || tpMana > me.mp)) { + // if we are allowed to clear + if (settings.allowClearing) { + // Don't go berserk on longer paths - also check that there are even mobs blocking us + if (cleared.at === 0 || getTickCount() - cleared.at > Time.seconds(3) && cleared.where.distance > 5 && me.checkForMobs({ range: 10 })) { + // only set that we cleared if we actually killed at least 1 mob + if (Attack.clear(10, null, null, null, settings.allowPicking)) { + // console.debug("Cleared Node"); + cleared.at = getTickCount(); + [cleared.where.x, cleared.where.y] = [node.x, node.y]; + } + } + } + + // Leap can be helpful on long paths but make sure we don't spam it + if (Skill.canUse(sdk.skills.LeapAttack)) { + // we can use leapAttack, now lets see if we should - either haven't used it yet or it's been long enough since last time + if (leaped.at === 0 || getTickCount() - leaped.at > Time.seconds(3) || leaped.from.distance > 5 || me.checkForMobs({ range: 6 })) { + // alright now if we have actually casted it set the values so we know + if (Skill.cast(sdk.skills.LeapAttack, sdk.skills.hand.Right, node.x, node.y)) { + leaped.at = getTickCount(); + [leaped.from.x, leaped.from.y] = [node.x, node.y]; + } + } + } + + /** + * whirlwind can be useful as well, implement it. + * Things to consider: + * 1) Can we cast whirlwind on the node? Is it blocked by something other than monsters. + * 2) If we can't cast on that node, is there another node between us and it that would work? + */ + if (Skill.canUse(sdk.skills.Whirlwind)) { + // we can use whirlwind, now lets see if we should - either haven't used it yet or it's been long enough since last time + if (whirled.at === 0 || getTickCount() - whirled.at > Time.seconds(3) || whirled.from.distance > 5 || me.checkForMobs({ range: 6 })) { + // alright now if we have actually casted it set the values so we know + if (Skill.cast(sdk.skills.Whirlwind, sdk.skills.hand.Right, node.x, node.y)) { + whirled.at = getTickCount(); + [whirled.from.x, whirled.from.y] = [node.x, node.y]; + } + } + } + } + } + + // Reduce node distance in new path + path = getPath(me.area, target.x, target.y, me.x, me.y, useTeleport ? 1 : 0, useTeleport ? rand(25, 35) : rand(10, 15)); + if (!path) throw new Error("moveTo: Failed to generate path."); + + path.reverse(); + PathDebug.drawPath(path); + settings.pop && path.pop(); + + if (fail > 0) { + console.debug("move retry " + fail); + Packet.flash(me.gid); + + if (fail >= settings.retry) { + console.log("Failed move: Retry = " + settings.retry); + break; + } + } + if (fail > 100) { + // why? + console.debug(settings); + throw new Error("Retry limit excessivly exceeded"); + } + fail++; + } + } + + /** + * @todo handle passing in a callback function + */ + + delay(5); + } + + useTeleport && Config.TeleSwitch && me.switchWeapons(Attack.getPrimarySlot() ^ 1); + PathDebug.removeHooks(); + + return getDistance(me, node.x, node.y) < 5; }; Pather.moveNear = function (x, y, minDist, givenSettings = {}) { - return Pather.move({ x: x, y: y }, Object.assign({ minDist: minDist }, givenSettings)); + return Pather.move({ x: x, y: y }, Object.assign({ minDist: minDist }, givenSettings)); }; Pather.moveTo = function (x, y, retry, clearPath = true, pop = false) { - return Pather.move({ x: x, y: y }, { retry: retry, pop: pop, clearSettings: { clearPath: clearPath } }); + return Pather.move({ x: x, y: y }, { retry: retry, pop: pop, clearSettings: { clearPath: clearPath } }); }; Pather.moveToLoc = function (target, givenSettings = {}) { - return Pather.move(target, givenSettings); + return Pather.move(target, givenSettings); }; Pather.moveToEx = function (x, y, givenSettings = {}) { - return Pather.move({ x: x, y: y }, givenSettings); + return Pather.move({ x: x, y: y }, givenSettings); }; // Add check in case "random" to return false if bot doesn't have cold plains wp yet Pather.useWaypoint = function useWaypoint(targetArea, check = false, getWP = false) { - switch (targetArea) { - case undefined: - throw new Error("useWaypoint: Invalid targetArea parameter: " + targetArea); - case null: - case "random": - check = true; - - break; - default: - if (typeof targetArea !== "number") throw new Error("useWaypoint: Invalid targetArea parameter"); - if (!this.wpAreas.includes(targetArea)) throw new Error("useWaypoint: Invalid area"); - - break; - } - - let wpTick = getTickCount(); - - for (let i = 0; i < 12; i += 1) { - if (me.area === targetArea || me.dead) { - break; - } - - if (me.inTown) { - if (me.inArea(sdk.areas.LutGholein)) { - let npc = Game.getNPC(NPC.Warriv); - - if (!!npc && npc.distance < 50) { - if (npc && npc.openMenu()) { - Misc.useMenu(sdk.menu.GoWest); - - if (!Misc.poll(() => me.gameReady && me.inArea(sdk.areas.RogueEncampment), 2000, 100)) { - throw new Error("Failed to go to act 1 using Warriv"); - } - } - } - } - - !getUIFlag(sdk.uiflags.Waypoint) && Town.getDistance("waypoint") > (Skill.haveTK ? 20 : 5) && Town.move("waypoint"); - } - - let wp = Game.getObject("waypoint"); - - if (!!wp && wp.area === me.area) { - let useTK = (Skill.useTK(wp) && i < 3); - let pingDelay = me.getPingDelay(); - - if (useTK && !getUIFlag(sdk.uiflags.Waypoint)) { - wp.distance > 21 && Pather.moveNearUnit(wp, 20); - i > 1 && checkCollision(me, wp, sdk.collision.Ranged) && Attack.getIntoPosition(wp, 20, sdk.collision.Ranged); - Skill.cast(sdk.skills.Telekinesis, sdk.skills.hand.Right, wp); - } else if (!me.inTown && wp.distance > 7) { - this.moveToUnit(wp); - } - - if (check || Config.WaypointMenu || !this.initialized) { - if (!useTK && (wp.distance > 5 || !getUIFlag(sdk.uiflags.Waypoint))) { - this.moveToUnit(wp) && Misc.click(0, 0, wp); - } - - // handle getUnit bug - if (me.inTown && !getUIFlag(sdk.uiflags.Waypoint) && wp.name.toLowerCase() === "dummy") { - Town.getDistance("waypoint") > 5 && Town.move("waypoint"); - Misc.click(0, 0, wp); - } - - let tick = getTickCount(); - - while (getTickCount() - tick < Math.max(Math.round((i + 1) * 1000 / (i / 5 + 1)), pingDelay * 2)) { - if (getUIFlag(sdk.uiflags.Waypoint)) { - delay(500); - - switch (targetArea) { - case "random": - let retry = 0; - - while (true) { - targetArea = this.nonTownWpAreas[rand(0, this.nonTownWpAreas.length - 1)]; - - // get a valid wp, avoid towns - if (getWaypoint(this.wpAreas.indexOf(targetArea))) { - break; - } - - // no valid areas, get the cold plains wp - // maybe just walk out of town instead? - if (retry >= 10) { - if (!getWaypoint(this.wpAreas.indexOf(sdk.areas.ColdPlains)) && me.cancel()) { - me.overhead("Trying to get the waypoint"); - if (this.getWP(sdk.areas.ColdPlains)) return true; - - throw new Error("Pather.useWaypoint: Failed to go to waypoint " + targetArea); - } - } - - retry++; - delay(25); - } - - break; - case null: - me.cancel(); - - return true; - } - - if (!getWaypoint(this.wpAreas.indexOf(targetArea)) && me.cancel()) { - me.overhead("Trying to get the waypoint"); - if (this.getWP(targetArea)) return true; - - throw new Error("Pather.useWaypoint: Failed to go to waypoint " + targetArea); - } - - break; - } - - delay(10); - } - - if (!getUIFlag(sdk.uiflags.Waypoint)) { - console.warn("waypoint retry " + (i + 1)); - let retry = Math.min(i + 1, 5); - let coord = CollMap.getRandCoordinate(me.x, -5 * retry, 5 * retry, me.y, -5 * retry, 5 * retry); - !!coord && this.moveTo(coord.x, coord.y); - delay(200); - i > 1 && (i % 3) === 0 && Packet.flash(me.gid, pingDelay); - - continue; - } - } - - if (!check || getUIFlag(sdk.uiflags.Waypoint)) { - delay(250); - wp.interact(targetArea); - let tick = getTickCount(); - - while (getTickCount() - tick < Math.max(Math.round((i + 1) * 1000 / (i / 5 + 1)), pingDelay * 4)) { - if (me.area === targetArea) { - delay(1500); - console.log("ÿc7End ÿc8(useWaypoint) ÿc0:: ÿc7targetArea: ÿc0" + getAreaName(targetArea) + " ÿc7myArea: ÿc0" + getAreaName(me.area) + "ÿc0 - ÿc7Duration: ÿc0" + (Time.format(getTickCount() - wpTick))); - - return true; - } - - delay(30); - } - - while (!me.gameReady) { - delay(1000); - } - - // In case lag causes the wp menu to stay open - getUIFlag(sdk.uiflags.Waypoint) && me.cancel(); - } + switch (targetArea) { + case undefined: + throw new Error("useWaypoint: Invalid targetArea parameter: " + targetArea); + case null: + case "random": + check = true; + + break; + default: + if (typeof targetArea !== "number") throw new Error("useWaypoint: Invalid targetArea parameter"); + if (!this.wpAreas.includes(targetArea)) throw new Error("useWaypoint: Invalid area"); + + break; + } + + let wpTick = getTickCount(); + + for (let i = 0; i < 12; i += 1) { + if (me.area === targetArea || me.dead) { + break; + } + + if (me.inTown) { + if (me.inArea(sdk.areas.LutGholein)) { + let npc = Game.getNPC(NPC.Warriv); + + if (!!npc && npc.distance < 50) { + if (npc && npc.openMenu()) { + Misc.useMenu(sdk.menu.GoWest); + + if (!Misc.poll(() => me.gameReady && me.inArea(sdk.areas.RogueEncampment), 2000, 100)) { + throw new Error("Failed to go to act 1 using Warriv"); + } + } + } + } + + !getUIFlag(sdk.uiflags.Waypoint) && Town.getDistance("waypoint") > (Skill.haveTK ? 20 : 5) && Town.move("waypoint"); + } + + let wp = Game.getObject("waypoint"); + + if (!!wp && wp.area === me.area) { + let useTK = (Skill.useTK(wp) && i < 3); + let pingDelay = me.getPingDelay(); + + if (useTK && !getUIFlag(sdk.uiflags.Waypoint)) { + wp.distance > 21 && Pather.moveNearUnit(wp, 20); + i > 1 && checkCollision(me, wp, sdk.collision.Ranged) && Attack.getIntoPosition(wp, 20, sdk.collision.Ranged); + Skill.cast(sdk.skills.Telekinesis, sdk.skills.hand.Right, wp); + } else if (!me.inTown && wp.distance > 7) { + this.moveToUnit(wp); + } + + if (check || Config.WaypointMenu || !this.initialized) { + if (!useTK && (wp.distance > 5 || !getUIFlag(sdk.uiflags.Waypoint))) { + this.moveToUnit(wp) && Misc.click(0, 0, wp); + } + + // handle getUnit bug + if (me.inTown && !getUIFlag(sdk.uiflags.Waypoint) && wp.name.toLowerCase() === "dummy") { + Town.getDistance("waypoint") > 5 && Town.move("waypoint"); + Misc.click(0, 0, wp); + } + + let tick = getTickCount(); + + while (getTickCount() - tick < Math.max(Math.round((i + 1) * 1000 / (i / 5 + 1)), pingDelay * 2)) { + if (getUIFlag(sdk.uiflags.Waypoint)) { + delay(500); + + switch (targetArea) { + case "random": + let retry = 0; + + while (true) { + targetArea = this.nonTownWpAreas[rand(0, this.nonTownWpAreas.length - 1)]; + + // get a valid wp, avoid towns + if (getWaypoint(this.wpAreas.indexOf(targetArea))) { + break; + } + + // no valid areas, get the cold plains wp + // maybe just walk out of town instead? + if (retry >= 10) { + if (!getWaypoint(this.wpAreas.indexOf(sdk.areas.ColdPlains)) && me.cancel()) { + me.overhead("Trying to get the waypoint"); + if (this.getWP(sdk.areas.ColdPlains)) return true; + + throw new Error("Pather.useWaypoint: Failed to go to waypoint " + targetArea); + } + } + + retry++; + delay(25); + } + + break; + case null: + me.cancel(); + + return true; + } + + if (!getWaypoint(this.wpAreas.indexOf(targetArea)) && me.cancel()) { + me.overhead("Trying to get the waypoint"); + if (this.getWP(targetArea)) return true; + + throw new Error("Pather.useWaypoint: Failed to go to waypoint " + targetArea); + } + + break; + } + + delay(10); + } + + if (!getUIFlag(sdk.uiflags.Waypoint)) { + console.warn("waypoint retry " + (i + 1)); + let retry = Math.min(i + 1, 5); + let coord = CollMap.getRandCoordinate(me.x, -5 * retry, 5 * retry, me.y, -5 * retry, 5 * retry); + !!coord && this.moveTo(coord.x, coord.y); + delay(200); + i > 1 && (i % 3) === 0 && Packet.flash(me.gid, pingDelay); + + continue; + } + } + + if (!check || getUIFlag(sdk.uiflags.Waypoint)) { + delay(250); + wp.interact(targetArea); + let tick = getTickCount(); + + while (getTickCount() - tick < Math.max(Math.round((i + 1) * 1000 / (i / 5 + 1)), pingDelay * 4)) { + if (me.area === targetArea) { + delay(1500); + console.log("ÿc7End ÿc8(useWaypoint) ÿc0:: ÿc7targetArea: ÿc0" + getAreaName(targetArea) + " ÿc7myArea: ÿc0" + getAreaName(me.area) + "ÿc0 - ÿc7Duration: ÿc0" + (Time.format(getTickCount() - wpTick))); + + return true; + } + + delay(30); + } + + while (!me.gameReady) { + delay(1000); + } + + // In case lag causes the wp menu to stay open + getUIFlag(sdk.uiflags.Waypoint) && me.cancel(); + } - i > 1 && (i % 3) === 0 && Packet.flash(me.gid, pingDelay); - // Activate check if we fail direct interact twice - i > 1 && (check = true); - } else { - Packet.flash(me.gid); - } + i > 1 && (i % 3) === 0 && Packet.flash(me.gid, pingDelay); + // Activate check if we fail direct interact twice + i > 1 && (check = true); + } else { + Packet.flash(me.gid); + } - // We can't seem to get the wp maybe attempt portal to town instead and try to use that wp - i >= 10 && !me.inTown && Town.goToTown(); + // We can't seem to get the wp maybe attempt portal to town instead and try to use that wp + i >= 10 && !me.inTown && Town.goToTown(); - delay(250); - } + delay(250); + } - if (me.area === targetArea) { - delay(500); - console.log("ÿc7End ÿc8(useWaypoint) ÿc0:: ÿc7targetArea: ÿc0" + getAreaName(targetArea) + " ÿc7myArea: ÿc0" + getAreaName(me.area) + "ÿc0 - ÿc7Duration: ÿc0" + (Time.format(getTickCount() - wpTick))); + if (me.area === targetArea) { + delay(500); + console.log("ÿc7End ÿc8(useWaypoint) ÿc0:: ÿc7targetArea: ÿc0" + getAreaName(targetArea) + " ÿc7myArea: ÿc0" + getAreaName(me.area) + "ÿc0 - ÿc7Duration: ÿc0" + (Time.format(getTickCount() - wpTick))); - return true; - } + return true; + } - throw new Error("useWaypoint: Failed to use waypoint to " + targetArea); + throw new Error("useWaypoint: Failed to use waypoint to " + targetArea); }; // credit - Legacy Autosmurf Pather.clearToExit = function (currentarea, targetarea, cleartype = true) { - let tick = getTickCount(); - let retry = 0; - console.log("ÿc8Kolbot-SoloPlayÿc0: Start clearToExit. ÿc8Currently in: ÿc0" + getAreaName(me.area) + "ÿc8Clearing to: ÿc0" + getAreaName(targetarea)); - - me.area !== currentarea && Pather.journeyTo(currentarea); - - if (me.area !== targetarea) { - do { - try { - Pather.moveToExit(targetarea, true, cleartype); - } catch (e) { - console.debug("Caught Error: ", e.message ? e.message : e); - } - - delay(500); - Misc.poll(() => me.gameReady, 1000, 100); - - if (retry > 5) { - console.log("ÿc8Kolbot-SoloPlayÿc0: clearToExit. ÿc2Failed to move to: ÿc0" + getAreaName(targetarea)); - - break; - } - - retry++; - } while (me.area !== targetarea); - } - - console.log("ÿc8Kolbot-SoloPlayÿc0: End clearToExit. Time elapsed: " + Time.format(getTickCount() - tick)); - return (me.area === targetarea); + let tick = getTickCount(); + let retry = 0; + console.log("ÿc8Kolbot-SoloPlayÿc0: Start clearToExit. ÿc8Currently in: ÿc0" + getAreaName(me.area) + "ÿc8Clearing to: ÿc0" + getAreaName(targetarea)); + + me.area !== currentarea && Pather.journeyTo(currentarea); + + if (me.area !== targetarea) { + do { + try { + Pather.moveToExit(targetarea, true, cleartype); + } catch (e) { + console.debug("Caught Error: ", e.message ? e.message : e); + } + + delay(500); + Misc.poll(() => me.gameReady, 1000, 100); + + if (retry > 5) { + console.log("ÿc8Kolbot-SoloPlayÿc0: clearToExit. ÿc2Failed to move to: ÿc0" + getAreaName(targetarea)); + + break; + } + + retry++; + } while (me.area !== targetarea); + } + + console.log("ÿc8Kolbot-SoloPlayÿc0: End clearToExit. Time elapsed: " + Time.format(getTickCount() - tick)); + return (me.area === targetarea); }; Pather.getWalkDistance = function (x, y, area = me.area, xx = me.x, yy = me.y, reductionType = 2, radius = 5) { - // distance between node x and x-1 - return (getPath(area, x, y, xx, yy, reductionType, radius) || []) - .map((e, i, s) => i && getDistance(s[i - 1], e) || 0) - .reduce((acc, cur) => acc + cur, 0) || Infinity; + // distance between node x and x-1 + return (getPath(area, x, y, xx, yy, reductionType, radius) || []) + .map((e, i, s) => i && getDistance(s[i - 1], e) || 0) + .reduce((acc, cur) => acc + cur, 0) || Infinity; }; diff --git a/libs/SoloPlay/Functions/PickitOverrides.js b/libs/SoloPlay/Functions/PickitOverrides.js index 7bb96f7e..2a654385 100644 --- a/libs/SoloPlay/Functions/PickitOverrides.js +++ b/libs/SoloPlay/Functions/PickitOverrides.js @@ -16,18 +16,18 @@ Pickit.Result.SOLOWANTS = 8; Pickit.Result.SOLOSYSTEM = 9; Pickit.minItemKeepGoldValue = function () { - const myGold = me.gold; - const cLvl = me.charlvl; - switch (true) { - case myGold < Math.min(Math.floor(500 + (cLvl * 100 * Math.sqrt(cLvl - 1))), 250000): - return 10; - case myGold < Math.min(Math.floor(500 + (cLvl * 250 * Math.sqrt(cLvl - 1))), 250000): - return 50; - case myGold < Math.min(Math.floor(500 + (cLvl * 500 * Math.sqrt(cLvl - 1))), 250000): - return 500; - default: - return 1000; - } + const myGold = me.gold; + const cLvl = me.charlvl; + switch (true) { + case myGold < Math.min(Math.floor(500 + (cLvl * 100 * Math.sqrt(cLvl - 1))), 250000): + return 10; + case myGold < Math.min(Math.floor(500 + (cLvl * 250 * Math.sqrt(cLvl - 1))), 250000): + return 50; + case myGold < Math.min(Math.floor(500 + (cLvl * 500 * Math.sqrt(cLvl - 1))), 250000): + return 500; + default: + return 1000; + } }; /** @@ -40,142 +40,142 @@ Pickit.classicMode = me.classic; * @param {ItemUnit} unit */ Pickit.checkItem = function (unit) { - const rval = NTIP.CheckItem(unit, false, true); - const resultObj = (result, line = null) => ({ - result: result, - line: line - }); - - // quick return on essentials - we know they aren't going to be in the other checks - if (Pickit.essentials.includes(unit.itemType)) return rval; - - if (!Pickit.classicMode) { - if ([sdk.items.runes.Ral, sdk.items.runes.Ort].includes(unit.classid) && Town.repairIngredientCheck(unit)) { - return resultObj(Pickit.Result.UTILITY); - } - - /** - * Need to redo this - */ - if (CharData.skillData.bow.onSwitch && [sdk.items.type.BowQuiver, sdk.items.type.CrossbowQuiver].includes(unit.itemType) && rval === Pickit.Result.WANTED) { - if ([sdk.items.type.Bow, sdk.items.type.AmazonBow].includes(CharData.skillData.bow.bowType) && unit.itemType === sdk.items.type.BowQuiver) { - return resultObj(Pickit.Result.SOLOWANTS, "Switch-Arrows"); - } else if (CharData.skillData.bow.bowType === sdk.items.type.Crossbow && unit.itemType === sdk.items.type.CrossbowQuiver) { - return resultObj(Pickit.Result.SOLOWANTS, "Switch-Bolts"); - } - } - } - - if (unit.classid === sdk.items.StaminaPotion && (me.charlvl < 18 || me.staminaPercent <= 85 || me.walking) && Item.getQuantityOwned(unit, true) < 2) { - return resultObj(Pickit.Result.WANTED, "LowStamina"); - } - - if (unit.classid === sdk.items.AntidotePotion && me.getState(sdk.states.Poison) && Item.getQuantityOwned(unit, true) < 2) { - return resultObj(Pickit.Result.WANTED, "Poisoned"); - } - - if (unit.classid === sdk.items.ThawingPotion && [sdk.states.Frozen, sdk.states.FrozenSolid].some(state => me.getState(state)) && Item.getQuantityOwned(unit, true) < 2) { - return resultObj(Pickit.Result.WANTED, "Frozen"); - } - - if (rval.result === Pickit.Result.WANTED) { - if (unit.isBroken) { - return resultObj(Pickit.Result.TRASH); - } - } - - if (SoloWants.checkItem(unit)) return resultObj(Pickit.Result.SOLOSYSTEM); - if (CraftingSystem.checkItem(unit)) return resultObj(Pickit.Result.CRAFTING); - if (Cubing.checkItem(unit)) return resultObj(Pickit.Result.CUBING); - if (Runewords.checkItem(unit)) return resultObj(Pickit.Result.RUNEWORD); - if (AutoEquip.hasTier(unit) && !unit.identified) return resultObj(Pickit.Result.UNID); - - if (unit.isCharm/* && NTIP.GetCharmTier(unit) > 0 && unit.identified */) { - if (CharmEquip.check(unit)) { - return resultObj(Pickit.Result.SOLOWANTS, "Autoequip charm Tier: " + NTIP.GetCharmTier(unit)); - } - - return NTIP.CheckItem(unit, NTIP_CheckListNoTier, true); - } - - if ((NTIP.GetMercTier(unit) > 0 || NTIP.GetTier(unit) > 0 || NTIP.GetSecondaryTier(unit) > 0) && unit.identified) { - if (Item.autoEquipCheck(unit)) { - return resultObj(Pickit.Result.SOLOWANTS, "Autoequip Tier: " + NTIP.GetTier(unit)); - } - - if (Item.autoEquipCheckMerc(unit)) { - return resultObj(Pickit.Result.SOLOWANTS, "Autoequip MercTier: " + NTIP.GetMercTier(unit)); - } - - if (Item.autoEquipCheckSecondary(unit)) { - return resultObj(Pickit.Result.SOLOWANTS, "Autoequip Secondary Tier: " + NTIP.GetSecondaryTier(unit)); - } - - return NTIP.CheckItem(unit, NTIP_CheckListNoTier, true); - } - - if (rval.result === Pickit.Result.WANTED && unit.isBaseType) { - if (NTIP.CheckItem(unit, NTIP.SoloCheckListNoTier)) { - return resultObj(Pickit.Result.SOLOWANTS, "Base Type Item"); - } - } - - // LowGold - if (rval.result === Pickit.Result.UNWANTED && !Town.ignoredItemTypes.includes(unit.itemType) && !unit.questItem - && (unit.isInInventory || (me.gold < Config.LowGold || me.gold < 500000))) { - // Gold doesn't take up room, just pick it up - if (unit.classid === sdk.items.Gold) return resultObj(Pickit.Result.TRASH); - - const itemValue = unit.getItemCost(sdk.items.cost.ToSell); - const itemValuePerSquare = itemValue / (unit.sizex * unit.sizey); - - if (itemValuePerSquare >= 2000) { - // If total gold is less than 500k pick up anything worth 2k gold per square to sell in town. - return resultObj(Pickit.Result.TRASH, "Valuable Item: " + itemValue); - } else if (itemValuePerSquare >= Pickit.minItemKeepGoldValue() && (me.gold < Config.LowGold || unit.isInInventory)) { - // If total gold is less than LowGold setting pick up anything worth 10 gold per square to sell in town. - return resultObj(Pickit.Result.TRASH, "LowGold Item: " + itemValue); - } - } - - return rval; + const rval = NTIP.CheckItem(unit, false, true); + const resultObj = (result, line = null) => ({ + result: result, + line: line + }); + + // quick return on essentials - we know they aren't going to be in the other checks + if (Pickit.essentials.includes(unit.itemType)) return rval; + + if (!Pickit.classicMode) { + if ([sdk.items.runes.Ral, sdk.items.runes.Ort].includes(unit.classid) && Town.repairIngredientCheck(unit)) { + return resultObj(Pickit.Result.UTILITY); + } + + /** + * Need to redo this + */ + if (CharData.skillData.bow.onSwitch && [sdk.items.type.BowQuiver, sdk.items.type.CrossbowQuiver].includes(unit.itemType) && rval === Pickit.Result.WANTED) { + if ([sdk.items.type.Bow, sdk.items.type.AmazonBow].includes(CharData.skillData.bow.bowType) && unit.itemType === sdk.items.type.BowQuiver) { + return resultObj(Pickit.Result.SOLOWANTS, "Switch-Arrows"); + } else if (CharData.skillData.bow.bowType === sdk.items.type.Crossbow && unit.itemType === sdk.items.type.CrossbowQuiver) { + return resultObj(Pickit.Result.SOLOWANTS, "Switch-Bolts"); + } + } + } + + if (unit.classid === sdk.items.StaminaPotion && (me.charlvl < 18 || me.staminaPercent <= 85 || me.walking) && Item.getQuantityOwned(unit, true) < 2) { + return resultObj(Pickit.Result.WANTED, "LowStamina"); + } + + if (unit.classid === sdk.items.AntidotePotion && me.getState(sdk.states.Poison) && Item.getQuantityOwned(unit, true) < 2) { + return resultObj(Pickit.Result.WANTED, "Poisoned"); + } + + if (unit.classid === sdk.items.ThawingPotion && [sdk.states.Frozen, sdk.states.FrozenSolid].some(state => me.getState(state)) && Item.getQuantityOwned(unit, true) < 2) { + return resultObj(Pickit.Result.WANTED, "Frozen"); + } + + if (rval.result === Pickit.Result.WANTED) { + if (unit.isBroken) { + return resultObj(Pickit.Result.TRASH); + } + } + + if (SoloWants.checkItem(unit)) return resultObj(Pickit.Result.SOLOSYSTEM); + if (CraftingSystem.checkItem(unit)) return resultObj(Pickit.Result.CRAFTING); + if (Cubing.checkItem(unit)) return resultObj(Pickit.Result.CUBING); + if (Runewords.checkItem(unit)) return resultObj(Pickit.Result.RUNEWORD); + if (AutoEquip.hasTier(unit) && !unit.identified) return resultObj(Pickit.Result.UNID); + + if (unit.isCharm/* && NTIP.GetCharmTier(unit) > 0 && unit.identified */) { + if (CharmEquip.check(unit)) { + return resultObj(Pickit.Result.SOLOWANTS, "Autoequip charm Tier: " + NTIP.GetCharmTier(unit)); + } + + return NTIP.CheckItem(unit, NTIP_CheckListNoTier, true); + } + + if ((NTIP.GetMercTier(unit) > 0 || NTIP.GetTier(unit) > 0 || NTIP.GetSecondaryTier(unit) > 0) && unit.identified) { + if (Item.autoEquipCheck(unit)) { + return resultObj(Pickit.Result.SOLOWANTS, "Autoequip Tier: " + NTIP.GetTier(unit)); + } + + if (Item.autoEquipCheckMerc(unit)) { + return resultObj(Pickit.Result.SOLOWANTS, "Autoequip MercTier: " + NTIP.GetMercTier(unit)); + } + + if (Item.autoEquipCheckSecondary(unit)) { + return resultObj(Pickit.Result.SOLOWANTS, "Autoequip Secondary Tier: " + NTIP.GetSecondaryTier(unit)); + } + + return NTIP.CheckItem(unit, NTIP_CheckListNoTier, true); + } + + if (rval.result === Pickit.Result.WANTED && unit.isBaseType) { + if (NTIP.CheckItem(unit, NTIP.SoloCheckListNoTier)) { + return resultObj(Pickit.Result.SOLOWANTS, "Base Type Item"); + } + } + + // LowGold + if (rval.result === Pickit.Result.UNWANTED && !Town.ignoredItemTypes.includes(unit.itemType) && !unit.questItem + && (unit.isInInventory || (me.gold < Config.LowGold || me.gold < 500000))) { + // Gold doesn't take up room, just pick it up + if (unit.classid === sdk.items.Gold) return resultObj(Pickit.Result.TRASH); + + const itemValue = unit.getItemCost(sdk.items.cost.ToSell); + const itemValuePerSquare = itemValue / (unit.sizex * unit.sizey); + + if (itemValuePerSquare >= 2000) { + // If total gold is less than 500k pick up anything worth 2k gold per square to sell in town. + return resultObj(Pickit.Result.TRASH, "Valuable Item: " + itemValue); + } else if (itemValuePerSquare >= Pickit.minItemKeepGoldValue() && (me.gold < Config.LowGold || unit.isInInventory)) { + // If total gold is less than LowGold setting pick up anything worth 10 gold per square to sell in town. + return resultObj(Pickit.Result.TRASH, "LowGold Item: " + itemValue); + } + } + + return rval; }; // @jaenster Pickit.amountOfPotsNeeded = function () { - let _a, _b, _c, _d; - let potTypes = [sdk.items.type.HealingPotion, sdk.items.type.ManaPotion, sdk.items.type.RejuvPotion]; - let hpMax = (Array.isArray(Config.HPBuffer) ? Config.HPBuffer[1] : Config.HPBuffer); - let mpMax = (Array.isArray(Config.MPBuffer) ? Config.MPBuffer[1] : Config.MPBuffer); - let rvMax = (Array.isArray(Config.RejuvBuffer) ? Config.RejuvBuffer[1] : Config.RejuvBuffer); - let needed = (_a = {}, - _a[sdk.items.type.HealingPotion] = (_b = {}, - _b[sdk.storage.Belt] = 0, - _b[sdk.storage.Inventory] = hpMax, - _b), - _a[sdk.items.type.ManaPotion] = (_c = {}, - _c[sdk.storage.Belt] = 0, - _c[sdk.storage.Inventory] = mpMax, - _c), - _a[sdk.items.type.RejuvPotion] = (_d = {}, - _d[sdk.storage.Belt] = 0, - _d[sdk.storage.Inventory] = rvMax, - _d), - _a); - if (hpMax > 0 || mpMax > 0 || rvMax > 0) { - me.getItemsEx() - .filter((pot) => potTypes.includes(pot.itemType) && (pot.isInBelt || pot.isInInventory)) - .forEach(function (pot) { - needed[pot.itemType][pot.location] -= 1; - }); - } - let missing = Storage.Belt.checkColumns(Pickit.beltSize); - Config.BeltColumn.forEach(function (column, index) { - if (column === "hp") {needed[sdk.items.type.HealingPotion][sdk.storage.Belt] = missing[index];} - if (column === "mp") {needed[sdk.items.type.ManaPotion][sdk.storage.Belt] = missing[index];} - if (column === "rv") {needed[sdk.items.type.RejuvPotion][sdk.storage.Belt] = missing[index];} - }); - return needed; + let _a, _b, _c, _d; + let potTypes = [sdk.items.type.HealingPotion, sdk.items.type.ManaPotion, sdk.items.type.RejuvPotion]; + let hpMax = (Array.isArray(Config.HPBuffer) ? Config.HPBuffer[1] : Config.HPBuffer); + let mpMax = (Array.isArray(Config.MPBuffer) ? Config.MPBuffer[1] : Config.MPBuffer); + let rvMax = (Array.isArray(Config.RejuvBuffer) ? Config.RejuvBuffer[1] : Config.RejuvBuffer); + let needed = (_a = {}, + _a[sdk.items.type.HealingPotion] = (_b = {}, + _b[sdk.storage.Belt] = 0, + _b[sdk.storage.Inventory] = hpMax, + _b), + _a[sdk.items.type.ManaPotion] = (_c = {}, + _c[sdk.storage.Belt] = 0, + _c[sdk.storage.Inventory] = mpMax, + _c), + _a[sdk.items.type.RejuvPotion] = (_d = {}, + _d[sdk.storage.Belt] = 0, + _d[sdk.storage.Inventory] = rvMax, + _d), + _a); + if (hpMax > 0 || mpMax > 0 || rvMax > 0) { + me.getItemsEx() + .filter((pot) => potTypes.includes(pot.itemType) && (pot.isInBelt || pot.isInInventory)) + .forEach(function (pot) { + needed[pot.itemType][pot.location] -= 1; + }); + } + let missing = Storage.Belt.checkColumns(Pickit.beltSize); + Config.BeltColumn.forEach(function (column, index) { + if (column === "hp") {needed[sdk.items.type.HealingPotion][sdk.storage.Belt] = missing[index];} + if (column === "mp") {needed[sdk.items.type.ManaPotion][sdk.storage.Belt] = missing[index];} + if (column === "rv") {needed[sdk.items.type.RejuvPotion][sdk.storage.Belt] = missing[index];} + }); + return needed; }; /** @@ -183,28 +183,28 @@ Pickit.amountOfPotsNeeded = function () { * @returns {boolean} */ Pickit.canFit = function (item) { - switch (item.itemType) { - case sdk.items.type.Gold: - return true; - case sdk.items.type.Scroll: - { - let tome = me.findItem(item.classid - 11, 0, sdk.storage.Inventory); - return (tome && tome.getStat(sdk.stats.Quantity) < 20) || Storage.Inventory.CanFit(item); - } - case sdk.items.type.HealingPotion: - case sdk.items.type.ManaPotion: - case sdk.items.type.RejuvPotion: - { - let pots = this.amountOfPotsNeeded(); - if (pots[item.itemType][sdk.storage.Belt] > 0) { - // this potion can go in belt - return true; - } - } - return Storage.Inventory.CanFit(item); - default: - return Storage.Inventory.CanFit(item); - } + switch (item.itemType) { + case sdk.items.type.Gold: + return true; + case sdk.items.type.Scroll: + { + let tome = me.findItem(item.classid - 11, 0, sdk.storage.Inventory); + return (tome && tome.getStat(sdk.stats.Quantity) < 20) || Storage.Inventory.CanFit(item); + } + case sdk.items.type.HealingPotion: + case sdk.items.type.ManaPotion: + case sdk.items.type.RejuvPotion: + { + let pots = this.amountOfPotsNeeded(); + if (pots[item.itemType][sdk.storage.Belt] > 0) { + // this potion can go in belt + return true; + } + } + return Storage.Inventory.CanFit(item); + default: + return Storage.Inventory.CanFit(item); + } }; /** @@ -212,152 +212,152 @@ Pickit.canFit = function (item) { * @returns {boolean} */ Pickit.canPick = function (unit) { - if (!unit) return false; - if (sdk.quest.items.includes(unit.classid) && me.getItem(unit.classid)) return false; - - // TODO: clean this up - - let tome, charm, i, potion, needPots, buffers, pottype, myKey, key; - - switch (unit.itemType) { - case sdk.items.type.Gold: - // Check current gold vs max capacity (cLvl*10000) and skip if full - return (me.getStat(sdk.stats.Gold) < me.getStat(sdk.stats.Level) * 10000); - case sdk.items.type.Scroll: - // 518 - Tome of Town Portal or 519 - Tome of Identify, mode 0 - inventory/stash - tome = me.getItem(unit.classid - 11, sdk.items.mode.inStorage); - - if (tome) { - do { - if (tome.isInInventory && tome.getStat(sdk.stats.Quantity) === 20) { - return false; // Skip a scroll if its tome is full - } - } while (tome.getNext()); - } else { - // If we don't have a tome, go ahead and keep 2 scrolls - return unit.classid === sdk.items.ScrollofIdentify && me.charlvl > 5 - ? false - : me.getItemsEx(unit.classid).filter(el => el.isInInventory).length < 2; - } - - break; - case sdk.items.type.Key: - // Assassins don't ever need keys - if (me.assassin) return false; - - myKey = me.getItem(sdk.items.Key, sdk.items.mode.inStorage); - key = Game.getItem(-1, -1, unit.gid); // Passed argument isn't an actual unit, we need to get it - - if (myKey && key) { - do { - if (myKey.isInInventory && myKey.getStat(sdk.stats.Quantity) + key.getStat(sdk.stats.Quantity) > 12) { - return false; - } - } while (myKey.getNext()); - } - - break; - case sdk.items.type.SmallCharm: - case sdk.items.type.LargeCharm: - case sdk.items.type.GrandCharm: - if (unit.unique) { - charm = me.getItem(unit.classid, sdk.items.mode.inStorage); - - if (charm) { - do { - // Skip Gheed's Fortune, Hellfire Torch or Annihilus if we already have one - if (charm.unique) return false; - } while (charm.getNext()); - } - } - - break; - case sdk.items.type.HealingPotion: - case sdk.items.type.ManaPotion: - case sdk.items.type.RejuvPotion: - needPots = 0; - - for (i = 0; i < 4; i += 1) { - if (typeof unit.code === "string" && unit.code.includes(Config.BeltColumn[i])) { - needPots += this.beltSize; - } - } - - potion = me.getItem(-1, sdk.items.mode.inBelt); - - if (potion) { - do { - if (potion.itemType === unit.itemType) { - needPots -= 1; - } - } while (potion.getNext()); - } - - // re-do this to pick items to cursor if we don't want them in our belt then place them in invo - let beltCheck = this.checkBelt(); - if (needPots < 1) { - buffers = ["HPBuffer", "MPBuffer", "RejuvBuffer"]; - - for (i = 0; i < buffers.length; i += 1) { - if (Config[buffers[i]]) { - pottype = (() => { - switch (buffers[i]) { - case "HPBuffer": - return sdk.items.type.HealingPotion; - case "MPBuffer": - return sdk.items.type.ManaPotion; - case "RejuvBuffer": - return sdk.items.type.RejuvPotion; - default: - return -1; - } - })(); - - if (unit.itemType === pottype) { - if (!Storage.Inventory.CanFit(unit)) return false; - - needPots = Config[buffers[i]]; - potion = me.getItem(-1, sdk.items.mode.inStorage); - - if (potion) { - do { - if (potion.itemType === pottype && potion.isInInventory) { - needPots -= 1; - } - } while (potion.getNext()); - } - } - - needPots > 0 && !beltCheck && Pickit.toCursorPick.push(unit.gid); - } - } - } - - if (needPots < 1) { - potion = me.getItem(); - - if (potion) { - do { - if (potion.itemType === unit.itemType && (potion.isInInventory || potion.isInBelt)) { - if (potion.classid < unit.classid) { - potion.use(); - needPots += 1; - - break; - } - } - } while (potion.getNext()); - } - } - - return (needPots > 0) || (me.charlvl < 10 && Storage.Inventory.CanFit(unit)); - case undefined: // Yes, it does happen - console.warn("undefined item (!?)"); - - return false; - } - - return true; + if (!unit) return false; + if (sdk.quest.items.includes(unit.classid) && me.getItem(unit.classid)) return false; + + // TODO: clean this up + + let tome, charm, i, potion, needPots, buffers, pottype, myKey, key; + + switch (unit.itemType) { + case sdk.items.type.Gold: + // Check current gold vs max capacity (cLvl*10000) and skip if full + return (me.getStat(sdk.stats.Gold) < me.getStat(sdk.stats.Level) * 10000); + case sdk.items.type.Scroll: + // 518 - Tome of Town Portal or 519 - Tome of Identify, mode 0 - inventory/stash + tome = me.getItem(unit.classid - 11, sdk.items.mode.inStorage); + + if (tome) { + do { + if (tome.isInInventory && tome.getStat(sdk.stats.Quantity) === 20) { + return false; // Skip a scroll if its tome is full + } + } while (tome.getNext()); + } else { + // If we don't have a tome, go ahead and keep 2 scrolls + return unit.classid === sdk.items.ScrollofIdentify && me.charlvl > 5 + ? false + : me.getItemsEx(unit.classid).filter(el => el.isInInventory).length < 2; + } + + break; + case sdk.items.type.Key: + // Assassins don't ever need keys + if (me.assassin) return false; + + myKey = me.getItem(sdk.items.Key, sdk.items.mode.inStorage); + key = Game.getItem(-1, -1, unit.gid); // Passed argument isn't an actual unit, we need to get it + + if (myKey && key) { + do { + if (myKey.isInInventory && myKey.getStat(sdk.stats.Quantity) + key.getStat(sdk.stats.Quantity) > 12) { + return false; + } + } while (myKey.getNext()); + } + + break; + case sdk.items.type.SmallCharm: + case sdk.items.type.LargeCharm: + case sdk.items.type.GrandCharm: + if (unit.unique) { + charm = me.getItem(unit.classid, sdk.items.mode.inStorage); + + if (charm) { + do { + // Skip Gheed's Fortune, Hellfire Torch or Annihilus if we already have one + if (charm.unique) return false; + } while (charm.getNext()); + } + } + + break; + case sdk.items.type.HealingPotion: + case sdk.items.type.ManaPotion: + case sdk.items.type.RejuvPotion: + needPots = 0; + + for (i = 0; i < 4; i += 1) { + if (typeof unit.code === "string" && unit.code.includes(Config.BeltColumn[i])) { + needPots += this.beltSize; + } + } + + potion = me.getItem(-1, sdk.items.mode.inBelt); + + if (potion) { + do { + if (potion.itemType === unit.itemType) { + needPots -= 1; + } + } while (potion.getNext()); + } + + // re-do this to pick items to cursor if we don't want them in our belt then place them in invo + let beltCheck = this.checkBelt(); + if (needPots < 1) { + buffers = ["HPBuffer", "MPBuffer", "RejuvBuffer"]; + + for (i = 0; i < buffers.length; i += 1) { + if (Config[buffers[i]]) { + pottype = (() => { + switch (buffers[i]) { + case "HPBuffer": + return sdk.items.type.HealingPotion; + case "MPBuffer": + return sdk.items.type.ManaPotion; + case "RejuvBuffer": + return sdk.items.type.RejuvPotion; + default: + return -1; + } + })(); + + if (unit.itemType === pottype) { + if (!Storage.Inventory.CanFit(unit)) return false; + + needPots = Config[buffers[i]]; + potion = me.getItem(-1, sdk.items.mode.inStorage); + + if (potion) { + do { + if (potion.itemType === pottype && potion.isInInventory) { + needPots -= 1; + } + } while (potion.getNext()); + } + } + + needPots > 0 && !beltCheck && Pickit.toCursorPick.push(unit.gid); + } + } + } + + if (needPots < 1) { + potion = me.getItem(); + + if (potion) { + do { + if (potion.itemType === unit.itemType && (potion.isInInventory || potion.isInBelt)) { + if (potion.classid < unit.classid) { + potion.use(); + needPots += 1; + + break; + } + } + } while (potion.getNext()); + } + } + + return (needPots > 0) || (me.charlvl < 10 && Storage.Inventory.CanFit(unit)); + case undefined: // Yes, it does happen + console.warn("undefined item (!?)"); + + return false; + } + + return true; }; Pickit.toCursorPick = []; @@ -370,239 +370,239 @@ Pickit.toCursorPick = []; * @param {boolean} clearBeforePick */ Pickit.pickItem = function (unit, status, keptLine, clearBeforePick = true) { - /** - * @constructor - * @param {ItemUnit} unit - */ - function ItemStats (unit) { - let self = this; - self.x = unit.x; - self.y = unit.y; - self.ilvl = unit.ilvl; - self.sockets = unit.sockets; - self.type = unit.itemType; - self.classid = unit.classid; - self.name = unit.name; - self.color = Item.color(unit); - self.gold = unit.getStat(sdk.stats.Gold); - self.dist = (unit.distance || Infinity); - let canTk = (Skill.haveTK && Pickit.tkable.includes(self.type) && Pickit.toCursorPick.indexOf(unit.gid) === -1 - && self.dist > 5 && self.dist < 20 && !checkCollision(me, unit, sdk.collision.WallOrRanged)); - self.useTk = canTk && (me.mpPercent > 50); - self.picked = false; - } - - let item, tick, gid, retry = false; - const itemCount = me.itemcount; - const cancelFlags = [sdk.uiflags.Inventory, sdk.uiflags.NPCMenu, sdk.uiflags.Waypoint, sdk.uiflags.Shop, sdk.uiflags.Stash, sdk.uiflags.Cube]; - - if (!unit || unit === undefined) return false; - - if (unit.gid) { - gid = unit.gid; - item = Game.getItem(-1, -1, gid); - } - - if (!item) return false; - - for (let i = 0; i < cancelFlags.length; i += 1) { - if (getUIFlag(cancelFlags[i])) { - delay(500); - me.cancel(0); - - break; - } - } - - const stats = new ItemStats(item); - const tkMana = stats.useTk ? Skill.getManaCost(sdk.skills.Telekinesis) * 2 : Infinity; - - MainLoop: - for (let i = 0; i < 3; i += 1) { - if (me.dead) return false; - if (!Game.getItem(-1, -1, gid)) { - break; - } - - while (!me.idle) { - delay(40); - } - - if (!item.onGroundOrDropping) { - break; - } - - let itemDist = item.distance; - // todo - allow picking near potions/scrolls while attacking distance < 5 - if (stats.useTk && me.mp > tkMana) { - Packet.telekinesis(item); - } else { - let checkItem = false; - const maxDist = (Config.FastPick || i < 1) ? 8 : 5; - if (item.distance > maxDist || checkCollision(me, item, sdk.collision.BlockWall)) { - let coll = (sdk.collision.BlockWall | sdk.collision.Objects | sdk.collision.ClosedDoor); - - if (!clearBeforePick && me.checkForMobs({ range: 5, coll: coll })) { - continue; - } - - if (clearBeforePick && item.checkForMobs({ range: 8, coll: coll })) { - try { - console.log("ÿc8PickItemÿc0 :: Clearing area around item I want to pick"); - Pickit.enabled = false; // Don't pick while trying to clear - Attack.clearPos(item.x, item.y, 10, false); - } finally { - Pickit.enabled = true; // Reset value - } - } - checkItem = true; - } - - if (checkItem || i > 0) { - if (copyUnit(item).x === undefined || !item.onGroundOrDropping) { - break; - } - if (!Pather.moveNearUnit({ x: stats.x, y: stats.y }, 5)) continue; - } - - let cursorUnit; - itemDist = item.distance; - // use packet first, if we fail and not using fast pick use click - Pickit.toCursorPick.includes(item.gid) - ? Packet.click(item, true) && (cursorUnit = Misc.poll(() => Game.getCursorUnit(), (itemDist > 10 ? 1000 : 250), 50)) && Storage.Inventory.MoveTo(cursorUnit) - : (Config.FastPick || i < 1) ? Packet.click(item) : Misc.click(0, 0, item); - } - - tick = getTickCount(); - - while (getTickCount() - tick < (itemDist > 10 ? 2000 : 1000)) { - item = copyUnit(item); - Pickit.toCursorPick.includes(item.gid) && Pickit.toCursorPick.remove(item.gid); - - if (stats.classid === sdk.items.Gold) { - if (!item.getStat(sdk.stats.Gold) || item.getStat(sdk.stats.Gold) < stats.gold) { - console.log("ÿc7Picked up " + stats.color + (item.getStat(sdk.stats.Gold) ? (item.getStat(sdk.stats.Gold) - stats.gold) : stats.gold) + " " + stats.name); - - return true; - } - } - - if (!item.onGroundOrDropping) { - switch (stats.classid) { - case sdk.items.Key: - console.log("ÿc7Picked up " + stats.color + stats.name + " ÿc7(" + Town.checkKeys() + "/12)"); - - return true; - case sdk.items.ScrollofTownPortal: - case sdk.items.ScrollofIdentify: - console.log("ÿc7Picked up " + stats.color + stats.name + " ÿc7(" + Town.checkScrolls(stats.classid === sdk.items.ScrollofTownPortal ? "tbk" : "ibk") + "/20)"); - - return true; - case sdk.items.Arrows: - case sdk.items.Bolts: - me.needRepair(); - - break; - } - - me.itemoncursor && Storage.Inventory.MoveTo(Game.getCursorUnit()); - - break MainLoop; - } - - delay(20); - } - - // TK failed, disable it - stats.useTk = false; - - //console.log("pick retry"); - } - - if (retry) return this.pickItem(unit, status, keptLine); - - stats.picked = me.itemcount > itemCount || !!me.getItem(-1, -1, gid); - - if (stats.picked) { - DataFile.updateStats("lastArea"); - - switch (status) { - case Pickit.Result.WANTED: - case Pickit.Result.SOLOWANTS: - console.log("ÿc7Picked up " + stats.color + stats.name + " ÿc0(ilvl " + stats.ilvl + (stats.sockets > 0 ? ") (sockets " + stats.sockets : "") + (keptLine ? ") (" + keptLine + ")" : ")")); - - if (this.ignoreLog.indexOf(stats.type) === -1) { - Item.logger("Kept", item); - Item.logItem("Kept", item, keptLine); - } - - if (item.identified && item.isInInventory && AutoEquip.wanted(item)) { - ((Item.autoEquipCheck(item) && Item.autoEquip("Field")) || (Item.autoEquipCheckSecondary(item) && Item.autoEquipSecondary("Field"))); - } - - break; - case Pickit.Result.CUBING: - console.log("ÿc7Picked up " + stats.color + stats.name + " ÿc0(ilvl " + stats.ilvl + ")" + " (Cubing)"); - Item.logger("Kept", item, "Cubing " + me.findItems(item.classid).length); - Cubing.update(); - - break; - case Pickit.Result.RUNEWORD: - console.log("ÿc7Picked up " + stats.color + stats.name + " ÿc0(ilvl " + stats.ilvl + ")" + " (Runewords)"); - Item.logger("Kept", item, "Runewords"); - Runewords.update(stats.classid, gid); - - break; - case Pickit.Result.CRAFTING: - console.log("ÿc7Picked up " + stats.color + stats.name + " ÿc0(ilvl " + stats.ilvl + ")" + " (Crafting System)"); - CraftingSystem.update(item); - - break; - case Pickit.Result.SOLOSYSTEM: - console.log("ÿc7Picked up " + stats.color + stats.name + " ÿc0(ilvl " + stats.ilvl + ")" + " (SoloWants System)"); - SoloWants.update(item); - - break; - default: - console.log("ÿc7Picked up " + stats.color + stats.name + " ÿc0(ilvl " + stats.ilvl + (keptLine ? ") (" + keptLine + ")" : ")")); - - break; - } - } + /** + * @constructor + * @param {ItemUnit} unit + */ + function ItemStats (unit) { + let self = this; + self.x = unit.x; + self.y = unit.y; + self.ilvl = unit.ilvl; + self.sockets = unit.sockets; + self.type = unit.itemType; + self.classid = unit.classid; + self.name = unit.name; + self.color = Item.color(unit); + self.gold = unit.getStat(sdk.stats.Gold); + self.dist = (unit.distance || Infinity); + let canTk = (Skill.haveTK && Pickit.tkable.includes(self.type) && Pickit.toCursorPick.indexOf(unit.gid) === -1 + && self.dist > 5 && self.dist < 20 && !checkCollision(me, unit, sdk.collision.WallOrRanged)); + self.useTk = canTk && (me.mpPercent > 50); + self.picked = false; + } + + let item, tick, gid, retry = false; + const itemCount = me.itemcount; + const cancelFlags = [sdk.uiflags.Inventory, sdk.uiflags.NPCMenu, sdk.uiflags.Waypoint, sdk.uiflags.Shop, sdk.uiflags.Stash, sdk.uiflags.Cube]; + + if (!unit || unit === undefined) return false; + + if (unit.gid) { + gid = unit.gid; + item = Game.getItem(-1, -1, gid); + } + + if (!item) return false; + + for (let i = 0; i < cancelFlags.length; i += 1) { + if (getUIFlag(cancelFlags[i])) { + delay(500); + me.cancel(0); + + break; + } + } + + const stats = new ItemStats(item); + const tkMana = stats.useTk ? Skill.getManaCost(sdk.skills.Telekinesis) * 2 : Infinity; + + MainLoop: + for (let i = 0; i < 3; i += 1) { + if (me.dead) return false; + if (!Game.getItem(-1, -1, gid)) { + break; + } + + while (!me.idle) { + delay(40); + } + + if (!item.onGroundOrDropping) { + break; + } + + let itemDist = item.distance; + // todo - allow picking near potions/scrolls while attacking distance < 5 + if (stats.useTk && me.mp > tkMana) { + Packet.telekinesis(item); + } else { + let checkItem = false; + const maxDist = (Config.FastPick || i < 1) ? 8 : 5; + if (item.distance > maxDist || checkCollision(me, item, sdk.collision.BlockWall)) { + let coll = (sdk.collision.BlockWall | sdk.collision.Objects | sdk.collision.ClosedDoor); + + if (!clearBeforePick && me.checkForMobs({ range: 5, coll: coll })) { + continue; + } + + if (clearBeforePick && item.checkForMobs({ range: 8, coll: coll })) { + try { + console.log("ÿc8PickItemÿc0 :: Clearing area around item I want to pick"); + Pickit.enabled = false; // Don't pick while trying to clear + Attack.clearPos(item.x, item.y, 10, false); + } finally { + Pickit.enabled = true; // Reset value + } + } + checkItem = true; + } + + if (checkItem || i > 0) { + if (copyUnit(item).x === undefined || !item.onGroundOrDropping) { + break; + } + if (!Pather.moveNearUnit({ x: stats.x, y: stats.y }, 5)) continue; + } + + let cursorUnit; + itemDist = item.distance; + // use packet first, if we fail and not using fast pick use click + Pickit.toCursorPick.includes(item.gid) + ? Packet.click(item, true) && (cursorUnit = Misc.poll(() => Game.getCursorUnit(), (itemDist > 10 ? 1000 : 250), 50)) && Storage.Inventory.MoveTo(cursorUnit) + : (Config.FastPick || i < 1) ? Packet.click(item) : Misc.click(0, 0, item); + } + + tick = getTickCount(); + + while (getTickCount() - tick < (itemDist > 10 ? 2000 : 1000)) { + item = copyUnit(item); + Pickit.toCursorPick.includes(item.gid) && Pickit.toCursorPick.remove(item.gid); + + if (stats.classid === sdk.items.Gold) { + if (!item.getStat(sdk.stats.Gold) || item.getStat(sdk.stats.Gold) < stats.gold) { + console.log("ÿc7Picked up " + stats.color + (item.getStat(sdk.stats.Gold) ? (item.getStat(sdk.stats.Gold) - stats.gold) : stats.gold) + " " + stats.name); + + return true; + } + } + + if (!item.onGroundOrDropping) { + switch (stats.classid) { + case sdk.items.Key: + console.log("ÿc7Picked up " + stats.color + stats.name + " ÿc7(" + Town.checkKeys() + "/12)"); + + return true; + case sdk.items.ScrollofTownPortal: + case sdk.items.ScrollofIdentify: + console.log("ÿc7Picked up " + stats.color + stats.name + " ÿc7(" + Town.checkScrolls(stats.classid === sdk.items.ScrollofTownPortal ? "tbk" : "ibk") + "/20)"); + + return true; + case sdk.items.Arrows: + case sdk.items.Bolts: + me.needRepair(); + + break; + } + + me.itemoncursor && Storage.Inventory.MoveTo(Game.getCursorUnit()); + + break MainLoop; + } + + delay(20); + } + + // TK failed, disable it + stats.useTk = false; + + //console.log("pick retry"); + } + + if (retry) return this.pickItem(unit, status, keptLine); + + stats.picked = me.itemcount > itemCount || !!me.getItem(-1, -1, gid); + + if (stats.picked) { + DataFile.updateStats("lastArea"); + + switch (status) { + case Pickit.Result.WANTED: + case Pickit.Result.SOLOWANTS: + console.log("ÿc7Picked up " + stats.color + stats.name + " ÿc0(ilvl " + stats.ilvl + (stats.sockets > 0 ? ") (sockets " + stats.sockets : "") + (keptLine ? ") (" + keptLine + ")" : ")")); + + if (this.ignoreLog.indexOf(stats.type) === -1) { + Item.logger("Kept", item); + Item.logItem("Kept", item, keptLine); + } + + if (item.identified && item.isInInventory && AutoEquip.wanted(item)) { + ((Item.autoEquipCheck(item) && Item.autoEquip("Field")) || (Item.autoEquipCheckSecondary(item) && Item.autoEquipSecondary("Field"))); + } + + break; + case Pickit.Result.CUBING: + console.log("ÿc7Picked up " + stats.color + stats.name + " ÿc0(ilvl " + stats.ilvl + ")" + " (Cubing)"); + Item.logger("Kept", item, "Cubing " + me.findItems(item.classid).length); + Cubing.update(); + + break; + case Pickit.Result.RUNEWORD: + console.log("ÿc7Picked up " + stats.color + stats.name + " ÿc0(ilvl " + stats.ilvl + ")" + " (Runewords)"); + Item.logger("Kept", item, "Runewords"); + Runewords.update(stats.classid, gid); + + break; + case Pickit.Result.CRAFTING: + console.log("ÿc7Picked up " + stats.color + stats.name + " ÿc0(ilvl " + stats.ilvl + ")" + " (Crafting System)"); + CraftingSystem.update(item); + + break; + case Pickit.Result.SOLOSYSTEM: + console.log("ÿc7Picked up " + stats.color + stats.name + " ÿc0(ilvl " + stats.ilvl + ")" + " (SoloWants System)"); + SoloWants.update(item); + + break; + default: + console.log("ÿc7Picked up " + stats.color + stats.name + " ÿc0(ilvl " + stats.ilvl + (keptLine ? ") (" + keptLine + ")" : ")")); + + break; + } + } - return true; + return true; }; Pickit.checkSpotForItems = function (spot, checkVsMyDist = false, range = Config.PickRange) { - if (spot.x === undefined) return false; - let itemList = []; - let item = Game.getItem(); - - if (item) { - do { - if (item.onGroundOrDropping && getDistance(spot, item) <= range) { - const spotDist = getDistance(spot, item); - const itemDistFromMe = item.distance; - if (Pickit.essentials.includes(item.itemType)) { - if (Pickit.checkItem(item).result && Pickit.canPick(item) && Pickit.canFit(item)) { - checkVsMyDist && itemDistFromMe < spotDist ? Pickit.essentials.push(copyUnit(item)) : itemList.push(copyUnit(item)); - } - } else if (item.itemType === sdk.items.type.Key) { - if (Pickit.canPick(item) && Pickit.checkItem(item).result) { - checkVsMyDist && itemDistFromMe < spotDist ? Pickit.pickList.push(copyUnit(item)) : itemList.push(copyUnit(item)); - } - } else if (Pickit.checkItem(item).result) { - if (checkVsMyDist && itemDistFromMe < spotDist) { - Pickit.pickList.push(copyUnit(item)); - } else { - return true; - } - } - } - } while (item.getNext()); - } - - return itemList.length > 3; + if (spot.x === undefined) return false; + let itemList = []; + let item = Game.getItem(); + + if (item) { + do { + if (item.onGroundOrDropping && getDistance(spot, item) <= range) { + const spotDist = getDistance(spot, item); + const itemDistFromMe = item.distance; + if (Pickit.essentials.includes(item.itemType)) { + if (Pickit.checkItem(item).result && Pickit.canPick(item) && Pickit.canFit(item)) { + checkVsMyDist && itemDistFromMe < spotDist ? Pickit.essentials.push(copyUnit(item)) : itemList.push(copyUnit(item)); + } + } else if (item.itemType === sdk.items.type.Key) { + if (Pickit.canPick(item) && Pickit.checkItem(item).result) { + checkVsMyDist && itemDistFromMe < spotDist ? Pickit.pickList.push(copyUnit(item)) : itemList.push(copyUnit(item)); + } + } else if (Pickit.checkItem(item).result) { + if (checkVsMyDist && itemDistFromMe < spotDist) { + Pickit.pickList.push(copyUnit(item)); + } else { + return true; + } + } + } + } while (item.getNext()); + } + + return itemList.length > 3; }; Pickit.pickList = []; @@ -610,212 +610,212 @@ Pickit.essentialList = []; // Might need to do a global list so this function and pickItems see the same items to prevent an item from being in both Pickit.essessntialsPick = function (clearBeforePick = false, ignoreGold = false, builtList = [], once = false) { - if (me.dead || me.inTown || (!Pickit.enabled && !clearBeforePick)) return false; - - Pickit.essentialList.concat(builtList, Pickit.pickList).filter(i => !!i && Pickit.essentials.includes(i.itemType)); - let item = Game.getItem(); - const maxDist = Skill.haveTK ? 15 : 5; - - if (item) { - do { - if (item.onGroundOrDropping && getDistance(me, item) <= maxDist && Pickit.essentials.includes(item.itemType)) { - if (Pickit.essentialList.some(el => el.gid === item.gid)) continue; - if (item.itemType !== sdk.items.type.Gold || getDistance(me, item) < 5) { - Pickit.essentialList.push(copyUnit(item)); - } - } - } while (item.getNext()); - } - - if (!Pickit.essentialList.length) return true; - - while (!me.idle) { - delay(40); - } - - while (Pickit.essentialList.length > 0) { - if (me.dead || !Pickit.enabled) return false; - - Pickit.essentialList.sort(this.sortItems); - const currItem = Pickit.essentialList[0]; - - // Check if the item unit is still valid and if it's on ground or being dropped - // Don't pick items behind walls/obstacles when walking - if (copyUnit(currItem).x !== undefined && currItem.onGroundOrDropping - && (Pather.useTeleport() || !checkCollision(me, currItem, sdk.collision.BlockWall))) { - // Check if the item should be picked - let status = this.checkItem(currItem); - - if (status.result && Pickit.canPick(currItem)) { - let canFit = (Storage.Inventory.CanFit(currItem) || Pickit.canFit(currItem)); - - // Field id when our used space is above a certain percent or if we are full try to make room with FieldID - // or if we have an exp shrine so we don't waste it - if ((Config.FieldID.Enabled || me.getState(sdk.states.ShrineExperience)) - && (!canFit || Storage.Inventory.UsedSpacePercent() > Config.FieldID.UsedSpace)) { - me.fieldID() && (canFit = (currItem.gid !== undefined && Storage.Inventory.CanFit(currItem))); - } - - // Try to make room by selling items in town - if (!canFit) { - // Check if any of the current inventory items can be stashed or need to be identified and eventually sold to make room - if (this.canMakeRoom()) { - console.log("ÿc7Trying to make room for " + Item.color(currItem) + currItem.name); - - // Go to town and do town chores - if (Town.visitTown()) { - // Recursive check after going to town. We need to remake item list because gids can change. - // Called only if room can be made so it shouldn't error out or block anything. - return this.essessntialsPick(clearBeforePick, ignoreGold, builtList, once); - } - - // Town visit failed - abort - console.log("ÿc7Unable to make room for " + Item.color(currItem) + currItem.name); - - return false; - } - - // Can't make room - Item.logger("No room for", currItem); - console.log("ÿc7Not enough room for " + Item.color(currItem) + currItem.name); - } - - // Item can fit - pick it up - if (canFit) { - let picked = this.pickItem(currItem, status.result, status.line, clearBeforePick); - if (picked && once) return true; - } - } - } - - Pickit.essentialList.shift(); - } - - return true; + if (me.dead || me.inTown || (!Pickit.enabled && !clearBeforePick)) return false; + + Pickit.essentialList.concat(builtList, Pickit.pickList).filter(i => !!i && Pickit.essentials.includes(i.itemType)); + let item = Game.getItem(); + const maxDist = Skill.haveTK ? 15 : 5; + + if (item) { + do { + if (item.onGroundOrDropping && getDistance(me, item) <= maxDist && Pickit.essentials.includes(item.itemType)) { + if (Pickit.essentialList.some(el => el.gid === item.gid)) continue; + if (item.itemType !== sdk.items.type.Gold || getDistance(me, item) < 5) { + Pickit.essentialList.push(copyUnit(item)); + } + } + } while (item.getNext()); + } + + if (!Pickit.essentialList.length) return true; + + while (!me.idle) { + delay(40); + } + + while (Pickit.essentialList.length > 0) { + if (me.dead || !Pickit.enabled) return false; + + Pickit.essentialList.sort(this.sortItems); + const currItem = Pickit.essentialList[0]; + + // Check if the item unit is still valid and if it's on ground or being dropped + // Don't pick items behind walls/obstacles when walking + if (copyUnit(currItem).x !== undefined && currItem.onGroundOrDropping + && (Pather.useTeleport() || !checkCollision(me, currItem, sdk.collision.BlockWall))) { + // Check if the item should be picked + let status = this.checkItem(currItem); + + if (status.result && Pickit.canPick(currItem)) { + let canFit = (Storage.Inventory.CanFit(currItem) || Pickit.canFit(currItem)); + + // Field id when our used space is above a certain percent or if we are full try to make room with FieldID + // or if we have an exp shrine so we don't waste it + if ((Config.FieldID.Enabled || me.getState(sdk.states.ShrineExperience)) + && (!canFit || Storage.Inventory.UsedSpacePercent() > Config.FieldID.UsedSpace)) { + me.fieldID() && (canFit = (currItem.gid !== undefined && Storage.Inventory.CanFit(currItem))); + } + + // Try to make room by selling items in town + if (!canFit) { + // Check if any of the current inventory items can be stashed or need to be identified and eventually sold to make room + if (this.canMakeRoom()) { + console.log("ÿc7Trying to make room for " + Item.color(currItem) + currItem.name); + + // Go to town and do town chores + if (Town.visitTown()) { + // Recursive check after going to town. We need to remake item list because gids can change. + // Called only if room can be made so it shouldn't error out or block anything. + return this.essessntialsPick(clearBeforePick, ignoreGold, builtList, once); + } + + // Town visit failed - abort + console.log("ÿc7Unable to make room for " + Item.color(currItem) + currItem.name); + + return false; + } + + // Can't make room + Item.logger("No room for", currItem); + console.log("ÿc7Not enough room for " + Item.color(currItem) + currItem.name); + } + + // Item can fit - pick it up + if (canFit) { + let picked = this.pickItem(currItem, status.result, status.line, clearBeforePick); + if (picked && once) return true; + } + } + } + + Pickit.essentialList.shift(); + } + + return true; }; Pickit.pickItems = function (range = Config.PickRange, once = false) { - if (me.dead || range < 0 || !Pickit.enabled) return false; - - let needMule = false; - const canUseMule = AutoMule.getInfo() && AutoMule.getInfo().hasOwnProperty("muleInfo"); - - while (!me.idle) { - delay(40); - } - - let item = Game.getItem(); - - if (item) { - do { - if (Pickit.ignoreList.has(item.gid)) continue; - if (Pickit.pickList.some(el => el.gid === item.gid)) continue; - if (item.onGroundOrDropping && getDistance(me, item) <= range) { - Pickit.pickList.push(copyUnit(item)); - } - } while (item.getNext()); - } - - if (Pickit.pickList.some(i => [sdk.items.type.HealingPotion, sdk.items.type.ManaPotion, sdk.items.type.RejuvPotion].includes(i.itemType))) { - me.clearBelt(); - Pickit.beltSize = Storage.BeltSize(); - } - - while (Pickit.pickList.length > 0) { - if (me.dead || !Pickit.enabled) return false; - Pickit.pickList.sort(this.sortItems); - const currItem = Pickit.pickList[0]; - - if (Pickit.ignoreList.has(currItem.gid)) { - Pickit.pickList.shift(); - - continue; - } - - // Check if the item unit is still valid and if it's on ground or being dropped - // Don't pick items behind walls/obstacles when walking - if (copyUnit(currItem).x !== undefined && currItem.onGroundOrDropping - && (Pather.useTeleport() || me.inTown || !checkCollision(me, currItem, sdk.collision.BlockWall))) { - // Check if the item should be picked - let status = this.checkItem(currItem); - - if (status.result && Pickit.canPick(currItem)) { - let canFit = (Storage.Inventory.CanFit(currItem) || Pickit.canFit(currItem)); - - // Field id when our used space is above a certain percent or if we are full try to make room with FieldID - if (Config.FieldID.Enabled && (!canFit || Storage.Inventory.UsedSpacePercent() > Config.FieldID.UsedSpace)) { - me.fieldID() && (canFit = (currItem.gid !== undefined && Storage.Inventory.CanFit(currItem))); - } - - if (!canFit && !me.checkForMobs({ range: 10 })) { - me.sortInventory(); - canFit = (Storage.Inventory.CanFit(currItem) || Pickit.canFit(currItem)); - } - - // Try to make room by selling items in town - if (!canFit) { - // Check if any of the current inventory items can be stashed or need to be identified and eventually sold to make room - if (this.canMakeRoom()) { - console.log("ÿc7Trying to make room for " + Item.color(currItem) + currItem.name); - - /** - * @todo - * - Try to sort inventory if it is safe to do so - * - Check to see if we can clear up enough buffer potions (exlcuding rejuvs) in order to pick the item - * - If all this fails after visiting town to clear inventory and stash items then globally ignore this item - * and potentially any other items it's size until our used space changes. Only way we should get to this point - * is the use of an additonal pickit file without muling setup. - */ - - // Go to town and do town chores - if (Town.visitTown()) { - // Recursive check after going to town. We need to remake item list because gids can change. - // Called only if room can be made so it shouldn't error out or block anything. - Pickit.ignoreList.clear(); // reset the list of ignored gids - return this.pickItems(range, once); - } - - // Town visit failed - abort - console.warn("Failed to visit town. ÿc7Not enough room for " + Item.color(currItem) + currItem.name); - - return false; - } - - // Can't make room - trigger automule - if (copyUnit(currItem).x !== undefined) { - Item.logger("No room for", currItem); - console.warn("ÿc7Not enough room for " + Item.color(currItem) + currItem.name); - // ignore the item now - Pickit.ignoreList.add(currItem.gid); - needMule = true; - - break; - } - } - - // Item can fit - pick it up - if (canFit) { - let picked = this.pickItem(currItem, status.result, status.line); - if (picked && once) return true; - } - } - } - - Pickit.pickList.shift(); - } - - // Quit current game and transfer the items to mule - if (needMule && canUseMule && AutoMule.getMuleItems().length > 0) { - console.log( - "ÿc7Muling items :: \n" - + "- ÿc7UsedStashSpacePercentÿc0: " + Storage.Stash.UsedSpacePercent() + "\n" - + "- ÿc7UsedInventorySpacePercentÿc0: " + Storage.Inventory.UsedSpacePercent() - ); - scriptBroadcast("mule"); - scriptBroadcast("quit"); - - return false; - } - - return true; + if (me.dead || range < 0 || !Pickit.enabled) return false; + + let needMule = false; + const canUseMule = AutoMule.getInfo() && AutoMule.getInfo().hasOwnProperty("muleInfo"); + + while (!me.idle) { + delay(40); + } + + let item = Game.getItem(); + + if (item) { + do { + if (Pickit.ignoreList.has(item.gid)) continue; + if (Pickit.pickList.some(el => el.gid === item.gid)) continue; + if (item.onGroundOrDropping && getDistance(me, item) <= range) { + Pickit.pickList.push(copyUnit(item)); + } + } while (item.getNext()); + } + + if (Pickit.pickList.some(i => [sdk.items.type.HealingPotion, sdk.items.type.ManaPotion, sdk.items.type.RejuvPotion].includes(i.itemType))) { + me.clearBelt(); + Pickit.beltSize = Storage.BeltSize(); + } + + while (Pickit.pickList.length > 0) { + if (me.dead || !Pickit.enabled) return false; + Pickit.pickList.sort(this.sortItems); + const currItem = Pickit.pickList[0]; + + if (Pickit.ignoreList.has(currItem.gid)) { + Pickit.pickList.shift(); + + continue; + } + + // Check if the item unit is still valid and if it's on ground or being dropped + // Don't pick items behind walls/obstacles when walking + if (copyUnit(currItem).x !== undefined && currItem.onGroundOrDropping + && (Pather.useTeleport() || me.inTown || !checkCollision(me, currItem, sdk.collision.BlockWall))) { + // Check if the item should be picked + let status = this.checkItem(currItem); + + if (status.result && Pickit.canPick(currItem)) { + let canFit = (Storage.Inventory.CanFit(currItem) || Pickit.canFit(currItem)); + + // Field id when our used space is above a certain percent or if we are full try to make room with FieldID + if (Config.FieldID.Enabled && (!canFit || Storage.Inventory.UsedSpacePercent() > Config.FieldID.UsedSpace)) { + me.fieldID() && (canFit = (currItem.gid !== undefined && Storage.Inventory.CanFit(currItem))); + } + + if (!canFit && !me.checkForMobs({ range: 10 })) { + me.sortInventory(); + canFit = (Storage.Inventory.CanFit(currItem) || Pickit.canFit(currItem)); + } + + // Try to make room by selling items in town + if (!canFit) { + // Check if any of the current inventory items can be stashed or need to be identified and eventually sold to make room + if (this.canMakeRoom()) { + console.log("ÿc7Trying to make room for " + Item.color(currItem) + currItem.name); + + /** + * @todo + * - Try to sort inventory if it is safe to do so + * - Check to see if we can clear up enough buffer potions (exlcuding rejuvs) in order to pick the item + * - If all this fails after visiting town to clear inventory and stash items then globally ignore this item + * and potentially any other items it's size until our used space changes. Only way we should get to this point + * is the use of an additonal pickit file without muling setup. + */ + + // Go to town and do town chores + if (Town.visitTown()) { + // Recursive check after going to town. We need to remake item list because gids can change. + // Called only if room can be made so it shouldn't error out or block anything. + Pickit.ignoreList.clear(); // reset the list of ignored gids + return this.pickItems(range, once); + } + + // Town visit failed - abort + console.warn("Failed to visit town. ÿc7Not enough room for " + Item.color(currItem) + currItem.name); + + return false; + } + + // Can't make room - trigger automule + if (copyUnit(currItem).x !== undefined) { + Item.logger("No room for", currItem); + console.warn("ÿc7Not enough room for " + Item.color(currItem) + currItem.name); + // ignore the item now + Pickit.ignoreList.add(currItem.gid); + needMule = true; + + break; + } + } + + // Item can fit - pick it up + if (canFit) { + let picked = this.pickItem(currItem, status.result, status.line); + if (picked && once) return true; + } + } + } + + Pickit.pickList.shift(); + } + + // Quit current game and transfer the items to mule + if (needMule && canUseMule && AutoMule.getMuleItems().length > 0) { + console.log( + "ÿc7Muling items :: \n" + + "- ÿc7UsedStashSpacePercentÿc0: " + Storage.Stash.UsedSpacePercent() + "\n" + + "- ÿc7UsedInventorySpacePercentÿc0: " + Storage.Inventory.UsedSpacePercent() + ); + scriptBroadcast("mule"); + scriptBroadcast("quit"); + + return false; + } + + return true; }; diff --git a/libs/SoloPlay/Functions/Polyfills.js b/libs/SoloPlay/Functions/Polyfills.js index db4dd1a6..84838762 100644 --- a/libs/SoloPlay/Functions/Polyfills.js +++ b/libs/SoloPlay/Functions/Polyfills.js @@ -7,13 +7,13 @@ */ if (!Array.prototype.at) { - Array.prototype.at = function (pos) { - if (pos < 0) { - pos += this.length; - } - if (pos < 0 || pos >= this.length) return undefined; - return this[pos]; - }; + Array.prototype.at = function (pos) { + if (pos < 0) { + pos += this.length; + } + if (pos < 0 || pos >= this.length) return undefined; + return this[pos]; + }; } /** @@ -22,142 +22,142 @@ if (!Array.prototype.at) { */ (function (global, _original) { - let __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); - }) : (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - o[k2] = m[k]; - })); - let __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { - Object.defineProperty(o, "default", { enumerable: true, value: v }); - }) : function(o, v) { - o.default = v; - }); - let __importStar = (this && this.__importStar) || function (mod) { - if (mod && mod.__esModule) return mod; - let result = {}; - if (mod != null) for (let k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); - __setModuleDefault(result, mod); - return result; - }; - let Worker = __importStar(require("../../modules/Worker")); - global._setTimeout = _original; - /** - * @param {function} cb - * @param {number} time - * @param args - * @constructor - */ - function Timer(cb, time, args) { - let _this = this; - if (time === void 0) { time = 0; } - if (args === void 0) { args = []; } - Timer.instances.push(this); - Worker.runInBackground["__setTimeout__" + (Timer.counter++)] = (function (startTick) { - return function () { - let finished = getTickCount() - startTick >= time; - if (finished) { - let index = Timer.instances.indexOf(_this); - // only if not removed from the time list - if (index > -1) { - Timer.instances.splice(index, 1); - cb.apply(undefined, args); - } - } - return !finished; - }; - })(getTickCount()); - } - Timer.instances = []; - Timer.counter = 0; - global.setTimeout = function (cb, time) { - if (time === void 0) { time = 0; } - let args = []; - for (let _i = 2; _i < arguments.length; _i++) { - args[_i - 2] = arguments[_i]; - } - if (typeof cb === "string") { - console.debug("Warning: Do not use raw code @ setTimeout and does not support lexical scoping"); - cb = [].filter.constructor(cb); - } - if (typeof cb !== "function") { - throw new TypeError("setTimeout callback needs to be a function"); - } - return new Timer(cb, time, args); - }; - /** - * - * @param {Timer} timer - */ - global.clearTimeout = function (timer) { - let index = Timer.instances.indexOf(timer); - if (index > -1) { - Timer.instances.splice(index, 1); - } - }; - // getScript(true).name.toString() !== 'default.dbj' && setTimeout(function () {/* test code*/}, 1000) + let __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); + }) : (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + o[k2] = m[k]; + })); + let __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { + Object.defineProperty(o, "default", { enumerable: true, value: v }); + }) : function(o, v) { + o.default = v; + }); + let __importStar = (this && this.__importStar) || function (mod) { + if (mod && mod.__esModule) return mod; + let result = {}; + if (mod != null) for (let k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); + __setModuleDefault(result, mod); + return result; + }; + let Worker = __importStar(require("../../modules/Worker")); + global._setTimeout = _original; + /** + * @param {function} cb + * @param {number} time + * @param args + * @constructor + */ + function Timer(cb, time, args) { + let _this = this; + if (time === void 0) { time = 0; } + if (args === void 0) { args = []; } + Timer.instances.push(this); + Worker.runInBackground["__setTimeout__" + (Timer.counter++)] = (function (startTick) { + return function () { + let finished = getTickCount() - startTick >= time; + if (finished) { + let index = Timer.instances.indexOf(_this); + // only if not removed from the time list + if (index > -1) { + Timer.instances.splice(index, 1); + cb.apply(undefined, args); + } + } + return !finished; + }; + })(getTickCount()); + } + Timer.instances = []; + Timer.counter = 0; + global.setTimeout = function (cb, time) { + if (time === void 0) { time = 0; } + let args = []; + for (let _i = 2; _i < arguments.length; _i++) { + args[_i - 2] = arguments[_i]; + } + if (typeof cb === "string") { + console.debug("Warning: Do not use raw code @ setTimeout and does not support lexical scoping"); + cb = [].filter.constructor(cb); + } + if (typeof cb !== "function") { + throw new TypeError("setTimeout callback needs to be a function"); + } + return new Timer(cb, time, args); + }; + /** + * + * @param {Timer} timer + */ + global.clearTimeout = function (timer) { + let index = Timer.instances.indexOf(timer); + if (index > -1) { + Timer.instances.splice(index, 1); + } + }; + // getScript(true).name.toString() !== 'default.dbj' && setTimeout(function () {/* test code*/}, 1000) })([].filter.constructor("return this")(), setTimeout); if (!Object.setPrototypeOf) { - // Only works in Chrome and FireFox, does not work in IE: - Object.defineProperty(Object.prototype, "setPrototypeOf", { - value: function (obj, proto) { - // @ts-ignore - if (obj.__proto__) { - // @ts-ignore - obj.__proto__ = proto; - return obj; - } else { - // If you want to return prototype of Object.create(null): - let Fn = function () { - for (let key in obj) { - Object.defineProperty(this, key, { - value: obj[key], - }); - } - }; - Fn.prototype = proto; - return new Fn(); - } - }, - enumerable: false, - }); + // Only works in Chrome and FireFox, does not work in IE: + Object.defineProperty(Object.prototype, "setPrototypeOf", { + value: function (obj, proto) { + // @ts-ignore + if (obj.__proto__) { + // @ts-ignore + obj.__proto__ = proto; + return obj; + } else { + // If you want to return prototype of Object.create(null): + let Fn = function () { + for (let key in obj) { + Object.defineProperty(this, key, { + value: obj[key], + }); + } + }; + Fn.prototype = proto; + return new Fn(); + } + }, + enumerable: false, + }); } if (!Object.values) { - Object.values = function (source) { - return Object.keys(source).map(function (k) { return source[k]; }); - }; + Object.values = function (source) { + return Object.keys(source).map(function (k) { return source[k]; }); + }; } if (!Object.entries) { - Object.entries = function (source) { - return Object.keys(source).map(function (k) { return [k, source[k]]; }); - }; + Object.entries = function (source) { + return Object.keys(source).map(function (k) { return [k, source[k]]; }); + }; } // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/is#polyfill // @ts-ignore if (!Object.is) { - Object.defineProperty(Object, "is", { - value: function (x, y) { - // SameValue algorithm - if (x === y) { - // return true if x and y are not 0, OR - // if x and y are both 0 of the same sign. - // This checks for cases 1 and 2 above. - return x !== 0 || 1 / x === 1 / y; - } else { - // return true if both x AND y evaluate to NaN. - // The only possibility for a variable to not be strictly equal to itself - // is when that variable evaluates to NaN (example: Number.NaN, 0/0, NaN). - // This checks for case 3. - // eslint-disable-next-line no-self-compare - return x !== x && y !== y; - } - } - }); + Object.defineProperty(Object, "is", { + value: function (x, y) { + // SameValue algorithm + if (x === y) { + // return true if x and y are not 0, OR + // if x and y are both 0 of the same sign. + // This checks for cases 1 and 2 above. + return x !== 0 || 1 / x === 1 / y; + } else { + // return true if both x AND y evaluate to NaN. + // The only possibility for a variable to not be strictly equal to itself + // is when that variable evaluates to NaN (example: Number.NaN, 0/0, NaN). + // This checks for case 3. + // eslint-disable-next-line no-self-compare + return x !== x && y !== y; + } + } + }); } // if (!Object.fromEntries) { @@ -185,32 +185,32 @@ if (!Object.is) { // https://stackoverflow.com/questions/7837456/how-to-compare-arrays-in-javascript if (!Array.prototype.equals) { - // Warn if overriding existing method - !!Array.prototype.equals && console.warn("Overriding existing Array.prototype.equals. Possible causes: New API defines the method, there's a framework conflict or you've got double inclusions in your code."); - // attach the .equals method to Array's prototype to call it on any array - Array.prototype.equals = function (array) { - // if the other array is a falsy value, return - if (!array) return false; - - // compare lengths - can save a lot of time - if (this.length !== array.length) return false; - - // call basic sort method, (my addition as I don't care if its the same order just if it contains the same values) - this.sort(); - array.sort(); - - for (let i = 0, l = this.length; i < l; i++) { - // Check if we have nested arrays - if (this[i] instanceof Array && array[i] instanceof Array) { - // recurse into the nested arrays - if (!this[i].equals(array[i])) return false; - } else if (this[i] !== array[i]) { - // Warning - two different object instances will never be equal: {x:20} != {x:20} - return false; - } - } - return true; - }; - // Hide method from for-in loops - Object.defineProperty(Array.prototype, "equals", { enumerable: false }); + // Warn if overriding existing method + !!Array.prototype.equals && console.warn("Overriding existing Array.prototype.equals. Possible causes: New API defines the method, there's a framework conflict or you've got double inclusions in your code."); + // attach the .equals method to Array's prototype to call it on any array + Array.prototype.equals = function (array) { + // if the other array is a falsy value, return + if (!array) return false; + + // compare lengths - can save a lot of time + if (this.length !== array.length) return false; + + // call basic sort method, (my addition as I don't care if its the same order just if it contains the same values) + this.sort(); + array.sort(); + + for (let i = 0, l = this.length; i < l; i++) { + // Check if we have nested arrays + if (this[i] instanceof Array && array[i] instanceof Array) { + // recurse into the nested arrays + if (!this[i].equals(array[i])) return false; + } else if (this[i] !== array[i]) { + // Warning - two different object instances will never be equal: {x:20} != {x:20} + return false; + } + } + return true; + }; + // Hide method from for-in loops + Object.defineProperty(Array.prototype, "equals", { enumerable: false }); } diff --git a/libs/SoloPlay/Functions/PrecastOverrides.js b/libs/SoloPlay/Functions/PrecastOverrides.js index 38b0df13..a6df6f4b 100644 --- a/libs/SoloPlay/Functions/PrecastOverrides.js +++ b/libs/SoloPlay/Functions/PrecastOverrides.js @@ -14,134 +14,134 @@ Precast.enabled = true; // Clay Goldem from Stone RW, Iron Golem from Metalgrid, Posion Creeper from Carrior Wind ring, Oak, HoW, or SoB from wisp new Overrides.Override(Precast, Precast.doPrecast, function (orignal, force) { - if (!Precast.enabled) return false; - - switch (me.classid) { - case sdk.player.class.Paladin: - // Force BO 30 seconds before it expires - if (this.haveCTA > -1) { - let forceBo = (force - || (getTickCount() - this.skills.battleOrders.tick >= this.skills.battleOrders.duration - 30000) - || !me.getState(sdk.states.BattleCommand)); - forceBo && this.precastCTA(forceBo); - } - - if (Skill.canUse(sdk.skills.HolyShield) - && Math.round(Skill.getManaCost(sdk.skills.HolyShield) * 100 / me.mpmax) < 35 - && (!me.getState(sdk.states.HolyShield) || force)) { - Precast.cast(sdk.skills.HolyShield); - } - - break; - case sdk.player.class.Barbarian: - let needShout = (Skill.canUse(sdk.skills.Shout) && (force || !me.getState(sdk.states.Shout))); - let needBo = (Skill.canUse(sdk.skills.BattleOrders) && (force || !me.getState(sdk.states.BattleOrders))); - let needBc = (Skill.canUse(sdk.skills.BattleCommand) && (force || !me.getState(sdk.states.BattleCommand))); - - if (needShout || needBo || needBc) { - let primary = Attack.getPrimarySlot(); - let { x, y } = me; - (needBo || needBc) && me.switchWeapons(this.getBetterSlot(sdk.skills.BattleOrders)); - - needBc && Precast.cast(sdk.skills.BattleCommand, x, y, true); - needBo && Precast.cast(sdk.skills.BattleOrders, x, y, true); - needShout && Precast.cast(sdk.skills.Shout, x, y, true); - needBc && Precast.cast(sdk.skills.BattleCommand, x, y, true); - - me.weaponswitch !== primary && me.switchWeapons(primary); - } - - if (CharData.skillData.haveChargedSkill(sdk.skills.Enchant) && !me.getState(sdk.states.Enchant) && me.gold > 500000) { - // Cast enchant - Attack.castCharges(sdk.skills.Enchant, me); - } - - break; - default: - return orignal(force); - } - - me.switchWeapons(Attack.getPrimarySlot()); - - return true; + if (!Precast.enabled) return false; + + switch (me.classid) { + case sdk.player.class.Paladin: + // Force BO 30 seconds before it expires + if (this.haveCTA > -1) { + let forceBo = (force + || (getTickCount() - this.skills.battleOrders.tick >= this.skills.battleOrders.duration - 30000) + || !me.getState(sdk.states.BattleCommand)); + forceBo && this.precastCTA(forceBo); + } + + if (Skill.canUse(sdk.skills.HolyShield) + && Math.round(Skill.getManaCost(sdk.skills.HolyShield) * 100 / me.mpmax) < 35 + && (!me.getState(sdk.states.HolyShield) || force)) { + Precast.cast(sdk.skills.HolyShield); + } + + break; + case sdk.player.class.Barbarian: + let needShout = (Skill.canUse(sdk.skills.Shout) && (force || !me.getState(sdk.states.Shout))); + let needBo = (Skill.canUse(sdk.skills.BattleOrders) && (force || !me.getState(sdk.states.BattleOrders))); + let needBc = (Skill.canUse(sdk.skills.BattleCommand) && (force || !me.getState(sdk.states.BattleCommand))); + + if (needShout || needBo || needBc) { + let primary = Attack.getPrimarySlot(); + let { x, y } = me; + (needBo || needBc) && me.switchWeapons(this.getBetterSlot(sdk.skills.BattleOrders)); + + needBc && Precast.cast(sdk.skills.BattleCommand, x, y, true); + needBo && Precast.cast(sdk.skills.BattleOrders, x, y, true); + needShout && Precast.cast(sdk.skills.Shout, x, y, true); + needBc && Precast.cast(sdk.skills.BattleCommand, x, y, true); + + me.weaponswitch !== primary && me.switchWeapons(primary); + } + + if (CharData.skillData.haveChargedSkill(sdk.skills.Enchant) && !me.getState(sdk.states.Enchant) && me.gold > 500000) { + // Cast enchant + Attack.castCharges(sdk.skills.Enchant, me); + } + + break; + default: + return orignal(force); + } + + me.switchWeapons(Attack.getPrimarySlot()); + + return true; }).apply(); Precast.summon = function (skillId, minionType) { - if (!Skill.canUse(skillId)) return false; - - let rv, retry = 0; - let count = Skill.getMaxSummonCount(skillId); - - while (me.getMinionCount(minionType) < count) { - rv = true; - let coord = CollMap.getRandCoordinate(me.x, -3, 3, me.y, -3, 3); // Get a random coordinate to summon using - let unit = Attack.getNearestMonster({ skipImmune: false }); - - if (unit && [sdk.summons.type.Golem, sdk.summons.type.Grizzly, sdk.summons.type.Shadow].includes(minionType) && unit.distance < 20 && !checkCollision(me, unit, sdk.collision.Ranged)) { - try { - if (Skill.cast(skillId, sdk.skills.hand.Right, unit)) { - if (me.getMinionCount(minionType) === count) { - continue; - } else { - retry++; - } - } else if (Skill.cast(skillId, sdk.skills.hand.Right, me.x, me.y)) { - if (me.getMinionCount(minionType) === count) { - continue; - } else { - retry++; - } - } - } catch (e) { - console.log(e); - } - } - - if (coord && Attack.castableSpot(coord.x, coord.y)) { - Skill.cast(skillId, sdk.skills.hand.Right, coord.x, coord.y); - - if (me.getMinionCount(minionType) === count) { - continue; - } else { - retry++; - } - } else if (Attack.castableSpot(me.x, me.y)) { - Skill.cast(skillId, sdk.skills.hand.Right, me.x, me.y); - - if (me.getMinionCount(minionType) === count) { - continue; - } else { - retry++; - } - } - - if (Skill.getManaCost(skillId) > me.mp && me.getMobCount(15) === 0) { - delay(1000); - retry++; - } - - if (retry > count * 2) { - if (me.inTown) { - if (Town.heal()) { - delay(100 + me.ping); - me.cancel(); - } - - Town.move("portalspot"); - Skill.cast(skillId, sdk.skills.hand.Right, me.x, me.y); - } else { - coord = CollMap.getRandCoordinate(me.x, -6, 6, me.y, -6, 6); - - // Keep bots from getting stuck trying to summon - if (coord && Attack.validSpot(coord.x, coord.y)) { - Pather.moveTo(coord.x, coord.y); - Skill.cast(skillId, sdk.skills.hand.Right, me.x, me.y); - } - } - - retry = 0; - } - } - - return !!rv; + if (!Skill.canUse(skillId)) return false; + + let rv, retry = 0; + let count = Skill.getMaxSummonCount(skillId); + + while (me.getMinionCount(minionType) < count) { + rv = true; + let coord = CollMap.getRandCoordinate(me.x, -3, 3, me.y, -3, 3); // Get a random coordinate to summon using + let unit = Attack.getNearestMonster({ skipImmune: false }); + + if (unit && [sdk.summons.type.Golem, sdk.summons.type.Grizzly, sdk.summons.type.Shadow].includes(minionType) && unit.distance < 20 && !checkCollision(me, unit, sdk.collision.Ranged)) { + try { + if (Skill.cast(skillId, sdk.skills.hand.Right, unit)) { + if (me.getMinionCount(minionType) === count) { + continue; + } else { + retry++; + } + } else if (Skill.cast(skillId, sdk.skills.hand.Right, me.x, me.y)) { + if (me.getMinionCount(minionType) === count) { + continue; + } else { + retry++; + } + } + } catch (e) { + console.log(e); + } + } + + if (coord && Attack.castableSpot(coord.x, coord.y)) { + Skill.cast(skillId, sdk.skills.hand.Right, coord.x, coord.y); + + if (me.getMinionCount(minionType) === count) { + continue; + } else { + retry++; + } + } else if (Attack.castableSpot(me.x, me.y)) { + Skill.cast(skillId, sdk.skills.hand.Right, me.x, me.y); + + if (me.getMinionCount(minionType) === count) { + continue; + } else { + retry++; + } + } + + if (Skill.getManaCost(skillId) > me.mp && me.getMobCount(15) === 0) { + delay(1000); + retry++; + } + + if (retry > count * 2) { + if (me.inTown) { + if (Town.heal()) { + delay(100 + me.ping); + me.cancel(); + } + + Town.move("portalspot"); + Skill.cast(skillId, sdk.skills.hand.Right, me.x, me.y); + } else { + coord = CollMap.getRandCoordinate(me.x, -6, 6, me.y, -6, 6); + + // Keep bots from getting stuck trying to summon + if (coord && Attack.validSpot(coord.x, coord.y)) { + Pather.moveTo(coord.x, coord.y); + Skill.cast(skillId, sdk.skills.hand.Right, me.x, me.y); + } + } + + retry = 0; + } + } + + return !!rv; }; diff --git a/libs/SoloPlay/Functions/PrototypeOverrides.js b/libs/SoloPlay/Functions/PrototypeOverrides.js index cc6155a0..5965b692 100644 --- a/libs/SoloPlay/Functions/PrototypeOverrides.js +++ b/libs/SoloPlay/Functions/PrototypeOverrides.js @@ -14,813 +14,813 @@ includeIfNotIncluded("SoloPlay/Functions/Polyfills.js"); * @description Unit prototypes for soloplay with checks to ensure forwards compatibility */ if (!Unit.prototype.hasOwnProperty("isCharm")) { - Object.defineProperty(Unit.prototype, "isCharm", { - get: function () { - if (this.type !== sdk.unittype.Item) return false; - return [sdk.items.SmallCharm, sdk.items.LargeCharm, sdk.items.GrandCharm].includes(this.classid); - }, - }); + Object.defineProperty(Unit.prototype, "isCharm", { + get: function () { + if (this.type !== sdk.unittype.Item) return false; + return [sdk.items.SmallCharm, sdk.items.LargeCharm, sdk.items.GrandCharm].includes(this.classid); + }, + }); } if (!Unit.prototype.hasOwnProperty("isGem")) { - Object.defineProperty(Unit.prototype, "isGem", { - get: function () { - if (this.type !== sdk.unittype.Item) return false; - return (this.itemType >= sdk.items.type.Amethyst && this.itemType <= sdk.items.type.Skull); - }, - }); + Object.defineProperty(Unit.prototype, "isGem", { + get: function () { + if (this.type !== sdk.unittype.Item) return false; + return (this.itemType >= sdk.items.type.Amethyst && this.itemType <= sdk.items.type.Skull); + }, + }); } if (!Unit.prototype.hasOwnProperty("isInsertable")) { - Object.defineProperty(Unit.prototype, "isInsertable", { - get: function () { - if (this.type !== sdk.unittype.Item) return false; - return [sdk.items.type.Jewel, sdk.items.type.Rune].includes(this.itemType) || this.isGem; - }, - }); + Object.defineProperty(Unit.prototype, "isInsertable", { + get: function () { + if (this.type !== sdk.unittype.Item) return false; + return [sdk.items.type.Jewel, sdk.items.type.Rune].includes(this.itemType) || this.isGem; + }, + }); } if (!Unit.prototype.hasOwnProperty("isRuneword")) { - Object.defineProperty(Unit.prototype, "isRuneword", { - get: function () { - if (this.type !== sdk.unittype.Item) return false; - return !!this.getFlag(sdk.items.flags.Runeword); - }, - }); + Object.defineProperty(Unit.prototype, "isRuneword", { + get: function () { + if (this.type !== sdk.unittype.Item) return false; + return !!this.getFlag(sdk.items.flags.Runeword); + }, + }); } if (!Unit.prototype.hasOwnProperty("isBroken")) { - Object.defineProperty(Unit.prototype, "isBroken", { - get: function () { - if (this.type !== sdk.unittype.Item) return false; - if (this.getStat(sdk.stats.Indestructible)) return false; - return !!this.getFlag(sdk.items.flags.Broken); - }, - }); + Object.defineProperty(Unit.prototype, "isBroken", { + get: function () { + if (this.type !== sdk.unittype.Item) return false; + if (this.getStat(sdk.stats.Indestructible)) return false; + return !!this.getFlag(sdk.items.flags.Broken); + }, + }); } if (!Unit.prototype.hasOwnProperty("isStunned")) { - Object.defineProperty(Unit.prototype, "isStunned", { - get: function () { - return this.getState(sdk.states.Stunned); - }, - }); + Object.defineProperty(Unit.prototype, "isStunned", { + get: function () { + return this.getState(sdk.states.Stunned); + }, + }); } if (!Unit.prototype.hasOwnProperty("isUnderCoS")) { - Object.defineProperty(Unit.prototype, "isUnderCoS", { - get: function () { - return this.getState(sdk.states.Cloaked); - }, - }); + Object.defineProperty(Unit.prototype, "isUnderCoS", { + get: function () { + return this.getState(sdk.states.Cloaked); + }, + }); } if (!Unit.prototype.hasOwnProperty("isUnderLowerRes")) { - Object.defineProperty(Unit.prototype, "isUnderLowerRes", { - get: function () { - return this.getState(sdk.states.LowerResist); - }, - }); + Object.defineProperty(Unit.prototype, "isUnderLowerRes", { + get: function () { + return this.getState(sdk.states.LowerResist); + }, + }); } if (!Unit.prototype.hasOwnProperty("isBaseType")) { - Object.defineProperty(Unit.prototype, "isBaseType", { - get: function () { - if (this.type !== sdk.unittype.Item) return false; - return [sdk.items.quality.Normal, sdk.items.quality.Superior].includes(this.quality) && !this.questItem && !this.isRuneword - && getBaseStat("items", this.classid, "gemsockets") > 0 && [sdk.items.type.Ring, sdk.items.type.Amulet].indexOf(this.itemType) === -1; - } - }); + Object.defineProperty(Unit.prototype, "isBaseType", { + get: function () { + if (this.type !== sdk.unittype.Item) return false; + return [sdk.items.quality.Normal, sdk.items.quality.Superior].includes(this.quality) && !this.questItem && !this.isRuneword + && getBaseStat("items", this.classid, "gemsockets") > 0 && [sdk.items.type.Ring, sdk.items.type.Amulet].indexOf(this.itemType) === -1; + } + }); } if (!Unit.prototype.hasOwnProperty("rawStrength")) { - Object.defineProperty(Unit.prototype, "rawStrength", { - get: function () { - const lvl = this.getStat(sdk.stats.Level); - const rawBonus = (i) => i.getStat(sdk.stats.Strength); - const perLvlBonus = (i) => lvl * i.getStat(sdk.stats.PerLevelStrength) / 8; - const bonus = ~~(this.getItemsEx() - .filter((i) => i.isEquipped || i.isEquippedCharm) - .map((i) => rawBonus(i) + perLvlBonus(i)) - .reduce((acc, v) => acc + v, 0)); - return this.getStat(sdk.stats.Strength) - bonus; - }, - }); + Object.defineProperty(Unit.prototype, "rawStrength", { + get: function () { + const lvl = this.getStat(sdk.stats.Level); + const rawBonus = (i) => i.getStat(sdk.stats.Strength); + const perLvlBonus = (i) => lvl * i.getStat(sdk.stats.PerLevelStrength) / 8; + const bonus = ~~(this.getItemsEx() + .filter((i) => i.isEquipped || i.isEquippedCharm) + .map((i) => rawBonus(i) + perLvlBonus(i)) + .reduce((acc, v) => acc + v, 0)); + return this.getStat(sdk.stats.Strength) - bonus; + }, + }); } if (!Unit.prototype.hasOwnProperty("rawDexterity")) { - Object.defineProperty(Unit.prototype, "rawDexterity", { - get: function () { - const lvl = this.getStat(sdk.stats.Level); - const rawBonus = (i) => i.getStat(sdk.stats.Dexterity); - const perLvlBonus = (i) => lvl * i.getStat(sdk.stats.PerLevelDexterity) / 8; - const bonus = ~~(this.getItemsEx() - .filter((i) => i.isEquipped || i.isEquippedCharm) - .map((i) => rawBonus(i) + perLvlBonus(i)) - .reduce((acc, v) => acc + v, 0)); - return this.getStat(sdk.stats.Dexterity) - bonus; - }, - }); + Object.defineProperty(Unit.prototype, "rawDexterity", { + get: function () { + const lvl = this.getStat(sdk.stats.Level); + const rawBonus = (i) => i.getStat(sdk.stats.Dexterity); + const perLvlBonus = (i) => lvl * i.getStat(sdk.stats.PerLevelDexterity) / 8; + const bonus = ~~(this.getItemsEx() + .filter((i) => i.isEquipped || i.isEquippedCharm) + .map((i) => rawBonus(i) + perLvlBonus(i)) + .reduce((acc, v) => acc + v, 0)); + return this.getStat(sdk.stats.Dexterity) - bonus; + }, + }); } if (!Unit.prototype.hasOwnProperty("upgradedStrReq")) { - Object.defineProperty(Unit.prototype, "upgradedStrReq", { - get: function () { - if (this.type !== sdk.unittype.Item) return false; - let code, id, baseReq, finalReq, ethereal = this.getFlag(sdk.items.flags.Ethereal); - let reqModifier = this.getStat(sdk.stats.ReqPercent); - - switch (this.itemclass) { - case sdk.items.class.Normal: - code = getBaseStat("items", this.classid, "ubercode").trim(); - - break; - case sdk.items.class.Exceptional: - code = getBaseStat("items", this.classid, "ultracode").trim(); - - break; - case sdk.items.class.Elite: - return this.strreq; - } - - id = NTIPAliasClassID[code]; - baseReq = getBaseStat("items", id, "reqstr"); - finalReq = baseReq + Math.floor(baseReq * reqModifier / 100); - ethereal && (finalReq -= 10); - return Math.max(finalReq, 0); - } - }); + Object.defineProperty(Unit.prototype, "upgradedStrReq", { + get: function () { + if (this.type !== sdk.unittype.Item) return false; + let code, id, baseReq, finalReq, ethereal = this.getFlag(sdk.items.flags.Ethereal); + let reqModifier = this.getStat(sdk.stats.ReqPercent); + + switch (this.itemclass) { + case sdk.items.class.Normal: + code = getBaseStat("items", this.classid, "ubercode").trim(); + + break; + case sdk.items.class.Exceptional: + code = getBaseStat("items", this.classid, "ultracode").trim(); + + break; + case sdk.items.class.Elite: + return this.strreq; + } + + id = NTIPAliasClassID[code]; + baseReq = getBaseStat("items", id, "reqstr"); + finalReq = baseReq + Math.floor(baseReq * reqModifier / 100); + ethereal && (finalReq -= 10); + return Math.max(finalReq, 0); + } + }); } if (!Unit.prototype.hasOwnProperty("upgradedDexReq")) { - Object.defineProperty(Unit.prototype, "upgradedDexReq", { - get: function () { - if (this.type !== sdk.unittype.Item) return false; - let code, id, baseReq, finalReq, ethereal = this.getFlag(sdk.items.flags.Ethereal); - let reqModifier = this.getStat(sdk.stats.ReqPercent); - - switch (this.itemclass) { - case sdk.items.class.Normal: - code = getBaseStat("items", this.classid, "ubercode").trim(); - - break; - case sdk.items.class.Exceptional: - code = getBaseStat("items", this.classid, "ultracode").trim(); - - break; - case sdk.items.class.Elite: - return this.dexreq; - } - - id = NTIPAliasClassID[code]; - baseReq = getBaseStat("items", id, "reqdex"); - finalReq = baseReq + Math.floor(baseReq * reqModifier / 100); - ethereal && (finalReq -= 10); - return Math.max(finalReq, 0); - } - }); + Object.defineProperty(Unit.prototype, "upgradedDexReq", { + get: function () { + if (this.type !== sdk.unittype.Item) return false; + let code, id, baseReq, finalReq, ethereal = this.getFlag(sdk.items.flags.Ethereal); + let reqModifier = this.getStat(sdk.stats.ReqPercent); + + switch (this.itemclass) { + case sdk.items.class.Normal: + code = getBaseStat("items", this.classid, "ubercode").trim(); + + break; + case sdk.items.class.Exceptional: + code = getBaseStat("items", this.classid, "ultracode").trim(); + + break; + case sdk.items.class.Elite: + return this.dexreq; + } + + id = NTIPAliasClassID[code]; + baseReq = getBaseStat("items", id, "reqdex"); + finalReq = baseReq + Math.floor(baseReq * reqModifier / 100); + ethereal && (finalReq -= 10); + return Math.max(finalReq, 0); + } + }); } if (!Unit.prototype.hasOwnProperty("upgradedLvlReq")) { - Object.defineProperty(Unit.prototype, "upgradedLvlReq", { - get: function () { - if (this.type !== sdk.unittype.Item) return false; - let code, id; - - switch (this.itemclass) { - case sdk.items.class.Normal: - code = getBaseStat("items", this.classid, "ubercode").trim(); - - break; - case sdk.items.class.Exceptional: - code = getBaseStat("items", this.classid, "ultracode").trim(); - - break; - case sdk.items.class.Elite: - return this.lvlreq; - } - - id = NTIPAliasClassID[code]; - return Math.max(getBaseStat("items", id, "levelreq"), 0); - } - }); + Object.defineProperty(Unit.prototype, "upgradedLvlReq", { + get: function () { + if (this.type !== sdk.unittype.Item) return false; + let code, id; + + switch (this.itemclass) { + case sdk.items.class.Normal: + code = getBaseStat("items", this.classid, "ubercode").trim(); + + break; + case sdk.items.class.Exceptional: + code = getBaseStat("items", this.classid, "ultracode").trim(); + + break; + case sdk.items.class.Elite: + return this.lvlreq; + } + + id = NTIPAliasClassID[code]; + return Math.max(getBaseStat("items", id, "levelreq"), 0); + } + }); } if (!Unit.prototype.hasOwnProperty("allRes")) { - Object.defineProperty(Unit.prototype, "allRes", { - get: function () { - if (this.type !== sdk.unittype.Item) return 0; - let fr = this.getStat(sdk.stats.FireResist); - let cr = this.getStat(sdk.stats.ColdResist); - let lr = this.getStat(sdk.stats.LightningResist); - let pr = this.getStat(sdk.stats.PoisonResist); - return (fr && cr && lr && pr) ? fr : 0; - } - }); + Object.defineProperty(Unit.prototype, "allRes", { + get: function () { + if (this.type !== sdk.unittype.Item) return 0; + let fr = this.getStat(sdk.stats.FireResist); + let cr = this.getStat(sdk.stats.ColdResist); + let lr = this.getStat(sdk.stats.LightningResist); + let pr = this.getStat(sdk.stats.PoisonResist); + return (fr && cr && lr && pr) ? fr : 0; + } + }); } if (!Unit.prototype.hasOwnProperty("prettyPrint")) { - Object.defineProperty(Unit.prototype, "prettyPrint", { - get: function () { - if (this.type !== sdk.unittype.Item) return this.name; - return this.fname.split("\n").reverse().join(" "); - } - }); + Object.defineProperty(Unit.prototype, "prettyPrint", { + get: function () { + if (this.type !== sdk.unittype.Item) return this.name; + return this.fname.split("\n").reverse().join(" "); + } + }); } if (!Unit.prototype.hasOwnProperty("quantityPercent")) { - Object.defineProperty(Unit.prototype, "quantityPercent", { - get: function () { - if (this.type !== sdk.unittype.Item) return 0; - let quantity = this.getStat(sdk.stats.Quantity); - if (!quantity) return 0; - let extraStack = this.getStat(sdk.stats.ExtraStack) || 0; - return ((quantity * 100) / (getBaseStat("items", this.classid, "maxstack") + extraStack)); - } - }); + Object.defineProperty(Unit.prototype, "quantityPercent", { + get: function () { + if (this.type !== sdk.unittype.Item) return 0; + let quantity = this.getStat(sdk.stats.Quantity); + if (!quantity) return 0; + let extraStack = this.getStat(sdk.stats.ExtraStack) || 0; + return ((quantity * 100) / (getBaseStat("items", this.classid, "maxstack") + extraStack)); + } + }); } /** * @param {number} difficulty */ Unit.prototype.getResPenalty = function (difficulty) { - difficulty > 2 && (difficulty = 2); - return me.gametype === sdk.game.gametype.Classic ? [0, 20, 50][difficulty] : [0, 40, 100][difficulty]; + difficulty > 2 && (difficulty = 2); + return me.gametype === sdk.game.gametype.Classic ? [0, 20, 50][difficulty] : [0, 40, 100][difficulty]; }; Unit.prototype.getItemType = function () { - switch (this.itemType) { - case sdk.items.type.Shield: - case sdk.items.type.AuricShields: - case sdk.items.type.VoodooHeads: - return "Shield"; - case sdk.items.type.Armor: - return "Armor"; - case sdk.items.type.Helm: - case sdk.items.type.PrimalHelm: - case sdk.items.type.Circlet: - case sdk.items.type.Pelt: - return "Helmet"; - case sdk.items.type.Scepter: - case sdk.items.type.Wand: - case sdk.items.type.Staff: - case sdk.items.type.Bow: - case sdk.items.type.Axe: - case sdk.items.type.Club: - case sdk.items.type.Sword: - case sdk.items.type.Hammer: - case sdk.items.type.Knife: - case sdk.items.type.Spear: - case sdk.items.type.Polearm: - case sdk.items.type.Crossbow: - case sdk.items.type.Mace: - case sdk.items.type.ThrowingKnife: - case sdk.items.type.ThrowingAxe: - case sdk.items.type.Javelin: - case sdk.items.type.Orb: - case sdk.items.type.AmazonBow: - case sdk.items.type.AmazonSpear: - case sdk.items.type.AmazonJavelin: - case sdk.items.type.MissilePotion: - case sdk.items.type.HandtoHand: - case sdk.items.type.AssassinClaw: - return "Weapon"; - // currently only use this function for socket related things so might as well make non-socketable things return false - // case sdk.items.type.BowQuiver: - // case sdk.items.type.CrossbowQuiver: - // //return "Quiver"; - // case sdk.items.type.Ring: - // //return "Ring"; - // case sdk.items.type.Amulet: - // //return "Amulet"; - // case sdk.items.type.Boots: - // //return "Boots"; - // case sdk.items.type.Gloves: - // //return "Gloves"; - // case sdk.items.type.Belt: - // //return "Belt"; - // default: - // return ""; - } - - return ""; + switch (this.itemType) { + case sdk.items.type.Shield: + case sdk.items.type.AuricShields: + case sdk.items.type.VoodooHeads: + return "Shield"; + case sdk.items.type.Armor: + return "Armor"; + case sdk.items.type.Helm: + case sdk.items.type.PrimalHelm: + case sdk.items.type.Circlet: + case sdk.items.type.Pelt: + return "Helmet"; + case sdk.items.type.Scepter: + case sdk.items.type.Wand: + case sdk.items.type.Staff: + case sdk.items.type.Bow: + case sdk.items.type.Axe: + case sdk.items.type.Club: + case sdk.items.type.Sword: + case sdk.items.type.Hammer: + case sdk.items.type.Knife: + case sdk.items.type.Spear: + case sdk.items.type.Polearm: + case sdk.items.type.Crossbow: + case sdk.items.type.Mace: + case sdk.items.type.ThrowingKnife: + case sdk.items.type.ThrowingAxe: + case sdk.items.type.Javelin: + case sdk.items.type.Orb: + case sdk.items.type.AmazonBow: + case sdk.items.type.AmazonSpear: + case sdk.items.type.AmazonJavelin: + case sdk.items.type.MissilePotion: + case sdk.items.type.HandtoHand: + case sdk.items.type.AssassinClaw: + return "Weapon"; + // currently only use this function for socket related things so might as well make non-socketable things return false + // case sdk.items.type.BowQuiver: + // case sdk.items.type.CrossbowQuiver: + // //return "Quiver"; + // case sdk.items.type.Ring: + // //return "Ring"; + // case sdk.items.type.Amulet: + // //return "Amulet"; + // case sdk.items.type.Boots: + // //return "Boots"; + // case sdk.items.type.Gloves: + // //return "Gloves"; + // case sdk.items.type.Belt: + // //return "Belt"; + // default: + // return ""; + } + + return ""; }; Unit.prototype.castChargedSkillEx = function (...args) { - let skillId, x, y, unit; - - switch (args.length) { - case 0: // item.castChargedSkill() - break; - case 1: - if (args[0] instanceof Unit) { // hellfire.castChargedSkill(monster); - unit = args[0]; - } else { - skillId = args[0]; - } - - break; - case 2: - if (typeof args[0] === "number") { - if (args[1] instanceof Unit) { // me.castChargedSkill(skillId,unit) - [skillId, unit] = [...args]; - } else if (typeof args[1] === "number") { // item.castChargedSkill(x,y) - [x, y] = [...args]; - } - } else { - throw new Error(" invalid arguments, expected (skillId, unit) or (x, y)"); - } - - break; - case 3: - // If all arguments are numbers - if (typeof args[0] === "number" && typeof args[1] === "number" && typeof args[2] === "number") { - [skillId, x, y] = [...args]; - } - - break; - default: - throw new Error("invalid arguments, expected 'me' object or 'item' unit"); - } - - // Charged skills can only be casted on x, y coordinates - unit && ([x, y] = [unit.x, unit.y]); - - if (this !== me && this.type !== sdk.unittype.Item) { - if (Developer.debugging.skills) { - console.debug( - "ÿc9CastChargedSkillÿc0 :: Wierd Error, invalid arguments, expected 'me' object or 'item' unit" + " unit type : " + this.type - ); - } - return false; - } - - // Called the function the unit, me. - if (this === me) { - if (!skillId) throw Error("Must supply skillId on me.castChargedSkill"); - - let chargedItems = []; - - CharData.skillData.chargedSkills.forEach(chargeSkill => { - if (chargeSkill.skill === skillId) { - console.debug(chargeSkill); - let item = me.getItem(-1, sdk.items.mode.Equipped, chargeSkill.gid); - !!item && chargedItems.push({ - charge: chargeSkill.skill, - level: chargeSkill.level, - item: item - }); - } - }); - - if (chargedItems.length === 0) { - console.log("ÿc9CastChargedSkillÿc0 :: Don't have the charged skill (" + skillId + "), or not enough charges"); - return false; - } - - let chargedItem = chargedItems - .sort((a, b) => a.charge.level - b.charge.level) - .first().item; - - // Check if item with charges is equipped on the switch spot - me.weaponswitch === 0 && chargedItem.isOnSwap && me.switchWeapons(1); - - return chargedItem.castChargedSkillEx.apply(chargedItem, args); - } else if (this.type === sdk.unittype.Item) { - let charge = this.getStat(-2)[sdk.stats.ChargedSkill]; // WARNING. Somehow this gives duplicates - - if (!charge) { - console.warn("ÿc9CastChargedSkillÿc0 :: No charged skill on this item"); - return false; - } - - if (skillId) { - if (charge instanceof Array) { - // Filter out all other charged skills - charge = charge - .filter(item => (item && item.skill === skillId) && !!item.charges) - .first(); - } else { - if (charge.skill !== skillId || !charge.charges) { - console.warn("No charges matching skillId"); - charge = false; - } - } - } else if (charge.length > 1) { - throw new Error("multiple charges on this item without a given skillId"); - } - - if (charge) { - const usePacket = ([ - sdk.skills.Valkyrie, sdk.skills.Decoy, sdk.skills.RaiseSkeleton, sdk.skills.ClayGolem, - sdk.skills.RaiseSkeletalMage, sdk.skills.BloodGolem, sdk.skills.Shout, - sdk.skills.IronGolem, sdk.skills.Revive, sdk.skills.Werewolf, sdk.skills.Werebear, - sdk.skills.OakSage, sdk.skills.SpiritWolf, sdk.skills.PoisonCreeper, sdk.skills.BattleOrders, - sdk.skills.SummonDireWolf, sdk.skills.Grizzly, sdk.skills.HeartofWolverine, sdk.skills.SpiritofBarbs, - sdk.skills.ShadowMaster, sdk.skills.ShadowWarrior, sdk.skills.BattleCommand, - ].indexOf(skillId) === -1); - - if (!usePacket) { - return Skill.cast(skillId, sdk.skills.hand.Right, x || me.x, y || me.y, this); // Non packet casting - } - - // Packet casting - // Setting skill on hand - new PacketBuilder() - .byte(sdk.packets.send.SelectSkill) - .word(charge.skill) - .byte(0x00) - .byte(0x00) - .dword(this.gid) - .send(); - // console.log("Set charge skill " + charge.skill + " on hand"); - // No need for a delay, since its TCP, the server recv's the next statement always after the send cast skill packet - - // Cast the skill - new PacketBuilder() - .byte(sdk.packets.send.RightSkillOnLocation) - .word(x || me.x) - .word(y || me.y) - .send(); - console.log("Cast charge skill " + charge.skill); - // The result of "successfully" casted is different, so we cant wait for it here. We have to assume it worked - - return true; - } - } - - return false; + let skillId, x, y, unit; + + switch (args.length) { + case 0: // item.castChargedSkill() + break; + case 1: + if (args[0] instanceof Unit) { // hellfire.castChargedSkill(monster); + unit = args[0]; + } else { + skillId = args[0]; + } + + break; + case 2: + if (typeof args[0] === "number") { + if (args[1] instanceof Unit) { // me.castChargedSkill(skillId,unit) + [skillId, unit] = [...args]; + } else if (typeof args[1] === "number") { // item.castChargedSkill(x,y) + [x, y] = [...args]; + } + } else { + throw new Error(" invalid arguments, expected (skillId, unit) or (x, y)"); + } + + break; + case 3: + // If all arguments are numbers + if (typeof args[0] === "number" && typeof args[1] === "number" && typeof args[2] === "number") { + [skillId, x, y] = [...args]; + } + + break; + default: + throw new Error("invalid arguments, expected 'me' object or 'item' unit"); + } + + // Charged skills can only be casted on x, y coordinates + unit && ([x, y] = [unit.x, unit.y]); + + if (this !== me && this.type !== sdk.unittype.Item) { + if (Developer.debugging.skills) { + console.debug( + "ÿc9CastChargedSkillÿc0 :: Wierd Error, invalid arguments, expected 'me' object or 'item' unit" + " unit type : " + this.type + ); + } + return false; + } + + // Called the function the unit, me. + if (this === me) { + if (!skillId) throw Error("Must supply skillId on me.castChargedSkill"); + + let chargedItems = []; + + CharData.skillData.chargedSkills.forEach(chargeSkill => { + if (chargeSkill.skill === skillId) { + console.debug(chargeSkill); + let item = me.getItem(-1, sdk.items.mode.Equipped, chargeSkill.gid); + !!item && chargedItems.push({ + charge: chargeSkill.skill, + level: chargeSkill.level, + item: item + }); + } + }); + + if (chargedItems.length === 0) { + console.log("ÿc9CastChargedSkillÿc0 :: Don't have the charged skill (" + skillId + "), or not enough charges"); + return false; + } + + let chargedItem = chargedItems + .sort((a, b) => a.charge.level - b.charge.level) + .first().item; + + // Check if item with charges is equipped on the switch spot + me.weaponswitch === 0 && chargedItem.isOnSwap && me.switchWeapons(1); + + return chargedItem.castChargedSkillEx.apply(chargedItem, args); + } else if (this.type === sdk.unittype.Item) { + let charge = this.getStat(-2)[sdk.stats.ChargedSkill]; // WARNING. Somehow this gives duplicates + + if (!charge) { + console.warn("ÿc9CastChargedSkillÿc0 :: No charged skill on this item"); + return false; + } + + if (skillId) { + if (charge instanceof Array) { + // Filter out all other charged skills + charge = charge + .filter(item => (item && item.skill === skillId) && !!item.charges) + .first(); + } else { + if (charge.skill !== skillId || !charge.charges) { + console.warn("No charges matching skillId"); + charge = false; + } + } + } else if (charge.length > 1) { + throw new Error("multiple charges on this item without a given skillId"); + } + + if (charge) { + const usePacket = ([ + sdk.skills.Valkyrie, sdk.skills.Decoy, sdk.skills.RaiseSkeleton, sdk.skills.ClayGolem, + sdk.skills.RaiseSkeletalMage, sdk.skills.BloodGolem, sdk.skills.Shout, + sdk.skills.IronGolem, sdk.skills.Revive, sdk.skills.Werewolf, sdk.skills.Werebear, + sdk.skills.OakSage, sdk.skills.SpiritWolf, sdk.skills.PoisonCreeper, sdk.skills.BattleOrders, + sdk.skills.SummonDireWolf, sdk.skills.Grizzly, sdk.skills.HeartofWolverine, sdk.skills.SpiritofBarbs, + sdk.skills.ShadowMaster, sdk.skills.ShadowWarrior, sdk.skills.BattleCommand, + ].indexOf(skillId) === -1); + + if (!usePacket) { + return Skill.cast(skillId, sdk.skills.hand.Right, x || me.x, y || me.y, this); // Non packet casting + } + + // Packet casting + // Setting skill on hand + new PacketBuilder() + .byte(sdk.packets.send.SelectSkill) + .word(charge.skill) + .byte(0x00) + .byte(0x00) + .dword(this.gid) + .send(); + // console.log("Set charge skill " + charge.skill + " on hand"); + // No need for a delay, since its TCP, the server recv's the next statement always after the send cast skill packet + + // Cast the skill + new PacketBuilder() + .byte(sdk.packets.send.RightSkillOnLocation) + .word(x || me.x) + .word(y || me.y) + .send(); + console.log("Cast charge skill " + charge.skill); + // The result of "successfully" casted is different, so we cant wait for it here. We have to assume it worked + + return true; + } + } + + return false; }; Unit.prototype.castSwitchChargedSkill = function (...args) { - let skillId, x, y, unit; - - switch (args.length) { - case 0: // item.castChargedSkill() - case 1: // hellfire.castChargedSkill(monster); - break; - case 2: - if (typeof args[0] === "number") { - if (args[1] instanceof Unit) { - // me.castChargedSkill(skillId, unit) - [skillId, unit] = [...args]; - } else if (typeof args[1] === "number") { - // item.castChargedSkill(x, y) - [x, y] = [...args]; - } - } else { - throw new Error(" invalid arguments, expected (skillId, unit) or (x, y)"); - } - - break; - case 3: // If all arguments are numbers - if (typeof args[0] === "number" && typeof args[1] === "number" && typeof args[2] === "number") { - [skillId, x, y] = [...args]; - } - - break; - default: - throw new Error("invalid arguments, expected 'me' object"); - } - - if (this !== me) throw Error("invalid arguments, expected 'me' object"); - - // Charged skills can only be casted on x, y coordinates - unit && ([x, y] = [unit.x, unit.y]); - - if (x === undefined || y === undefined) return false; - - // Called the function the unit, me. - if (this === me) { - if (!skillId) throw Error("Must supply skillId on me.castChargedSkill"); - - /** @type {{ charge: number, level: number, item: ItemUnit }[]} */ - let chargedItems = []; - - CharData.skillData.chargedSkillsOnSwitch.forEach(chargeSkill => { - if (chargeSkill.skill === skillId) { - console.debug(chargeSkill); - let item = me.getItem(-1, sdk.items.mode.Equipped, chargeSkill.gid); - !!item && chargedItems.push({ - charge: chargeSkill.skill, - level: chargeSkill.level, - item: item - }); - } - }); - - if (chargedItems.length === 0) { - console.log("ÿc9SwitchCastChargedSkillÿc0 :: Don't have the charged skill (" + skillId + "), or not enough charges"); - return false; - } - - me.weaponswitch === 0 && me.switchWeapons(1); - - let chargedItem = chargedItems - .sort((a, b) => a.charge.level - b.charge.level) - .first().item; - return chargedItem.castChargedSkillEx.apply(chargedItem, args); - } - - return false; + let skillId, x, y, unit; + + switch (args.length) { + case 0: // item.castChargedSkill() + case 1: // hellfire.castChargedSkill(monster); + break; + case 2: + if (typeof args[0] === "number") { + if (args[1] instanceof Unit) { + // me.castChargedSkill(skillId, unit) + [skillId, unit] = [...args]; + } else if (typeof args[1] === "number") { + // item.castChargedSkill(x, y) + [x, y] = [...args]; + } + } else { + throw new Error(" invalid arguments, expected (skillId, unit) or (x, y)"); + } + + break; + case 3: // If all arguments are numbers + if (typeof args[0] === "number" && typeof args[1] === "number" && typeof args[2] === "number") { + [skillId, x, y] = [...args]; + } + + break; + default: + throw new Error("invalid arguments, expected 'me' object"); + } + + if (this !== me) throw Error("invalid arguments, expected 'me' object"); + + // Charged skills can only be casted on x, y coordinates + unit && ([x, y] = [unit.x, unit.y]); + + if (x === undefined || y === undefined) return false; + + // Called the function the unit, me. + if (this === me) { + if (!skillId) throw Error("Must supply skillId on me.castChargedSkill"); + + /** @type {{ charge: number, level: number, item: ItemUnit }[]} */ + let chargedItems = []; + + CharData.skillData.chargedSkillsOnSwitch.forEach(chargeSkill => { + if (chargeSkill.skill === skillId) { + console.debug(chargeSkill); + let item = me.getItem(-1, sdk.items.mode.Equipped, chargeSkill.gid); + !!item && chargedItems.push({ + charge: chargeSkill.skill, + level: chargeSkill.level, + item: item + }); + } + }); + + if (chargedItems.length === 0) { + console.log("ÿc9SwitchCastChargedSkillÿc0 :: Don't have the charged skill (" + skillId + "), or not enough charges"); + return false; + } + + me.weaponswitch === 0 && me.switchWeapons(1); + + let chargedItem = chargedItems + .sort((a, b) => a.charge.level - b.charge.level) + .first().item; + return chargedItem.castChargedSkillEx.apply(chargedItem, args); + } + + return false; }; Unit.prototype.getStatEx = function (id, subid) { - let i, temp, rval, regex; - - switch (id) { - case sdk.stats.AllRes: //calculates all res, doesnt exists trough - { // Block scope due to the variable declaration - // Get all res - let allres = [ - this.getStatEx(sdk.stats.FireResist), - this.getStatEx(sdk.stats.ColdResist), - this.getStatEx(sdk.stats.LightningResist), - this.getStatEx(sdk.stats.PoisonResist) - ]; - - // What is the minimum of the 4? - let min = Math.min.apply(null, allres); - - // Cap all res to the minimum amount of res - allres = allres.map(res => res > min ? min : res); - - // Get it in local variables, its more easy to read - let [fire, cold, light, psn] = allres; - - return fire === cold && cold === light && light === psn ? min : 0; - } - case sdk.stats.MaxMana: - rval = this.getStat(sdk.stats.MaxMana); - - if (rval > 446) { - return rval - 16777216; // Fix for negative values (Gull knife) - } - - return rval; - case sdk.stats.ToBlock: - switch (this.classid) { - case sdk.items.Buckler: - return this.getStat(sdk.stats.ToBlock); - case sdk.items.PreservedHead: - case sdk.items.MummifiedTrophy: - case sdk.items.MinionSkull: - return this.getStat(sdk.stats.ToBlock) - 3; - case sdk.items.SmallShield: - case sdk.items.ZombieHead: - case sdk.items.FetishTrophy: - case sdk.items.HellspawnSkull: - return this.getStat(sdk.stats.ToBlock) - 5; - case sdk.items.KiteShield: - case sdk.items.UnravellerHead: - case sdk.items.SextonTrophy: - case sdk.items.OverseerSkull: - return this.getStat(sdk.stats.ToBlock) - 8; - case sdk.items.SpikedShield: - case sdk.items.Defender: - case sdk.items.GargoyleHead: - case sdk.items.CantorTrophy: - case sdk.items.SuccubusSkull: - case sdk.items.Targe: - case sdk.items.AkaranTarge: - return this.getStat(sdk.stats.ToBlock) - 10; - case sdk.items.LargeShield: - case sdk.items.RoundShield: - case sdk.items.DemonHead: - case sdk.items.HierophantTrophy: - case sdk.items.BloodlordSkull: - return this.getStat(sdk.stats.ToBlock) - 12; - case sdk.items.Scutum: - return this.getStat(sdk.stats.ToBlock) - 14; - case sdk.items.Rondache: - case sdk.items.AkaranRondache: - return this.getStat(sdk.stats.ToBlock) - 15; - case sdk.items.GothicShield: - case sdk.items.AncientShield: - return this.getStat(sdk.stats.ToBlock) - 16; - case sdk.items.BarbedShield: - return this.getStat(sdk.stats.ToBlock) - 17; - case sdk.items.DragonShield: - return this.getStat(sdk.stats.ToBlock) - 18; - case sdk.items.VortexShield: - return this.getStat(sdk.stats.ToBlock) - 19; - case sdk.items.BoneShield: - case sdk.items.GrimShield: - case sdk.items.Luna: - case sdk.items.BladeBarrier: - case sdk.items.TrollNest: - case sdk.items.HeraldicShield: - case sdk.items.ProtectorShield: - return this.getStat(sdk.stats.ToBlock) - 20; - case sdk.items.Heater: - case sdk.items.Monarch: - case sdk.items.AerinShield: - case sdk.items.GildedShield: - case sdk.items.ZakarumShield: - return this.getStat(sdk.stats.ToBlock) - 22; - case sdk.items.TowerShield: - case sdk.items.Pavise: - case sdk.items.Hyperion: - case sdk.items.Aegis: - case sdk.items.Ward: - return this.getStat(sdk.stats.ToBlock) - 24; - case sdk.items.CrownShield: - case sdk.items.RoyalShield: - case sdk.items.KurastShield: - return this.getStat(sdk.stats.ToBlock) - 25; - case sdk.items.SacredRondache: - return this.getStat(sdk.stats.ToBlock) - 28; - case sdk.items.SacredTarge: - return this.getStat(sdk.stats.ToBlock) - 30; - } - - break; - case sdk.stats.MinDamage: - case sdk.stats.MaxDamage: - if (subid === 1) { - temp = this.getStat(-1); - rval = 0; - - for (i = 0; i < temp.length; i += 1) { - switch (temp[i][0]) { - case id: // plus one handed dmg - case id + 2: // plus two handed dmg - // There are 2 occurrences of min/max if the item has +damage. Total damage is the sum of both. - // First occurrence is +damage, second is base item damage. - - if (rval) { // First occurence stored, return if the second one exists - return rval; - } - - if (this.getStat(temp[i][0]) > 0 && this.getStat(temp[i][0]) > temp[i][2]) { - rval = temp[i][2]; // Store the potential +dmg value - } - - break; - } - } - - return 0; - } - - break; - case sdk.stats.Defense: - if (subid === 0) { - if ([0, 1].indexOf(this.mode) < 0) { - break; - } - - switch (this.itemType) { - case sdk.items.type.Jewel: - case sdk.items.type.SmallCharm: - case sdk.items.type.LargeCharm: - case sdk.items.type.GrandCharm: - // defense is the same as plusdefense for these items - return this.getStat(sdk.stats.Defense); - } - - !this.desc && (this.desc = this.description); - - if (this.desc) { - temp = this.desc.split("\n"); - regex = new RegExp("\\+\\d+ " + getLocaleString(sdk.locale.text.Defense).replace(/^\s+|\s+$/g, "")); - - for (let i = 0; i < temp.length; i += 1) { - if (temp[i].match(regex, "i")) { - return parseInt(temp[i].replace(/ÿc[0-9!"+<;.*]/, ""), 10); - } - } - } - - return 0; - } - - break; - case sdk.stats.PoisonMinDamage: - if (subid === 1) { - return Math.round(this.getStat(sdk.stats.PoisonMinDamage) * this.getStat(sdk.stats.PoisonLength) / 256); - } - - break; - case sdk.stats.AddClassSkills: - if (subid === undefined) { - for (let i = 0; i < 7; i += 1) { - let cSkill = this.getStat(sdk.stats.AddClassSkills, i); - if (cSkill) return cSkill; - } - - return 0; - } - - break; - case sdk.stats.AddSkillTab: - if (subid === undefined) { - temp = Object.values(sdk.skills.tabs); - - for (let i = 0; i < temp.length; i += 1) { - let sTab = this.getStat(sdk.stats.AddSkillTab, temp[i]); - if (sTab) return sTab; - } - - return 0; - } - - break; - case sdk.stats.SkillOnAttack: - case sdk.stats.SkillOnKill: - case sdk.stats.SkillOnDeath: - case sdk.stats.SkillOnStrike: - case sdk.stats.SkillOnLevelUp: - case sdk.stats.SkillWhenStruck: - case sdk.stats.ChargedSkill: - if (subid === 1) { - temp = this.getStat(-2); - - if (temp.hasOwnProperty(id)) { - if (temp[id] instanceof Array) { - for (i = 0; i < temp[id].length; i += 1) { - // fix reference to undefined property temp[id][i].skill. - if (temp[id][i] !== undefined && temp[id][i].skill !== undefined) { - return temp[id][i].skill; - } - } - } else { - return temp[id].skill; - } - } - - return 0; - } - - if (subid === 2) { - temp = this.getStat(-2); - - if (temp.hasOwnProperty(id)) { - if (temp[id] instanceof Array) { - for (i = 0; i < temp[id].length; i += 1) { - if (temp[id][i] !== undefined) { - return temp[id][i].level; - } - } - } else { - return temp[id].level; - } - } - - return 0; - } - - break; - case sdk.stats.PerLevelHp: // (for example Fortitude with hp per lvl can be defined now with 1.5) - return this.getStat(sdk.stats.PerLevelHp) / 2048; - } - - if (this.getFlag(sdk.items.flags.Runeword)) { - switch (id) { - case sdk.stats.ArmorPercent: - if ([0, 1].indexOf(this.mode) < 0) { - break; - } - - this.desc === undefined && (this.desc = this.description); - - if (this.desc) { - temp = !!this.desc ? this.desc.split("\n") : ""; - - for (let i = 0; i < temp.length; i += 1) { - if (temp[i].match(getLocaleString(sdk.locale.text.EnhancedDefense).replace(/^\s+|\s+$/g, ""), "i")) { - return parseInt(temp[i].replace(/ÿc[0-9!"+<;.*]/, ""), 10); - } - } - } - - return 0; - case sdk.stats.EnhancedDamage: - if ([0, 1].indexOf(this.mode) < 0) { - break; - } - - this.desc === undefined && (this.desc = this.description); - - if (this.desc) { - temp = !!this.desc ? this.desc.split("\n") : ""; - - for (let i = 0; i < temp.length; i += 1) { - if (temp[i].match(getLocaleString(sdk.locale.text.EnhancedDamage).replace(/^\s+|\s+$/g, ""), "i")) { - return parseInt(temp[i].replace(/ÿc[0-9!"+<;.*]/, ""), 10); - } - } - } - - return 0; - } - } - - if (subid === undefined) { - return this.getStat(id); - } - - return this.getStat(id, subid); + let i, temp, rval, regex; + + switch (id) { + case sdk.stats.AllRes: //calculates all res, doesnt exists trough + { // Block scope due to the variable declaration + // Get all res + let allres = [ + this.getStatEx(sdk.stats.FireResist), + this.getStatEx(sdk.stats.ColdResist), + this.getStatEx(sdk.stats.LightningResist), + this.getStatEx(sdk.stats.PoisonResist) + ]; + + // What is the minimum of the 4? + let min = Math.min.apply(null, allres); + + // Cap all res to the minimum amount of res + allres = allres.map(res => res > min ? min : res); + + // Get it in local variables, its more easy to read + let [fire, cold, light, psn] = allres; + + return fire === cold && cold === light && light === psn ? min : 0; + } + case sdk.stats.MaxMana: + rval = this.getStat(sdk.stats.MaxMana); + + if (rval > 446) { + return rval - 16777216; // Fix for negative values (Gull knife) + } + + return rval; + case sdk.stats.ToBlock: + switch (this.classid) { + case sdk.items.Buckler: + return this.getStat(sdk.stats.ToBlock); + case sdk.items.PreservedHead: + case sdk.items.MummifiedTrophy: + case sdk.items.MinionSkull: + return this.getStat(sdk.stats.ToBlock) - 3; + case sdk.items.SmallShield: + case sdk.items.ZombieHead: + case sdk.items.FetishTrophy: + case sdk.items.HellspawnSkull: + return this.getStat(sdk.stats.ToBlock) - 5; + case sdk.items.KiteShield: + case sdk.items.UnravellerHead: + case sdk.items.SextonTrophy: + case sdk.items.OverseerSkull: + return this.getStat(sdk.stats.ToBlock) - 8; + case sdk.items.SpikedShield: + case sdk.items.Defender: + case sdk.items.GargoyleHead: + case sdk.items.CantorTrophy: + case sdk.items.SuccubusSkull: + case sdk.items.Targe: + case sdk.items.AkaranTarge: + return this.getStat(sdk.stats.ToBlock) - 10; + case sdk.items.LargeShield: + case sdk.items.RoundShield: + case sdk.items.DemonHead: + case sdk.items.HierophantTrophy: + case sdk.items.BloodlordSkull: + return this.getStat(sdk.stats.ToBlock) - 12; + case sdk.items.Scutum: + return this.getStat(sdk.stats.ToBlock) - 14; + case sdk.items.Rondache: + case sdk.items.AkaranRondache: + return this.getStat(sdk.stats.ToBlock) - 15; + case sdk.items.GothicShield: + case sdk.items.AncientShield: + return this.getStat(sdk.stats.ToBlock) - 16; + case sdk.items.BarbedShield: + return this.getStat(sdk.stats.ToBlock) - 17; + case sdk.items.DragonShield: + return this.getStat(sdk.stats.ToBlock) - 18; + case sdk.items.VortexShield: + return this.getStat(sdk.stats.ToBlock) - 19; + case sdk.items.BoneShield: + case sdk.items.GrimShield: + case sdk.items.Luna: + case sdk.items.BladeBarrier: + case sdk.items.TrollNest: + case sdk.items.HeraldicShield: + case sdk.items.ProtectorShield: + return this.getStat(sdk.stats.ToBlock) - 20; + case sdk.items.Heater: + case sdk.items.Monarch: + case sdk.items.AerinShield: + case sdk.items.GildedShield: + case sdk.items.ZakarumShield: + return this.getStat(sdk.stats.ToBlock) - 22; + case sdk.items.TowerShield: + case sdk.items.Pavise: + case sdk.items.Hyperion: + case sdk.items.Aegis: + case sdk.items.Ward: + return this.getStat(sdk.stats.ToBlock) - 24; + case sdk.items.CrownShield: + case sdk.items.RoyalShield: + case sdk.items.KurastShield: + return this.getStat(sdk.stats.ToBlock) - 25; + case sdk.items.SacredRondache: + return this.getStat(sdk.stats.ToBlock) - 28; + case sdk.items.SacredTarge: + return this.getStat(sdk.stats.ToBlock) - 30; + } + + break; + case sdk.stats.MinDamage: + case sdk.stats.MaxDamage: + if (subid === 1) { + temp = this.getStat(-1); + rval = 0; + + for (i = 0; i < temp.length; i += 1) { + switch (temp[i][0]) { + case id: // plus one handed dmg + case id + 2: // plus two handed dmg + // There are 2 occurrences of min/max if the item has +damage. Total damage is the sum of both. + // First occurrence is +damage, second is base item damage. + + if (rval) { // First occurence stored, return if the second one exists + return rval; + } + + if (this.getStat(temp[i][0]) > 0 && this.getStat(temp[i][0]) > temp[i][2]) { + rval = temp[i][2]; // Store the potential +dmg value + } + + break; + } + } + + return 0; + } + + break; + case sdk.stats.Defense: + if (subid === 0) { + if ([0, 1].indexOf(this.mode) < 0) { + break; + } + + switch (this.itemType) { + case sdk.items.type.Jewel: + case sdk.items.type.SmallCharm: + case sdk.items.type.LargeCharm: + case sdk.items.type.GrandCharm: + // defense is the same as plusdefense for these items + return this.getStat(sdk.stats.Defense); + } + + !this.desc && (this.desc = this.description); + + if (this.desc) { + temp = this.desc.split("\n"); + regex = new RegExp("\\+\\d+ " + getLocaleString(sdk.locale.text.Defense).replace(/^\s+|\s+$/g, "")); + + for (let i = 0; i < temp.length; i += 1) { + if (temp[i].match(regex, "i")) { + return parseInt(temp[i].replace(/ÿc[0-9!"+<;.*]/, ""), 10); + } + } + } + + return 0; + } + + break; + case sdk.stats.PoisonMinDamage: + if (subid === 1) { + return Math.round(this.getStat(sdk.stats.PoisonMinDamage) * this.getStat(sdk.stats.PoisonLength) / 256); + } + + break; + case sdk.stats.AddClassSkills: + if (subid === undefined) { + for (let i = 0; i < 7; i += 1) { + let cSkill = this.getStat(sdk.stats.AddClassSkills, i); + if (cSkill) return cSkill; + } + + return 0; + } + + break; + case sdk.stats.AddSkillTab: + if (subid === undefined) { + temp = Object.values(sdk.skills.tabs); + + for (let i = 0; i < temp.length; i += 1) { + let sTab = this.getStat(sdk.stats.AddSkillTab, temp[i]); + if (sTab) return sTab; + } + + return 0; + } + + break; + case sdk.stats.SkillOnAttack: + case sdk.stats.SkillOnKill: + case sdk.stats.SkillOnDeath: + case sdk.stats.SkillOnStrike: + case sdk.stats.SkillOnLevelUp: + case sdk.stats.SkillWhenStruck: + case sdk.stats.ChargedSkill: + if (subid === 1) { + temp = this.getStat(-2); + + if (temp.hasOwnProperty(id)) { + if (temp[id] instanceof Array) { + for (i = 0; i < temp[id].length; i += 1) { + // fix reference to undefined property temp[id][i].skill. + if (temp[id][i] !== undefined && temp[id][i].skill !== undefined) { + return temp[id][i].skill; + } + } + } else { + return temp[id].skill; + } + } + + return 0; + } + + if (subid === 2) { + temp = this.getStat(-2); + + if (temp.hasOwnProperty(id)) { + if (temp[id] instanceof Array) { + for (i = 0; i < temp[id].length; i += 1) { + if (temp[id][i] !== undefined) { + return temp[id][i].level; + } + } + } else { + return temp[id].level; + } + } + + return 0; + } + + break; + case sdk.stats.PerLevelHp: // (for example Fortitude with hp per lvl can be defined now with 1.5) + return this.getStat(sdk.stats.PerLevelHp) / 2048; + } + + if (this.getFlag(sdk.items.flags.Runeword)) { + switch (id) { + case sdk.stats.ArmorPercent: + if ([0, 1].indexOf(this.mode) < 0) { + break; + } + + this.desc === undefined && (this.desc = this.description); + + if (this.desc) { + temp = !!this.desc ? this.desc.split("\n") : ""; + + for (let i = 0; i < temp.length; i += 1) { + if (temp[i].match(getLocaleString(sdk.locale.text.EnhancedDefense).replace(/^\s+|\s+$/g, ""), "i")) { + return parseInt(temp[i].replace(/ÿc[0-9!"+<;.*]/, ""), 10); + } + } + } + + return 0; + case sdk.stats.EnhancedDamage: + if ([0, 1].indexOf(this.mode) < 0) { + break; + } + + this.desc === undefined && (this.desc = this.description); + + if (this.desc) { + temp = !!this.desc ? this.desc.split("\n") : ""; + + for (let i = 0; i < temp.length; i += 1) { + if (temp[i].match(getLocaleString(sdk.locale.text.EnhancedDamage).replace(/^\s+|\s+$/g, ""), "i")) { + return parseInt(temp[i].replace(/ÿc[0-9!"+<;.*]/, ""), 10); + } + } + } + + return 0; + } + } + + if (subid === undefined) { + return this.getStat(id); + } + + return this.getStat(id, subid); }; /** @@ -829,36 +829,36 @@ Unit.prototype.getStatEx = function (id, subid) { * @returns Boolean */ Unit.prototype.haveRunes = function (itemInfo = []) { - if (this === undefined || this.type > 1) return false; - if (!Array.isArray(itemInfo) || typeof itemInfo[0] !== "number") return false; - let itemList = this.getItemsEx().filter(i => i.isInStorage && i.itemType === sdk.items.type.Rune); - if (!itemList.length || itemList.length < itemInfo.length) return false; - let checkedGids = []; - - for (let i = 0; i < itemInfo.length; i++) { - let rCheck = itemInfo[i]; - - if (!itemList.some(i => { - if (i.classid === rCheck && checkedGids.indexOf(i.gid) === -1) { - checkedGids.push(i.gid); - return true; - } - return false; - })) { - return false; - } - } - - return true; + if (this === undefined || this.type > 1) return false; + if (!Array.isArray(itemInfo) || typeof itemInfo[0] !== "number") return false; + let itemList = this.getItemsEx().filter(i => i.isInStorage && i.itemType === sdk.items.type.Rune); + if (!itemList.length || itemList.length < itemInfo.length) return false; + let checkedGids = []; + + for (let i = 0; i < itemInfo.length; i++) { + let rCheck = itemInfo[i]; + + if (!itemList.some(i => { + if (i.classid === rCheck && checkedGids.indexOf(i.gid) === -1) { + checkedGids.push(i.gid); + return true; + } + return false; + })) { + return false; + } + } + + return true; }; Unit.prototype.getMobs = function ({ range, coll, type }) { - if (this === undefined) return []; - const _this = this; - return getUnits(sdk.unittype.Monster) - .filter(function (mon) { - return mon.attackable && getDistance(_this, mon) < range - && (!type || ((type & mon.spectype))) - && (!coll || !checkCollision(_this, mon, coll)); - }); + if (this === undefined) return []; + const _this = this; + return getUnits(sdk.unittype.Monster) + .filter(function (mon) { + return mon.attackable && getDistance(_this, mon) < range + && (!type || ((type & mon.spectype))) + && (!coll || !checkCollision(_this, mon, coll)); + }); }; diff --git a/libs/SoloPlay/Functions/Quest.js b/libs/SoloPlay/Functions/Quest.js index 0d26b9e1..5c5a79e1 100644 --- a/libs/SoloPlay/Functions/Quest.js +++ b/libs/SoloPlay/Functions/Quest.js @@ -7,650 +7,650 @@ */ const Quest = { - preReqs: function () { - /** - * @param {string} task - * @param {() => boolean} req - * @returns {boolean} - */ - const getReq = (task, req = () => true) => { - for (let i = 0; i < 5 && !req(); i++) { - Loader.runScript(task); - } - return req(); - }; - - if (me.accessToAct(2)) { - !me.cube && getReq("cube", () => me.cube); - - if (!me.staff && !me.horadricstaff) { - !me.amulet && getReq("amulet", () => me.amulet); - !me.shaft && getReq("staff", () => me.shaft); - } - } - - if (me.accessToAct(3) && !me.travincal && !me.khalimswill) { - !me.eye && getReq("eye", () => me.eye); - !me.heart && getReq("heart", () => me.heart); - !me.brain && getReq("brain", () => me.brain); - } - }, - - cubeItems: function (outcome, ...classids) { - if (me.getItem(outcome) - || outcome === sdk.quest.item.HoradricStaff && me.horadricstaff - || outcome === sdk.quest.item.KhalimsWill && me.travincal) { - return true; - } - - !me.inTown && Town.goToTown(); - outcome === sdk.quest.item.HoradricStaff - ? me.overhead("cubing staff") - : outcome === sdk.quest.item.KhalimsWill - ? me.overhead("cubing flail") - : me.overhead("cubing " + outcome); - - Town.doChores(); - Town.openStash(); - Cubing.emptyCube(); - - for (let classid of classids) { - let cubingItem = me.getItem(classid); - - if (!cubingItem || !Storage.Cube.MoveTo(cubingItem)) { - return false; - } - } - - Misc.poll(() => Cubing.openCube(), 5000, 1000); - - let wantedItem; - let tick = getTickCount(); - - while (getTickCount() - tick < 5000) { - if (Cubing.openCube()) { - transmute(); - delay(750 + me.ping); - - wantedItem = me.getItem(outcome); - - if (wantedItem) { - Storage.Inventory.MoveTo(wantedItem); - me.cancel(); - - break; - } - } - } - - me.cancel(); - - outcome === sdk.items.quest.HoradricStaff && Town.npcInteract("cain"); - - return me.getItem(outcome); - }, - - placeStaff: function () { - if (me.horadricstaff) return true; - - let tick = getTickCount(); - let orifice = Misc.poll(() => Game.getObject(sdk.objects.HoradricStaffHolder)); - if (!orifice) return false; - - let hstaff = (me.getItem(sdk.items.quest.HoradricStaff) || Quest.cubeItems(sdk.items.quest.HoradricStaff, sdk.items.quest.ShaftoftheHoradricStaff, sdk.items.quest.ViperAmulet)); - - if (hstaff) { - if (hstaff.location !== sdk.storage.Inventory) { - !me.inTown && Town.goToTown(); - - if (!Storage.Inventory.CanFit(hstaff)) { - Town.clearJunk(); - me.sortInventory(); - } - - hstaff.isInStash && Town.openStash(); - hstaff.isInCube && Cubing.openCube(); - Storage.Inventory.MoveTo(hstaff); - me.cancelUIFlags(); - Town.move("portalspot") && Pather.usePortal(null, me.name); - } - } - - Pather.moveToPreset(me.area, sdk.unittype.Object, 152); - Misc.openChest(orifice); - - if (!hstaff) { - if (getTickCount() - tick < 500) { - delay(500 + me.ping); - } - - return false; - } - - clickItemAndWait(sdk.clicktypes.click.item.Left, hstaff); - submitItem(); - delay(750 + me.ping); - - // Clear cursor of staff - credit @Jaenster - let item = me.getItemsEx().filter((el) => el.isInInventory).first(); - let _b = [item.x, item.y, item.location], x = _b[0], y = _b[1], loc = _b[2]; - clickItemAndWait(sdk.clicktypes.click.item.Left, item); - clickItemAndWait(sdk.clicktypes.click.item.Left, x, y, loc); - delay(750 + me.ping); - - return true; - }, - - tyraelTomb: function () { - Pather.moveTo(22629, 15714); - Pather.moveTo(22609, 15707); - Pather.moveTo(22579, 15704); - Pather.moveTo(22577, 15649, 10); - Pather.moveTo(22577, 15609, 10); + preReqs: function () { + /** + * @param {string} task + * @param {() => boolean} req + * @returns {boolean} + */ + const getReq = (task, req = () => true) => { + for (let i = 0; i < 5 && !req(); i++) { + Loader.runScript(task); + } + return req(); + }; + + if (me.accessToAct(2)) { + !me.cube && getReq("cube", () => me.cube); + + if (!me.staff && !me.horadricstaff) { + !me.amulet && getReq("amulet", () => me.amulet); + !me.shaft && getReq("staff", () => me.shaft); + } + } + + if (me.accessToAct(3) && !me.travincal && !me.khalimswill) { + !me.eye && getReq("eye", () => me.eye); + !me.heart && getReq("heart", () => me.heart); + !me.brain && getReq("brain", () => me.brain); + } + }, + + cubeItems: function (outcome, ...classids) { + if (me.getItem(outcome) + || outcome === sdk.quest.item.HoradricStaff && me.horadricstaff + || outcome === sdk.quest.item.KhalimsWill && me.travincal) { + return true; + } + + !me.inTown && Town.goToTown(); + outcome === sdk.quest.item.HoradricStaff + ? me.overhead("cubing staff") + : outcome === sdk.quest.item.KhalimsWill + ? me.overhead("cubing flail") + : me.overhead("cubing " + outcome); + + Town.doChores(); + Town.openStash(); + Cubing.emptyCube(); + + for (let classid of classids) { + let cubingItem = me.getItem(classid); + + if (!cubingItem || !Storage.Cube.MoveTo(cubingItem)) { + return false; + } + } + + Misc.poll(() => Cubing.openCube(), 5000, 1000); + + let wantedItem; + let tick = getTickCount(); + + while (getTickCount() - tick < 5000) { + if (Cubing.openCube()) { + transmute(); + delay(750 + me.ping); + + wantedItem = me.getItem(outcome); + + if (wantedItem) { + Storage.Inventory.MoveTo(wantedItem); + me.cancel(); + + break; + } + } + } + + me.cancel(); + + outcome === sdk.items.quest.HoradricStaff && Town.npcInteract("cain"); + + return me.getItem(outcome); + }, + + placeStaff: function () { + if (me.horadricstaff) return true; + + let tick = getTickCount(); + let orifice = Misc.poll(() => Game.getObject(sdk.objects.HoradricStaffHolder)); + if (!orifice) return false; + + let hstaff = (me.getItem(sdk.items.quest.HoradricStaff) || Quest.cubeItems(sdk.items.quest.HoradricStaff, sdk.items.quest.ShaftoftheHoradricStaff, sdk.items.quest.ViperAmulet)); + + if (hstaff) { + if (hstaff.location !== sdk.storage.Inventory) { + !me.inTown && Town.goToTown(); + + if (!Storage.Inventory.CanFit(hstaff)) { + Town.clearJunk(); + me.sortInventory(); + } + + hstaff.isInStash && Town.openStash(); + hstaff.isInCube && Cubing.openCube(); + Storage.Inventory.MoveTo(hstaff); + me.cancelUIFlags(); + Town.move("portalspot") && Pather.usePortal(null, me.name); + } + } + + Pather.moveToPreset(me.area, sdk.unittype.Object, 152); + Misc.openChest(orifice); + + if (!hstaff) { + if (getTickCount() - tick < 500) { + delay(500 + me.ping); + } + + return false; + } + + clickItemAndWait(sdk.clicktypes.click.item.Left, hstaff); + submitItem(); + delay(750 + me.ping); + + // Clear cursor of staff - credit @Jaenster + let item = me.getItemsEx().filter((el) => el.isInInventory).first(); + let _b = [item.x, item.y, item.location], x = _b[0], y = _b[1], loc = _b[2]; + clickItemAndWait(sdk.clicktypes.click.item.Left, item); + clickItemAndWait(sdk.clicktypes.click.item.Left, x, y, loc); + delay(750 + me.ping); + + return true; + }, + + tyraelTomb: function () { + Pather.moveTo(22629, 15714); + Pather.moveTo(22609, 15707); + Pather.moveTo(22579, 15704); + Pather.moveTo(22577, 15649, 10); + Pather.moveTo(22577, 15609, 10); - let tyrael = Game.getNPC(NPC.Tyrael); - if (!tyrael) return false; - - for (let talk = 0; talk < 3; talk += 1) { - tyrael.distance > 3 && Pather.moveToUnit(tyrael); - - tyrael.interact(); - delay(1000 + me.ping); - me.cancel(); - - if (Pather.getPortal(null)) { - me.cancel(); - break; - } - } - - !me.inTown && Town.goToTown(); - - return true; - }, - - stashItem: function (classid) { - let questItem = typeof classid === "object" ? classid : me.getItem(classid); - if (!questItem) return false; - myPrint("Stashing: " + questItem.fname.split("\n").reverse().join(" ")); - - !me.inTown && Town.goToTown(); - Town.openStash(); - - if (!Storage.Stash.CanFit(questItem)) { - Town.sortStash(true); - - if (!Storage.Stash.CanFit(questItem)) return false; - } - - Storage.Stash.MoveTo(questItem); - - return questItem.isInStash; - }, - - collectItem: function (classid, chestID) { - if (me.getItem(classid)) return true; - - if (chestID !== undefined) { - let chest = Game.getObject(chestID); - if (!chest || !Misc.openChest(chest)) return false; - } - - let questItem = Misc.poll(() => Game.getItem(classid), 3000, 100 + me.ping); - - if (Storage.Inventory.CanFit(questItem)) { - Pickit.pickItem(questItem); - } else { - Town.visitTown(); - Pickit.pickItem(questItem); - Pickit.pickItems(); - } - - return me.getItem(classid); - }, - - equipItem: function (classid, loc) { - let questItem = me.getItem(classid); - !getUIFlag(sdk.uiflags.Stash) && me.cancel(); - - if (questItem) { - me.dualWielding && Item.removeItem(sdk.body.LeftArm); - - if (!Item.equip(questItem, loc)) { - Pickit.pickItems(); - console.log("ÿc8Kolbot-SoloPlayÿc0: failed to equip " + classid + " .(Quest.equipItem)"); - } - } else { - console.log("ÿc8Kolbot-SoloPlayÿc0: Lost " + classid + " before trying to equip it. (Quest.equipItem)"); - return false; - } - - if (me.itemoncursor) { - let olditem = Game.getCursorUnit(); - - if (olditem) { - if (Storage.Inventory.CanFit(olditem)) { - console.log("ÿc8Kolbot-SoloPlayÿc0: Keeping weapon"); - - Storage.Inventory.MoveTo(olditem); - } else { - me.cancel(); - console.log("ÿc8Kolbot-SoloPlayÿc0: No room to keep weapon"); - - olditem.drop(); - } - } - } - - me.cancelUIFlags(); - - return questItem.bodylocation === loc; - }, - - smashSomething: function (classid) { - let tool = classid === sdk.objects.CompellingOrb - ? sdk.items.quest.KhalimsWill - : classid === sdk.quest.chest.HellForge - ? sdk.items.quest.HellForgeHammer - : null; - let smashable = Game.getObject(classid); - - if (me.equipped.get(sdk.body.RightArm).classid !== tool || !me.getItem(tool)) return false; - if (!smashable) return false; - let tick = getTickCount(); - let questTool = me.getItem(tool); - - while (me.getItem(tool)) { - smashable.distance > 4 && Pather.moveToEx(smashable.x, smashable.y, { clearSettings: { allowClearing: false } }); - Skill.cast(sdk.skills.Attack, sdk.skills.hand.Right, smashable); - smashable.interact(); - - if (getTickCount() - tick > Time.seconds(30)) { - console.warn("Timed out trying to smash quest object"); - - return false; - } - - if (!questTool.isEquipped) { - break; - } - - delay(750 + me.ping); - } - - return !me.getItem(tool); - }, - - /** - * @param {string} npcName - * @param {number | number[]} action - * @returns {boolean} - */ - npcAction: function (npcName, action) { - if (!npcName || !action) return false; - !Array.isArray(action) && (action = [action]); - - !me.inTown && Town.goToTown(); - npcName = npcName.capitalize(true); - Town.move(NPC[npcName]); - let npc = Misc.poll(() => Game.getNPC(NPC[npcName])); - - Packet.flash(me.gid); - delay(1 + me.ping * 2); - - if (npc && npc.openMenu()) { - action.forEach(menuOption => Misc.useMenu(menuOption) && delay(100 + me.ping)); - return true; - } - - return false; - }, - - // Akara reset for build change - characterRespec: function () { - if (me.respec || SetUp.currentBuild === SetUp.finalBuild) return; - - switch (true) { - case me.charlvl >= CharInfo.respecOne && SetUp.currentBuild === "Start": - case CharInfo.respecTwo > 0 && me.charlvl >= CharInfo.respecTwo && SetUp.currentBuild === "Stepping": - case me.charlvl === SetUp.finalRespec() && SetUp.currentBuild === "Leveling": - if (!me.den) { - myPrint("time to respec, but den is incomplete"); - return; - } - - let preSkillAmount = me.getStat(sdk.stats.NewSkills); - let preStatAmount = me.getStat(sdk.stats.StatPts); - let npc; - - Town.goToTown(1); - myPrint("time to respec"); - - for (let i = 0; i < 2; i++) { - // attempt packet respec on first try - if (i === 0) { - npc = Town.npcInteract("akara"); - me.cancelUIFlags(); - delay(100 + me.ping); - npc && sendPacket(1, sdk.packets.send.EntityAction, 4, 0, 4, npc.gid, 4, 0); - } else { - this.npcAction("akara", [sdk.menu.Respec, sdk.menu.Ok]); - } - - Misc.checkQuest(sdk.quest.id.Respec, sdk.quest.states.Completed); - delay(10 + me.ping * 2); - - if (me.respec || (me.getStat(sdk.stats.NewSkills) > preSkillAmount && me.getStat(sdk.stats.StatPts) > preStatAmount)) { - me.data.currentBuild = CharInfo.getActiveBuild(); - me.data[sdk.difficulty.nameOf(me.diff).toLowerCase()].respecUsed = true; - CharData.updateData("me", me.data); - delay(750 + me.ping * 2); - Town.clearBelt(); - myPrint("respec done, restarting"); - delay(1000 + me.ping); - scriptBroadcast("quit"); - } - } - - break; - } - }, - - // Credit dzik or laz unsure who for this - useSocketQuest: function (item = undefined) { - if (SetUp.finalBuild === "Socketmule") return false; - - try { - if (!item || item.mode === sdk.items.mode.onGround) throw new Error("Couldn't find item"); - if (!me.getQuest(sdk.quest.id.SiegeOnHarrogath, sdk.quest.states.ReqComplete)) throw new Error("Quest unavailable"); - if (item.sockets > 0 || getBaseStat("items", item.classid, "gemsockets") === 0) throw new Error("Item cannot be socketed"); - if (!Storage.Inventory.CanFit(item)) throw new Error("(useSocketQuest) No space to get item back"); - if (me.act !== 5 || !me.inTown) { - if (!Town.goToTown(5)) throw new Error("Failed to go to act 5"); - } - - if (item.isInStash && (!Town.openStash() || !Storage.Inventory.MoveTo(item))) { - throw new Error("Failed to move item from stash to inventory"); - } - - let invo = me.findItems(-1, sdk.items.mode.inStorage, sdk.storage.Inventory); - let slot = item.bodylocation; - - // Take note of all the items in the invo minus the item to socket - for (let i = 0; i < invo.length; i++) { - if (item.gid !== invo[i].gid) { - invo[i] = invo[i].x + "/" + invo[i].y; - } - } - - if (!this.npcAction("larzuk", sdk.menu.AddSockets)) throw new Error("Failed to interact with Lazruk"); - if (!getUIFlag(sdk.uiflags.SubmitItem)) throw new Error("Failed to open SubmitItem screen"); - if (!item.toCursor()) throw new Error("Couldn't get item"); - - submitItem(); - delay(500 + me.ping); - Packet.questRefresh(); - - item = false; // Delete item reference, it's not longer valid anyway - let items = me.findItems(-1, sdk.items.mode.inStorage, sdk.storage.Inventory); - - for (let i = 0; i < items.length; i++) { - if (invo.indexOf(items[i].x + "/" + items[i].y) === -1) { - item = items[i]; - } - } - - if (!item || item.sockets === 0) { - me.itemoncursor && Storage.Stash.MoveTo(item); - throw new Error("Failed to socket item"); - } - - Item.logItem("Used my " + sdk.difficulty.nameOf(me.diff) + " socket quest on : ", item, null, true); - D2Bot.printToConsole("Kolbot-SoloPlay :: Used my " + sdk.difficulty.nameOf(me.diff) + " socket quest on : " + item.name, sdk.colors.D2Bot.Gold); - CharData.updateData(sdk.difficulty.nameOf(me.diff), "socketUsed", true); - me.data[sdk.difficulty.nameOf(me.diff).toLowerCase()].socketUsed = true; - me.update(); - - if (!slot && !item.isInStash) { - // Move item back to stash - if (Storage.Stash.CanFit(item)) { - Town.move("stash"); - Storage.Stash.MoveTo(item); - me.cancel(); - } - } - - slot && Item.equip(item, slot); - } catch (e) { - myPrint(e); - me.itemoncursor && Storage.Inventory.MoveTo(Game.getCursorUnit()); - me.cancelUIFlags(); - - return false; - } - - return true; - }, - - // Credit whoever did useSocketQuest, I modified that to come up with this - useImbueQuest: function (item = undefined) { - if (SetUp.finalBuild === "Imbuemule") return false; - - try { - if (!item || item.mode === sdk.items.mode.onGround) throw new Error("Couldn't find item"); - if (!Misc.checkQuest(sdk.quest.id.ToolsoftheTrade, sdk.quest.states.ReqComplete)) throw new Error("Quest unavailable"); - if (item.sockets > 0 || item.quality > sdk.items.quality.Superior) throw new Error("Item cannot be imbued"); - if (!Storage.Inventory.CanFit(item)) throw new Error("(useImbueQuest) No space to get item back"); - if (me.act !== 1 || !me.inTown) { - if (!Town.goToTown(1)) throw new Error("Failed to go to act 1"); - } - - if (item.isInStash && (!Town.openStash() || !Storage.Inventory.MoveTo(item))) { - throw new Error("Failed to move item from stash to inventory"); - } - - let invo = me.findItems(-1, sdk.items.mode.inStorage, sdk.storage.Inventory); - let slot = item.bodylocation; - - // Take note of all the items in the invo minus the item to socket - for (let i = 0; i < invo.length; i++) { - if (item.gid !== invo[i].gid) { - invo[i] = invo[i].x + "/" + invo[i].y; - } - } - - if (!this.npcAction("charsi", sdk.menu.Imbue)) throw new Error("Failed to interact with Charsi"); - if (!getUIFlag(sdk.uiflags.SubmitItem)) throw new Error("Failed to open SubmitItem screen"); - if (!item.toCursor()) throw new Error("Couldn't get item"); - - submitItem(); - delay(500 + me.ping); - Packet.questRefresh(); - - item = false; // Delete item reference, it's not longer valid anyway - let items = me.findItems(-1, sdk.items.mode.inStorage, sdk.storage.Inventory); - - for (let i = 0; i < items.length; i++) { - if (invo.indexOf(items[i].x + "/" + items[i].y) === -1) { - item = items[i]; - } - } - - if (!item || !item.rare) { - me.itemoncursor && Storage.Stash.MoveTo(item); - throw new Error("Failed to imbue item"); - } - - Item.logItem("Used my " + sdk.difficulty.nameOf(me.diff) + " imbue quest on : ", item, null, true); - D2Bot.printToConsole("Kolbot-SoloPlay :: Used my " + sdk.difficulty.nameOf(me.diff) + " imbue quest on : " + item.name, sdk.colors.D2Bot.Gold); - CharData.updateData(sdk.difficulty.nameOf(me.diff), "imbueUsed", true); - me.data[sdk.difficulty.nameOf(me.diff).toLowerCase()].imbueUsed = true; - me.update(); - - if (!slot && !item.isInStash) { - // Move item back to stash - if (Storage.Stash.CanFit(item)) { - Town.move("stash"); - Storage.Stash.MoveTo(item); - me.cancel(); - } - } - - slot && Item.equip(item, slot); - } catch (e) { - myPrint(e); - me.itemoncursor && Storage.Inventory.MoveTo(Game.getCursorUnit()); - me.cancelUIFlags(); - - return false; - } - - return true; - }, - - unfinishedQuests: function () { - const highestAct = me.highestAct; - // Act 1 - // Tools of the trade - let malus = me.getItem(sdk.items.quest.HoradricMalus); - !!malus && Town.goToTown(1) && Town.npcInteract("charsi"); - - let imbueItem = Misc.checkItemsForImbueing(); - (imbueItem) && Quest.useImbueQuest(imbueItem) && Item.autoEquip(); - - // Drop wirts leg at startup - let leg = me.getItem(sdk.items.quest.WirtsLeg); - if (leg) { - !me.inTown && Town.goToTown(); - leg.isInStash && Town.openStash() && Storage.Inventory.MoveTo(leg) && delay(300); - getUIFlag(sdk.uiflags.Stash) && me.cancel(); - leg.drop(); - } - - // Act 2 - if (highestAct >= 2) { - // Radament skill book - let book = me.getItem(sdk.quest.item.BookofSkill); - if (book) { - book.isInStash && Town.openStash() && delay(300); - Misc.poll(() => { - book.use(); - if (me.getStat(sdk.stats.NewSkills) > 0) { - console.log("ÿc8Kolbot-SoloPlayÿc0: used Radament skill book"); - AutoSkill.init(Config.AutoSkill.Build, Config.AutoSkill.Save); - return true; - } - return false; - }, 1000, 100); - } - } - - // Act 3 - if (highestAct >= 3) { - // Figurine -> Golden Bird - if (me.getItem(sdk.items.quest.AJadeFigurine)) { - myPrint("starting jade figurine"); - Town.goToTown(3) && Town.npcInteract("meshif"); - } - - // Golden Bird -> Ashes - (me.getItem(sdk.items.quest.TheGoldenBird)) && Town.goToTown(3) && Town.npcInteract("alkor"); - - // Potion of life - let pol = me.getItem(sdk.items.quest.PotofLife); - if (pol) { - pol.isInStash && Town.openStash() && delay(300); - pol.use() && console.log("ÿc8Kolbot-SoloPlayÿc0: used potion of life"); - } - - // LamEssen's Tome - let tome = me.getItem(sdk.items.quest.LamEsensTome); - if (tome) { - !me.inTown && Town.goToTown(3); - tome.isInStash && Town.openStash() && Storage.Inventory.MoveTo(tome) && delay(300); - Town.npcInteract("alkor") && delay(300); - me.getStat(sdk.stats.StatPts) > 0 && AutoStat.init(Config.AutoStat.Build, Config.AutoStat.Save, Config.AutoStat.BlockChance, Config.AutoStat.UseBulk); - console.log("ÿc8Kolbot-SoloPlayÿc0: LamEssen Tome completed"); - } - - // Free Lam Essen quest - if (me.accessToAct(3) && !me.getQuest(sdk.quest.id.LamEsensTome, sdk.quest.states.Completed)) { - !me.inArea(sdk.areas.KurastDocktown) && Town.goToTown(3); - Town.move("alkor"); - let unit = getUnit(1, "alkor"); - if (unit) { - sendPacket(1, sdk.packets.send.QuestMessage, 4, unit.gid, 4, 564); - delay((me.ping || 0) * 2 + 200); - unit.openMenu(); - me.cancel(); - me.cancel(); - } - } - - // Remove Khalim's Will if quest not completed and restarting run. - let kw = me.getItem(sdk.items.quest.KhalimsWill); - if (kw) { - if (me.equipped.get(sdk.body.RightArm).classid === sdk.items.quest.KhalimsWill) { - Town.clearInventory(); - delay(500); - Quest.stashItem(sdk.items.quest.KhalimsWill); - console.log("ÿc8Kolbot-SoloPlayÿc0: removed khalims will"); - Item.autoEquip(); - } - } - - // Killed council but haven't talked to cain - if (!Misc.checkQuest(sdk.quest.id.TheBlackenedTemple, sdk.quest.states.Completed) && Misc.checkQuest(sdk.quest.id.TheBlackenedTemple, 4)) { - me.overhead("Finishing Travincal by talking to cain"); - Town.goToTown(3) && Town.npcInteract("cain") && delay(300); - me.cancel(); - } - } - - // Act 4 - if (highestAct >= 4) { - // Drop hellforge hammer and soulstone at startup - let hammer = me.getItem(sdk.items.quest.HellForgeHammer); - if (hammer) { - !me.inTown && Town.goToTown(); - hammer.isInStash && Town.openStash() && Storage.Inventory.MoveTo(hammer) && delay(300); - getUIFlag(sdk.uiflags.Stash) && me.cancel(); - hammer.drop(); - } - - let soulstone = me.getItem(sdk.items.quest.MephistosSoulstone); - if (soulstone) { - !me.inTown && Town.goToTown(); - soulstone.isInStash && Town.openStash() && Storage.Inventory.MoveTo(soulstone) && delay(300); - getUIFlag(sdk.uiflags.Stash) && me.cancel(); - soulstone.drop(); - } - } - - // Act 5 - if (highestAct === 5) { - let socketItem = Misc.checkItemsForSocketing(); - !!socketItem && Quest.useSocketQuest(socketItem); - - // Scroll of resistance - let sor = me.getItem(sdk.items.quest.ScrollofResistance); - if (sor) { - sor.isInStash && Town.openStash() && delay(300); - sor.use() && console.log("ÿc8Kolbot-SoloPlayÿc0: used scroll of resistance"); - } - - if (Misc.checkQuest(sdk.quest.id.PrisonofIce, 7/** Used the scroll */) - && !Misc.checkQuest(sdk.quest.id.PrisonofIce, sdk.quest.states.Completed)) { - // never talked to anya after drinking potion, lets do that - Town.npcInteract("anya"); - } - } - - Misc.checkSocketables(); - - Town.heal(); - me.cancelUIFlags(); - - return true; - }, + let tyrael = Game.getNPC(NPC.Tyrael); + if (!tyrael) return false; + + for (let talk = 0; talk < 3; talk += 1) { + tyrael.distance > 3 && Pather.moveToUnit(tyrael); + + tyrael.interact(); + delay(1000 + me.ping); + me.cancel(); + + if (Pather.getPortal(null)) { + me.cancel(); + break; + } + } + + !me.inTown && Town.goToTown(); + + return true; + }, + + stashItem: function (classid) { + let questItem = typeof classid === "object" ? classid : me.getItem(classid); + if (!questItem) return false; + myPrint("Stashing: " + questItem.fname.split("\n").reverse().join(" ")); + + !me.inTown && Town.goToTown(); + Town.openStash(); + + if (!Storage.Stash.CanFit(questItem)) { + Town.sortStash(true); + + if (!Storage.Stash.CanFit(questItem)) return false; + } + + Storage.Stash.MoveTo(questItem); + + return questItem.isInStash; + }, + + collectItem: function (classid, chestID) { + if (me.getItem(classid)) return true; + + if (chestID !== undefined) { + let chest = Game.getObject(chestID); + if (!chest || !Misc.openChest(chest)) return false; + } + + let questItem = Misc.poll(() => Game.getItem(classid), 3000, 100 + me.ping); + + if (Storage.Inventory.CanFit(questItem)) { + Pickit.pickItem(questItem); + } else { + Town.visitTown(); + Pickit.pickItem(questItem); + Pickit.pickItems(); + } + + return me.getItem(classid); + }, + + equipItem: function (classid, loc) { + let questItem = me.getItem(classid); + !getUIFlag(sdk.uiflags.Stash) && me.cancel(); + + if (questItem) { + me.dualWielding && Item.removeItem(sdk.body.LeftArm); + + if (!Item.equip(questItem, loc)) { + Pickit.pickItems(); + console.log("ÿc8Kolbot-SoloPlayÿc0: failed to equip " + classid + " .(Quest.equipItem)"); + } + } else { + console.log("ÿc8Kolbot-SoloPlayÿc0: Lost " + classid + " before trying to equip it. (Quest.equipItem)"); + return false; + } + + if (me.itemoncursor) { + let olditem = Game.getCursorUnit(); + + if (olditem) { + if (Storage.Inventory.CanFit(olditem)) { + console.log("ÿc8Kolbot-SoloPlayÿc0: Keeping weapon"); + + Storage.Inventory.MoveTo(olditem); + } else { + me.cancel(); + console.log("ÿc8Kolbot-SoloPlayÿc0: No room to keep weapon"); + + olditem.drop(); + } + } + } + + me.cancelUIFlags(); + + return questItem.bodylocation === loc; + }, + + smashSomething: function (classid) { + let tool = classid === sdk.objects.CompellingOrb + ? sdk.items.quest.KhalimsWill + : classid === sdk.quest.chest.HellForge + ? sdk.items.quest.HellForgeHammer + : null; + let smashable = Game.getObject(classid); + + if (me.equipped.get(sdk.body.RightArm).classid !== tool || !me.getItem(tool)) return false; + if (!smashable) return false; + let tick = getTickCount(); + let questTool = me.getItem(tool); + + while (me.getItem(tool)) { + smashable.distance > 4 && Pather.moveToEx(smashable.x, smashable.y, { clearSettings: { allowClearing: false } }); + Skill.cast(sdk.skills.Attack, sdk.skills.hand.Right, smashable); + smashable.interact(); + + if (getTickCount() - tick > Time.seconds(30)) { + console.warn("Timed out trying to smash quest object"); + + return false; + } + + if (!questTool.isEquipped) { + break; + } + + delay(750 + me.ping); + } + + return !me.getItem(tool); + }, + + /** + * @param {string} npcName + * @param {number | number[]} action + * @returns {boolean} + */ + npcAction: function (npcName, action) { + if (!npcName || !action) return false; + !Array.isArray(action) && (action = [action]); + + !me.inTown && Town.goToTown(); + npcName = npcName.capitalize(true); + Town.move(NPC[npcName]); + let npc = Misc.poll(() => Game.getNPC(NPC[npcName])); + + Packet.flash(me.gid); + delay(1 + me.ping * 2); + + if (npc && npc.openMenu()) { + action.forEach(menuOption => Misc.useMenu(menuOption) && delay(100 + me.ping)); + return true; + } + + return false; + }, + + // Akara reset for build change + characterRespec: function () { + if (me.respec || SetUp.currentBuild === SetUp.finalBuild) return; + + switch (true) { + case me.charlvl >= CharInfo.respecOne && SetUp.currentBuild === "Start": + case CharInfo.respecTwo > 0 && me.charlvl >= CharInfo.respecTwo && SetUp.currentBuild === "Stepping": + case me.charlvl === SetUp.finalRespec() && SetUp.currentBuild === "Leveling": + if (!me.den) { + myPrint("time to respec, but den is incomplete"); + return; + } + + let preSkillAmount = me.getStat(sdk.stats.NewSkills); + let preStatAmount = me.getStat(sdk.stats.StatPts); + let npc; + + Town.goToTown(1); + myPrint("time to respec"); + + for (let i = 0; i < 2; i++) { + // attempt packet respec on first try + if (i === 0) { + npc = Town.npcInteract("akara"); + me.cancelUIFlags(); + delay(100 + me.ping); + npc && sendPacket(1, sdk.packets.send.EntityAction, 4, 0, 4, npc.gid, 4, 0); + } else { + this.npcAction("akara", [sdk.menu.Respec, sdk.menu.Ok]); + } + + Misc.checkQuest(sdk.quest.id.Respec, sdk.quest.states.Completed); + delay(10 + me.ping * 2); + + if (me.respec || (me.getStat(sdk.stats.NewSkills) > preSkillAmount && me.getStat(sdk.stats.StatPts) > preStatAmount)) { + me.data.currentBuild = CharInfo.getActiveBuild(); + me.data[sdk.difficulty.nameOf(me.diff).toLowerCase()].respecUsed = true; + CharData.updateData("me", me.data); + delay(750 + me.ping * 2); + Town.clearBelt(); + myPrint("respec done, restarting"); + delay(1000 + me.ping); + scriptBroadcast("quit"); + } + } + + break; + } + }, + + // Credit dzik or laz unsure who for this + useSocketQuest: function (item = undefined) { + if (SetUp.finalBuild === "Socketmule") return false; + + try { + if (!item || item.mode === sdk.items.mode.onGround) throw new Error("Couldn't find item"); + if (!me.getQuest(sdk.quest.id.SiegeOnHarrogath, sdk.quest.states.ReqComplete)) throw new Error("Quest unavailable"); + if (item.sockets > 0 || getBaseStat("items", item.classid, "gemsockets") === 0) throw new Error("Item cannot be socketed"); + if (!Storage.Inventory.CanFit(item)) throw new Error("(useSocketQuest) No space to get item back"); + if (me.act !== 5 || !me.inTown) { + if (!Town.goToTown(5)) throw new Error("Failed to go to act 5"); + } + + if (item.isInStash && (!Town.openStash() || !Storage.Inventory.MoveTo(item))) { + throw new Error("Failed to move item from stash to inventory"); + } + + let invo = me.findItems(-1, sdk.items.mode.inStorage, sdk.storage.Inventory); + let slot = item.bodylocation; + + // Take note of all the items in the invo minus the item to socket + for (let i = 0; i < invo.length; i++) { + if (item.gid !== invo[i].gid) { + invo[i] = invo[i].x + "/" + invo[i].y; + } + } + + if (!this.npcAction("larzuk", sdk.menu.AddSockets)) throw new Error("Failed to interact with Lazruk"); + if (!getUIFlag(sdk.uiflags.SubmitItem)) throw new Error("Failed to open SubmitItem screen"); + if (!item.toCursor()) throw new Error("Couldn't get item"); + + submitItem(); + delay(500 + me.ping); + Packet.questRefresh(); + + item = false; // Delete item reference, it's not longer valid anyway + let items = me.findItems(-1, sdk.items.mode.inStorage, sdk.storage.Inventory); + + for (let i = 0; i < items.length; i++) { + if (invo.indexOf(items[i].x + "/" + items[i].y) === -1) { + item = items[i]; + } + } + + if (!item || item.sockets === 0) { + me.itemoncursor && Storage.Stash.MoveTo(item); + throw new Error("Failed to socket item"); + } + + Item.logItem("Used my " + sdk.difficulty.nameOf(me.diff) + " socket quest on : ", item, null, true); + D2Bot.printToConsole("Kolbot-SoloPlay :: Used my " + sdk.difficulty.nameOf(me.diff) + " socket quest on : " + item.name, sdk.colors.D2Bot.Gold); + CharData.updateData(sdk.difficulty.nameOf(me.diff), "socketUsed", true); + me.data[sdk.difficulty.nameOf(me.diff).toLowerCase()].socketUsed = true; + me.update(); + + if (!slot && !item.isInStash) { + // Move item back to stash + if (Storage.Stash.CanFit(item)) { + Town.move("stash"); + Storage.Stash.MoveTo(item); + me.cancel(); + } + } + + slot && Item.equip(item, slot); + } catch (e) { + myPrint(e); + me.itemoncursor && Storage.Inventory.MoveTo(Game.getCursorUnit()); + me.cancelUIFlags(); + + return false; + } + + return true; + }, + + // Credit whoever did useSocketQuest, I modified that to come up with this + useImbueQuest: function (item = undefined) { + if (SetUp.finalBuild === "Imbuemule") return false; + + try { + if (!item || item.mode === sdk.items.mode.onGround) throw new Error("Couldn't find item"); + if (!Misc.checkQuest(sdk.quest.id.ToolsoftheTrade, sdk.quest.states.ReqComplete)) throw new Error("Quest unavailable"); + if (item.sockets > 0 || item.quality > sdk.items.quality.Superior) throw new Error("Item cannot be imbued"); + if (!Storage.Inventory.CanFit(item)) throw new Error("(useImbueQuest) No space to get item back"); + if (me.act !== 1 || !me.inTown) { + if (!Town.goToTown(1)) throw new Error("Failed to go to act 1"); + } + + if (item.isInStash && (!Town.openStash() || !Storage.Inventory.MoveTo(item))) { + throw new Error("Failed to move item from stash to inventory"); + } + + let invo = me.findItems(-1, sdk.items.mode.inStorage, sdk.storage.Inventory); + let slot = item.bodylocation; + + // Take note of all the items in the invo minus the item to socket + for (let i = 0; i < invo.length; i++) { + if (item.gid !== invo[i].gid) { + invo[i] = invo[i].x + "/" + invo[i].y; + } + } + + if (!this.npcAction("charsi", sdk.menu.Imbue)) throw new Error("Failed to interact with Charsi"); + if (!getUIFlag(sdk.uiflags.SubmitItem)) throw new Error("Failed to open SubmitItem screen"); + if (!item.toCursor()) throw new Error("Couldn't get item"); + + submitItem(); + delay(500 + me.ping); + Packet.questRefresh(); + + item = false; // Delete item reference, it's not longer valid anyway + let items = me.findItems(-1, sdk.items.mode.inStorage, sdk.storage.Inventory); + + for (let i = 0; i < items.length; i++) { + if (invo.indexOf(items[i].x + "/" + items[i].y) === -1) { + item = items[i]; + } + } + + if (!item || !item.rare) { + me.itemoncursor && Storage.Stash.MoveTo(item); + throw new Error("Failed to imbue item"); + } + + Item.logItem("Used my " + sdk.difficulty.nameOf(me.diff) + " imbue quest on : ", item, null, true); + D2Bot.printToConsole("Kolbot-SoloPlay :: Used my " + sdk.difficulty.nameOf(me.diff) + " imbue quest on : " + item.name, sdk.colors.D2Bot.Gold); + CharData.updateData(sdk.difficulty.nameOf(me.diff), "imbueUsed", true); + me.data[sdk.difficulty.nameOf(me.diff).toLowerCase()].imbueUsed = true; + me.update(); + + if (!slot && !item.isInStash) { + // Move item back to stash + if (Storage.Stash.CanFit(item)) { + Town.move("stash"); + Storage.Stash.MoveTo(item); + me.cancel(); + } + } + + slot && Item.equip(item, slot); + } catch (e) { + myPrint(e); + me.itemoncursor && Storage.Inventory.MoveTo(Game.getCursorUnit()); + me.cancelUIFlags(); + + return false; + } + + return true; + }, + + unfinishedQuests: function () { + const highestAct = me.highestAct; + // Act 1 + // Tools of the trade + let malus = me.getItem(sdk.items.quest.HoradricMalus); + !!malus && Town.goToTown(1) && Town.npcInteract("charsi"); + + let imbueItem = Misc.checkItemsForImbueing(); + (imbueItem) && Quest.useImbueQuest(imbueItem) && Item.autoEquip(); + + // Drop wirts leg at startup + let leg = me.getItem(sdk.items.quest.WirtsLeg); + if (leg) { + !me.inTown && Town.goToTown(); + leg.isInStash && Town.openStash() && Storage.Inventory.MoveTo(leg) && delay(300); + getUIFlag(sdk.uiflags.Stash) && me.cancel(); + leg.drop(); + } + + // Act 2 + if (highestAct >= 2) { + // Radament skill book + let book = me.getItem(sdk.quest.item.BookofSkill); + if (book) { + book.isInStash && Town.openStash() && delay(300); + Misc.poll(() => { + book.use(); + if (me.getStat(sdk.stats.NewSkills) > 0) { + console.log("ÿc8Kolbot-SoloPlayÿc0: used Radament skill book"); + AutoSkill.init(Config.AutoSkill.Build, Config.AutoSkill.Save); + return true; + } + return false; + }, 1000, 100); + } + } + + // Act 3 + if (highestAct >= 3) { + // Figurine -> Golden Bird + if (me.getItem(sdk.items.quest.AJadeFigurine)) { + myPrint("starting jade figurine"); + Town.goToTown(3) && Town.npcInteract("meshif"); + } + + // Golden Bird -> Ashes + (me.getItem(sdk.items.quest.TheGoldenBird)) && Town.goToTown(3) && Town.npcInteract("alkor"); + + // Potion of life + let pol = me.getItem(sdk.items.quest.PotofLife); + if (pol) { + pol.isInStash && Town.openStash() && delay(300); + pol.use() && console.log("ÿc8Kolbot-SoloPlayÿc0: used potion of life"); + } + + // LamEssen's Tome + let tome = me.getItem(sdk.items.quest.LamEsensTome); + if (tome) { + !me.inTown && Town.goToTown(3); + tome.isInStash && Town.openStash() && Storage.Inventory.MoveTo(tome) && delay(300); + Town.npcInteract("alkor") && delay(300); + me.getStat(sdk.stats.StatPts) > 0 && AutoStat.init(Config.AutoStat.Build, Config.AutoStat.Save, Config.AutoStat.BlockChance, Config.AutoStat.UseBulk); + console.log("ÿc8Kolbot-SoloPlayÿc0: LamEssen Tome completed"); + } + + // Free Lam Essen quest + if (me.accessToAct(3) && !me.getQuest(sdk.quest.id.LamEsensTome, sdk.quest.states.Completed)) { + !me.inArea(sdk.areas.KurastDocktown) && Town.goToTown(3); + Town.move("alkor"); + let unit = getUnit(1, "alkor"); + if (unit) { + sendPacket(1, sdk.packets.send.QuestMessage, 4, unit.gid, 4, 564); + delay((me.ping || 0) * 2 + 200); + unit.openMenu(); + me.cancel(); + me.cancel(); + } + } + + // Remove Khalim's Will if quest not completed and restarting run. + let kw = me.getItem(sdk.items.quest.KhalimsWill); + if (kw) { + if (me.equipped.get(sdk.body.RightArm).classid === sdk.items.quest.KhalimsWill) { + Town.clearInventory(); + delay(500); + Quest.stashItem(sdk.items.quest.KhalimsWill); + console.log("ÿc8Kolbot-SoloPlayÿc0: removed khalims will"); + Item.autoEquip(); + } + } + + // Killed council but haven't talked to cain + if (!Misc.checkQuest(sdk.quest.id.TheBlackenedTemple, sdk.quest.states.Completed) && Misc.checkQuest(sdk.quest.id.TheBlackenedTemple, 4)) { + me.overhead("Finishing Travincal by talking to cain"); + Town.goToTown(3) && Town.npcInteract("cain") && delay(300); + me.cancel(); + } + } + + // Act 4 + if (highestAct >= 4) { + // Drop hellforge hammer and soulstone at startup + let hammer = me.getItem(sdk.items.quest.HellForgeHammer); + if (hammer) { + !me.inTown && Town.goToTown(); + hammer.isInStash && Town.openStash() && Storage.Inventory.MoveTo(hammer) && delay(300); + getUIFlag(sdk.uiflags.Stash) && me.cancel(); + hammer.drop(); + } + + let soulstone = me.getItem(sdk.items.quest.MephistosSoulstone); + if (soulstone) { + !me.inTown && Town.goToTown(); + soulstone.isInStash && Town.openStash() && Storage.Inventory.MoveTo(soulstone) && delay(300); + getUIFlag(sdk.uiflags.Stash) && me.cancel(); + soulstone.drop(); + } + } + + // Act 5 + if (highestAct === 5) { + let socketItem = Misc.checkItemsForSocketing(); + !!socketItem && Quest.useSocketQuest(socketItem); + + // Scroll of resistance + let sor = me.getItem(sdk.items.quest.ScrollofResistance); + if (sor) { + sor.isInStash && Town.openStash() && delay(300); + sor.use() && console.log("ÿc8Kolbot-SoloPlayÿc0: used scroll of resistance"); + } + + if (Misc.checkQuest(sdk.quest.id.PrisonofIce, 7/** Used the scroll */) + && !Misc.checkQuest(sdk.quest.id.PrisonofIce, sdk.quest.states.Completed)) { + // never talked to anya after drinking potion, lets do that + Town.npcInteract("anya"); + } + } + + Misc.checkSocketables(); + + Town.heal(); + me.cancelUIFlags(); + + return true; + }, }; diff --git a/libs/SoloPlay/Functions/RunewordsOverrides.js b/libs/SoloPlay/Functions/RunewordsOverrides.js index c7c7f07e..3ee80501 100644 --- a/libs/SoloPlay/Functions/RunewordsOverrides.js +++ b/libs/SoloPlay/Functions/RunewordsOverrides.js @@ -8,51 +8,51 @@ !includeIfNotIncluded("core/Runewords.js"); Runeword.PDiamondShield = Runeword.addRuneword( - "PDiamondShield", 3, - [sdk.items.gems.Perfect.Diamond, sdk.items.gems.Perfect.Diamond, sdk.items.gems.Perfect.Diamond], - [sdk.items.type.AnyShield] + "PDiamondShield", 3, + [sdk.items.gems.Perfect.Diamond, sdk.items.gems.Perfect.Diamond, sdk.items.gems.Perfect.Diamond], + [sdk.items.type.AnyShield] ); Runewords.checkRunewords = function () { - // keep a const reference of our items so failed checks don't remove items from the list - const itemsRef = me.findItems(-1, sdk.items.mode.inStorage); + // keep a const reference of our items so failed checks don't remove items from the list + const itemsRef = me.findItems(-1, sdk.items.mode.inStorage); - for (let i = 0; i < Config.Runewords.length; i += 1) { - let itemList = []; // reset item list - let items = itemsRef.slice(); // copy itemsRef + for (let i = 0; i < Config.Runewords.length; i += 1) { + let itemList = []; // reset item list + let items = itemsRef.slice(); // copy itemsRef - const [runeword, wantedBase, ethFlag] = Config.Runewords[i]; - if (runeword.reqLvl > me.charlvl) continue; // skip runeword if we don't meet the level requirement - let base = this.getBase(runeword, wantedBase, (ethFlag || 0)); // check base + const [runeword, wantedBase, ethFlag] = Config.Runewords[i]; + if (runeword.reqLvl > me.charlvl) continue; // skip runeword if we don't meet the level requirement + let base = this.getBase(runeword, wantedBase, (ethFlag || 0)); // check base - if (base) { - itemList.push(base); // push the base + if (base) { + itemList.push(base); // push the base - for (let j = 0; j < runeword.runes.length; j += 1) { - for (let k = 0; k < items.length; k += 1) { - if (items[k].classid === runeword.runes[j]) { // rune matched - itemList.push(items[k]); // push into the item list - items.splice(k, 1); // remove from item list as to not count it twice + for (let j = 0; j < runeword.runes.length; j += 1) { + for (let k = 0; k < items.length; k += 1) { + if (items[k].classid === runeword.runes[j]) { // rune matched + itemList.push(items[k]); // push into the item list + items.splice(k, 1); // remove from item list as to not count it twice - k -= 1; + k -= 1; - break; // stop item cycle - we found the item - } - } + break; // stop item cycle - we found the item + } + } - // can't complete runeword - go to next one - if (itemList.length !== j + 2) { - break; - } + // can't complete runeword - go to next one + if (itemList.length !== j + 2) { + break; + } - if (itemList.length === runeword.runes.length + 1) { // runes + base - return itemList; // these items are our runeword - } - } - } - } + if (itemList.length === runeword.runes.length + 1) { // runes + base + return itemList; // these items are our runeword + } + } + } + } - return false; + return false; }; /** @@ -64,38 +64,38 @@ Runewords.checkRunewords = function () { * @returns {ItemUnit | false} */ Runewords.getBase = function (runeword, base, ethFlag, reroll) { - let item = typeof base === "object" - ? base - : me.getItem(base, sdk.items.mode.inStorage); - - if (item) { - do { - if (item && item.quality < sdk.items.quality.Magic - && item.sockets === runeword.sockets && runeword.itemTypes.includes(item.itemType)) { - /** - * check if item has items socketed in it - * better check than getFlag(sdk.items.flags.Runeword) because randomly socketed items return false for it - */ - - if ((!reroll && !item.getItem() && Item.betterBaseThanWearing(item, Developer.debugging.baseCheck)) - || (reroll && item.getItem() && !NTIP.CheckItem(item, this.pickitEntries) && !Item.autoEquipCheckMerc(item, true) && !Item.autoEquipCheck(item, true))) { - if (!ethFlag || (ethFlag === Roll.Eth && item.ethereal) || (ethFlag === Roll.NonEth && !item.ethereal)) { - return copyUnit(item); - } - } - } - } while (typeof base !== "object" && item.getNext()); - } - - return false; + let item = typeof base === "object" + ? base + : me.getItem(base, sdk.items.mode.inStorage); + + if (item) { + do { + if (item && item.quality < sdk.items.quality.Magic + && item.sockets === runeword.sockets && runeword.itemTypes.includes(item.itemType)) { + /** + * check if item has items socketed in it + * better check than getFlag(sdk.items.flags.Runeword) because randomly socketed items return false for it + */ + + if ((!reroll && !item.getItem() && Item.betterBaseThanWearing(item, Developer.debugging.baseCheck)) + || (reroll && item.getItem() && !NTIP.CheckItem(item, this.pickitEntries) && !Item.autoEquipCheckMerc(item, true) && !Item.autoEquipCheck(item, true))) { + if (!ethFlag || (ethFlag === Roll.Eth && item.ethereal) || (ethFlag === Roll.NonEth && !item.ethereal)) { + return copyUnit(item); + } + } + } + } while (typeof base !== "object" && item.getNext()); + } + + return false; }; Runewords.checkRune = function (...runes) { - if (!Config.MakeRunewords || runes.length < 1) return false; + if (!Config.MakeRunewords || runes.length < 1) return false; - for (let classid of runes) { - if (this.needList.includes(classid)) return true; - } + for (let classid of runes) { + if (this.needList.includes(classid)) return true; + } - return false; + return false; }; diff --git a/libs/SoloPlay/Functions/SkillOverrides.js b/libs/SoloPlay/Functions/SkillOverrides.js index a10c906e..50365c3d 100644 --- a/libs/SoloPlay/Functions/SkillOverrides.js +++ b/libs/SoloPlay/Functions/SkillOverrides.js @@ -10,217 +10,217 @@ includeIfNotIncluded("SoloPlay/Tools/Developer.js"); Skill.forcePacket = (Developer.forcePacketCasting.enabled && !Developer.forcePacketCasting.excludeProfiles.includes(me.profile)); Skill.casterSkills = [ - sdk.skills.FireBolt, sdk.skills.ChargedBolt, sdk.skills.IceBolt, sdk.skills.FrostNova, sdk.skills.IceBlast, sdk.skills.FireBall, - sdk.skills.Nova, sdk.skills.Lightning, sdk.skills.ChainLightning, sdk.skills.Teleport, sdk.skills.GlacialSpike, sdk.skills.Meteor, - sdk.skills.Blizzard, sdk.skills.FrozenOrb, sdk.skills.BoneSpear, sdk.skills.Decrepify, sdk.skills.PoisonNova, sdk.skills.BoneSpirit, - sdk.skills.HolyBolt, sdk.skills.BlessedHammer, sdk.skills.FistoftheHeavens, sdk.skills.Howl, sdk.skills.Taunt, sdk.skills.BattleCry, - sdk.skills.WarCry, sdk.skills.Firestorm, sdk.skills.MoltenBoulder, sdk.skills.ArcticBlast, sdk.skills.Fissure, sdk.skills.Twister, - sdk.skills.Volcano, sdk.skills.Armageddon, sdk.skills.Hurricane, sdk.skills.FireBlast, sdk.skills.ShockField, sdk.skills.ChargedBoltSentry, - /* sdk.skills.WakeofFire, */ sdk.skills.LightningSentry, sdk.skills.DeathSentry + sdk.skills.FireBolt, sdk.skills.ChargedBolt, sdk.skills.IceBolt, sdk.skills.FrostNova, sdk.skills.IceBlast, sdk.skills.FireBall, + sdk.skills.Nova, sdk.skills.Lightning, sdk.skills.ChainLightning, sdk.skills.Teleport, sdk.skills.GlacialSpike, sdk.skills.Meteor, + sdk.skills.Blizzard, sdk.skills.FrozenOrb, sdk.skills.BoneSpear, sdk.skills.Decrepify, sdk.skills.PoisonNova, sdk.skills.BoneSpirit, + sdk.skills.HolyBolt, sdk.skills.BlessedHammer, sdk.skills.FistoftheHeavens, sdk.skills.Howl, sdk.skills.Taunt, sdk.skills.BattleCry, + sdk.skills.WarCry, sdk.skills.Firestorm, sdk.skills.MoltenBoulder, sdk.skills.ArcticBlast, sdk.skills.Fissure, sdk.skills.Twister, + sdk.skills.Volcano, sdk.skills.Armageddon, sdk.skills.Hurricane, sdk.skills.FireBlast, sdk.skills.ShockField, sdk.skills.ChargedBoltSentry, + /* sdk.skills.WakeofFire, */ sdk.skills.LightningSentry, sdk.skills.DeathSentry ]; // Cast a skill on self, Unit or coords. Always use packet casting for caster skills becasue it's more stable. Skill.cast = function (skillId, hand, x, y, item) { - switch (true) { - case me.inTown && !this.townSkill(skillId): // cant cast this in town - case !item && (!Skill.canUse(skillId) || this.getManaCost(skillId) > me.mp): // Dont have this skill or dont have enough mana for this - case !this.wereFormCheck(skillId): // can't cast in wereform - return false; - case skillId === undefined: - throw new Error("Unit.cast: Must supply a skill ID"); - } - - hand === undefined && (hand = this.getHand(skillId)); - x === undefined && (x = me.x); - y === undefined && (y = me.y); - - // Check mana cost, charged skills don't use mana - if (!item && this.getManaCost(skillId) > me.mp) { - // Maybe delay on ALL skills that we don't have enough mana for? - if (Config.AttackSkill.concat([sdk.skills.StaticField, sdk.skills.Teleport]).concat(Config.LowManaSkill).includes(skillId)) { - console.debug("Skill: " + getSkillById(skillId) + " manaCost: " + this.getManaCost(skillId) + " myMana: " + me.mp); - delay(300); - } - - return false; - } - - if (!this.setSkill(skillId, hand, item)) return false; - - if (Config.PacketCasting > 1 || [sdk.skills.Teleport, sdk.skills.Telekinesis].includes(skillId) - || (this.forcePacket && this.casterSkills.includes(skillId) && (!!me.realm || [sdk.skills.Teeth, sdk.skills.Tornado].indexOf(skillId) === -1))) { - switch (typeof x) { - case "number": - Packet.castSkill(hand, x, y); - delay(250); - - break; - case "object": - Packet.unitCast(hand, x); - delay(250); - - break; - } - } else { - let [clickType, shift] = (() => { - switch (hand) { - case sdk.skills.hand.Left: // Left hand + Shift - return [sdk.clicktypes.click.map.LeftDown, sdk.clicktypes.shift.Shift]; - case sdk.skills.hand.LeftNoShift: // Left hand + No Shift - return [sdk.clicktypes.click.map.LeftDown, sdk.clicktypes.shift.NoShift]; - case sdk.skills.hand.RightShift: // Right hand + Shift - return [sdk.clicktypes.click.map.RightDown, sdk.clicktypes.shift.Shift]; - case sdk.skills.hand.Right: // Right hand + No Shift - default: - return [sdk.clicktypes.click.map.RightDown, sdk.clicktypes.shift.NoShift]; - } - })(); - - MainLoop: - for (let n = 0; n < 3; n += 1) { - typeof x === "object" ? clickMap(clickType, shift, x) : clickMap(clickType, shift, x, y); - delay(20); - typeof x === "object" ? clickMap(clickType + 2, shift, x) : clickMap(clickType + 2, shift, x, y); - - for (let i = 0; i < 8; i += 1) { - if (me.attacking) { - break MainLoop; - } - - delay(20); - } - } - - while (me.attacking) { - delay(10); - } - } - - // account for lag, state 121 doesn't kick in immediately - if (this.isTimed(skillId)) { - for (let i = 0; i < 10; i++) { - if ([sdk.player.mode.GettingHit, sdk.player.mode.Blocking].includes(me.mode) || me.skillDelay) { - break; - } - - delay(10); - } - } - - return true; + switch (true) { + case me.inTown && !this.townSkill(skillId): // cant cast this in town + case !item && (!Skill.canUse(skillId) || this.getManaCost(skillId) > me.mp): // Dont have this skill or dont have enough mana for this + case !this.wereFormCheck(skillId): // can't cast in wereform + return false; + case skillId === undefined: + throw new Error("Unit.cast: Must supply a skill ID"); + } + + hand === undefined && (hand = this.getHand(skillId)); + x === undefined && (x = me.x); + y === undefined && (y = me.y); + + // Check mana cost, charged skills don't use mana + if (!item && this.getManaCost(skillId) > me.mp) { + // Maybe delay on ALL skills that we don't have enough mana for? + if (Config.AttackSkill.concat([sdk.skills.StaticField, sdk.skills.Teleport]).concat(Config.LowManaSkill).includes(skillId)) { + console.debug("Skill: " + getSkillById(skillId) + " manaCost: " + this.getManaCost(skillId) + " myMana: " + me.mp); + delay(300); + } + + return false; + } + + if (!this.setSkill(skillId, hand, item)) return false; + + if (Config.PacketCasting > 1 || [sdk.skills.Teleport, sdk.skills.Telekinesis].includes(skillId) + || (this.forcePacket && this.casterSkills.includes(skillId) && (!!me.realm || [sdk.skills.Teeth, sdk.skills.Tornado].indexOf(skillId) === -1))) { + switch (typeof x) { + case "number": + Packet.castSkill(hand, x, y); + delay(250); + + break; + case "object": + Packet.unitCast(hand, x); + delay(250); + + break; + } + } else { + let [clickType, shift] = (() => { + switch (hand) { + case sdk.skills.hand.Left: // Left hand + Shift + return [sdk.clicktypes.click.map.LeftDown, sdk.clicktypes.shift.Shift]; + case sdk.skills.hand.LeftNoShift: // Left hand + No Shift + return [sdk.clicktypes.click.map.LeftDown, sdk.clicktypes.shift.NoShift]; + case sdk.skills.hand.RightShift: // Right hand + Shift + return [sdk.clicktypes.click.map.RightDown, sdk.clicktypes.shift.Shift]; + case sdk.skills.hand.Right: // Right hand + No Shift + default: + return [sdk.clicktypes.click.map.RightDown, sdk.clicktypes.shift.NoShift]; + } + })(); + + MainLoop: + for (let n = 0; n < 3; n += 1) { + typeof x === "object" ? clickMap(clickType, shift, x) : clickMap(clickType, shift, x, y); + delay(20); + typeof x === "object" ? clickMap(clickType + 2, shift, x) : clickMap(clickType + 2, shift, x, y); + + for (let i = 0; i < 8; i += 1) { + if (me.attacking) { + break MainLoop; + } + + delay(20); + } + } + + while (me.attacking) { + delay(10); + } + } + + // account for lag, state 121 doesn't kick in immediately + if (this.isTimed(skillId)) { + for (let i = 0; i < 10; i++) { + if ([sdk.player.mode.GettingHit, sdk.player.mode.Blocking].includes(me.mode) || me.skillDelay) { + break; + } + + delay(10); + } + } + + return true; }; Skill.switchCast = function (skillId, givenSettings = {}) { - const settings = Object.assign({}, { - hand: undefined, - x: undefined, - y: undefined, - switchBack: true, - oSkill: false - }, givenSettings); - - switch (true) { - case me.classic: // No switch in classic - case me.inTown && !this.townSkill(skillId): // cant cast this in town - case this.getManaCost(skillId) > me.mp: // dont have enough mana for this - case !this.wereFormCheck(skillId): // can't cast in wereform - //case (!me.getSkill(skillId, sdk.skills.subindex.SoftPoints) && !settings.oSkill): // Dont have this skill - return false; - case skillId === undefined: - throw new Error("Unit.cast: Must supply a skill ID"); - } - - settings.hand === undefined && (settings.hand = this.getHand(skillId)); - settings.x === undefined && (settings.x = me.x); - settings.y === undefined && (settings.y = me.y); - - // Check mana cost, charged skills don't use mana - if (this.getManaCost(skillId) > me.mp) { - // Maybe delay on ALL skills that we don't have enough mana for? - if (Config.AttackSkill.concat([sdk.skills.StaticField, sdk.skills.Teleport]).concat(Config.LowManaSkill).includes(skillId)) { - delay(300); - } - - return false; - } - - // switch to secondary - me.weaponswitch === 0 && me.switchWeapons(1); - - // Failed to set the skill, switch back - if (!this.setSkill(skillId, settings.hand)) { - me.switchWeapons(0); - return false; - } - - if (me.paladin && settings.hand !== sdk.skills.hand.Right) { - // set aura - Skill.setSkill(Config.AttackSkill[2], sdk.skills.hand.Right); - } - - if ((this.forcePacket && this.casterSkills.includes(skillId) && (!!me.realm || [sdk.skills.Teeth, sdk.skills.Tornado].indexOf(skillId) === -1)) - || Config.PacketCasting > 1 - || skillId === sdk.skills.Teleport) { - switch (typeof settings.x) { - case "number": - Packet.castSkill(settings.hand, settings.x, settings.y); - - break; - case "object": - Packet.unitCast(settings.hand, settings.x); - - break; - } - // make sure we give enough time back so we don't fail our next cast - delay(250); - } else { - let [clickType, shift] = (() => { - switch (settings.hand) { - case sdk.skills.hand.Left: // Left hand + Shift - return [sdk.clicktypes.click.map.LeftDown, sdk.clicktypes.shift.Shift]; - case sdk.skills.hand.LeftNoShift: // Left hand + No Shift - return [sdk.clicktypes.click.map.LeftDown, sdk.clicktypes.shift.NoShift]; - case sdk.skills.hand.RightShift: // Right hand + Shift - return [sdk.clicktypes.click.map.RightDown, sdk.clicktypes.shift.Shift]; - case sdk.skills.hand.Right: // Right hand + No Shift - default: - return [sdk.clicktypes.click.map.RightDown, sdk.clicktypes.shift.NoShift]; - } - })(); - - MainLoop: - for (let n = 0; n < 3; n += 1) { - typeof settings.x === "object" ? clickMap(clickType, shift, settings.x) : clickMap(clickType, shift, settings.x, settings.y); - delay(20); - typeof settings.x === "object" ? clickMap(clickType + 2, shift, settings.x) : clickMap(clickType + 2, shift, settings.x, settings.y); - - for (let i = 0; i < 8; i++) { - if (me.attacking) { - break MainLoop; - } - - delay(20); - } - } - - while (me.attacking) { - delay(10); - } - } - - // account for lag, state 121 doesn't kick in immediately - if (this.isTimed(skillId)) { - for (let i = 0; i < 10; i++) { - if ([sdk.player.mode.GettingHit, sdk.player.mode.Blocking].includes(me.mode) || me.skillDelay) { - break; - } - - delay(10); - } - } - - // switch back to main secondary - me.weaponswitch === 1 && settings.switchBack && me.switchWeapons(0); - - return true; + const settings = Object.assign({}, { + hand: undefined, + x: undefined, + y: undefined, + switchBack: true, + oSkill: false + }, givenSettings); + + switch (true) { + case me.classic: // No switch in classic + case me.inTown && !this.townSkill(skillId): // cant cast this in town + case this.getManaCost(skillId) > me.mp: // dont have enough mana for this + case !this.wereFormCheck(skillId): // can't cast in wereform + //case (!me.getSkill(skillId, sdk.skills.subindex.SoftPoints) && !settings.oSkill): // Dont have this skill + return false; + case skillId === undefined: + throw new Error("Unit.cast: Must supply a skill ID"); + } + + settings.hand === undefined && (settings.hand = this.getHand(skillId)); + settings.x === undefined && (settings.x = me.x); + settings.y === undefined && (settings.y = me.y); + + // Check mana cost, charged skills don't use mana + if (this.getManaCost(skillId) > me.mp) { + // Maybe delay on ALL skills that we don't have enough mana for? + if (Config.AttackSkill.concat([sdk.skills.StaticField, sdk.skills.Teleport]).concat(Config.LowManaSkill).includes(skillId)) { + delay(300); + } + + return false; + } + + // switch to secondary + me.weaponswitch === 0 && me.switchWeapons(1); + + // Failed to set the skill, switch back + if (!this.setSkill(skillId, settings.hand)) { + me.switchWeapons(0); + return false; + } + + if (me.paladin && settings.hand !== sdk.skills.hand.Right) { + // set aura + Skill.setSkill(Config.AttackSkill[2], sdk.skills.hand.Right); + } + + if ((this.forcePacket && this.casterSkills.includes(skillId) && (!!me.realm || [sdk.skills.Teeth, sdk.skills.Tornado].indexOf(skillId) === -1)) + || Config.PacketCasting > 1 + || skillId === sdk.skills.Teleport) { + switch (typeof settings.x) { + case "number": + Packet.castSkill(settings.hand, settings.x, settings.y); + + break; + case "object": + Packet.unitCast(settings.hand, settings.x); + + break; + } + // make sure we give enough time back so we don't fail our next cast + delay(250); + } else { + let [clickType, shift] = (() => { + switch (settings.hand) { + case sdk.skills.hand.Left: // Left hand + Shift + return [sdk.clicktypes.click.map.LeftDown, sdk.clicktypes.shift.Shift]; + case sdk.skills.hand.LeftNoShift: // Left hand + No Shift + return [sdk.clicktypes.click.map.LeftDown, sdk.clicktypes.shift.NoShift]; + case sdk.skills.hand.RightShift: // Right hand + Shift + return [sdk.clicktypes.click.map.RightDown, sdk.clicktypes.shift.Shift]; + case sdk.skills.hand.Right: // Right hand + No Shift + default: + return [sdk.clicktypes.click.map.RightDown, sdk.clicktypes.shift.NoShift]; + } + })(); + + MainLoop: + for (let n = 0; n < 3; n += 1) { + typeof settings.x === "object" ? clickMap(clickType, shift, settings.x) : clickMap(clickType, shift, settings.x, settings.y); + delay(20); + typeof settings.x === "object" ? clickMap(clickType + 2, shift, settings.x) : clickMap(clickType + 2, shift, settings.x, settings.y); + + for (let i = 0; i < 8; i++) { + if (me.attacking) { + break MainLoop; + } + + delay(20); + } + } + + while (me.attacking) { + delay(10); + } + } + + // account for lag, state 121 doesn't kick in immediately + if (this.isTimed(skillId)) { + for (let i = 0; i < 10; i++) { + if ([sdk.player.mode.GettingHit, sdk.player.mode.Blocking].includes(me.mode) || me.skillDelay) { + break; + } + + delay(10); + } + } + + // switch back to main secondary + me.weaponswitch === 1 && settings.switchBack && me.switchWeapons(0); + + return true; }; diff --git a/libs/SoloPlay/Functions/SoloEvents.js b/libs/SoloPlay/Functions/SoloEvents.js index f96294fb..0fd6cbac 100644 --- a/libs/SoloPlay/Functions/SoloEvents.js +++ b/libs/SoloPlay/Functions/SoloEvents.js @@ -6,472 +6,472 @@ */ (function (root, factory) { - if (typeof module === "object" && typeof module.exports === "object") { - let v = factory(); - if (v !== undefined) module.exports = v; - } else if (typeof define === "function" && define.amd) { - define([], factory); - } else { - root.SoloEvents = factory(); - } + if (typeof module === "object" && typeof module.exports === "object") { + let v = factory(); + if (v !== undefined) module.exports = v; + } else if (typeof define === "function" && define.amd) { + define([], factory); + } else { + root.SoloEvents = factory(); + } }(this, function () { - const SoloEvents = { - filePath: "libs/SoloPlay/Threads/EventThread.js", - check: false, - inGame: false, - cloneWalked: false, - townChicken: { - disabled: false, - running: false, - }, - profileResponded: false, - gameInfo: { - gameName: "", - gamePass: "", - }, - - outOfGameCheck: function () { - if (!this.check) return false; - - if (this.gameInfo.gameName.length > 0) { - D2Bot.printToConsole("Kolbot-SoloPlay :: SoloEvents.outOfGameCheck(): Attempting to join other bots game", sdk.colors.D2Bot.Gold); - SoloEvents.inGame = true; - me.blockMouse = true; - - delay(2000); - joinGame(this.gameInfo.gameName, this.gameInfo.gamePass); - - me.blockMouse = false; - - delay(5000); - - while (me.ingame) { - delay(1000); - } - - console.log("ÿc8Kolbot-SoloPlayÿc0: End of SoloEvents.outOfGameCheck()"); - SoloEvents.inGame = false; - SoloEvents.check = false; - this.gameInfo.gameName = ""; - this.gameInfo.gamePass = ""; - - return true; - } - - return false; - }, - - inGameCheck: function () { - if (me.ingame && me.hell && !me.classic && Misc.getPlayerCount() > 1) { - let possibleChars = this.getCharacterNames(); - - for (let i = 0; i < possibleChars.length; i++) { - if (Misc.findPlayer(possibleChars[i].toLowerCase())) { - if (!me.inArea(sdk.areas.RogueEncampment)) { - Town.goToTown(1); - } - - Town.move("stash"); - - let torch, anni, tick = getTickCount(); - - me.overhead("Waiting for charm to drop"); - while (getTickCount() - tick < 120 * 1000) { - anni = me.findItem(sdk.items.SmallCharm, sdk.items.mode.onGround, -1, sdk.items.quality.Unique); - torch = me.findItem(sdk.items.LargeCharm, sdk.items.mode.onGround, -1, sdk.items.quality.Unique); - - if (torch || anni) { - break; - } - } - - if (torch || anni) { - for (let j = 0; j < 12 || me.findItem((anni ? sdk.items.SmallCharm : sdk.items.LargeCharm), sdk.items.mode.inStorage, -1, sdk.items.quality.Unique); j++) { - Town.move("stash"); - me.overhead("Looking for " + (anni ? "Annihilus" : "Torch")); - Pickit.pickItems(); - delay(10000); // 10 seconds - } - } else { - me.overhead("No charm on ground"); - } - - quit(); - return true; - } - } - - console.log("Couldnt find player"); - } - - return false; - }, - - getProfiles: function () { - let profileInfo, realm = me.realm.toLowerCase(), profileList = []; - //realm = "useast"; // testing purposes - - if (!FileTools.exists("logs/Kolbot-SoloPlay/" + realm)) { - return profileList; - } - - let files = dopen("logs/Kolbot-SoloPlay/" + realm + "/").getFiles(); - - for (let i = 0; i < files.length; i++) { - try { - profileInfo = Tracker.readObj("logs/Kolbot-SoloPlay/" + realm + "/" + files[i]); - if (profileList.indexOf(profileInfo.profile) === -1) { - profileList.push(profileInfo.profile); - } - } catch (e) { - console.log(e); - } - } - - return profileList; - }, - - getCharacterNames: function () { - let characterInfo, realm = me.realm.toLowerCase(), charList = []; - //realm = "useast"; // testing purposes - - if (!FileTools.exists("logs/Kolbot-SoloPlay/" + realm)) { - return charList; - } - - let files = dopen("logs/Kolbot-SoloPlay/" + realm + "/").getFiles(); - - for (let i = 0; i < files.length; i++) { - try { - characterInfo = Tracker.readObj("logs/Kolbot-SoloPlay/" + realm + "/" + files[i]); - if (charList.indexOf(characterInfo.charName) === -1) { - charList.push(characterInfo.charName); - } - } catch (e) { - console.log(e); - } - } - - return charList; - }, - - sendToProfile: function (profile, message, mode = 65) { - if (profile.toLowerCase() !== me.profile.toLowerCase()) { - sendCopyData(null, profile, mode, JSON.stringify(message)); - } - }, - - sendToList: function (message, mode = 55) { - let profiles = this.getProfiles(); - - if (!profiles || profiles === undefined) { - return false; - } - - return profiles.forEach((profileName) => { - if (profileName.toLowerCase() !== me.profile.toLowerCase()) { - sendCopyData(null, profileName, mode, JSON.stringify(message)); - } - }); - }, - - dropCharm: function (charm) { - if (!charm || charm === undefined) return false; - - D2Bot.printToConsole("Kolbot-SoloPlay :: Dropping " + charm.name, sdk.colors.D2Bot.Orange); - let orginalLocation = { act: me.act, area: me.area, x: me.x, y: me.y }; - - if (!me.inTown) { - Town.goToTown(1); - Town.move("stash"); - charm.drop(); - } - - if (me.act !== orginalLocation.act) { - Town.goToTown(orginalLocation.act); - } - - Town.move("portalspot"); - - if (!Pather.usePortal(orginalLocation.area)) { - Pather.journeyTo(orginalLocation.area); - } - - return Pather.moveTo(orginalLocation.x, orginalLocation.y); - }, - - // @todo redo this, I think better option would be to make this it's own script - // end the current script but insert it to be continued after dclone is dead - killdclone: function () { - D2Bot.printToConsole("Kolbot-SoloPlay :: Trying to kill DClone.", sdk.colors.D2Bot.Orange); - let orginalLocation = { area: me.area, x: me.x, y: me.y }; - - !me.inTown && Town.goToTown(); - - if (me.accessToAct(2) && Pather.checkWP(sdk.areas.ArcaneSanctuary)) { - Pather.useWaypoint(sdk.areas.ArcaneSanctuary); - Precast.doPrecast(true); - - if (!Pather.usePortal(null)) { - console.log("ÿc8Kolbot-SoloPlayÿc1: Failed to move to Palace Cellar"); - } - } else if (Pather.checkWP(sdk.areas.InnerCloister)) { - Pather.useWaypoint(sdk.areas.InnerCloister); - Pather.moveTo(20047, 4898); - } else { - Pather.useWaypoint(sdk.areas.ColdPlains); - Pather.moveToExit([sdk.areas.BloodMoor, sdk.areas.DenofEvil], true); - Pather.moveToPreset(me.area, sdk.unittype.Monster, sdk.monsters.preset.Corpsefire, 0, 0, false, true); - } - - Attack.killTarget(sdk.monsters.DiabloClone); - Pickit.pickItems(); - - let newAnni = Game.getItem(sdk.items.SmallCharm, sdk.items.mode.onGround); - let oldAnni = me.findItem(sdk.items.SmallCharm, sdk.items.mode.inStorage, -1, sdk.items.quality.Unique); - - if (newAnni && oldAnni) { - this.sendToList({ profile: me.profile, ladder: me.ladder }, 60); - - let tick = getTickCount(); - - while (getTickCount() - tick < 10000) { - me.overhead("Waiting to see if I get a response from other profiles"); - - if (this.profileResponded) { - me.overhead("Recieved response, dropping old Annihilus in Rogue Encampment"); - break; - } - - delay(50); - } - - if (newAnni && oldAnni && this.profileResponded) { - this.dropCharm(oldAnni); - } else { - me.overhead("No response from other profiles"); - } - - SoloEvents.profileResponded = false; - Pickit.pickItems(); - } - - if ((newAnni && oldAnni && !this.profileResponded) && AutoMule.getInfo() && AutoMule.getInfo().hasOwnProperty("torchMuleInfo")) { - scriptBroadcast("muleAnni"); - } - - // Move back to where we orignally where - Pather.journeyTo(orginalLocation.area); - Pather.moveTo(orginalLocation.x, orginalLocation.y); - SoloEvents.cloneWalked = false; - }, - - moveSettings: { - allowTeleport: false, - allowClearing: false, - allowPicking: false, - allowTown: false, - retry: 10, - }, - - moveTo: function (x, y, givenSettings) { - // Abort if dead - if (me.dead) return false; - return Pather.move({ x: x, y: y }, Object.assign({}, SoloEvents.moveSettings, givenSettings)); - }, - - skip: function () { - let tick = getTickCount(); - myPrint("Attempting baal wave skip"); - - try { - // Disable anything that will cause us to stop - [Precast.enabled, Pickit.enabled] = [false, false]; - SoloEvents.townChicken.disabled = true; - me.barbarian && (Config.FindItem = false); - - // Prep, move to throne entrance - while (getTickCount() - tick < 6500) { - this.moveTo(15091, 5073, { allowTeleport: true }); - } - - tick = getTickCount(); - - // 5 second delay (5000ms), then leave throne - while (getTickCount() - tick < 5000) { - this.moveTo(15098, 5082, { allowTeleport: true }); - } - - tick = getTickCount(); - this.moveTo(15099, 5078); // Re-enter throne - - // 2 second delay (2000ms) - while (getTickCount() - tick < 2000) { - this.moveTo(15098, 5082); - } - - this.moveTo(15099, 5078); - } finally { - // Re-enable - [Precast.enabled, Pickit.enabled] = [true, true]; - SoloEvents.townChicken.disabled = false; - } - - let skipWorked = getUnits(sdk.unittype.Monster) - .some(mon => mon.attackable && mon.x >= 15072 && mon.x <= 15118 && mon.y >= 5002 && mon.y <= 5079); - myPrint("skip " + (skipWorked ? "worked" : "failed")); - }, - - dodge: function () { - let diablo = Game.getMonster(sdk.monsters.Diablo); - // Credit @Jaenster - const shouldDodge = function (coord) { - return !!diablo && getUnits(sdk.unittype.Missile) - // For every missle that isnt from our merc - .filter((missile) => missile && diablo && diablo.gid === missile.owner) - // if any - .some(function (missile) { - let xoff = Math.abs(coord.x - missile.targetx); - let yoff = Math.abs(coord.y - missile.targety); - let xdist = Math.abs(coord.x - missile.x); - let ydist = Math.abs(coord.y - missile.y); - // If missile wants to hit is and is close to us - return xoff < 10 && yoff < 10 && xdist < 15 && ydist < 15; - }); - }; - - if (diablo && shouldDodge(me)) { - let tick = getTickCount(); - let overrides = { allowTeleport: false, allowClearing: false, allowTown: false }; - - try { - // Disable anything that will cause us to stop - [Precast.enabled, Pickit.enabled] = [false, false]; - SoloEvents.townChicken.disabled = true; - console.log("DODGE"); - // Disable things that will cause us to stop - let dist = me.assassin ? 15 : 3; - - while (getTickCount() - tick < 2000) { - // Above D - if (me.y <= diablo.y) { - // Move east - me.x <= diablo.x && this.moveTo(diablo.x + dist, diablo.y, overrides); - // Move south - me.x > diablo.x && this.moveTo(diablo.x, diablo.y + dist, overrides); - } - - // Below D - if (me.y > diablo.y) { - // Move west - me.x >= diablo.x && this.moveTo(diablo.x - dist, diablo.y, overrides); - // Move north - me.x < diablo.x && this.moveTo(diablo.x, diablo.y - dist, overrides); - } - } - } finally { - // Re-enable - [Precast.enabled, Pickit.enabled] = [true, true]; - SoloEvents.townChicken.disabled = false; - } - } - }, - - finishDen: function () { - Pickit.pickItems(); - - // No Tome, or tome has no tps, or no scrolls - if (!me.canTpToTown() && !me.inTown) { - // should really check how close the town exit is - Pather.moveToExit([sdk.areas.BloodMoor, sdk.areas.ColdPlains], true); - Pather.getWP(sdk.areas.ColdPlains); - Pather.useWaypoint(sdk.areas.RogueEncampment); - } else { - Town.goToTown(1); - } - - Town.npcInteract("akara"); - }, - - bugAndy: function () { - Town.goToTown(); - Pather.changeAct(); - delay(2000 + me.ping); - - // Now check my area - if (me.act === 2) { - // Act change sucessful, Andy has been bugged - myPrint("Andy bug " + (!me.getQuest(sdk.quest.id.SistersToTheSlaughter, 15) ? "sucessful" : "failed")); - scriptBroadcast("quit"); - } - }, - - diaEvent: function (bytes = []) { - if (!bytes.length) return; - // dia lightning - if (bytes[0] === 0x4C && bytes[6] === 193) { - // Messaging.sendToScript(SoloEvents.filePath, "dodge"); - me.emit("soloEvent", "dodge"); - } - }, - - skippedWaves: [], - - baalEvent: function (bytes = []) { - if (!bytes.length) return; - // baal wave - if (bytes[0] === 0xA4) { - if ((me.hell && me.paladin && !Attack.auradin) - || me.barbarian - || me.gold < 5000 - || (!me.baal && SetUp.finalBuild !== "Bumper")) { - let waveMonster = ((bytes[1]) | (bytes[2] << 8)); - let wave = [ - sdk.monsters.WarpedShaman, - sdk.monsters.BaalSubjectMummy, - sdk.monsters.Council4, - sdk.monsters.VenomLord2, - sdk.monsters.ListerTheTormenter - ].indexOf(waveMonster); - console.debug("Wave # " + wave); - if (SoloEvents.skippedWaves.includes(wave)) return; - const waveBoss = { - COLENZO: 0, - ACHMEL: 1, - BARTUC: 2, - VENTAR: 3, - LISTER: 4 - }; - - switch (wave) { - case waveBoss.COLENZO: - break; - case waveBoss.ACHMEL: - if ((me.paladin && !Attack.auradin && me.hell) - || (me.barbarian && ((me.charlvl < CharInfo.levelCap && !me.baal) - || me.hardcore))) { - Messaging.sendToScript(SoloEvents.filePath, "skip"); - SoloEvents.skippedWaves.push(wave); - } - - break; - case waveBoss.BARTUC: - case waveBoss.VENTAR: - break; - case waveBoss.LISTER: - if ((me.barbarian && (me.charlvl < CharInfo.levelCap || !me.baal || me.hardcore)) - || (me.charlvl < CharInfo.levelCap && (me.gold < 5000 || (!me.baal && SetUp.finalBuild !== "Bumper")))) { - // Messaging.sendToScript(SoloEvents.filePath, "skip"); - me.emit("soloEvent", "skip"); - SoloEvents.skippedWaves.push(wave); - } - - break; - } - } - } - }, - }; - - return SoloEvents; + const SoloEvents = { + filePath: "libs/SoloPlay/Threads/EventThread.js", + check: false, + inGame: false, + cloneWalked: false, + townChicken: { + disabled: false, + running: false, + }, + profileResponded: false, + gameInfo: { + gameName: "", + gamePass: "", + }, + + outOfGameCheck: function () { + if (!this.check) return false; + + if (this.gameInfo.gameName.length > 0) { + D2Bot.printToConsole("Kolbot-SoloPlay :: SoloEvents.outOfGameCheck(): Attempting to join other bots game", sdk.colors.D2Bot.Gold); + SoloEvents.inGame = true; + me.blockMouse = true; + + delay(2000); + joinGame(this.gameInfo.gameName, this.gameInfo.gamePass); + + me.blockMouse = false; + + delay(5000); + + while (me.ingame) { + delay(1000); + } + + console.log("ÿc8Kolbot-SoloPlayÿc0: End of SoloEvents.outOfGameCheck()"); + SoloEvents.inGame = false; + SoloEvents.check = false; + this.gameInfo.gameName = ""; + this.gameInfo.gamePass = ""; + + return true; + } + + return false; + }, + + inGameCheck: function () { + if (me.ingame && me.hell && !me.classic && Misc.getPlayerCount() > 1) { + let possibleChars = this.getCharacterNames(); + + for (let i = 0; i < possibleChars.length; i++) { + if (Misc.findPlayer(possibleChars[i].toLowerCase())) { + if (!me.inArea(sdk.areas.RogueEncampment)) { + Town.goToTown(1); + } + + Town.move("stash"); + + let torch, anni, tick = getTickCount(); + + me.overhead("Waiting for charm to drop"); + while (getTickCount() - tick < 120 * 1000) { + anni = me.findItem(sdk.items.SmallCharm, sdk.items.mode.onGround, -1, sdk.items.quality.Unique); + torch = me.findItem(sdk.items.LargeCharm, sdk.items.mode.onGround, -1, sdk.items.quality.Unique); + + if (torch || anni) { + break; + } + } + + if (torch || anni) { + for (let j = 0; j < 12 || me.findItem((anni ? sdk.items.SmallCharm : sdk.items.LargeCharm), sdk.items.mode.inStorage, -1, sdk.items.quality.Unique); j++) { + Town.move("stash"); + me.overhead("Looking for " + (anni ? "Annihilus" : "Torch")); + Pickit.pickItems(); + delay(10000); // 10 seconds + } + } else { + me.overhead("No charm on ground"); + } + + quit(); + return true; + } + } + + console.log("Couldnt find player"); + } + + return false; + }, + + getProfiles: function () { + let profileInfo, realm = me.realm.toLowerCase(), profileList = []; + //realm = "useast"; // testing purposes + + if (!FileTools.exists("logs/Kolbot-SoloPlay/" + realm)) { + return profileList; + } + + let files = dopen("logs/Kolbot-SoloPlay/" + realm + "/").getFiles(); + + for (let i = 0; i < files.length; i++) { + try { + profileInfo = Tracker.readObj("logs/Kolbot-SoloPlay/" + realm + "/" + files[i]); + if (profileList.indexOf(profileInfo.profile) === -1) { + profileList.push(profileInfo.profile); + } + } catch (e) { + console.log(e); + } + } + + return profileList; + }, + + getCharacterNames: function () { + let characterInfo, realm = me.realm.toLowerCase(), charList = []; + //realm = "useast"; // testing purposes + + if (!FileTools.exists("logs/Kolbot-SoloPlay/" + realm)) { + return charList; + } + + let files = dopen("logs/Kolbot-SoloPlay/" + realm + "/").getFiles(); + + for (let i = 0; i < files.length; i++) { + try { + characterInfo = Tracker.readObj("logs/Kolbot-SoloPlay/" + realm + "/" + files[i]); + if (charList.indexOf(characterInfo.charName) === -1) { + charList.push(characterInfo.charName); + } + } catch (e) { + console.log(e); + } + } + + return charList; + }, + + sendToProfile: function (profile, message, mode = 65) { + if (profile.toLowerCase() !== me.profile.toLowerCase()) { + sendCopyData(null, profile, mode, JSON.stringify(message)); + } + }, + + sendToList: function (message, mode = 55) { + let profiles = this.getProfiles(); + + if (!profiles || profiles === undefined) { + return false; + } + + return profiles.forEach((profileName) => { + if (profileName.toLowerCase() !== me.profile.toLowerCase()) { + sendCopyData(null, profileName, mode, JSON.stringify(message)); + } + }); + }, + + dropCharm: function (charm) { + if (!charm || charm === undefined) return false; + + D2Bot.printToConsole("Kolbot-SoloPlay :: Dropping " + charm.name, sdk.colors.D2Bot.Orange); + let orginalLocation = { act: me.act, area: me.area, x: me.x, y: me.y }; + + if (!me.inTown) { + Town.goToTown(1); + Town.move("stash"); + charm.drop(); + } + + if (me.act !== orginalLocation.act) { + Town.goToTown(orginalLocation.act); + } + + Town.move("portalspot"); + + if (!Pather.usePortal(orginalLocation.area)) { + Pather.journeyTo(orginalLocation.area); + } + + return Pather.moveTo(orginalLocation.x, orginalLocation.y); + }, + + // @todo redo this, I think better option would be to make this it's own script + // end the current script but insert it to be continued after dclone is dead + killdclone: function () { + D2Bot.printToConsole("Kolbot-SoloPlay :: Trying to kill DClone.", sdk.colors.D2Bot.Orange); + let orginalLocation = { area: me.area, x: me.x, y: me.y }; + + !me.inTown && Town.goToTown(); + + if (me.accessToAct(2) && Pather.checkWP(sdk.areas.ArcaneSanctuary)) { + Pather.useWaypoint(sdk.areas.ArcaneSanctuary); + Precast.doPrecast(true); + + if (!Pather.usePortal(null)) { + console.log("ÿc8Kolbot-SoloPlayÿc1: Failed to move to Palace Cellar"); + } + } else if (Pather.checkWP(sdk.areas.InnerCloister)) { + Pather.useWaypoint(sdk.areas.InnerCloister); + Pather.moveTo(20047, 4898); + } else { + Pather.useWaypoint(sdk.areas.ColdPlains); + Pather.moveToExit([sdk.areas.BloodMoor, sdk.areas.DenofEvil], true); + Pather.moveToPreset(me.area, sdk.unittype.Monster, sdk.monsters.preset.Corpsefire, 0, 0, false, true); + } + + Attack.killTarget(sdk.monsters.DiabloClone); + Pickit.pickItems(); + + let newAnni = Game.getItem(sdk.items.SmallCharm, sdk.items.mode.onGround); + let oldAnni = me.findItem(sdk.items.SmallCharm, sdk.items.mode.inStorage, -1, sdk.items.quality.Unique); + + if (newAnni && oldAnni) { + this.sendToList({ profile: me.profile, ladder: me.ladder }, 60); + + let tick = getTickCount(); + + while (getTickCount() - tick < 10000) { + me.overhead("Waiting to see if I get a response from other profiles"); + + if (this.profileResponded) { + me.overhead("Recieved response, dropping old Annihilus in Rogue Encampment"); + break; + } + + delay(50); + } + + if (newAnni && oldAnni && this.profileResponded) { + this.dropCharm(oldAnni); + } else { + me.overhead("No response from other profiles"); + } + + SoloEvents.profileResponded = false; + Pickit.pickItems(); + } + + if ((newAnni && oldAnni && !this.profileResponded) && AutoMule.getInfo() && AutoMule.getInfo().hasOwnProperty("torchMuleInfo")) { + scriptBroadcast("muleAnni"); + } + + // Move back to where we orignally where + Pather.journeyTo(orginalLocation.area); + Pather.moveTo(orginalLocation.x, orginalLocation.y); + SoloEvents.cloneWalked = false; + }, + + moveSettings: { + allowTeleport: false, + allowClearing: false, + allowPicking: false, + allowTown: false, + retry: 10, + }, + + moveTo: function (x, y, givenSettings) { + // Abort if dead + if (me.dead) return false; + return Pather.move({ x: x, y: y }, Object.assign({}, SoloEvents.moveSettings, givenSettings)); + }, + + skip: function () { + let tick = getTickCount(); + myPrint("Attempting baal wave skip"); + + try { + // Disable anything that will cause us to stop + [Precast.enabled, Pickit.enabled] = [false, false]; + SoloEvents.townChicken.disabled = true; + me.barbarian && (Config.FindItem = false); + + // Prep, move to throne entrance + while (getTickCount() - tick < 6500) { + this.moveTo(15091, 5073, { allowTeleport: true }); + } + + tick = getTickCount(); + + // 5 second delay (5000ms), then leave throne + while (getTickCount() - tick < 5000) { + this.moveTo(15098, 5082, { allowTeleport: true }); + } + + tick = getTickCount(); + this.moveTo(15099, 5078); // Re-enter throne + + // 2 second delay (2000ms) + while (getTickCount() - tick < 2000) { + this.moveTo(15098, 5082); + } + + this.moveTo(15099, 5078); + } finally { + // Re-enable + [Precast.enabled, Pickit.enabled] = [true, true]; + SoloEvents.townChicken.disabled = false; + } + + let skipWorked = getUnits(sdk.unittype.Monster) + .some(mon => mon.attackable && mon.x >= 15072 && mon.x <= 15118 && mon.y >= 5002 && mon.y <= 5079); + myPrint("skip " + (skipWorked ? "worked" : "failed")); + }, + + dodge: function () { + let diablo = Game.getMonster(sdk.monsters.Diablo); + // Credit @Jaenster + const shouldDodge = function (coord) { + return !!diablo && getUnits(sdk.unittype.Missile) + // For every missle that isnt from our merc + .filter((missile) => missile && diablo && diablo.gid === missile.owner) + // if any + .some(function (missile) { + let xoff = Math.abs(coord.x - missile.targetx); + let yoff = Math.abs(coord.y - missile.targety); + let xdist = Math.abs(coord.x - missile.x); + let ydist = Math.abs(coord.y - missile.y); + // If missile wants to hit is and is close to us + return xoff < 10 && yoff < 10 && xdist < 15 && ydist < 15; + }); + }; + + if (diablo && shouldDodge(me)) { + let tick = getTickCount(); + let overrides = { allowTeleport: false, allowClearing: false, allowTown: false }; + + try { + // Disable anything that will cause us to stop + [Precast.enabled, Pickit.enabled] = [false, false]; + SoloEvents.townChicken.disabled = true; + console.log("DODGE"); + // Disable things that will cause us to stop + let dist = me.assassin ? 15 : 3; + + while (getTickCount() - tick < 2000) { + // Above D + if (me.y <= diablo.y) { + // Move east + me.x <= diablo.x && this.moveTo(diablo.x + dist, diablo.y, overrides); + // Move south + me.x > diablo.x && this.moveTo(diablo.x, diablo.y + dist, overrides); + } + + // Below D + if (me.y > diablo.y) { + // Move west + me.x >= diablo.x && this.moveTo(diablo.x - dist, diablo.y, overrides); + // Move north + me.x < diablo.x && this.moveTo(diablo.x, diablo.y - dist, overrides); + } + } + } finally { + // Re-enable + [Precast.enabled, Pickit.enabled] = [true, true]; + SoloEvents.townChicken.disabled = false; + } + } + }, + + finishDen: function () { + Pickit.pickItems(); + + // No Tome, or tome has no tps, or no scrolls + if (!me.canTpToTown() && !me.inTown) { + // should really check how close the town exit is + Pather.moveToExit([sdk.areas.BloodMoor, sdk.areas.ColdPlains], true); + Pather.getWP(sdk.areas.ColdPlains); + Pather.useWaypoint(sdk.areas.RogueEncampment); + } else { + Town.goToTown(1); + } + + Town.npcInteract("akara"); + }, + + bugAndy: function () { + Town.goToTown(); + Pather.changeAct(); + delay(2000 + me.ping); + + // Now check my area + if (me.act === 2) { + // Act change sucessful, Andy has been bugged + myPrint("Andy bug " + (!me.getQuest(sdk.quest.id.SistersToTheSlaughter, 15) ? "sucessful" : "failed")); + scriptBroadcast("quit"); + } + }, + + diaEvent: function (bytes = []) { + if (!bytes.length) return; + // dia lightning + if (bytes[0] === 0x4C && bytes[6] === 193) { + // Messaging.sendToScript(SoloEvents.filePath, "dodge"); + me.emit("soloEvent", "dodge"); + } + }, + + skippedWaves: [], + + baalEvent: function (bytes = []) { + if (!bytes.length) return; + // baal wave + if (bytes[0] === 0xA4) { + if ((me.hell && me.paladin && !Attack.auradin) + || me.barbarian + || me.gold < 5000 + || (!me.baal && SetUp.finalBuild !== "Bumper")) { + let waveMonster = ((bytes[1]) | (bytes[2] << 8)); + let wave = [ + sdk.monsters.WarpedShaman, + sdk.monsters.BaalSubjectMummy, + sdk.monsters.Council4, + sdk.monsters.VenomLord2, + sdk.monsters.ListerTheTormenter + ].indexOf(waveMonster); + console.debug("Wave # " + wave); + if (SoloEvents.skippedWaves.includes(wave)) return; + const waveBoss = { + COLENZO: 0, + ACHMEL: 1, + BARTUC: 2, + VENTAR: 3, + LISTER: 4 + }; + + switch (wave) { + case waveBoss.COLENZO: + break; + case waveBoss.ACHMEL: + if ((me.paladin && !Attack.auradin && me.hell) + || (me.barbarian && ((me.charlvl < CharInfo.levelCap && !me.baal) + || me.hardcore))) { + Messaging.sendToScript(SoloEvents.filePath, "skip"); + SoloEvents.skippedWaves.push(wave); + } + + break; + case waveBoss.BARTUC: + case waveBoss.VENTAR: + break; + case waveBoss.LISTER: + if ((me.barbarian && (me.charlvl < CharInfo.levelCap || !me.baal || me.hardcore)) + || (me.charlvl < CharInfo.levelCap && (me.gold < 5000 || (!me.baal && SetUp.finalBuild !== "Bumper")))) { + // Messaging.sendToScript(SoloEvents.filePath, "skip"); + me.emit("soloEvent", "skip"); + SoloEvents.skippedWaves.push(wave); + } + + break; + } + } + } + }, + }; + + return SoloEvents; })); diff --git a/libs/SoloPlay/Functions/SoloWants.js b/libs/SoloPlay/Functions/SoloWants.js index 3b22441f..f1dc8a76 100644 --- a/libs/SoloPlay/Functions/SoloWants.js +++ b/libs/SoloPlay/Functions/SoloWants.js @@ -6,253 +6,253 @@ */ const SoloWants = { - needList: [], - validGids: [], + needList: [], + validGids: [], - /** - * @param {ItemUnit} item - * @returns {boolean} - */ - checkItem: function (item) { - if (!item) return false; - if (this.validGids.includes(item.gid)) return true; - let i = 0; - for (let el of this.needList) { - if (item.isInsertable) { - if (el.needed.includes(item.classid)) { - this.validGids.push(item.gid); - this.needList[i].needed.splice(this.needList[i].needed.indexOf(item.classid), 1); - if (this.needList[i].needed.length === 0) { - // no more needed items so remove from list - this.needList.splice(i, 1); - } - return true; - } - } - i++; // keep track of index - } + /** + * @param {ItemUnit} item + * @returns {boolean} + */ + checkItem: function (item) { + if (!item) return false; + if (this.validGids.includes(item.gid)) return true; + let i = 0; + for (let el of this.needList) { + if (item.isInsertable) { + if (el.needed.includes(item.classid)) { + this.validGids.push(item.gid); + this.needList[i].needed.splice(this.needList[i].needed.indexOf(item.classid), 1); + if (this.needList[i].needed.length === 0) { + // no more needed items so remove from list + this.needList.splice(i, 1); + } + return true; + } + } + i++; // keep track of index + } - return false; - }, + return false; + }, - /** - * @param {ItemUnit} item - * @returns {boolean} - */ - keepItem: function (item) { - if (!item) return false; - return this.validGids.includes(item.gid); - }, + /** + * @param {ItemUnit} item + * @returns {boolean} + */ + keepItem: function (item) { + if (!item) return false; + return this.validGids.includes(item.gid); + }, - buildList: function () { - let myItems = me.getItemsEx() - .filter(function (item) { - if (item.isRuneword || item.questItem) return false; - return item.quality >= sdk.items.quality.Normal && (item.sockets > 0 || getBaseStat("items", item.classid, "gemsockets") > 0); - }); - myItems - .filter(item => item.isEquipped) - .forEach(item => SoloWants.addToList(item)); - myItems - .filter(item => item.isInStorage && item.quality >= sdk.items.quality.Magic && item.getItemType() && AutoEquip.wanted(item)) - .forEach(item => SoloWants.addToList(item)); - - return myItems.forEach(item => SoloWants.checkItem(item)); - }, + buildList: function () { + let myItems = me.getItemsEx() + .filter(function (item) { + if (item.isRuneword || item.questItem) return false; + return item.quality >= sdk.items.quality.Normal && (item.sockets > 0 || getBaseStat("items", item.classid, "gemsockets") > 0); + }); + myItems + .filter(item => item.isEquipped) + .forEach(item => SoloWants.addToList(item)); + myItems + .filter(item => item.isInStorage && item.quality >= sdk.items.quality.Magic && item.getItemType() && AutoEquip.wanted(item)) + .forEach(item => SoloWants.addToList(item)); + + return myItems.forEach(item => SoloWants.checkItem(item)); + }, - /** - * @param {ItemUnit} item - * @returns {boolean} - */ - addToList: function (item) { - if (!item || me.classic || item.isRuneword) return false; - if (SoloWants.needList.some(check => item.classid === check.classid)) return false; - let hasWantedItems; - let list = []; - let socketedWith = item.getItemsEx(); - let numSockets = item.sockets; - let curr = Config.socketables.find(({ classid }) => item.classid === classid); + /** + * @param {ItemUnit} item + * @returns {boolean} + */ + addToList: function (item) { + if (!item || me.classic || item.isRuneword) return false; + if (SoloWants.needList.some(check => item.classid === check.classid)) return false; + let hasWantedItems; + let list = []; + let socketedWith = item.getItemsEx(); + let numSockets = item.sockets; + let curr = Config.socketables.find(({ classid }) => item.classid === classid); - if (curr && curr.socketWith.length > 0) { - hasWantedItems = socketedWith.some(el => curr.socketWith.includes(el.classid)); - if (hasWantedItems && socketedWith.length === numSockets) { - return true; // this item is full - } + if (curr && curr.socketWith.length > 0) { + hasWantedItems = socketedWith.some(el => curr.socketWith.includes(el.classid)); + if (hasWantedItems && socketedWith.length === numSockets) { + return true; // this item is full + } - if (curr.socketWith.includes(sdk.items.runes.Hel)) { - let merc = me.getMerc(); - switch (true) { - case Item.autoEquipCheck(item, true) && me.trueStr >= item.strreq && me.trueDex >= item.dexreq: - case Item.autoEquipCheckMerc(item, true) && !!merc && merc.rawStrength >= item.strreq && merc.rawDexterity >= item.dexreq: - curr.socketWith.splice(curr.socketWith.indexOf(sdk.items.runes.Hel), 1); - break; - } - } + if (curr.socketWith.includes(sdk.items.runes.Hel)) { + let merc = me.getMerc(); + switch (true) { + case Item.autoEquipCheck(item, true) && me.trueStr >= item.strreq && me.trueDex >= item.dexreq: + case Item.autoEquipCheckMerc(item, true) && !!merc && merc.rawStrength >= item.strreq && merc.rawDexterity >= item.dexreq: + curr.socketWith.splice(curr.socketWith.indexOf(sdk.items.runes.Hel), 1); + break; + } + } - if (curr.socketWith.length > 1 && hasWantedItems) { - // handle different wanted socketables, if we already have a wanted socketable inserted then remove it from the check list - socketedWith.forEach(function (socketed) { - if (curr.socketWith.length > 1 && curr.socketWith.includes(socketed.classid)) { - curr.socketWith.splice(curr.socketWith.indexOf(socketed.classid), 1); - } - }); - } + if (curr.socketWith.length > 1 && hasWantedItems) { + // handle different wanted socketables, if we already have a wanted socketable inserted then remove it from the check list + socketedWith.forEach(function (socketed) { + if (curr.socketWith.length > 1 && curr.socketWith.includes(socketed.classid)) { + curr.socketWith.splice(curr.socketWith.indexOf(socketed.classid), 1); + } + }); + } - // add the wanted items to the list - for (let i = 0; i < numSockets - (hasWantedItems ? socketedWith.length : 0); i++) { - // handle different wanted socketables - curr.socketWith.length === numSockets ? list.push(curr.socketWith[i]) : list.push(curr.socketWith[0]); - } + // add the wanted items to the list + for (let i = 0; i < numSockets - (hasWantedItems ? socketedWith.length : 0); i++) { + // handle different wanted socketables + curr.socketWith.length === numSockets ? list.push(curr.socketWith[i]) : list.push(curr.socketWith[0]); + } - // currently no sockets but we might use our socket quest on it - numSockets === 0 && curr.useSocketQuest && list.push(curr.socketWith[0]); + // currently no sockets but we might use our socket quest on it + numSockets === 0 && curr.useSocketQuest && list.push(curr.socketWith[0]); - // if temp socketables are used for this item and its not already socketed with wanted items add the temp items too - if (!hasWantedItems && !!curr.temp && !!curr.temp.length > 0) { - for (let i = 0; i < numSockets - socketedWith.length; i++) { - list.push(curr.temp[0]); - } - // Make sure we keep a hel rune so we can unsocket temp socketables if needed - if (!SoloWants.needList.some(check => sdk.items.runes.Hel === check.classid)) { - let hel = me.getItemsEx(sdk.items.runes.Hel, sdk.items.mode.inStorage); - // we don't have any hel runes and its not already in our needList - if ((!hel || hel.length === 0)) { - SoloWants.needList.push({ classid: sdk.items.runes.Hel, needed: [sdk.items.runes.Hel] }); - } else if (!hel.some(check => SoloWants.validGids.includes(check.gid))) { - SoloWants.needList.push({ classid: sdk.items.runes.Hel, needed: [sdk.items.runes.Hel] }); - } - } - } - } else { - let itemtype = item.getItemType(); - if (!itemtype) return false; - let gemType = ["Helmet", "Armor"].includes(itemtype) - ? "Ruby" : itemtype === "Shield" - ? "Diamond" : itemtype === "Weapon" && !Check.currentBuild().caster - ? "Skull" : ""; - let runeType; + // if temp socketables are used for this item and its not already socketed with wanted items add the temp items too + if (!hasWantedItems && !!curr.temp && !!curr.temp.length > 0) { + for (let i = 0; i < numSockets - socketedWith.length; i++) { + list.push(curr.temp[0]); + } + // Make sure we keep a hel rune so we can unsocket temp socketables if needed + if (!SoloWants.needList.some(check => sdk.items.runes.Hel === check.classid)) { + let hel = me.getItemsEx(sdk.items.runes.Hel, sdk.items.mode.inStorage); + // we don't have any hel runes and its not already in our needList + if ((!hel || hel.length === 0)) { + SoloWants.needList.push({ classid: sdk.items.runes.Hel, needed: [sdk.items.runes.Hel] }); + } else if (!hel.some(check => SoloWants.validGids.includes(check.gid))) { + SoloWants.needList.push({ classid: sdk.items.runes.Hel, needed: [sdk.items.runes.Hel] }); + } + } + } + } else { + let itemtype = item.getItemType(); + if (!itemtype) return false; + let gemType = ["Helmet", "Armor"].includes(itemtype) + ? "Ruby" : itemtype === "Shield" + ? "Diamond" : itemtype === "Weapon" && !Check.currentBuild().caster + ? "Skull" : ""; + let runeType; - // Tir rune in normal, Io rune otherwise and Shael's if assassin TODO: use jewels too - !gemType && (runeType = me.normal ? "Tir" : me.assassin ? "Shael" : "Io"); + // Tir rune in normal, Io rune otherwise and Shael's if assassin TODO: use jewels too + !gemType && (runeType = me.normal ? "Tir" : me.assassin ? "Shael" : "Io"); - hasWantedItems = socketedWith.some(el => gemType ? el.itemType === sdk.items.type[gemType] : el.classid === sdk.items.runes[runeType]); - if (hasWantedItems && socketedWith.length === numSockets) { - return true; // this item is full - } + hasWantedItems = socketedWith.some(el => gemType ? el.itemType === sdk.items.type[gemType] : el.classid === sdk.items.runes[runeType]); + if (hasWantedItems && socketedWith.length === numSockets) { + return true; // this item is full + } - for (let i = 0; i < numSockets - socketedWith.length; i++) { - list.push(gemType ? sdk.items.gems.Perfect[gemType] : sdk.items.runes[runeType]); - } - } + for (let i = 0; i < numSockets - socketedWith.length; i++) { + list.push(gemType ? sdk.items.gems.Perfect[gemType] : sdk.items.runes[runeType]); + } + } - // add to our needList so we pick the items - return list.length > 0 ? this.needList.push({ classid: item.classid, needed: list }) : false; - }, + // add to our needList so we pick the items + return list.length > 0 ? this.needList.push({ classid: item.classid, needed: list }) : false; + }, - /** - * @param {ItemUnit} item - * @returns {boolean} - */ - update: function (item) { - if (!item) return false; - if (this.validGids.includes(item.gid)) return true; // already in the list - let i = 0; - for (let el of this.needList) { - if (!me.getItem(el.classid)) { - // We no longer have the item we wanted socketables for - this.needList.splice(i, 1); - continue; - } - if (item.isInsertable) { - if (el.needed.includes(item.classid)) { - this.validGids.push(item.gid); - this.needList[i].needed.splice(this.needList[i].needed.indexOf(item.classid), 1); - if (this.needList[i].needed.length === 0) { - // no more needed items so remove from list - this.needList.splice(i, 1); - } - return true; - } - } - i++; // keep track of index - } + /** + * @param {ItemUnit} item + * @returns {boolean} + */ + update: function (item) { + if (!item) return false; + if (this.validGids.includes(item.gid)) return true; // already in the list + let i = 0; + for (let el of this.needList) { + if (!me.getItem(el.classid)) { + // We no longer have the item we wanted socketables for + this.needList.splice(i, 1); + continue; + } + if (item.isInsertable) { + if (el.needed.includes(item.classid)) { + this.validGids.push(item.gid); + this.needList[i].needed.splice(this.needList[i].needed.indexOf(item.classid), 1); + if (this.needList[i].needed.length === 0) { + // no more needed items so remove from list + this.needList.splice(i, 1); + } + return true; + } + } + i++; // keep track of index + } - return false; - }, + return false; + }, - ensureList: function () { - let i = 0; - for (let el of this.needList) { - if (!me.getItem(el.classid)) { - // We no longer have the item we wanted socketables for - this.needList.splice(i, 1); - continue; - } - i++; // keep track of index - } - }, + ensureList: function () { + let i = 0; + for (let el of this.needList) { + if (!me.getItem(el.classid)) { + // We no longer have the item we wanted socketables for + this.needList.splice(i, 1); + continue; + } + i++; // keep track of index + } + }, - // Cube ingredients - checkSubrecipes: function () { - for (let el of this.needList) { - for (let i = 0; i < el.needed.length; i++) { - switch (true) { - case [ - sdk.items.gems.Perfect.Ruby, sdk.items.gems.Perfect.Sapphire, sdk.items.gems.Perfect.Topaz, sdk.items.gems.Perfect.Emerald, - sdk.items.gems.Perfect.Amethyst, sdk.items.gems.Perfect.Diamond, sdk.items.gems.Perfect.Skull].includes(el.needed[i]): - if (Cubing.subRecipes.indexOf(el.needed[i]) === -1) { - Cubing.subRecipes.push(el.needed[i]); - Cubing.recipes.push({ - Ingredients: [el.needed[i] - 1, el.needed[i] - 1, el.needed[i] - 1], - Index: 0, - AlwaysEnabled: true, - MainRecipe: "Crafting" - }); - } + // Cube ingredients + checkSubrecipes: function () { + for (let el of this.needList) { + for (let i = 0; i < el.needed.length; i++) { + switch (true) { + case [ + sdk.items.gems.Perfect.Ruby, sdk.items.gems.Perfect.Sapphire, sdk.items.gems.Perfect.Topaz, sdk.items.gems.Perfect.Emerald, + sdk.items.gems.Perfect.Amethyst, sdk.items.gems.Perfect.Diamond, sdk.items.gems.Perfect.Skull].includes(el.needed[i]): + if (Cubing.subRecipes.indexOf(el.needed[i]) === -1) { + Cubing.subRecipes.push(el.needed[i]); + Cubing.recipes.push({ + Ingredients: [el.needed[i] - 1, el.needed[i] - 1, el.needed[i] - 1], + Index: 0, + AlwaysEnabled: true, + MainRecipe: "Crafting" + }); + } - break; - case el.needed[i] >= sdk.items.runes.El && el.needed[i] <= sdk.items.runes.Ort: - if (Cubing.subRecipes.indexOf(el.needed[i]) === -1) { - Cubing.subRecipes.push(el.needed[i]); - Cubing.recipes.push({ - Ingredients: [el.needed[i] - 1, el.needed[i] - 1, el.needed[i] - 1], - Index: Recipe.Rune, - AlwaysEnabled: true, - MainRecipe: "Crafting" - }); - } + break; + case el.needed[i] >= sdk.items.runes.El && el.needed[i] <= sdk.items.runes.Ort: + if (Cubing.subRecipes.indexOf(el.needed[i]) === -1) { + Cubing.subRecipes.push(el.needed[i]); + Cubing.recipes.push({ + Ingredients: [el.needed[i] - 1, el.needed[i] - 1, el.needed[i] - 1], + Index: Recipe.Rune, + AlwaysEnabled: true, + MainRecipe: "Crafting" + }); + } - break; - // case el.needed[i] >= sdk.items.runes.Thul && el.needed[i] <= sdk.items.runes.Lem: - // // gems repeat so should be able to math this out chipped (TASRED) -> repeat flawed (TASRED) - // if (Cubing.subRecipes.indexOf(el.needed[i]) === -1) { - // Cubing.subRecipes.push(el.needed[i]); - // Cubing.recipes.push({ - // Ingredients: [el.needed[i] - 1, el.needed[i] - 1, el.needed[i] - 1], - // Index: Recipe.Rune, - // AlwaysEnabled: true, - // MainRecipe: "Crafting" - // }); - // } + break; + // case el.needed[i] >= sdk.items.runes.Thul && el.needed[i] <= sdk.items.runes.Lem: + // // gems repeat so should be able to math this out chipped (TASRED) -> repeat flawed (TASRED) + // if (Cubing.subRecipes.indexOf(el.needed[i]) === -1) { + // Cubing.subRecipes.push(el.needed[i]); + // Cubing.recipes.push({ + // Ingredients: [el.needed[i] - 1, el.needed[i] - 1, el.needed[i] - 1], + // Index: Recipe.Rune, + // AlwaysEnabled: true, + // MainRecipe: "Crafting" + // }); + // } - // break; - // case el.needed[i] >= sdk.items.runes.Mal && el.needed[i] <= sdk.items.runes.Zod: - // // gems repeat so should be able to math this out Base (TASRED) -> repeat Flawless (TASRE) (stops at Emerald) - // if (Cubing.subRecipes.indexOf(el.needed[i]) === -1) { - // Cubing.subRecipes.push(el.needed[i]); - // Cubing.recipes.push({ - // Ingredients: [el.needed[i] - 1, el.needed[i] - 1], - // Index: Recipe.Rune, - // AlwaysEnabled: true, - // MainRecipe: "Crafting" - // }); - // } + // break; + // case el.needed[i] >= sdk.items.runes.Mal && el.needed[i] <= sdk.items.runes.Zod: + // // gems repeat so should be able to math this out Base (TASRED) -> repeat Flawless (TASRE) (stops at Emerald) + // if (Cubing.subRecipes.indexOf(el.needed[i]) === -1) { + // Cubing.subRecipes.push(el.needed[i]); + // Cubing.recipes.push({ + // Ingredients: [el.needed[i] - 1, el.needed[i] - 1], + // Index: Recipe.Rune, + // AlwaysEnabled: true, + // MainRecipe: "Crafting" + // }); + // } - // break; - } - } - } + // break; + } + } + } - return true; - }, + return true; + }, }; diff --git a/libs/SoloPlay/Functions/StorageOverrides.js b/libs/SoloPlay/Functions/StorageOverrides.js index 129f8904..3e188290 100644 --- a/libs/SoloPlay/Functions/StorageOverrides.js +++ b/libs/SoloPlay/Functions/StorageOverrides.js @@ -9,683 +9,683 @@ includeIfNotIncluded("SoloPlay/Tools/Developer.js"); (function() { - /** - * @constructor - * @param {string} name - container name - * @param {number} width - container width - * @param {number} height - container height - * @param {number} location - container location - */ - function Container(name, width, height, location) { - this.name = name; - this.width = width; - this.height = height; - this.location = location; - /** @type {number[][]} */ - this.buffer = []; - /** @type {ItemUnit[]} */ - this.itemList = []; - this.openPositions = this.height * this.width; - - for (let h = 0; h < this.height; h += 1) { - this.buffer.push([]); - - for (let w = 0; w < this.width; w += 1) { - this.buffer[h][w] = 0; - } - } - } - - /** - * @param {ItemUnit} item - */ - Container.prototype.Mark = function (item) { - let x, y; - - // Make sure it is in this container. - if (item.location !== this.location || (item.mode !== sdk.items.mode.inStorage && item.mode !== sdk.items.mode.inBelt)) { - return false; - } - - // Mark item in buffer. - for (x = item.x; x < (item.x + item.sizex); x += 1) { - for (y = item.y; y < (item.y + item.sizey); y += 1) { - this.buffer[y][x] = this.itemList.length + 1; - this.openPositions -= 1; - } - } - - // Add item to list. - this.itemList.push(copyUnit(item)); - - return true; - }; - - /** - * @param {ItemUnit} item - * @param {number[][]} baseRef - */ - Container.prototype.IsLocked = function (item, baseRef) { - let h, w; - let reference = baseRef.slice(0); - - // Make sure it is in this container. - if (item.mode !== sdk.items.mode.inStorage || item.location !== this.location) { - return false; - } - - // Make sure the item is ours - if (!item.getParent() || item.getParent().type !== me.type || item.getParent().gid !== me.gid) { - return false; - } - - //Insure valid reference. - if (typeof (reference) !== "object" || reference.length !== this.buffer.length || reference[0].length !== this.buffer[0].length) { - throw new Error("Storage.IsLocked: Invalid inventory reference"); - } - - try { - // Check if the item lies in a locked spot. - for (h = item.y; h < (item.y + item.sizey); h += 1) { - for (w = item.x; w < (item.x + item.sizex); w += 1) { - if (reference[h][w] === 0) { - return true; - } - } - } - } catch (e2) { - throw new Error("Storage.IsLocked error! Item info: " + item.name + " " + item.y + " " + item.sizey + " " + item.x + " " + item.sizex + " " + item.mode + " " + item.location); - } - - return false; - }; - - Container.prototype.Reset = function () { - let h, w; - - for (h = 0; h < this.height; h += 1) { - for (w = 0; w < this.width; w += 1) { - this.buffer[h][w] = 0; - } - } - - this.itemList = []; - this.openPositions = this.height * this.width; - - return true; - }; - - /** - * @param {string} name - */ - Container.prototype.cubeSpot = function (name) { - if (name !== "Stash") return true; - - let cube = me.getItem(sdk.quest.item.Cube); - if (!cube) return false; - - // Cube is in correct location - if (cube && cube.isInStash && cube.x === 0 && cube.y === 0) { - return true; - } - - let makeCubeSpot = this.MakeSpot(cube, { x: 0, y: 0 }, true); // NOTE: passing these in buffer order [h/x][w/y] - - if (makeCubeSpot) { - // this item cannot be moved - if (makeCubeSpot === -1) return false; - // we couldnt move the item - if (!this.MoveToSpot(cube, makeCubeSpot.y, makeCubeSpot.x)) return false; - } - - return true; - }; - - /** - * @param {ItemUnit} item - */ - Container.prototype.CanFit = function (item) { - return (!!this.FindSpot(item)); - }; - - /** - * @param {number[]} itemIdsLeft - * @param {number[]} itemIdsRight - */ - Container.prototype.SortItems = function (itemIdsLeft, itemIdsRight) { - Storage.Reload(); - - this.cubeSpot(this.name); - - let x, y, nPos; - - for (y = this.width - 1; y >= 0; y--) { - for (x = this.height - 1; x >= 0; x--) { - - delay(1); - - if (this.buffer[x][y] === 0) { - continue; // nothing on this spot - } - - let item = this.itemList[this.buffer[x][y] - 1]; - - if (item.classid === sdk.quest.item.Cube && item.isInStash && item.x === 0 && item.y === 0) { - continue; // dont touch the cube - } - - let ix = item.y, iy = item.x; // x and y are backwards! - - if (this.location !== item.location) { - D2Bot.printToConsole("StorageOverrides.js>SortItems WARNING: Detected a non-storage item in the list: " + item.name + " at " + ix + "," + iy, sdk.colors.D2Bot.Gold); - continue; // dont try to touch non-storage items | TODO: prevent non-storage items from getting this far - } - - if (this.location === sdk.storage.Inventory && this.IsLocked(item, Config.Inventory)) { - continue; // locked spot / item - } - - if (ix < x || iy < y) { - continue; // not top left part of item - } - - if (item.type !== sdk.unittype.Item) { - D2Bot.printToConsole("StorageOverrides.js>SortItems WARNING: Detected a non-item in the list: " + item.name + " at " + ix + "," + iy, sdk.colors.D2Bot.Gold); - continue; // dont try to touch non-items | TODO: prevent non-items from getting this far - } - - if (item.mode === sdk.items.mode.onGround) { - D2Bot.printToConsole("StorageOverrides.js>SortItems WARNING: Detected a ground item in the list: " + item.name + " at " + ix + "," + iy, sdk.colors.D2Bot.Gold); - continue; // dont try to touch ground items | TODO: prevent ground items from getting this far - } - - // always sort stash left-to-right - if (this.location === sdk.storage.Stash) { - nPos = this.FindSpot(item); - } else if (this.location === sdk.storage.Inventory && ((!itemIdsLeft && !itemIdsRight) || !itemIdsLeft || itemIdsRight.includes(item.classid) || itemIdsLeft.indexOf(item.classid) === -1)) { - // sort from right by default or if specified - nPos = this.FindSpot(item, true, false, SetUp.sortSettings.ItemsSortedFromRightPriority); - } else if (this.location === sdk.storage.Inventory && itemIdsRight.indexOf(item.classid) === -1 && itemIdsLeft.includes(item.classid)) { - // sort from left only if specified - nPos = this.FindSpot(item, false, false, SetUp.sortSettings.ItemsSortedFromLeftPriority); - } - - // skip if no better spot found - if (!nPos || (nPos.x === ix && nPos.y === iy)) { - continue; - } - - if (!this.MoveToSpot(item, nPos.y, nPos.x)) { - continue; // we couldnt move the item - } - - // We moved an item so reload & restart - Storage.Reload(); - y = this.width - 0; - - break; // Loop again from begin - } - } - - // this.Dump(); - - return true; - }; - - /** - * @param {ItemUnit} item - * @param {boolean} reverseX - * @param {boolean} reverseY - * @param {number[]} priorityClassIds - */ - Container.prototype.FindSpot = function (item, reverseX, reverseY, priorityClassIds) { - // Make sure it's a valid item - if (!item) return false; - - /** - * @todo review this to see why it sometimes fails when there is actually enough room - */ - - let x, y, nx, ny, makeSpot; - let xDir = 1, yDir = 1; - - let startX = 0; - let startY = 0; - let endX = this.width - (item.sizex - 1); - let endY = this.height - (item.sizey - 1); - - Storage.Reload(); - - if (item.sizex && item.sizey && item.gid === undefined) { - // fake item we are checking if we can fit a certain sized item so mock some props to it - item.gid = -1; - item.classid = -1; - item.quality = -1; - item.gfx = -1; - } - - Storage.Reload(); - - if (reverseX) { // right-to-left - startX = endX - 1; - endX = -1; // stops at 0 - xDir = -1; - } - - if (reverseY) { // bottom-to-top - startY = endY - 1; - endY = -1; // stops at 0 - yDir = -1; - } - - //Loop buffer looking for spot to place item. - for (y = startX; y !== endX; y += xDir) { - Loop: - for (x = startY; x !== endY; x += yDir) { - //Check if there is something in this spot. - if (this.buffer[x][y] > 0) { - - // TODO: add makespot logic here. priorityClassIds should only be used when sorting -- in town, where it's safe! - // TODO: collapse this down to just a MakeSpot(item, location) call, and have MakeSpot do the priority checks right at the top - let bufferItemClass = this.itemList[this.buffer[x][y] - 1].classid; - let bufferItemGfx = this.itemList[this.buffer[x][y] - 1].gfx; - let bufferItemQuality = this.itemList[this.buffer[x][y] - 1].quality; - - if (SetUp.sortSettings.PrioritySorting && priorityClassIds && priorityClassIds.includes(item.classid) - && !this.IsLocked(this.itemList[this.buffer[x][y] - 1], Config.Inventory) // don't try to make a spot by moving locked items! TODO: move this to the start of loop - && (priorityClassIds.indexOf(bufferItemClass) === -1 - || priorityClassIds.indexOf(item.classid) < priorityClassIds.indexOf(bufferItemClass))) { // item in this spot needs to move! - makeSpot = this.MakeSpot(item, { x: x, y: y }); // NOTE: passing these in buffer order [h/x][w/y] - - if (item.classid !== bufferItemClass // higher priority item - || (item.classid === bufferItemClass && item.quality > bufferItemQuality) // same class, higher quality item - || (item.classid === bufferItemClass && item.quality === bufferItemQuality && item.gfx > bufferItemGfx) // same quality, higher graphic item - || (Config.AutoEquip && item.classid === bufferItemClass && item.quality === bufferItemQuality && item.gfx === bufferItemGfx // same graphic, higher tier item - && NTIP.GetTier(item) > NTIP.GetTier(this.itemList[this.buffer[x][y] - 1]))) { - makeSpot = this.MakeSpot(item, { x: x, y: y }); // NOTE: passing these in buffer order [h/x][w/y] - - if (makeSpot) { - // this item cannot be moved - if (makeSpot === -1) return false; - - return makeSpot; - } - } - } - - if (item.gid === undefined) return false; - - // ignore same gid - if (item.gid !== this.itemList[this.buffer[x][y] - 1].gid ) { - continue; - } - } - - //Loop the item size to make sure we can fit it. - for (nx = 0; nx < item.sizey; nx += 1) { - for (ny = 0; ny < item.sizex; ny += 1) { - if (this.buffer[x + nx][y + ny]) { - // ignore same gid - if (item.gid !== this.itemList[this.buffer[x + nx][y + ny] - 1].gid ) { - continue Loop; - } - } - } - } - - return ({ x: x, y: y }); - } - } - - return false; - }; - - /** - * @param {ItemUnit} item - * @param {{x: number, y: number}} location - * @param {boolean} force - */ - Container.prototype.MakeSpot = function (item, location, force) { - let x, y, endx, endy, tmpLocation; - let [itemsToMove, itemsMoved] = [[], []]; - // TODO: test the scenario where all possible items have been moved, but this item still can't be placed - // e.g. if there are many LCs in an inventory and the spot for a GC can't be freed up without - // moving other items that ARE NOT part of the position desired - - // Make sure it's a valid item and item is in a priority sorting list - if (!item || !item.classid - || (SetUp.sortSettings.ItemsSortedFromRightPriority.indexOf(item.classid) === -1 - && SetUp.sortSettings.ItemsSortedFromLeftPriority.indexOf(item.classid) === -1 - && !force)) { - return false; // only continue if the item is in the priority sort list - } - - // Make sure the item could even fit at the desired location - if (!location //|| !(location.x >= 0) || !(location.y >= 0) - || ((location.y + (item.sizex - 1)) > (this.width - 1)) - || ((location.x + (item.sizey - 1)) > (this.height - 1))) { - return false; // location invalid or item could not ever fit in the location - } - - Storage.Reload(); - - // Do not continue if the container doesn't have enough openPositions. - // TODO: esd1 - this could be extended to use Stash for moving things if inventory is too tightly packed - if (item.sizex * item.sizey > this.openPositions) { - return -1; // return a non-false answer to FindSpot so it doesn't keep looking - } - - endy = location.y + (item.sizex - 1); - endx = location.x + (item.sizey - 1); - - // Collect a list of all the items in the way of using this position - for (x = location.x; x <= endx; x += 1) { // item height - for (y = location.y; y <= endy; y += 1) { // item width - if ( this.buffer[x][y] === 0 ) { - continue; // nothing to move from this spot - } else if (item.gid === this.itemList[this.buffer[x][y] - 1].gid) { - continue; // ignore same gid - } else { - itemsToMove.push(copyUnit(this.itemList[this.buffer[x][y] - 1])); // track items that need to move - } - } - } - - // Move any item(s) out of the way - if (itemsToMove.length) { - for (let i = 0; i < itemsToMove.length; i++) { - let reverseX = !(SetUp.sortSettings.ItemsSortedFromRight.includes(item.classid)); - tmpLocation = this.FindSpot(itemsToMove[i], reverseX, false); - // D2Bot.printToConsole(itemsToMove[i].name + " moving from " + itemsToMove[i].x + "," + itemsToMove[i].y + " to " + tmpLocation.y + "," + tmpLocation.x, sdk.colors.D2Bot.Gold); - - if (this.MoveToSpot(itemsToMove[i], tmpLocation.y, tmpLocation.x)) { - // D2Bot.printToConsole(itemsToMove[i].name + " moved to " + tmpLocation.y + "," + tmpLocation.x, sdk.colors.D2Bot.Gold); - itemsMoved.push(copyUnit(itemsToMove[i])); - Storage.Reload(); // success on this item, reload! - delay(1); // give reload a moment of time to avoid moving the same item twice - } else { - D2Bot.printToConsole(itemsToMove[i].name + " failed to move to " + tmpLocation.y + "," + tmpLocation.x, sdk.colors.D2Bot.Gold); - - return false; - } - } - } - - //D2Bot.printToConsole("MakeSpot success! " + item.name + " can now be placed at " + location.y + "," + location.x, sdk.colors.D2Bot.Gold); - return ({ x: location.x, y: location.y }); - }; - - /** - * @param {ItemUnit} item - * @param {number} mX - * @param {number} mY - */ - Container.prototype.MoveToSpot = function (item, mX, mY) { - let cItem, cube; - - // Cube -> Stash, must place item in inventory first - if (item.location === sdk.storage.Cube && this.location === sdk.storage.Stash && !Storage.Inventory.MoveTo(item)) { - return false; - } - - // Can't deal with items on ground! - if (item.mode === sdk.items.mode.onGround) return false; - // Item already on the cursor. - if (me.itemoncursor && item.mode !== sdk.items.mode.onCursor) return false; - - // Make sure stash is open - if (this.location === sdk.storage.Stash && !Town.openStash()) return false; - - const [orgX, orgY, orgLoc] = [item.x, item.y, item.location]; - const moveItem = (x, y, location) => { - for (let n = 0; n < 5; n += 1) { - switch (location) { - case sdk.storage.Belt: - cItem = Game.getCursorUnit(); - cItem !== null && sendPacket(1, sdk.packets.send.ItemToBelt, 4, cItem.gid, 4, y); - - break; - case sdk.storage.Inventory: - sendPacket(1, sdk.packets.send.ItemToBuffer, 4, item.gid, 4, x, 4, y, 4, 0x00); - - break; - case sdk.storage.Cube: - cItem = Game.getCursorUnit(); - cube = me.getItem(sdk.quest.item.Cube); - (cItem !== null && cube !== null) && sendPacket(1, sdk.packets.send.ItemToCube, 4, cItem.gid, 4, cube.gid); - - break; - case sdk.storage.Stash: - sendPacket(1, sdk.packets.send.ItemToBuffer, 4, item.gid, 4, x, 4, y, 4, 0x04); - - break; - default: - clickItemAndWait(sdk.clicktypes.click.item.Left, x, y, location); - - break; - } - - let nDelay = getTickCount(); - - while ((getTickCount() - nDelay) < Math.max(1000, me.ping * 2 + 200)) { - if (!me.itemoncursor) return true; - - delay(10 + me.ping); - } - } - - return false; - }; - - if (Packet.itemToCursor(item)) { - if (moveItem(mX, mY, this.location)) return true; - moveItem(orgX, orgY, orgLoc) && console.debug("Failed to move " + item.fname + " to " + mX + "/" + mY); - } - - return false; - }; - - /** - * @param {ItemUnit} item - */ - Container.prototype.MoveTo = function (item) { - let nPos; - - try { - //Can we even fit it in here? - nPos = this.FindSpot(item); - if (!nPos) return false; - - return this.MoveToSpot(item, nPos.y, nPos.x); - } catch (e) { - console.log("Storage.Container.MoveTo caught error : " + e + " - " + e.toSource()); - - return false; - } - }; - - Container.prototype.Dump = function () { - let x, y, string; - - if (this.UsedSpacePercent() > 60) { - for (x = 0; x < this.height; x += 1) { - string = ""; - - for (y = 0; y < this.width; y += 1) { - string += (this.buffer[x][y] > 0) ? "ÿc1x" : "ÿc0o"; - } - - console.log(string); - } - } - - console.log("ÿc9SoloPlayÿc0: " + this.name + " has used " + this.UsedSpacePercent().toFixed(2) + "% of its total space"); - }; - - Container.prototype.UsedSpacePercent = function () { - let usedSpace = 0; - let totalSpace = this.height * this.width; - - Storage.Reload(); - - for (let x = 0; x < this.height; x += 1) { - for (let y = 0; y < this.width; y += 1) { - if (this.buffer[x][y] > 0) { - usedSpace += 1; - } - } - } - - return usedSpace * 100 / totalSpace; - }; - - /** - * @param {number[][]} baseRef - */ - Container.prototype.Compare = function (baseRef) { - let h, w, n, item, itemList, reference; - - Storage.Reload(); - - try { - itemList = []; - reference = baseRef.slice(0, baseRef.length); - - //Insure valid reference. - if (typeof (reference) !== "object" || reference.length !== this.buffer.length || reference[0].length !== this.buffer[0].length) { - throw new Error("Unable to compare different containers."); - } - - for (h = 0; h < this.height; h += 1) { - Loop: - for (w = 0; w < this.width; w += 1) { - item = this.itemList[this.buffer[h][w] - 1]; - - if (!item) { - continue; - } - - for (n = 0; n < itemList.length; n += 1) { - if (itemList[n].gid === item.gid) { - continue Loop; - } - } - - //Check if the buffers changed and the current buffer has an item there. - if (this.buffer[h][w] > 0 && reference[h][w] > 0) { - itemList.push(copyUnit(item)); - } - } - } - - return itemList; - } catch (e) { - return false; - } - }; - - Container.prototype.toSource = function () { - return this.buffer.toSource(); - }; - - /** - * @type {storage} Storage - */ - const Storage = new function () { - this.Init = () => { - this.StashY = me.classic ? 4 : Developer.plugyMode ? 10 : 8; - this.Inventory = new Container("Inventory", 10, 4, 3); - this.TradeScreen = new Container("Inventory", 10, 4, 5); - this.Stash = new Container("Stash", (Developer.plugyMode ? 10 : 6), this.StashY, 7); - this.Belt = new Container("Belt", 4 * this.BeltSize(), 1, 2); - - /** - * @description Return column status (needed potions in each column) - * @param {0 | 1 | 2 | 3 | 4} beltSize - * @returns {[number, number, number, number]} - */ - this.Belt.checkColumns = function (beltSize) { - beltSize === undefined && (beltSize = this.width / 4); - let col = [beltSize, beltSize, beltSize, beltSize]; - let pot = me.getItem(-1, sdk.items.mode.inBelt); - - // No potions - if (!pot) return col; - - do { - col[pot.x % 4] -= 1; - } while (pot.getNext()); - - return col; - }; - - this.Cube = new Container("Horadric Cube", 3, 4, 6); - this.InvRef = []; - - this.Reload(); - }; - - this.BeltSize = function () { - let item = me.getItem(-1, sdk.items.mode.Equipped); // get equipped item - if (!item) return 1; // nothing equipped - - do { - if (item.bodylocation === sdk.body.Belt) { - switch (item.code) { - case "lbl": // sash - case "vbl": // light belt - return 2; - case "mbl": // belt - case "tbl": // heavy belt - return 3; - default: // everything else - return 4; - } - } - } while (item.getNext()); - - return 1; // no belt - }; - - this.Reload = function () { - this.Inventory.Reset(); - this.Stash.Reset(); - this.Belt.Reset(); - this.Cube.Reset(); - this.TradeScreen.Reset(); - - let item = me.getItem(); - if (!item) return false; - - do { - switch (item.location) { - case sdk.storage.Inventory: - this.Inventory.Mark(item); - - break; - case sdk.storage.TradeWindow: - this.TradeScreen.Mark(item); - - break; - case sdk.storage.Belt: - this.Belt.Mark(item); - - break; - case sdk.storage.Cube: - this.Cube.Mark(item); - - break; - case sdk.storage.Stash: - this.Stash.Mark(item); - - break; - } - } while (item.getNext()); - - return true; - }; - }; - - // export to global scope - global.Storage = Storage; + /** + * @constructor + * @param {string} name - container name + * @param {number} width - container width + * @param {number} height - container height + * @param {number} location - container location + */ + function Container(name, width, height, location) { + this.name = name; + this.width = width; + this.height = height; + this.location = location; + /** @type {number[][]} */ + this.buffer = []; + /** @type {ItemUnit[]} */ + this.itemList = []; + this.openPositions = this.height * this.width; + + for (let h = 0; h < this.height; h += 1) { + this.buffer.push([]); + + for (let w = 0; w < this.width; w += 1) { + this.buffer[h][w] = 0; + } + } + } + + /** + * @param {ItemUnit} item + */ + Container.prototype.Mark = function (item) { + let x, y; + + // Make sure it is in this container. + if (item.location !== this.location || (item.mode !== sdk.items.mode.inStorage && item.mode !== sdk.items.mode.inBelt)) { + return false; + } + + // Mark item in buffer. + for (x = item.x; x < (item.x + item.sizex); x += 1) { + for (y = item.y; y < (item.y + item.sizey); y += 1) { + this.buffer[y][x] = this.itemList.length + 1; + this.openPositions -= 1; + } + } + + // Add item to list. + this.itemList.push(copyUnit(item)); + + return true; + }; + + /** + * @param {ItemUnit} item + * @param {number[][]} baseRef + */ + Container.prototype.IsLocked = function (item, baseRef) { + let h, w; + let reference = baseRef.slice(0); + + // Make sure it is in this container. + if (item.mode !== sdk.items.mode.inStorage || item.location !== this.location) { + return false; + } + + // Make sure the item is ours + if (!item.getParent() || item.getParent().type !== me.type || item.getParent().gid !== me.gid) { + return false; + } + + //Insure valid reference. + if (typeof (reference) !== "object" || reference.length !== this.buffer.length || reference[0].length !== this.buffer[0].length) { + throw new Error("Storage.IsLocked: Invalid inventory reference"); + } + + try { + // Check if the item lies in a locked spot. + for (h = item.y; h < (item.y + item.sizey); h += 1) { + for (w = item.x; w < (item.x + item.sizex); w += 1) { + if (reference[h][w] === 0) { + return true; + } + } + } + } catch (e2) { + throw new Error("Storage.IsLocked error! Item info: " + item.name + " " + item.y + " " + item.sizey + " " + item.x + " " + item.sizex + " " + item.mode + " " + item.location); + } + + return false; + }; + + Container.prototype.Reset = function () { + let h, w; + + for (h = 0; h < this.height; h += 1) { + for (w = 0; w < this.width; w += 1) { + this.buffer[h][w] = 0; + } + } + + this.itemList = []; + this.openPositions = this.height * this.width; + + return true; + }; + + /** + * @param {string} name + */ + Container.prototype.cubeSpot = function (name) { + if (name !== "Stash") return true; + + let cube = me.getItem(sdk.quest.item.Cube); + if (!cube) return false; + + // Cube is in correct location + if (cube && cube.isInStash && cube.x === 0 && cube.y === 0) { + return true; + } + + let makeCubeSpot = this.MakeSpot(cube, { x: 0, y: 0 }, true); // NOTE: passing these in buffer order [h/x][w/y] + + if (makeCubeSpot) { + // this item cannot be moved + if (makeCubeSpot === -1) return false; + // we couldnt move the item + if (!this.MoveToSpot(cube, makeCubeSpot.y, makeCubeSpot.x)) return false; + } + + return true; + }; + + /** + * @param {ItemUnit} item + */ + Container.prototype.CanFit = function (item) { + return (!!this.FindSpot(item)); + }; + + /** + * @param {number[]} itemIdsLeft + * @param {number[]} itemIdsRight + */ + Container.prototype.SortItems = function (itemIdsLeft, itemIdsRight) { + Storage.Reload(); + + this.cubeSpot(this.name); + + let x, y, nPos; + + for (y = this.width - 1; y >= 0; y--) { + for (x = this.height - 1; x >= 0; x--) { + + delay(1); + + if (this.buffer[x][y] === 0) { + continue; // nothing on this spot + } + + let item = this.itemList[this.buffer[x][y] - 1]; + + if (item.classid === sdk.quest.item.Cube && item.isInStash && item.x === 0 && item.y === 0) { + continue; // dont touch the cube + } + + let ix = item.y, iy = item.x; // x and y are backwards! + + if (this.location !== item.location) { + D2Bot.printToConsole("StorageOverrides.js>SortItems WARNING: Detected a non-storage item in the list: " + item.name + " at " + ix + "," + iy, sdk.colors.D2Bot.Gold); + continue; // dont try to touch non-storage items | TODO: prevent non-storage items from getting this far + } + + if (this.location === sdk.storage.Inventory && this.IsLocked(item, Config.Inventory)) { + continue; // locked spot / item + } + + if (ix < x || iy < y) { + continue; // not top left part of item + } + + if (item.type !== sdk.unittype.Item) { + D2Bot.printToConsole("StorageOverrides.js>SortItems WARNING: Detected a non-item in the list: " + item.name + " at " + ix + "," + iy, sdk.colors.D2Bot.Gold); + continue; // dont try to touch non-items | TODO: prevent non-items from getting this far + } + + if (item.mode === sdk.items.mode.onGround) { + D2Bot.printToConsole("StorageOverrides.js>SortItems WARNING: Detected a ground item in the list: " + item.name + " at " + ix + "," + iy, sdk.colors.D2Bot.Gold); + continue; // dont try to touch ground items | TODO: prevent ground items from getting this far + } + + // always sort stash left-to-right + if (this.location === sdk.storage.Stash) { + nPos = this.FindSpot(item); + } else if (this.location === sdk.storage.Inventory && ((!itemIdsLeft && !itemIdsRight) || !itemIdsLeft || itemIdsRight.includes(item.classid) || itemIdsLeft.indexOf(item.classid) === -1)) { + // sort from right by default or if specified + nPos = this.FindSpot(item, true, false, SetUp.sortSettings.ItemsSortedFromRightPriority); + } else if (this.location === sdk.storage.Inventory && itemIdsRight.indexOf(item.classid) === -1 && itemIdsLeft.includes(item.classid)) { + // sort from left only if specified + nPos = this.FindSpot(item, false, false, SetUp.sortSettings.ItemsSortedFromLeftPriority); + } + + // skip if no better spot found + if (!nPos || (nPos.x === ix && nPos.y === iy)) { + continue; + } + + if (!this.MoveToSpot(item, nPos.y, nPos.x)) { + continue; // we couldnt move the item + } + + // We moved an item so reload & restart + Storage.Reload(); + y = this.width - 0; + + break; // Loop again from begin + } + } + + // this.Dump(); + + return true; + }; + + /** + * @param {ItemUnit} item + * @param {boolean} reverseX + * @param {boolean} reverseY + * @param {number[]} priorityClassIds + */ + Container.prototype.FindSpot = function (item, reverseX, reverseY, priorityClassIds) { + // Make sure it's a valid item + if (!item) return false; + + /** + * @todo review this to see why it sometimes fails when there is actually enough room + */ + + let x, y, nx, ny, makeSpot; + let xDir = 1, yDir = 1; + + let startX = 0; + let startY = 0; + let endX = this.width - (item.sizex - 1); + let endY = this.height - (item.sizey - 1); + + Storage.Reload(); + + if (item.sizex && item.sizey && item.gid === undefined) { + // fake item we are checking if we can fit a certain sized item so mock some props to it + item.gid = -1; + item.classid = -1; + item.quality = -1; + item.gfx = -1; + } + + Storage.Reload(); + + if (reverseX) { // right-to-left + startX = endX - 1; + endX = -1; // stops at 0 + xDir = -1; + } + + if (reverseY) { // bottom-to-top + startY = endY - 1; + endY = -1; // stops at 0 + yDir = -1; + } + + //Loop buffer looking for spot to place item. + for (y = startX; y !== endX; y += xDir) { + Loop: + for (x = startY; x !== endY; x += yDir) { + //Check if there is something in this spot. + if (this.buffer[x][y] > 0) { + + // TODO: add makespot logic here. priorityClassIds should only be used when sorting -- in town, where it's safe! + // TODO: collapse this down to just a MakeSpot(item, location) call, and have MakeSpot do the priority checks right at the top + let bufferItemClass = this.itemList[this.buffer[x][y] - 1].classid; + let bufferItemGfx = this.itemList[this.buffer[x][y] - 1].gfx; + let bufferItemQuality = this.itemList[this.buffer[x][y] - 1].quality; + + if (SetUp.sortSettings.PrioritySorting && priorityClassIds && priorityClassIds.includes(item.classid) + && !this.IsLocked(this.itemList[this.buffer[x][y] - 1], Config.Inventory) // don't try to make a spot by moving locked items! TODO: move this to the start of loop + && (priorityClassIds.indexOf(bufferItemClass) === -1 + || priorityClassIds.indexOf(item.classid) < priorityClassIds.indexOf(bufferItemClass))) { // item in this spot needs to move! + makeSpot = this.MakeSpot(item, { x: x, y: y }); // NOTE: passing these in buffer order [h/x][w/y] + + if (item.classid !== bufferItemClass // higher priority item + || (item.classid === bufferItemClass && item.quality > bufferItemQuality) // same class, higher quality item + || (item.classid === bufferItemClass && item.quality === bufferItemQuality && item.gfx > bufferItemGfx) // same quality, higher graphic item + || (Config.AutoEquip && item.classid === bufferItemClass && item.quality === bufferItemQuality && item.gfx === bufferItemGfx // same graphic, higher tier item + && NTIP.GetTier(item) > NTIP.GetTier(this.itemList[this.buffer[x][y] - 1]))) { + makeSpot = this.MakeSpot(item, { x: x, y: y }); // NOTE: passing these in buffer order [h/x][w/y] + + if (makeSpot) { + // this item cannot be moved + if (makeSpot === -1) return false; + + return makeSpot; + } + } + } + + if (item.gid === undefined) return false; + + // ignore same gid + if (item.gid !== this.itemList[this.buffer[x][y] - 1].gid ) { + continue; + } + } + + //Loop the item size to make sure we can fit it. + for (nx = 0; nx < item.sizey; nx += 1) { + for (ny = 0; ny < item.sizex; ny += 1) { + if (this.buffer[x + nx][y + ny]) { + // ignore same gid + if (item.gid !== this.itemList[this.buffer[x + nx][y + ny] - 1].gid ) { + continue Loop; + } + } + } + } + + return ({ x: x, y: y }); + } + } + + return false; + }; + + /** + * @param {ItemUnit} item + * @param {{x: number, y: number}} location + * @param {boolean} force + */ + Container.prototype.MakeSpot = function (item, location, force) { + let x, y, endx, endy, tmpLocation; + let [itemsToMove, itemsMoved] = [[], []]; + // TODO: test the scenario where all possible items have been moved, but this item still can't be placed + // e.g. if there are many LCs in an inventory and the spot for a GC can't be freed up without + // moving other items that ARE NOT part of the position desired + + // Make sure it's a valid item and item is in a priority sorting list + if (!item || !item.classid + || (SetUp.sortSettings.ItemsSortedFromRightPriority.indexOf(item.classid) === -1 + && SetUp.sortSettings.ItemsSortedFromLeftPriority.indexOf(item.classid) === -1 + && !force)) { + return false; // only continue if the item is in the priority sort list + } + + // Make sure the item could even fit at the desired location + if (!location //|| !(location.x >= 0) || !(location.y >= 0) + || ((location.y + (item.sizex - 1)) > (this.width - 1)) + || ((location.x + (item.sizey - 1)) > (this.height - 1))) { + return false; // location invalid or item could not ever fit in the location + } + + Storage.Reload(); + + // Do not continue if the container doesn't have enough openPositions. + // TODO: esd1 - this could be extended to use Stash for moving things if inventory is too tightly packed + if (item.sizex * item.sizey > this.openPositions) { + return -1; // return a non-false answer to FindSpot so it doesn't keep looking + } + + endy = location.y + (item.sizex - 1); + endx = location.x + (item.sizey - 1); + + // Collect a list of all the items in the way of using this position + for (x = location.x; x <= endx; x += 1) { // item height + for (y = location.y; y <= endy; y += 1) { // item width + if ( this.buffer[x][y] === 0 ) { + continue; // nothing to move from this spot + } else if (item.gid === this.itemList[this.buffer[x][y] - 1].gid) { + continue; // ignore same gid + } else { + itemsToMove.push(copyUnit(this.itemList[this.buffer[x][y] - 1])); // track items that need to move + } + } + } + + // Move any item(s) out of the way + if (itemsToMove.length) { + for (let i = 0; i < itemsToMove.length; i++) { + let reverseX = !(SetUp.sortSettings.ItemsSortedFromRight.includes(item.classid)); + tmpLocation = this.FindSpot(itemsToMove[i], reverseX, false); + // D2Bot.printToConsole(itemsToMove[i].name + " moving from " + itemsToMove[i].x + "," + itemsToMove[i].y + " to " + tmpLocation.y + "," + tmpLocation.x, sdk.colors.D2Bot.Gold); + + if (this.MoveToSpot(itemsToMove[i], tmpLocation.y, tmpLocation.x)) { + // D2Bot.printToConsole(itemsToMove[i].name + " moved to " + tmpLocation.y + "," + tmpLocation.x, sdk.colors.D2Bot.Gold); + itemsMoved.push(copyUnit(itemsToMove[i])); + Storage.Reload(); // success on this item, reload! + delay(1); // give reload a moment of time to avoid moving the same item twice + } else { + D2Bot.printToConsole(itemsToMove[i].name + " failed to move to " + tmpLocation.y + "," + tmpLocation.x, sdk.colors.D2Bot.Gold); + + return false; + } + } + } + + //D2Bot.printToConsole("MakeSpot success! " + item.name + " can now be placed at " + location.y + "," + location.x, sdk.colors.D2Bot.Gold); + return ({ x: location.x, y: location.y }); + }; + + /** + * @param {ItemUnit} item + * @param {number} mX + * @param {number} mY + */ + Container.prototype.MoveToSpot = function (item, mX, mY) { + let cItem, cube; + + // Cube -> Stash, must place item in inventory first + if (item.location === sdk.storage.Cube && this.location === sdk.storage.Stash && !Storage.Inventory.MoveTo(item)) { + return false; + } + + // Can't deal with items on ground! + if (item.mode === sdk.items.mode.onGround) return false; + // Item already on the cursor. + if (me.itemoncursor && item.mode !== sdk.items.mode.onCursor) return false; + + // Make sure stash is open + if (this.location === sdk.storage.Stash && !Town.openStash()) return false; + + const [orgX, orgY, orgLoc] = [item.x, item.y, item.location]; + const moveItem = (x, y, location) => { + for (let n = 0; n < 5; n += 1) { + switch (location) { + case sdk.storage.Belt: + cItem = Game.getCursorUnit(); + cItem !== null && sendPacket(1, sdk.packets.send.ItemToBelt, 4, cItem.gid, 4, y); + + break; + case sdk.storage.Inventory: + sendPacket(1, sdk.packets.send.ItemToBuffer, 4, item.gid, 4, x, 4, y, 4, 0x00); + + break; + case sdk.storage.Cube: + cItem = Game.getCursorUnit(); + cube = me.getItem(sdk.quest.item.Cube); + (cItem !== null && cube !== null) && sendPacket(1, sdk.packets.send.ItemToCube, 4, cItem.gid, 4, cube.gid); + + break; + case sdk.storage.Stash: + sendPacket(1, sdk.packets.send.ItemToBuffer, 4, item.gid, 4, x, 4, y, 4, 0x04); + + break; + default: + clickItemAndWait(sdk.clicktypes.click.item.Left, x, y, location); + + break; + } + + let nDelay = getTickCount(); + + while ((getTickCount() - nDelay) < Math.max(1000, me.ping * 2 + 200)) { + if (!me.itemoncursor) return true; + + delay(10 + me.ping); + } + } + + return false; + }; + + if (Packet.itemToCursor(item)) { + if (moveItem(mX, mY, this.location)) return true; + moveItem(orgX, orgY, orgLoc) && console.debug("Failed to move " + item.fname + " to " + mX + "/" + mY); + } + + return false; + }; + + /** + * @param {ItemUnit} item + */ + Container.prototype.MoveTo = function (item) { + let nPos; + + try { + //Can we even fit it in here? + nPos = this.FindSpot(item); + if (!nPos) return false; + + return this.MoveToSpot(item, nPos.y, nPos.x); + } catch (e) { + console.log("Storage.Container.MoveTo caught error : " + e + " - " + e.toSource()); + + return false; + } + }; + + Container.prototype.Dump = function () { + let x, y, string; + + if (this.UsedSpacePercent() > 60) { + for (x = 0; x < this.height; x += 1) { + string = ""; + + for (y = 0; y < this.width; y += 1) { + string += (this.buffer[x][y] > 0) ? "ÿc1x" : "ÿc0o"; + } + + console.log(string); + } + } + + console.log("ÿc9SoloPlayÿc0: " + this.name + " has used " + this.UsedSpacePercent().toFixed(2) + "% of its total space"); + }; + + Container.prototype.UsedSpacePercent = function () { + let usedSpace = 0; + let totalSpace = this.height * this.width; + + Storage.Reload(); + + for (let x = 0; x < this.height; x += 1) { + for (let y = 0; y < this.width; y += 1) { + if (this.buffer[x][y] > 0) { + usedSpace += 1; + } + } + } + + return usedSpace * 100 / totalSpace; + }; + + /** + * @param {number[][]} baseRef + */ + Container.prototype.Compare = function (baseRef) { + let h, w, n, item, itemList, reference; + + Storage.Reload(); + + try { + itemList = []; + reference = baseRef.slice(0, baseRef.length); + + //Insure valid reference. + if (typeof (reference) !== "object" || reference.length !== this.buffer.length || reference[0].length !== this.buffer[0].length) { + throw new Error("Unable to compare different containers."); + } + + for (h = 0; h < this.height; h += 1) { + Loop: + for (w = 0; w < this.width; w += 1) { + item = this.itemList[this.buffer[h][w] - 1]; + + if (!item) { + continue; + } + + for (n = 0; n < itemList.length; n += 1) { + if (itemList[n].gid === item.gid) { + continue Loop; + } + } + + //Check if the buffers changed and the current buffer has an item there. + if (this.buffer[h][w] > 0 && reference[h][w] > 0) { + itemList.push(copyUnit(item)); + } + } + } + + return itemList; + } catch (e) { + return false; + } + }; + + Container.prototype.toSource = function () { + return this.buffer.toSource(); + }; + + /** + * @type {storage} Storage + */ + const Storage = new function () { + this.Init = () => { + this.StashY = me.classic ? 4 : Developer.plugyMode ? 10 : 8; + this.Inventory = new Container("Inventory", 10, 4, 3); + this.TradeScreen = new Container("Inventory", 10, 4, 5); + this.Stash = new Container("Stash", (Developer.plugyMode ? 10 : 6), this.StashY, 7); + this.Belt = new Container("Belt", 4 * this.BeltSize(), 1, 2); + + /** + * @description Return column status (needed potions in each column) + * @param {0 | 1 | 2 | 3 | 4} beltSize + * @returns {[number, number, number, number]} + */ + this.Belt.checkColumns = function (beltSize) { + beltSize === undefined && (beltSize = this.width / 4); + let col = [beltSize, beltSize, beltSize, beltSize]; + let pot = me.getItem(-1, sdk.items.mode.inBelt); + + // No potions + if (!pot) return col; + + do { + col[pot.x % 4] -= 1; + } while (pot.getNext()); + + return col; + }; + + this.Cube = new Container("Horadric Cube", 3, 4, 6); + this.InvRef = []; + + this.Reload(); + }; + + this.BeltSize = function () { + let item = me.getItem(-1, sdk.items.mode.Equipped); // get equipped item + if (!item) return 1; // nothing equipped + + do { + if (item.bodylocation === sdk.body.Belt) { + switch (item.code) { + case "lbl": // sash + case "vbl": // light belt + return 2; + case "mbl": // belt + case "tbl": // heavy belt + return 3; + default: // everything else + return 4; + } + } + } while (item.getNext()); + + return 1; // no belt + }; + + this.Reload = function () { + this.Inventory.Reset(); + this.Stash.Reset(); + this.Belt.Reset(); + this.Cube.Reset(); + this.TradeScreen.Reset(); + + let item = me.getItem(); + if (!item) return false; + + do { + switch (item.location) { + case sdk.storage.Inventory: + this.Inventory.Mark(item); + + break; + case sdk.storage.TradeWindow: + this.TradeScreen.Mark(item); + + break; + case sdk.storage.Belt: + this.Belt.Mark(item); + + break; + case sdk.storage.Cube: + this.Cube.Mark(item); + + break; + case sdk.storage.Stash: + this.Stash.Mark(item); + + break; + } + } while (item.getNext()); + + return true; + }; + }; + + // export to global scope + global.Storage = Storage; })(); diff --git a/libs/SoloPlay/Functions/TownOverrides.js b/libs/SoloPlay/Functions/TownOverrides.js index 5667e381..c07ebbc4 100644 --- a/libs/SoloPlay/Functions/TownOverrides.js +++ b/libs/SoloPlay/Functions/TownOverrides.js @@ -15,38 +15,38 @@ includeIfNotIncluded("core/Town.js"); new Overrides.Override(Town, Town.drinkPots, function(orignal, type) { - const objDrank = orignal(type, false); - const pots = {}; - pots[sdk.items.StaminaPotion] = "stamina"; - pots[sdk.items.AntidotePotion] = "antidote"; - pots[sdk.items.ThawingPotion] = "thawing"; - - try { - if (objDrank.potName) { - let objID = objDrank.potName.split(" ")[0].toLowerCase(); - - if (objID) { - // non-english version - if (!CharData.pots.has(objID)) { - typeof type === "number" ? (objID = pots[objID]) : (objID = type.toLowerCase()); - } - - if (!CharData.pots.get(objID).active() || CharData.pots.get(objID).timeLeft() <= 0) { - CharData.pots.get(objID).tick = getTickCount(); - CharData.pots.get(objID).duration = objDrank.quantity * 30 * 1000; - } else { - CharData.pots.get(objID).duration += (objDrank.quantity * 30 * 1000) - (getTickCount() - CharData.pots.get(objID).tick); - } - - console.log("ÿc9DrinkPotsÿc0 :: drank " + objDrank.quantity + " " + objDrank.potName + "s. Timer [" + Time.format(CharData.pots.get(objID).duration) + "]"); - } - } - } catch (e) { - console.error(e); - return false; - } - - return true; + const objDrank = orignal(type, false); + const pots = {}; + pots[sdk.items.StaminaPotion] = "stamina"; + pots[sdk.items.AntidotePotion] = "antidote"; + pots[sdk.items.ThawingPotion] = "thawing"; + + try { + if (objDrank.potName) { + let objID = objDrank.potName.split(" ")[0].toLowerCase(); + + if (objID) { + // non-english version + if (!CharData.pots.has(objID)) { + typeof type === "number" ? (objID = pots[objID]) : (objID = type.toLowerCase()); + } + + if (!CharData.pots.get(objID).active() || CharData.pots.get(objID).timeLeft() <= 0) { + CharData.pots.get(objID).tick = getTickCount(); + CharData.pots.get(objID).duration = objDrank.quantity * 30 * 1000; + } else { + CharData.pots.get(objID).duration += (objDrank.quantity * 30 * 1000) - (getTickCount() - CharData.pots.get(objID).tick); + } + + console.log("ÿc9DrinkPotsÿc0 :: drank " + objDrank.quantity + " " + objDrank.potName + "s. Timer [" + Time.format(CharData.pots.get(objID).duration) + "]"); + } + } + } catch (e) { + console.error(e); + return false; + } + + return true; }).apply(); // ugly for now but proxy the functions I moved to Me.js in case somewhere the base functions are being used @@ -68,10 +68,10 @@ Town.sell = []; // Removed Missle Potions for easy gold // Items that won't be stashed Town.ignoredItemTypes = [ - sdk.items.type.BowQuiver, sdk.items.type.CrossbowQuiver, sdk.items.type.Book, - sdk.items.type.Scroll, sdk.items.type.Key, sdk.items.type.HealingPotion, - sdk.items.type.ManaPotion, sdk.items.type.RejuvPotion, sdk.items.type.StaminaPotion, - sdk.items.type.AntidotePotion, sdk.items.type.ThawingPotion + sdk.items.type.BowQuiver, sdk.items.type.CrossbowQuiver, sdk.items.type.Book, + sdk.items.type.Scroll, sdk.items.type.Key, sdk.items.type.HealingPotion, + sdk.items.type.ManaPotion, sdk.items.type.RejuvPotion, sdk.items.type.StaminaPotion, + sdk.items.type.AntidotePotion, sdk.items.type.ThawingPotion ]; /** @@ -80,7 +80,7 @@ Town.ignoredItemTypes = [ * @returns {boolean} */ Town.systemsKeep = function (item) { - return (AutoEquip.wanted(item) || Cubing.keepItem(item) || Runewords.keepItem(item) || CraftingSystem.keepItem(item) || SoloWants.keepItem(item)); + return (AutoEquip.wanted(item) || Cubing.keepItem(item) || Runewords.keepItem(item) || CraftingSystem.keepItem(item) || SoloWants.keepItem(item)); }; /** @@ -88,23 +88,23 @@ Town.systemsKeep = function (item) { * @returns {boolean} */ Town.needForceID = function (item) { - const result = Pickit.checkItem(item); - return ([Pickit.Result.WANTED, Pickit.Result.CUBING].includes(result.result) && !item.identified && AutoEquip.hasTier(item)); + const result = Pickit.checkItem(item); + return ([Pickit.Result.WANTED, Pickit.Result.CUBING].includes(result.result) && !item.identified && AutoEquip.hasTier(item)); }; Town.haveItemsToSell = function () { - let temp = []; - while (Town.sell.length) { - let i = Town.sell.shift(); - if (typeof i === "undefined") continue; - let realItem = me.getItem(i.classid, -1, i.gid); - if (realItem && realItem.isInStorage && !Town.ignoreType(realItem.itemType) && realItem.sellable && !Town.systemsKeep(realItem)) { - temp.push(realItem); - } - } - Town.sell = temp.slice(0); - - return Town.sell.length; + let temp = []; + while (Town.sell.length) { + let i = Town.sell.shift(); + if (typeof i === "undefined") continue; + let realItem = me.getItem(i.classid, -1, i.gid); + if (realItem && realItem.isInStorage && !Town.ignoreType(realItem.itemType) && realItem.sellable && !Town.systemsKeep(realItem)) { + temp.push(realItem); + } + } + Town.sell = temp.slice(0); + + return Town.sell.length; }; /** @@ -112,45 +112,45 @@ Town.haveItemsToSell = function () { * @returns {boolean} */ Town.sellItems = function (itemList = []) { - !itemList.length && (itemList = Town.sell); - - if (this.initNPC("Shop", "sell")) { - while (itemList.length) { - let item = itemList.shift(); - - if (!item.isInStorage) continue; - - try { - if (getUIFlag(sdk.uiflags.Shop) || getUIFlag(sdk.uiflags.NPCMenu)) { - console.log("sell " + item.prettyPrint); - Item.logger("Sold", item); - item.sell(); - delay(100); - } - } catch (e) { - console.error(e); - } - } - } - - return !itemList.length; + !itemList.length && (itemList = Town.sell); + + if (this.initNPC("Shop", "sell")) { + while (itemList.length) { + let item = itemList.shift(); + + if (!item.isInStorage) continue; + + try { + if (getUIFlag(sdk.uiflags.Shop) || getUIFlag(sdk.uiflags.NPCMenu)) { + console.log("sell " + item.prettyPrint); + Item.logger("Sold", item); + item.sell(); + delay(100); + } + } catch (e) { + console.error(e); + } + } + } + + return !itemList.length; }; Town.checkScrolls = function (id, force = false) { - let tome = me.findItem(id, sdk.items.mode.inStorage, sdk.storage.Inventory); - - if (!tome) { - switch (id) { - case sdk.items.TomeofIdentify: - case "ibk": - return (Config.FieldID.Enabled || force) ? 0 : 20; // Ignore missing ID tome if we aren't using field ID - case sdk.items.TomeofTownPortal: - case "tbk": - return 0; // Force TP tome check - } - } - - return tome.getStat(sdk.stats.Quantity); + let tome = me.findItem(id, sdk.items.mode.inStorage, sdk.storage.Inventory); + + if (!tome) { + switch (id) { + case sdk.items.TomeofIdentify: + case "ibk": + return (Config.FieldID.Enabled || force) ? 0 : 20; // Ignore missing ID tome if we aren't using field ID + case sdk.items.TomeofTownPortal: + case "tbk": + return 0; // Force TP tome check + } + } + + return tome.getStat(sdk.stats.Quantity); }; /** @@ -161,515 +161,515 @@ Town.checkScrolls = function (id, force = false) { * @returns {void} */ Town.itemResult = function (item, result, system = "", sell = false) { - let timer = 0; - sell && !getInteractedNPC() && (sell = false); - - switch (result.result) { - case Pickit.Result.WANTED: - case Pickit.Result.SOLOWANTS: - Item.logger("Kept", item); - Item.logItem("Kept", item, result.line); - system === "Field" && ((Item.autoEquipCheck(item) && Item.autoEquip("Field")) || (Item.autoEquipCheckSecondary(item) && Item.autoEquipSecondary("Field"))); - - break; - case Pickit.Result.UNID: - // At low level its not worth keeping these items until we can Id them it just takes up too much room - if (sell && me.charlvl < 10 && item.magic && item.classid !== sdk.items.SmallCharm) { - Item.logger("Sold", item); - item.sell(); - } - - break; - case Pickit.Result.CUBING: - Item.logger("Kept", item, "Cubing-" + system); - Cubing.update(); - - break; - case Pickit.Result.RUNEWORD: - break; - case Pickit.Result.CRAFTING: - Item.logger("Kept", item, "CraftSys-" + system); - CraftingSystem.update(item); - - break; - case Pickit.Result.SOLOSYSTEM: - Item.logger("Kept", item, "SoloWants-" + system); - SoloWants.update(item); - - break; - default: - if (!item.sellable || !sell) return; - - switch (true) { - case (Developer.debugging.smallCharm && item.classid === sdk.items.SmallCharm): - case (Developer.debugging.largeCharm && item.classid === sdk.items.LargeCharm): - case (Developer.debugging.grandCharm && item.classid === sdk.items.GrandCharm): - Item.logItem("Sold", item); - - break; - default: - Item.logger("Sold", item); - - break; - } - - item.sell(); - timer = getTickCount() - this.sellTimer; // shop speedup test - - if (timer > 0 && timer < 500) { - delay(timer); - } - - break; - } + let timer = 0; + sell && !getInteractedNPC() && (sell = false); + + switch (result.result) { + case Pickit.Result.WANTED: + case Pickit.Result.SOLOWANTS: + Item.logger("Kept", item); + Item.logItem("Kept", item, result.line); + system === "Field" && ((Item.autoEquipCheck(item) && Item.autoEquip("Field")) || (Item.autoEquipCheckSecondary(item) && Item.autoEquipSecondary("Field"))); + + break; + case Pickit.Result.UNID: + // At low level its not worth keeping these items until we can Id them it just takes up too much room + if (sell && me.charlvl < 10 && item.magic && item.classid !== sdk.items.SmallCharm) { + Item.logger("Sold", item); + item.sell(); + } + + break; + case Pickit.Result.CUBING: + Item.logger("Kept", item, "Cubing-" + system); + Cubing.update(); + + break; + case Pickit.Result.RUNEWORD: + break; + case Pickit.Result.CRAFTING: + Item.logger("Kept", item, "CraftSys-" + system); + CraftingSystem.update(item); + + break; + case Pickit.Result.SOLOSYSTEM: + Item.logger("Kept", item, "SoloWants-" + system); + SoloWants.update(item); + + break; + default: + if (!item.sellable || !sell) return; + + switch (true) { + case (Developer.debugging.smallCharm && item.classid === sdk.items.SmallCharm): + case (Developer.debugging.largeCharm && item.classid === sdk.items.LargeCharm): + case (Developer.debugging.grandCharm && item.classid === sdk.items.GrandCharm): + Item.logItem("Sold", item); + + break; + default: + Item.logger("Sold", item); + + break; + } + + item.sell(); + timer = getTickCount() - this.sellTimer; // shop speedup test + + if (timer > 0 && timer < 500) { + delay(timer); + } + + break; + } }; Town.identify = function () { - /** - * @todo use cain we are closer to him than our shop npc - */ - if (me.gold < 15000 && NPCAction.cainID(true)) return true; - - let list = (Storage.Inventory.Compare(Config.Inventory) || []) - .filter(item => !item.identified); - if (!list.length) return false; - - // Avoid unnecessary NPC visits - // Only unid items or sellable junk (low level) should trigger a NPC visit - if (!list.some(item => { - let identified = item.identified; - if (!identified && AutoEquip.hasTier(item)) return true; - return ([Pickit.Result.UNID, Pickit.Result.TRASH].includes(Pickit.checkItem(item).result)); - })) { - return false; - } - - let tome = me.getTome(sdk.items.TomeofIdentify); - // if we have a tome might as well use it - this might prevent us from having to run from one npc to another - if (tome && tome.getStat(sdk.stats.Quantity) > 0 && Town.getDistance(Town.tasks[me.act - 1].Shop) > 5) { - // not in the field but oh well no need to repeat the code - if (me.fieldID() && !me.getUnids().length) { - return true; - } - } - - let npc = Town.initNPC("Shop", "identify"); - if (!npc) return false; - - tome && tome.getStat(sdk.stats.Quantity) < list.length && NPCAction.fillTome(sdk.items.TomeofIdentify); - - MainLoop: - while (list.length > 0) { - const item = list.shift(); - - if (!Town.ignoreType(item.itemType) && item.isInInventory && !item.identified) { - let result = Pickit.checkItem(item).result; - // Force ID for unid items matching autoEquip/cubing criteria - Town.needForceID(item) && (result = -1); - - switch (result) { - // Items for gold, will sell magics, etc. w/o id, but at low levels - // magics are often not worth iding. - case Pickit.Result.TRASH: - Item.logger("Sold", item); - item.sell(); - - break; - case Pickit.Result.UNID: - let idTool = me.getIdTool(); - - if (idTool) { - this.identifyItem(item, idTool); - } else { - let scroll = npc.getItem(sdk.items.ScrollofIdentify); - - if (scroll) { - if (!Storage.Inventory.CanFit(scroll)) { - let tpTome = me.getTome(sdk.items.TomeofTownPortal); - !!tpTome && tpTome.sell() && delay(500); - } - - delay(500); - Storage.Inventory.CanFit(scroll) && scroll.buy(); - } - - scroll = me.findItem(sdk.items.ScrollofIdentify, sdk.items.mode.inStorage, sdk.storage.Inventory); - - if (!scroll) { - break MainLoop; - } - - this.identifyItem(item, scroll); - } - - result = Pickit.checkItem(item); - Town.itemResult(item, result, "TownId", true); - - break; - } - } - } - - NPCAction.fillTome(sdk.items.TomeofTownPortal); // Check for TP tome in case it got sold for ID scrolls - - return true; + /** + * @todo use cain we are closer to him than our shop npc + */ + if (me.gold < 15000 && NPCAction.cainID(true)) return true; + + let list = (Storage.Inventory.Compare(Config.Inventory) || []) + .filter(item => !item.identified); + if (!list.length) return false; + + // Avoid unnecessary NPC visits + // Only unid items or sellable junk (low level) should trigger a NPC visit + if (!list.some(item => { + let identified = item.identified; + if (!identified && AutoEquip.hasTier(item)) return true; + return ([Pickit.Result.UNID, Pickit.Result.TRASH].includes(Pickit.checkItem(item).result)); + })) { + return false; + } + + let tome = me.getTome(sdk.items.TomeofIdentify); + // if we have a tome might as well use it - this might prevent us from having to run from one npc to another + if (tome && tome.getStat(sdk.stats.Quantity) > 0 && Town.getDistance(Town.tasks[me.act - 1].Shop) > 5) { + // not in the field but oh well no need to repeat the code + if (me.fieldID() && !me.getUnids().length) { + return true; + } + } + + let npc = Town.initNPC("Shop", "identify"); + if (!npc) return false; + + tome && tome.getStat(sdk.stats.Quantity) < list.length && NPCAction.fillTome(sdk.items.TomeofIdentify); + + MainLoop: + while (list.length > 0) { + const item = list.shift(); + + if (!Town.ignoreType(item.itemType) && item.isInInventory && !item.identified) { + let result = Pickit.checkItem(item).result; + // Force ID for unid items matching autoEquip/cubing criteria + Town.needForceID(item) && (result = -1); + + switch (result) { + // Items for gold, will sell magics, etc. w/o id, but at low levels + // magics are often not worth iding. + case Pickit.Result.TRASH: + Item.logger("Sold", item); + item.sell(); + + break; + case Pickit.Result.UNID: + let idTool = me.getIdTool(); + + if (idTool) { + this.identifyItem(item, idTool); + } else { + let scroll = npc.getItem(sdk.items.ScrollofIdentify); + + if (scroll) { + if (!Storage.Inventory.CanFit(scroll)) { + let tpTome = me.getTome(sdk.items.TomeofTownPortal); + !!tpTome && tpTome.sell() && delay(500); + } + + delay(500); + Storage.Inventory.CanFit(scroll) && scroll.buy(); + } + + scroll = me.findItem(sdk.items.ScrollofIdentify, sdk.items.mode.inStorage, sdk.storage.Inventory); + + if (!scroll) { + break MainLoop; + } + + this.identifyItem(item, scroll); + } + + result = Pickit.checkItem(item); + Town.itemResult(item, result, "TownId", true); + + break; + } + } + } + + NPCAction.fillTome(sdk.items.TomeofTownPortal); // Check for TP tome in case it got sold for ID scrolls + + return true; }; Town.needStash = function () { - if (Config.StashGold - && me.getStat(sdk.stats.Gold) >= Config.StashGold - && me.getStat(sdk.stats.GoldBank) < 25e5) { - return true; - } - - let items = (Storage.Inventory.Compare(Config.Inventory) || []) - .filter(item => !Town.ignoreType(item.itemType) && (!item.isCharm || !CharmEquip.keptGids.has(item.gid))); - - for (let i = 0; i < items.length; i += 1) { - if (Storage.Stash.CanFit(items[i])) { - return true; - } - } - - return false; + if (Config.StashGold + && me.getStat(sdk.stats.Gold) >= Config.StashGold + && me.getStat(sdk.stats.GoldBank) < 25e5) { + return true; + } + + let items = (Storage.Inventory.Compare(Config.Inventory) || []) + .filter(item => !Town.ignoreType(item.itemType) && (!item.isCharm || !CharmEquip.keptGids.has(item.gid))); + + for (let i = 0; i < items.length; i += 1) { + if (Storage.Stash.CanFit(items[i])) { + return true; + } + } + + return false; }; /** * @param {ItemUnit} item */ Town.canStash = function (item) { - if (Town.ignoreType(item.itemType) - || [sdk.items.quest.HoradricStaff, sdk.items.quest.KhalimsWill].includes(item.classid) - || (item.isCharm && CharmEquip.check(item))) { - return false; - } + if (Town.ignoreType(item.itemType) + || [sdk.items.quest.HoradricStaff, sdk.items.quest.KhalimsWill].includes(item.classid) + || (item.isCharm && CharmEquip.check(item))) { + return false; + } - !Storage.Stash.CanFit(item) && this.sortStash(true); + !Storage.Stash.CanFit(item) && this.sortStash(true); - return Storage.Stash.CanFit(item); + return Storage.Stash.CanFit(item); }; Town.stash = function (stashGold = true) { - if (!this.needStash()) return true; - !getUIFlag(sdk.uiflags.Stash) && me.cancel(); - - let items = (Storage.Inventory.Compare(Config.Inventory) || []); - - if (items.length > 0) { - Storage.Stash.SortItems(); - - items.forEach(item => { - if (this.canStash(item)) { - const pickResult = Pickit.checkItem(item).result; - switch (true) { - case pickResult !== Pickit.Result.UNWANTED && pickResult !== Pickit.Result.TRASH: - case Town.systemsKeep(item): - case AutoEquip.wanted(item) && pickResult === Pickit.Result.UNWANTED: // wanted but can't use yet - case !item.sellable: // quest/essences/keys/ect - if ([sdk.quest.item.PotofLife, sdk.quest.item.ScrollofResistance].includes(item.classid)) { - // don't stash item, use it - let refName = item.prettyPrint; - if (item.use()) { - console.log("Used " + refName); - return; - } - } - Storage.Stash.MoveTo(item) && Item.logger("Stashed", item); - - break; - } - } - }); - } - - - // Stash gold - if (stashGold) { - if (me.getStat(sdk.stats.Gold) >= Config.StashGold && me.getStat(sdk.stats.GoldBank) < 25e5 && this.openStash()) { - gold(me.getStat(sdk.stats.Gold), 3); - delay(1000); // allow UI to initialize - me.cancel(); - } - } - - return true; + if (!this.needStash()) return true; + !getUIFlag(sdk.uiflags.Stash) && me.cancel(); + + let items = (Storage.Inventory.Compare(Config.Inventory) || []); + + if (items.length > 0) { + Storage.Stash.SortItems(); + + items.forEach(item => { + if (this.canStash(item)) { + const pickResult = Pickit.checkItem(item).result; + switch (true) { + case pickResult !== Pickit.Result.UNWANTED && pickResult !== Pickit.Result.TRASH: + case Town.systemsKeep(item): + case AutoEquip.wanted(item) && pickResult === Pickit.Result.UNWANTED: // wanted but can't use yet + case !item.sellable: // quest/essences/keys/ect + if ([sdk.quest.item.PotofLife, sdk.quest.item.ScrollofResistance].includes(item.classid)) { + // don't stash item, use it + let refName = item.prettyPrint; + if (item.use()) { + console.log("Used " + refName); + return; + } + } + Storage.Stash.MoveTo(item) && Item.logger("Stashed", item); + + break; + } + } + }); + } + + + // Stash gold + if (stashGold) { + if (me.getStat(sdk.stats.Gold) >= Config.StashGold && me.getStat(sdk.stats.GoldBank) < 25e5 && this.openStash()) { + gold(me.getStat(sdk.stats.Gold), 3); + delay(1000); // allow UI to initialize + me.cancel(); + } + } + + return true; }; Town.sortStash = function (force = false) { - if (Storage.Stash.UsedSpacePercent() < 50 && !force) return true; - return Storage.Stash.SortItems(); + if (Storage.Stash.UsedSpacePercent() < 50 && !force) return true; + return Storage.Stash.SortItems(); }; Town.clearInventory = function () { - console.log("ÿc8Start ÿc0:: ÿc8clearInventory"); - let clearInvoTick = getTickCount(); - - // If we are at an npc already, open the window otherwise moving potions around fails - if (getUIFlag(sdk.uiflags.NPCMenu) && !getUIFlag(sdk.uiflags.Shop)) { - try { - console.debug("Open npc menu"); - !!getInteractedNPC() && Misc.useMenu(sdk.menu.Trade); - } catch (e) { - console.error(e); - me.cancelUIFlags(); - } - } - - // Remove potions in the wrong slot of our belt - me.clearBelt(); - - // Return potions from inventory to belt - me.cleanUpInvoPotions(); - - // Cleanup remaining potions - console.debug("clearInventory: start clean-up remaining pots"); - let sellOrDrop = []; - let potsInInventory = me.getItemsEx() - .filter((p) => p.isInInventory && [ - sdk.items.type.HealingPotion, sdk.items.type.ManaPotion, sdk.items.type.RejuvPotion, - sdk.items.type.ThawingPotion, sdk.items.type.AntidotePotion, sdk.items.type.StaminaPotion - ].includes(p.itemType)); - - if (potsInInventory.length > 0) { - let [hp, mp, rv, specials] = [[], [], [], []]; - - while (potsInInventory.length) { - (function (p) { - switch (p.itemType) { - case sdk.items.type.HealingPotion: - return (hp.push(p)); - case sdk.items.type.ManaPotion: - return (mp.push(p)); - case sdk.items.type.RejuvPotion: - return (rv.push(p)); - case sdk.items.type.ThawingPotion: - case sdk.items.type.AntidotePotion: - case sdk.items.type.StaminaPotion: - default: // shuts d2bs up - return (specials.push(p)); - } - })(potsInInventory.shift()); - } - - // Cleanup healing potions - while (hp.length > Config.HPBuffer) { - sellOrDrop.push(hp.shift()); - } - - // Cleanup mana potions - while (mp.length > Config.MPBuffer) { - sellOrDrop.push(mp.shift()); - } - - // Cleanup rejuv potions - while (rv.length > Config.RejuvBuffer) { - sellOrDrop.push(rv.shift()); - } - - // Clean up special pots - while (specials.length) { - specials.shift().interact(); - delay(200); - } - } - - if (Config.FieldID.Enabled && !me.getItem(sdk.items.TomeofIdentify)) { - let scrolls = me.getItemsEx().filter(i => i.isInInventory && i.classid === sdk.items.ScrollofIdentify); - - while (scrolls.length > 2) { - sellOrDrop.push(scrolls.shift()); - } - } - - // Any leftover items from a failed ID (crashed game, disconnect etc.) - const ignoreTypes = [ - sdk.items.type.Book, sdk.items.type.Key, sdk.items.type.HealingPotion, sdk.items.type.ManaPotion, sdk.items.type.RejuvPotion - ]; - let items = (Storage.Inventory.Compare(Config.Inventory) || []) - .filter(function (item) { - if (!item) return false; - if (item.classid === sdk.items.TomeofIdentify && !Config.FieldID.Enabled) return true; - if (ignoreTypes.indexOf(item.itemType) === -1 && item.sellable && !Town.systemsKeep(item)) { - return true; - } - return false; - }); - - /** - * @type {ItemUnit[]} - */ - let sell = []; - - /** - * @param {ItemUnit} item - * @returns {boolean} - */ - const classItemType = (item) => [ - sdk.items.type.Wand, sdk.items.type.VoodooHeads, sdk.items.type.AuricShields, sdk.items.type.PrimalHelm, sdk.items.type.Pelt - ].includes(item.itemType); - - items.length > 0 && items.forEach(function (item) { - let result = Pickit.checkItem(item).result; - - if ([Pickit.Result.UNWANTED, Pickit.Result.TRASH].indexOf(result) === -1) { - if ((item.isBaseType && item.sockets > 0) || (classItemType(item) && item.normal && item.sockets === 0)) { - if (!Item.betterThanStashed(item) && !Item.betterBaseThanWearing(item, Developer.debugging.baseCheck)) { - if (NTIP.CheckItem(item, NTIP_CheckListNoTier) === Pickit.Result.UNWANTED) { - result = Pickit.Result.TRASH; - } - } - } - } - - !item.identified && (result = -1); - [Pickit.Result.UNWANTED, Pickit.Result.TRASH].includes(result) && sell.push(item); - }); - - sell = (sell.length > 0 ? sell.concat(sellOrDrop) : sellOrDrop.slice(0)); - sell.length > 0 && this.initNPC("Shop", "clearInventory") && sell.forEach(function (item) { - try { - if (getUIFlag(sdk.uiflags.Shop) || getUIFlag(sdk.uiflags.NPCMenu)) { - console.log("clearInventory sell " + item.prettyPrint); - Item.logger("Sold", item); - item.sell(); - delay(100); - } - } catch (e) { - console.error(e); - } - }); - - Town.sell = []; - - console.log("ÿc8Exit clearInventory ÿc0- ÿc7Duration: ÿc0" + Time.format(getTickCount() - clearInvoTick)); - - return true; + console.log("ÿc8Start ÿc0:: ÿc8clearInventory"); + let clearInvoTick = getTickCount(); + + // If we are at an npc already, open the window otherwise moving potions around fails + if (getUIFlag(sdk.uiflags.NPCMenu) && !getUIFlag(sdk.uiflags.Shop)) { + try { + console.debug("Open npc menu"); + !!getInteractedNPC() && Misc.useMenu(sdk.menu.Trade); + } catch (e) { + console.error(e); + me.cancelUIFlags(); + } + } + + // Remove potions in the wrong slot of our belt + me.clearBelt(); + + // Return potions from inventory to belt + me.cleanUpInvoPotions(); + + // Cleanup remaining potions + console.debug("clearInventory: start clean-up remaining pots"); + let sellOrDrop = []; + let potsInInventory = me.getItemsEx() + .filter((p) => p.isInInventory && [ + sdk.items.type.HealingPotion, sdk.items.type.ManaPotion, sdk.items.type.RejuvPotion, + sdk.items.type.ThawingPotion, sdk.items.type.AntidotePotion, sdk.items.type.StaminaPotion + ].includes(p.itemType)); + + if (potsInInventory.length > 0) { + let [hp, mp, rv, specials] = [[], [], [], []]; + + while (potsInInventory.length) { + (function (p) { + switch (p.itemType) { + case sdk.items.type.HealingPotion: + return (hp.push(p)); + case sdk.items.type.ManaPotion: + return (mp.push(p)); + case sdk.items.type.RejuvPotion: + return (rv.push(p)); + case sdk.items.type.ThawingPotion: + case sdk.items.type.AntidotePotion: + case sdk.items.type.StaminaPotion: + default: // shuts d2bs up + return (specials.push(p)); + } + })(potsInInventory.shift()); + } + + // Cleanup healing potions + while (hp.length > Config.HPBuffer) { + sellOrDrop.push(hp.shift()); + } + + // Cleanup mana potions + while (mp.length > Config.MPBuffer) { + sellOrDrop.push(mp.shift()); + } + + // Cleanup rejuv potions + while (rv.length > Config.RejuvBuffer) { + sellOrDrop.push(rv.shift()); + } + + // Clean up special pots + while (specials.length) { + specials.shift().interact(); + delay(200); + } + } + + if (Config.FieldID.Enabled && !me.getItem(sdk.items.TomeofIdentify)) { + let scrolls = me.getItemsEx().filter(i => i.isInInventory && i.classid === sdk.items.ScrollofIdentify); + + while (scrolls.length > 2) { + sellOrDrop.push(scrolls.shift()); + } + } + + // Any leftover items from a failed ID (crashed game, disconnect etc.) + const ignoreTypes = [ + sdk.items.type.Book, sdk.items.type.Key, sdk.items.type.HealingPotion, sdk.items.type.ManaPotion, sdk.items.type.RejuvPotion + ]; + let items = (Storage.Inventory.Compare(Config.Inventory) || []) + .filter(function (item) { + if (!item) return false; + if (item.classid === sdk.items.TomeofIdentify && !Config.FieldID.Enabled) return true; + if (ignoreTypes.indexOf(item.itemType) === -1 && item.sellable && !Town.systemsKeep(item)) { + return true; + } + return false; + }); + + /** + * @type {ItemUnit[]} + */ + let sell = []; + + /** + * @param {ItemUnit} item + * @returns {boolean} + */ + const classItemType = (item) => [ + sdk.items.type.Wand, sdk.items.type.VoodooHeads, sdk.items.type.AuricShields, sdk.items.type.PrimalHelm, sdk.items.type.Pelt + ].includes(item.itemType); + + items.length > 0 && items.forEach(function (item) { + let result = Pickit.checkItem(item).result; + + if ([Pickit.Result.UNWANTED, Pickit.Result.TRASH].indexOf(result) === -1) { + if ((item.isBaseType && item.sockets > 0) || (classItemType(item) && item.normal && item.sockets === 0)) { + if (!Item.betterThanStashed(item) && !Item.betterBaseThanWearing(item, Developer.debugging.baseCheck)) { + if (NTIP.CheckItem(item, NTIP_CheckListNoTier) === Pickit.Result.UNWANTED) { + result = Pickit.Result.TRASH; + } + } + } + } + + !item.identified && (result = -1); + [Pickit.Result.UNWANTED, Pickit.Result.TRASH].includes(result) && sell.push(item); + }); + + sell = (sell.length > 0 ? sell.concat(sellOrDrop) : sellOrDrop.slice(0)); + sell.length > 0 && this.initNPC("Shop", "clearInventory") && sell.forEach(function (item) { + try { + if (getUIFlag(sdk.uiflags.Shop) || getUIFlag(sdk.uiflags.NPCMenu)) { + console.log("clearInventory sell " + item.prettyPrint); + Item.logger("Sold", item); + item.sell(); + delay(100); + } + } catch (e) { + console.error(e); + } + }); + + Town.sell = []; + + console.log("ÿc8Exit clearInventory ÿc0- ÿc7Duration: ÿc0" + Time.format(getTickCount() - clearInvoTick)); + + return true; }; Town.clearJunk = function () { - let junkItems = me.getItemsEx() - .filter(i => i.isInStorage && !Town.ignoreType(i.itemType) && i.sellable && !Town.systemsKeep(i)); - if (!junkItems.length) return false; - - console.log("ÿc8Start ÿc0:: ÿc8clearJunk"); - let clearJunkTick = getTickCount(); - - /** - * @type {ItemUnit[][]} - */ - let [totalJunk, junkToSell, junkToDrop] = [[], [], []]; - - /** - * @param {string} str - * @param {ItemUnit} item - * @returns {boolean} - */ - const getToItem = (str = "", item = null) => { - if (!getUIFlag(sdk.uiflags.Stash) && item.isInStash && !Town.openStash()) { - throw new Error("ÿc9" + str + "ÿc0 :: Failed to get " + item.prettyPrint + " from stash"); - } - if (item.isInCube && !Cubing.emptyCube()) throw new Error("ÿc9" + str + "ÿc0 :: Failed to remove " + item.prettyPrint + " from cube"); - return true; - }; - - while (junkItems.length > 0) { - const junk = junkItems.shift(); - const pickitResult = Pickit.checkItem(junk).result; - - try { - if ([Pickit.Result.UNWANTED, Pickit.Result.TRASH].includes(pickitResult)) { - console.log("ÿc9JunkCheckÿc0 :: Junk: " + junk.prettyPrint + " Pickit Result: " + pickitResult); - getToItem("JunkCheck", junk) && totalJunk.push(junk); - - continue; - } - - if (pickitResult !== Pickit.Result.WANTED) { - if (!junk.identified && !Cubing.keepItem(junk) && !CraftingSystem.keepItem(junk) && junk.quality < sdk.items.quality.Set) { - console.log("ÿc9UnidJunkCheckÿc0 :: Junk: " + junk.prettyPrint + " Junk type: " + junk.itemType + " Pickit Result: " + pickitResult); - getToItem("UnidJunkCheck", junk) && totalJunk.push(junk); - - continue; - } - } - - if (junk.isRuneword && !AutoEquip.wanted(junk)) { - console.log("ÿc9AutoEquipJunkCheckÿc0 :: Junk: " + junk.prettyPrint + " Junk type: " + junk.itemType + " Pickit Result: " + pickitResult); - getToItem("AutoEquipJunkCheck", junk) && totalJunk.push(junk); - - continue; - } - - if (junk.isBaseType && [Pickit.Result.CUBING, Pickit.Result.SOLOWANTS].includes(pickitResult)) { - if (!Item.betterThanStashed(junk)) { - console.log("ÿc9BetterThanStashedCheckÿc0 :: Base: " + junk.prettyPrint + " Junk type: " + junk.itemType + " Pickit Result: " + pickitResult); - getToItem("BetterThanStashedCheck", junk) && totalJunk.push(junk); - - continue; - } - - if (!Item.betterBaseThanWearing(junk, Developer.debugging.baseCheck)) { - console.log("ÿc9BetterThanWearingCheckÿc0 :: Base: " + junk.prettyPrint + " Junk type: " + junk.itemType + " Pickit Result: " + pickitResult); - getToItem("BetterThanWearingCheck", junk) && totalJunk.push(junk); - - continue; - } - } - } catch (e) { - console.warn(e.message ? e.message : e); - } - } - - if (totalJunk.length > 0) { - totalJunk - .sort((a, b) => b.getItemCost(sdk.items.cost.ToSell) - a.getItemCost(sdk.items.cost.ToSell)) - .forEach(junk => { - // extra check should ensure no pickit wanted items get sold/dropped - if (NTIP.CheckItem(junk, NTIP_CheckListNoTier) === Pickit.Result.WANTED) return; - if (junk.isInInventory || (Storage.Inventory.CanFit(junk) && Storage.Inventory.MoveTo(junk))) { - junkToSell.push(junk); - } else { - junkToDrop.push(junk); - } - }); - - myPrint("Junk items to sell: " + junkToSell.length); - Town.initNPC("Shop", "clearInventory"); - - if (getUIFlag(sdk.uiflags.Shop) || (Config.PacketShopping && getInteractedNPC() && getInteractedNPC().itemcount > 0)) { - for (let i = 0; i < junkToSell.length; i++) { - console.log("ÿc9JunkCheckÿc0 :: Sell " + junkToSell[i].prettyPrint); - Item.logger("Sold", junkToSell[i]); - Developer.debugging.junkCheck && Item.logItem("JunkCheck Sold", junkToSell[i]); - - junkToSell[i].sell(); - delay(100); - } - } - - me.cancelUIFlags(); - - for (let i = 0; i < junkToDrop.length; i++) { - console.log("ÿc9JunkCheckÿc0 :: Drop " + junkToDrop[i].prettyPrint); - Item.logger("Sold", junkToDrop[i]); - Developer.debugging.junkCheck && Item.logItem("JunkCheck Sold", junkToDrop[i]); - - junkToDrop[i].drop(); - delay(100); - } - } - - console.log("ÿc8Exit clearJunk ÿc0- ÿc7Duration: ÿc0" + Time.format(getTickCount() - clearJunkTick)); - - return true; + let junkItems = me.getItemsEx() + .filter(i => i.isInStorage && !Town.ignoreType(i.itemType) && i.sellable && !Town.systemsKeep(i)); + if (!junkItems.length) return false; + + console.log("ÿc8Start ÿc0:: ÿc8clearJunk"); + let clearJunkTick = getTickCount(); + + /** + * @type {ItemUnit[][]} + */ + let [totalJunk, junkToSell, junkToDrop] = [[], [], []]; + + /** + * @param {string} str + * @param {ItemUnit} item + * @returns {boolean} + */ + const getToItem = (str = "", item = null) => { + if (!getUIFlag(sdk.uiflags.Stash) && item.isInStash && !Town.openStash()) { + throw new Error("ÿc9" + str + "ÿc0 :: Failed to get " + item.prettyPrint + " from stash"); + } + if (item.isInCube && !Cubing.emptyCube()) throw new Error("ÿc9" + str + "ÿc0 :: Failed to remove " + item.prettyPrint + " from cube"); + return true; + }; + + while (junkItems.length > 0) { + const junk = junkItems.shift(); + const pickitResult = Pickit.checkItem(junk).result; + + try { + if ([Pickit.Result.UNWANTED, Pickit.Result.TRASH].includes(pickitResult)) { + console.log("ÿc9JunkCheckÿc0 :: Junk: " + junk.prettyPrint + " Pickit Result: " + pickitResult); + getToItem("JunkCheck", junk) && totalJunk.push(junk); + + continue; + } + + if (pickitResult !== Pickit.Result.WANTED) { + if (!junk.identified && !Cubing.keepItem(junk) && !CraftingSystem.keepItem(junk) && junk.quality < sdk.items.quality.Set) { + console.log("ÿc9UnidJunkCheckÿc0 :: Junk: " + junk.prettyPrint + " Junk type: " + junk.itemType + " Pickit Result: " + pickitResult); + getToItem("UnidJunkCheck", junk) && totalJunk.push(junk); + + continue; + } + } + + if (junk.isRuneword && !AutoEquip.wanted(junk)) { + console.log("ÿc9AutoEquipJunkCheckÿc0 :: Junk: " + junk.prettyPrint + " Junk type: " + junk.itemType + " Pickit Result: " + pickitResult); + getToItem("AutoEquipJunkCheck", junk) && totalJunk.push(junk); + + continue; + } + + if (junk.isBaseType && [Pickit.Result.CUBING, Pickit.Result.SOLOWANTS].includes(pickitResult)) { + if (!Item.betterThanStashed(junk)) { + console.log("ÿc9BetterThanStashedCheckÿc0 :: Base: " + junk.prettyPrint + " Junk type: " + junk.itemType + " Pickit Result: " + pickitResult); + getToItem("BetterThanStashedCheck", junk) && totalJunk.push(junk); + + continue; + } + + if (!Item.betterBaseThanWearing(junk, Developer.debugging.baseCheck)) { + console.log("ÿc9BetterThanWearingCheckÿc0 :: Base: " + junk.prettyPrint + " Junk type: " + junk.itemType + " Pickit Result: " + pickitResult); + getToItem("BetterThanWearingCheck", junk) && totalJunk.push(junk); + + continue; + } + } + } catch (e) { + console.warn(e.message ? e.message : e); + } + } + + if (totalJunk.length > 0) { + totalJunk + .sort((a, b) => b.getItemCost(sdk.items.cost.ToSell) - a.getItemCost(sdk.items.cost.ToSell)) + .forEach(junk => { + // extra check should ensure no pickit wanted items get sold/dropped + if (NTIP.CheckItem(junk, NTIP_CheckListNoTier) === Pickit.Result.WANTED) return; + if (junk.isInInventory || (Storage.Inventory.CanFit(junk) && Storage.Inventory.MoveTo(junk))) { + junkToSell.push(junk); + } else { + junkToDrop.push(junk); + } + }); + + myPrint("Junk items to sell: " + junkToSell.length); + Town.initNPC("Shop", "clearInventory"); + + if (getUIFlag(sdk.uiflags.Shop) || (Config.PacketShopping && getInteractedNPC() && getInteractedNPC().itemcount > 0)) { + for (let i = 0; i < junkToSell.length; i++) { + console.log("ÿc9JunkCheckÿc0 :: Sell " + junkToSell[i].prettyPrint); + Item.logger("Sold", junkToSell[i]); + Developer.debugging.junkCheck && Item.logItem("JunkCheck Sold", junkToSell[i]); + + junkToSell[i].sell(); + delay(100); + } + } + + me.cancelUIFlags(); + + for (let i = 0; i < junkToDrop.length; i++) { + console.log("ÿc9JunkCheckÿc0 :: Drop " + junkToDrop[i].prettyPrint); + Item.logger("Sold", junkToDrop[i]); + Developer.debugging.junkCheck && Item.logItem("JunkCheck Sold", junkToDrop[i]); + + junkToDrop[i].drop(); + delay(100); + } + } + + console.log("ÿc8Exit clearJunk ÿc0- ÿc7Duration: ÿc0" + Time.format(getTickCount() - clearJunkTick)); + + return true; }; Town.lastChores = 0; Town.fillTomes = function () { - NPCAction.fillTome(sdk.items.TomeofTownPortal); - Config.FieldID.Enabled && NPCAction.fillTome(sdk.items.TomeofIdentify); - !!me.getItem(sdk.items.TomeofTownPortal) && this.clearScrolls(); + NPCAction.fillTome(sdk.items.TomeofTownPortal); + Config.FieldID.Enabled && NPCAction.fillTome(sdk.items.TomeofIdentify); + !!me.getItem(sdk.items.TomeofTownPortal) && this.clearScrolls(); }; /** @@ -679,95 +679,95 @@ Town.fillTomes = function () { * @returns {boolean} */ Town.doChores = function (repair = false, givenTasks = {}) { - const extraTasks = Object.assign({}, { - thawing: false, - antidote: false, - stamina: false, - fullChores: false, - }, givenTasks); - - delay(250); - - console.info(true); - console.time("doChores"); - console.debug("doChores Inital Gold :: " + me.gold); - - !me.inTown && Town.goToTown(); - - // Burst of speed while in town - if (Skill.canUse(sdk.skills.BurstofSpeed) && !me.getState(sdk.states.BurstofSpeed)) { - Skill.cast(sdk.skills.BurstofSpeed, sdk.skills.hand.Right); - } - - const preAct = me.act; - - /** - * @todo light chores if last chores was < minute? 2 minutes idk yet - */ - - me.switchWeapons(Attack.getPrimarySlot()); - extraTasks.fullChores && Quest.unfinishedQuests(); - - // Use cainId if we are low on gold or we are closer to him than the shopNPC - if (me.getUnids().length) { - if (me.gold < 5000 - || Town.getDistance("cain") < Town.getDistance(Town.tasks[me.act - 1].Heal)) { - NPCAction.cainID(true); - } - } - - // maybe a check if need healing first, as we might have just used a potion - this.heal(); - this.identify(); - this.clearInventory(); - Town.fillTomes(); - NPCAction.buyPotions(); - this.buyKeys(); - extraTasks.thawing && CharData.pots.get("thawing").need() && Town.buyPots(12, "Thawing", true); - extraTasks.antidote && CharData.pots.get("antidote").need() && Town.buyPots(12, "Antidote", true); - extraTasks.stamina && Town.buyPots(12, "Stamina", true); - NPCAction.shopItems(); - NPCAction.repair(repair); - NPCAction.reviveMerc(); - NPCAction.gamble(); - - // if (me.inArea(sdk.areas.LutGholein) && me.normal && me.gold > 10000) { - // // shop at Elzix - what about others? - // NPCAction.shopAt(NPC.Elzix); - // } - Cubing.emptyCube(); - Runewords.makeRunewords(); - Cubing.doCubing(); - Runewords.makeRunewords(); - AutoEquip.run(); - Mercenary.hireMerc(); - Item.autoEquipMerc(); - Town.haveItemsToSell() && Town.sellItems() && me.cancelUIFlags(); - this.clearJunk(); - this.stash(); - - // check pots again, we might have enough gold now if we didn't before - me.needPotions() && NPCAction.buyPotions() && me.cancelUIFlags(); - // check repair again, we might have enough gold now if we didn't before - me.needRepair() && NPCAction.repair() && me.cancelUIFlags(); - - me.sortInventory(); - Quest.characterRespec(); - - me.act !== preAct && this.goToTown(preAct); - me.cancelUIFlags(); - !me.barbarian && !Precast.checkCTA() && Precast.doPrecast(false); - - if (me.expansion) { - Attack.checkBowOnSwitch(); - Attack.getCurrentChargedSkillIds(); - Pather.checkForTeleCharges(); - } - - delay(300); - console.debug("doChores Ending Gold :: " + me.gold); - console.info(false, null, "doChores"); - Town.lastChores = getTickCount(); - - return true; + const extraTasks = Object.assign({}, { + thawing: false, + antidote: false, + stamina: false, + fullChores: false, + }, givenTasks); + + delay(250); + + console.info(true); + console.time("doChores"); + console.debug("doChores Inital Gold :: " + me.gold); + + !me.inTown && Town.goToTown(); + + // Burst of speed while in town + if (Skill.canUse(sdk.skills.BurstofSpeed) && !me.getState(sdk.states.BurstofSpeed)) { + Skill.cast(sdk.skills.BurstofSpeed, sdk.skills.hand.Right); + } + + const preAct = me.act; + + /** + * @todo light chores if last chores was < minute? 2 minutes idk yet + */ + + me.switchWeapons(Attack.getPrimarySlot()); + extraTasks.fullChores && Quest.unfinishedQuests(); + + // Use cainId if we are low on gold or we are closer to him than the shopNPC + if (me.getUnids().length) { + if (me.gold < 5000 + || Town.getDistance("cain") < Town.getDistance(Town.tasks[me.act - 1].Heal)) { + NPCAction.cainID(true); + } + } + + // maybe a check if need healing first, as we might have just used a potion + this.heal(); + this.identify(); + this.clearInventory(); + Town.fillTomes(); + NPCAction.buyPotions(); + this.buyKeys(); + extraTasks.thawing && CharData.pots.get("thawing").need() && Town.buyPots(12, "Thawing", true); + extraTasks.antidote && CharData.pots.get("antidote").need() && Town.buyPots(12, "Antidote", true); + extraTasks.stamina && Town.buyPots(12, "Stamina", true); + NPCAction.shopItems(); + NPCAction.repair(repair); + NPCAction.reviveMerc(); + NPCAction.gamble(); + + // if (me.inArea(sdk.areas.LutGholein) && me.normal && me.gold > 10000) { + // // shop at Elzix - what about others? + // NPCAction.shopAt(NPC.Elzix); + // } + Cubing.emptyCube(); + Runewords.makeRunewords(); + Cubing.doCubing(); + Runewords.makeRunewords(); + AutoEquip.run(); + Mercenary.hireMerc(); + Item.autoEquipMerc(); + Town.haveItemsToSell() && Town.sellItems() && me.cancelUIFlags(); + this.clearJunk(); + this.stash(); + + // check pots again, we might have enough gold now if we didn't before + me.needPotions() && NPCAction.buyPotions() && me.cancelUIFlags(); + // check repair again, we might have enough gold now if we didn't before + me.needRepair() && NPCAction.repair() && me.cancelUIFlags(); + + me.sortInventory(); + Quest.characterRespec(); + + me.act !== preAct && this.goToTown(preAct); + me.cancelUIFlags(); + !me.barbarian && !Precast.checkCTA() && Precast.doPrecast(false); + + if (me.expansion) { + Attack.checkBowOnSwitch(); + Attack.getCurrentChargedSkillIds(); + Pather.checkForTeleCharges(); + } + + delay(300); + console.debug("doChores Ending Gold :: " + me.gold); + console.info(false, null, "doChores"); + Town.lastChores = getTickCount(); + + return true; }; diff --git a/libs/SoloPlay/Modules/Clear.js b/libs/SoloPlay/Modules/Clear.js index 421644a4..1abbaaae 100644 --- a/libs/SoloPlay/Modules/Clear.js +++ b/libs/SoloPlay/Modules/Clear.js @@ -1,206 +1,206 @@ var __importDefault = (this && this.__importDefault) || function (mod) { - return (mod && mod.__esModule) ? mod : { "default": mod }; + return (mod && mod.__esModule) ? mod : { "default": mod }; }; (function (factory) { - if (typeof module === "object" && typeof module.exports === "object") { - const v = factory(require, exports); - if (v !== undefined) module.exports = v; - } else if (typeof define === "function" && define.amd) { - define(["require", "exports", "../../modules/sdk", "./Events", "./Coords", "./MissileData"], factory); - } + if (typeof module === "object" && typeof module.exports === "object") { + const v = factory(require, exports); + if (v !== undefined) module.exports = v; + } else if (typeof define === "function" && define.amd) { + define(["require", "exports", "../../modules/sdk", "./Events", "./Coords", "./MissileData"], factory); + } })(function (require, exports) { - "use strict"; - if (!isIncluded("libs/SoloPlay/Functions/PatherOverrides.js")) { include("libs/SoloPlay/Functions/PatherOverrides.js"); } - include("SoloPlay/Functions/ClassAttackOverrides/" + sdk.player.class.nameOf(me.classid) + "Attacks.js"); - global["__________ignoreMonster"] = []; - const sdk_1 = __importDefault(require("../../modules/sdk")); - const Events_1 = require("./Events"); - const Coords_1 = require("./Coords"); - const MissileData_1 = __importDefault(require("./GameData/MissileData")); - const defaults = { - range: 14, - spectype: 0, - once: false, - nodes: [], - callback: undefined, - filter: undefined //(monster: Monster, node: {x, y}[]) => true - }; - const shamans = [ - sdk_1.default.monsters.FallenShaman, sdk_1.default.monsters.CarverShaman2, sdk_1.default.monsters.DevilkinShaman2, sdk_1.default.monsters.DarkShaman1, - sdk_1.default.monsters.WarpedShaman, sdk_1.default.monsters.CarverShaman, sdk_1.default.monsters.DevilkinShaman, sdk_1.default.monsters.DarkShaman2 - ]; - const fallens = [ - sdk_1.default.monsters.Fallen, sdk_1.default.monsters.Carver2, sdk_1.default.monsters.Devilkin2, sdk_1.default.monsters.DarkOne1, - sdk_1.default.monsters.WarpedFallen, sdk_1.default.monsters.Carver1, sdk_1.default.monsters.Devilkin, sdk_1.default.monsters.DarkOne2 - ]; - const clearDistance = function (x, y, xx, yy) { - getUnits(sdk.unittype.Monster).forEach(function (monster) { - if (typeof monster["beendead"] === "undefined") - monster.beendead = false; - monster.beendead = monster.beendead || monster.dead; - }); - let path = getPath(me.area, x, y, xx, yy, 0, 4); - if (!path || !path.length) return Infinity; + "use strict"; + if (!isIncluded("libs/SoloPlay/Functions/PatherOverrides.js")) { include("libs/SoloPlay/Functions/PatherOverrides.js"); } + include("SoloPlay/Functions/ClassAttackOverrides/" + sdk.player.class.nameOf(me.classid) + "Attacks.js"); + global["__________ignoreMonster"] = []; + const sdk_1 = __importDefault(require("../../modules/sdk")); + const Events_1 = require("./Events"); + const Coords_1 = require("./Coords"); + const MissileData_1 = __importDefault(require("./GameData/MissileData")); + const defaults = { + range: 14, + spectype: 0, + once: false, + nodes: [], + callback: undefined, + filter: undefined //(monster: Monster, node: {x, y}[]) => true + }; + const shamans = [ + sdk_1.default.monsters.FallenShaman, sdk_1.default.monsters.CarverShaman2, sdk_1.default.monsters.DevilkinShaman2, sdk_1.default.monsters.DarkShaman1, + sdk_1.default.monsters.WarpedShaman, sdk_1.default.monsters.CarverShaman, sdk_1.default.monsters.DevilkinShaman, sdk_1.default.monsters.DarkShaman2 + ]; + const fallens = [ + sdk_1.default.monsters.Fallen, sdk_1.default.monsters.Carver2, sdk_1.default.monsters.Devilkin2, sdk_1.default.monsters.DarkOne1, + sdk_1.default.monsters.WarpedFallen, sdk_1.default.monsters.Carver1, sdk_1.default.monsters.Devilkin, sdk_1.default.monsters.DarkOne2 + ]; + const clearDistance = function (x, y, xx, yy) { + getUnits(sdk.unittype.Monster).forEach(function (monster) { + if (typeof monster["beendead"] === "undefined") + monster.beendead = false; + monster.beendead = monster.beendead || monster.dead; + }); + let path = getPath(me.area, x, y, xx, yy, 0, 4); + if (!path || !path.length) return Infinity; - return path.reduce(function (acc, v, i, arr) { - let prev = i ? arr[i - 1] : v; - return acc + Math.sqrt((prev.x - v.x) * (prev.x - v.x) + (prev.y - v.y) * (prev.y - v.y)); - }, 0); - }; - var exporting = (function (_settings) { - if (_settings === void 0) { _settings = {}; } - let settings = Object.assign({}, defaults, _settings); - // The bigger - let smallStepRange = settings.range / 3 * 2; - // Get an array with arrays going away from you (what we gonna walk after clearing, within range) - let nearestNode = settings.nodes[settings.nodes.index]; + return path.reduce(function (acc, v, i, arr) { + let prev = i ? arr[i - 1] : v; + return acc + Math.sqrt((prev.x - v.x) * (prev.x - v.x) + (prev.y - v.y) * (prev.y - v.y)); + }, 0); + }; + var exporting = (function (_settings) { + if (_settings === void 0) { _settings = {}; } + let settings = Object.assign({}, defaults, _settings); + // The bigger + let smallStepRange = settings.range / 3 * 2; + // Get an array with arrays going away from you (what we gonna walk after clearing, within range) + let nearestNode = settings.nodes[settings.nodes.index]; - const backTrack = function (units, missiles) { - if (settings.nodes.index < 2) return false; - //ToDo; backtrack further if that is a safer bet - let nodesBack = Math.min(settings.nodes.index, 5); - me.overhead("backtracking " + nodesBack + " nodes"); - settings.nodes.index -= nodesBack; - nearestNode = settings.nodes[settings.nodes.index]; - // stationary missiles that deal damages - let enhancedMissiles = missiles.map(function (m) { return ({ missile: m, data: MissileData_1.default[m.classid] }); }); - let missilesOnFloor = enhancedMissiles - .filter(function (m) { return !!m.data; }) - .filter(function (m) { return m.data.velocity === 0 && (m.data.minDamage > 0 || m.data.eMin > 0) && m.missile.hits(nearestNode); }); - while (missilesOnFloor.length > 0 && settings.nodes.index > 0) { - console.log("missilesOnFloor"); - console.log(missilesOnFloor); - nodesBack += 1; - settings.nodes.index -= 1; - nearestNode = settings.nodes[settings.nodes.index]; - missilesOnFloor = enhancedMissiles.filter(function (m) { return !!m.data; }) - .filter(function (m) { return m.data.velocity === 0 && (m.data.minDamage > 0 || m.data.eMin > 0) && m.missile.hits(nearestNode); }); - } + const backTrack = function (units, missiles) { + if (settings.nodes.index < 2) return false; + //ToDo; backtrack further if that is a safer bet + let nodesBack = Math.min(settings.nodes.index, 5); + me.overhead("backtracking " + nodesBack + " nodes"); + settings.nodes.index -= nodesBack; + nearestNode = settings.nodes[settings.nodes.index]; + // stationary missiles that deal damages + let enhancedMissiles = missiles.map(function (m) { return ({ missile: m, data: MissileData_1.default[m.classid] }); }); + let missilesOnFloor = enhancedMissiles + .filter(function (m) { return !!m.data; }) + .filter(function (m) { return m.data.velocity === 0 && (m.data.minDamage > 0 || m.data.eMin > 0) && m.missile.hits(nearestNode); }); + while (missilesOnFloor.length > 0 && settings.nodes.index > 0) { + console.log("missilesOnFloor"); + console.log(missilesOnFloor); + nodesBack += 1; + settings.nodes.index -= 1; + nearestNode = settings.nodes[settings.nodes.index]; + missilesOnFloor = enhancedMissiles.filter(function (m) { return !!m.data; }) + .filter(function (m) { return m.data.velocity === 0 && (m.data.minDamage > 0 || m.data.eMin > 0) && m.missile.hits(nearestNode); }); + } - let old = Pather.forceRun; - Pather.forceRun = true; - try { - let x = nearestNode.x, y = nearestNode.y; - // If the path between me and the node we wanna run back to is blocked dont do it - if (CollMap.checkColl(me, {x: x, y: y}, Coords_1.Collision.BLOCK_MISSILE, 3)) { - me.overhead("Before backtracking, clear near me"); - let unit = units.first(); - unit && ClassAttack.doAttack(unit); - settings.nodes.index += nodesBack; - return true; - } + let old = Pather.forceRun; + Pather.forceRun = true; + try { + let x = nearestNode.x, y = nearestNode.y; + // If the path between me and the node we wanna run back to is blocked dont do it + if (CollMap.checkColl(me, {x: x, y: y}, Coords_1.Collision.BLOCK_MISSILE, 3)) { + me.overhead("Before backtracking, clear near me"); + let unit = units.first(); + unit && ClassAttack.doAttack(unit); + settings.nodes.index += nodesBack; + return true; + } - me.overhead("backtracking " + nodesBack + " nodes"); - if (Pather.getWalkDistance(x, y) > getDistance(me, nearestNode) * 1.5) { - if (Pather.canTeleport() && getDistance(me, nearestNode) < 35) { - Pather.teleportTo(x, y); - } else { - Pather.moveToOverride(x, y); - } - } else { - Pather.walkTo(x, y); - } - start = [x, y]; - } finally { - Pather.forceRun = old; - } - return true; - }; + me.overhead("backtracking " + nodesBack + " nodes"); + if (Pather.getWalkDistance(x, y) > getDistance(me, nearestNode) * 1.5) { + if (Pather.canTeleport() && getDistance(me, nearestNode) < 35) { + Pather.teleportTo(x, y); + } else { + Pather.moveToOverride(x, y); + } + } else { + Pather.walkTo(x, y); + } + start = [x, y]; + } finally { + Pather.forceRun = old; + } + return true; + }; - let start = [], startArea = me.area, cachedNodes = undefined; - const getUnits_filtered = function () { - let monsters = getUnits(1, -1) - .filter(function (m) { return m.area === me.area && m.attackable && !global["__________ignoreMonster"].includes(m.gid); }) - .filter(function (unit) { return ( // Shamaans have a higher range - (function (range) { - return start.length // If start has a length - ? getDistance(start[0], start[1], unit) < range // If it has a range smaller as from the start point (when using me.clear) - : unit.distance < range; - } // if "me" move, the object doesnt move. So, check distance of object - )(shamans.includes(unit.classid) ? settings.range * 1.6 : settings.range) + let start = [], startArea = me.area, cachedNodes = undefined; + const getUnits_filtered = function () { + let monsters = getUnits(1, -1) + .filter(function (m) { return m.area === me.area && m.attackable && !global["__________ignoreMonster"].includes(m.gid); }) + .filter(function (unit) { return ( // Shamaans have a higher range + (function (range) { + return start.length // If start has a length + ? getDistance(start[0], start[1], unit) < range // If it has a range smaller as from the start point (when using me.clear) + : unit.distance < range; + } // if "me" move, the object doesnt move. So, check distance of object + )(shamans.includes(unit.classid) ? settings.range * 1.6 : settings.range) // clear monsters on the path || (( /* cache the nodes*/cachedNodes = cachedNodes || settings.nodes - .slice(settings.nodes.index, settings.nodes.index + 5) - .filter(function (el) { return el.distance < 30; })) - .some(function (node) { return getDistance(unit, node.x, node.y) < smallStepRange; }))) + .slice(settings.nodes.index, settings.nodes.index + 5) + .filter(function (el) { return el.distance < 30; })) + .some(function (node) { return getDistance(unit, node.x, node.y) < smallStepRange; }))) && !CollMap.checkColl(me, unit, Coords_1.Collision.BLOCK_MISSILE, 5); }) - .filter(function (unit) { - if (!settings.spectype || typeof settings.spectype !== "number") - return true; // No spectype = all monsters - // noinspection JSBitwiseOperatorUsage - return unit.spectype & settings.spectype; - }); - if (settings.filter) { - monsters = monsters.filter(settings.filter); - } - // too much monsters, quick sort - if (monsters.length > 7) { - return monsters.sort(function (a, b) { return a.distance - b.distance; }); - } - return monsters.sort(function (a, b) { - // shamans are a mess early game - var isShamanA = shamans.includes(a.classid); - var isFallenB = fallens.includes(b.classid); - if (isShamanA && isFallenB && !checkCollision(me, a, 0x7) /*line of sight*/) { - // return shaman first, if we have a direct line of sight - return -1; - } - if (typeof a["beendead"] !== "undefined" && typeof b["beendead"] === "undefined" && a["beendead"] && !b["beendead"]) { - return 1; // those that been dead before (aka fallens) will be moved up from the list, so we are more likely to pwn shamans on a safe moment - } - return clearDistance(me.x, me.y, a.x, a.y) - (clearDistance(me.x, me.y, b.x, b.y)); - }); - }; - // If we clear around _me_ we move around, but just clear around where we started - var units; - if (me === this) { - start = [me.x, me.y]; - } + .filter(function (unit) { + if (!settings.spectype || typeof settings.spectype !== "number") + return true; // No spectype = all monsters + // noinspection JSBitwiseOperatorUsage + return unit.spectype & settings.spectype; + }); + if (settings.filter) { + monsters = monsters.filter(settings.filter); + } + // too much monsters, quick sort + if (monsters.length > 7) { + return monsters.sort(function (a, b) { return a.distance - b.distance; }); + } + return monsters.sort(function (a, b) { + // shamans are a mess early game + var isShamanA = shamans.includes(a.classid); + var isFallenB = fallens.includes(b.classid); + if (isShamanA && isFallenB && !checkCollision(me, a, 0x7) /*line of sight*/) { + // return shaman first, if we have a direct line of sight + return -1; + } + if (typeof a["beendead"] !== "undefined" && typeof b["beendead"] === "undefined" && a["beendead"] && !b["beendead"]) { + return 1; // those that been dead before (aka fallens) will be moved up from the list, so we are more likely to pwn shamans on a safe moment + } + return clearDistance(me.x, me.y, a.x, a.y) - (clearDistance(me.x, me.y, b.x, b.y)); + }); + }; + // If we clear around _me_ we move around, but just clear around where we started + var units; + if (me === this) { + start = [me.x, me.y]; + } - var backtracked = false; - while ((units = getUnits_filtered()).length) { - exporting.emit("sorting", units); - // sorting algorithm can also take out monsters - if (!units.length) { - break; - } - // near monsters we can handle kinda depends on our health. - var maxNearMonsters = Math.floor((4 * (1 / me.hpmax * me.hp)) + 1); - if (!backtracked) { - var nearUnits = units.filter(function (unit) { return unit.attackable && unit.distance < 10; }); - var nearMissiles = getUnits(sdk_1.default.unittype.Missile) - .filter(function (unit) { var _a, _b, _c; return unit.distance < 10 && ((_a = unit.getParent()) === null || _a === void 0 ? void 0 : _a.gid) !== me.gid && ((_b = unit.getParent()) === null || _b === void 0 ? void 0 : _b.gid) !== ((_c = me.getMerc()) === null || _c === void 0 ? void 0 : _c.gid); }) - .filter(function (m) { return MissileData_1.default[m.classid] && (MissileData_1.default[m.classid].velocity > 0 || m.hits(me)) && (MissileData_1.default[m.classid].minDamage > 0 || MissileData_1.default[m.classid].eMin > 0); }); - me.overhead("backtrack counter (" + (nearUnits.length + nearMissiles.length) + "/" + maxNearMonsters + ")"); - if ((nearUnits.length + nearMissiles.length) >= maxNearMonsters && ((me.mp / me.mpmax) > 0.2 || me.getItemsEx().filter(function (i) { return (i.isInBelt || i.isInInventory) && i.itemType === sdk_1.default.itemtype.manapotion; }).length > 0)) { - me.overhead("Want to backtrack"); - if (backTrack(units, nearMissiles)) { - backtracked = true; - continue; // we maybe wanna attack someone else now - } - } - } - backtracked = false; - var unit = units.shift(); - if (settings.callback && settings.callback()) { - return; - } - // Do something with the effort to not kill monsters that are too harsh - var result = ClassAttack.doAttack(unit); - if (typeof unit.casts === "undefined") { - unit.casts = 0; - } - // cant attack this monsters, skip it - if (result === 2 || unit.casts++ > 30) { - console.log("Skip this monster"); - global["__________ignoreMonster"].push(unit.gid); - } - if (settings.once || startArea !== me.area) return true; - Pickit.pickItems(3, true); - } + var backtracked = false; + while ((units = getUnits_filtered()).length) { + exporting.emit("sorting", units); + // sorting algorithm can also take out monsters + if (!units.length) { + break; + } + // near monsters we can handle kinda depends on our health. + var maxNearMonsters = Math.floor((4 * (1 / me.hpmax * me.hp)) + 1); + if (!backtracked) { + var nearUnits = units.filter(function (unit) { return unit.attackable && unit.distance < 10; }); + var nearMissiles = getUnits(sdk_1.default.unittype.Missile) + .filter(function (unit) { var _a, _b, _c; return unit.distance < 10 && ((_a = unit.getParent()) === null || _a === void 0 ? void 0 : _a.gid) !== me.gid && ((_b = unit.getParent()) === null || _b === void 0 ? void 0 : _b.gid) !== ((_c = me.getMerc()) === null || _c === void 0 ? void 0 : _c.gid); }) + .filter(function (m) { return MissileData_1.default[m.classid] && (MissileData_1.default[m.classid].velocity > 0 || m.hits(me)) && (MissileData_1.default[m.classid].minDamage > 0 || MissileData_1.default[m.classid].eMin > 0); }); + me.overhead("backtrack counter (" + (nearUnits.length + nearMissiles.length) + "/" + maxNearMonsters + ")"); + if ((nearUnits.length + nearMissiles.length) >= maxNearMonsters && ((me.mp / me.mpmax) > 0.2 || me.getItemsEx().filter(function (i) { return (i.isInBelt || i.isInInventory) && i.itemType === sdk_1.default.itemtype.manapotion; }).length > 0)) { + me.overhead("Want to backtrack"); + if (backTrack(units, nearMissiles)) { + backtracked = true; + continue; // we maybe wanna attack someone else now + } + } + } + backtracked = false; + var unit = units.shift(); + if (settings.callback && settings.callback()) { + return; + } + // Do something with the effort to not kill monsters that are too harsh + var result = ClassAttack.doAttack(unit); + if (typeof unit.casts === "undefined") { + unit.casts = 0; + } + // cant attack this monsters, skip it + if (result === 2 || unit.casts++ > 30) { + console.log("Skip this monster"); + global["__________ignoreMonster"].push(unit.gid); + } + if (settings.once || startArea !== me.area) return true; + Pickit.pickItems(3, true); + } - return true; - }).bind(me); - Object.keys(Events_1.Events.prototype).forEach(function (key) { return exporting[key] = Events_1.Events.prototype[key]; }); - return exporting; + return true; + }).bind(me); + Object.keys(Events_1.Events.prototype).forEach(function (key) { return exporting[key] = Events_1.Events.prototype[key]; }); + return exporting; }); diff --git a/libs/SoloPlay/Modules/Coords.js b/libs/SoloPlay/Modules/Coords.js index 9dcaecb1..112f8e1c 100644 --- a/libs/SoloPlay/Modules/Coords.js +++ b/libs/SoloPlay/Modules/Coords.js @@ -1,204 +1,204 @@ /* eslint-disable dot-notation */ (function (factory) { - if (typeof module === "object" && typeof module.exports === "object") { - var v = factory(require, exports); - if (v !== undefined) module.exports = v; - } else if (typeof define === "function" && define.amd) { - define(["require", "exports", "../../modules/sdk"], factory); - } + if (typeof module === "object" && typeof module.exports === "object") { + var v = factory(require, exports); + if (v !== undefined) module.exports = v; + } else if (typeof define === "function" && define.amd) { + define(["require", "exports", "../../modules/sdk"], factory); + } })(function (require, exports) { - "use strict"; - Object.defineProperty(exports, "__esModule", { value: true }); - exports.getSpotsFor = exports.findCastingSpotRange = exports.findCastingSpotSkill = exports.isBlockedBetween = exports.getCollisionBetweenCoords = exports.convertToCoordArray = exports.getCoordsBetween = exports.Collision = exports.BlockBits = void 0; - const sdk_1 = require("../../modules/sdk"); - var BlockBits; - (function (BlockBits) { - BlockBits[BlockBits["BlockWall"] = 1] = "BlockWall"; - // Simply put, if the monster should be drawn - BlockBits[BlockBits["LineOfSight"] = 2] = "LineOfSight"; - // Its a bit weird but it seems if this is set if you go out of range to hit a monster - BlockBits[BlockBits["Ranged"] = 4] = "Ranged"; - // This naming comes from d2bs, but not sure if its accurate - BlockBits[BlockBits["PlayerToWalk"] = 8] = "PlayerToWalk"; - // This is some light setting, not usefull. Its mostly around doors and waypoints. NOTE: Also set in dungeon areas when monster is in another room - BlockBits[BlockBits["DarkArea"] = 16] = "DarkArea"; - // Is it a cast blocker? Like a stone or whatever. Not 100% accurate - BlockBits[BlockBits["Casting"] = 32] = "Casting"; - // Tell me if you see it! - BlockBits[BlockBits["Unknown_NeverSeen"] = 64] = "Unknown_NeverSeen"; - // These are always set if you check collision between you and a monster - BlockBits[BlockBits["Players"] = 128] = "Players"; - BlockBits[BlockBits["Monsters"] = 256] = "Monsters"; - BlockBits[BlockBits["Items"] = 512] = "Items"; - BlockBits[BlockBits["Objects"] = 1024] = "Objects"; - // Between me / spot is a door that is closed - BlockBits[BlockBits["ClosedDoor"] = 2048] = "ClosedDoor"; - // This one is odd, its nearly always set. But not for monsters that fly over lava for example - // Blizzard / meteor and prob other skills are only castable on spots with this set - BlockBits[BlockBits["IsOnFloor"] = 4096] = "IsOnFloor"; - // Flavie, merc. - BlockBits[BlockBits["FriendlyNPC"] = 8192] = "FriendlyNPC"; - BlockBits[BlockBits["Unknown_3"] = 16384] = "Unknown_3"; - BlockBits[BlockBits["DeadBodies"] = 32768] = "DeadBodies"; - })(BlockBits = exports.BlockBits || (exports.BlockBits = {})); - var Collision; - (function (Collision) { - // Collisions that cause a missile to burst - Collision[Collision["BLOCK_MISSILE"] = 2062] = "BLOCK_MISSILE"; - })(Collision = exports.Collision || (exports.Collision = {})); - function getCoordsBetween(x1, y1, x2, y2) { - const abs = Math.abs, min = Math.min, max = Math.max, floor = Math.floor; - const A = { x: x1, y: y1 }; - const B = { x: x2, y: y2 }; - if (max(x1, x2) - min(x1, x2) < max(y1, y2) - min(y1, y2)) { - // noinspection JSSuspiciousNameCombination - return getCoordsBetween(y1, x1, y2, x2).map(function (_a) { - let x = _a.x, y = _a.y; - return ({ x: y, y: x }); - }); - } - function slope(a, b) { - if (a.x === b.x) return null; - return (b.y - a.y) / (b.x - a.x); - } - function intercept(point, slope) { - // vertical line - if (slope === null) return point.x; - return point.y - slope * point.x; - } - const m = slope(A, B); - const b = intercept(A, m); - const coordinates = []; - for (let x = min(A.x, B.x); x <= max(A.x, B.x); x++) { - let y = m * x + b; - coordinates.push({ x: x, y: y }); - } - return coordinates.map(function (_a) { - let x = _a.x, y = _a.y; - return ({ x: floor(x), y: floor(y) }); - }) - .filter(function (el, idx, self) { return self.findIndex(function (other) { return other.x === el.x && other.y === el.y; }) === idx; }); - } - exports.getCoordsBetween = getCoordsBetween; - const convertToCoordArray = function (args, caller, length) { - if (length === void 0) { length = 2; } - var coords = []; - for (var i = 0; i < args.length; i++) { - if (typeof args[i] === "number" && i < args.length - 1) { - coords.push({ x: args[i], y: args[++i] }); - } else { - coords.push(args[i]); - } - } - if (coords.length !== length) throw TypeError("Didnt give proper arguments to " + caller); - return coords; - }; - exports.convertToCoordArray = convertToCoordArray; - function getCollisionBetweenCoords() { - var args = []; - for (var _i = 0; _i < arguments.length; _i++) { - args[_i] = arguments[_i]; - } - var _a = exports.convertToCoordArray(args, "getCollisionBetweenCoords", 2), one = _a[0], two = _a[1]; - if (getDistance(one, two) > 50) { - return -1; - } - try { - return getCoordsBetween(one.x, one.y, two.x, two.y) - .reduce(function (acc, cur) { - return (acc | 0) + "use strict"; + Object.defineProperty(exports, "__esModule", { value: true }); + exports.getSpotsFor = exports.findCastingSpotRange = exports.findCastingSpotSkill = exports.isBlockedBetween = exports.getCollisionBetweenCoords = exports.convertToCoordArray = exports.getCoordsBetween = exports.Collision = exports.BlockBits = void 0; + const sdk_1 = require("../../modules/sdk"); + var BlockBits; + (function (BlockBits) { + BlockBits[BlockBits["BlockWall"] = 1] = "BlockWall"; + // Simply put, if the monster should be drawn + BlockBits[BlockBits["LineOfSight"] = 2] = "LineOfSight"; + // Its a bit weird but it seems if this is set if you go out of range to hit a monster + BlockBits[BlockBits["Ranged"] = 4] = "Ranged"; + // This naming comes from d2bs, but not sure if its accurate + BlockBits[BlockBits["PlayerToWalk"] = 8] = "PlayerToWalk"; + // This is some light setting, not usefull. Its mostly around doors and waypoints. NOTE: Also set in dungeon areas when monster is in another room + BlockBits[BlockBits["DarkArea"] = 16] = "DarkArea"; + // Is it a cast blocker? Like a stone or whatever. Not 100% accurate + BlockBits[BlockBits["Casting"] = 32] = "Casting"; + // Tell me if you see it! + BlockBits[BlockBits["Unknown_NeverSeen"] = 64] = "Unknown_NeverSeen"; + // These are always set if you check collision between you and a monster + BlockBits[BlockBits["Players"] = 128] = "Players"; + BlockBits[BlockBits["Monsters"] = 256] = "Monsters"; + BlockBits[BlockBits["Items"] = 512] = "Items"; + BlockBits[BlockBits["Objects"] = 1024] = "Objects"; + // Between me / spot is a door that is closed + BlockBits[BlockBits["ClosedDoor"] = 2048] = "ClosedDoor"; + // This one is odd, its nearly always set. But not for monsters that fly over lava for example + // Blizzard / meteor and prob other skills are only castable on spots with this set + BlockBits[BlockBits["IsOnFloor"] = 4096] = "IsOnFloor"; + // Flavie, merc. + BlockBits[BlockBits["FriendlyNPC"] = 8192] = "FriendlyNPC"; + BlockBits[BlockBits["Unknown_3"] = 16384] = "Unknown_3"; + BlockBits[BlockBits["DeadBodies"] = 32768] = "DeadBodies"; + })(BlockBits = exports.BlockBits || (exports.BlockBits = {})); + var Collision; + (function (Collision) { + // Collisions that cause a missile to burst + Collision[Collision["BLOCK_MISSILE"] = 2062] = "BLOCK_MISSILE"; + })(Collision = exports.Collision || (exports.Collision = {})); + function getCoordsBetween(x1, y1, x2, y2) { + const abs = Math.abs, min = Math.min, max = Math.max, floor = Math.floor; + const A = { x: x1, y: y1 }; + const B = { x: x2, y: y2 }; + if (max(x1, x2) - min(x1, x2) < max(y1, y2) - min(y1, y2)) { + // noinspection JSSuspiciousNameCombination + return getCoordsBetween(y1, x1, y2, x2).map(function (_a) { + let x = _a.x, y = _a.y; + return ({ x: y, y: x }); + }); + } + function slope(a, b) { + if (a.x === b.x) return null; + return (b.y - a.y) / (b.x - a.x); + } + function intercept(point, slope) { + // vertical line + if (slope === null) return point.x; + return point.y - slope * point.x; + } + const m = slope(A, B); + const b = intercept(A, m); + const coordinates = []; + for (let x = min(A.x, B.x); x <= max(A.x, B.x); x++) { + let y = m * x + b; + coordinates.push({ x: x, y: y }); + } + return coordinates.map(function (_a) { + let x = _a.x, y = _a.y; + return ({ x: floor(x), y: floor(y) }); + }) + .filter(function (el, idx, self) { return self.findIndex(function (other) { return other.x === el.x && other.y === el.y; }) === idx; }); + } + exports.getCoordsBetween = getCoordsBetween; + const convertToCoordArray = function (args, caller, length) { + if (length === void 0) { length = 2; } + var coords = []; + for (var i = 0; i < args.length; i++) { + if (typeof args[i] === "number" && i < args.length - 1) { + coords.push({ x: args[i], y: args[++i] }); + } else { + coords.push(args[i]); + } + } + if (coords.length !== length) throw TypeError("Didnt give proper arguments to " + caller); + return coords; + }; + exports.convertToCoordArray = convertToCoordArray; + function getCollisionBetweenCoords() { + var args = []; + for (var _i = 0; _i < arguments.length; _i++) { + args[_i] = arguments[_i]; + } + var _a = exports.convertToCoordArray(args, "getCollisionBetweenCoords", 2), one = _a[0], two = _a[1]; + if (getDistance(one, two) > 50) { + return -1; + } + try { + return getCoordsBetween(one.x, one.y, two.x, two.y) + .reduce(function (acc, cur) { + return (acc | 0) // | (getCollision(me.area, cur.x+1, cur.y-1) | 0) // | (getCollision(me.area, cur.x+1, cur.y) | 0) // | (getCollision(me.area, cur.x+1, cur.y+1) | 0) // | (getCollision(me.area, cur.x, cur.y-1) | 0) | (getCollision(me.area, cur.x, cur.y) | 0); - }, 0); - } catch (e) { - return -1; // Area not loaded - } - } - exports.getCollisionBetweenCoords = getCollisionBetweenCoords; - function isBlockedBetween() { - var args = []; - for (var _i = 0; _i < arguments.length; _i++) { - args[_i] = arguments[_i]; - } - var collision = getCollisionBetweenCoords.apply(null, args); - return !!(collision & (0 + }, 0); + } catch (e) { + return -1; // Area not loaded + } + } + exports.getCollisionBetweenCoords = getCollisionBetweenCoords; + function isBlockedBetween() { + var args = []; + for (var _i = 0; _i < arguments.length; _i++) { + args[_i] = arguments[_i]; + } + var collision = getCollisionBetweenCoords.apply(null, args); + return !!(collision & (0 | BlockBits.LineOfSight | BlockBits.Ranged | BlockBits.Casting | BlockBits.ClosedDoor | BlockBits.DarkArea | BlockBits.Objects)); - } - exports.isBlockedBetween = isBlockedBetween; - function checkCollisionBetween(unit1, unit2, coll) { - let args = []; - args.push(unit1, unit2); - let collision = getCollisionBetweenCoords.apply(null, args); - return !!(collision & (0 | coll)); - } - exports.checkCollisionBetween = checkCollisionBetween; - Room.prototype.isInRoom = function () { - let args = []; - for (let _i = 0; _i < arguments.length; _i++) { - args[_i] = arguments[_i]; - } - let _a = exports.convertToCoordArray(args, "isInRoom", 1)[0], x = _a[0], y = _a[1]; - return this && x >= this.x * 5 && x < this.x * 5 + this.xsize && y >= this.y * 5 && y < this.y * 5 + this.ysize; - }; - function findCastingSpotSkill(skill, unit, minRange, thickness, collision) { - if (minRange === void 0) { minRange = 5; } - if (thickness === void 0) { thickness = 5; } - if (collision === void 0) { collision = Collision.BLOCK_MISSILE; } - let range = Skill.getRange(skill); - console.log("Searching range for", skill, Object.keys(sdk_1.skills).find(function (el) { return sdk_1.skills[el] === skill; }), range); - return findCastingSpotRange(range, unit, minRange, thickness, collision); - } - exports.findCastingSpotSkill = findCastingSpotSkill; - function findCastingSpotRange(range, unit, minRange, thickness, collision) { - if (minRange === void 0) { minRange = 5; } - if (thickness === void 0) { thickness = 5; } - if (collision === void 0) { collision = Collision.BLOCK_MISSILE; } - let spots = getSpotsFor(collision, thickness, unit) - .sort(function (a, b) { - if (CollMap.checkColl(a, me, BlockBits.BlockWall, 7)) return 1; - return getDistance(me, a) - getDistance(me, b); - }); - return spots.find(function (a) { - var dist = getDistance(unit.x, unit.y, a.x, a.y); - return dist < range && dist > minRange; - }); - } - exports.findCastingSpotRange = findCastingSpotRange; - var lines = []; - function getSpotsFor(collision, thickness, unit) { - var spots = []; - var fieldSize = 75; - for (var oX = -fieldSize; oX < fieldSize; oX++) { - for (var oY = -fieldSize; oY < fieldSize; oY++) { - var _a = [unit.x + oX, unit.y + oY], x = _a[0], y = _a[1]; - if (getDistance(unit.x, unit.y, x, y) > 40) continue; - var isCol = !!(getCollision(unit.area, x, y) & collision); - for (var i = -2; i < 2 && !isCol; i++) { - for (var j = -2; j < 2 && !isCol; j++) { - isCol = isCol && !!(getCollision(unit.area, x + i, y + j) & collision); - } - } - // if it isnt a collision to start with - if (!isCol) { - spots.push({ x: x, y: y }); - } - } - } - spots = spots.filter(function (el) { return !CollMap.checkColl(el, unit, collision, thickness); }); - //lines.splice(0, lines.length); - /*spots.map(function (_a) { + } + exports.isBlockedBetween = isBlockedBetween; + function checkCollisionBetween(unit1, unit2, coll) { + let args = []; + args.push(unit1, unit2); + let collision = getCollisionBetweenCoords.apply(null, args); + return !!(collision & (0 | coll)); + } + exports.checkCollisionBetween = checkCollisionBetween; + Room.prototype.isInRoom = function () { + let args = []; + for (let _i = 0; _i < arguments.length; _i++) { + args[_i] = arguments[_i]; + } + let _a = exports.convertToCoordArray(args, "isInRoom", 1)[0], x = _a[0], y = _a[1]; + return this && x >= this.x * 5 && x < this.x * 5 + this.xsize && y >= this.y * 5 && y < this.y * 5 + this.ysize; + }; + function findCastingSpotSkill(skill, unit, minRange, thickness, collision) { + if (minRange === void 0) { minRange = 5; } + if (thickness === void 0) { thickness = 5; } + if (collision === void 0) { collision = Collision.BLOCK_MISSILE; } + let range = Skill.getRange(skill); + console.log("Searching range for", skill, Object.keys(sdk_1.skills).find(function (el) { return sdk_1.skills[el] === skill; }), range); + return findCastingSpotRange(range, unit, minRange, thickness, collision); + } + exports.findCastingSpotSkill = findCastingSpotSkill; + function findCastingSpotRange(range, unit, minRange, thickness, collision) { + if (minRange === void 0) { minRange = 5; } + if (thickness === void 0) { thickness = 5; } + if (collision === void 0) { collision = Collision.BLOCK_MISSILE; } + let spots = getSpotsFor(collision, thickness, unit) + .sort(function (a, b) { + if (CollMap.checkColl(a, me, BlockBits.BlockWall, 7)) return 1; + return getDistance(me, a) - getDistance(me, b); + }); + return spots.find(function (a) { + var dist = getDistance(unit.x, unit.y, a.x, a.y); + return dist < range && dist > minRange; + }); + } + exports.findCastingSpotRange = findCastingSpotRange; + var lines = []; + function getSpotsFor(collision, thickness, unit) { + var spots = []; + var fieldSize = 75; + for (var oX = -fieldSize; oX < fieldSize; oX++) { + for (var oY = -fieldSize; oY < fieldSize; oY++) { + var _a = [unit.x + oX, unit.y + oY], x = _a[0], y = _a[1]; + if (getDistance(unit.x, unit.y, x, y) > 40) continue; + var isCol = !!(getCollision(unit.area, x, y) & collision); + for (var i = -2; i < 2 && !isCol; i++) { + for (var j = -2; j < 2 && !isCol; j++) { + isCol = isCol && !!(getCollision(unit.area, x + i, y + j) & collision); + } + } + // if it isnt a collision to start with + if (!isCol) { + spots.push({ x: x, y: y }); + } + } + } + spots = spots.filter(function (el) { return !CollMap.checkColl(el, unit, collision, thickness); }); + //lines.splice(0, lines.length); + /*spots.map(function (_a) { var x = _a.x, y = _a.y; return lines.push(new Line(x + 1, y + 1, x, y, 0x70, true)); });*/ - return spots; - } - exports.getSpotsFor = getSpotsFor; + return spots; + } + exports.getSpotsFor = getSpotsFor; }); diff --git a/libs/SoloPlay/Modules/Events.js b/libs/SoloPlay/Modules/Events.js index 9297de5e..ed3f0084 100644 --- a/libs/SoloPlay/Modules/Events.js +++ b/libs/SoloPlay/Modules/Events.js @@ -6,95 +6,95 @@ */ (function (factory) { - if (typeof module === "object" && typeof module.exports === "object") { - let v = factory(require, exports); - if (v !== undefined) module.exports = v; - } else if (typeof define === "function" && define.amd) { - define(["require", "exports"], factory); - } + if (typeof module === "object" && typeof module.exports === "object") { + let v = factory(require, exports); + if (v !== undefined) module.exports = v; + } else if (typeof define === "function" && define.amd) { + define(["require", "exports"], factory); + } })(function (require, exports) { - "use strict"; - Object.defineProperty(exports, "__esModule", { value: true }); - exports.Events = void 0; - let handlers = new WeakMap(); - let onceHandlers = new WeakMap(); - const __spreadArray = (this && this.__spreadArray) || function (to, from) { - for (let i = 0, il = from.length, j = to.length; i < il; i++, j++) { - to[j] = from[i]; - } - return to; - }; - // eslint-disable-next-line no-var - var Events = /** @class */ (function () { - function Events() { - } - // Generic type S to give to EventHandler to typehint this function gets the same this as where the event is registered - Events.prototype.on = function (key, handler, handlerType) { - if (handlerType === void 0) { - handlerType = handlers; - } + "use strict"; + Object.defineProperty(exports, "__esModule", { value: true }); + exports.Events = void 0; + let handlers = new WeakMap(); + let onceHandlers = new WeakMap(); + const __spreadArray = (this && this.__spreadArray) || function (to, from) { + for (let i = 0, il = from.length, j = to.length; i < il; i++, j++) { + to[j] = from[i]; + } + return to; + }; + // eslint-disable-next-line no-var + var Events = /** @class */ (function () { + function Events() { + } + // Generic type S to give to EventHandler to typehint this function gets the same this as where the event is registered + Events.prototype.on = function (key, handler, handlerType) { + if (handlerType === void 0) { + handlerType = handlers; + } - let map, set; + let map, set; - !handlerType.has(this) - ? handlerType.set(this, map = new Map) - : map = handlerType.get(this); - !map.has(key) - ? map.set(key, set = []) - : set = map.get(key); - // Add this handler, since it has to be unique we dont need to check if it exists - set.push(handler); - // console.debug(set, map); - // console.trace(); - return this; - }; - Events.prototype.once = function (key, handler) { - return this.on(key, handler, onceHandlers); - }; - Events.prototype.off = function (key, handler) { - let _this = this; - [handlers, onceHandlers].forEach(function (handlerType) { - let map, set, index; - !handlerType.has(_this) - ? handlerType.set(_this, map = new Map) - : map = handlerType.get(_this); - !map.has(key) - ? map.set(key, set = []) - : set = map.get(key); - index = set.indexOf(handler); - if (index > -1) { - set.splice(index, 1); - } - }); - return this; - }; - Events.prototype.emit = function (key) { - let _this = this; - let _a, _b; - let args = []; - for (let _i = 1; _i < arguments.length; _i++) { - args[_i - 1] = arguments[_i]; - } - let onceSet = ((_a = onceHandlers.get(this)) === null || _a === void 0 ? void 0 : _a.get(key)); - let restSet = ((_b = handlers.get(this)) === null || _b === void 0 ? void 0 : _b.get(key)); - // store callbacks in a set to avoid duplicate handlers - let callbacks = __spreadArray(__spreadArray([], (onceSet && onceSet.splice(0, onceSet.length) || [])), restSet ? restSet : []); - callbacks.forEach(function (el) { - return el.apply(_this, args); - }); - return this; - }; - return Events; - }()); + !handlerType.has(this) + ? handlerType.set(this, map = new Map) + : map = handlerType.get(this); + !map.has(key) + ? map.set(key, set = []) + : set = map.get(key); + // Add this handler, since it has to be unique we dont need to check if it exists + set.push(handler); + // console.debug(set, map); + // console.trace(); + return this; + }; + Events.prototype.once = function (key, handler) { + return this.on(key, handler, onceHandlers); + }; + Events.prototype.off = function (key, handler) { + let _this = this; + [handlers, onceHandlers].forEach(function (handlerType) { + let map, set, index; + !handlerType.has(_this) + ? handlerType.set(_this, map = new Map) + : map = handlerType.get(_this); + !map.has(key) + ? map.set(key, set = []) + : set = map.get(key); + index = set.indexOf(handler); + if (index > -1) { + set.splice(index, 1); + } + }); + return this; + }; + Events.prototype.emit = function (key) { + let _this = this; + let _a, _b; + let args = []; + for (let _i = 1; _i < arguments.length; _i++) { + args[_i - 1] = arguments[_i]; + } + let onceSet = ((_a = onceHandlers.get(this)) === null || _a === void 0 ? void 0 : _a.get(key)); + let restSet = ((_b = handlers.get(this)) === null || _b === void 0 ? void 0 : _b.get(key)); + // store callbacks in a set to avoid duplicate handlers + let callbacks = __spreadArray(__spreadArray([], (onceSet && onceSet.splice(0, onceSet.length) || [])), restSet ? restSet : []); + callbacks.forEach(function (el) { + return el.apply(_this, args); + }); + return this; + }; + return Events; + }()); - // @ts-ignore - Unit.prototype.on = Events.prototype.on; - // @ts-ignore - Unit.prototype.off = Events.prototype.off; - // @ts-ignore - Unit.prototype.once = Events.prototype.once; - // @ts-ignore - Unit.prototype.emit = Events.prototype.emit; + // @ts-ignore + Unit.prototype.on = Events.prototype.on; + // @ts-ignore + Unit.prototype.off = Events.prototype.off; + // @ts-ignore + Unit.prototype.once = Events.prototype.once; + // @ts-ignore + Unit.prototype.emit = Events.prototype.emit; - exports.Events = Events; + exports.Events = Events; }); diff --git a/libs/SoloPlay/Modules/GameData/AreaData.js b/libs/SoloPlay/Modules/GameData/AreaData.js index edf5e582..e0ef7053 100644 --- a/libs/SoloPlay/Modules/GameData/AreaData.js +++ b/libs/SoloPlay/Modules/GameData/AreaData.js @@ -4,202 +4,202 @@ * @param {function} require */ (function (module, require) { - const MonsterData = require("./MonsterData"); - const SUPER = [0, 0, 0, 1, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 1, 0, 0, 1, 0, 0, 0, 1, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 1, 1, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 0, 0, 0, 3, 0, 1, 0, 0, 0, 0, 0, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 1, 0, 1, 4, 0, 2, 3, 1, 0, 1, 1, 0, 0, 0, 1, 3, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 5, 1, 1, 1, 1, 3]; - const AREA_LOCALE_STRING = [5389, 5055, 5054, 5053, 5052, 5051, 5050, 5049, 5048, 5047, 5046, 5045, 5044, 5043, 5042, 5041, 5040, 5039, 5038, 5037, 5036, 5035, 5034, 5033, 5032, 5031, 5030, 5029, 5028, 5027, 5026, 5025, 5024, 5023, 5022, 5021, 5020, 5019, 5018, 788, 852, 851, 850, 849, 848, 847, 846, 845, 844, 843, 842, 841, 840, 839, 838, 837, 836, 835, 834, 833, 832, 831, 830, 829, 828, 827, 826, 826, 826, 826, 826, 826, 826, 825, 824, 820, 819, 818, 817, 816, 815, 814, 813, 812, 810, 811, 809, 808, 806, 805, 807, 804, 845, 844, 803, 802, 801, 800, 799, 798, 797, 796, 795, 790, 792, 793, 794, 791, 789, 22646, 22647, 22648, 22649, 22650, 22651, 22652, 22653, 22654, 22655, 22656, 22657, 22658, 22659, 22660, 22662, 21865, 21866, 21867, 22663, 22664, 22665, 22667, 22666, 5389, 5389, 5389, 5018]; - const MONSTER_KEYS = [ - ["mon1", "mon2", "mon3", "mon4", "mon5", "mon6", "mon7", "mon8", "mon9", "mon10"], - ["nmon1", "nmon2", "nmon3", "nmon4", "nmon5", "nmon6", "nmon7", "nmon8", "nmon9", "nmon10"], - ][me.diff && 1]; // mon is for normal, nmon is for nm/hell, umon is specific to picking champion/uniques in normal - const LocaleStringName = require("./LocaleStringID").LocaleStringName; - const AREA_INDEX_COUNT = 137; - - /** - * @typedef AreaDataObj - * @type {object} - * @property {number} Super = number of super uniques present in this area - * @property {number} Index = areaID - * @property {number} Act = act this area is in [0-4] - * @property {number} MonsterDensity = value used to determine monster population density - * @property {number} ChampionPacks.Min = minimum number of champion or unique packs that spawn here - * @property {number} ChampionPacks.Max = maximum number of champion or unique packs that spawn here - * @property {number} Waypoint = number in waypoint menu that leads to this area - * @property {number} Level = level of area (use GameData.areaLevel) - * @property {number} Size.x = width of area - * @property {number} Size.y = depth of area - * @property {number} Monsters = array of monsters that can spawn in this area - * @property {number} LocaleString = locale string index for getLocaleString - */ - - /** @type {AreaDataObj[]} */ - const AreaData = new Array(AREA_INDEX_COUNT); - - for (let i = 0; i < AreaData.length; i++) { - let index = i; - AreaData[i] = ({ - Super: SUPER[index], - Index: index, - Act: getBaseStat("levels", index, "Act"), - MonsterDensity: getBaseStat("levels", index, ["MonDen", "MonDen(N)", "MonDen(H)"][me.diff]), - ChampionPacks: ({ - Min: getBaseStat("levels", index, ["MonUMin", "MonUMin(N)", "MonUMin(H)"][me.diff]), - Max: getBaseStat("levels", index, ["MonUMax", "MonUMax(N)", "MonUMax(H)"][me.diff]) - }), - Waypoint: getBaseStat("levels", index, "Waypoint"), - Level: getBaseStat("levels", index, ["MonLvl1Ex", "MonLvl2Ex", "MonLvl3Ex"][me.diff]), - Size: (() => { - if (index === 111) { // frigid highlands doesn't specify size, manual measurement - return { x: 210, y: 710 }; - } - - if (index === 112) { // arreat plateau doesn't specify size, manual measurement - return { x: 690, y: 230 }; - } - - return { - x: getBaseStat("leveldefs", index, ["SizeX", "SizeX(N)", "SizeX(H)"][me.diff]), - y: getBaseStat("leveldefs", index, ["SizeY", "SizeY(N)", "SizeY(H)"][me.diff]) - }; - })(), - Monsters: (MONSTER_KEYS.map(key => getBaseStat("levels", index, key)).filter(key => key !== 65535)), - /** - * Check if this area has a monster of a certain type - * @function - * @param {number} type - monster type to check for - * @returns {boolean} - */ - hasMonsterType: function (type) { - return this.Monsters.some(monId => MonsterData[monId].Type === type); - }, - /** - * Iterate through each monster in this area and apply a callback function - * @function - * @param {function} cb - callback function to apply to each monster - */ - forEachMonster: function (cb) { - if (typeof cb === "function") { - this.Monsters.forEach(monID => { - cb(MonsterData[monID], MonsterData[monID].Rarity * (MonsterData[monID].GroupCount.Min + MonsterData[monID].GroupCount.Max) / 2); - }); - } - }, - /** - * Iterate through each monster and minion in this area and apply a callback function - * @function - * @param {function} cb - callback function to apply to each monster - */ - forEachMonsterAndMinion: function (cb) { - if (typeof cb === "function") { - this.Monsters.forEach(monID => { - let rarity = MonsterData[monID].Rarity * (MonsterData[monID].GroupCount.Min + MonsterData[monID].GroupCount.Max) / 2; - cb(MonsterData[monID], rarity, null); - MonsterData[monID].Minions.forEach(minionID => { - let minionrarity = MonsterData[monID].Rarity * (MonsterData[monID].MinionCount.Min + MonsterData[monID].MinionCount.Max) / 2 / MonsterData[monID].Minions.length; - cb(MonsterData[minionID], minionrarity, MonsterData[monID]); - }); - }); - } - }, - LocaleString: getLocaleString(AREA_LOCALE_STRING[index]), - InternalName: LocaleStringName[AREA_LOCALE_STRING[index]], - /** - * Check if area is a town area - * @function - */ - townArea: function () { - return AreaData[[sdk.areas.RogueEncampment, sdk.areas.LutGholein, sdk.areas.KurastDocktown, sdk.areas.PandemoniumFortress, sdk.areas.Harrogath][this.Act]]; - }, - /** - * @function - */ - haveWaypoint: function () { - // get the last area that got a WP - let wpArea = this.nearestWaypointArea(); - - // If you dont need a wp, we want at least the town's wp - return getWaypoint(Pather.wpAreas.indexOf(wpArea || this.townArea().Index)); - }, - /** - * Find nearest waypoint in area - * @function - */ - nearestWaypointArea: function () { - // plot toward this are - const plot = Pather.plotCourse(this.Index, this.townArea().Index); - - // get the last area that got a WP - return plot.course.filter(el => Pather.wpAreas.indexOf(el) > -1).last(); - }, - /** - * @function - * @return {PresetUnit|undefined} - */ - waypointPreset: function () { - const wpIDs = [119, 145, 156, 157, 237, 238, 288, 323, 324, 398, 402, 429, 494, 496, 511, 539]; - for (let i = 0, preset, wpArea = this.nearestWaypointArea(); i < wpIDs.length || preset; i++) { - if ((preset = Game.getPresetObject(wpArea, wpIDs[i]))) { - return preset; - } - } - - return undefined; - }, - }); - } - - /** - * @property {function} AreaData.findByName - * @param {string} whatToFind - * @returns - */ - AreaData.findByName = function (whatToFind) { - let matches = AreaData.map(area => [Math.min(whatToFind.diffCount(area.LocaleString), whatToFind.diffCount(area.InternalName)), area]).sort((a, b) => a[0] - b[0]); - - return matches[0][1]; - }; - - AreaData.dungeons = { - DenOfEvil: [sdk.areas.DenofEvil], - - Hole: [sdk.areas.HoleLvl1, sdk.areas.HoleLvl2, ], - - Pit: [sdk.areas.PitLvl1, sdk.areas.PitLvl2], - - Cave: [sdk.areas.CaveLvl1, sdk.areas.CaveLvl2], - - UndergroundPassage: [sdk.areas.UndergroundPassageLvl1, sdk.areas.UndergroundPassageLvl2, ], - - Cellar: [sdk.areas.TowerCellarLvl1, sdk.areas.TowerCellarLvl2, sdk.areas.TowerCellarLvl3, sdk.areas.TowerCellarLvl4, sdk.areas.TowerCellarLvl5, ], - - // act 2 - A2Sewers: [sdk.areas.A2SewersLvl1, sdk.areas.A2SewersLvl2, sdk.areas.A2SewersLvl3, ], - - StonyTomb: [sdk.areas.StonyTombLvl1, sdk.areas.StonyTombLvl2, ], - - HallsOfDead: [sdk.areas.HallsoftheDeadLvl1, sdk.areas.HallsoftheDeadLvl2, sdk.areas.HallsoftheDeadLvl3, ], - - ClawViperTemple: [sdk.areas.ClawViperTempleLvl1, sdk.areas.ClawViperTempleLvl2, ], - - MaggotLair: [sdk.areas.MaggotLairLvl1, sdk.areas.MaggotLairLvl2, sdk.areas.MaggotLairLvl3, ], - - Tombs: [sdk.areas.TalRashasTomb1, sdk.areas.TalRashasTomb2, sdk.areas.TalRashasTomb3, sdk.areas.TalRashasTomb4, sdk.areas.TalRashasTomb5, sdk.areas.TalRashasTomb6, sdk.areas.TalRashasTomb7, ], - - // act 3 - Swamp: [sdk.areas.SwampyPitLvl1, sdk.areas.SwampyPitLvl2, sdk.areas.SwampyPitLvl3, ], - - FlayerDungeon: [sdk.areas.FlayerDungeonLvl1, sdk.areas.FlayerDungeonLvl2, sdk.areas.FlayerDungeonLvl3, ], - - A3Sewers: [sdk.areas.A3SewersLvl1, sdk.areas.A3SewersLvl2, ], - - HighLevelForgottenTemples: [sdk.areas.ForgottenTemple, sdk.areas.RuinedFane, sdk.areas.DisusedReliquary], - - LowLevelForgottenTemples: [sdk.areas.RuinedTemple, sdk.areas.DisusedFane, sdk.areas.ForgottenReliquary], - - // act 4 has no areas like that - - // act 5 - RedPortalPits: [sdk.areas.Abaddon, sdk.areas.PitofAcheron, sdk.areas.InfernalPit, ], - }; + const MonsterData = require("./MonsterData"); + const SUPER = [0, 0, 0, 1, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 1, 0, 0, 1, 0, 0, 0, 1, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 1, 1, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 0, 0, 0, 3, 0, 1, 0, 0, 0, 0, 0, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 1, 0, 1, 4, 0, 2, 3, 1, 0, 1, 1, 0, 0, 0, 1, 3, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 5, 1, 1, 1, 1, 3]; + const AREA_LOCALE_STRING = [5389, 5055, 5054, 5053, 5052, 5051, 5050, 5049, 5048, 5047, 5046, 5045, 5044, 5043, 5042, 5041, 5040, 5039, 5038, 5037, 5036, 5035, 5034, 5033, 5032, 5031, 5030, 5029, 5028, 5027, 5026, 5025, 5024, 5023, 5022, 5021, 5020, 5019, 5018, 788, 852, 851, 850, 849, 848, 847, 846, 845, 844, 843, 842, 841, 840, 839, 838, 837, 836, 835, 834, 833, 832, 831, 830, 829, 828, 827, 826, 826, 826, 826, 826, 826, 826, 825, 824, 820, 819, 818, 817, 816, 815, 814, 813, 812, 810, 811, 809, 808, 806, 805, 807, 804, 845, 844, 803, 802, 801, 800, 799, 798, 797, 796, 795, 790, 792, 793, 794, 791, 789, 22646, 22647, 22648, 22649, 22650, 22651, 22652, 22653, 22654, 22655, 22656, 22657, 22658, 22659, 22660, 22662, 21865, 21866, 21867, 22663, 22664, 22665, 22667, 22666, 5389, 5389, 5389, 5018]; + const MONSTER_KEYS = [ + ["mon1", "mon2", "mon3", "mon4", "mon5", "mon6", "mon7", "mon8", "mon9", "mon10"], + ["nmon1", "nmon2", "nmon3", "nmon4", "nmon5", "nmon6", "nmon7", "nmon8", "nmon9", "nmon10"], + ][me.diff && 1]; // mon is for normal, nmon is for nm/hell, umon is specific to picking champion/uniques in normal + const LocaleStringName = require("./LocaleStringID").LocaleStringName; + const AREA_INDEX_COUNT = 137; + + /** + * @typedef AreaDataObj + * @type {object} + * @property {number} Super = number of super uniques present in this area + * @property {number} Index = areaID + * @property {number} Act = act this area is in [0-4] + * @property {number} MonsterDensity = value used to determine monster population density + * @property {number} ChampionPacks.Min = minimum number of champion or unique packs that spawn here + * @property {number} ChampionPacks.Max = maximum number of champion or unique packs that spawn here + * @property {number} Waypoint = number in waypoint menu that leads to this area + * @property {number} Level = level of area (use GameData.areaLevel) + * @property {number} Size.x = width of area + * @property {number} Size.y = depth of area + * @property {number} Monsters = array of monsters that can spawn in this area + * @property {number} LocaleString = locale string index for getLocaleString + */ + + /** @type {AreaDataObj[]} */ + const AreaData = new Array(AREA_INDEX_COUNT); + + for (let i = 0; i < AreaData.length; i++) { + let index = i; + AreaData[i] = ({ + Super: SUPER[index], + Index: index, + Act: getBaseStat("levels", index, "Act"), + MonsterDensity: getBaseStat("levels", index, ["MonDen", "MonDen(N)", "MonDen(H)"][me.diff]), + ChampionPacks: ({ + Min: getBaseStat("levels", index, ["MonUMin", "MonUMin(N)", "MonUMin(H)"][me.diff]), + Max: getBaseStat("levels", index, ["MonUMax", "MonUMax(N)", "MonUMax(H)"][me.diff]) + }), + Waypoint: getBaseStat("levels", index, "Waypoint"), + Level: getBaseStat("levels", index, ["MonLvl1Ex", "MonLvl2Ex", "MonLvl3Ex"][me.diff]), + Size: (() => { + if (index === 111) { // frigid highlands doesn't specify size, manual measurement + return { x: 210, y: 710 }; + } + + if (index === 112) { // arreat plateau doesn't specify size, manual measurement + return { x: 690, y: 230 }; + } + + return { + x: getBaseStat("leveldefs", index, ["SizeX", "SizeX(N)", "SizeX(H)"][me.diff]), + y: getBaseStat("leveldefs", index, ["SizeY", "SizeY(N)", "SizeY(H)"][me.diff]) + }; + })(), + Monsters: (MONSTER_KEYS.map(key => getBaseStat("levels", index, key)).filter(key => key !== 65535)), + /** + * Check if this area has a monster of a certain type + * @function + * @param {number} type - monster type to check for + * @returns {boolean} + */ + hasMonsterType: function (type) { + return this.Monsters.some(monId => MonsterData[monId].Type === type); + }, + /** + * Iterate through each monster in this area and apply a callback function + * @function + * @param {function} cb - callback function to apply to each monster + */ + forEachMonster: function (cb) { + if (typeof cb === "function") { + this.Monsters.forEach(monID => { + cb(MonsterData[monID], MonsterData[monID].Rarity * (MonsterData[monID].GroupCount.Min + MonsterData[monID].GroupCount.Max) / 2); + }); + } + }, + /** + * Iterate through each monster and minion in this area and apply a callback function + * @function + * @param {function} cb - callback function to apply to each monster + */ + forEachMonsterAndMinion: function (cb) { + if (typeof cb === "function") { + this.Monsters.forEach(monID => { + let rarity = MonsterData[monID].Rarity * (MonsterData[monID].GroupCount.Min + MonsterData[monID].GroupCount.Max) / 2; + cb(MonsterData[monID], rarity, null); + MonsterData[monID].Minions.forEach(minionID => { + let minionrarity = MonsterData[monID].Rarity * (MonsterData[monID].MinionCount.Min + MonsterData[monID].MinionCount.Max) / 2 / MonsterData[monID].Minions.length; + cb(MonsterData[minionID], minionrarity, MonsterData[monID]); + }); + }); + } + }, + LocaleString: getLocaleString(AREA_LOCALE_STRING[index]), + InternalName: LocaleStringName[AREA_LOCALE_STRING[index]], + /** + * Check if area is a town area + * @function + */ + townArea: function () { + return AreaData[[sdk.areas.RogueEncampment, sdk.areas.LutGholein, sdk.areas.KurastDocktown, sdk.areas.PandemoniumFortress, sdk.areas.Harrogath][this.Act]]; + }, + /** + * @function + */ + haveWaypoint: function () { + // get the last area that got a WP + let wpArea = this.nearestWaypointArea(); + + // If you dont need a wp, we want at least the town's wp + return getWaypoint(Pather.wpAreas.indexOf(wpArea || this.townArea().Index)); + }, + /** + * Find nearest waypoint in area + * @function + */ + nearestWaypointArea: function () { + // plot toward this are + const plot = Pather.plotCourse(this.Index, this.townArea().Index); + + // get the last area that got a WP + return plot.course.filter(el => Pather.wpAreas.indexOf(el) > -1).last(); + }, + /** + * @function + * @return {PresetUnit|undefined} + */ + waypointPreset: function () { + const wpIDs = [119, 145, 156, 157, 237, 238, 288, 323, 324, 398, 402, 429, 494, 496, 511, 539]; + for (let i = 0, preset, wpArea = this.nearestWaypointArea(); i < wpIDs.length || preset; i++) { + if ((preset = Game.getPresetObject(wpArea, wpIDs[i]))) { + return preset; + } + } + + return undefined; + }, + }); + } + + /** + * @property {function} AreaData.findByName + * @param {string} whatToFind + * @returns + */ + AreaData.findByName = function (whatToFind) { + let matches = AreaData.map(area => [Math.min(whatToFind.diffCount(area.LocaleString), whatToFind.diffCount(area.InternalName)), area]).sort((a, b) => a[0] - b[0]); + + return matches[0][1]; + }; + + AreaData.dungeons = { + DenOfEvil: [sdk.areas.DenofEvil], + + Hole: [sdk.areas.HoleLvl1, sdk.areas.HoleLvl2, ], + + Pit: [sdk.areas.PitLvl1, sdk.areas.PitLvl2], + + Cave: [sdk.areas.CaveLvl1, sdk.areas.CaveLvl2], + + UndergroundPassage: [sdk.areas.UndergroundPassageLvl1, sdk.areas.UndergroundPassageLvl2, ], + + Cellar: [sdk.areas.TowerCellarLvl1, sdk.areas.TowerCellarLvl2, sdk.areas.TowerCellarLvl3, sdk.areas.TowerCellarLvl4, sdk.areas.TowerCellarLvl5, ], + + // act 2 + A2Sewers: [sdk.areas.A2SewersLvl1, sdk.areas.A2SewersLvl2, sdk.areas.A2SewersLvl3, ], + + StonyTomb: [sdk.areas.StonyTombLvl1, sdk.areas.StonyTombLvl2, ], + + HallsOfDead: [sdk.areas.HallsoftheDeadLvl1, sdk.areas.HallsoftheDeadLvl2, sdk.areas.HallsoftheDeadLvl3, ], + + ClawViperTemple: [sdk.areas.ClawViperTempleLvl1, sdk.areas.ClawViperTempleLvl2, ], + + MaggotLair: [sdk.areas.MaggotLairLvl1, sdk.areas.MaggotLairLvl2, sdk.areas.MaggotLairLvl3, ], + + Tombs: [sdk.areas.TalRashasTomb1, sdk.areas.TalRashasTomb2, sdk.areas.TalRashasTomb3, sdk.areas.TalRashasTomb4, sdk.areas.TalRashasTomb5, sdk.areas.TalRashasTomb6, sdk.areas.TalRashasTomb7, ], + + // act 3 + Swamp: [sdk.areas.SwampyPitLvl1, sdk.areas.SwampyPitLvl2, sdk.areas.SwampyPitLvl3, ], + + FlayerDungeon: [sdk.areas.FlayerDungeonLvl1, sdk.areas.FlayerDungeonLvl2, sdk.areas.FlayerDungeonLvl3, ], + + A3Sewers: [sdk.areas.A3SewersLvl1, sdk.areas.A3SewersLvl2, ], + + HighLevelForgottenTemples: [sdk.areas.ForgottenTemple, sdk.areas.RuinedFane, sdk.areas.DisusedReliquary], + + LowLevelForgottenTemples: [sdk.areas.RuinedTemple, sdk.areas.DisusedFane, sdk.areas.ForgottenReliquary], + + // act 4 has no areas like that + + // act 5 + RedPortalPits: [sdk.areas.Abaddon, sdk.areas.PitofAcheron, sdk.areas.InfernalPit, ], + }; - module.exports = AreaData; + module.exports = AreaData; })(module, require); diff --git a/libs/SoloPlay/Modules/GameData/GameData.js b/libs/SoloPlay/Modules/GameData/GameData.js index 0de3daab..860ed0f1 100644 --- a/libs/SoloPlay/Modules/GameData/GameData.js +++ b/libs/SoloPlay/Modules/GameData/GameData.js @@ -8,2023 +8,2023 @@ */ // todo - remove the magic numbers here (function (module, require) { - const MonsterData = require("./MonsterData"); - const AreaData = require("./AreaData"); - const MissileData = require("./MissileData"); - const Coords_1 = require("../Coords"); - const sdk = require("../../../modules/sdk"); - - function isAlive(unit) { - return Boolean(unit && unit.hp); - } - - function isEnemy(unit) { - return Boolean(unit && isAlive(unit) && unit.getStat(sdk.stats.Alignment) !== 2 && typeof unit.classid === "number" && MonsterData[unit.classid].Killable); - } - - function onGround(item) { - return item.onGroundOrDropping; - } - - const GameData = { - myReference: me, - townAreas: [0, 1, 40, 75, 103, 109], - HPLookup: [["1", "1", "1"], ["7", "107", "830"], ["9", "113", "852"], ["12", "120", "875"], ["15", "125", "897"], ["17", "132", "920"], ["20", "139", "942"], ["23", "145", "965"], ["27", "152", "987"], ["31", "157", "1010"], ["35", "164", "1032"], ["36", "171", "1055"], ["40", "177", "1077"], ["44", "184", "1100"], ["48", "189", "1122"], ["52", "196", "1145"], ["56", "203", "1167"], ["60", "209", "1190"], ["64", "216", "1212"], ["68", "221", "1235"], ["73", "228", "1257"], ["78", "236", "1280"], ["84", "243", "1302"], ["89", "248", "1325"], ["94", "255", "1347"], ["100", "261", "1370"], ["106", "268", "1392"], ["113", "275", "1415"], ["120", "280", "1437"], ["126", "287", "1460"], ["134", "320", "1482"], ["142", "355", "1505"], ["150", "388", "1527"], ["158", "423", "1550"], ["166", "456", "1572"], ["174", "491", "1595"], ["182", "525", "1617"], ["190", "559", "1640"], ["198", "593", "1662"], ["206", "627", "1685"], ["215", "661", "1707"], ["225", "696", "1730"], ["234", "729", "1752"], ["243", "764", "1775"], ["253", "797", "1797"], ["262", "832", "1820"], ["271", "867", "1842"], ["281", "900", "1865"], ["290", "935", "1887"], ["299", "968", "1910"], ["310", "1003", "1932"], ["321", "1037", "1955"], ["331", "1071", "1977"], ["342", "1105", "2000"], ["352", "1139", "2030"], ["363", "1173", "2075"], ["374", "1208", "2135"], ["384", "1241", "2222"], ["395", "1276", "2308"], ["406", "1309", "2394"], ["418", "1344", "2480"], ["430", "1379", "2567"], ["442", "1412", "2653"], ["454", "1447", "2739"], ["466", "1480", "2825"], ["477", "1515", "2912"], ["489", "1549", "2998"], ["501", "1583", "3084"], ["513", "1617", "3170"], ["525", "1651", "3257"], ["539", "1685", "3343"], ["552", "1720", "3429"], ["565", "1753", "3515"], ["579", "1788", "3602"], ["592", "1821", "3688"], ["605", "1856", "3774"], ["618", "1891", "3860"], ["632", "1924", "3947"], ["645", "1959", "4033"], ["658", "1992", "4119"], ["673", "2027", "4205"], ["688", "2061", "4292"], ["702", "2095", "4378"], ["717", "2129", "4464"], ["732", "2163", "4550"], ["746", "2197", "4637"], ["761", "2232", "4723"], ["775", "2265", "4809"], ["790", "2300", "4895"], ["805", "2333", "4982"], ["821", "2368", "5068"], ["837", "2403", "5154"], ["853", "2436", "5240"], ["868", "2471", "5327"], ["884", "2504", "5413"], ["900", "2539", "5499"], ["916", "2573", "5585"], ["932", "2607", "5672"], ["948", "2641", "5758"], ["964", "2675", "5844"], ["982", "2709", "5930"], ["999", "2744", "6017"], ["1016", "2777", "6103"], ["1033", "2812", "6189"], ["1051", "2845", "6275"], ["1068", "2880", "6362"], ["1085", "2915", "6448"], ["1103", "2948", "6534"], ["1120", "2983", "6620"], ["1137", "3016", "6707"], ["10000", "10000", "10000"]], - monsterLevel: function (monsterID, areaID) { - return me.diff ? AreaData.hasOwnProperty(areaID) && AreaData[areaID].Level : MonsterData.hasOwnProperty(monsterID) && MonsterData[monsterID].Level; // levels on nm/hell are determined by area, not by monster data - }, - monsterExp: function (monsterID, areaID, adjustLevel = 0) { - return Experience.monsterExp[Math.min(Experience.monsterExp.length - 1, this.monsterLevel(monsterID, areaID) + adjustLevel)][me.diff] * MonsterData[monsterID].ExperienceModifier / 100; - }, - eliteExp: function (monsterID, areaID) { - return this.monsterExp(monsterID, areaID, 2) * 3; - }, - monsterAvgHP: function (monsterID, areaID, adjustLevel = 0) { - return this.HPLookup[Math.min(this.HPLookup.length - 1, this.monsterLevel(monsterID, areaID) + adjustLevel)][me.diff] * (getBaseStat("monstats", monsterID, "minHP") + getBaseStat("monstats", monsterID, "maxHP")) / 200; - }, - monsterMaxHP: function (monsterID, areaID, adjustLevel = 0) { - return this.HPLookup[Math.min(this.HPLookup.length - 1, this.monsterLevel(monsterID, areaID) + adjustLevel)][me.diff] * getBaseStat("monstats", monsterID, "maxHP") / 100; - }, - eliteAvgHP: function (monsterID, areaID) { - return (6 - me.diff) / 2 * this.monsterAvgHP(monsterID, areaID, 2); - }, - monsterDamageModifier: function () { - return 1 + (this.multiplayerModifier() - 1) * 0.0625; - }, - monsterMaxDmg: function (monsterID, areaID, adjustLevel = 0) { - let level = this.monsterLevel(monsterID, areaID) + adjustLevel; - return Math.max.apply(null, [MonsterData[monsterID].Attack1MaxDmg, MonsterData[monsterID].Attack2MaxDmg, MonsterData[monsterID].Skill1MaxDmg]) * level / 100 * this.monsterDamageModifier(); - }, - // https://www.diabloii.net/forums/threads/monster-damage-increase-per-player-count.570346/ - monsterAttack1AvgDmg: function (monsterID, areaID, adjustLevel = 0) { - let level = this.monsterLevel(monsterID, areaID) + adjustLevel; - return ((MonsterData[monsterID].Attack1MinDmg + MonsterData[monsterID].Attack1MaxDmg) / 2) * level / 100 * this.monsterDamageModifier(); - }, - monsterAttack2AvgDmg: function (monsterID, areaID, adjustLevel = 0) { - let level = this.monsterLevel(monsterID, areaID) + adjustLevel; - return ((MonsterData[monsterID].Attack2MinDmg + MonsterData[monsterID].Attack2MaxDmg) / 2) * level / 100 * this.monsterDamageModifier(); - }, - monsterSkill1AvgDmg: function (monsterID, areaID, adjustLevel = 0) { - let level = this.monsterLevel(monsterID, areaID) + adjustLevel; - return ((MonsterData[monsterID].Skill1MinDmg + MonsterData[monsterID].Skill1MaxDmg) / 2) * level / 100 * this.monsterDamageModifier(); - }, - monsterAvgDmg: function (monsterID, areaID, adjustLevel = 0) { - let attack1 = this.monsterAttack1AvgDmg(monsterID, areaID, adjustLevel); - let attack2 = this.monsterAttack2AvgDmg(monsterID, areaID, adjustLevel); - let skill1 = this.monsterSkill1AvgDmg(monsterID, areaID, adjustLevel); - let dmgs = [attack1, attack2, skill1].filter(x => x > 0); - // ignore 0 dmg to avoid reducing average - if (!dmgs.length) return 0; - return dmgs.reduce((acc, v) => acc + v) / dmgs.length; - }, - averagePackSize: monsterID => (MonsterData[monsterID].GroupCount.Min + MonsterData[monsterID].MinionCount.Min + MonsterData[monsterID].GroupCount.Max + MonsterData[monsterID].MinionCount.Max) / 2, - areaLevel: function (areaID) { - // levels on nm/hell are determined by area, not by monster data - if (me.diff) return AreaData[areaID].Level; - - let levels = 0, total = 0; - - AreaData[areaID].forEachMonsterAndMinion((mon, rarity) => { - levels += mon.Level * rarity; - total += rarity; - }); - - return Math.round(levels / total); - }, - areaImmunities: function (areaID) { - let resists = { Physical: 0, Magic: 0, Fire: 0, Lightning: 0, Cold: 0, Poison: 0 }; - - AreaData[areaID].forEachMonsterAndMinion(mon => { - for (let k in resists) { - resists[k] = Math.max(resists[k], mon[k]); - } - }); - - return Object.keys(resists).filter(key => resists[key] >= 100); - }, - levelModifier: function (clvl, mlvl) { - let bonus; - - if (clvl < 25 || mlvl < clvl) { - bonus = Experience.expCurve[Math.min(20, Math.max(0, Math.floor(mlvl - clvl + 10)))] / 255; - } else { - bonus = clvl / mlvl; - } - - return bonus * Experience.expPenalty[Math.min(30, Math.max(0, Math.round(clvl - 69)))] / 1024; - }, - multiplayerModifier: function (count) { - if (!count) { - let party = getParty(GameData.myReference); - if (!party) return 1; - - count = 1; - - while (party.getNext()) { - count++; - } - } - - return (count + 1) / 2; - }, - partyModifier: function (playerID) { - let party = getParty(GameData.myReference), level = 0, total = 0; - if (!party) return 1; - - let partyid = party.partyid; - - do { - if (party.partyid === partyid) { - total += party.level; - - if (playerID === party.name || playerID === party.gid) { - level = party.level; - } - } - } while (party.getNext()); - - return level / total; - }, - killExp: function (playerID, monsterID, areaID) { - let exp = this.monsterExp(monsterID, areaID), party = getParty(GameData.myReference); - if (!party) return 0; - - let level = 0, total = 0; - let gamesize = 0; - let partyid = party.partyid; - - do { - gamesize++; - - if (party.partyid === partyid) { - total += party.level; - - if (playerID === party.name || playerID === party.gid) { - level = party.level; - } - } - } while (party.getNext()); - - return Math.floor(exp * this.levelModifier(level, this.monsterLevel(monsterID, areaID)) * this.multiplayerModifier(gamesize) * level / total); - }, - baseLevel: function (...skillIDs) { - return skillIDs.reduce((total, skillID) => total + GameData.myReference.getSkill(skillID, 0), 0); - }, - skillLevel: function (...skillIDs) { - return skillIDs.reduce((total, skillID) => total + GameData.myReference.getSkill(skillID, 1), 0); - }, - skillCooldown: function (skillID) { - return getBaseStat("Skills", skillID, "delay") !== -1; - }, - stagedDamage: function (l, a, b, c, d, e, f, hitshift = 0, mult = 1) { - if (l > 28) { - a += f * (l - 28); - l = 28; - } - - if (l > 22) { - a += e * (l - 22); - l = 22; - } - - if (l > 16) { - a += d * (l - 16); - l = 16; - } - - if (l > 8) { - a += c * (l - 8); - l = 8; - } - - a += b * (Math.max(0, l) - 1); - - return (mult * a) << hitshift; - }, - damageTypes: ["Physical", "Fire", "Lightning", "Magic", "Cold", "Poison", "?", "?", "?", "Physical"], // 9 is Stun, but stun isn't an element - synergyCalc: { // TODO: add melee skill damage and synergies - they are poop - - // sorc fire spells - 36: [47, 0.16, 56, 0.16], // fire bolt - 41: [37, 0.13], // inferno - 46: [37, 0.04, 51, 0.01], // blaze - 47: [36, 0.14, 56, 0.14], // fire ball - 51: [37, 0.04, 41, 0.01], // fire wall - 52: [37, 0.09], // enchant - 56: [36, 0.05, 47, 0.05], // meteor - 62: [36, 0.03, 47, 0.03], // hydra - - // sorc lightning spells - 38: [49, 0.06], // charged bolt - 49: [38, 0.08, 48, 0.08, 53, 0.08], // lightning - 53: [38, 0.04, 48, 0.04, 49, 0.04], // chain lightning - - // sorc cold spells - 39: [44, 0.15, 45, 0.15, 55, 0.15, 59, 0.15, 64, 0.15], // ice bolt - 44: [59, 0.10, 64, 0.10], // frost nova - 45: [39, 0.08, 59, 0.08, 64, 0.08], // ice blast - 55: [39, 0.05, 45, 0.05, 64, 0.05], // glacial spike - 59: [39, 0.05, 45, 0.05, 55, 0.05], // blizzard - 64: [39, 0.02], // frozen orb - - // assassin traps - 251: [256, 0.09, 261, 0.09, 262, 0.09, 271, 0.09, 272, 0.09, 276, 0.09], // fireblast - 256: [261, 0.11, 271, 0.11, 276, 0.11], // shock web - 261: [251, 0.06, 271, 0.06, 276, 0.06], // charged bolt sentry - 262: [251, 0.08, 272, 0.08], // wake of fire sentry - 271: [256, 0.12, 261, 0.12, 276, 0.12], // lightning sentry - 272: [251, 0.10, 276, 0.10, 262, 0.07], // inferno sentry - 276: [271, 0.12], // death sentry - - // necro bone spells - 67: [78, 0.15, 84, 0.15, 88, 0.15, 93, 0.15], // teeth - 73: [83, 0.20, 92, 0.20], // poison dagger - 83: [73, 0.15, 92, 0.15], // poison explosion - 84: [67, 0.07, 78, 0.07, 88, 0.07, 93, 0.07], // bone spear - 92: [73, 0.10, 83, 0.10], // poison nova - 93: [67, 0.06, 78, 0.06, 84, 0.06, 88, 0.06], // bone spirit - - // barb war cry - 154: [130, 0.06, 137, 0.06, 146, 0.06], // war cry - - // paladin combat spells - 101: [112, 0.50, 121, 0.50], // holy bolt - 112: [108, 0.14, 115, 0.14], // blessed hammer - 121: [118, 0.07], // fist of heavens - - // paladin auras - 102: [100, 0.18, 125, 0.06], // holy fire - 114: [105, 0.15, 125, 0.07], // holy freeze - 118: [110, 0.12, 125, 0.04], // holy shock - - // durid elemental skills - 225: [229, 0.23, 234, 0.23], // firestorm - 229: [244, 0.10, 225, 0.08], // molten boulder - 234: [225, 0.12, 244, 0.12], // fissure (eruption) - 244: [229, 0.12, 234, 0.12, 249, 0.12], // volcano - 249: [225, 0.14, 229, 0.14, 244, 0.14], // armageddon - 230: [250, 0.15, 235, 0.15], // arctic blast - 240: [245, 0.10, 250, 0.10], // twister - 245: [235, 0.09, 240, 0.09, 250, 0.09], // tornado - 250: [240, 0.09, 245, 0.09], // hurricane - - // durid feral skills - 238: [222, 0.18], // rabies - 239: [225, 0.22, 229, 0.22, 234, 0.22, 244, 0.22], // fire claws - - // amazon bow/xbow skills - 11: [21, 0.12], // cold arrow - 21: [11, 0.08], // ice arrow - 31: [11, 0.12], // freezing arrow - 7: [16, 0.12], // fire arrow - 16: [7, 0.12], // exploding arrow - 27: [16, 0.10], // immolation arrow - - // amazon spear/javalin skills - 14: [20, 0.10, 24, 0.10, 34, 0.10, 35, 0.10], // power strike - 20: [14, 0.03, 24, 0.03, 34, 0.03, 35, 0.03], // lightning bolt - 24: [14, 0.10, 20, 0.10, 34, 0.10, 35, 0.10], // charged strike - 34: [14, 0.08, 20, 0.08, 24, 0.10, 35, 0.10], // lightning strike - 35: [14, 0.01, 20, 0.01, 24, 0.01, 34, 0.01], // lightning fury - 15: [25, 0.12], // poison javalin - 25: [15, 0.10], // plague javalin - }, - noMinSynergy: [14, 20, 24, 34, 35, 49, 53, 118, 256, 261, 271, 276], - skillMult: { - 15: 25, - 25: 25, - 41: 25, - 46: 75, - 51: 75, - 73: 25, - 83: 25, - 92: 25, - 222: 25, - 225: 75, - 230: 25, - 238: 25, - 272: 25 / 3 - }, - baseSkillDamage: function (skillID) { // TODO: rework skill damage to use both damage fields - let l = this.skillLevel(skillID), m = this.skillMult[skillID] || 1; - let dmgFields = [["MinDam", "MinLevDam1", "MinLevDam2", "MinLevDam3", "MinLevDam4", "MinLevDam5", "MaxDam", "MaxLevDam1", "MaxLevDam2", "MaxLevDam3", "MaxLevDam4", "MaxLevDam5"], ["EMin", "EMinLev1", "EMinLev2", "EMinLev3", "EMinLev4", "EMinLev5", "EMax", "EMaxLev1", "EMaxLev2", "EMaxLev3", "EMaxLev4", "EMaxLev5"]]; - - if (skillID === 70) { - return { - type: "Physical", - pmin: this.stagedDamage(l, getBaseStat("skills", skillID, dmgFields[1][0]), getBaseStat("skills", skillID, dmgFields[1][1]), getBaseStat("skills", skillID, dmgFields[1][2]), getBaseStat("skills", skillID, dmgFields[1][3]), getBaseStat("skills", skillID, dmgFields[1][4]), getBaseStat("skills", skillID, dmgFields[1][5]), getBaseStat("skills", skillID, "HitShift"), m), - pmax: this.stagedDamage(l, getBaseStat("skills", skillID, dmgFields[1][0]), getBaseStat("skills", skillID, dmgFields[1][1]), getBaseStat("skills", skillID, dmgFields[1][2]), getBaseStat("skills", skillID, dmgFields[1][3]), getBaseStat("skills", skillID, dmgFields[1][4]), getBaseStat("skills", skillID, dmgFields[1][5]), getBaseStat("skills", skillID, "HitShift"), m), - min: 0, max: 0 - }; - } else { - let type = getBaseStat("skills", skillID, "EType"); - - return { - type: this.damageTypes[type], - pmin: this.stagedDamage(l, getBaseStat("skills", skillID, dmgFields[0][0]), getBaseStat("skills", skillID, dmgFields[0][1]), getBaseStat("skills", skillID, dmgFields[0][2]), getBaseStat("skills", skillID, dmgFields[0][3]), getBaseStat("skills", skillID, dmgFields[0][4]), getBaseStat("skills", skillID, dmgFields[0][5]), getBaseStat("skills", skillID, "HitShift"), m), - pmax: this.stagedDamage(l, getBaseStat("skills", skillID, dmgFields[0][6]), getBaseStat("skills", skillID, dmgFields[0][7]), getBaseStat("skills", skillID, dmgFields[0][8]), getBaseStat("skills", skillID, dmgFields[0][9]), getBaseStat("skills", skillID, dmgFields[0][10]), getBaseStat("skills", skillID, dmgFields[0][11]), getBaseStat("skills", skillID, "HitShift"), m), - min: type ? this.stagedDamage(l, getBaseStat("skills", skillID, dmgFields[1][0]), getBaseStat("skills", skillID, dmgFields[1][1]), getBaseStat("skills", skillID, dmgFields[1][2]), getBaseStat("skills", skillID, dmgFields[1][3]), getBaseStat("skills", skillID, dmgFields[1][4]), getBaseStat("skills", skillID, dmgFields[1][5]), getBaseStat("skills", skillID, "HitShift"), m) : 0, - max: type ? this.stagedDamage(l, getBaseStat("skills", skillID, dmgFields[1][6]), getBaseStat("skills", skillID, dmgFields[1][7]), getBaseStat("skills", skillID, dmgFields[1][8]), getBaseStat("skills", skillID, dmgFields[1][9]), getBaseStat("skills", skillID, dmgFields[1][10]), getBaseStat("skills", skillID, dmgFields[1][11]), getBaseStat("skills", skillID, "HitShift"), m) : 0 - }; - } - }, - skillRadius: { - //47: 8, - //48: 5, // Nova - 55: 3, - 56: 12, - 92: 24, - 154: 12, - 249: 24, - 250: 24, - 251: 3, - }, - novaLike: { - 44: true, - 48: true, - 92: true, - 112: true, - 154: true, - 249: true, - 250: true, - }, - wolfBanned: { - 225: true, - 229: true, - 230: true, - 233: true, - 234: true, - 235: true, - 240: true, - 243: true, - 244: true, - 245: true, - 250: true, - }, - bearBanned: { - 225: true, - 229: true, - 230: true, - 232: true, - 234: true, - 235: true, - 238: true, - 240: true, - 244: true, - 245: true, - 248: true, - }, - humanBanned: { - 232: true, - 233: true, - 238: true, - 239: true, - 242: true, - 243: true, - 248: true, - }, - nonDamage: { - // Some fakes to avoid these - - 54: true, // teleport - 217: true, // scroll identify - 218: true, // portal scroll - 219: true, // I assume this is the book of scroll - 220: true, // book portal. Not really a skill you want to use, do you - 117: true, // Holy shield. Holy shield it self doesnt give damage - 278: true, // venom adds damage, but doesnt do damage on its own - - // Remove all the trap skills, as we prefer to calculate this upon demand - 261: true, // lighting bolt - 271: true, // lighting sentry - 276: true, // Death sentry only works on corpses, we calculate this within attack - 262: true, // wake of fire - 272: true, // inferno - }, - shiftState: function () { - if (GameData.myReference.getState(139)) return "wolf"; - if (GameData.myReference.getState(140)) return "bear"; - return "human"; - }, - bestForm: function (skillID) { - if (this.shiftState() === "human" && this.humanBanned[skillID]) { - let highest = { ID: 0, Level: 0 }; - - if (!this.wolfBanned[skillID] && this.skillLevel(223) > highest.Level) { - highest.ID = 223; - highest.Level = this.skillLevel(223); - } - - if (!this.bearBanned[skillID] && this.skillLevel(228) > highest.Level) { - highest.ID = 228; - highest.Level = this.skillLevel(228); - } - - return highest.ID; - } else if (this.shiftState() === "wolf" && this.wolfBanned[skillID]) { - return 223; - } else if (this.shiftState() === "bear" && this.bearBanned[skillID]) { - return 228; - } - - return 0; - }, - physicalAttackDamage: function (skillID) { - let dmg = (() => { - switch (skillID) { - case sdk.skills.Bash: - return 45 + (5 + GameData.myReference.getSkill(skillID, 1)) + (5 * GameData.myReference.getSkill(sdk.skills.Stun, 0)); - case sdk.skills.Stun: - return (8 * GameData.myReference.getSkill(sdk.skills.Bash, 0)); - case sdk.skills.Concentrate: - return (65 + (5 * GameData.myReference.getSkill(skillID, 1)) + (5 * GameData.myReference.getSkill(sdk.skills.Bash, 0)) + (10 * GameData.myReference.getSkill(sdk.skills.BattleOrders, 0))); - case sdk.skills.LeapAttack: - return (70 + (30 * GameData.myReference.getSkill(skillID, 1)) + (10 * GameData.myReference.getSkill(sdk.skills.Leap, 0))); - case sdk.skills.Whirlwind: - return (8 * GameData.myReference.getSkill(skillID, 1)) - 58; - default: - return 0; - } - })(); - - // return (((GameData.myReference.getStat(sdk.stats.MaxDamage) + GameData.myReference.getStat(sdk.stats.MinDamage)) / 2) + (GameData.myReference.getStat(sdk.stats.Strength) * dmg)) / 100; - return dmg; - }, - dmgModifier: function (skillID, target) { - let aps = (typeof target === "number" ? this.averagePackSize(target) : 1); - let eliteBonus = (target.spectype && target.isSpecial) ? 1 : 0, hitcap = 1; - - switch (skillID) { // charged bolt/strike excluded, it's so unreliably random - case 15: // poison javalin - case 25: // plague javalin - case 16: // exploding arrow - case 27: // immolation arrow - case 31: // freezing arrow - case 35: // lightning fury - case 44: // frost nova - case 48: // nova - case 56: // meteor - case 59: // blizzard - case 64: // frozen orb - case 83: // poison explosion - case 92: // poison nova - case 112: // blessed hammer - case 154: // war cry - case 229: // molten boulder - case 234: // fissure - case 249: // armageddon - case 244: // volcano - case 250: // hurricane - case 251: // fireblast - case 261: // charged bolt sentry - case 262: // wake of fire - case 55: // glacial spike - case 47: // fire ball - case 42: // Static field. - case 38: // charged bolt - hitcap = Infinity; - break; - case 34: // lightning strike - hitcap = 1 + this.skillLevel(34); - break; - case 67: // teeth - hitcap = 1 + this.skillLevel(67); - break; - case 53: // chain lightning - hitcap = 5 + ((this.skillLevel(53) / 5) | 0); - break; - case 24: - hitcap = 3 + ((this.skillLevel(24) / 5) | 0); - break; - case 49: // lightning - case 84: // bone spear - case 271: // lightning sentry - case 276: // death sentry - hitcap = aps ? Math.sqrt(aps / Math.PI) * 2 : 1; - break; - default: - hitcap = 1; - break; - } - - if (typeof target !== "number") { - let unit = Game.getMonster(); - let radius = this.skillRadius[skillID] || 18; - - if (unit) { - do { - if (aps >= hitcap) { - break; - } - - if (target.gid !== unit.gid && getDistance(unit, this.novaLike[skillID] ? GameData.myReference : target) <= radius && isEnemy(unit)) { - aps++; - - if (unit.isSpecial) { - eliteBonus++; - } - } - } while (unit.getNext()); - } - } else { - aps = Math.min(aps, hitcap); - } - - aps += eliteBonus * (4 - me.diff) / 2; - - return aps; - }, - - /** - * @typedef skillDmgObj - * @property {string} type - * @property {number} pmin - * @property {number} pmax - * @property {number} min - * @property {number} max - * @property {boolean} [undeadOnly] - * - * @param {number} skillID - * @param {Monster} unit - * @returns {skillDmgObj} - */ - skillDamage: function (skillID, unit) { - // TODO: caluclate basic attack damage - if (skillID === sdk.skills.Attack) return { type: "Physical", pmin: 2, pmax: 8, min: 0, max: 0 }; // short sword, no reqs - - if (this.skillLevel(skillID) < 1) { - return { - type: this.damageTypes[getBaseStat("skills", skillID, "EType")], - pmin: 0, - pmax: 0, - min: 0, - max: 0 - }; - } - - let dmg = this.baseSkillDamage(skillID); - let mastery = 1, psynergy = 1, synergy = 1, shots = 1, sl = 0; - - if (this.synergyCalc[skillID]) { - let sc = this.synergyCalc[skillID]; - - for (let c = 0; c < sc.length; c += 2) { - sl = this.baseLevel(sc[c]); - - if (skillID === 229 || skillID === 244) { - if (sc[c] === 229 || sc[c] === 244) { // molten boulder and volcano - psynergy += sl * sc[c + 1]; // they only synergize physical with each other - } else { - synergy += sl * sc[c + 1]; // all other skills synergize only fire with these skills - } - } else { - psynergy += sl * sc[c + 1]; - synergy += sl * sc[c + 1]; - } - } - } - - if (skillID === 227 || skillID === 237 || skillID === 247) { - sl = this.skillLevel(247); - psynergy += 0.15 + sl * 0.10; - synergy += 0.15 + sl * 0.10; - } - - switch (dmg.type) { - case "Fire": // fire mastery - case "Lightning": // lightning mastery - case "Cold": // cold mastery - case "Poison": // poison mastery - case "Magic": // magic mastery - mastery = 1 + GameData.myReference.getStat(this.masteryMap[dmg.type]) / 100; - dmg.min *= mastery; - dmg.max *= mastery; - - break; - } - - dmg.pmin *= psynergy; - dmg.pmax *= psynergy; - - if (this.noMinSynergy.indexOf(skillID) < 0) { - dmg.min *= synergy; - } - - dmg.max *= synergy; - - switch (skillID) { - case 102: // holy fire - dmg.min *= 6; // weapon damage is 6x the aura damage - dmg.max *= 6; - break; - case 114: // holy freeze - dmg.min *= 5; // weapon damage is 5x the aura damage - dmg.max *= 5; - break; - case 118: // holy shock - dmg.min *= 6; // weapon damage is 6x the aura damage - dmg.max *= 6; - break; - case 249: // armageddon - dmg.pmin = dmg.pmax = 0; - break; - case 24: // charged strike - dmg.max *= 3 + ((this.skillLevel(24) / 5) | 0); - } - - dmg.pmin >>= 8; - dmg.pmax >>= 8; - dmg.min >>= 8; - dmg.max >>= 8; - - switch (skillID) { - case sdk.skills.ChargedBolt: // more than one bolt can hit but may calc this as splashdamage instead - if (unit) { - let baseId = getBaseStat("monstats", unit.classid, "baseid"); - let size = getBaseStat("monstats2", baseId, "sizex"); - (typeof size !== "number" || size < 1 || size > 3) && (size = 3); - let dist = unit.distance; - const modifier = size === 1 ? 0.5 : size === 3 ? 1.5 : size === 2 && dist < 5 ? 1.2 : 1; - dmg.min *= modifier; - dmg.max *= modifier; - } - - // need to take into account the amount of bolts released - // the size of the unit we are targetting - // the distance from the target - break; - case 59: // blizzard - on average hits twice - dmg.min *= 2; - dmg.max *= 2; - break; - case 62: // hydra - 3 heads - dmg.min *= 3; - dmg.max *= 3; - break; - case 64: // frozen orb - on average hits ~5 times - dmg.min *= 5; - dmg.max *= 5; - break; - case 70: // skeleton - a hit per skeleton - sl = this.skillLevel(70); - shots = sl < 4 ? sl : (2 + sl / 3) | 0; - sl = Math.max(0, sl - 3); - dmg.pmin = shots * (dmg.pmin + 1 + this.skillLevel(69) * 2) * (1 + sl * 0.07); - dmg.pmax = shots * (dmg.pmax + 2 + this.skillLevel(69) * 2) * (1 + sl * 0.07); - break; - case 94: // fire golem - sl = this.skillLevel(94); - dmg.min = [10, 15, 18][me.diff] + dmg.min + (this.stagedDamage(sl + 7, 2, 1, 2, 3, 5, 7) >> 1) * 6; // basically holy fire added - dmg.max = [27, 39, 47][me.diff] + dmg.max + (this.stagedDamage(sl + 7, 6, 1, 2, 3, 5, 7) >> 1) * 6; - break; - case 101: // holy bolt - dmg.undeadOnly = true; - break; - case 112: // blessed hammer - sl = this.skillLevel(113); - - if (sl > 0) { - mastery = (100 + ((45 + this.skillLevel(113) * 15) >> 1)) / 100; // hammer gets half concentration dmg bonus - dmg.min *= mastery; - dmg.max *= mastery; - } - - break; - case sdk.skills.Raven: // raven - a hit per raven - shots = Math.min(5, this.skillLevel(221)); // 1-5 ravens - dmg.pmin *= shots; - dmg.pmax *= shots; - break; - case sdk.skills.SummonSpiritWolf: // spirit wolf - a hit per wolf - shots = Math.min(5, this.skillLevel(227)); - dmg.pmin *= shots; - dmg.pmax *= shots; - break; - case sdk.skills.SummonDireWolf: // dire wolf - a hit per wolf - shots = Math.min(3, this.skillLevel(237)); - dmg.pmin *= shots; - dmg.pmax *= shots; - break; - case sdk.skills.Twister: // twister - dmg.pmin *= 3; - dmg.pmax *= 3; - break; - case 261: // charged bolt sentry - case 262: // wake of fire - case 271: // lightning sentry - case 272: // inferno sentry - case 276: // death sentry - dmg.min *= 5; // can have 5 traps out at a time - dmg.max *= 5; - break; - case sdk.skills.StaticField: - if (!(unit instanceof Unit)) { - break; - } - // No cap in classic - let staticCap = (me.gametype === sdk.game.gametype.Classic ? 0 : [0, 33, 50][me.diff]); - const [monsterId, areaId] = [unit.classid, unit.area]; - let percentLeft = (unit.hp * 100 / unit.hpmax); - if (staticCap > percentLeft) { - break; - } - - const maxReal = this.monsterMaxHP(monsterId, areaId, unit.charlvl - this.monsterLevel(monsterId, areaId)); - let hpReal = maxReal / 100 * percentLeft; - let potencialDmg = (hpReal / 100 * percentLeft) * 0.25; - - let tmpDmg = (maxReal / 100 * percentLeft) * (0.25); - - // We do need to calculate the extra damage, or less damage due to resistance - let resist = this.monsterResist(unit, "Lightning"); - let pierce = GameData.myReference.getStat(this.pierceMap.Lightning); - - let conviction = this.getConviction(); - // if (conviction && !unit.getState(sdk.states.Conviction)) conviction = 0; //ToDo; enable when fixed telestomp - resist -= (resist >= 100 ? conviction / 5 : conviction); - resist = (resist < 100 ? Math.max(-100, resist - pierce) : 100); - tmpDmg = potencialDmg * ((100 - resist) / 100); - const percentageDamage = 100 / maxReal * tmpDmg; - - let avgDmg = tmpDmg; - let overCap = percentLeft - staticCap - percentageDamage; - if (overCap < 0) { - let maxDmgPercentage = percentageDamage - Math.abs(overCap); - avgDmg = maxReal / 100 * maxDmgPercentage; - } - avgDmg = avgDmg > 0 && avgDmg || 0; - //console.log('Static will chop off -> ' + (100 / maxReal * avgDmg) + '%'); - dmg.min = avgDmg; - dmg.max = avgDmg; - break; - } - - dmg.pmin |= 0; - dmg.pmax |= 0; - dmg.min |= 0; - dmg.max |= 0; - - return dmg; - }, - - /** - * Calculate actual average damage this skill does taking into account splash/range of skill - * @param {number} skillID - * @param {Monster | number | string} unit - * @returns {number} - * @todo - * - build me metadata - then use it to calulate a range of skills rather than redo the exact same calculations. - * example: trying to check the damage of blizard and then frozen orb, - * currently it would check our stats, then check amp and conviction - those could all be pre-built as they aren't going to change - */ - avgSkillDamage: function (skillID, unit) { - if (skillID === undefined || unit === undefined || !skillID || !unit || !Skill.canUse(skillID)) return 0; - const ampDmg = Skill.canUse(sdk.skills.AmplifyDamage) ? 100 : (Skill.canUse(sdk.skills.Decrepify) ? 50 : 0); - - /** - * - * @param {skillDmgObj} skillData - * @param {Monster | number | string} unit - * @returns - */ - const getTotalDmg = function (skillData, unit) { - const isUndead = (typeof unit === "number" ? MonsterData[unit].Undead : MonsterData[unit.classid].Undead); - const conviction = GameData.getConviction(); - let totalDmg = 0; - let avgPDmg = (skillData.pmin + skillData.pmax) / 2; - let avgDmg = (skillData.min + skillData.max) / 2; - - if (avgPDmg > 0) { - let presist = GameData.monsterResist(unit, "Physical"); - presist -= (presist >= 100 ? ampDmg / 5 : ampDmg); - presist = Math.max(-100, Math.min(100, presist)); - totalDmg += avgPDmg * (100 - presist) / 100; - } - if (avgDmg > 0 && (!isUndead || !skillData.undeadOnly)) { - let resist = GameData.monsterResist(unit, skillData.type); - let pierce = GameData.myReference.getStat(GameData.pierceMap[skillData.type]); - if (GameData.convictionEligible[skillData.type]) { - resist -= (resist >= 100 ? conviction / 5 : conviction); - } - resist = (resist < 100 ? Math.max(-100, resist - pierce) : 100); - totalDmg += avgDmg * (100 - resist) / 100; - } - return totalDmg; - }; - - /** - * @param {number} skill - * @param {number} splash - * @param {Monster} target - * @returns {number} - */ - const calculateSplashDamage = function (skill, splash, target) { - return getUnits(sdk.unittype.Monster) - .filter((mon) => mon.attackable && getDistance(target, mon) < splash) - .reduce(function (acc, cur) { - let _a = GameData.skillDamage(skill, cur); - return acc + getTotalDmg(_a, cur); - }, 0); - }; - - /** - * @param {number} skill - * @param {Monster} target - * @returns {number} - */ - const calculateChainDamage = function (skill, target) { - skill === undefined && (skill = -1); - let rawDmg = 0, totalDmg = 0, range = 0, hits = 0; - switch (skill) { - case sdk.skills.ChainLightning: - hits = Math.round((25 + me.getSkill(sdk.skills.ChainLightning, sdk.skills.subindex.SoftPoints)) / 5); - range = 13; - break; - } - let units = getUnits(sdk.unittype.Monster) - .filter((mon) => mon.attackable && getDistance(mon, target) < range) - .sort((a, b) => getDistance(target, a) - getDistance(target, b)); - if (units.length === 1) { - rawDmg = GameData.skillDamage(skill, target); - return getTotalDmg(rawDmg, target); - } else { - console.log("Units to check: " + units.length); - for (let i = 0; i < units.length; i++) { - if (units[i] !== undefined) { - rawDmg = GameData.skillDamage(skill, units[i]); - totalDmg += getTotalDmg(rawDmg, units[i]); - if (i > hits) { break; } - } else { - units.splice(i, 1); - i -= 1; - } - } - return totalDmg; - } - }; - - const calculateRawStaticDamage = function (distanceUnit) { - distanceUnit === undefined && (distanceUnit = me); - if (!Skill.canUse(sdk.skills.StaticField)) return 0; - const range = Skill.getRange(sdk.skills.StaticField); - const cap = (me.gametype === sdk.game.gametype.Classic ? 1 : [1, 25, 50][me.diff]); - const pierce = me.getStat(sdk.stats.PierceLtng); - return getUnits(sdk.unittype.Monster) - .filter(function (mon) { - return mon.attackable && getDistance(mon, distanceUnit) < range; - }).reduce(function (acc, unit) { - let classId = unit.classid, areaId = unit.area; - let maxHealth = GameData.monsterAvgHP(classId, areaId, unit.charlvl - GameData.monsterLevel(classId, areaId)); - let currentHealth = maxHealth / 100 * (unit.hp * 100 / unit.hpmax), baseDamage = currentHealth * 0.25; - // monsterRes already considers conviction state - let monsterRes = unit.getStat(sdk.stats.LightResist); - let totalRes = Math.min(100, Math.max(-100, monsterRes - pierce)); - // calculate the actual damage we do - let potentialDamage = baseDamage / (100 / (100 - totalRes)); - let cappedAtHealth = maxHealth / 100 * cap; - // cap max damage - let actualDamage = currentHealth - Math.max(cappedAtHealth, (currentHealth - potentialDamage)); - return acc + (actualDamage); - }, 0); - }; - - const calculateThroughDamage = function () { - // determine maximum potential distance of this missile - // build points from me -> monster -> max distance - // iterate points checking if any monsters are in the path - // check collision at each point and break if we encounter a missisle blocker - // special considerations for molten boulder: - // - check monster size, based on size the boulder may knock back or go through them - // - if we encounter a collision that causes the boulder to burst, add explosion damage - // - }; - - /** - * - * @param {Monster} unit - */ - const calcVolcanoDamage = function (unit) { - let velocity = unit.currentVelocity; - /** @type {skillDmgObj} */ - let baseDmg = GameData.skillDamage(sdk.skills.Volcano, unit); - // since these are random, lets take them into account but not at their full value - let missleDmg = Object.assign({}, baseDmg); - missleDmg.pmin /= 2; - missleDmg.pmax /= 2; - missleDmg.min /= 2; - missleDmg.max /= 2; - // sorta guess work for now, needs improvment to really figure out on average how many times the actual - // volcano damages the monster cast on based on size/speed - let modifier = (!unit.isMoving || velocity === 1) ? 5 : velocity === 2 ? 3 : 1; - if (modifier > 1) { - baseDmg.pmin *= modifier; - baseDmg.pmax *= modifier; - baseDmg.min *= modifier; - baseDmg.max *= modifier; - } - - // sum the total in the range of the volcano missiles - // what about monsters just directly on the volcano? - return getUnits(sdk.unittype.Monster) - .filter((mon) => mon.attackable && getDistance(unit, mon) < 15) - .reduce(function (acc, cur) { - return acc + getTotalDmg(missleDmg, cur); - }, getTotalDmg(baseDmg, unit)); - }; - - /** - * @todo some skills need special handling - * - Bone spear and Lightning both pierce enemies in a straight path, need to calculate include monsters in that path - * to the total damage done as this can make the difference between wanting to use this skill vs another - * - Fire Wall is similar only seems to be a random angle - * - Inferno/Artic blast same as bonespear/lightning - * - Molten boulder needs to take into account monster size and angle of cast - * - Zeal, can hit same enemy multiple times or multiple enemies, would change total damage applied based on enemy targetted so needs to be handled - * Others? - */ - switch (skillID) { - case sdk.skills.Blizzard: - case sdk.skills.Meteor: - case sdk.skills.FireBall: - case sdk.skills.GlacialSpike: - case sdk.skills.ChargedBolt: - case sdk.skills.Fissure: - let { x, y } = unit; - let rad = skillID === sdk.skills.Volcano ? 15 : skillID === sdk.skills.Fissure ? 10 : 5; - - if (!Attack.validSpot(x, y, skillID, unit.classid)) { - return 0; - } - - return calculateSplashDamage(skillID, rad, unit); - case sdk.skills.Volcano: - if (!Attack.validSpot(unit.x, unit.y, skillID, unit.classid)) { - return 0; - } - - return calcVolcanoDamage(unit); - case sdk.skills.FrostNova: - case sdk.skills.Nova: - return calculateSplashDamage(skillID, 6, unit); - case sdk.skills.StaticField: - return calculateRawStaticDamage(unit); - case sdk.skills.ChainLightning: - return calculateChainDamage(skillID, unit); - default: - return getTotalDmg(this.skillDamage(skillID, unit), unit); - } - }, - allSkillDamage: function (unit) { - let skills = {}; - let self = this; - GameData.myReference.getSkill(4).forEach(function (skill) { - if (self.nonDamage.hasOwnProperty(skill[0])) { - return false; // Doesnt do damage - } - return skills[skill[0]] = self.skillDamage(skill[0], unit); - }); - - return skills; - }, - convictionEligible: { - Fire: true, - Lightning: true, - Cold: true, - }, - lowerResistEligible: { - Fire: true, - Lightning: true, - Cold: true, - Poison: true, - }, - resistMap: { - Physical: 36, - Fire: 39, - Lightning: 41, - Cold: 43, - Poison: 45, - Magic: 37, - }, - masteryMap: { - Fire: 329, - Lightning: 330, - Cold: 331, - Poison: 332, - Magic: 357, - }, - pierceMap: { - Fire: 333, - Lightning: 334, - Cold: 335, - Poison: 336, - Magic: 358, - }, - ignoreSkill: { - 40: true, - 50: true, - 60: true, - }, - buffs: { - 8: 1, - 9: 1, - 13: 1, - 17: 1, - 18: 1, - 23: 1, - 28: 1, - 29: 1, - 32: 1, - 37: 1, - 40: 2, - 46: 1, - 50: 2, - 52: 1, - 57: 1, - 58: 1, - 60: 2, - 61: 1, - 63: 1, - 65: 1, - 68: 1, - 69: 1, - 79: 1, - 89: 1, - 98: 3, - 99: 3, - 100: 3, - 102: 3, - 103: 3, - 104: 3, - 105: 3, - 108: 3, - 109: 3, - 110: 3, - 113: 3, - 114: 3, - 115: 3, - 118: 3, - 119: 3, - 120: 3, - 122: 3, - 123: 3, - 124: 3, - 125: 3, - 127: 1, - 128: 1, - 129: 1, - 134: 1, - 135: 1, - 136: 1, - 138: 1, - 141: 1, - 145: 1, - 148: 1, - 149: 1, - 153: 1, - 155: 1, - 221: 1, - 222: 4, - 223: 5, - 224: 1, - 226: 6, - 227: 7, - 228: 5, - 231: 4, - 235: 1, - 236: 6, - 237: 7, - 241: 4, - 246: 6, - 247: 7, - 249: 1, - 250: 1, - 258: 8, - 267: 8, - 268: 9, - 279: 9, - }, - preAttackable: [ - sdk.skills.MagicArrow, sdk.skills.FireArrow, sdk.skills.MultipleShot, sdk.skills.ExplodingArrow, sdk.skills.IceArrow, sdk.skills.GuidedArrow, sdk.skills.ImmolationArrow, sdk.skills.Strafe, - sdk.skills.PlagueJavelin, sdk.skills.LightningFury, - sdk.skills.FireBolt, sdk.skills.Inferno, sdk.skills.Blaze, sdk.skills.FireBall, sdk.skills.FireWall, sdk.skills.Meteor, sdk.skills.Hydra, - sdk.skills.ChargedBolt, sdk.skills.Nova, sdk.skills.Lightning, sdk.skills.ChainLightning, - sdk.skills.IceBolt, sdk.skills.FrostNova, sdk.skills.IceBlast, sdk.skills.Blizzard, sdk.skills.FrozenOrb, - sdk.skills.AmplifyDamage, sdk.skills.DimVision, sdk.skills.Weaken, sdk.skills.IronMaiden, sdk.skills.Terror, sdk.skills.Confuse, sdk.skills.LifeTap, sdk.skills.Attract, sdk.skills.Decrepify, sdk.skills.LowerResist, - sdk.skills.Teeth, sdk.skills.BoneSpear, sdk.skills.PoisonNova, - sdk.skills.BlessedHammer, - sdk.skills.WarCry, - sdk.skills.Twister, sdk.skills.Tornado, - sdk.skills.FireBlast, sdk.skills.ShockWeb, - ], - monsterResist: function (unit, type) { - let stat = this.resistMap[type]; - return stat ? (unit.getStat ? unit.getStat(stat) : MonsterData[unit][type]) : 0; - }, - getConviction: function () { - let merc = GameData.myReference.getMerc(), sl = this.skillLevel(123); // conviction - if (( // Either me, or merc is wearing a conviction - merc && merc.getItemsEx().filter(item => item.getPrefix(sdk.locale.items.Infinity)).first() - || GameData.myReference.getItemsEx(-1, 1).filter(item => item.getPrefix(sdk.locale.items.Infinity)).first())) { - sl = 12; - } - return sl > 0 ? Math.min(150, 30 + (sl - 1) * 5) : 0; - }, - getAmp: function () { - return this.skillLevel(66) ? 100 : (this.skillLevel(87) ? 50 : 0); - }, - monsterEffort: function (unit, areaID, skillDamageInfo = undefined, parent = undefined, preattack = false, all = false) { - let buffDmg = []; - const allData = []; - const buffDamageInfo = {}; - const newSkillDamageInfo = {}; - const eret = { effort: Infinity, skill: -1, type: "Physical" }; - const useCooldown = (typeof unit === "number" ? false : Boolean(me.skillDelay)); - const hp = this.monsterMaxHP(typeof unit === "number" ? unit : unit.classid, areaID); - const conviction = this.getConviction(), ampDmg = this.getAmp(); - const isUndead = (typeof unit === "number" ? MonsterData[unit].Undead : MonsterData[unit.classid].Undead); - skillDamageInfo = skillDamageInfo || this.allSkillDamage(unit); - console.debug(unit, "---", hp); - // if (conviction && unit instanceof Unit && !unit.getState(sdk.states.Conviction)) conviction = 0; //ToDo; enable when fixed telestomp - - for (let sk in skillDamageInfo) { - if (this.buffs[sk]) { - if (typeof unit === "number") { - buffDmg[this.buffs[sk]] = 0; - buffDamageInfo[sk] = skillDamageInfo[sk]; - } - } else { - newSkillDamageInfo[sk] = skillDamageInfo[sk]; - } - } - - skillDamageInfo = newSkillDamageInfo; - - for (let sk in buffDamageInfo) { - // static field has a fix'd ceiling, calculated already - if ([sdk.skills.StaticField].indexOf(sk) !== -1) continue; - - let avgPDmg = (buffDamageInfo[sk].pmin + buffDamageInfo[sk].pmax) / 2; - let avgDmg = (buffDamageInfo[sk].min + buffDamageInfo[sk].max) / 2; - let tmpDmg = 0; - - if (avgPDmg > 0) { - let presist = this.monsterResist(unit, "Physical"); - - presist -= (presist >= 100 ? ampDmg / 5 : ampDmg); - presist = Math.max(-100, Math.min(100, presist)); - tmpDmg += avgPDmg * (100 - presist) / 100; - } - - if (avgDmg > 0 && (!isUndead || !buffDamageInfo[sk].undeadOnly) && sk !== sdk.skills.StaticField) { - let resist = this.monsterResist(unit, buffDamageInfo[sk].type); - let pierce = GameData.myReference.getStat(this.pierceMap[buffDamageInfo[sk].type]); - - if (this.convictionEligible[buffDamageInfo[sk].type]) { - resist -= (resist >= 100 ? conviction / 5 : conviction); - } - - if (resist < 100) { - resist = Math.max(-100, resist - pierce); - } else { - resist = 100; - } - - tmpDmg += avgDmg * (100 - resist) / 100; - } - - if (this.buffs[sk] === 1) { - buffDmg[this.buffs[sk]] += tmpDmg; - } else { - buffDmg[this.buffs[sk]] = Math.max(buffDmg[this.buffs[sk]], tmpDmg); - } - } - - buffDmg = buffDmg.reduce((t, v) => t + v, 0); - - for (let sk in skillDamageInfo) { - if (preattack && this.preAttackable.indexOf(parseInt(sk)) === -1) continue; // cant preattack this skill - if (!this.ignoreSkill[sk] && (!useCooldown || !this.skillCooldown(sk | 0))) { - let avgPDmg = (skillDamageInfo[sk].pmin + skillDamageInfo[sk].pmax) / 2, totalDmg = buffDmg; - let avgDmg = (skillDamageInfo[sk].min + skillDamageInfo[sk].max) / 2; - - if (avgPDmg > 0 && sk !== sdk.skills.StaticField) { - let presist = this.monsterResist(unit, "Physical"); - - presist -= (presist >= 100 ? ampDmg / 5 : ampDmg); - presist = Math.max(-100, Math.min(100, presist)); - totalDmg += avgPDmg * (100 - presist) / 100; - } - - if (avgDmg > 0 && (!isUndead || !skillDamageInfo[sk].undeadOnly)) { - let resist = this.monsterResist(unit, skillDamageInfo[sk].type); - let pierce = GameData.myReference.getStat(this.pierceMap[skillDamageInfo[sk].type]); - - if (this.convictionEligible[skillDamageInfo[sk].type]) { - resist -= (resist >= 100 ? conviction / 5 : conviction); - } - - if (resist < 100) { - resist = Math.max(-100, resist - pierce); - } else { - resist = 100; - } - - totalDmg += sk !== sdk.skills.StaticField - && 0 - || avgDmg * (100 - resist) / 100; - - } - console.debug(hp, "---/", totalDmg); - let tmpEffort = Math.ceil(hp / totalDmg); - - tmpEffort /= this.dmgModifier(sk | 0, parent || unit); - - // care for mana - if (GameData.myReference.mp < Skill.getManaCost(sk)) { - tmpEffort *= 5; // More effort in a skill we dont have mana for - } - - // check valid location, blizzard and meteor fail over lava - if (typeof unit === "object") { - if ([sdk.skills.Blizzard, sdk.skills.Meteor].indexOf(sk) && !Attack.validSpot(unit.x, unit.y, sk, unit.classid)) { - tmpEffort *= 5; - } - } - - // Use less cool down spells, if something better is around - /* if (this.skillCooldown(sk | 0)) { - console.log("tmpEffort: " + (Math.ceil(tmpEffort)) + " eretEffor: " + eret.effort); - tmpEffort *= 5; - } */ - if (tmpEffort <= eret.effort) { - eret.effort = tmpEffort; - eret.skill = sk | 0; - eret.type = skillDamageInfo[eret.skill].type; - eret.name = getSkillById(eret.skill); - eret.cooldown = this.skillCooldown(sk | 0); - if (all) { - allData.unshift(copyObj(eret)); - } - } - } - } - console.log(eret, allData); - if (all && allData.length) return allData; - if (eret.skill >= 0) return eret; - return null; - }, - effectiveMonsterEffort: function (unit, areaID) { - if (unit === undefined) return null; - areaID === undefined && (areaID = me.area); - const allData = []; - const buffDamageInfo = {}; - const newSkillDamageInfo = {}; - let buffDmg = []; - let eret = { effort: Infinity, skill: -1, type: "Physical" }; - let hp = this.monsterMaxHP(typeof unit === "number" ? unit : unit.classid, areaID); - let conviction = this.getConviction(), ampDmg = this.getAmp(); - let isUndead = (typeof unit === "number" ? MonsterData[unit].Undead : MonsterData[unit.classid].Undead); - let skillDamageInfo = this.allSkillDamage(unit); - - for (let sk in skillDamageInfo) { - if (this.buffs[sk]) { - if (typeof unit === "number") { - buffDmg[this.buffs[sk]] = 0; - buffDamageInfo[sk] = skillDamageInfo[sk]; - } - } else { - newSkillDamageInfo[sk] = skillDamageInfo[sk]; - } - } - - skillDamageInfo = newSkillDamageInfo; - - for (let sk in buffDamageInfo) { - // static field has a fix'd ceiling, calculated already - if ([sdk.skills.StaticField].indexOf(sk) !== -1) continue; - - let avgPDmg = (buffDamageInfo[sk].pmin + buffDamageInfo[sk].pmax) / 2; - let avgDmg = (buffDamageInfo[sk].min + buffDamageInfo[sk].max) / 2; - let tmpDmg = 0; - - if (avgPDmg > 0) { - let presist = this.monsterResist(unit, "Physical"); - - presist -= (presist >= 100 ? ampDmg / 5 : ampDmg); - presist = Math.max(-100, Math.min(100, presist)); - tmpDmg += avgPDmg * (100 - presist) / 100; - } - - if (avgDmg > 0 && (!isUndead || !buffDamageInfo[sk].undeadOnly) && sk !== sdk.skills.StaticField) { - let resist = this.monsterResist(unit, buffDamageInfo[sk].type); - let pierce = GameData.myReference.getStat(this.pierceMap[buffDamageInfo[sk].type]); - - if (this.convictionEligible[buffDamageInfo[sk].type]) { - resist -= (resist >= 100 ? conviction / 5 : conviction); - } - - if (resist < 100) { - resist = Math.max(-100, resist - pierce); - } else { - resist = 100; - } - - tmpDmg += avgDmg * (100 - resist) / 100; - } - - if (this.buffs[sk] === 1) { - buffDmg[this.buffs[sk]] += tmpDmg; - } else { - buffDmg[this.buffs[sk]] = Math.max(buffDmg[this.buffs[sk]], tmpDmg); - } - } - - buffDmg = buffDmg.reduce((t, v) => t + v, 0); - - for (let sk in skillDamageInfo) { - if (!this.ignoreSkill[sk]) { - let avgPDmg = (skillDamageInfo[sk].pmin + skillDamageInfo[sk].pmax) / 2, totalDmg = buffDmg; - let avgDmg = (skillDamageInfo[sk].min + skillDamageInfo[sk].max) / 2; - - if (avgPDmg > 0 && sk !== sdk.skills.StaticField) { - let presist = this.monsterResist(unit, "Physical"); - - presist -= (presist >= 100 ? ampDmg / 5 : ampDmg); - presist = Math.max(-100, Math.min(100, presist)); - totalDmg += avgPDmg * (100 - presist) / 100; - } - - if (avgDmg > 0 && (!isUndead || !skillDamageInfo[sk].undeadOnly)) { - let resist = this.monsterResist(unit, skillDamageInfo[sk].type); - let pierce = GameData.myReference.getStat(this.pierceMap[skillDamageInfo[sk].type]); - - if (this.convictionEligible[skillDamageInfo[sk].type]) { - resist -= (resist >= 100 ? conviction / 5 : conviction); - } - - if (resist < 100) { - resist = Math.max(-100, resist - pierce); - } else { - resist = 100; - } - - totalDmg += sk !== sdk.skills.StaticField && 0 || avgDmg * (100 - resist) / 100; - - } - - let tmpEffort = Math.ceil(hp / totalDmg); - - tmpEffort /= this.dmgModifier(sk | 0, unit); - - // care for mana - if (GameData.myReference.mp < Skill.getManaCost(sk)) { - tmpEffort *= 5; // More effort in a skill we dont have mana for - } - - // check valid location, blizzard and meteor fail over lava - if ([sdk.skills.Blizzard, sdk.skills.Meteor].indexOf(sk) && !Attack.validSpot(unit.x, unit.y, sk, unit.classid)) { - tmpEffort *= 5; - } - - if (tmpEffort <= eret.effort) { - eret.effort = tmpEffort; - eret.skill = sk | 0; - eret.type = skillDamageInfo[eret.skill].type; - eret.name = getSkillById(eret.skill); - eret.cooldown = this.skillCooldown(sk | 0); - allData.unshift(copyObj(eret)); - } - } - } - if (allData.length) return allData; - if (eret.skill >= 0) return eret; - return null; - }, - areaEffort: function (areaID, skills) { - let effortpool = 0, raritypool = 0, dmgAcc = 0; - - skills = skills || this.allSkillDamage(); - - AreaData[areaID].forEachMonsterAndMinion((mon, rarity, parent) => { - effortpool += rarity * this.monsterEffort(mon.Index, areaID, skills, parent && parent.Index).effort; - raritypool += rarity; - - dmgAcc += rarity * this.monsterAvgDmg(mon.Index, areaID); - }); - - // console.debug('avg dmg '+ AreaData[areaID].LocaleString+' -- ' + dmgAcc+' -- ' + avgDmg); - - return (raritypool ? effortpool / raritypool : Infinity); - }, - areaSoloExp: function (areaID, skills) { - let procentageBroke = ((100 - Math.min(100, Math.max(0, (100 / (Config.LowGold || 1) * me.gold))))); - let brokeness = 1 + (procentageBroke / 100 / 3 * 1); - let effortpool = 0, raritypool = 0, dmgAcc = 0; - - skills = skills || this.allSkillDamage(); - AreaData[areaID].forEachMonsterAndMinion((mon, rarity, parent) => { - effortpool += rarity * this.monsterExp(mon.Index, areaID) * this.levelModifier(GameData.myReference.charlvl, this.monsterLevel(mon.Index, areaID)) / this.monsterEffort(mon.Index, areaID, skills, parent && parent.Index).effort; - raritypool += rarity; - - dmgAcc += (rarity * this.monsterAvgDmg(mon.Index, areaID)); - }); - - let log = 1, avgDmg = 0; - if (brokeness !== 1) { - log = ((5 - Math.log(areaID)) * (brokeness * 0.6)); - avgDmg = (raritypool ? dmgAcc / raritypool : Infinity) * log; - } - - return (raritypool ? effortpool / raritypool : 0) - (avgDmg); - }, - mostUsedSkills: function (force = false) { - if (!force && GameData.myReference.hasOwnProperty("__cachedMostUsedSkills") && GameData.myReference.__cachedMostUsedSkills) return GameData.myReference.__cachedMostUsedSkills; - - const effort = [], uniqueSkills = []; - for (let i = 50; i < 120; i++) { - try { - effort.push(GameData.monsterEffort(i, sdk.areas.ThroneofDestruction)); - } catch (e) { - /*dontcare*/ - } - } - - effort - .filter(e => e !== null && typeof e === "object" && e.hasOwnProperty("skill")) - .filter(x => GameData.myReference.getSkill(x.skill, 0)) // Only skills where we have hard points in - .filter(x => Skills.class[x.skill] < 7) // Needs to be a skill of a class, not my class but a class - .map(x => - // Search for this unique skill - ( - uniqueSkills.find(u => u.skillId === x.skill) - // Or add it and return the value - || ( - ( - uniqueSkills.push({ skillId: x.skill, used: 0 }) - && false - ) - || uniqueSkills[uniqueSkills.length - 1] - ) - ).used++ && false - // In the end always return x - || x - ); - - return (GameData.myReference.__cachedMostUsedSkills = uniqueSkills.sort((a, b) => b.used - a.used)); - }, - - attackStartingFrame: function (weaponClass, charClass = GameData.myReference.classid) { - // amazon and sorceress only - /* - Weapon: hth 1hs 2hs 1ht 2ht stf bow xbw - StartingFrame: 1 2 2 2 2 2 0 0 - */ - if (charClass === sdk.player.class.Amazon || charClass === sdk.player.class.Sorceress) { - if (weaponClass === "hth") return 1; - if (["1hs", "2hs", "1ht", "2ht", "stf"].includes(weaponClass)) return 2; - } - return 0; - }, - - /*weaponSpeedModifier: function (weapon1Code, charClass = GameData.myReference.classid, weapon2Code = null) { - let weapons = new CSV("sdk/txt/weapons.txt"); - let weapon1Data = weapons.findObject("code", weapon1Code); - if (!weapon2Code) { - return weapon1Data.speed; - } - let weapon2Data = weapons.findObject("code", weapon2Code); - if (!weapon2Data) { - return weapon1Data.speed; - } - return (weapon1Data.speed + weapon2Data.speed) / 2; - },*/ - - attackModeForSkill: function (skillId, charClass = GameData.myReference.classid) { - //TODO: - if (skillId === sdk.skills.Smite) return "S1"; - /* - A1: - normal attack or attack skills like - "bow and crossbow" skills, energy strike, chain lightning strike, charged strike, - opposing tiger strike, cobra strike, phoenix strike - Slash, paralyze, concentrate, amok - barbarian rage, - mangle , fire claws , anger poison dagger - victim, zeal, revenge, conversion - - A2: - normal attack - - KK: kick (kick barrel) or assassin skills dragon claw, dragon tail - - S1: skill 1 - (evade, avoid, escape) - shield attack smite - - S2: skill 2 - stationary traps, fire blast , Shock net, blade guard - - S3: skill 3 - Secondary blow of the barbarian with dual weapons - Hunger, rabies - - S4: - Secondary blow of the assassin with dual claws - Secondary throw of the barbarian dual throwing - - TH: - Throw - poison throwing spear, lightning strike, plague throwing spit, flashing mischief - */ - return "A1"; - }, - - weaponAttackAnimationSpeed: function (baseRate, skill, weaponClass, charClass = GameData.myReference.classid, shiftState = null) { - /*if (shiftState == "bear") { - let framesPerDirection = this.weaponFramesPerDirection(skill, weaponClass, charClass); - let baseSpeed = this.weaponAttackAnimationSpeed(baseRate, skill, weaponClass, charClass); - let weaponIAS = 0; - let weaponSpeedModifier = 0; - let delay = baseRate * framesPerDirection / ((256 + weaponIAS - weaponSpeedModifier) * baseSpeed ​​/ 100); - return baseRate* - }*/ - //TODO: vampire form or werewolf - let attackMode = this.attackModeForSkill(skill, charClass); - switch (true) { - case charClass === sdk.player.class.Assassin && attackMode.startsWith("A") && weaponClass.startsWith("ht") && weaponClass !== "hth": - return 208; - case charClass === sdk.player.class.Assassin && attackMode === "S2": - return 128; - case charClass === sdk.player.class.Assassin && attackMode === "S4" && weaponClass === "ht2": - return 208; - } - // wolf or bear : - //AnimationSpeed ​​= [Hitshift * NeutralFrames / Delay] - return 256; - }, - - weaponFramesPerDirection: function (skill, weaponClass, charClass = GameData.myReference.classid) { - let attackMode = this.attackModeForSkill(skill, charClass); - /* - 2HT = “2 Hand Thrust” Spear - STF = “Staff” Staff, Large Axe, Maul, Pole arm - 2HS = “2 Hand Swing” 2-Handed Sword - BOW = “Bow” Bow - XBW = “Crossbow” Crossbow - HT1 = “One Hand-to-Hand” Shield + Claws - HT2 = “”Two Hand-to-Hand” Claws + Claws - 1HT = “1 Hand Thrust” Shield + (Throwing potion, Knife, Throwing Knife, Javelin) - 1HS = “1 Hand Swing” Shield + (Axe, Wand, Club, Scepter, Mace, Hammer, Sword, Throwing Axe, Orb) - HTH = “Hand To Hand” Shield + no weapon - 1SS = “Left Swing Right Swing” Left = 1HS, Right = 1HS - 1JT = “Left Jab Right Thrust” Left = 1HT, Right = 1HT - 1ST = “Left Swing Right Thrust” Left = 1HS, Right = 1HT - 1JS = “Left Jab Right Swing” Left = 1HT, Right = 1HS - */ - - /* - Amazon Assassin Barbarian Druid Necromancer Paladin Sorceress - A1 HTH 08 13 256 06 11 256 06 12 256 08 16 256 08 15 256 07 14 256 09 16 256 - A2 HTH --- 06 12 256 --- --- --- --- 08 16 256 - A1 HTx 06 11 208 - A2 HTx 06 12 208 - A1 1HS 10 16 256 07 15 256 07 16 256 09 19 256 09 19 256 07 15 256 12 20 256 - A1 2HS 12 20 256 11 23 256 08 18 256 10 21 256 11 23 256 08 18 256 14 24 256 - A2 2HS --- --- --- --- --- 08 19 256 --- - A1 1HT 09 15 256 07 15 256 07 16 256 08 19 256 09 19 256 08 17 256 11 19 256 - A1 2HT 11 18 256 10 23 256 09 19 256 09 23 256 10 24 256 08 20 256 13 23 256 - A2 2HT --- --- --- --- --- 09 20 256 --- - A1 STF 12 20 256 09 19 256 09 19 256 09 17 256 11 20 256 09 18 256 11 18 256 - A1 BOW 06 14 256 07 16 256 07 15 256 08 16 256 09 18 256 08 16 256 09 17 256 - A1 XBW 09 20 256 10 21 256 10 20 256 10 20 256 11 20 256 10 20 256 11 20 256 - - TH xxx 09 16 256 07 16 256 08 16 256 08 18 256 10 20 256 08 16 256 10 20 256 - KK xxx 04 13 256 - - S1 xxx xx 09 256 07 12 256 - S2 xxx 04 08 128 - S3 1Jx 08 12 256 - S3 1Sx 07 12 256 - S4 1Jx 08 16 256 - S4 1Sx 09 16 256 - S4 HT2 06 12 208 - - -------------------------------------------------- ------------------------------------------------ - Werewolf bear fetish vampire - A1 xxx 07 13 xxx 07 12 xxx 08 12 256 09 14 176 - S3 xxx 06 10 xxx 06 10 xxx - NU xxx xx 09 xxx xx 10 xxx - - -------------------------------------------------- ------------------------------------------------ - Rogue City Guard Eisenwolf Barbarian Mercenary - A1 xxx 06 15 256 11 16 256 06 15 256 05/12 16 256 - */ - switch (true) { - case charClass === sdk.player.class.Sorceress && attackMode.startsWith("A") && weaponClass === "hth": - return 16; - case charClass === sdk.player.class.Sorceress && attackMode === "A1" && weaponClass === "1hs": - return 20; - case charClass === sdk.player.class.Sorceress && attackMode.startsWith("A") && weaponClass === "2hs": - return 24; - case charClass === sdk.player.class.Sorceress && attackMode === "A1" && weaponClass === "1ht": - return 19; - case charClass === sdk.player.class.Sorceress && attackMode.startsWith("A") && weaponClass === "2ht": - return 23; - case charClass === sdk.player.class.Sorceress && attackMode === "A1" && weaponClass === "stf": - return 18; - case charClass === sdk.player.class.Sorceress && attackMode === "A1" && weaponClass === "bow": - return 17; - case charClass === sdk.player.class.Sorceress && attackMode === "A1" && weaponClass === "xbw": - return 20; - case charClass === sdk.player.class.Sorceress && attackMode === "TH": - return 20; - - case charClass === sdk.player.class.Paladin && attackMode.startsWith("A") && weaponClass === "hth": - return 14; - case charClass === sdk.player.class.Paladin && attackMode === "A1" && weaponClass === "1hs": - return 15; - case charClass === sdk.player.class.Paladin && attackMode === "A1" && weaponClass === "2hs": - return 18; - case charClass === sdk.player.class.Paladin && attackMode === "A2" && weaponClass === "2hs": - return 19; - case charClass === sdk.player.class.Paladin && attackMode === "A1" && weaponClass === "1ht": - return 17; - case charClass === sdk.player.class.Paladin && attackMode === "A1" && weaponClass === "2ht": - return 20; - case charClass === sdk.player.class.Paladin && attackMode === "A2" && weaponClass === "2ht": - return 20; - case charClass === sdk.player.class.Paladin && attackMode === "A1" && weaponClass === "stf": - return 18; - case charClass === sdk.player.class.Paladin && attackMode === "A1" && weaponClass === "bow": - return 16; - case charClass === sdk.player.class.Paladin && attackMode === "A1" && weaponClass === "xbw": - return 20; - case charClass === sdk.player.class.Paladin && attackMode === "TH": - return 16; - case charClass === sdk.player.class.Paladin && attackMode === "S1": - return 12; - - //TODO: full trag oul set - case charClass === sdk.player.class.Necromancer && attackMode.startsWith("A") && weaponClass === "hth": - return 15; - case charClass === sdk.player.class.Necromancer && attackMode === "A1" && weaponClass === "1hs": - return 19; - case charClass === sdk.player.class.Necromancer && attackMode.startsWith("A") && weaponClass === "2hs": - return 23; - case charClass === sdk.player.class.Necromancer && attackMode === "A1" && weaponClass === "1ht": - return 19; - case charClass === sdk.player.class.Necromancer && attackMode.startsWith("A") && weaponClass === "2ht": - return 24; - case charClass === sdk.player.class.Necromancer && attackMode === "A1" && weaponClass === "stf": - return 20; - case charClass === sdk.player.class.Necromancer && attackMode === "A1" && weaponClass === "bow": - return 18; - case charClass === sdk.player.class.Necromancer && attackMode === "A1" && weaponClass === "xbw": - return 20; - case charClass === sdk.player.class.Necromancer && attackMode === "TH": - return 20; - - case this.shiftState() === "wolf" && attackMode === "A1": - return 13; - case this.shiftState() === "wolf" && attackMode === "S3": - return 10; - - case this.shiftState() === "bear" && attackMode === "A1": - return 12; - case this.shiftState() === "bear" && attackMode === "S3": - return 10; - - case charClass === sdk.player.class.Druid && attackMode.startsWith("A") && weaponClass === "hth": - return 16; - case charClass === sdk.player.class.Druid && attackMode === "A1" && weaponClass === "1hs": - return 19; - case charClass === sdk.player.class.Druid && attackMode.startsWith("A") && weaponClass === "2hs": - return 21; - case charClass === sdk.player.class.Druid && attackMode === "A1" && weaponClass === "1ht": - return 19; - case charClass === sdk.player.class.Druid && attackMode.startsWith("A") && weaponClass === "2ht": - return 23; - case charClass === sdk.player.class.Druid && attackMode === "A1" && weaponClass === "stf": - return 17; - case charClass === sdk.player.class.Druid && attackMode === "A1" && weaponClass === "bow": - return 16; - case charClass === sdk.player.class.Druid && attackMode === "A1" && weaponClass === "xbw": - return 20; - case charClass === sdk.player.class.Druid && attackMode === "TH": - return 18; - - case charClass === sdk.player.class.Barbarian && attackMode.startsWith("A") && weaponClass === "hth": - return 12; - case charClass === sdk.player.class.Barbarian && attackMode === "A1" && weaponClass === "1hs": - return 16; - case charClass === sdk.player.class.Barbarian && attackMode.startsWith("A") && weaponClass === "2hs": - return 18; - case charClass === sdk.player.class.Barbarian && attackMode === "A1" && weaponClass === "1ht": - return 16; - case charClass === sdk.player.class.Barbarian && attackMode.startsWith("A") && weaponClass === "2ht": - return 19; - case charClass === sdk.player.class.Barbarian && attackMode === "A1" && weaponClass === "stf": - return 19; - case charClass === sdk.player.class.Barbarian && attackMode === "A1" && weaponClass === "bow": - return 15; - case charClass === sdk.player.class.Barbarian && attackMode === "A1" && weaponClass === "xbw": - return 20; - case charClass === sdk.player.class.Barbarian && attackMode === "TH": - return 16; - case charClass === sdk.player.class.Barbarian && attackMode === "S3": - return 12; - case charClass === sdk.player.class.Barbarian && attackMode === "S4": - return 16; - - case charClass === sdk.player.class.Assassin && attackMode === "A1" && weaponClass.startsWith("ht"): - return 11; - case charClass === sdk.player.class.Assassin && attackMode === "A2" && weaponClass.startsWith("ht"): - return 12; - case charClass === sdk.player.class.Assassin && attackMode === "A1" && weaponClass === "1hs": - return 15; - case charClass === sdk.player.class.Assassin && attackMode.startsWith("A") && weaponClass === "2hs": - return 23; - case charClass === sdk.player.class.Assassin && attackMode === "A1" && weaponClass === "1ht": - return 15; - case charClass === sdk.player.class.Assassin && attackMode.startsWith("A") && weaponClass === "2ht": - return 23; - case charClass === sdk.player.class.Assassin && attackMode === "A1" && weaponClass === "stf": - return 19; - case charClass === sdk.player.class.Assassin && attackMode === "A1" && weaponClass === "bow": - return 16; - case charClass === sdk.player.class.Assassin && attackMode === "A1" && weaponClass === "xbw": - return 21; - case charClass === sdk.player.class.Assassin && attackMode === "TH": - return 16; - case charClass === sdk.player.class.Assassin && attackMode === "KK": - return 13; - case charClass === sdk.player.class.Assassin && attackMode === "S2": - return 8; - case charClass === sdk.player.class.Assassin && attackMode === "S4" && weaponClass === "ht2": - return 12; - - case charClass === sdk.player.class.Amazon && attackMode.startsWith("A") && weaponClass === "hth": - return 13; - case charClass === sdk.player.class.Amazon && attackMode === "A1" && weaponClass === "1hs": - return 16; - case charClass === sdk.player.class.Amazon && attackMode.startsWith("A") && weaponClass === "2hs": - return 20; - case charClass === sdk.player.class.Amazon && attackMode === "A1" && weaponClass === "1ht": - return 15; - case charClass === sdk.player.class.Amazon && attackMode.startsWith("A") && weaponClass === "2ht": - return 18; - case charClass === sdk.player.class.Amazon && attackMode === "A1" && weaponClass === "stf": - return 20; - case charClass === sdk.player.class.Amazon && attackMode === "A1" && weaponClass === "bow": - return 14; - case charClass === sdk.player.class.Amazon && attackMode === "A1" && weaponClass === "xbw": - return 20; - case charClass === sdk.player.class.Amazon && attackMode === "TH": - return 16; - case charClass === sdk.player.class.Amazon && attackMode === "S1": - return 9; - } - return -1; - }, - - // attackFrames: function (skillId, weaponCode, ias = GameData.myReference.getStat(sdk.stats.Fasterattackrate), charClass = GameData.myReference.classid, weapon2Code = null) { - // // https://diablo3.ingame.de/forum/threads/1218516-FAQ-Bewegungs-und-Animationsgeschwindigkeiten-Teil-2?s=&postid=17610874 - // /* - // TODO - // bear or wolf only : - - // frames = {(256 * framesPerDirection) / [animationSpeed ​​* (100 + effectiveIAS + skillsIAS - weaponSpeedModifier + coldEffect) / 100]} - 1 - - // where : - // animationSpeed ​​= 256 * NeutralFrames / delay - // delay = 256 * CharFrames / ((100 + weaponIAS - weaponSpeedModifier) * CharSpeed ​​/ 100) - - // framesPerDirection ... The sum of all frames of our attack animation of the Werform. - // NeutralFrames ... Sum of the frames of our neutral animation (use while standing). - // CharFrames ... The sum of all frames of the attack animation that we would use in the unchanged state (with the exception of two-handed swords). - // CharSpeed ​​... This is the animation speed of the attack animation that the unchanged character would use. - // weaponIAS ... All IAS on our weapon or weapon base - // */ - // let weaponData = (new CSV("sdk/txt/weapons.txt")).findObject("code", weaponCode); - // if (!weaponData) { - // console.log(sdk.colors.Orange + "No weapon data found for code " + weaponCode); - // } - // let weaponClass = weaponData.wclass; - // let baseRate = 100; - // const BASE_ANIMATION_SPEED = 256; - - // let animationSpeed = this.weaponAttackAnimationSpeed(baseRate, weaponClass, charClass, this.shiftState()); - // let effectiveIAS = 120 * ias / (120 + ias); - // let skillsIAS = 0; //TODO: fanaticism or other sills bonus + slowdown skills malus - // let weaponSpeedModifier = (typeof weaponData.speed == "string") ? isNaN(parseInt(weaponData.speed)) ? 0 : parseInt(weaponData.speed) : weaponData.speed;// this.weaponSpeedModifier(weaponCode, charClass, weapon2Code); - // // me.getState(sdk.states.Frozen) or me.getState(sdk.states.Cold) ? - // let coldEffect = GameData.myReference.getState(sdk.states.Frozen) ? -50 : 0; // If we are affected by cold, as a player we receive a penalty of 50. - // let acceleration = baseRate + effectiveIAS + skillsIAS - weaponSpeedModifier + coldEffect; - // acceleration = Math.min(175, Math.max(15, acceleration)); - // let startingFrame = this.attackStartingFrame(weaponClass, charClass); - // let framesPerDirection = this.weaponFramesPerDirection(skillId, weaponClass, charClass); - // if (framesPerDirection < 1) { - // console.log(sdk.colors.Orange + "wrong value for framesPerDirection, IAS calculation may be wrong"); - // } - - // console.log("skillId " + skillId); - // console.log("charClass " + charClass); - // console.log("weaponCode " + weaponCode); - // console.log("weaponClass " + weaponClass); - // console.log("ias " + ias); - // console.log("effectiveIAS " + effectiveIAS); - // console.log("skillsIAS " + skillsIAS); - // console.log("weaponSpeedModifier " + weaponSpeedModifier); - // console.log("coldEffect " + coldEffect); - // console.log("acceleration " + acceleration); - // console.log("startingFrame " + startingFrame); - // console.log("framesPerDirection " + framesPerDirection); - // let frames = Math.ceil(BASE_ANIMATION_SPEED * (framesPerDirection - startingFrame) / Math.floor(animationSpeed * acceleration / 100)) - 1; - // return frames; - // }, - - // attackDuration: function (skillId, weaponCode, ias = GameData.myReference.getStat(sdk.stats.Fasterattackrate), charClass = GameData.myReference.classid, weapon2Code = null) { - // // https://diablo3.ingame.de/forum/threads/1218516-FAQ-Bewegungs-und-Animationsgeschwindigkeiten-Teil-2?s=&postid=17610874 - // return this.attackFrames(skillId, weaponCode, ias, charClass) / 25; - // }, - timeTillMissleImpact: function (skillId, monster) { - if (monster === undefined || skillId === undefined || !monster.attackable) return 0; - let missileName = getBaseStat("skills", skillId, "cltmissile"); - let missile = MissileData[missileName]; - if (!missile) { - missileName = getBaseStat("skills", skillId, "srvmissile"); - missile = MissileData[missileName]; - } - if (missile && missile.velocity > 0) { - const missileVelocityTPS = missile.velocity; - const missileVelocityTPF = missileVelocityTPS / 25; - const distanceForMissile = getDistance(me, monster); - // too far for missile to reach this position - if (distanceForMissile > missile.range) return 0; - const castTimeS = me.castingDuration(skillId); - return ((distanceForMissile / ((missileVelocityTPS / 32) * 25)) + castTimeS); - } - return 0; - } - }; - - function calculateKillableFallensByFrostNova() { - if (!Skill.canUse(sdk.skills.FrostNova)) return 0; - let fallens = [sdk.monsters.Fallen, sdk.monsters.Carver2, sdk.monsters.Devilkin2, sdk.monsters.DarkOne1, sdk.monsters.WarpedFallen, sdk.monsters.Carver1, sdk.monsters.Devilkin, sdk.monsters.DarkOne2]; - let area = me.area; - return getUnits(sdk.unittype.Monster) - .filter(unit => !!unit && fallens.includes(unit.classid) && unit.distance < 7) - .filter(function (unit) { - return unit.attackable - && typeof unit.x === "number" // happens if monster despawns - && !checkCollision(me, unit, Coords_1.Collision.BLOCK_MISSILE) - && unit.getStat(sdk.stats.ColdResist) < 100; - //&& !unit.getState(sdk.states.Frozen); - }) - .reduce(function (acc, cur) { - let classId = cur.classid, minDmg = GameData.skillDamage(sdk.skills.FrostNova, cur).min; - //let charLvl = GameData.monsterLevel(classId, area); - let currentHealth = GameData.monsterMaxHP(classId, area, cur.charlvl - GameData.monsterLevel(classId, area)) / 100 * (cur.hp * 100 / cur.hpmax); - if (currentHealth < minDmg) { - acc++; - } - return acc; - }, 0); - } - - function calculateKillableSummonsByNova() { - if (!Skill.canUse(sdk.skills.Nova)) return 0; - let summons = [ - sdk.monsters.Fallen, sdk.monsters.Carver2, sdk.monsters.Devilkin2, sdk.monsters.DarkOne1, sdk.monsters.WarpedFallen, sdk.monsters.Carver1, sdk.monsters.Devilkin, sdk.monsters.DarkOne2, - sdk.monsters.BurningDead, sdk.monsters.Returned1, sdk.monsters.Returned2, sdk.monsters.BoneWarrior1, sdk.monsters.BoneWarrior2 - ]; - return getUnits(sdk.unittype.Monster) - .filter(unit => !!unit && summons.includes(unit.classid) && unit.distance < 7) - .filter(function (unit) { - return unit.attackable - && typeof unit.x === "number" // happens if monster despawns - && !checkCollision(me, unit, Coords_1.Collision.BLOCK_MISSILE) - && Attack.checkResist(unit, "lightning"); - }) - .reduce(function (acc, cur) { - let classId = cur.classid, areaId = cur.area, minDmg = GameData.skillDamage(sdk.skills.Nova, cur).min; - let currentHealth = GameData.monsterMaxHP(classId, areaId, cur.charlvl - GameData.monsterLevel(classId, areaId)) / 100 * (cur.hp * 100 / cur.hpmax); - if (currentHealth < minDmg) { - acc++; - } - return acc; - }, 0); - } - - Object.defineProperty(Unit.prototype, "currentVelocity", { - get: function () { - if (!this.isMoving || this.isFrozen) return 0; - const velocity = this.isRunning ? MonsterData[this.classid].Run : MonsterData[this.classid].Velocity; - if (this.isChilled) { - let malus = MonsterData[this.classid].ColdEffect; - (malus > 0) && (malus = malus - 256); - return Math.max(1, ~~(velocity * (1 + malus))); - } - return velocity; - } - }); - - function targetPointForSkill (skillId, monster) { - if (monster === undefined || skillId === undefined || !monster.attackable) return null; - let missileName = getBaseStat("skills", skillId, "cltmissile"); - let missile = MissileData[missileName]; - if (!missile) { - missileName = getBaseStat("skills", skillId, "srvmissile"); - missile = MissileData[missileName]; - } - if (missile && missile.velocity > 0) { - if (monster.isMoving && (monster.targetx !== me.x || monster.targety !== me.y)) { - let startX = monster.x, startY = monster.y; - // tiles per second velocities - // ToDo: is monster slowed by freeze or something ? - let monsterVelocityTPS = monster.currentVelocity; - let missileVelocityTPS = missile.velocity; - // tiles per frame velocities - let monsterVelocityTPF = monsterVelocityTPS / 25; - let missileVelocityTPF = missileVelocityTPS / 25; - //console.log("monster is moving to "+monster.targetx+", "+monster.targety + " at speed "+monsterVelocity); - let path = getPath(monster.area, startX, startY, monster.targetx, monster.targety, 2, 1); - if (path && path.length) { - // path is reversed from target to monster, we will check from last path position (target) to monster position - path.reverse(); - let [diffS, diffF, found] = [0, 0, 0]; - let time = { missile: {}, monster: {} }; - for (let i = 0; i < path.length; i++) { - let pos = path[i]; - // ToDo : does missile spawn at me position ? - let distanceForMissile = getDistance(me, pos); - if (distanceForMissile > missile.range) { - // too far for missile to reach this position - continue; - } - let distanceForMonster = getDistance({ x: startX, y: startY }, pos); - let timeForMonsterF = distanceForMonster / monsterVelocityTPF; - // time in seconds - // let castTimeS = GameData.castingDuration(skillId); - // let timeForMissileS = distanceForMissile / missileVelocityTPS + castTimeS; - // time in frames - let castTimeF = me.castingFrames(skillId); - let timeForMissileF = distanceForMissile / missileVelocityTPF + castTimeF; - // let timeForMonsterS = distanceForMonster / monsterVelocityTPS; - // Todo: missile and monster size - // diff seconds - // diffS = timeForMissileS-timeForMonsterS; - // diff frames - diffF = timeForMissileF - timeForMonsterF; - // diff > 0 : missile will reach pos after monster - // diff < 0 : missile will reach pos before monster - // console.log("time for monster to reach "+pos+" = "+timeForMonster); - // console.log("time for missile to reach "+pos+" = "+timeForMissile); - // console.log("diff = "+diff) - if (i === 0 && diffF >= 0) { - // last path position and missile is late, we can't predict next monster target, shoot at last path position - // it may fail because monster may be moving at other target while missile is arriving - // console.log("missile will be too late"); - found = pos; - // time.missile.seconds = timeForMissileS; - time.missile.frames = timeForMissileF; - // time.monster.seconds = timeForMonsterS; - time.monster.frames = timeForMonsterF; - break; - } - // the number of frames needed for unit to move 1 tile - let timeToMoveOneTileMonsterF = 1 / monsterVelocityTPF; - // let timeToMoveOneTileMissileF = 1 / missileVelocityTPF; - // while missile is travelling, monster will continue to move - // if the difference is greater than the time a monster will move 1 tile, the missile will miss - // todo: monster size, missile size - if (diffF >= -1 * timeToMoveOneTileMonsterF && diffF <= 1 * timeToMoveOneTileMonsterF) { - found = pos; - // time.missile.seconds = timeForMissileS; - time.missile.frames = timeForMissileF; - // time.monster.seconds = timeForMonsterS; - time.monster.frames = timeForMonsterF; - break; - } - } - if (found) { - // console.log("missile will hit monster in "+time.missile.seconds+" ("+time.missile.frames+") at "+found.x+", "+found.y); - // console.log("time for monster = "+time.monster.seconds+ " ("+time.monster.frames+")") - // console.log("diff missile-monster = "+diffS+ " ("+diffF+")"); - return found; - } - } - } - } - return null; - } - - // Export data - GameData.isEnemy = isEnemy; - GameData.isAlive = isAlive; - GameData.onGround = onGround; - GameData.calculateKillableFallensByFrostNova = calculateKillableFallensByFrostNova; - GameData.calculateKillableSummonsByNova = calculateKillableSummonsByNova; - GameData.targetPointForSkill = targetPointForSkill; - module.exports = GameData; + const MonsterData = require("./MonsterData"); + const AreaData = require("./AreaData"); + const MissileData = require("./MissileData"); + const Coords_1 = require("../Coords"); + const sdk = require("../../../modules/sdk"); + + function isAlive(unit) { + return Boolean(unit && unit.hp); + } + + function isEnemy(unit) { + return Boolean(unit && isAlive(unit) && unit.getStat(sdk.stats.Alignment) !== 2 && typeof unit.classid === "number" && MonsterData[unit.classid].Killable); + } + + function onGround(item) { + return item.onGroundOrDropping; + } + + const GameData = { + myReference: me, + townAreas: [0, 1, 40, 75, 103, 109], + HPLookup: [["1", "1", "1"], ["7", "107", "830"], ["9", "113", "852"], ["12", "120", "875"], ["15", "125", "897"], ["17", "132", "920"], ["20", "139", "942"], ["23", "145", "965"], ["27", "152", "987"], ["31", "157", "1010"], ["35", "164", "1032"], ["36", "171", "1055"], ["40", "177", "1077"], ["44", "184", "1100"], ["48", "189", "1122"], ["52", "196", "1145"], ["56", "203", "1167"], ["60", "209", "1190"], ["64", "216", "1212"], ["68", "221", "1235"], ["73", "228", "1257"], ["78", "236", "1280"], ["84", "243", "1302"], ["89", "248", "1325"], ["94", "255", "1347"], ["100", "261", "1370"], ["106", "268", "1392"], ["113", "275", "1415"], ["120", "280", "1437"], ["126", "287", "1460"], ["134", "320", "1482"], ["142", "355", "1505"], ["150", "388", "1527"], ["158", "423", "1550"], ["166", "456", "1572"], ["174", "491", "1595"], ["182", "525", "1617"], ["190", "559", "1640"], ["198", "593", "1662"], ["206", "627", "1685"], ["215", "661", "1707"], ["225", "696", "1730"], ["234", "729", "1752"], ["243", "764", "1775"], ["253", "797", "1797"], ["262", "832", "1820"], ["271", "867", "1842"], ["281", "900", "1865"], ["290", "935", "1887"], ["299", "968", "1910"], ["310", "1003", "1932"], ["321", "1037", "1955"], ["331", "1071", "1977"], ["342", "1105", "2000"], ["352", "1139", "2030"], ["363", "1173", "2075"], ["374", "1208", "2135"], ["384", "1241", "2222"], ["395", "1276", "2308"], ["406", "1309", "2394"], ["418", "1344", "2480"], ["430", "1379", "2567"], ["442", "1412", "2653"], ["454", "1447", "2739"], ["466", "1480", "2825"], ["477", "1515", "2912"], ["489", "1549", "2998"], ["501", "1583", "3084"], ["513", "1617", "3170"], ["525", "1651", "3257"], ["539", "1685", "3343"], ["552", "1720", "3429"], ["565", "1753", "3515"], ["579", "1788", "3602"], ["592", "1821", "3688"], ["605", "1856", "3774"], ["618", "1891", "3860"], ["632", "1924", "3947"], ["645", "1959", "4033"], ["658", "1992", "4119"], ["673", "2027", "4205"], ["688", "2061", "4292"], ["702", "2095", "4378"], ["717", "2129", "4464"], ["732", "2163", "4550"], ["746", "2197", "4637"], ["761", "2232", "4723"], ["775", "2265", "4809"], ["790", "2300", "4895"], ["805", "2333", "4982"], ["821", "2368", "5068"], ["837", "2403", "5154"], ["853", "2436", "5240"], ["868", "2471", "5327"], ["884", "2504", "5413"], ["900", "2539", "5499"], ["916", "2573", "5585"], ["932", "2607", "5672"], ["948", "2641", "5758"], ["964", "2675", "5844"], ["982", "2709", "5930"], ["999", "2744", "6017"], ["1016", "2777", "6103"], ["1033", "2812", "6189"], ["1051", "2845", "6275"], ["1068", "2880", "6362"], ["1085", "2915", "6448"], ["1103", "2948", "6534"], ["1120", "2983", "6620"], ["1137", "3016", "6707"], ["10000", "10000", "10000"]], + monsterLevel: function (monsterID, areaID) { + return me.diff ? AreaData.hasOwnProperty(areaID) && AreaData[areaID].Level : MonsterData.hasOwnProperty(monsterID) && MonsterData[monsterID].Level; // levels on nm/hell are determined by area, not by monster data + }, + monsterExp: function (monsterID, areaID, adjustLevel = 0) { + return Experience.monsterExp[Math.min(Experience.monsterExp.length - 1, this.monsterLevel(monsterID, areaID) + adjustLevel)][me.diff] * MonsterData[monsterID].ExperienceModifier / 100; + }, + eliteExp: function (monsterID, areaID) { + return this.monsterExp(monsterID, areaID, 2) * 3; + }, + monsterAvgHP: function (monsterID, areaID, adjustLevel = 0) { + return this.HPLookup[Math.min(this.HPLookup.length - 1, this.monsterLevel(monsterID, areaID) + adjustLevel)][me.diff] * (getBaseStat("monstats", monsterID, "minHP") + getBaseStat("monstats", monsterID, "maxHP")) / 200; + }, + monsterMaxHP: function (monsterID, areaID, adjustLevel = 0) { + return this.HPLookup[Math.min(this.HPLookup.length - 1, this.monsterLevel(monsterID, areaID) + adjustLevel)][me.diff] * getBaseStat("monstats", monsterID, "maxHP") / 100; + }, + eliteAvgHP: function (monsterID, areaID) { + return (6 - me.diff) / 2 * this.monsterAvgHP(monsterID, areaID, 2); + }, + monsterDamageModifier: function () { + return 1 + (this.multiplayerModifier() - 1) * 0.0625; + }, + monsterMaxDmg: function (monsterID, areaID, adjustLevel = 0) { + let level = this.monsterLevel(monsterID, areaID) + adjustLevel; + return Math.max.apply(null, [MonsterData[monsterID].Attack1MaxDmg, MonsterData[monsterID].Attack2MaxDmg, MonsterData[monsterID].Skill1MaxDmg]) * level / 100 * this.monsterDamageModifier(); + }, + // https://www.diabloii.net/forums/threads/monster-damage-increase-per-player-count.570346/ + monsterAttack1AvgDmg: function (monsterID, areaID, adjustLevel = 0) { + let level = this.monsterLevel(monsterID, areaID) + adjustLevel; + return ((MonsterData[monsterID].Attack1MinDmg + MonsterData[monsterID].Attack1MaxDmg) / 2) * level / 100 * this.monsterDamageModifier(); + }, + monsterAttack2AvgDmg: function (monsterID, areaID, adjustLevel = 0) { + let level = this.monsterLevel(monsterID, areaID) + adjustLevel; + return ((MonsterData[monsterID].Attack2MinDmg + MonsterData[monsterID].Attack2MaxDmg) / 2) * level / 100 * this.monsterDamageModifier(); + }, + monsterSkill1AvgDmg: function (monsterID, areaID, adjustLevel = 0) { + let level = this.monsterLevel(monsterID, areaID) + adjustLevel; + return ((MonsterData[monsterID].Skill1MinDmg + MonsterData[monsterID].Skill1MaxDmg) / 2) * level / 100 * this.monsterDamageModifier(); + }, + monsterAvgDmg: function (monsterID, areaID, adjustLevel = 0) { + let attack1 = this.monsterAttack1AvgDmg(monsterID, areaID, adjustLevel); + let attack2 = this.monsterAttack2AvgDmg(monsterID, areaID, adjustLevel); + let skill1 = this.monsterSkill1AvgDmg(monsterID, areaID, adjustLevel); + let dmgs = [attack1, attack2, skill1].filter(x => x > 0); + // ignore 0 dmg to avoid reducing average + if (!dmgs.length) return 0; + return dmgs.reduce((acc, v) => acc + v) / dmgs.length; + }, + averagePackSize: monsterID => (MonsterData[monsterID].GroupCount.Min + MonsterData[monsterID].MinionCount.Min + MonsterData[monsterID].GroupCount.Max + MonsterData[monsterID].MinionCount.Max) / 2, + areaLevel: function (areaID) { + // levels on nm/hell are determined by area, not by monster data + if (me.diff) return AreaData[areaID].Level; + + let levels = 0, total = 0; + + AreaData[areaID].forEachMonsterAndMinion((mon, rarity) => { + levels += mon.Level * rarity; + total += rarity; + }); + + return Math.round(levels / total); + }, + areaImmunities: function (areaID) { + let resists = { Physical: 0, Magic: 0, Fire: 0, Lightning: 0, Cold: 0, Poison: 0 }; + + AreaData[areaID].forEachMonsterAndMinion(mon => { + for (let k in resists) { + resists[k] = Math.max(resists[k], mon[k]); + } + }); + + return Object.keys(resists).filter(key => resists[key] >= 100); + }, + levelModifier: function (clvl, mlvl) { + let bonus; + + if (clvl < 25 || mlvl < clvl) { + bonus = Experience.expCurve[Math.min(20, Math.max(0, Math.floor(mlvl - clvl + 10)))] / 255; + } else { + bonus = clvl / mlvl; + } + + return bonus * Experience.expPenalty[Math.min(30, Math.max(0, Math.round(clvl - 69)))] / 1024; + }, + multiplayerModifier: function (count) { + if (!count) { + let party = getParty(GameData.myReference); + if (!party) return 1; + + count = 1; + + while (party.getNext()) { + count++; + } + } + + return (count + 1) / 2; + }, + partyModifier: function (playerID) { + let party = getParty(GameData.myReference), level = 0, total = 0; + if (!party) return 1; + + let partyid = party.partyid; + + do { + if (party.partyid === partyid) { + total += party.level; + + if (playerID === party.name || playerID === party.gid) { + level = party.level; + } + } + } while (party.getNext()); + + return level / total; + }, + killExp: function (playerID, monsterID, areaID) { + let exp = this.monsterExp(monsterID, areaID), party = getParty(GameData.myReference); + if (!party) return 0; + + let level = 0, total = 0; + let gamesize = 0; + let partyid = party.partyid; + + do { + gamesize++; + + if (party.partyid === partyid) { + total += party.level; + + if (playerID === party.name || playerID === party.gid) { + level = party.level; + } + } + } while (party.getNext()); + + return Math.floor(exp * this.levelModifier(level, this.monsterLevel(monsterID, areaID)) * this.multiplayerModifier(gamesize) * level / total); + }, + baseLevel: function (...skillIDs) { + return skillIDs.reduce((total, skillID) => total + GameData.myReference.getSkill(skillID, 0), 0); + }, + skillLevel: function (...skillIDs) { + return skillIDs.reduce((total, skillID) => total + GameData.myReference.getSkill(skillID, 1), 0); + }, + skillCooldown: function (skillID) { + return getBaseStat("Skills", skillID, "delay") !== -1; + }, + stagedDamage: function (l, a, b, c, d, e, f, hitshift = 0, mult = 1) { + if (l > 28) { + a += f * (l - 28); + l = 28; + } + + if (l > 22) { + a += e * (l - 22); + l = 22; + } + + if (l > 16) { + a += d * (l - 16); + l = 16; + } + + if (l > 8) { + a += c * (l - 8); + l = 8; + } + + a += b * (Math.max(0, l) - 1); + + return (mult * a) << hitshift; + }, + damageTypes: ["Physical", "Fire", "Lightning", "Magic", "Cold", "Poison", "?", "?", "?", "Physical"], // 9 is Stun, but stun isn't an element + synergyCalc: { // TODO: add melee skill damage and synergies - they are poop + + // sorc fire spells + 36: [47, 0.16, 56, 0.16], // fire bolt + 41: [37, 0.13], // inferno + 46: [37, 0.04, 51, 0.01], // blaze + 47: [36, 0.14, 56, 0.14], // fire ball + 51: [37, 0.04, 41, 0.01], // fire wall + 52: [37, 0.09], // enchant + 56: [36, 0.05, 47, 0.05], // meteor + 62: [36, 0.03, 47, 0.03], // hydra + + // sorc lightning spells + 38: [49, 0.06], // charged bolt + 49: [38, 0.08, 48, 0.08, 53, 0.08], // lightning + 53: [38, 0.04, 48, 0.04, 49, 0.04], // chain lightning + + // sorc cold spells + 39: [44, 0.15, 45, 0.15, 55, 0.15, 59, 0.15, 64, 0.15], // ice bolt + 44: [59, 0.10, 64, 0.10], // frost nova + 45: [39, 0.08, 59, 0.08, 64, 0.08], // ice blast + 55: [39, 0.05, 45, 0.05, 64, 0.05], // glacial spike + 59: [39, 0.05, 45, 0.05, 55, 0.05], // blizzard + 64: [39, 0.02], // frozen orb + + // assassin traps + 251: [256, 0.09, 261, 0.09, 262, 0.09, 271, 0.09, 272, 0.09, 276, 0.09], // fireblast + 256: [261, 0.11, 271, 0.11, 276, 0.11], // shock web + 261: [251, 0.06, 271, 0.06, 276, 0.06], // charged bolt sentry + 262: [251, 0.08, 272, 0.08], // wake of fire sentry + 271: [256, 0.12, 261, 0.12, 276, 0.12], // lightning sentry + 272: [251, 0.10, 276, 0.10, 262, 0.07], // inferno sentry + 276: [271, 0.12], // death sentry + + // necro bone spells + 67: [78, 0.15, 84, 0.15, 88, 0.15, 93, 0.15], // teeth + 73: [83, 0.20, 92, 0.20], // poison dagger + 83: [73, 0.15, 92, 0.15], // poison explosion + 84: [67, 0.07, 78, 0.07, 88, 0.07, 93, 0.07], // bone spear + 92: [73, 0.10, 83, 0.10], // poison nova + 93: [67, 0.06, 78, 0.06, 84, 0.06, 88, 0.06], // bone spirit + + // barb war cry + 154: [130, 0.06, 137, 0.06, 146, 0.06], // war cry + + // paladin combat spells + 101: [112, 0.50, 121, 0.50], // holy bolt + 112: [108, 0.14, 115, 0.14], // blessed hammer + 121: [118, 0.07], // fist of heavens + + // paladin auras + 102: [100, 0.18, 125, 0.06], // holy fire + 114: [105, 0.15, 125, 0.07], // holy freeze + 118: [110, 0.12, 125, 0.04], // holy shock + + // durid elemental skills + 225: [229, 0.23, 234, 0.23], // firestorm + 229: [244, 0.10, 225, 0.08], // molten boulder + 234: [225, 0.12, 244, 0.12], // fissure (eruption) + 244: [229, 0.12, 234, 0.12, 249, 0.12], // volcano + 249: [225, 0.14, 229, 0.14, 244, 0.14], // armageddon + 230: [250, 0.15, 235, 0.15], // arctic blast + 240: [245, 0.10, 250, 0.10], // twister + 245: [235, 0.09, 240, 0.09, 250, 0.09], // tornado + 250: [240, 0.09, 245, 0.09], // hurricane + + // durid feral skills + 238: [222, 0.18], // rabies + 239: [225, 0.22, 229, 0.22, 234, 0.22, 244, 0.22], // fire claws + + // amazon bow/xbow skills + 11: [21, 0.12], // cold arrow + 21: [11, 0.08], // ice arrow + 31: [11, 0.12], // freezing arrow + 7: [16, 0.12], // fire arrow + 16: [7, 0.12], // exploding arrow + 27: [16, 0.10], // immolation arrow + + // amazon spear/javalin skills + 14: [20, 0.10, 24, 0.10, 34, 0.10, 35, 0.10], // power strike + 20: [14, 0.03, 24, 0.03, 34, 0.03, 35, 0.03], // lightning bolt + 24: [14, 0.10, 20, 0.10, 34, 0.10, 35, 0.10], // charged strike + 34: [14, 0.08, 20, 0.08, 24, 0.10, 35, 0.10], // lightning strike + 35: [14, 0.01, 20, 0.01, 24, 0.01, 34, 0.01], // lightning fury + 15: [25, 0.12], // poison javalin + 25: [15, 0.10], // plague javalin + }, + noMinSynergy: [14, 20, 24, 34, 35, 49, 53, 118, 256, 261, 271, 276], + skillMult: { + 15: 25, + 25: 25, + 41: 25, + 46: 75, + 51: 75, + 73: 25, + 83: 25, + 92: 25, + 222: 25, + 225: 75, + 230: 25, + 238: 25, + 272: 25 / 3 + }, + baseSkillDamage: function (skillID) { // TODO: rework skill damage to use both damage fields + let l = this.skillLevel(skillID), m = this.skillMult[skillID] || 1; + let dmgFields = [["MinDam", "MinLevDam1", "MinLevDam2", "MinLevDam3", "MinLevDam4", "MinLevDam5", "MaxDam", "MaxLevDam1", "MaxLevDam2", "MaxLevDam3", "MaxLevDam4", "MaxLevDam5"], ["EMin", "EMinLev1", "EMinLev2", "EMinLev3", "EMinLev4", "EMinLev5", "EMax", "EMaxLev1", "EMaxLev2", "EMaxLev3", "EMaxLev4", "EMaxLev5"]]; + + if (skillID === 70) { + return { + type: "Physical", + pmin: this.stagedDamage(l, getBaseStat("skills", skillID, dmgFields[1][0]), getBaseStat("skills", skillID, dmgFields[1][1]), getBaseStat("skills", skillID, dmgFields[1][2]), getBaseStat("skills", skillID, dmgFields[1][3]), getBaseStat("skills", skillID, dmgFields[1][4]), getBaseStat("skills", skillID, dmgFields[1][5]), getBaseStat("skills", skillID, "HitShift"), m), + pmax: this.stagedDamage(l, getBaseStat("skills", skillID, dmgFields[1][0]), getBaseStat("skills", skillID, dmgFields[1][1]), getBaseStat("skills", skillID, dmgFields[1][2]), getBaseStat("skills", skillID, dmgFields[1][3]), getBaseStat("skills", skillID, dmgFields[1][4]), getBaseStat("skills", skillID, dmgFields[1][5]), getBaseStat("skills", skillID, "HitShift"), m), + min: 0, max: 0 + }; + } else { + let type = getBaseStat("skills", skillID, "EType"); + + return { + type: this.damageTypes[type], + pmin: this.stagedDamage(l, getBaseStat("skills", skillID, dmgFields[0][0]), getBaseStat("skills", skillID, dmgFields[0][1]), getBaseStat("skills", skillID, dmgFields[0][2]), getBaseStat("skills", skillID, dmgFields[0][3]), getBaseStat("skills", skillID, dmgFields[0][4]), getBaseStat("skills", skillID, dmgFields[0][5]), getBaseStat("skills", skillID, "HitShift"), m), + pmax: this.stagedDamage(l, getBaseStat("skills", skillID, dmgFields[0][6]), getBaseStat("skills", skillID, dmgFields[0][7]), getBaseStat("skills", skillID, dmgFields[0][8]), getBaseStat("skills", skillID, dmgFields[0][9]), getBaseStat("skills", skillID, dmgFields[0][10]), getBaseStat("skills", skillID, dmgFields[0][11]), getBaseStat("skills", skillID, "HitShift"), m), + min: type ? this.stagedDamage(l, getBaseStat("skills", skillID, dmgFields[1][0]), getBaseStat("skills", skillID, dmgFields[1][1]), getBaseStat("skills", skillID, dmgFields[1][2]), getBaseStat("skills", skillID, dmgFields[1][3]), getBaseStat("skills", skillID, dmgFields[1][4]), getBaseStat("skills", skillID, dmgFields[1][5]), getBaseStat("skills", skillID, "HitShift"), m) : 0, + max: type ? this.stagedDamage(l, getBaseStat("skills", skillID, dmgFields[1][6]), getBaseStat("skills", skillID, dmgFields[1][7]), getBaseStat("skills", skillID, dmgFields[1][8]), getBaseStat("skills", skillID, dmgFields[1][9]), getBaseStat("skills", skillID, dmgFields[1][10]), getBaseStat("skills", skillID, dmgFields[1][11]), getBaseStat("skills", skillID, "HitShift"), m) : 0 + }; + } + }, + skillRadius: { + //47: 8, + //48: 5, // Nova + 55: 3, + 56: 12, + 92: 24, + 154: 12, + 249: 24, + 250: 24, + 251: 3, + }, + novaLike: { + 44: true, + 48: true, + 92: true, + 112: true, + 154: true, + 249: true, + 250: true, + }, + wolfBanned: { + 225: true, + 229: true, + 230: true, + 233: true, + 234: true, + 235: true, + 240: true, + 243: true, + 244: true, + 245: true, + 250: true, + }, + bearBanned: { + 225: true, + 229: true, + 230: true, + 232: true, + 234: true, + 235: true, + 238: true, + 240: true, + 244: true, + 245: true, + 248: true, + }, + humanBanned: { + 232: true, + 233: true, + 238: true, + 239: true, + 242: true, + 243: true, + 248: true, + }, + nonDamage: { + // Some fakes to avoid these + + 54: true, // teleport + 217: true, // scroll identify + 218: true, // portal scroll + 219: true, // I assume this is the book of scroll + 220: true, // book portal. Not really a skill you want to use, do you + 117: true, // Holy shield. Holy shield it self doesnt give damage + 278: true, // venom adds damage, but doesnt do damage on its own + + // Remove all the trap skills, as we prefer to calculate this upon demand + 261: true, // lighting bolt + 271: true, // lighting sentry + 276: true, // Death sentry only works on corpses, we calculate this within attack + 262: true, // wake of fire + 272: true, // inferno + }, + shiftState: function () { + if (GameData.myReference.getState(139)) return "wolf"; + if (GameData.myReference.getState(140)) return "bear"; + return "human"; + }, + bestForm: function (skillID) { + if (this.shiftState() === "human" && this.humanBanned[skillID]) { + let highest = { ID: 0, Level: 0 }; + + if (!this.wolfBanned[skillID] && this.skillLevel(223) > highest.Level) { + highest.ID = 223; + highest.Level = this.skillLevel(223); + } + + if (!this.bearBanned[skillID] && this.skillLevel(228) > highest.Level) { + highest.ID = 228; + highest.Level = this.skillLevel(228); + } + + return highest.ID; + } else if (this.shiftState() === "wolf" && this.wolfBanned[skillID]) { + return 223; + } else if (this.shiftState() === "bear" && this.bearBanned[skillID]) { + return 228; + } + + return 0; + }, + physicalAttackDamage: function (skillID) { + let dmg = (() => { + switch (skillID) { + case sdk.skills.Bash: + return 45 + (5 + GameData.myReference.getSkill(skillID, 1)) + (5 * GameData.myReference.getSkill(sdk.skills.Stun, 0)); + case sdk.skills.Stun: + return (8 * GameData.myReference.getSkill(sdk.skills.Bash, 0)); + case sdk.skills.Concentrate: + return (65 + (5 * GameData.myReference.getSkill(skillID, 1)) + (5 * GameData.myReference.getSkill(sdk.skills.Bash, 0)) + (10 * GameData.myReference.getSkill(sdk.skills.BattleOrders, 0))); + case sdk.skills.LeapAttack: + return (70 + (30 * GameData.myReference.getSkill(skillID, 1)) + (10 * GameData.myReference.getSkill(sdk.skills.Leap, 0))); + case sdk.skills.Whirlwind: + return (8 * GameData.myReference.getSkill(skillID, 1)) - 58; + default: + return 0; + } + })(); + + // return (((GameData.myReference.getStat(sdk.stats.MaxDamage) + GameData.myReference.getStat(sdk.stats.MinDamage)) / 2) + (GameData.myReference.getStat(sdk.stats.Strength) * dmg)) / 100; + return dmg; + }, + dmgModifier: function (skillID, target) { + let aps = (typeof target === "number" ? this.averagePackSize(target) : 1); + let eliteBonus = (target.spectype && target.isSpecial) ? 1 : 0, hitcap = 1; + + switch (skillID) { // charged bolt/strike excluded, it's so unreliably random + case 15: // poison javalin + case 25: // plague javalin + case 16: // exploding arrow + case 27: // immolation arrow + case 31: // freezing arrow + case 35: // lightning fury + case 44: // frost nova + case 48: // nova + case 56: // meteor + case 59: // blizzard + case 64: // frozen orb + case 83: // poison explosion + case 92: // poison nova + case 112: // blessed hammer + case 154: // war cry + case 229: // molten boulder + case 234: // fissure + case 249: // armageddon + case 244: // volcano + case 250: // hurricane + case 251: // fireblast + case 261: // charged bolt sentry + case 262: // wake of fire + case 55: // glacial spike + case 47: // fire ball + case 42: // Static field. + case 38: // charged bolt + hitcap = Infinity; + break; + case 34: // lightning strike + hitcap = 1 + this.skillLevel(34); + break; + case 67: // teeth + hitcap = 1 + this.skillLevel(67); + break; + case 53: // chain lightning + hitcap = 5 + ((this.skillLevel(53) / 5) | 0); + break; + case 24: + hitcap = 3 + ((this.skillLevel(24) / 5) | 0); + break; + case 49: // lightning + case 84: // bone spear + case 271: // lightning sentry + case 276: // death sentry + hitcap = aps ? Math.sqrt(aps / Math.PI) * 2 : 1; + break; + default: + hitcap = 1; + break; + } + + if (typeof target !== "number") { + let unit = Game.getMonster(); + let radius = this.skillRadius[skillID] || 18; + + if (unit) { + do { + if (aps >= hitcap) { + break; + } + + if (target.gid !== unit.gid && getDistance(unit, this.novaLike[skillID] ? GameData.myReference : target) <= radius && isEnemy(unit)) { + aps++; + + if (unit.isSpecial) { + eliteBonus++; + } + } + } while (unit.getNext()); + } + } else { + aps = Math.min(aps, hitcap); + } + + aps += eliteBonus * (4 - me.diff) / 2; + + return aps; + }, + + /** + * @typedef skillDmgObj + * @property {string} type + * @property {number} pmin + * @property {number} pmax + * @property {number} min + * @property {number} max + * @property {boolean} [undeadOnly] + * + * @param {number} skillID + * @param {Monster} unit + * @returns {skillDmgObj} + */ + skillDamage: function (skillID, unit) { + // TODO: caluclate basic attack damage + if (skillID === sdk.skills.Attack) return { type: "Physical", pmin: 2, pmax: 8, min: 0, max: 0 }; // short sword, no reqs + + if (this.skillLevel(skillID) < 1) { + return { + type: this.damageTypes[getBaseStat("skills", skillID, "EType")], + pmin: 0, + pmax: 0, + min: 0, + max: 0 + }; + } + + let dmg = this.baseSkillDamage(skillID); + let mastery = 1, psynergy = 1, synergy = 1, shots = 1, sl = 0; + + if (this.synergyCalc[skillID]) { + let sc = this.synergyCalc[skillID]; + + for (let c = 0; c < sc.length; c += 2) { + sl = this.baseLevel(sc[c]); + + if (skillID === 229 || skillID === 244) { + if (sc[c] === 229 || sc[c] === 244) { // molten boulder and volcano + psynergy += sl * sc[c + 1]; // they only synergize physical with each other + } else { + synergy += sl * sc[c + 1]; // all other skills synergize only fire with these skills + } + } else { + psynergy += sl * sc[c + 1]; + synergy += sl * sc[c + 1]; + } + } + } + + if (skillID === 227 || skillID === 237 || skillID === 247) { + sl = this.skillLevel(247); + psynergy += 0.15 + sl * 0.10; + synergy += 0.15 + sl * 0.10; + } + + switch (dmg.type) { + case "Fire": // fire mastery + case "Lightning": // lightning mastery + case "Cold": // cold mastery + case "Poison": // poison mastery + case "Magic": // magic mastery + mastery = 1 + GameData.myReference.getStat(this.masteryMap[dmg.type]) / 100; + dmg.min *= mastery; + dmg.max *= mastery; + + break; + } + + dmg.pmin *= psynergy; + dmg.pmax *= psynergy; + + if (this.noMinSynergy.indexOf(skillID) < 0) { + dmg.min *= synergy; + } + + dmg.max *= synergy; + + switch (skillID) { + case 102: // holy fire + dmg.min *= 6; // weapon damage is 6x the aura damage + dmg.max *= 6; + break; + case 114: // holy freeze + dmg.min *= 5; // weapon damage is 5x the aura damage + dmg.max *= 5; + break; + case 118: // holy shock + dmg.min *= 6; // weapon damage is 6x the aura damage + dmg.max *= 6; + break; + case 249: // armageddon + dmg.pmin = dmg.pmax = 0; + break; + case 24: // charged strike + dmg.max *= 3 + ((this.skillLevel(24) / 5) | 0); + } + + dmg.pmin >>= 8; + dmg.pmax >>= 8; + dmg.min >>= 8; + dmg.max >>= 8; + + switch (skillID) { + case sdk.skills.ChargedBolt: // more than one bolt can hit but may calc this as splashdamage instead + if (unit) { + let baseId = getBaseStat("monstats", unit.classid, "baseid"); + let size = getBaseStat("monstats2", baseId, "sizex"); + (typeof size !== "number" || size < 1 || size > 3) && (size = 3); + let dist = unit.distance; + const modifier = size === 1 ? 0.5 : size === 3 ? 1.5 : size === 2 && dist < 5 ? 1.2 : 1; + dmg.min *= modifier; + dmg.max *= modifier; + } + + // need to take into account the amount of bolts released + // the size of the unit we are targetting + // the distance from the target + break; + case 59: // blizzard - on average hits twice + dmg.min *= 2; + dmg.max *= 2; + break; + case 62: // hydra - 3 heads + dmg.min *= 3; + dmg.max *= 3; + break; + case 64: // frozen orb - on average hits ~5 times + dmg.min *= 5; + dmg.max *= 5; + break; + case 70: // skeleton - a hit per skeleton + sl = this.skillLevel(70); + shots = sl < 4 ? sl : (2 + sl / 3) | 0; + sl = Math.max(0, sl - 3); + dmg.pmin = shots * (dmg.pmin + 1 + this.skillLevel(69) * 2) * (1 + sl * 0.07); + dmg.pmax = shots * (dmg.pmax + 2 + this.skillLevel(69) * 2) * (1 + sl * 0.07); + break; + case 94: // fire golem + sl = this.skillLevel(94); + dmg.min = [10, 15, 18][me.diff] + dmg.min + (this.stagedDamage(sl + 7, 2, 1, 2, 3, 5, 7) >> 1) * 6; // basically holy fire added + dmg.max = [27, 39, 47][me.diff] + dmg.max + (this.stagedDamage(sl + 7, 6, 1, 2, 3, 5, 7) >> 1) * 6; + break; + case 101: // holy bolt + dmg.undeadOnly = true; + break; + case 112: // blessed hammer + sl = this.skillLevel(113); + + if (sl > 0) { + mastery = (100 + ((45 + this.skillLevel(113) * 15) >> 1)) / 100; // hammer gets half concentration dmg bonus + dmg.min *= mastery; + dmg.max *= mastery; + } + + break; + case sdk.skills.Raven: // raven - a hit per raven + shots = Math.min(5, this.skillLevel(221)); // 1-5 ravens + dmg.pmin *= shots; + dmg.pmax *= shots; + break; + case sdk.skills.SummonSpiritWolf: // spirit wolf - a hit per wolf + shots = Math.min(5, this.skillLevel(227)); + dmg.pmin *= shots; + dmg.pmax *= shots; + break; + case sdk.skills.SummonDireWolf: // dire wolf - a hit per wolf + shots = Math.min(3, this.skillLevel(237)); + dmg.pmin *= shots; + dmg.pmax *= shots; + break; + case sdk.skills.Twister: // twister + dmg.pmin *= 3; + dmg.pmax *= 3; + break; + case 261: // charged bolt sentry + case 262: // wake of fire + case 271: // lightning sentry + case 272: // inferno sentry + case 276: // death sentry + dmg.min *= 5; // can have 5 traps out at a time + dmg.max *= 5; + break; + case sdk.skills.StaticField: + if (!(unit instanceof Unit)) { + break; + } + // No cap in classic + let staticCap = (me.gametype === sdk.game.gametype.Classic ? 0 : [0, 33, 50][me.diff]); + const [monsterId, areaId] = [unit.classid, unit.area]; + let percentLeft = (unit.hp * 100 / unit.hpmax); + if (staticCap > percentLeft) { + break; + } + + const maxReal = this.monsterMaxHP(monsterId, areaId, unit.charlvl - this.monsterLevel(monsterId, areaId)); + let hpReal = maxReal / 100 * percentLeft; + let potencialDmg = (hpReal / 100 * percentLeft) * 0.25; + + let tmpDmg = (maxReal / 100 * percentLeft) * (0.25); + + // We do need to calculate the extra damage, or less damage due to resistance + let resist = this.monsterResist(unit, "Lightning"); + let pierce = GameData.myReference.getStat(this.pierceMap.Lightning); + + let conviction = this.getConviction(); + // if (conviction && !unit.getState(sdk.states.Conviction)) conviction = 0; //ToDo; enable when fixed telestomp + resist -= (resist >= 100 ? conviction / 5 : conviction); + resist = (resist < 100 ? Math.max(-100, resist - pierce) : 100); + tmpDmg = potencialDmg * ((100 - resist) / 100); + const percentageDamage = 100 / maxReal * tmpDmg; + + let avgDmg = tmpDmg; + let overCap = percentLeft - staticCap - percentageDamage; + if (overCap < 0) { + let maxDmgPercentage = percentageDamage - Math.abs(overCap); + avgDmg = maxReal / 100 * maxDmgPercentage; + } + avgDmg = avgDmg > 0 && avgDmg || 0; + //console.log('Static will chop off -> ' + (100 / maxReal * avgDmg) + '%'); + dmg.min = avgDmg; + dmg.max = avgDmg; + break; + } + + dmg.pmin |= 0; + dmg.pmax |= 0; + dmg.min |= 0; + dmg.max |= 0; + + return dmg; + }, + + /** + * Calculate actual average damage this skill does taking into account splash/range of skill + * @param {number} skillID + * @param {Monster | number | string} unit + * @returns {number} + * @todo + * - build me metadata - then use it to calulate a range of skills rather than redo the exact same calculations. + * example: trying to check the damage of blizard and then frozen orb, + * currently it would check our stats, then check amp and conviction - those could all be pre-built as they aren't going to change + */ + avgSkillDamage: function (skillID, unit) { + if (skillID === undefined || unit === undefined || !skillID || !unit || !Skill.canUse(skillID)) return 0; + const ampDmg = Skill.canUse(sdk.skills.AmplifyDamage) ? 100 : (Skill.canUse(sdk.skills.Decrepify) ? 50 : 0); + + /** + * + * @param {skillDmgObj} skillData + * @param {Monster | number | string} unit + * @returns + */ + const getTotalDmg = function (skillData, unit) { + const isUndead = (typeof unit === "number" ? MonsterData[unit].Undead : MonsterData[unit.classid].Undead); + const conviction = GameData.getConviction(); + let totalDmg = 0; + let avgPDmg = (skillData.pmin + skillData.pmax) / 2; + let avgDmg = (skillData.min + skillData.max) / 2; + + if (avgPDmg > 0) { + let presist = GameData.monsterResist(unit, "Physical"); + presist -= (presist >= 100 ? ampDmg / 5 : ampDmg); + presist = Math.max(-100, Math.min(100, presist)); + totalDmg += avgPDmg * (100 - presist) / 100; + } + if (avgDmg > 0 && (!isUndead || !skillData.undeadOnly)) { + let resist = GameData.monsterResist(unit, skillData.type); + let pierce = GameData.myReference.getStat(GameData.pierceMap[skillData.type]); + if (GameData.convictionEligible[skillData.type]) { + resist -= (resist >= 100 ? conviction / 5 : conviction); + } + resist = (resist < 100 ? Math.max(-100, resist - pierce) : 100); + totalDmg += avgDmg * (100 - resist) / 100; + } + return totalDmg; + }; + + /** + * @param {number} skill + * @param {number} splash + * @param {Monster} target + * @returns {number} + */ + const calculateSplashDamage = function (skill, splash, target) { + return getUnits(sdk.unittype.Monster) + .filter((mon) => mon.attackable && getDistance(target, mon) < splash) + .reduce(function (acc, cur) { + let _a = GameData.skillDamage(skill, cur); + return acc + getTotalDmg(_a, cur); + }, 0); + }; + + /** + * @param {number} skill + * @param {Monster} target + * @returns {number} + */ + const calculateChainDamage = function (skill, target) { + skill === undefined && (skill = -1); + let rawDmg = 0, totalDmg = 0, range = 0, hits = 0; + switch (skill) { + case sdk.skills.ChainLightning: + hits = Math.round((25 + me.getSkill(sdk.skills.ChainLightning, sdk.skills.subindex.SoftPoints)) / 5); + range = 13; + break; + } + let units = getUnits(sdk.unittype.Monster) + .filter((mon) => mon.attackable && getDistance(mon, target) < range) + .sort((a, b) => getDistance(target, a) - getDistance(target, b)); + if (units.length === 1) { + rawDmg = GameData.skillDamage(skill, target); + return getTotalDmg(rawDmg, target); + } else { + console.log("Units to check: " + units.length); + for (let i = 0; i < units.length; i++) { + if (units[i] !== undefined) { + rawDmg = GameData.skillDamage(skill, units[i]); + totalDmg += getTotalDmg(rawDmg, units[i]); + if (i > hits) { break; } + } else { + units.splice(i, 1); + i -= 1; + } + } + return totalDmg; + } + }; + + const calculateRawStaticDamage = function (distanceUnit) { + distanceUnit === undefined && (distanceUnit = me); + if (!Skill.canUse(sdk.skills.StaticField)) return 0; + const range = Skill.getRange(sdk.skills.StaticField); + const cap = (me.gametype === sdk.game.gametype.Classic ? 1 : [1, 25, 50][me.diff]); + const pierce = me.getStat(sdk.stats.PierceLtng); + return getUnits(sdk.unittype.Monster) + .filter(function (mon) { + return mon.attackable && getDistance(mon, distanceUnit) < range; + }).reduce(function (acc, unit) { + let classId = unit.classid, areaId = unit.area; + let maxHealth = GameData.monsterAvgHP(classId, areaId, unit.charlvl - GameData.monsterLevel(classId, areaId)); + let currentHealth = maxHealth / 100 * (unit.hp * 100 / unit.hpmax), baseDamage = currentHealth * 0.25; + // monsterRes already considers conviction state + let monsterRes = unit.getStat(sdk.stats.LightResist); + let totalRes = Math.min(100, Math.max(-100, monsterRes - pierce)); + // calculate the actual damage we do + let potentialDamage = baseDamage / (100 / (100 - totalRes)); + let cappedAtHealth = maxHealth / 100 * cap; + // cap max damage + let actualDamage = currentHealth - Math.max(cappedAtHealth, (currentHealth - potentialDamage)); + return acc + (actualDamage); + }, 0); + }; + + const calculateThroughDamage = function () { + // determine maximum potential distance of this missile + // build points from me -> monster -> max distance + // iterate points checking if any monsters are in the path + // check collision at each point and break if we encounter a missisle blocker + // special considerations for molten boulder: + // - check monster size, based on size the boulder may knock back or go through them + // - if we encounter a collision that causes the boulder to burst, add explosion damage + // + }; + + /** + * + * @param {Monster} unit + */ + const calcVolcanoDamage = function (unit) { + let velocity = unit.currentVelocity; + /** @type {skillDmgObj} */ + let baseDmg = GameData.skillDamage(sdk.skills.Volcano, unit); + // since these are random, lets take them into account but not at their full value + let missleDmg = Object.assign({}, baseDmg); + missleDmg.pmin /= 2; + missleDmg.pmax /= 2; + missleDmg.min /= 2; + missleDmg.max /= 2; + // sorta guess work for now, needs improvment to really figure out on average how many times the actual + // volcano damages the monster cast on based on size/speed + let modifier = (!unit.isMoving || velocity === 1) ? 5 : velocity === 2 ? 3 : 1; + if (modifier > 1) { + baseDmg.pmin *= modifier; + baseDmg.pmax *= modifier; + baseDmg.min *= modifier; + baseDmg.max *= modifier; + } + + // sum the total in the range of the volcano missiles + // what about monsters just directly on the volcano? + return getUnits(sdk.unittype.Monster) + .filter((mon) => mon.attackable && getDistance(unit, mon) < 15) + .reduce(function (acc, cur) { + return acc + getTotalDmg(missleDmg, cur); + }, getTotalDmg(baseDmg, unit)); + }; + + /** + * @todo some skills need special handling + * - Bone spear and Lightning both pierce enemies in a straight path, need to calculate include monsters in that path + * to the total damage done as this can make the difference between wanting to use this skill vs another + * - Fire Wall is similar only seems to be a random angle + * - Inferno/Artic blast same as bonespear/lightning + * - Molten boulder needs to take into account monster size and angle of cast + * - Zeal, can hit same enemy multiple times or multiple enemies, would change total damage applied based on enemy targetted so needs to be handled + * Others? + */ + switch (skillID) { + case sdk.skills.Blizzard: + case sdk.skills.Meteor: + case sdk.skills.FireBall: + case sdk.skills.GlacialSpike: + case sdk.skills.ChargedBolt: + case sdk.skills.Fissure: + let { x, y } = unit; + let rad = skillID === sdk.skills.Volcano ? 15 : skillID === sdk.skills.Fissure ? 10 : 5; + + if (!Attack.validSpot(x, y, skillID, unit.classid)) { + return 0; + } + + return calculateSplashDamage(skillID, rad, unit); + case sdk.skills.Volcano: + if (!Attack.validSpot(unit.x, unit.y, skillID, unit.classid)) { + return 0; + } + + return calcVolcanoDamage(unit); + case sdk.skills.FrostNova: + case sdk.skills.Nova: + return calculateSplashDamage(skillID, 6, unit); + case sdk.skills.StaticField: + return calculateRawStaticDamage(unit); + case sdk.skills.ChainLightning: + return calculateChainDamage(skillID, unit); + default: + return getTotalDmg(this.skillDamage(skillID, unit), unit); + } + }, + allSkillDamage: function (unit) { + let skills = {}; + let self = this; + GameData.myReference.getSkill(4).forEach(function (skill) { + if (self.nonDamage.hasOwnProperty(skill[0])) { + return false; // Doesnt do damage + } + return skills[skill[0]] = self.skillDamage(skill[0], unit); + }); + + return skills; + }, + convictionEligible: { + Fire: true, + Lightning: true, + Cold: true, + }, + lowerResistEligible: { + Fire: true, + Lightning: true, + Cold: true, + Poison: true, + }, + resistMap: { + Physical: 36, + Fire: 39, + Lightning: 41, + Cold: 43, + Poison: 45, + Magic: 37, + }, + masteryMap: { + Fire: 329, + Lightning: 330, + Cold: 331, + Poison: 332, + Magic: 357, + }, + pierceMap: { + Fire: 333, + Lightning: 334, + Cold: 335, + Poison: 336, + Magic: 358, + }, + ignoreSkill: { + 40: true, + 50: true, + 60: true, + }, + buffs: { + 8: 1, + 9: 1, + 13: 1, + 17: 1, + 18: 1, + 23: 1, + 28: 1, + 29: 1, + 32: 1, + 37: 1, + 40: 2, + 46: 1, + 50: 2, + 52: 1, + 57: 1, + 58: 1, + 60: 2, + 61: 1, + 63: 1, + 65: 1, + 68: 1, + 69: 1, + 79: 1, + 89: 1, + 98: 3, + 99: 3, + 100: 3, + 102: 3, + 103: 3, + 104: 3, + 105: 3, + 108: 3, + 109: 3, + 110: 3, + 113: 3, + 114: 3, + 115: 3, + 118: 3, + 119: 3, + 120: 3, + 122: 3, + 123: 3, + 124: 3, + 125: 3, + 127: 1, + 128: 1, + 129: 1, + 134: 1, + 135: 1, + 136: 1, + 138: 1, + 141: 1, + 145: 1, + 148: 1, + 149: 1, + 153: 1, + 155: 1, + 221: 1, + 222: 4, + 223: 5, + 224: 1, + 226: 6, + 227: 7, + 228: 5, + 231: 4, + 235: 1, + 236: 6, + 237: 7, + 241: 4, + 246: 6, + 247: 7, + 249: 1, + 250: 1, + 258: 8, + 267: 8, + 268: 9, + 279: 9, + }, + preAttackable: [ + sdk.skills.MagicArrow, sdk.skills.FireArrow, sdk.skills.MultipleShot, sdk.skills.ExplodingArrow, sdk.skills.IceArrow, sdk.skills.GuidedArrow, sdk.skills.ImmolationArrow, sdk.skills.Strafe, + sdk.skills.PlagueJavelin, sdk.skills.LightningFury, + sdk.skills.FireBolt, sdk.skills.Inferno, sdk.skills.Blaze, sdk.skills.FireBall, sdk.skills.FireWall, sdk.skills.Meteor, sdk.skills.Hydra, + sdk.skills.ChargedBolt, sdk.skills.Nova, sdk.skills.Lightning, sdk.skills.ChainLightning, + sdk.skills.IceBolt, sdk.skills.FrostNova, sdk.skills.IceBlast, sdk.skills.Blizzard, sdk.skills.FrozenOrb, + sdk.skills.AmplifyDamage, sdk.skills.DimVision, sdk.skills.Weaken, sdk.skills.IronMaiden, sdk.skills.Terror, sdk.skills.Confuse, sdk.skills.LifeTap, sdk.skills.Attract, sdk.skills.Decrepify, sdk.skills.LowerResist, + sdk.skills.Teeth, sdk.skills.BoneSpear, sdk.skills.PoisonNova, + sdk.skills.BlessedHammer, + sdk.skills.WarCry, + sdk.skills.Twister, sdk.skills.Tornado, + sdk.skills.FireBlast, sdk.skills.ShockWeb, + ], + monsterResist: function (unit, type) { + let stat = this.resistMap[type]; + return stat ? (unit.getStat ? unit.getStat(stat) : MonsterData[unit][type]) : 0; + }, + getConviction: function () { + let merc = GameData.myReference.getMerc(), sl = this.skillLevel(123); // conviction + if (( // Either me, or merc is wearing a conviction + merc && merc.getItemsEx().filter(item => item.getPrefix(sdk.locale.items.Infinity)).first() + || GameData.myReference.getItemsEx(-1, 1).filter(item => item.getPrefix(sdk.locale.items.Infinity)).first())) { + sl = 12; + } + return sl > 0 ? Math.min(150, 30 + (sl - 1) * 5) : 0; + }, + getAmp: function () { + return this.skillLevel(66) ? 100 : (this.skillLevel(87) ? 50 : 0); + }, + monsterEffort: function (unit, areaID, skillDamageInfo = undefined, parent = undefined, preattack = false, all = false) { + let buffDmg = []; + const allData = []; + const buffDamageInfo = {}; + const newSkillDamageInfo = {}; + const eret = { effort: Infinity, skill: -1, type: "Physical" }; + const useCooldown = (typeof unit === "number" ? false : Boolean(me.skillDelay)); + const hp = this.monsterMaxHP(typeof unit === "number" ? unit : unit.classid, areaID); + const conviction = this.getConviction(), ampDmg = this.getAmp(); + const isUndead = (typeof unit === "number" ? MonsterData[unit].Undead : MonsterData[unit.classid].Undead); + skillDamageInfo = skillDamageInfo || this.allSkillDamage(unit); + console.debug(unit, "---", hp); + // if (conviction && unit instanceof Unit && !unit.getState(sdk.states.Conviction)) conviction = 0; //ToDo; enable when fixed telestomp + + for (let sk in skillDamageInfo) { + if (this.buffs[sk]) { + if (typeof unit === "number") { + buffDmg[this.buffs[sk]] = 0; + buffDamageInfo[sk] = skillDamageInfo[sk]; + } + } else { + newSkillDamageInfo[sk] = skillDamageInfo[sk]; + } + } + + skillDamageInfo = newSkillDamageInfo; + + for (let sk in buffDamageInfo) { + // static field has a fix'd ceiling, calculated already + if ([sdk.skills.StaticField].indexOf(sk) !== -1) continue; + + let avgPDmg = (buffDamageInfo[sk].pmin + buffDamageInfo[sk].pmax) / 2; + let avgDmg = (buffDamageInfo[sk].min + buffDamageInfo[sk].max) / 2; + let tmpDmg = 0; + + if (avgPDmg > 0) { + let presist = this.monsterResist(unit, "Physical"); + + presist -= (presist >= 100 ? ampDmg / 5 : ampDmg); + presist = Math.max(-100, Math.min(100, presist)); + tmpDmg += avgPDmg * (100 - presist) / 100; + } + + if (avgDmg > 0 && (!isUndead || !buffDamageInfo[sk].undeadOnly) && sk !== sdk.skills.StaticField) { + let resist = this.monsterResist(unit, buffDamageInfo[sk].type); + let pierce = GameData.myReference.getStat(this.pierceMap[buffDamageInfo[sk].type]); + + if (this.convictionEligible[buffDamageInfo[sk].type]) { + resist -= (resist >= 100 ? conviction / 5 : conviction); + } + + if (resist < 100) { + resist = Math.max(-100, resist - pierce); + } else { + resist = 100; + } + + tmpDmg += avgDmg * (100 - resist) / 100; + } + + if (this.buffs[sk] === 1) { + buffDmg[this.buffs[sk]] += tmpDmg; + } else { + buffDmg[this.buffs[sk]] = Math.max(buffDmg[this.buffs[sk]], tmpDmg); + } + } + + buffDmg = buffDmg.reduce((t, v) => t + v, 0); + + for (let sk in skillDamageInfo) { + if (preattack && this.preAttackable.indexOf(parseInt(sk)) === -1) continue; // cant preattack this skill + if (!this.ignoreSkill[sk] && (!useCooldown || !this.skillCooldown(sk | 0))) { + let avgPDmg = (skillDamageInfo[sk].pmin + skillDamageInfo[sk].pmax) / 2, totalDmg = buffDmg; + let avgDmg = (skillDamageInfo[sk].min + skillDamageInfo[sk].max) / 2; + + if (avgPDmg > 0 && sk !== sdk.skills.StaticField) { + let presist = this.monsterResist(unit, "Physical"); + + presist -= (presist >= 100 ? ampDmg / 5 : ampDmg); + presist = Math.max(-100, Math.min(100, presist)); + totalDmg += avgPDmg * (100 - presist) / 100; + } + + if (avgDmg > 0 && (!isUndead || !skillDamageInfo[sk].undeadOnly)) { + let resist = this.monsterResist(unit, skillDamageInfo[sk].type); + let pierce = GameData.myReference.getStat(this.pierceMap[skillDamageInfo[sk].type]); + + if (this.convictionEligible[skillDamageInfo[sk].type]) { + resist -= (resist >= 100 ? conviction / 5 : conviction); + } + + if (resist < 100) { + resist = Math.max(-100, resist - pierce); + } else { + resist = 100; + } + + totalDmg += sk !== sdk.skills.StaticField + && 0 + || avgDmg * (100 - resist) / 100; + + } + console.debug(hp, "---/", totalDmg); + let tmpEffort = Math.ceil(hp / totalDmg); + + tmpEffort /= this.dmgModifier(sk | 0, parent || unit); + + // care for mana + if (GameData.myReference.mp < Skill.getManaCost(sk)) { + tmpEffort *= 5; // More effort in a skill we dont have mana for + } + + // check valid location, blizzard and meteor fail over lava + if (typeof unit === "object") { + if ([sdk.skills.Blizzard, sdk.skills.Meteor].indexOf(sk) && !Attack.validSpot(unit.x, unit.y, sk, unit.classid)) { + tmpEffort *= 5; + } + } + + // Use less cool down spells, if something better is around + /* if (this.skillCooldown(sk | 0)) { + console.log("tmpEffort: " + (Math.ceil(tmpEffort)) + " eretEffor: " + eret.effort); + tmpEffort *= 5; + } */ + if (tmpEffort <= eret.effort) { + eret.effort = tmpEffort; + eret.skill = sk | 0; + eret.type = skillDamageInfo[eret.skill].type; + eret.name = getSkillById(eret.skill); + eret.cooldown = this.skillCooldown(sk | 0); + if (all) { + allData.unshift(copyObj(eret)); + } + } + } + } + console.log(eret, allData); + if (all && allData.length) return allData; + if (eret.skill >= 0) return eret; + return null; + }, + effectiveMonsterEffort: function (unit, areaID) { + if (unit === undefined) return null; + areaID === undefined && (areaID = me.area); + const allData = []; + const buffDamageInfo = {}; + const newSkillDamageInfo = {}; + let buffDmg = []; + let eret = { effort: Infinity, skill: -1, type: "Physical" }; + let hp = this.monsterMaxHP(typeof unit === "number" ? unit : unit.classid, areaID); + let conviction = this.getConviction(), ampDmg = this.getAmp(); + let isUndead = (typeof unit === "number" ? MonsterData[unit].Undead : MonsterData[unit.classid].Undead); + let skillDamageInfo = this.allSkillDamage(unit); + + for (let sk in skillDamageInfo) { + if (this.buffs[sk]) { + if (typeof unit === "number") { + buffDmg[this.buffs[sk]] = 0; + buffDamageInfo[sk] = skillDamageInfo[sk]; + } + } else { + newSkillDamageInfo[sk] = skillDamageInfo[sk]; + } + } + + skillDamageInfo = newSkillDamageInfo; + + for (let sk in buffDamageInfo) { + // static field has a fix'd ceiling, calculated already + if ([sdk.skills.StaticField].indexOf(sk) !== -1) continue; + + let avgPDmg = (buffDamageInfo[sk].pmin + buffDamageInfo[sk].pmax) / 2; + let avgDmg = (buffDamageInfo[sk].min + buffDamageInfo[sk].max) / 2; + let tmpDmg = 0; + + if (avgPDmg > 0) { + let presist = this.monsterResist(unit, "Physical"); + + presist -= (presist >= 100 ? ampDmg / 5 : ampDmg); + presist = Math.max(-100, Math.min(100, presist)); + tmpDmg += avgPDmg * (100 - presist) / 100; + } + + if (avgDmg > 0 && (!isUndead || !buffDamageInfo[sk].undeadOnly) && sk !== sdk.skills.StaticField) { + let resist = this.monsterResist(unit, buffDamageInfo[sk].type); + let pierce = GameData.myReference.getStat(this.pierceMap[buffDamageInfo[sk].type]); + + if (this.convictionEligible[buffDamageInfo[sk].type]) { + resist -= (resist >= 100 ? conviction / 5 : conviction); + } + + if (resist < 100) { + resist = Math.max(-100, resist - pierce); + } else { + resist = 100; + } + + tmpDmg += avgDmg * (100 - resist) / 100; + } + + if (this.buffs[sk] === 1) { + buffDmg[this.buffs[sk]] += tmpDmg; + } else { + buffDmg[this.buffs[sk]] = Math.max(buffDmg[this.buffs[sk]], tmpDmg); + } + } + + buffDmg = buffDmg.reduce((t, v) => t + v, 0); + + for (let sk in skillDamageInfo) { + if (!this.ignoreSkill[sk]) { + let avgPDmg = (skillDamageInfo[sk].pmin + skillDamageInfo[sk].pmax) / 2, totalDmg = buffDmg; + let avgDmg = (skillDamageInfo[sk].min + skillDamageInfo[sk].max) / 2; + + if (avgPDmg > 0 && sk !== sdk.skills.StaticField) { + let presist = this.monsterResist(unit, "Physical"); + + presist -= (presist >= 100 ? ampDmg / 5 : ampDmg); + presist = Math.max(-100, Math.min(100, presist)); + totalDmg += avgPDmg * (100 - presist) / 100; + } + + if (avgDmg > 0 && (!isUndead || !skillDamageInfo[sk].undeadOnly)) { + let resist = this.monsterResist(unit, skillDamageInfo[sk].type); + let pierce = GameData.myReference.getStat(this.pierceMap[skillDamageInfo[sk].type]); + + if (this.convictionEligible[skillDamageInfo[sk].type]) { + resist -= (resist >= 100 ? conviction / 5 : conviction); + } + + if (resist < 100) { + resist = Math.max(-100, resist - pierce); + } else { + resist = 100; + } + + totalDmg += sk !== sdk.skills.StaticField && 0 || avgDmg * (100 - resist) / 100; + + } + + let tmpEffort = Math.ceil(hp / totalDmg); + + tmpEffort /= this.dmgModifier(sk | 0, unit); + + // care for mana + if (GameData.myReference.mp < Skill.getManaCost(sk)) { + tmpEffort *= 5; // More effort in a skill we dont have mana for + } + + // check valid location, blizzard and meteor fail over lava + if ([sdk.skills.Blizzard, sdk.skills.Meteor].indexOf(sk) && !Attack.validSpot(unit.x, unit.y, sk, unit.classid)) { + tmpEffort *= 5; + } + + if (tmpEffort <= eret.effort) { + eret.effort = tmpEffort; + eret.skill = sk | 0; + eret.type = skillDamageInfo[eret.skill].type; + eret.name = getSkillById(eret.skill); + eret.cooldown = this.skillCooldown(sk | 0); + allData.unshift(copyObj(eret)); + } + } + } + if (allData.length) return allData; + if (eret.skill >= 0) return eret; + return null; + }, + areaEffort: function (areaID, skills) { + let effortpool = 0, raritypool = 0, dmgAcc = 0; + + skills = skills || this.allSkillDamage(); + + AreaData[areaID].forEachMonsterAndMinion((mon, rarity, parent) => { + effortpool += rarity * this.monsterEffort(mon.Index, areaID, skills, parent && parent.Index).effort; + raritypool += rarity; + + dmgAcc += rarity * this.monsterAvgDmg(mon.Index, areaID); + }); + + // console.debug('avg dmg '+ AreaData[areaID].LocaleString+' -- ' + dmgAcc+' -- ' + avgDmg); + + return (raritypool ? effortpool / raritypool : Infinity); + }, + areaSoloExp: function (areaID, skills) { + let procentageBroke = ((100 - Math.min(100, Math.max(0, (100 / (Config.LowGold || 1) * me.gold))))); + let brokeness = 1 + (procentageBroke / 100 / 3 * 1); + let effortpool = 0, raritypool = 0, dmgAcc = 0; + + skills = skills || this.allSkillDamage(); + AreaData[areaID].forEachMonsterAndMinion((mon, rarity, parent) => { + effortpool += rarity * this.monsterExp(mon.Index, areaID) * this.levelModifier(GameData.myReference.charlvl, this.monsterLevel(mon.Index, areaID)) / this.monsterEffort(mon.Index, areaID, skills, parent && parent.Index).effort; + raritypool += rarity; + + dmgAcc += (rarity * this.monsterAvgDmg(mon.Index, areaID)); + }); + + let log = 1, avgDmg = 0; + if (brokeness !== 1) { + log = ((5 - Math.log(areaID)) * (brokeness * 0.6)); + avgDmg = (raritypool ? dmgAcc / raritypool : Infinity) * log; + } + + return (raritypool ? effortpool / raritypool : 0) - (avgDmg); + }, + mostUsedSkills: function (force = false) { + if (!force && GameData.myReference.hasOwnProperty("__cachedMostUsedSkills") && GameData.myReference.__cachedMostUsedSkills) return GameData.myReference.__cachedMostUsedSkills; + + const effort = [], uniqueSkills = []; + for (let i = 50; i < 120; i++) { + try { + effort.push(GameData.monsterEffort(i, sdk.areas.ThroneofDestruction)); + } catch (e) { + /*dontcare*/ + } + } + + effort + .filter(e => e !== null && typeof e === "object" && e.hasOwnProperty("skill")) + .filter(x => GameData.myReference.getSkill(x.skill, 0)) // Only skills where we have hard points in + .filter(x => Skills.class[x.skill] < 7) // Needs to be a skill of a class, not my class but a class + .map(x => + // Search for this unique skill + ( + uniqueSkills.find(u => u.skillId === x.skill) + // Or add it and return the value + || ( + ( + uniqueSkills.push({ skillId: x.skill, used: 0 }) + && false + ) + || uniqueSkills[uniqueSkills.length - 1] + ) + ).used++ && false + // In the end always return x + || x + ); + + return (GameData.myReference.__cachedMostUsedSkills = uniqueSkills.sort((a, b) => b.used - a.used)); + }, + + attackStartingFrame: function (weaponClass, charClass = GameData.myReference.classid) { + // amazon and sorceress only + /* + Weapon: hth 1hs 2hs 1ht 2ht stf bow xbw + StartingFrame: 1 2 2 2 2 2 0 0 + */ + if (charClass === sdk.player.class.Amazon || charClass === sdk.player.class.Sorceress) { + if (weaponClass === "hth") return 1; + if (["1hs", "2hs", "1ht", "2ht", "stf"].includes(weaponClass)) return 2; + } + return 0; + }, + + /*weaponSpeedModifier: function (weapon1Code, charClass = GameData.myReference.classid, weapon2Code = null) { + let weapons = new CSV("sdk/txt/weapons.txt"); + let weapon1Data = weapons.findObject("code", weapon1Code); + if (!weapon2Code) { + return weapon1Data.speed; + } + let weapon2Data = weapons.findObject("code", weapon2Code); + if (!weapon2Data) { + return weapon1Data.speed; + } + return (weapon1Data.speed + weapon2Data.speed) / 2; + },*/ + + attackModeForSkill: function (skillId, charClass = GameData.myReference.classid) { + //TODO: + if (skillId === sdk.skills.Smite) return "S1"; + /* + A1: + normal attack or attack skills like + "bow and crossbow" skills, energy strike, chain lightning strike, charged strike, + opposing tiger strike, cobra strike, phoenix strike + Slash, paralyze, concentrate, amok + barbarian rage, + mangle , fire claws , anger poison dagger + victim, zeal, revenge, conversion + + A2: + normal attack + + KK: kick (kick barrel) or assassin skills dragon claw, dragon tail + + S1: skill 1 + (evade, avoid, escape) + shield attack smite + + S2: skill 2 + stationary traps, fire blast , Shock net, blade guard + + S3: skill 3 + Secondary blow of the barbarian with dual weapons + Hunger, rabies + + S4: + Secondary blow of the assassin with dual claws + Secondary throw of the barbarian dual throwing + + TH: + Throw + poison throwing spear, lightning strike, plague throwing spit, flashing mischief + */ + return "A1"; + }, + + weaponAttackAnimationSpeed: function (baseRate, skill, weaponClass, charClass = GameData.myReference.classid, shiftState = null) { + /*if (shiftState == "bear") { + let framesPerDirection = this.weaponFramesPerDirection(skill, weaponClass, charClass); + let baseSpeed = this.weaponAttackAnimationSpeed(baseRate, skill, weaponClass, charClass); + let weaponIAS = 0; + let weaponSpeedModifier = 0; + let delay = baseRate * framesPerDirection / ((256 + weaponIAS - weaponSpeedModifier) * baseSpeed ​​/ 100); + return baseRate* + }*/ + //TODO: vampire form or werewolf + let attackMode = this.attackModeForSkill(skill, charClass); + switch (true) { + case charClass === sdk.player.class.Assassin && attackMode.startsWith("A") && weaponClass.startsWith("ht") && weaponClass !== "hth": + return 208; + case charClass === sdk.player.class.Assassin && attackMode === "S2": + return 128; + case charClass === sdk.player.class.Assassin && attackMode === "S4" && weaponClass === "ht2": + return 208; + } + // wolf or bear : + //AnimationSpeed ​​= [Hitshift * NeutralFrames / Delay] + return 256; + }, + + weaponFramesPerDirection: function (skill, weaponClass, charClass = GameData.myReference.classid) { + let attackMode = this.attackModeForSkill(skill, charClass); + /* + 2HT = “2 Hand Thrust” Spear + STF = “Staff” Staff, Large Axe, Maul, Pole arm + 2HS = “2 Hand Swing” 2-Handed Sword + BOW = “Bow” Bow + XBW = “Crossbow” Crossbow + HT1 = “One Hand-to-Hand” Shield + Claws + HT2 = “”Two Hand-to-Hand” Claws + Claws + 1HT = “1 Hand Thrust” Shield + (Throwing potion, Knife, Throwing Knife, Javelin) + 1HS = “1 Hand Swing” Shield + (Axe, Wand, Club, Scepter, Mace, Hammer, Sword, Throwing Axe, Orb) + HTH = “Hand To Hand” Shield + no weapon + 1SS = “Left Swing Right Swing” Left = 1HS, Right = 1HS + 1JT = “Left Jab Right Thrust” Left = 1HT, Right = 1HT + 1ST = “Left Swing Right Thrust” Left = 1HS, Right = 1HT + 1JS = “Left Jab Right Swing” Left = 1HT, Right = 1HS + */ + + /* + Amazon Assassin Barbarian Druid Necromancer Paladin Sorceress + A1 HTH 08 13 256 06 11 256 06 12 256 08 16 256 08 15 256 07 14 256 09 16 256 + A2 HTH --- 06 12 256 --- --- --- --- 08 16 256 + A1 HTx 06 11 208 + A2 HTx 06 12 208 + A1 1HS 10 16 256 07 15 256 07 16 256 09 19 256 09 19 256 07 15 256 12 20 256 + A1 2HS 12 20 256 11 23 256 08 18 256 10 21 256 11 23 256 08 18 256 14 24 256 + A2 2HS --- --- --- --- --- 08 19 256 --- + A1 1HT 09 15 256 07 15 256 07 16 256 08 19 256 09 19 256 08 17 256 11 19 256 + A1 2HT 11 18 256 10 23 256 09 19 256 09 23 256 10 24 256 08 20 256 13 23 256 + A2 2HT --- --- --- --- --- 09 20 256 --- + A1 STF 12 20 256 09 19 256 09 19 256 09 17 256 11 20 256 09 18 256 11 18 256 + A1 BOW 06 14 256 07 16 256 07 15 256 08 16 256 09 18 256 08 16 256 09 17 256 + A1 XBW 09 20 256 10 21 256 10 20 256 10 20 256 11 20 256 10 20 256 11 20 256 + + TH xxx 09 16 256 07 16 256 08 16 256 08 18 256 10 20 256 08 16 256 10 20 256 + KK xxx 04 13 256 + + S1 xxx xx 09 256 07 12 256 + S2 xxx 04 08 128 + S3 1Jx 08 12 256 + S3 1Sx 07 12 256 + S4 1Jx 08 16 256 + S4 1Sx 09 16 256 + S4 HT2 06 12 208 + + -------------------------------------------------- ------------------------------------------------ + Werewolf bear fetish vampire + A1 xxx 07 13 xxx 07 12 xxx 08 12 256 09 14 176 + S3 xxx 06 10 xxx 06 10 xxx + NU xxx xx 09 xxx xx 10 xxx + + -------------------------------------------------- ------------------------------------------------ + Rogue City Guard Eisenwolf Barbarian Mercenary + A1 xxx 06 15 256 11 16 256 06 15 256 05/12 16 256 + */ + switch (true) { + case charClass === sdk.player.class.Sorceress && attackMode.startsWith("A") && weaponClass === "hth": + return 16; + case charClass === sdk.player.class.Sorceress && attackMode === "A1" && weaponClass === "1hs": + return 20; + case charClass === sdk.player.class.Sorceress && attackMode.startsWith("A") && weaponClass === "2hs": + return 24; + case charClass === sdk.player.class.Sorceress && attackMode === "A1" && weaponClass === "1ht": + return 19; + case charClass === sdk.player.class.Sorceress && attackMode.startsWith("A") && weaponClass === "2ht": + return 23; + case charClass === sdk.player.class.Sorceress && attackMode === "A1" && weaponClass === "stf": + return 18; + case charClass === sdk.player.class.Sorceress && attackMode === "A1" && weaponClass === "bow": + return 17; + case charClass === sdk.player.class.Sorceress && attackMode === "A1" && weaponClass === "xbw": + return 20; + case charClass === sdk.player.class.Sorceress && attackMode === "TH": + return 20; + + case charClass === sdk.player.class.Paladin && attackMode.startsWith("A") && weaponClass === "hth": + return 14; + case charClass === sdk.player.class.Paladin && attackMode === "A1" && weaponClass === "1hs": + return 15; + case charClass === sdk.player.class.Paladin && attackMode === "A1" && weaponClass === "2hs": + return 18; + case charClass === sdk.player.class.Paladin && attackMode === "A2" && weaponClass === "2hs": + return 19; + case charClass === sdk.player.class.Paladin && attackMode === "A1" && weaponClass === "1ht": + return 17; + case charClass === sdk.player.class.Paladin && attackMode === "A1" && weaponClass === "2ht": + return 20; + case charClass === sdk.player.class.Paladin && attackMode === "A2" && weaponClass === "2ht": + return 20; + case charClass === sdk.player.class.Paladin && attackMode === "A1" && weaponClass === "stf": + return 18; + case charClass === sdk.player.class.Paladin && attackMode === "A1" && weaponClass === "bow": + return 16; + case charClass === sdk.player.class.Paladin && attackMode === "A1" && weaponClass === "xbw": + return 20; + case charClass === sdk.player.class.Paladin && attackMode === "TH": + return 16; + case charClass === sdk.player.class.Paladin && attackMode === "S1": + return 12; + + //TODO: full trag oul set + case charClass === sdk.player.class.Necromancer && attackMode.startsWith("A") && weaponClass === "hth": + return 15; + case charClass === sdk.player.class.Necromancer && attackMode === "A1" && weaponClass === "1hs": + return 19; + case charClass === sdk.player.class.Necromancer && attackMode.startsWith("A") && weaponClass === "2hs": + return 23; + case charClass === sdk.player.class.Necromancer && attackMode === "A1" && weaponClass === "1ht": + return 19; + case charClass === sdk.player.class.Necromancer && attackMode.startsWith("A") && weaponClass === "2ht": + return 24; + case charClass === sdk.player.class.Necromancer && attackMode === "A1" && weaponClass === "stf": + return 20; + case charClass === sdk.player.class.Necromancer && attackMode === "A1" && weaponClass === "bow": + return 18; + case charClass === sdk.player.class.Necromancer && attackMode === "A1" && weaponClass === "xbw": + return 20; + case charClass === sdk.player.class.Necromancer && attackMode === "TH": + return 20; + + case this.shiftState() === "wolf" && attackMode === "A1": + return 13; + case this.shiftState() === "wolf" && attackMode === "S3": + return 10; + + case this.shiftState() === "bear" && attackMode === "A1": + return 12; + case this.shiftState() === "bear" && attackMode === "S3": + return 10; + + case charClass === sdk.player.class.Druid && attackMode.startsWith("A") && weaponClass === "hth": + return 16; + case charClass === sdk.player.class.Druid && attackMode === "A1" && weaponClass === "1hs": + return 19; + case charClass === sdk.player.class.Druid && attackMode.startsWith("A") && weaponClass === "2hs": + return 21; + case charClass === sdk.player.class.Druid && attackMode === "A1" && weaponClass === "1ht": + return 19; + case charClass === sdk.player.class.Druid && attackMode.startsWith("A") && weaponClass === "2ht": + return 23; + case charClass === sdk.player.class.Druid && attackMode === "A1" && weaponClass === "stf": + return 17; + case charClass === sdk.player.class.Druid && attackMode === "A1" && weaponClass === "bow": + return 16; + case charClass === sdk.player.class.Druid && attackMode === "A1" && weaponClass === "xbw": + return 20; + case charClass === sdk.player.class.Druid && attackMode === "TH": + return 18; + + case charClass === sdk.player.class.Barbarian && attackMode.startsWith("A") && weaponClass === "hth": + return 12; + case charClass === sdk.player.class.Barbarian && attackMode === "A1" && weaponClass === "1hs": + return 16; + case charClass === sdk.player.class.Barbarian && attackMode.startsWith("A") && weaponClass === "2hs": + return 18; + case charClass === sdk.player.class.Barbarian && attackMode === "A1" && weaponClass === "1ht": + return 16; + case charClass === sdk.player.class.Barbarian && attackMode.startsWith("A") && weaponClass === "2ht": + return 19; + case charClass === sdk.player.class.Barbarian && attackMode === "A1" && weaponClass === "stf": + return 19; + case charClass === sdk.player.class.Barbarian && attackMode === "A1" && weaponClass === "bow": + return 15; + case charClass === sdk.player.class.Barbarian && attackMode === "A1" && weaponClass === "xbw": + return 20; + case charClass === sdk.player.class.Barbarian && attackMode === "TH": + return 16; + case charClass === sdk.player.class.Barbarian && attackMode === "S3": + return 12; + case charClass === sdk.player.class.Barbarian && attackMode === "S4": + return 16; + + case charClass === sdk.player.class.Assassin && attackMode === "A1" && weaponClass.startsWith("ht"): + return 11; + case charClass === sdk.player.class.Assassin && attackMode === "A2" && weaponClass.startsWith("ht"): + return 12; + case charClass === sdk.player.class.Assassin && attackMode === "A1" && weaponClass === "1hs": + return 15; + case charClass === sdk.player.class.Assassin && attackMode.startsWith("A") && weaponClass === "2hs": + return 23; + case charClass === sdk.player.class.Assassin && attackMode === "A1" && weaponClass === "1ht": + return 15; + case charClass === sdk.player.class.Assassin && attackMode.startsWith("A") && weaponClass === "2ht": + return 23; + case charClass === sdk.player.class.Assassin && attackMode === "A1" && weaponClass === "stf": + return 19; + case charClass === sdk.player.class.Assassin && attackMode === "A1" && weaponClass === "bow": + return 16; + case charClass === sdk.player.class.Assassin && attackMode === "A1" && weaponClass === "xbw": + return 21; + case charClass === sdk.player.class.Assassin && attackMode === "TH": + return 16; + case charClass === sdk.player.class.Assassin && attackMode === "KK": + return 13; + case charClass === sdk.player.class.Assassin && attackMode === "S2": + return 8; + case charClass === sdk.player.class.Assassin && attackMode === "S4" && weaponClass === "ht2": + return 12; + + case charClass === sdk.player.class.Amazon && attackMode.startsWith("A") && weaponClass === "hth": + return 13; + case charClass === sdk.player.class.Amazon && attackMode === "A1" && weaponClass === "1hs": + return 16; + case charClass === sdk.player.class.Amazon && attackMode.startsWith("A") && weaponClass === "2hs": + return 20; + case charClass === sdk.player.class.Amazon && attackMode === "A1" && weaponClass === "1ht": + return 15; + case charClass === sdk.player.class.Amazon && attackMode.startsWith("A") && weaponClass === "2ht": + return 18; + case charClass === sdk.player.class.Amazon && attackMode === "A1" && weaponClass === "stf": + return 20; + case charClass === sdk.player.class.Amazon && attackMode === "A1" && weaponClass === "bow": + return 14; + case charClass === sdk.player.class.Amazon && attackMode === "A1" && weaponClass === "xbw": + return 20; + case charClass === sdk.player.class.Amazon && attackMode === "TH": + return 16; + case charClass === sdk.player.class.Amazon && attackMode === "S1": + return 9; + } + return -1; + }, + + // attackFrames: function (skillId, weaponCode, ias = GameData.myReference.getStat(sdk.stats.Fasterattackrate), charClass = GameData.myReference.classid, weapon2Code = null) { + // // https://diablo3.ingame.de/forum/threads/1218516-FAQ-Bewegungs-und-Animationsgeschwindigkeiten-Teil-2?s=&postid=17610874 + // /* + // TODO + // bear or wolf only : + + // frames = {(256 * framesPerDirection) / [animationSpeed ​​* (100 + effectiveIAS + skillsIAS - weaponSpeedModifier + coldEffect) / 100]} - 1 + + // where : + // animationSpeed ​​= 256 * NeutralFrames / delay + // delay = 256 * CharFrames / ((100 + weaponIAS - weaponSpeedModifier) * CharSpeed ​​/ 100) + + // framesPerDirection ... The sum of all frames of our attack animation of the Werform. + // NeutralFrames ... Sum of the frames of our neutral animation (use while standing). + // CharFrames ... The sum of all frames of the attack animation that we would use in the unchanged state (with the exception of two-handed swords). + // CharSpeed ​​... This is the animation speed of the attack animation that the unchanged character would use. + // weaponIAS ... All IAS on our weapon or weapon base + // */ + // let weaponData = (new CSV("sdk/txt/weapons.txt")).findObject("code", weaponCode); + // if (!weaponData) { + // console.log(sdk.colors.Orange + "No weapon data found for code " + weaponCode); + // } + // let weaponClass = weaponData.wclass; + // let baseRate = 100; + // const BASE_ANIMATION_SPEED = 256; + + // let animationSpeed = this.weaponAttackAnimationSpeed(baseRate, weaponClass, charClass, this.shiftState()); + // let effectiveIAS = 120 * ias / (120 + ias); + // let skillsIAS = 0; //TODO: fanaticism or other sills bonus + slowdown skills malus + // let weaponSpeedModifier = (typeof weaponData.speed == "string") ? isNaN(parseInt(weaponData.speed)) ? 0 : parseInt(weaponData.speed) : weaponData.speed;// this.weaponSpeedModifier(weaponCode, charClass, weapon2Code); + // // me.getState(sdk.states.Frozen) or me.getState(sdk.states.Cold) ? + // let coldEffect = GameData.myReference.getState(sdk.states.Frozen) ? -50 : 0; // If we are affected by cold, as a player we receive a penalty of 50. + // let acceleration = baseRate + effectiveIAS + skillsIAS - weaponSpeedModifier + coldEffect; + // acceleration = Math.min(175, Math.max(15, acceleration)); + // let startingFrame = this.attackStartingFrame(weaponClass, charClass); + // let framesPerDirection = this.weaponFramesPerDirection(skillId, weaponClass, charClass); + // if (framesPerDirection < 1) { + // console.log(sdk.colors.Orange + "wrong value for framesPerDirection, IAS calculation may be wrong"); + // } + + // console.log("skillId " + skillId); + // console.log("charClass " + charClass); + // console.log("weaponCode " + weaponCode); + // console.log("weaponClass " + weaponClass); + // console.log("ias " + ias); + // console.log("effectiveIAS " + effectiveIAS); + // console.log("skillsIAS " + skillsIAS); + // console.log("weaponSpeedModifier " + weaponSpeedModifier); + // console.log("coldEffect " + coldEffect); + // console.log("acceleration " + acceleration); + // console.log("startingFrame " + startingFrame); + // console.log("framesPerDirection " + framesPerDirection); + // let frames = Math.ceil(BASE_ANIMATION_SPEED * (framesPerDirection - startingFrame) / Math.floor(animationSpeed * acceleration / 100)) - 1; + // return frames; + // }, + + // attackDuration: function (skillId, weaponCode, ias = GameData.myReference.getStat(sdk.stats.Fasterattackrate), charClass = GameData.myReference.classid, weapon2Code = null) { + // // https://diablo3.ingame.de/forum/threads/1218516-FAQ-Bewegungs-und-Animationsgeschwindigkeiten-Teil-2?s=&postid=17610874 + // return this.attackFrames(skillId, weaponCode, ias, charClass) / 25; + // }, + timeTillMissleImpact: function (skillId, monster) { + if (monster === undefined || skillId === undefined || !monster.attackable) return 0; + let missileName = getBaseStat("skills", skillId, "cltmissile"); + let missile = MissileData[missileName]; + if (!missile) { + missileName = getBaseStat("skills", skillId, "srvmissile"); + missile = MissileData[missileName]; + } + if (missile && missile.velocity > 0) { + const missileVelocityTPS = missile.velocity; + const missileVelocityTPF = missileVelocityTPS / 25; + const distanceForMissile = getDistance(me, monster); + // too far for missile to reach this position + if (distanceForMissile > missile.range) return 0; + const castTimeS = me.castingDuration(skillId); + return ((distanceForMissile / ((missileVelocityTPS / 32) * 25)) + castTimeS); + } + return 0; + } + }; + + function calculateKillableFallensByFrostNova() { + if (!Skill.canUse(sdk.skills.FrostNova)) return 0; + let fallens = [sdk.monsters.Fallen, sdk.monsters.Carver2, sdk.monsters.Devilkin2, sdk.monsters.DarkOne1, sdk.monsters.WarpedFallen, sdk.monsters.Carver1, sdk.monsters.Devilkin, sdk.monsters.DarkOne2]; + let area = me.area; + return getUnits(sdk.unittype.Monster) + .filter(unit => !!unit && fallens.includes(unit.classid) && unit.distance < 7) + .filter(function (unit) { + return unit.attackable + && typeof unit.x === "number" // happens if monster despawns + && !checkCollision(me, unit, Coords_1.Collision.BLOCK_MISSILE) + && unit.getStat(sdk.stats.ColdResist) < 100; + //&& !unit.getState(sdk.states.Frozen); + }) + .reduce(function (acc, cur) { + let classId = cur.classid, minDmg = GameData.skillDamage(sdk.skills.FrostNova, cur).min; + //let charLvl = GameData.monsterLevel(classId, area); + let currentHealth = GameData.monsterMaxHP(classId, area, cur.charlvl - GameData.monsterLevel(classId, area)) / 100 * (cur.hp * 100 / cur.hpmax); + if (currentHealth < minDmg) { + acc++; + } + return acc; + }, 0); + } + + function calculateKillableSummonsByNova() { + if (!Skill.canUse(sdk.skills.Nova)) return 0; + let summons = [ + sdk.monsters.Fallen, sdk.monsters.Carver2, sdk.monsters.Devilkin2, sdk.monsters.DarkOne1, sdk.monsters.WarpedFallen, sdk.monsters.Carver1, sdk.monsters.Devilkin, sdk.monsters.DarkOne2, + sdk.monsters.BurningDead, sdk.monsters.Returned1, sdk.monsters.Returned2, sdk.monsters.BoneWarrior1, sdk.monsters.BoneWarrior2 + ]; + return getUnits(sdk.unittype.Monster) + .filter(unit => !!unit && summons.includes(unit.classid) && unit.distance < 7) + .filter(function (unit) { + return unit.attackable + && typeof unit.x === "number" // happens if monster despawns + && !checkCollision(me, unit, Coords_1.Collision.BLOCK_MISSILE) + && Attack.checkResist(unit, "lightning"); + }) + .reduce(function (acc, cur) { + let classId = cur.classid, areaId = cur.area, minDmg = GameData.skillDamage(sdk.skills.Nova, cur).min; + let currentHealth = GameData.monsterMaxHP(classId, areaId, cur.charlvl - GameData.monsterLevel(classId, areaId)) / 100 * (cur.hp * 100 / cur.hpmax); + if (currentHealth < minDmg) { + acc++; + } + return acc; + }, 0); + } + + Object.defineProperty(Unit.prototype, "currentVelocity", { + get: function () { + if (!this.isMoving || this.isFrozen) return 0; + const velocity = this.isRunning ? MonsterData[this.classid].Run : MonsterData[this.classid].Velocity; + if (this.isChilled) { + let malus = MonsterData[this.classid].ColdEffect; + (malus > 0) && (malus = malus - 256); + return Math.max(1, ~~(velocity * (1 + malus))); + } + return velocity; + } + }); + + function targetPointForSkill (skillId, monster) { + if (monster === undefined || skillId === undefined || !monster.attackable) return null; + let missileName = getBaseStat("skills", skillId, "cltmissile"); + let missile = MissileData[missileName]; + if (!missile) { + missileName = getBaseStat("skills", skillId, "srvmissile"); + missile = MissileData[missileName]; + } + if (missile && missile.velocity > 0) { + if (monster.isMoving && (monster.targetx !== me.x || monster.targety !== me.y)) { + let startX = monster.x, startY = monster.y; + // tiles per second velocities + // ToDo: is monster slowed by freeze or something ? + let monsterVelocityTPS = monster.currentVelocity; + let missileVelocityTPS = missile.velocity; + // tiles per frame velocities + let monsterVelocityTPF = monsterVelocityTPS / 25; + let missileVelocityTPF = missileVelocityTPS / 25; + //console.log("monster is moving to "+monster.targetx+", "+monster.targety + " at speed "+monsterVelocity); + let path = getPath(monster.area, startX, startY, monster.targetx, monster.targety, 2, 1); + if (path && path.length) { + // path is reversed from target to monster, we will check from last path position (target) to monster position + path.reverse(); + let [diffS, diffF, found] = [0, 0, 0]; + let time = { missile: {}, monster: {} }; + for (let i = 0; i < path.length; i++) { + let pos = path[i]; + // ToDo : does missile spawn at me position ? + let distanceForMissile = getDistance(me, pos); + if (distanceForMissile > missile.range) { + // too far for missile to reach this position + continue; + } + let distanceForMonster = getDistance({ x: startX, y: startY }, pos); + let timeForMonsterF = distanceForMonster / monsterVelocityTPF; + // time in seconds + // let castTimeS = GameData.castingDuration(skillId); + // let timeForMissileS = distanceForMissile / missileVelocityTPS + castTimeS; + // time in frames + let castTimeF = me.castingFrames(skillId); + let timeForMissileF = distanceForMissile / missileVelocityTPF + castTimeF; + // let timeForMonsterS = distanceForMonster / monsterVelocityTPS; + // Todo: missile and monster size + // diff seconds + // diffS = timeForMissileS-timeForMonsterS; + // diff frames + diffF = timeForMissileF - timeForMonsterF; + // diff > 0 : missile will reach pos after monster + // diff < 0 : missile will reach pos before monster + // console.log("time for monster to reach "+pos+" = "+timeForMonster); + // console.log("time for missile to reach "+pos+" = "+timeForMissile); + // console.log("diff = "+diff) + if (i === 0 && diffF >= 0) { + // last path position and missile is late, we can't predict next monster target, shoot at last path position + // it may fail because monster may be moving at other target while missile is arriving + // console.log("missile will be too late"); + found = pos; + // time.missile.seconds = timeForMissileS; + time.missile.frames = timeForMissileF; + // time.monster.seconds = timeForMonsterS; + time.monster.frames = timeForMonsterF; + break; + } + // the number of frames needed for unit to move 1 tile + let timeToMoveOneTileMonsterF = 1 / monsterVelocityTPF; + // let timeToMoveOneTileMissileF = 1 / missileVelocityTPF; + // while missile is travelling, monster will continue to move + // if the difference is greater than the time a monster will move 1 tile, the missile will miss + // todo: monster size, missile size + if (diffF >= -1 * timeToMoveOneTileMonsterF && diffF <= 1 * timeToMoveOneTileMonsterF) { + found = pos; + // time.missile.seconds = timeForMissileS; + time.missile.frames = timeForMissileF; + // time.monster.seconds = timeForMonsterS; + time.monster.frames = timeForMonsterF; + break; + } + } + if (found) { + // console.log("missile will hit monster in "+time.missile.seconds+" ("+time.missile.frames+") at "+found.x+", "+found.y); + // console.log("time for monster = "+time.monster.seconds+ " ("+time.monster.frames+")") + // console.log("diff missile-monster = "+diffS+ " ("+diffF+")"); + return found; + } + } + } + } + return null; + } + + // Export data + GameData.isEnemy = isEnemy; + GameData.isAlive = isAlive; + GameData.onGround = onGround; + GameData.calculateKillableFallensByFrostNova = calculateKillableFallensByFrostNova; + GameData.calculateKillableSummonsByNova = calculateKillableSummonsByNova; + GameData.targetPointForSkill = targetPointForSkill; + module.exports = GameData; })(module, require); diff --git a/libs/SoloPlay/Modules/GameData/LocaleStringID.js b/libs/SoloPlay/Modules/GameData/LocaleStringID.js index 133617d5..96332bc3 100644 --- a/libs/SoloPlay/Modules/GameData/LocaleStringID.js +++ b/libs/SoloPlay/Modules/GameData/LocaleStringID.js @@ -4,7803 +4,7803 @@ * @desc locale string indexes from NameStr ids */ (function (module) { - let LocaleStringID = { - "WarrivAct1IntroGossip1": 0, - "WarrivAct1IntroPalGossip1": 1, - "WarrivGossip1": 2, - "WarrivGossip2": 3, - "WarrivGossip3": 4, - "WarrivGossip4": 5, - "WarrivGossip5": 6, - "WarrivGossip6": 7, - "WarrivGossip7": 8, - "WarrivGossip8": 9, - "WarrivGossip9": 10, - "AkaraIntroGossip1": 11, - "AkaraIntroSorGossip1": 12, - "AkaraGossip1": 13, - "AkaraGossip2": 14, - "AkaraGossip3": 15, - "AkaraGossip4": 16, - "AkaraGossip5": 17, - "AkaraGossip6": 18, - "AkaraGossip7": 19, - "AkaraGossip8": 20, - "AkaraGossip9": 21, - "AkaraGossip10": 22, - "AkaraGossip11": 23, - "KashyaIntroGossip1": 24, - "KashyaIntroAmaGossip1": 25, - "KashyaGossip1": 26, - "KashyaGossip2": 27, - "KashyaGossip3": 28, - "KashyaGossip4": 29, - "KashyaGossip5": 30, - "KashyaGossip6": 31, - "KashyaGossip7": 32, - "KashyaGossip8": 33, - "KashyaGossip9": 34, - "KashyaGossip10": 35, - "CharsiIntroGossip1": 36, - "CharsiIntroBarGossip1": 37, - "CharsiGossip1": 38, - "CharsiGossip2": 39, - "CharsiGossip3": 40, - "CharsiGossip4": 41, - "CharsiGossip5": 42, - "CharsiGossip6": 43, - "CharsiGossip7": 44, - "GheedIntroGossip1": 45, - "GheedIntroNecGossip1": 46, - "GheedGossip1": 47, - "GheedGossip2": 48, - "GheedGossip3": 49, - "GheedGossip4": 50, - "GheedGossip5": 51, - "GheedGossip6": 52, - "GheedGossip7": 53, - "CainGossip1": 54, - "CainGossip2": 55, - "CainGossip3": 56, - "CainGossip4": 57, - "CainGossip5": 58, - "RogueSignpostGossip1": 59, - "RogueSignpostGossip2": 60, - "RogueSignpostGossip3": 61, - "RogueSignpostGossip4": 62, - "RogueSignpostGossip5": 63, - "A1Q1InitAkara": 64, - "A1Q1AfterInitAkara": 65, - "A1Q1AfterInitKashya": 66, - "A1Q1AfterInitCharsiMain": 67, - "A1Q1AfterInitCharsiAlt": 68, - "A1Q1AfterInitGheed": 69, - "A1Q1AfterInitWarriv": 70, - "A1Q1EarlyReturnAkara": 71, - "A1Q1EarlyReturnKashya": 72, - "A1Q1EarlyReturnCharsi": 73, - "A1Q1EarlyReturnGheed": 74, - "A1Q1EarlyReturnWarriv": 75, - "A1Q1SuccessfulAkara": 76, - "A1Q1SuccessfulKashya": 77, - "A1Q1SuccessfulCharsi": 78, - "A1Q1SuccessfulGheed": 79, - "A1Q1SuccessfulWarriv": 80, - "A1Q2InitKashya": 81, - "A1Q2AfterInitKashya": 82, - "A1Q2AfterInitCharsi": 83, - "A1Q2AfterInitGheed": 84, - "A1Q2AfterInitAkara": 85, - "A1Q2AfterInitWarriv": 86, - "A1Q2EarlyReturnKashya": 87, - "A1Q2EarlyReturnAkara": 88, - "A1Q2EarlyReturnCharsi": 89, - "A1Q2EarlyReturnGheed": 90, - "A1Q2EarlyReturnWarriv": 91, - "A1Q2SuccessfulKashya": 92, - "A1Q2SuccessfulAkara": 93, - "A1Q2SuccessfulCharsi": 94, - "A1Q2SuccessfulGheed": 95, - "A1Q2SuccessfulWarriv": 96, - "A1Q4InitAkara": 97, - "A1Q4AfterInitScrollKashya": 98, - "A1Q4AfterInitScrollAkara": 99, - "A1Q4AfterInitScrollCharsi": 100, - "A1Q4AfterInitScrollWarriv": 101, - "A1Q4AfterInitScrollGheed": 102, - "A1Q4InstructionsCharsi": 103, - "A1Q4EarlyReturnSAkara": 104, - "A1Q4EarlyReturnSKashya": 105, - "A1Q4EarlyReturnSGheed": 106, - "A1Q4EarlyReturnSWarriv": 107, - "A1Q4SuccessfulScrollKashya": 108, - "A1Q4SuccessfulScrollCharsi": 109, - "A1Q4SuccessfulScrollGheed": 110, - "A1Q4SuccessfulScrollWarriv": 111, - "A1Q4InstructionsAkara": 112, - "A1Q4EarlyReturnKashya": 113, - "A1Q4EarlyReturnCharsi": 114, - "A1Q4EarlyReturnGheed": 115, - "A1Q4EarlyReturnWarriv": 116, - "A1Q4EarlyReturnAkara": 117, - "A1Q4QuestSuccessfulAkara": 118, - "A1Q4QuestSuccessfulKashya": 119, - "A1Q4QuestSuccessfulGheed": 120, - "A1Q4QuestSuccessfulCharsi": 121, - "A1Q4QuestSuccessfulWarriv": 122, - "A1Q4QuestSuccessfulCain": 123, - "A1Q4RescuedByHeroCain": 124, - "A1Q4RescuedByRoguesCain": 125, - "A1Q4TragedyOfTristramCain": 126, - "A1Q5InitQuestTome": 127, - "A1Q5AfterInitGheed": 128, - "A1Q5AfterInitCharsi": 129, - "A1Q5AfterInitAkara": 130, - "A1Q5AfterInitCain": 131, - "A1Q5AfterInitWarriv": 132, - "A1Q5AfterInitKashya": 133, - "A1Q5EarlyReturnKashya": 134, - "A1Q5EarlyReturnCain": 135, - "A1Q5EarlyReturnWarriv": 136, - "A1Q5EarlyReturnCharsi": 137, - "A1Q5EarlyReturnAkara": 138, - "A1Q5EarlyReturnGheed": 139, - "A1Q5SuccessfulKashya": 140, - "A1Q5SuccessfulWarriv": 141, - "A1Q5SuccessfulGheed": 142, - "A1Q5SuccessfulAkara": 143, - "A1Q5SuccessfulCharsi": 144, - "A1Q5SuccessfulCain": 145, - "A1Q3InitCharsi": 146, - "A1Q3AfterInitCain": 147, - "A1Q3AfterInitAkara": 148, - "A1Q3AfterInitKashya": 149, - "A1Q3AfterInitCharsi": 150, - "A1Q3AfterInitGheed": 151, - "A1Q3AfterInitGheedAlt": 152, - "A1Q3AfterInitWarriv": 153, - "A1Q3EarlyReturnCain": 154, - "A1Q3EarlyReturnAkara": 155, - "A1Q3EarlyReturnKashya": 156, - "A1Q3EarlyReturnCharsi": 157, - "A1Q3EarlyReturnGheed": 158, - "A1Q3EarlyReturnWarriv": 159, - "A1Q3SuccessfulCain": 160, - "A1Q3SuccessfulAkara": 161, - "A1Q3SuccessfulKashya": 162, - "A1Q3SuccessfulCharsi": 163, - "A1Q3SuccessfulGheed": 164, - "A1Q3SuccessfulWarriv": 165, - "A1Q6InitCain": 166, - "A1Q6AfterInitCain": 167, - "A1Q6AfterInitAkara": 168, - "A1Q6AfterInitCharsi": 169, - "A1Q6AfterInitGheed": 170, - "A1Q6AfterInitWarriv": 171, - "A1Q6AfterInitKashya": 172, - "A1Q6EarlyReturnCain": 173, - "A1Q6EarlyReturnAkara": 174, - "A1Q6EarlyReturnGheed": 175, - "A1Q6EarlyReturnCharsi": 176, - "A1Q6EarlyReturnWarriv": 177, - "A1Q6EarlyReturn2Kashya": 178, - "A1Q6SuccessfulAkara": 179, - "A1Q6SuccessfulCharsi": 180, - "A1Q6SuccessfulKashya": 181, - "A1Q6SuccessfulGheed": 182, - "A1Q6SuccessfulWarriv": 183, - "A1Q6SuccessfulCain": 184, - "PalaceGuardGossip1": 185, - "PalaceGuardGossip2": 186, - "PalaceGuardGossip3": 187, - "PalaceGuardGossip4": 188, - "PalaceGuardGossip5": 189, - "GriezIntroGossip1": 190, - "GriezGossip1": 191, - "GriezGossip2": 192, - "GriezGossip3": 193, - "GriezGossip4": 194, - "GriezGossip5": 195, - "GriezGossip6": 196, - "GriezGossip7": 197, - "GriezGossip8": 198, - "GriezGossip9": 199, - "GriezGossip10": 200, - "GriezGossip11": 201, - "GriezGossip12": 202, - "ElzixIntroGossip1": 203, - "ElzixIntroNecGossip1": 204, - "ElzixGossip1": 205, - "ElzixGossip2": 206, - "ElzixGossip3": 207, - "ElzixGossip4": 208, - "ElzixGossip5": 209, - "ElzixGossip6": 210, - "ElzixGossip7": 211, - "ElzixGossip8": 212, - "ElzixGossip9": 213, - "ElzixGossip10": 214, - "WarrivAct2IntroGossip1": 215, - "WarrivAct2Gossip1": 216, - "WarrivAct2Gossip2": 217, - "WarrivAct2Gossip3": 218, - "WarrivAct2Gossip4": 219, - "WarrivAct2Gossip5": 220, - "AtmaIntroGossip1": 221, - "AtmaGossip1": 222, - "AtmaGossip2": 223, - "AtmaGossip3": 224, - "AtmaGossip4": 225, - "AtmaGossip5": 226, - "AtmaGossip6": 227, - "AtmaGossip7": 228, - "AtmaGossip8": 229, - "GeglashIntroGossip1": 230, - "GeglashIntroBarGossip1": 231, - "GeglashGossip1": 232, - "GeglashGossip2": 233, - "GeglashGossip3": 234, - "GeglashGossip4": 235, - "GeglashGossip5": 236, - "GeglashGossip6": 237, - "GeglashGossip7": 238, - "GeglashGossip8": 239, - "GeglashGossip9": 240, - "MeshifIntroGossip1": 241, - "MeshifIntroAmaGossip1": 242, - "MeshifGossip1": 243, - "MeshifGossip2": 244, - "MeshifGossip3": 245, - "MeshifGossip4": 246, - "MeshifGossip5": 247, - "MeshifGossip6": 248, - "MeshifGossip7": 249, - "MeshifGossip8": 250, - "MeshifGossip9": 251, - "MeshifGossip10": 252, - "JerhynActIntroGossip1": 253, - "JerhynActIntroMoreGossip1": 254, - "JerhynIntroGossip1": 255, - "JerhynGossip1": 256, - "JerhynGossip2": 257, - "JerhynGossip3": 258, - "JerhynGossip4": 259, - "JerhynGossip5": 260, - "JerhynGossip6": 261, - "JerhynGossip7": 262, - "FaraIntroGossip1": 263, - "FaraIntroPalGossip1": 264, - "FaraGossip1": 265, - "FaraGossip2": 266, - "FaraGossip3": 267, - "FaraGossip4": 268, - "FaraGossip5": 269, - "FaraGossip6": 270, - "FaraGossip7": 271, - "FaraGossip8": 272, - "FaraGossip9": 273, - "LysanderIntroGossip1": 274, - "LysanderGossip1": 275, - "LysanderGossip2": 276, - "LysanderGossip3": 277, - "LysanderGossip4": 278, - "LysanderGossip5": 279, - "LysanderGossip6": 280, - "LysanderGossip7": 281, - "LysanderGossip8": 282, - "LysanderGossip9": 283, - "LysanderGossip10": 284, - "DrognanIntroGossip1": 285, - "DrognanIntroSorGossip1": 286, - "DrognanGossip1": 287, - "DrognanGossip2": 288, - "DrognanGossip3": 289, - "DrognanGossip4": 290, - "DrognanGossip5": 291, - "DrognanGossip6": 292, - "DrognanGossip7": 293, - "DrognanGossip8": 294, - "DrognanGossip9": 295, - "DrognanGossip10": 296, - "CainAct2Gossip1": 297, - "CainAct2Gossip2": 298, - "CainAct2Gossip3": 299, - "CainAct2Gossip4": 300, - "CainAct2Gossip5": 301, - "TyraelGossip1": 302, - "Desert2GuardGossip1": 303, - "A2Q1InitAtma": 304, - "A2Q1AfterInitGreiz": 305, - "A2Q1AfterInitElzix": 306, - "A2Q1AfterInitWarrivAct2": 307, - "A2Q1AfterInitGeglash": 308, - "A2Q1AfterInitFara": 309, - "A2Q1AfterInitAtma": 310, - "A2Q1AfterInitMeshif": 311, - "A2Q1AfterInitDrognan": 312, - "A2Q1AfterInitLysander": 313, - "A2Q1AfterInitCain": 314, - "A2Q1EarlyReturnWarrivAct2": 315, - "A2Q1EarlyReturnMeshif": 316, - "A2Q1EarlyReturnAtma": 317, - "A2Q1EarlyReturnGreiz": 318, - "A2Q1EarlyReturnGeglash": 319, - "A2Q1EarlyReturnElzix": 320, - "A2Q1EarlyReturnLysander": 321, - "A2Q1EarlyReturnDrognan": 322, - "A2Q1EarlyReturnFara": 323, - "A2Q1EarlyReturnCain": 324, - "A2Q1SuccessfulGreiz": 325, - "A2Q1SuccessfulDrognan": 326, - "A2Q1SuccessfulLysander": 327, - "A2Q1SuccessfulMeshif": 328, - "A2Q1SuccessfulGeglash": 329, - "A2Q1SuccessfulElzix": 330, - "A2Q1SuccessfulWarrivAct2": 331, - "A2Q1SuccessfulFara": 332, - "A2Q1SuccessfulCain": 333, - "A2Q1SuccessfulAtma": 334, - "A2Q2EarlyReturnScrollCain": 335, - "A2Q2EarlyReturnCapCain": 336, - "A2Q2EarlyReturnStaveCain": 337, - "A2Q2EarlyReturnCubeCain": 338, - "A2Q2SuccessfulStaffCain": 339, - "A2Q3AfterInitJerhyn": 340, - "A2Q3AfterInitGreiz": 341, - "A2Q3AfterInitElzix": 342, - "A2Q3AfterInitWarrivAct2": 343, - "A2Q3AfterInitAtma": 344, - "A2Q3AfterInitGeglash": 345, - "A2Q3AfterInitFara": 346, - "A2Q3AfterInitLysander": 347, - "A2Q3AfterInitDrognan": 348, - "A2Q3AfterInitMeshif": 349, - "A2Q3AfterInitCain": 350, - "A2Q3EarlyReturnJerhyn": 351, - "A2Q3EarlyReturnGreiz": 352, - "A2Q3EarlyReturnWarrivAct2": 353, - "A2Q3EarlyReturnGeglash": 354, - "A2Q3EarlyReturnMeshif": 355, - "A2Q3EarlyReturnFara": 356, - "A2Q3EarlyReturnLysander": 357, - "A2Q3EarlyReturnDrognan": 358, - "A2Q3EarlyReturnElzix": 359, - "A2Q3EarlyReturnCain": 360, - "A2Q3EarlyReturnAtma": 361, - "A2Q3SuccessfulJerhyn": 362, - "A2Q3SuccessfulGreiz": 363, - "A2Q3SuccessfulElzix": 364, - "A2Q3SuccessfulGeglash": 365, - "A2Q3SuccessfulWarrivAct2": 366, - "A2Q3SuccessfulMeshif": 367, - "A2Q3SuccessfulAtma": 368, - "A2Q3SuccessfulFara": 369, - "A2Q3SuccessfulLysander": 370, - "A2Q3SuccessfulDrognan": 371, - "A2Q3SuccessfulCain": 372, - "A2Q4InitDrognan": 373, - "A2Q4AfterInitFara": 374, - "A2Q4AfterInitGreiz": 375, - "A2Q4AfterInitElzix": 376, - "A2Q4AfterInitJerhyn": 377, - "A2Q4AfterInitCain": 378, - "A2Q4AfterInitGeglash": 379, - "A2Q4AfterInitAtma": 380, - "A2Q4AfterInitWarrivAct2": 381, - "A2Q4AfterInitLysander": 382, - "A2Q4AfterInitDrognan": 383, - "A2Q4AfterInitMeshif": 384, - "A2Q4EarlyReturnElzix": 385, - "A2Q4EarlyReturnJerhyn": 386, - "A2Q4EarlyReturnGreiz": 387, - "A2Q4EarlyReturnDrognan": 388, - "A2Q4EarlyReturnLysander": 389, - "A2Q4EarlyReturnFara": 390, - "A2Q4EarlyReturnGeglash": 391, - "A2Q4EarlyReturnMeshif": 392, - "A2Q4EarlyReturnAtma": 393, - "A2Q4EarlyReturnWarrivAct2": 394, - "A2Q4EarlyReturnCain": 395, - "A2Q4SuccessfulNarrator": 396, - "A2Q4SuccessfulGriez": 397, - "A2Q4SuccessfulJerhyn": 398, - "A2Q4SuccessfulDrognan": 399, - "A2Q4SuccessfulElzix": 400, - "A2Q4SuccessfulGeglash": 401, - "A2Q4SuccessfulMeshif": 402, - "A2Q4SuccessfulWarrivAct2": 403, - "A2Q4SuccessfulFara": 404, - "A2Q4SuccessfulLysander": 405, - "A2Q4SuccessfulAtma": 406, - "A2Q4SuccessfulCain": 407, - "A2Q5EarlyReturnGreiz": 408, - "A2Q5EarlyReturnJerhyn": 409, - "A2Q5EarlyReturnDrognan": 410, - "A2Q5EarlyReturnLysander": 411, - "A2Q5EarlyReturnMeshif": 412, - "A2Q5EarlyReturnWarrivAct2": 413, - "A2Q5EarlyReturnAtma": 414, - "A2Q5EarlyReturnGeglash": 415, - "A2Q5EarlyReturnFara": 416, - "A2Q5EarlyReturnElzix": 417, - "A2Q5EarlyReturnCain": 418, - "A2Q5SuccessfulGreiz": 419, - "A2Q5SuccessfulGeglash": 420, - "A2Q5SuccessfulJerhyn": 421, - "A2Q5SuccessfulDrognan": 422, - "A2Q5SuccessfulElzix": 423, - "A2Q5SuccessfulWarrivAct2": 424, - "A2Q5SuccessfulMeshif": 425, - "A2Q5SuccessfulLysander": 426, - "A2Q5SuccessfulAtma": 427, - "A2Q5SuccessfulFara": 428, - "A2Q5SuccessfulCain": 429, - "A2Q6InitJerhyn": 430, - "A2Q6AfterInitJerhyn": 431, - "A2Q6AfterInitElzix": 432, - "A2Q6AfterInitWarrivAct2": 433, - "A2Q6AfterInitAtma": 434, - "A2Q6AfterInitGeglash": 435, - "A2Q6AfterInitMeshif": 436, - "A2Q6AfterInitFara": 437, - "A2Q6AfterInitLysander": 438, - "A2Q6AfterInitDrognan": 439, - "A2Q6AfterInitCain": 440, - "A2Q6AfterInitGreiz": 441, - "A2Q6SuccessfulJerhyn": 442, - "A2Q6SuccessfulElzix": 443, - "A2Q6SuccessfulLysander": 444, - "A2Q6SuccessfulAtma": 445, - "A2Q6SuccessfulWarrivAct2": 446, - "A2Q6SuccessfulFara": 447, - "A2Q6SuccessfulGeglash": 448, - "A2Q6SuccessfulDrognan": 449, - "A2Q6SuccessfulMeshif": 450, - "A2Q6SuccessfulGreiz": 451, - "A2Q6SuccessfulCain": 452, - "NatalyaIntroGossip1": 453, - "NatalyaGossip1": 454, - "NatalyaGossip2": 455, - "NatalyaGossip3": 456, - "NatalyaGossip4": 457, - "CainAct3IntroGossip1": 458, - "CainAct3Gossip1": 459, - "CainAct3Gossip2": 460, - "CainAct3Gossip3": 461, - "CainAct3Gossip4": 462, - "CainAct3Gossip5": 463, - "CainAct3Gossip6": 464, - "HratliActIntroGossip1": 465, - "HratliActIntroSorGossip1": 466, - "HratliGossip1": 467, - "HratliGossip2": 468, - "HratliGossip3": 469, - "HratliGossip4": 470, - "HratliGossip5": 471, - "HratliGossip6": 472, - "HratliGossip7": 473, - "HratliGossip8": 474, - "HratliGossip9": 475, - "HratliGossip10": 476, - "HratliGossip11": 477, - "MeshifAct3IntroGossip1": 478, - "MeshifAct3IntroBarGossip1": 479, - "MeshifAct3Gossip1": 480, - "MeshifAct3Gossip2": 481, - "MeshifAct3Gossip3": 482, - "MeshifAct3Gossip4": 483, - "MeshifAct3Gossip5": 484, - "MeshifAct3Gossip6": 485, - "MeshifAct3Gossip7": 486, - "MeshifAct3Gossip8": 487, - "MeshifAct3Gossip9": 488, - "MeshifAct3Gossip10": 489, - "AshearaIntroGossip1": 490, - "AshearaIntroAmaGossip1": 491, - "AshearaGossip1": 492, - "AshearaGossip2": 493, - "AshearaGossip3": 494, - "AshearaGossip4": 495, - "AshearaGossip5": 496, - "AshearaGossip6": 497, - "AshearaGossip7": 498, - "AshearaGossip8": 499, - "AshearaGossip9": 500, - "AlkorIntroGossip1": 501, - "AlkorIntroNecGossip1": 502, - "AlkorGossip1": 503, - "AlkorGossip2": 504, - "AlkorGossip3": 505, - "AlkorGossip4": 506, - "AlkorGossip5": 507, - "AlkorGossip6": 508, - "AlkorGossip7": 509, - "AlkorGossip8": 510, - "AlkorGossip9": 511, - "AlkorGossip10": 512, - "AlkorGossip11": 513, - "OrmusIntroGossip1": 514, - "OrmusIntroPalGossip1": 515, - "OrmusGossip1": 516, - "OrmusGossip2": 517, - "OrmusGossip3": 518, - "OrmusGossip4": 519, - "OrmusGossip5": 520, - "OrmusGossip6": 521, - "OrmusGossip7": 522, - "OrmusGossip8": 523, - "OrmusGossip9": 524, - "OrmusGossip10": 525, - "OrmusGossip11": 526, - "A3Q4Init1CainAct3": 527, - "A3Q4Init1Asheara": 528, - "A3Q4Init2MeshifAct3": 529, - "A3Q4Init2Natalya": 530, - "A3Q4Init3CainAct3": 531, - "A3Q4Init3Hratli": 532, - "A3Q4Init3Asheara": 533, - "A3Q4AfterInitAlkor": 534, - "A3Q4AfterInitOrmus": 535, - "A3Q4AfterInitHratli": 536, - "A3Q4AfterInitNatalya": 537, - "A3Q4SuccessfulAlkor": 538, - "A3Q4SuccessfulMeshifAct3": 539, - "A3Q4SuccessfulCainAct3": 540, - "A3Q4SuccessfulOrmus": 541, - "A3Q4SuccessfulNatalya": 542, - "A3Q2InitCain": 543, - "A3Q2EarlyReturnHeartCain": 544, - "A3Q2EarlyReturnEyeCain": 545, - "A3Q2EarlyReturnBrainCain": 546, - "A3Q2EarlyReturnFlailCain": 547, - "A3Q2SuccessfulCain": 548, - "A3Q1InitAlkor": 549, - "A3Q1AfterInitAlkor": 550, - "A3Q1AfterInitOrmus": 551, - "A3Q1AfterInitMeshifAct3": 552, - "A3Q1AfterInitAsheara": 553, - "A3Q1AfterInitHratli": 554, - "A3Q1AfterInitCainAct3": 555, - "A3Q1AfterInitNatalya": 556, - "A3Q1EarlyReturnAlkor": 557, - "A3Q1EarlyReturnOrmus": 558, - "A3Q1EarlyReturnMeshifAct3": 559, - "A3Q1EarlyReturnAsheara": 560, - "A3Q1EarlyReturnHratli": 561, - "A3Q1EarlyReturnCainAct3": 562, - "A3Q1EarlyReturnNatalya": 563, - "A3Q1SuccessfulAlkor": 564, - "A3Q1SuccessfulOrmus": 565, - "A3Q1SuccessfulMeshifAct3": 566, - "A3Q1SuccessfulAsheara": 567, - "A3Q1SuccessfulHratli": 568, - "A3Q1SuccessfulCainAct3": 569, - "A3Q1SuccessfulNatalya": 570, - "A3Q3InitHratli": 571, - "A3Q3AfterInitAlkor": 572, - "A3Q3AfterInitOrmus": 573, - "A3Q3AfterInitMeshifAct3": 574, - "A3Q3AfterInitAsheara": 575, - "A3Q3AfterInitHratli": 576, - "A3Q3AfterInitCainAct3": 577, - "A3Q3AfterInitNatalya": 578, - "A3Q3EarlyReturnAlkor": 579, - "A3Q3EarlyReturnOrmus": 580, - "A3Q3EarlyReturnMeshifAct3": 581, - "A3Q3EarlyReturnAsheara": 582, - "A3Q3EarlyReturnHratli": 583, - "A3Q3EarlyReturnCainAct3": 584, - "A3Q3EarlyReturnNatalya": 585, - "A3Q3SuccessfulAlkor": 586, - "A3Q3SuccessfulOrmus": 587, - "A3Q3SuccessfulMeshifAct3": 588, - "A3Q3SuccessfulAsheara": 589, - "A3Q3SuccessfulHratli": 590, - "A3Q3SuccessfulCainAct3": 591, - "A3Q3SuccessfulNatalya": 592, - "A3Q3RewardOrmus": 593, - "A3Q5InitOrmus": 594, - "A3Q5AfterInitAlkor": 595, - "A3Q5AfterInitAlkorVA": 596, - "A3Q5AfterInitOrmus": 597, - "A3Q5AfterInitOrmusVA": 598, - "A3Q5AfterInitMeshifAct3": 599, - "A3Q5AfterInitMeshifAct3VA": 600, - "A3Q5AfterInitAsheara": 601, - "A3Q5AfterInitAshearaVA": 602, - "A3Q5AfterInitHratli": 603, - "A3Q5AfterInitHratliVA": 604, - "A3Q5AfterInitCainAct3": 605, - "A3Q5AfterInitCainAct3VA": 606, - "A3Q5AfterInitNatalya": 607, - "A3Q5AfterInitNatalyaVA": 608, - "A3Q5EarlyReturnAlkor": 609, - "A3Q5EarlyReturnAlkorVA": 610, - "A3Q5EarlyReturnOrmus": 611, - "A3Q5EarlyReturnMeshifAct3": 612, - "A3Q5EarlyReturnMeshifAct3VA": 613, - "A3Q5EarlyReturnAsheara": 614, - "A3Q5EarlyReturnAshearaVA": 615, - "A3Q5EarlyReturnHratli": 616, - "A3Q5EarlyReturnHratliVA": 617, - "A3Q5EarlyReturnCainAct3": 618, - "A3Q5EarlyReturnNatalya": 619, - "A3Q5EarlyReturnNatalyaVA": 620, - "A3Q5SuccessfulAlkor": 621, - "A3Q5SuccessfulOrmus": 622, - "A3Q5SuccessfulMeshifAct3": 623, - "A3Q5SuccessfulAsheara": 624, - "A3Q5SuccessfulHratli": 625, - "A3Q5SuccessfulCainAct3": 626, - "A3Q5SuccessfulNatalya": 627, - "A3Q6InitOrmus": 628, - "A3Q6AfterInitAlkor": 629, - "A3Q6AfterInitAlkorVA": 630, - "A3Q6AfterInitOrmus": 631, - "A3Q6AfterInitOrmusVA": 632, - "A3Q6AfterInitMeshifAct3": 633, - "A3Q6AfterInitMeshifAct3VA": 634, - "A3Q6AfterInitAsheara": 635, - "A3Q6AfterInitAshearaVA": 636, - "A3Q6AfterInitHratli": 637, - "A3Q6AfterInitHratliVA": 638, - "A3Q6AfterInitCainAct3": 639, - "A3Q6AfterInitCainAct3VA": 640, - "A3Q6AfterInitNatalya": 641, - "A3Q6AfterInitNatalyaVA": 642, - "A3Q6EarlyReturnAlkor": 643, - "A3Q6EarlyReturnAlkorVA": 644, - "A3Q6EarlyReturnOrmus": 645, - "A3Q6EarlyReturnOrmusVA": 646, - "A3Q6EarlyReturnMeshifAct3": 647, - "A3Q6EarlyReturnMeshifAct3VA": 648, - "A3Q6EarlyReturnAsheara": 649, - "A3Q6EarlyReturnAshearaVA": 650, - "A3Q6EarlyReturnHratli": 651, - "A3Q6EarlyReturnHratliVA": 652, - "A3Q6EarlyReturnCainAct3": 653, - "A3Q6EarlyReturnCainAct3VA": 654, - "A3Q6EarlyReturnNatalya": 655, - "A3Q6EarlyReturnNatalyaVA": 656, - "A3Q6SuccessfulAlkor": 657, - "A3Q6SuccessfulOrmus": 658, - "A3Q6SuccessfulMeshifAct3": 659, - "A3Q6SuccessfulAsheara": 660, - "A3Q6SuccessfulHratli": 661, - "A3Q6SuccessfulCainAct3": 662, - "A3Q6SuccessfulNatalya": 663, - "TyraelActIntroGossip1": 664, - "TyraelAct4Gossip1": 665, - "CainAct4IntroGossip1": 666, - "CainAct4Gossip1": 667, - "HellsAngelGossip1": 668, - "HellsAngelGossip2": 669, - "A4Q1InitTyrael": 670, - "A4Q1AfterInitTyrael": 671, - "A4Q1AfterInitCain": 672, - "A4Q1EarlyReturnTyrael": 673, - "A4Q1EarlyReturnCain": 674, - "A4Q1SuccessfulIzual": 675, - "A4Q1SuccessfulTyrael": 676, - "A4Q1SuccessfulCain": 677, - "A4Q3InitHasStoneCain": 678, - "A4Q3InitNoStoneCain": 679, - "A4Q3SuccessfulCain": 680, - "A4Q2InitTyrael": 681, - "A4Q2AfterInitCain": 682, - "A4Q2AfterInitTyrael": 683, - "A4Q2SuccessfulTyrael": 684, - "A4Q2SuccessfulCain": 685, - "D2bnetHelp50": 686, - "D2bnetHelp": 687, - "D2bnetHelp2a": 688, - "D2bnetHelpa": 689, - "D2bnetHelp1": 690, - "D2bnetHelp2": 691, - "D2bnetHelp3": 692, - "D2bnetHelp4": 693, - "D2bnetHelp5": 694, - "D2bnetHelp5a": 695, - "D2bnetHelp6": 696, - "D2bnetHelp7": 697, - "D2bnetHelp8": 698, - "D2bnetHelp9": 699, - "D2bnetHelp10": 700, - "D2bnetHelp11": 701, - "D2bnetHelp36": 702, - "D2bnetHelp36a": 703, - "D2bnetHelp37": 704, - "D2bnetHelp37a": 705, - "D2bnetHelp38": 706, - "D2bnetHelp39": 707, - "D2bnetHelp40": 708, - "D2bnetHelp41": 709, - "D2bnetHelp42": 710, - "D2bnetHelp42a": 711, - "D2bnetHelp43": 712, - "D2bnetHelp44": 713, - "D2bnetHelp44ab": 714, - "D2bnetHelp44a": 715, - "D2bnetHelp45": 716, - "D2bnetHelp45b": 717, - "D2bnetHelp45a": 718, - "D2bnetHel46": 719, - "D2bnetHelp46a": 720, - "D2bnetHelp47": 721, - "D2bnetHelp48": 722, - "D2bnetHelp49": 723, - "D2bnetHelp12": 724, - "D2bnetHelp12c": 725, - "D2bnetHelp12b": 726, - "D2bnetHelp12a": 727, - "D2bnetHelp13": 728, - "D2bnetHelp13b": 729, - "D2bnetHelp13a": 730, - "D2bnetHelp14": 731, - "D2bnetHelp14a": 732, - "D2bnetHelp15": 733, - "D2bnetHelp15b": 734, - "D2bnetHelp15a": 735, - "D2bnetHelp16": 736, - "D2bnetHelp16b": 737, - "D2bnetHelp16a": 738, - "D2bnetHelp17": 739, - "D2bnetHelp17a": 740, - "D2bnetHelp18": 741, - "D2bnetHelp18a": 742, - "D2bnetHelp19": 743, - "D2bnetHelp19a": 744, - "D2bnetHelp20": 745, - "D2bnetHelp20a": 746, - "D2bnetHelp21": 747, - "D2bnetHelp21a": 748, - "D2bnetHelp22": 749, - "D2bnetHelp22a": 750, - "D2bnetHelp23": 751, - "D2bnetHelp23a": 752, - "D2bnetHelp24": 753, - "D2bnetHelp24a": 754, - "D2bnetHelp25": 755, - "D2bnetHelp25a": 756, - "D2bnetHelp26": 757, - "D2bnetHelp26b": 758, - "D2bnetHelp26a": 759, - "D2bnetHelp27": 760, - "D2bnetHelp27a": 761, - "D2bnetHelp28": 762, - "D2bnetHelp28a": 763, - "D2bnetHelp29": 764, - "D2bnetHelp29a": 765, - "D2bnetHelp30": 766, - "D2bnetHelp30a": 767, - "D2bnetHelp31": 768, - "D2bnetHelp31a": 769, - "D2bnetHelp32": 770, - "D2bnetHelp32a": 771, - "D2bnetHelp33": 772, - "D2bnetHelp34": 773, - "D2bnetHelp35": 774, - "D2bnetHelp51": 775, - "D2bnetHelp52": 776, - "D2bnetHelp53": 777, - "D2bnetHelp54": 778, - "D2bnetHelp55": 779, - "D2bnetHelp56": 780, - "D2bnetHelp57": 781, - "D2bnetHelp58": 782, - "D2bnetHelp59": 783, - "D2bnetHelp60": 784, - "D2bnetHelp61": 785, - "D2bnetHelp62": 786, - "D2bnetHelp63": 787, - "Moo Moo Farm": 788, - "Chaos Sanctum": 789, - "The Pandemonium Fortress": 790, - "River of Flame": 791, - "Outer Steppes": 792, - "Plains of Despair": 793, - "City of the Damned": 794, - "Durance of Hate Level 3": 795, - "Durance of Hate Level 2": 796, - "Durance of Hate Level 1": 797, - "Disused Reliquary": 798, - "Ruined Fane": 799, - "Forgotten Temple": 800, - "Forgotten Reliquary": 801, - "Disused Fane": 802, - "Ruined Temple": 803, - "Flayer Dungeon Level 3": 804, - "Flayer Dungeon Level 2": 805, - "Flayer Dungeon Level 1": 806, - "Swampy Pit Level 3": 807, - "Swampy Pit Level 2": 808, - "Swampy Pit Level 1": 809, - "Spider Cave": 810, - "Spider Cavern": 811, - "Travincal": 812, - "Kurast Causeway": 813, - "Upper Kurast": 814, - "Kurast Bazaar": 815, - "Lower Kurast": 816, - "Flayer Jungle": 817, - "Great Marsh": 818, - "Spider Forest": 819, - "Kurast Docktown": 820, - "Durance of Hate": 821, - "Flayer Dungeon": 822, - "Swampy Pit": 823, - "Arcane Sanctuary": 824, - "Duriel's Lair": 825, - "Tal Rasha's Tomb": 826, - "Ancient Tunnels": 827, - "Maggot Lair Level 3": 828, - "Maggot Lair Level 2": 829, - "Maggot Lair Level 1": 830, - "Claw Viper Temple Level 2": 831, - "Halls of the Dead Level 3": 832, - "Stony Tomb Level 2": 833, - "Claw Viper Temple Level 1": 834, - "Halls of the Dead Level 2": 835, - "Halls of the Dead Level 1": 836, - "Stony Tomb Level 1": 837, - "Palace Cellar Level 3": 838, - "Palace Cellar Level 2": 839, - "Palace Cellar Level 1 \tPalace Cellar Level 1": 840, - "Harem Level 2": 841, - "Harem Level 1": 842, - "Sewers Level 3": 843, - "Sewers Level 2": 844, - "Sewers Level 1": 845, - "Canyon of the Magi": 846, - "Valley of Snakes": 847, - "Lost City": 848, - "Far Oasis": 849, - "Dry Hills": 850, - "Rocky Waste": 851, - "Lut Gholein": 852, - "Maggot Lair": 853, - "Claw Viper Temple": 854, - "Halls of the Dead": 855, - "Stony Tomb": 856, - "Palace Cellar": 857, - "Harem": 858, - "Sewers": 859, - "To The Moo Moo Farm": 860, - "To Chaos Sanctum": 861, - "To The River of Flame": 862, - "To The Outer Steppes": 863, - "To The Plains of Despair": 864, - "To The City of the Damned": 865, - "To The Pandemonium Fortress": 866, - "To The Durance of Hate Level 3": 867, - "To The Durance of Hate Level 2": 868, - "To The Durance of Hate Level 1": 869, - "To The Disused Reliquary": 870, - "To The Ruined Fane": 871, - "To The Forgotten Temple": 872, - "To The Forgotten Reliquary": 873, - "To The Disused Fane": 874, - "To The Ruined Temple": 875, - "To The Flayer Dungeon Level 1": 876, - "To The Flayer Dungeon Level 2": 877, - "To The Flayer Dungeon Level 3": 878, - "To The Swampy Pit Level 3": 879, - "To The Swampy Pit Level 2": 880, - "To The Swampy Pit Level 1": 881, - "To The Spider Cave": 882, - "To The Spider Cavern": 883, - "To Travincal": 884, - "To The Kurast Causeway": 885, - "To Upper Kurast": 886, - "To The Kurast Bazaar": 887, - "To Lower Kurast": 888, - "To The Flayer Jungle": 889, - "To The Great Marsh": 890, - "To The Spider Forest": 891, - "To The Kurast Docktown": 892, - "To The Arcane Sanctuary": 893, - "To Duriel's Lair": 894, - "To Tal Rasha's Tomb": 895, - "To The Ancient Tunnels": 896, - "To The Maggot Lair Level 3": 897, - "To The Maggot Lair Level 2": 898, - "To The Maggot Lair Level 1": 899, - "To The Claw Viper Temple Level 2": 900, - "To The Halls of the Dead Level 3": 901, - "To The Stony Tomb Level 2": 902, - "To The Claw Viper Temple Level 1": 903, - "To The Halls of the Dead Level 2": 904, - "To The Halls of the Dead Level 1": 905, - "To The Stony Tomb Level 1": 906, - "To The Palace Cellar Level 3": 907, - "To The Palace Cellar Level 2": 908, - "To The Palace Cellar Level 1 \tTo The Palace Cellar Level 1 ": 909, - "To The Harem Level 2": 910, - "To The Harem Level 1": 911, - "To The Sewers Level 3": 912, - "To The Sewers Level 2": 913, - "To The Sewers Level 1": 914, - "To The Canyon of the Magi": 915, - "To The Valley of Snakes": 916, - "To The Lost City": 917, - "To The Far Oasis": 918, - "To The Dry Hills": 919, - "To The Rocky Waste": 920, - "To Lut Gholein": 921, - "qstsa2q0": 922, - "qstsa2q1": 923, - "qstsa2q2": 924, - "qstsa2q3": 925, - "qstsa2q4": 926, - "qstsa2q5": 927, - "qstsa2q6": 928, - "qstsa3q0": 929, - "qstsa3q1": 930, - "qstsa3q2": 931, - "qstsa3q3": 932, - "qstsa3q4": 933, - "qstsa3q5": 934, - "qstsa3q6": 935, - "qstsa4q0": 936, - "qstsa4q1": 937, - "qstsa4q2": 938, - "qstsa4q3": 939, - "qstsa2q01": 940, - "qstsa2q11": 941, - "qstsa2q12": 942, - "qstsa2q13": 943, - "qstsa2q21": 944, - "qstsa2q22": 945, - "qstsa2q23": 946, - "qstsa2q24": 947, - "qstsa2q25": 948, - "qstsa2q31": 949, - "qstsa2q31a": 950, - "qstsa2q32": 951, - "qstsa2q33": 952, - "qstsa2q41": 953, - "qstsa2q41a": 954, - "qstsa2q42": 955, - "qstsa2q43": 956, - "qstsa2q51": 957, - "qstsa2q52": 958, - "qstsa2q53": 959, - "qstsa2q61": 960, - "qstsa2q61a": 961, - "qstsa2q62": 962, - "qstsa2q63": 963, - "qstsa2q63a": 964, - "qstsa2q64": 965, - "qstsa2q65": 966, - "qstsa3q01": 967, - "qstsa3q11": 968, - "qstsa3q12": 969, - "qstsa3q21": 970, - "qstsa3q22": 971, - "qstsa3q23": 972, - "qstsa3q24": 973, - "qstsa3q25": 974, - "qstsa3q26": 975, - "qstsa3q21a": 976, - "qstsa3q31": 977, - "qstsa3q32": 978, - "qstsa3q33": 979, - "qstsa3q34": 980, - "qstsa3q35": 981, - "qstsa3q41": 982, - "qstsa3q42": 983, - "qstsa3q43": 984, - "qstsa3q44": 985, - "qstsa3q45": 986, - "qstsa3q51": 987, - "qstsa3q52": 988, - "qstsa3q53": 989, - "qstsa3q61": 990, - "qstsa3q62": 991, - "qstsa3q63": 992, - "qstsa3q31a": 993, - "qstsa3q51a": 994, - "qstsa3q61a": 995, - "qstsa4q11": 996, - "qstsa4q12": 997, - "qstsa4q13a": 998, - "qstsa4q13": 999, - "qstsa4q31": 1000, - "qstsa4q32": 1001, - "qstsa4q33": 1002, - "qstsa4q34": 1003, - "qstsa4q21": 1004, - "qstsa4q22": 1005, - "qstsa4q23": 1006, - "qstsa4q24": 1007, - "asheara": 1008, - "hratli": 1009, - "alkor": 1010, - "ormus": 1011, - "nikita": 1012, - "tyrael": 1013, - "Izual": 1014, - "izual": 1015, - "Jamella": 1016, - "halbu": 1017, - "Malachai": 1018, - "merca201": 1019, - "merca202": 1020, - "merca203": 1021, - "merca204": 1022, - "merca205": 1023, - "merca206": 1024, - "merca207": 1025, - "merca208": 1026, - "merca209": 1027, - "merca210": 1028, - "merca211": 1029, - "merca212": 1030, - "merca213": 1031, - "merca214": 1032, - "merca215": 1033, - "merca216": 1034, - "merca217": 1035, - "merca218": 1036, - "merca219": 1037, - "merca220": 1038, - "merca221": 1039, - "merca222": 1040, - "merca223": 1041, - "merca224": 1042, - "merca225": 1043, - "merca226": 1044, - "merca227": 1045, - "merca228": 1046, - "merca229": 1047, - "merca230": 1048, - "merca231": 1049, - "merca232": 1050, - "merca233": 1051, - "merca234": 1052, - "merca235": 1053, - "merca236": 1054, - "merca237": 1055, - "merca238": 1056, - "merca239": 1057, - "merca240": 1058, - "merca241": 1059, - "qf1": 1060, - "qf2": 1061, - "KhalimFlail": 1062, - "SuperKhalimFlail": 1063, - "qey": 1064, - "qbr": 1065, - "qhr": 1066, - "The Feature Creep": 1067, - "Hell Bovine": 1068, - "Playersubtitles00": 1069, - "Playersubtitles01": 1070, - "Playersubtitles02": 1071, - "Playersubtitles03": 1072, - "Playersubtitles04": 1073, - "Playersubtitles05": 1074, - "Playersubtitles06": 1075, - "Playersubtitles07": 1076, - "Playersubtitles09": 1077, - "Playersubtitles10": 1078, - "Playersubtitles11": 1079, - "Playersubtitles12": 1080, - "Playersubtitles13": 1081, - "Playersubtitles14": 1082, - "Playersubtitles15": 1083, - "Playersubtitles16": 1084, - "Playersubtitles17": 1085, - "Playersubtitles18": 1086, - "Playersubtitles21": 1087, - "Playersubtitles22": 1088, - "Playersubtitles23": 1089, - "Playersubtitles24": 1090, - "Playersubtitles25": 1091, - "Playersubtitles26": 1092, - "Playersubtitles27": 1093, - "Playersubtitles28": 1094, - "LeaveCampAma": 1095, - "LeaveCampBar": 1096, - "LeaveCampPal": 1097, - "LeaveCampSor": 1098, - "LeaveCampNec": 1099, - "EnterDOEAma": 1100, - "EnterDOEBar": 1101, - "EnterDOEPal": 1102, - "EnterDOESor": 1103, - "EnterDOENec": 1104, - "EnterBurialAma": 1105, - "EnterBurialBar": 1106, - "EnterBurialPal": 1107, - "EnterBurialSor": 1108, - "EnterBurialNec": 1109, - "EnterMonasteryAma": 1110, - "EnterMonasteryBar": 1111, - "EnterMonasteryPal": 1112, - "EnterMonasterySor": 1113, - "EnterMonasteryNec": 1114, - "EnterForgottenTAma": 1115, - "EnterForgottenTBar": 1116, - "EnterForgottenTPal": 1117, - "EnterForgottenTSor": 1118, - "EnterForgottenTNec": 1119, - "EnterJailAma": 1120, - "EnterJailBar": 1121, - "EnterJailPal": 1122, - "EnterJailSor": 1123, - "EnterJailNec": 1124, - "Barracksremoved": 1129, - "EnterCatacombsAma": 1130, - "EnterCatacombsBar": 1131, - "EnterCatacombsPal": 1132, - "EnterCatacombsSor": 1133, - "EnterCatacombsNec": 1134, - "CompletingDOEAma": 1135, - "CompletingDOEBar": 1136, - "CompletingDOEPal": 1137, - "CompletingDOESor": 1138, - "CompletingDOENec": 1139, - "CompletingBurialAma": 1140, - "CompletingBurialBar": 1141, - "CompletingBurialPal": 1142, - "CompletingBurialSor": 1143, - "CompletingBurialNec": 1144, - "FindingInifusAma": 1145, - "FindingInifusBar": 1146, - "FindingInifusPal": 1147, - "FindingInifusSor": 1148, - "FindingInifusNec": 1149, - "FindingCairnAma": 1150, - "FindingCairnBar": 1151, - "FindingCairnPal": 1152, - "FindingCairnSor": 1153, - "FindingCairnNec": 1154, - "FindingTristramAma": 1155, - "FindingTristramBar": 1156, - "FindingTristramPal": 1157, - "FindingTristramSor": 1158, - "FindingTristramNec": 1159, - "RescueCainAma": 1160, - "RescueCainBar": 1161, - "RescueCainPal": 1162, - "RescueCainSor": 1163, - "RescueCainNec": 1164, - "HoradricMalusAma": 1165, - "HoradricMalusBar": 1166, - "HoradricMalusPal": 1167, - "HoradricMalusSor": 1168, - "HoradricMalusNec": 1169, - "CompletingForgottenTAma": 1170, - "CompletingForgottenTBar": 1171, - "CompletingForgottenTPal": 21924, - "CompletingForgottenTSor": 1173, - "CompletingForgottenTNec": 1174, - "CompletingAndarielAma": 1175, - "CompletingAndarielBar": 1176, - "CompletingAndarielPal": 1177, - "CompletingAndarielSor": 1178, - "CompletingAndarielNec": 1179, - "EnteringRadamentAma": 1180, - "EnteringRadamentBar": 1181, - "EnteringRadamentPal": 1182, - "EnteringRadamentSor": 1183, - "EnteringRadamentNec": 1184, - "CompletingRadamentAma": 1185, - "CompletingRadamentBar": 1186, - "CompletingRadamentPal": 1187, - "CompletingRadamentSor": 1188, - "CompletingRadamentNec": 1189, - "BeginTaintedSunAma": 1190, - "BeginTaintedSunBar": 1191, - "BeginTaintedSunPal": 1192, - "BeginTaintedSunSor": 1193, - "BeginTaintedSunNec": 1194, - "EnteringClawViperAma": 1195, - "EnteringClawViperBar": 1196, - "EnteringClawViperPal": 1197, - "EnteringClawViperSor": 1198, - "EnteringClawViperNec": 1199, - "CompletingTaintedSunAma": 1200, - "CompletingTaintedSunBar": 1201, - "CompletingTaintedSunPal": 1202, - "CompletingTaintedSunSor": 1203, - "CompletingTaintedSunNec": 1204, - "EnteringArcaneAma": 1205, - "EnteringArcaneBar": 1206, - "EnteringArcanePal": 1207, - "EnteringArcaneSor": 1208, - "EnteringArcaneNec": 1209, - "FindingSummonerAma": 1210, - "FindingSummonerBar": 1211, - "FindingSummonerPal": 1212, - "FindingSummonerSor": 1213, - "FindingSummonerNec": 1214, - "CompletingSummonerAma": 1215, - "CompletingSummonerBar": 1216, - "CompletingSummonerPal": 1217, - "CompletingSummonerSor": 1218, - "CompletingSummonerNec": 1219, - "FindingdecoyTombAma": 1220, - "FindingdecoyTombBar": 1221, - "FindingdecoyTombPal": 1222, - "FindingdecoyTombSor": 1223, - "FindingdecoyTombNec": 1224, - "FindingTrueTombAma": 1225, - "FindingTrueTombBar": 1226, - "FindingTrueTombPal": 1227, - "FindingTrueTombSor": 1228, - "FindingTrueTombNec": 1229, - "CompletingTombAma": 1230, - "CompletingTombBar": 1231, - "CompletingTombPal": 1232, - "CompletingTombSor": 1233, - "CompletingTombNec": 1234, - "nodarkwanderer": 1235, - "FindingLamEsenAma": 1236, - "FindingLamEsenBar": 1237, - "FindingLamEsenPal": 1238, - "FindingLamEsenSor": 1239, - "FindingLamEsenNec": 1240, - "CompletingLamEsenAma": 1241, - "CompletingLamEsenBar": 1242, - "CompletingLamEsenPal": 1243, - "CompletingLamEsenSor": 1244, - "CompletingLamEsenNec": 1245, - "FindingBeneathCityAma": 1246, - "FindingBeneathCityBar": 1247, - "FindingBeneathCityPal": 1248, - "FindingBeneathCitySor": 1249, - "FindingBeneathCityNec": 1250, - "FindingDrainLeverAma": 1251, - "FindingDrainLeverBar": 1252, - "FindingDrainLeverPal": 1253, - "FindingDrainLeverSor": 1254, - "FindingDrainLeverNec": 1255, - "CompletingBeneathCityAma": 1256, - "CompletingBeneathCityBar": 1257, - "CompletingBeneathCityPal": 1258, - "CompletingBeneathCitySor": 1259, - "CompletingBeneathCityNec": 1260, - "CompletingBladeAma": 1261, - "CompletingBladeBar": 1262, - "CompletingBladePal": 1263, - "CompletingBladeSor": 1264, - "CompletingBladeNec": 1265, - "FindingJadeFigAma": 1270, - "FindingTempleAma": 1271, - "FindingTempleBar": 1272, - "FindingTemplePal": 1273, - "FindingTempleSor": 1274, - "FindingTempleNec": 1275, - "CompletingTempleAma": 1276, - "CompletingTempleBar": 1277, - "CompletingTemplePal": 1278, - "CompletingTempleSor": 1279, - "CompletingTempleNec": 1280, - "FindingGuardianTowerAma": 1281, - "FindingGuardianTowerBar": 1282, - "FindingGuardianTowerPal": 1283, - "FindingGuardianTowerSor": 1284, - "FindingGuardianTowerNec": 1285, - "CompletingGuardianTowerAma": 1286, - "CompletingGuardianTowerBar": 1287, - "CompletingGuardianTowerPal": 1288, - "CompletingGuardianTowerSor": 1289, - "CompletingGuardianTowerNec": 1290, - "FreezingIzualAma": 21972, - "FreezingIzualBar": 1292, - "FreezingIzualPal": 1293, - "FreezingIzualSor": 1294, - "FreezingIzualNec": 1295, - "Eskillname0": 1296, - "Eskillsd0": 1297, - "Eskillld0": 1298, - "Eskillan0": 1299, - "EskillnameExp1": 1300, - "EskillsExpd1": 1301, - "EskilllExpd1": 1302, - "EskillExpan1": 1303, - "Eskillname2": 1304, - "Eskillsd2": 1305, - "Eskillld2": 1306, - "Eskillan2": 1307, - "Eskillname3": 1308, - "Eskillsd3": 1309, - "Eskillld3": 1310, - "Eskillan3": 1311, - "Eskillname4": 1312, - "Eskillsd4": 1313, - "Eskillld4": 1314, - "Eskillan4": 1315, - "Eskillname5": 1316, - "Eskillsd5": 1317, - "Eskillld5": 1318, - "Eskillan5": 1319, - "Eskillname6": 1320, - "Eskillsd6": 1321, - "Eskillld6": 1322, - "Eskillan6": 1323, - "Eskillname7": 1324, - "Eskillsd7": 1325, - "Eskillld7": 1326, - "Eskillan7": 1327, - "Eskillname8": 1328, - "Eskillsd8": 1329, - "Eskillld8": 1330, - "Eskillan8": 1331, - "Eskillname9": 1332, - "Eskillsd9": 1333, - "Eskillld9": 1334, - "Eskillan9": 1335, - "Eskillname10": 1336, - "Eskillsd10": 1337, - "Eskillld10": 1338, - "Eskillan10": 1339, - "Eskillname11": 1340, - "Eskillsd11": 1341, - "Eskillld11": 1342, - "Eskillan11": 1343, - "Eskillname12": 1344, - "Eskillsd12": 1345, - "Eskillld12": 1346, - "Eskillan12": 1347, - "Eskillname13": 1348, - "Eskillsd13": 1349, - "Eskillld13": 1350, - "Eskillan13": 1351, - "Eskillname14": 1352, - "Eskillsd14": 1353, - "Eskillld14": 1354, - "Eskillan14": 1355, - "Eskillname15": 1356, - "Eskillsd15": 1357, - "Eskillld15": 1358, - "Eskillan15": 1359, - "Eskillname16": 1360, - "Eskillsd16": 1361, - "Eskillld16": 1362, - "Eskillan16": 1363, - "Eskillname17": 1364, - "Eskillsd17": 1365, - "Eskillld17": 1366, - "Eskillan17": 1367, - "Eskillname18": 1368, - "Eskillsd18": 1369, - "Eskillld18": 1370, - "Eskillan18": 1371, - "Eskillname19": 1372, - "Eskillsd19": 1373, - "Eskillld19": 1374, - "Eskillan19": 1375, - "Eskillname20": 1376, - "Eskillsd20": 1377, - "Eskillld20": 1378, - "Eskillan20": 1379, - "Eskillname21": 1380, - "Eskillsd21": 1381, - "Eskillld21": 1382, - "Eskillan21": 1383, - "Eskillname22": 1384, - "Eskillsd22": 1385, - "Eskillld22": 1386, - "Eskillan22": 1387, - "Eskillname23": 1388, - "Eskillsd23": 1389, - "Eskillld23": 1390, - "Eskillan23": 1391, - "Eskillname24": 1392, - "Eskillsd24": 1393, - "Eskillld24": 1394, - "Eskillan24": 1395, - "Eskillname25": 1396, - "Eskillsd25": 1397, - "Eskillld25": 1398, - "Eskillan25": 1399, - "Eskillname26": 1400, - "Eskillsd26": 1401, - "Eskillld26": 1402, - "Eskillan26": 1403, - "Eskillname27": 1404, - "Eskillsd27": 1405, - "Eskillld27": 1406, - "Eskillan27": 1407, - "Eskillname28": 1408, - "Eskillsd28": 1409, - "Eskillld28": 1410, - "Eskillan28": 1411, - "Eskillname29": 1412, - "Eskillsd29": 1413, - "Eskillld29": 1414, - "Eskillan29": 1415, - "Eskillname30": 1416, - "Eskillsd30": 1417, - "Eskillld30": 1418, - "Eskillan30": 1419, - "Eskillname31": 1420, - "Eskillsd31": 1421, - "Eskillld31": 1422, - "Eskillan31": 1423, - "Eskillname32": 1424, - "Eskillsd32": 1425, - "Eskillld32": 1426, - "Eskillan32": 1427, - "Eskillname33": 1428, - "Eskillsd33": 1429, - "Eskillld33": 1430, - "Eskillan33": 1431, - "Eskillname34": 1432, - "Eskillsd34": 1433, - "Eskillld34": 1434, - "Eskillan34": 1435, - "Eskillname35": 1436, - "Eskillsd35": 1437, - "Eskillld35": 1438, - "Eskillan35": 1439, - "Eskillname36": 1440, - "Eskillsd36": 1441, - "Eskillld36": 1442, - "Eskillan36": 1443, - "Eskillname37": 1444, - "Eskillsd37": 1445, - "Eskillld37": 1446, - "Eskillan37": 1447, - "Eskillname38": 1448, - "Eskillsd38": 1449, - "Eskillld38": 1450, - "Eskillan38": 1451, - "Eskillname39": 1452, - "Eskillsd39": 1453, - "Eskillld39": 1454, - "Eskillan39": 1455, - "Eskillname40": 1456, - "Eskillsd40": 1457, - "Eskillld40": 1458, - "Eskillan40": 1459, - "Eskillname41": 1460, - "Eskillsd41": 1461, - "Eskillld41": 1462, - "Eskillan41": 1463, - "Eskillname42": 1464, - "Eskillsd42": 1465, - "Eskillld42": 1466, - "Eskillan42": 1467, - "Eskillname43": 1468, - "Eskillsd43": 1469, - "Eskillld43": 1470, - "Eskillan43": 1471, - "Eskillname44": 1472, - "Eskillsd44": 1473, - "Eskillld44": 1474, - "Eskillan44": 1475, - "Eskillname45": 1476, - "Eskillsd45": 1477, - "Eskillld45": 1478, - "Eskillan45": 1479, - "Eskillname46": 1480, - "Eskillsd46": 1481, - "Eskillld46": 1482, - "Eskillan46": 1483, - "Eskillname47": 1484, - "Eskillsd47": 1485, - "Eskillld47": 1486, - "Eskillan47": 1487, - "Eskillname48": 1488, - "Eskillsd48": 1489, - "Eskillld48": 1490, - "Eskillan48": 1491, - "Eskillname49": 1492, - "Eskillsd49": 1493, - "Eskillld49": 1494, - "Eskillan49": 1495, - "Eskillname50": 1496, - "Eskillsd50": 1497, - "Eskillld50": 1498, - "Eskillan50": 1499, - "Eskillname51": 1500, - "Eskillsd51": 1501, - "Eskillld51": 1502, - "Eskillan51": 1503, - "Eskillname52": 1504, - "Eskillsd52": 1505, - "Eskillld52": 1506, - "Eskillan52": 1507, - "Eskillname53": 1508, - "Eskillsd53": 1509, - "Eskillld53": 1510, - "Eskillan53": 1511, - "Eskillname54": 1512, - "Eskillsd54": 1513, - "Eskillld54": 1514, - "Eskillan54": 1515, - "Eskillname55": 1516, - "Eskillsd55": 1517, - "Eskillld55": 1518, - "Eskillan55": 1519, - "Eskillname56": 1520, - "Eskillsd56": 1521, - "Eskillld56": 1522, - "Eskillan56": 1523, - "Eskillname57": 1524, - "Eskillsd57": 1525, - "Eskillld57": 1526, - "Eskillan57": 1527, - "Eskillname58": 1528, - "Eskillsd58": 1529, - "Eskillld58": 1530, - "Eskillan58": 1531, - "Eskillname59": 1532, - "Eskillsd59": 1533, - "Eskillld59": 1534, - "Eskillan59": 1535, - "ESkillHawk": 22278, - "ESkillSpikes": 22279, - "ESkillStars": 22280, - "ESkillWolf": 22281, - "ESkillWolves": 22282, - "ESkillShoots": 22283, - "ESkillTimes": 22284, - "ESkillSpikes2": 22285, - "ob1": 20281, - "ob2": 20282, - "ob3": 20283, - "ob4": 20284, - "ob5": 21778, - "ne1": 20332, - "ne2": 20333, - "ne3": 20334, - "ne4": 20335, - "ne5": 20336, - "dr1": 20320, - "dr2": 20318, - "dr3": 20319, - "dr4": 20317, - "dr5": 20321, - "as1": 20285, - "as2": 20286, - "as3": 20287, - "as4": 20288, - "as5": 20289, - "as6": 20290, - "as7": 20291, - "AmaOnly": 20426, - "SorOnly": 20427, - "NecOnly": 20428, - "PalOnly": 20429, - "BarOnly": 20430, - "DruOnly": 20431, - "AssOnly": 20432, - "WeaponDescH2H": 21258, - "Seige Tower": 22352, - "RotWalker": 22353, - "ReanimatedHorde": 22354, - "ProwlingDead": 22355, - "UnholyCorpse": 22356, - "DefiledWarrior": 22357, - "Seige Beast": 1580, - "CrushBiest": 22359, - "BloodBringer": 22360, - "GoreBearer": 22361, - "DeamonSteed": 22362, - "WailingSpirit": 22363, - "LifeSeeker": 22364, - "LifeStealer": 22365, - "DeathlyVisage": 22366, - "BoundSpirit": 22367, - "BanishedSoul": 22368, - "Deathexp": 22369, - "Minionexp": 22370, - "Slayerexp": 22371, - "IceBoar": 22372, - "FireBoar": 22373, - "HellSpawn": 22374, - "IceSpawn": 22375, - "GreaterHellSpawn": 22376, - "GreaterIceSpawn": 22377, - "FanaticMinion": 22378, - "BerserkSlayer": 22379, - "ConsumedFireBoar": 22380, - "ConsumedIceBoar": 22381, - "FrenziedHellSpawn": 22382, - "FrenziedIceSpawn": 22383, - "InsaneHellSpawn": 22384, - "InsaneIceSpawn": 22385, - "Succubusexp": 22386, - "VileTemptress": 22387, - "StygianHarlot": 22388, - "BlightWing": 1611, - "BloodWitch": 1612, - "Dominus": 22391, - "VileWitch": 22392, - "StygianFury": 22393, - "MageWing": 1616, - "HellWitch": 1617, - "OverSeer": 22396, - "Lasher": 22397, - "OverLord": 22398, - "BloodBoss": 22399, - "HellWhip": 22400, - "MinionSpawner": 22401, - "MinionSlayerSpawner": 22402, - "MinionIce/fireBoarSpawner": 22403, - "Minionice/hellSpawnSpawner": 22404, - "MinionGreaterIce/hellSpawnSpawner": 22405, - "Imp1": 22406, - "Imp2": 22407, - "Imp3": 22408, - "Imp4": 22409, - "Imp5": 22410, - "CapsJoinMenu4": 1633, - "CapsJoinMenu5": 1634, - "Guild 1": 1635, - "Guild 2": 1636, - "Guild 3": 1637, - "Guild 4": 1638, - "Guild 5": 1639, - "To Guild 5": 1640, - "To Guild 4": 1641, - "To Guild 3": 1642, - "To Guild 2": 1643, - "To Guild 1": 1644, - "CapsBnet9": 1645, - "CapsBnet10": 1646, - "CapsBnet11": 1647, - "CapsBnet12": 1648, - "CapsBnet13": 1649, - "CapsBnet14": 1650, - "CapsBnet15": 1651, - "CapsGuildName": 1652, - "CapsGuildTag": 1653, - "GuildText1": 1654, - "GuildText2": 1655, - "Ladder3": 1656, - "Ladder7": 1657, - "gmGuildTitle": 1658, - "gmGuildName": 1659, - "gmGuildTag": 1660, - "gmWWW": 1661, - "gmGuildCharter": 1662, - "gmGuildCurrentGolds": 1663, - "gmGuildNextLevel": 1664, - "gmGuildMaster": 1665, - "gmOfficer": 1666, - "gmName": 1667, - "gmClass": 1668, - "gmLevel": 1669, - "gmDonate": 1670, - "gmRemove": 1671, - "gmPal": 1672, - "gmSor": 1673, - "gmAma": 1674, - "gmNec": 1675, - "gmBar": 1676, - "gmChangeSym": 1677, - "gmChangeCharter": 1678, - "gmChangeWebLink": 1679, - "Guild Portal": 1680, - "createdguildsuccess": 1681, - "createdguildfailure": 1682, - "inviteguildsuccess": 1683, - "inviteguildfailure": 1684, - "inviteguildins": 1685, - "joinedguildsuccess": 1686, - "joinedguildfailure": 1687, - "quitguildsuccess": 1688, - "quitguildfailure": 1689, - "guildentererror": 1690, - "strGuildMasterKicked": 1691, - "strGuildPerk1": 1692, - "strGuildPerk2": 1693, - "strGuildPerk3": 1694, - "strGuildPerk4": 1695, - "strGuildPerk5": 1696, - "strGuildPerk6": 1697, - "strGuildGoldDonated": 1698, - "strGuildDonateGold": 1699, - "gmGuildCurrentGoldPopup": 1700, - "gmGuildNextLevelPopup": 1701, - "gmGuildDonateGoldPopup": 1702, - "Message Board": 1703, - "Trophy Case": 1704, - "Guild Vault": 1705, - "Steeg Stone": 1706, - "guildaccepticon": 1707, - "guildmsgtext": 1708, - "ScrollFormat": 1709, - "BookFormat": 1710, - "HiqualityFormat": 1711, - "LowqualityFormat": 1712, - "HerbFormat": 1713, - "MagicFormat": 1714, - "GemmedNormalName": 1715, - "BodyPartsFormat": 1716, - "PlayerBodyPartFormat": 1717, - "RareFormat": 1718, - "SetItemFormat": 1719, - "ChampionFormat": 1720, - "Monster1Format": 1721, - "Monster2Format": 1722, - "Low Quality": 1723, - "Damaged": 1724, - "Cracked": 1725, - "Crude": 20910, - "Hiquality": 1727, - "Gemmed": 1728, - "Resiliant": 1729, - "Sturdy": 1730, - "Strong": 1731, - "Glorious": 1732, - "Blessed": 1733, - "Saintly": 1734, - "Holy": 1735, - "Devious": 1736, - "Fortified": 1737, - "Urgent": 1738, - "Fleet": 1739, - "Muscular": 1740, - "Jagged": 1741, - "Deadly": 1742, - "Vicious": 1743, - "Brutal": 1744, - "Massive": 1745, - "Savage": 1746, - "Merciless": 1747, - "Vulpine": 1748, - "Swift": 1749, - "Artful": 1750, - "Skillful": 1751, - "Adroit": 1752, - "Tireless": 1753, - "Rugged": 1754, - "Bronze": 1755, - "Iron": 1756, - "Steel": 1757, - "Silver": 1758, - "Gold": 1759, - "Platinum": 1760, - "Meteoric": 1761, - "Sharp": 1762, - "Fine": 1763, - "Warrior's": 1764, - "Soldier's": 1765, - "Knight's": 1766, - "Lord's": 1767, - "King's": 1768, - "Howling": 1769, - "Fortuitous": 1770, - "Brilliant": 1771, - "Omniscient": 1772, - "Sage": 1773, - "Shrewd": 1774, - "Vivid": 1775, - "Glimmering": 1776, - "Glowing": 1777, - "Bright": 1778, - "Solar": 1779, - "Lizard's": 1780, - "Forceful": 1781, - "Snake's": 1782, - "Serpent's": 1783, - "Drake's": 1784, - "Dragon's": 1785, - "Wyrm's": 1786, - "Dazzling": 1787, - "Facinating": 1788, - "Prismatic": 1789, - "Azure": 1790, - "Lapis": 1791, - "Cobalt": 1792, - "Indigo": 1793, - "Sapphire": 1794, - "Cerulean": 1795, - "Red": 1796, - "Crimson": 1797, - "Burgundy": 1798, - "Garnet": 1799, - "Russet": 1800, - "Ruby": 1801, - "Vermilion": 1802, - "Orange": 1803, - "Ocher": 1804, - "Tangerine": 1805, - "Coral": 1806, - "Crackling": 1807, - "Amber": 1808, - "Forked": 1809, - "Green": 20905, - "Beryl": 1811, - "Jade": 1812, - "Viridian": 1813, - "Vital": 1814, - "Emerald": 1815, - "Enduring": 1816, - "Fletcher's": 1817, - "Archer's": 1818, - "Monk's": 1819, - "Priest's": 1820, - "Summoner's": 1821, - "Necromancer's": 1822, - "Angel's": 1823, - "Arch-Angel's": 1824, - "Slayer's": 1825, - "Berserker's": 2507, - "Kicking": 1827, - "Triumphant": 1828, - "Mighty": 1829, - "Energizing": 1830, - "Strengthening": 1831, - "Empowering": 1832, - "Brisk": 1833, - "Tough": 1834, - "Hardy": 1835, - "Robust": 1836, - "of Health": 1837, - "of Protection": 1838, - "of Absorption": 1839, - "of Warding": 1840, - "of the Sentinel": 1841, - "of Guarding": 1842, - "of Negation": 1843, - "of Piercing": 1844, - "of Bashing": 1845, - "of Puncturing": 1846, - "of Thorns": 1847, - "of Spikes": 1848, - "of Readiness": 1849, - "of Alacrity": 1850, - "of Swiftness": 1851, - "of Quickness": 1852, - "of Blocking": 1853, - "of Deflecting": 1854, - "of the Apprentice": 1855, - "of the Magus": 1856, - "of Frost": 1857, - "of the Glacier": 1858, - "of Warmth": 1859, - "of Flame": 1860, - "of Fire": 1861, - "of Burning": 1862, - "of Shock": 1863, - "of Lightning": 1864, - "of Thunder": 1865, - "of Craftsmanship": 1866, - "of Quality": 1867, - "of Maiming": 1868, - "of Slaying": 1869, - "of Gore": 1870, - "of Carnage": 1871, - "of Slaughter": 1872, - "of Worth": 1873, - "of Measure": 1874, - "of Excellence": 1875, - "of Performance": 1876, - "of Blight": 1877, - "of Venom": 1878, - "of Pestilence": 1879, - "of Dexterity": 1880, - "of Skill": 1881, - "of Accuracy": 1882, - "of Precision": 1883, - "of Perfection": 1884, - "of Balance": 1885, - "of Stability": 1886, - "of the Horse": 1887, - "of Regeneration": 1888, - "of Regrowth": 1889, - "of Vileness": 1890, - "of Greed": 1891, - "of Wealth": 1892, - "of Chance": 1893, - "of Fortune": 1894, - "of Energy": 1895, - "of the Mind": 1896, - "of Brilliance": 1897, - "of Sorcery": 1898, - "of Wizardry": 1899, - "of the Bear": 1900, - "of Light": 1901, - "of Radiance": 1902, - "of the Sun": 1903, - "of Life": 1904, - "of the Jackal": 1905, - "of the Fox": 1906, - "of the Wolf": 1907, - "of the Tiger": 1908, - "of the Mammoth": 1909, - "of the Colosuss": 1910, - "of the Leech": 1911, - "of the Locust": 1912, - "of the Bat": 1913, - "of the Vampire": 1914, - "of Defiance": 1915, - "of Remedy": 1916, - "of Amelioration": 1917, - "of Ice": 1918, - "of Simplicity": 1919, - "of Ease": 1920, - "of the Mule": 1921, - "of Strength": 1922, - "of Might": 1923, - "of the Ox": 1924, - "of the Giant": 1925, - "of the Titan": 1926, - "of Pacing": 1927, - "of Haste": 1928, - "of Speed": 1929, - "cap": 1930, - "skp": 1931, - "hlm": 1932, - "fhl": 1933, - "ghm": 1934, - "crn": 1935, - "msk": 1936, - "qui": 1937, - "lea": 1938, - "hla": 1939, - "stu": 1940, - "rng": 1941, - "scl": 1942, - "chn": 1943, - "brs": 1944, - "spl": 1945, - "plt": 1946, - "fld": 1947, - "gth": 1948, - "ful": 1949, - "aar": 1950, - "ltp": 1951, - "buc": 1952, - "sml": 1953, - "lrg": 1954, - "kit": 1955, - "tow": 1956, - "gts": 1957, - "lgl": 1958, - "vgl": 1959, - "mgl": 1960, - "tgl": 1961, - "hgl": 1962, - "lbt": 1963, - "vbt": 1964, - "mbt": 1965, - "tbt": 1966, - "hbt": 1967, - "lbl": 1968, - "vbl": 1969, - "mbl": 1970, - "tbl": 1971, - "hbl": 1972, - "bhm": 1973, - "bsh": 1974, - "spk": 1975, - "hax": 1976, - "axe": 1977, - "2ax": 1978, - "mpi": 1979, - "wax": 1980, - "lax": 1981, - "bax": 1982, - "btx": 1983, - "gax": 1984, - "gix": 1985, - "wnd": 1986, - "ywn": 1987, - "bwn": 1988, - "gwn": 1989, - "clb": 1990, - "scp": 1991, - "gsc": 1992, - "wsp": 1993, - "spc": 1994, - "mac": 1995, - "mst": 1996, - "fla": 1997, - "whm": 1998, - "mau": 1999, - "gma": 2000, - "ssd": 2001, - "scm": 2002, - "sbr": 2003, - "flc": 2004, - "crs": 2005, - "bsd": 2006, - "lsd": 2007, - "wsd": 2008, - "2hs": 2009, - "clm": 2010, - "gis": 2011, - "bsw": 2012, - "flb": 2013, - "gsd": 2014, - "dgr": 2015, - "dir": 2016, - "kri": 2017, - "bld": 2018, - "tkf": 2019, - "tax": 2020, - "bkf": 2021, - "bal": 2022, - "jav": 2023, - "pil": 2024, - "ssp": 2025, - "glv": 2026, - "tsp": 2027, - "spr": 2028, - "tri": 2029, - "brn": 2030, - "spt": 2031, - "pik": 2032, - "bar": 2033, - "vou": 2034, - "scy": 2035, - "pax": 2036, - "hal": 2037, - "wsc": 2038, - "sst": 2039, - "lst": 2040, - "cst": 2041, - "bst": 2042, - "wst": 2043, - "sbw": 2044, - "hbw": 2045, - "lbw": 2046, - "cbw": 2047, - "sbb": 2048, - "lbb": 2049, - "swb": 2050, - "lwb": 2051, - "lxb": 2052, - "mxb": 2053, - "hxb": 2054, - "rxb": 2055, - "xpk": 2056, - "xsh": 2057, - "xh9": 2058, - "zhb": 2059, - "ztb": 2060, - "zmb": 2061, - "zvb": 2062, - "zlb": 2063, - "xhb": 2064, - "xtb": 2065, - "xmb": 2066, - "xvb": 2067, - "xlb": 2068, - "xhg": 2069, - "xtg": 2070, - "xmg": 2071, - "xvg": 2072, - "xlg": 2073, - "xts": 2074, - "xow": 2075, - "xit": 2076, - "xrg": 2077, - "xml": 2078, - "xuc": 2079, - "xtp": 2080, - "xar": 2081, - "xul": 2082, - "xth": 2083, - "xld": 2084, - "xlt": 2085, - "xpl": 2086, - "xrs": 2087, - "xhn": 2088, - "xcl": 2089, - "xng": 2090, - "xtu": 2091, - "xla": 2092, - "xea": 2093, - "xui": 2094, - "xsk": 2095, - "xrn": 2096, - "xhm": 2097, - "xhl": 2098, - "xlm": 2099, - "xkp": 2100, - "xap": 2101, - "8rx": 2102, - "8hx": 2103, - "8mx": 2104, - "8lx": 2105, - "8lw": 2106, - "8sw": 2107, - "8l8": 2108, - "8s8": 2109, - "8cb": 2110, - "8lb": 2111, - "8hb": 2112, - "8sb": 2113, - "8ws": 2114, - "8bs": 2115, - "8cs": 2116, - "8ls": 2117, - "8ss": 2118, - "9wc": 2119, - "9h9": 2120, - "9pa": 2121, - "9s8": 2122, - "9vo": 2123, - "9b7": 2124, - "9p9": 2125, - "9st": 2126, - "9br": 2127, - "9tr": 2128, - "9sr": 2129, - "9ts": 2130, - "9gl": 2131, - "9s9": 2132, - "9pi": 2133, - "9ja": 2134, - "9b8": 2135, - "9bk": 2136, - "9ta": 2137, - "9tk": 2138, - "9bl": 2139, - "9kr": 2140, - "9di": 2141, - "9dg": 2142, - "9gd": 2143, - "9fb": 2144, - "9gs": 2145, - "9cm": 2146, - "92h": 2147, - "9wd": 2148, - "9ls": 2149, - "9bs": 2150, - "9cr": 2151, - "9fc": 2152, - "9sb": 2153, - "9sm": 2154, - "9ss": 2155, - "9gm": 2156, - "9m9": 2157, - "9wh": 2158, - "9fl": 2159, - "9mt": 2160, - "9ma": 2161, - "9sp": 2162, - "9ws": 2163, - "9qs": 2164, - "9sc": 2165, - "9cl": 2166, - "9gw": 2167, - "9bw": 2168, - "9yw": 2169, - "9wn": 2170, - "9gi": 2171, - "9ga": 2172, - "9bt": 2173, - "9ba": 2174, - "9la": 2175, - "9wa": 2176, - "9mp": 2177, - "92a": 2178, - "9ax": 2179, - "9ha": 2180, - "9b9": 2181, - "gpl": 2182, - "opl": 2183, - "gpm": 2184, - "opm": 2185, - "gps": 2186, - "ops": 2187, - "gidbinn": 2188, - "g33": 2189, - "d33": 2190, - "leg": 2191, - "Malus": 2192, - "hdm": 2193, - "hfh": 2194, - "hst": 2195, - "msf": 2196, - "orifice": 2197, - "elx": 2198, - "tbk": 2199, - "tsc": 2200, - "ibk": 2201, - "isc": 2202, - "RightClicktoUse": 2203, - "RightClicktoOpen": 2204, - "RightClicktoRead": 2205, - "InsertScrolls": 2206, - "vps": 2207, - "yps": 2208, - "rvs": 2209, - "rvl": 2210, - "wms": 2211, - "amu": 2212, - "vip": 2213, - "rin": 2214, - "gld": 2215, - "bks": 2216, - "bkd": 2217, - "aqv": 2218, - "tch": 2219, - "cqv": 2220, - "Key": 2221, - "key": 2222, - "luv": 2223, - "xyz": 2224, - "shrine": 2225, - "teleport pad": 2226, - "j34": 2227, - "g34": 2228, - "bbb": 2229, - "LamTome": 2230, - "box": 2231, - "tr1": 2232, - "mss": 2233, - "ass": 2234, - "ear": 2235, - "gcv": 2236, - "gfv": 2237, - "gsv": 2238, - "gzv": 2239, - "gpv": 2240, - "gcy": 2241, - "gfy": 2242, - "gsy": 2243, - "gly": 2244, - "gpy": 2245, - "gcb": 2246, - "gfb": 2247, - "gsb": 2248, - "glb": 2249, - "gpb": 2250, - "gcg": 2251, - "gfg": 2252, - "glg": 2253, - "gsg": 2254, - "gpg": 2255, - "gcr": 2256, - "gfr": 2257, - "gsr": 2258, - "glr": 2259, - "gpr": 2260, - "gcw": 2261, - "gfw": 2262, - "gsw": 2263, - "glw": 2264, - "gpw": 2265, - "hp1": 2266, - "hp2": 2267, - "hp3": 2268, - "hp4": 2269, - "hp5": 2270, - "mp1": 2271, - "mp2": 2272, - "mp3": 2273, - "mp4": 2274, - "mp5": 2275, - "hrb": 20434, - "skc": 2277, - "skf": 2278, - "sku": 2279, - "skl": 2280, - "skz": 2281, - "Beast": 2282, - "Eagle": 2283, - "Raven": 2284, - "Viper": 2285, - "GhoulRI": 2286, - "Skull": 2287, - "Blood": 2288, - "Dread": 2289, - "Doom": 2290, - "Grim": 2291, - "Bone": 2292, - "Death": 2293, - "Shadow": 2294, - "Storm": 2295, - "Rune": 2296, - "PlagueRI": 2297, - "Stone": 2298, - "Wraith": 2989, - "Spirit": 2300, - "Demon": 2301, - "Cruel": 2302, - "Empyrion": 2303, - "Bramble": 2304, - "Pain": 2305, - "Loath": 2306, - "Glyph": 2307, - "Imp": 2308, - "Fiend": 2309, - "Hailstone": 2310, - "Gale": 2311, - "Dire": 2312, - "Soul": 2313, - "Brimstone": 2314, - "Corpse": 2315, - "Carrion": 2316, - "Holocaust": 2317, - "Havoc": 2318, - "Bitter": 2319, - "Entropy": 2320, - "Chaos": 2321, - "Order": 2322, - "Rift": 2323, - "Corruption": 2324, - "bite": 2325, - "scratch": 2326, - "scalpel": 2327, - "fang": 2328, - "gutter": 2329, - "thirst": 2330, - "razor": 2331, - "scythe": 2332, - "edge": 2333, - "saw": 2334, - "splitter": 2335, - "cleaver": 2336, - "sever": 2337, - "sunder": 2338, - "rend": 2339, - "mangler": 2340, - "slayer": 2341, - "reaver": 2342, - "Spawn": 2343, - "gnash": 2344, - "star": 2345, - "blow": 2346, - "smasher": 2347, - "Bane": 2348, - "crusher": 2349, - "breaker": 2350, - "grinder": 2351, - "crack": 2352, - "mallet": 2353, - "knell": 2354, - "lance": 2355, - "spike": 2356, - "impaler": 2357, - "skewer": 2358, - "prod": 2359, - "scourge": 2360, - "wand": 2361, - "wrack": 2362, - "barb": 2363, - "needle": 2364, - "dart": 2365, - "bolt": 2366, - "quarrel": 2367, - "fletch": 2368, - "flight": 2369, - "nock": 2370, - "horn": 2371, - "stinger": 2372, - "quill": 2373, - "goad": 2374, - "branch": 2375, - "spire": 2376, - "song": 2377, - "call": 2378, - "cry": 2379, - "spell": 2380, - "chant": 2381, - "weaver": 2382, - "gnarl": 2383, - "visage": 2384, - "crest": 2385, - "circlet": 2386, - "veil": 2387, - "hood": 2388, - "mask": 2389, - "brow": 2390, - "casque": 2391, - "visor": 2392, - "cowl": 2393, - "hide": 2394, - "Pelt": 2395, - "carapace": 2396, - "coat": 2397, - "wrap": 2398, - "suit": 2399, - "cloak": 2400, - "shroud": 2401, - "jack": 2402, - "mantle": 2403, - "guard": 2404, - "badge": 2405, - "rock": 2406, - "aegis": 2407, - "ward": 2408, - "tower": 2409, - "shield": 2410, - "wing": 2411, - "mark": 2412, - "emblem": 2413, - "hand": 2414, - "fist": 2415, - "claw": 2416, - "clutches": 2417, - "grip": 2418, - "grasp": 2419, - "hold": 2420, - "touch": 2421, - "finger": 2422, - "knuckle": 2423, - "shank": 2424, - "spur": 2425, - "tread": 2426, - "stalker": 2427, - "greave": 2428, - "blazer": 2429, - "nails": 2430, - "trample": 2431, - "Brogues": 2432, - "track": 2433, - "slippers": 2434, - "clasp": 2435, - "buckle": 2436, - "harness": 2437, - "lock": 2438, - "fringe": 2439, - "winding": 2440, - "chain": 2441, - "strap": 2442, - "lash": 2443, - "cord": 2444, - "knot": 2445, - "circle": 2446, - "loop": 2447, - "eye": 2448, - "turn": 2449, - "spiral": 2450, - "coil": 2451, - "gyre": 2452, - "band": 2453, - "whorl": 2454, - "talisman": 2455, - "heart": 2456, - "noose": 2457, - "necklace": 2458, - "collar": 2459, - "beads": 2460, - "torc": 2461, - "gorget": 2462, - "scarab": 2463, - "wood": 2464, - "brand": 2465, - "bludgeon": 2466, - "cudgel": 2467, - "loom": 2468, - "harp": 2469, - "master": 2470, - "barRI": 2471, - "hew": 2472, - "crook": 2473, - "mar": 2474, - "shell": 2475, - "stake": 2476, - "picket": 2477, - "pale": 2478, - "flange": 2479, - "Civerb's Vestments": 2480, - "Hsarus' Trim": 2481, - "Cleglaw's Brace": 2482, - "Iratha's Finery": 2483, - "Isenhart's Armory": 2484, - "Vidala's Rig": 2485, - "Milabrega's Regalia": 2486, - "Cathan's Traps": 2487, - "Tancred's Battlegear": 2488, - "Sigon's Complete Steel": 2489, - "Infernal Tools": 2490, - "Berserker's Garb": 2491, - "Death's Disguise": 2492, - "Angelical Raiment": 2493, - "Arctic Gear": 2494, - "Arcanna's Tricks": 2495, - "Civerb's": 2496, - "Hsarus'\tHsaru's": 2497, - "Cleglaw's": 2498, - "Iratha's": 2499, - "Isenhart's": 2500, - "Vidala's": 2501, - "Milabrega's": 2502, - "Cathan's": 2503, - "Tancred's": 2504, - "Sigon's": 2505, - "Infernal": 2506, - "Death's": 2508, - "Angelical": 2509, - "Arctic": 2510, - "Arcanna's": 2511, - "Ward": 2512, - "Iron Heel": 2513, - "Tooth": 2514, - "Collar": 2515, - "Lightbrand": 2516, - "Barb": 2517, - "Orb": 2518, - "Rule": 2519, - "Crowbill": 2520, - "Visor": 2521, - "Cranium": 2522, - "Headgear": 2523, - "Hand": 2524, - "Sickle": 2525, - "Horn": 2526, - "Sign": 2527, - "Icon": 2528, - "Iron Fist": 2529, - "Claw": 2530, - "Cuff": 2531, - "Parry": 2532, - "Fetlock": 2533, - "Rod": 2534, - "Mesh": 2535, - "Spine": 2536, - "Shelter": 2537, - "Torch": 2538, - "Hauberk": 2539, - "Guard": 2540, - "Mantle": 2541, - "Furs": 2542, - "Deathwand": 2543, - "CudgelSI3S": 2544, - "Iron Stay": 2545, - "Pincers": 2546, - "Coil": 2547, - "Case": 2548, - "Ambush": 2549, - "Diadem": 2550, - "Visage": 2551, - "Hobnails": 2552, - "Gage": 2553, - "SignSI3S": 2554, - "Hatchet": 2555, - "Touch": 2556, - "Halo": 2557, - "Binding": 2558, - "Head": 2559, - "Horns": 2560, - "Snare": 2561, - "Robe": 2562, - "Sigil": 2563, - "Weird": 2564, - "Sabot": 2565, - "Wings": 2566, - "Mitts": 2567, - "Flesh": 2568, - "Cord": 2569, - "Seal": 2570, - "SkullSI5S": 2571, - "Wrap": 2572, - "GuardSI6S": 2573, - "The Gnasher": 2574, - "Deathspade": 2575, - "Bladebone": 2576, - "Mindrend": 2577, - "Rakescar": 2578, - "Fechmars Axe": 2579, - "Goreshovel": 2580, - "The Chieftan": 2581, - "Brainhew": 2582, - "The Humongous": 2583, - "Iros Torch": 2584, - "Maelstromwrath": 2585, - "Gravenspine": 2586, - "Umes Lament": 2587, - "Felloak": 2588, - "Knell Striker": 2589, - "Rusthandle": 2590, - "Stormeye": 2591, - "Stoutnail": 2592, - "Crushflange": 2593, - "Bloodrise": 2594, - "The Generals Tan Do Li Ga": 2595, - "Ironstone": 2596, - "Bonesob": 2597, - "Steeldriver": 2598, - "Rixots Keen": 2599, - "Blood Crescent": 2600, - "Krintizs Skewer": 2601, - "Gleamscythe": 2602, - "Azurewrath": 2603, - "Griswolds Edge": 2604, - "Hellplague": 2605, - "Culwens Point": 2606, - "Shadowfang": 2607, - "Soulflay": 2608, - "Kinemils Awl": 2609, - "Blacktongue": 2610, - "Ripsaw": 2611, - "The Patriarch": 2612, - "Gull": 2613, - "The Diggler": 2614, - "The Jade Tan Do": 2615, - "Irices Shard": 2616, - "The Dragon Chang": 2617, - "Razortine": 2618, - "Bloodthief": 2619, - "Lance of Yaggai": 2620, - "The Tannr Gorerod": 2621, - "Dimoaks Hew": 2622, - "Steelgoad": 2623, - "Soul Harvest": 2624, - "The Battlebranch": 2625, - "Woestave": 2626, - "The Grim Reaper": 2627, - "Bane Ash": 2628, - "Serpent Lord": 2629, - "Lazarus Spire": 2630, - "The Salamander": 2631, - "The Iron Jang Bong": 2632, - "Pluckeye": 2633, - "Witherstring": 2634, - "Rimeraven": 2635, - "Piercerib": 2636, - "Pullspite": 2637, - "Wizendraw": 2638, - "Hellclap": 2639, - "Blastbark": 2640, - "Leadcrow": 2641, - "Ichorsting": 2642, - "Hellcast": 2643, - "Doomspittle": 2644, - "War Bonnet": 2645, - "Tarnhelm": 2646, - "Coif of Glory": 2647, - "Duskdeep": 2648, - "Wormskull": 2649, - "Howltusk": 2650, - "Undead Crown": 2651, - "The Face of Horror": 2652, - "Greyform": 2653, - "Blinkbats Form": 2654, - "The Centurion": 2655, - "Twitchthroe": 2656, - "Darkglow": 2657, - "Hawkmail": 2658, - "Sparking Mail": 2659, - "Venomsward": 2660, - "Iceblink": 2661, - "Boneflesh": 2662, - "Rockfleece": 2663, - "Rattlecage": 2664, - "Goldskin": 2665, - "Victors Silk": 2666, - "Heavenly Garb": 2667, - "Pelta Lunata": 2668, - "Umbral Disk": 2669, - "Stormguild": 2670, - "Wall of the Eyeless": 2671, - "Swordback Hold": 2672, - "Steelclash": 2673, - "Bverrit Keep": 2674, - "The Ward": 2675, - "The Hand of Broc": 2676, - "Bloodfist": 2677, - "Chance Guards": 2678, - "Magefist": 2679, - "Frostburn": 2680, - "Hotspur": 2681, - "Gorefoot": 2682, - "Treads of Cthon": 2683, - "Goblin Toe": 2684, - "Tearhaunch": 2685, - "Lenyms Cord": 2686, - "Snakecord": 2687, - "Nightsmoke": 2688, - "Goldwrap": 2689, - "Bladebuckle": 2690, - "Nokozan Relic": 2691, - "The Eye of Etlich": 2692, - "The Mahim-Oak Curio": 2693, - "Nagelring": 2694, - "Manald Heal": 2695, - "Gorgethroat": 2696, - "Amulet of the Viper": 2697, - "Staff of Kings": 2698, - "Horadric Staff": 2699, - "Hell Forge Hammer": 2700, - "The Stone of Jordan": 2701, - "GloomUM": 2702, - "Gray": 2703, - "DireUM": 2704, - "Black": 2705, - "ShadowUM": 2706, - "Haze": 2707, - "Wind": 2708, - "StormUM": 2709, - "Warp": 2710, - "Night": 2711, - "Moon": 2712, - "Star": 2713, - "Pit": 2714, - "Fire": 2715, - "Cold": 2716, - "Seethe": 2717, - "SharpUM": 2718, - "AshUM": 2719, - "Blade": 2720, - "SteelUM": 2721, - "StoneUM": 2722, - "Rust": 2723, - "Mold": 2724, - "Blight": 2725, - "Plague": 2726, - "Rot": 2727, - "Ooze": 2728, - "Puke": 2729, - "Snot": 2730, - "Bile": 2731, - "BloodUM": 2732, - "Pulse": 2733, - "Gut": 2734, - "Gore": 2735, - "FleshUM": 2736, - "BoneUM": 2737, - "SpineUM": 2738, - "Mind": 2739, - "SpiritUM": 2740, - "SoulUM": 2741, - "Wrath": 2742, - "GriefUM": 2743, - "Foul": 2744, - "Vile": 2745, - "Sin": 2746, - "ChaosUM": 2747, - "DreadUM": 2748, - "DoomUM": 2749, - "BaneUM": 2750, - "DeathUM": 2751, - "ViperUM": 2752, - "Dragon": 2753, - "Devil": 2754, - "touchUM": 2755, - "spellUM": 2756, - "feast": 2757, - "wound": 2758, - "grin": 2759, - "maim": 2760, - "hack": 2761, - "biteUM": 2762, - "rendUM": 2763, - "burn": 2764, - "rip": 2765, - "kill": 2766, - "callUM": 2767, - "vex": 2768, - "jade": 2769, - "web": 2770, - "shieldUM": 2771, - "KillerUM": 2772, - "RazorUM": 2773, - "drinker": 2774, - "shifter": 2775, - "crawler": 2776, - "dancer": 2777, - "bender": 2778, - "weaverUM": 2779, - "eater": 2780, - "widow": 2781, - "maggot": 2782, - "spawn": 2783, - "wight": 2784, - "GrumbleUM": 2785, - "GrowlerUM": 2786, - "SnarlUM": 2787, - "wolf": 2788, - "crow": 2789, - "raven": 2790, - "hawk": 2791, - "cloud": 2792, - "BangUM": 2793, - "head": 2794, - "skullUM": 2795, - "browUM": 2796, - "eyeUM": 2797, - "maw": 2798, - "tongue": 2799, - "fangUM": 2800, - "hornUM": 2801, - "thorn": 2802, - "clawUM": 2803, - "fistUM": 2804, - "heartUM": 2805, - "shankUM": 2806, - "skinUM": 2807, - "wingUM": 2808, - "pox": 2809, - "fester": 2810, - "blister": 3291, - "pus": 2812, - "SlimeUM": 2813, - "drool": 2814, - "froth": 2815, - "sludge": 2816, - "venom": 2817, - "poison": 2818, - "break": 2819, - "shard": 2820, - "flame": 2821, - "maul": 2822, - "thirstUM": 2823, - "lust": 2824, - "the Hammer": 2825, - "the Axe": 2826, - "the Sharp": 2827, - "the Jagged": 2828, - "the Flayer": 2829, - "the Slasher": 2830, - "the Impaler": 2831, - "the Hunter": 2832, - "the Slayer": 2833, - "the Mauler": 2834, - "the Destroyer": 2835, - "theQuick": 2836, - "the Witch": 2837, - "the Mad": 2838, - "the Wraith": 2839, - "the Shade": 2840, - "the Dead": 2841, - "the Unholy": 2842, - "the Howler": 2843, - "the Grim": 2844, - "the Dark": 2845, - "the Tainted": 2846, - "the Unclean": 2847, - "the Hungry": 2848, - "the Cold": 2849, - "The Cow King": 2850, - "Grand Vizier of Chaos": 2851, - "Lord De Seis": 2852, - "Infector of Souls": 2853, - "Riftwraith the Cannibal": 2854, - "Taintbreeder": 2855, - "The Tormentor": 2856, - "Winged Death": 2857, - "Maffer Dragonhand": 2858, - "Wyand Voidfinger": 2859, - "Toorc Icefist": 2860, - "Bremm Sparkfist": 2861, - "Geleb Flamefinger": 2862, - "Ismail Vilehand": 2863, - "Icehawk Riftwing": 2864, - "Sarina the Battlemaid": 2865, - "Stormtree": 2866, - "Witch Doctor Endugu": 2867, - "Web Mage the Burning": 2868, - "Bishibosh": 2869, - "Bonebreak": 2870, - "Coldcrow": 2871, - "Rakanishu": 2872, - "Treehead WoodFist": 2873, - "Griswold": 2874, - "The Countess": 2875, - "Pitspawn Fouldog": 2876, - "Flamespike the Crawler": 2877, - "Boneash": 2878, - "Radament": 2879, - "Bloodwitch the Wild": 2880, - "Fangskin": 2881, - "Beetleburst": 2882, - "Leatherarm": 2883, - "Coldworm the Burrower": 2884, - "Fire Eye": 2885, - "Dark Elder": 2886, - "The Summoner": 2887, - "Ancient Kaa the Soulless": 2888, - "The Smith": 2889, - "DeckardCain": 2890, - "Gheed": 2891, - "Akara": 2892, - "Kashya": 2893, - "Charsi": 2894, - "Wariv": 2895, - "Warriv": 2896, - "Rogue": 2897, - "StygianDoll": 2898, - "SoulKiller": 2899, - "Flayer": 2900, - "Fetish": 2901, - "RatMan": 2902, - "Undead StygianDoll": 2903, - "Undead SoulKiller": 2904, - "Undead Flayer": 2905, - "Undead Fetish": 2906, - "Undead RatMan": 2907, - "DarkFamiliar": 2908, - "BloodDiver": 2909, - "Gloombat": 2910, - "DesertWing": 2911, - "Banished": 2912, - "BloodLord": 2913, - "DarkLord": 2914, - "NightLord": 2915, - "GhoulLord": 2916, - "Spikefist": 2917, - "Thrasher": 2918, - "BrambleHulk": 2919, - "ThornedHulk": 2920, - "SpiderMagus": 2921, - "FlameSpider": 2922, - "PoisonSpinner": 2923, - "SandFisher": 2924, - "Arach": 2925, - "BloodWing": 2926, - "BloodHook": 2927, - "Feeder": 2928, - "Sucker": 2929, - "WingedNightmare": 2930, - "HellBuzzard": 2931, - "UndeadScavenger": 2932, - "CarrionBird": 2933, - "Unraveler": 2934, - "Guardian": 2935, - "HollowOne": 2936, - "Horadrim Ancient": 2937, - "AlbinoRoach": 2938, - "SteelWeevil": 2939, - "Scarab": 2940, - "SandWarrior": 2941, - "DungSoldier": 2942, - "HellSwarm": 2943, - "PlagueBugs": 2944, - "BlackLocusts": 2945, - "Itchies": 2946, - "HellCat": 2947, - "NightTiger": 2948, - "SaberCat": 2949, - "Huntress": 2950, - "RazorPitDemon": 2951, - "TreeLurker": 2952, - "CaveLeaper": 2953, - "TombCreeper": 2954, - "SandLeaper": 2955, - "TombViper": 2956, - "PitViper": 2957, - "Salamander": 2958, - "ClawViper": 2959, - "SerpentMagus": 2960, - "WorldKiller": 2961, - "GiantLamprey": 2962, - "Devourer": 2963, - "RockWorm": 2964, - "SandMaggot": 2965, - "JungleUrchin": 2966, - "RazorSpine": 2967, - "ThornBeast": 2968, - "SpikeFiend": 2969, - "QuillRat": 2970, - "HellClan": 2971, - "MoonClan": 2972, - "NightClan": 2973, - "DeathClan": 2974, - "BloodClan": 2975, - "TempleGuard": 2976, - "DoomApe": 2977, - "JungleHunter": 2978, - "RockDweller": 2979, - "DuneBeast": 2980, - "FleshHunter": 2981, - "BlackRogue": 2982, - "DarkStalker": 2983, - "VileHunter": 2984, - "DarkHunter": 2985, - "DarkShape": 2986, - "Apparition": 2987, - "Specter": 2988, - "Ghost": 2990, - "Assailant": 2991, - "Infidel": 2992, - "Invader": 2993, - "Marauder": 2994, - "SandRaider": 2995, - "GargantuanBeast": 2996, - "WailingBeast": 2997, - "Yeti": 2998, - "Crusher": 2999, - "Brute": 3000, - "CloudStalker": 3001, - "BlackVulture": 3002, - "BlackRaptor": 3003, - "BloodHawk": 3004, - "FoulCrow": 3005, - "PlagueBearer": 3006, - "Ghoul": 3007, - "DrownedCarcass": 3008, - "HungryDead": 3009, - "Zombie": 3010, - "Skeleton": 3011, - "Horror": 3012, - "Returned": 3013, - "BurningDead": 3014, - "BoneWarrior": 3015, - "Damned": 3016, - "Disfigured": 3017, - "Misshapen": 3018, - "Tainted": 3019, - "Afflicted": 3020, - "Andariel": 3021, - "Natalya": 3022, - "Drognan": 3023, - "Atma": 3024, - "Fara": 3025, - "Lysander": 3026, - "Jerhyn": 3027, - "jerhyn": 3028, - "Geglash": 3029, - "Elzix": 3030, - "Greiz": 3031, - "Meshif": 3032, - "Camel": 3033, - "Cadaver": 3034, - "PreservedDead": 3035, - "Embalmed": 3036, - "DriedCorpse": 3037, - "Decayed": 3038, - "Urdar": 3039, - "Mauler": 3040, - "Gorbelly": 3041, - "Blunderbore": 3042, - "WorldKillerYoung": 3043, - "GiantLampreyYoung": 3044, - "DevourerYoung": 3045, - "RockWormYoung": 3046, - "SandMaggotYoung": 3047, - "WorldKillerEgg": 3048, - "GiantLampreyEgg": 3049, - "DevourerEgg": 3050, - "RockWormEgg": 3051, - "SandMaggotEgg": 3052, - "Maggot": 3053, - "Duriel": 3054, - "BloodHawkNest": 3055, - "FlyingScimitar": 3056, - "CloudStalkerNest": 3057, - "BlackVultureNest": 3058, - "FoulCrowNest": 3059, - "Diablo": 3060, - "Baal": 3061, - "Mephisto": 3062, - "Cantor": 3063, - "Heirophant": 3064, - "Sexton": 3065, - "Zealot": 3066, - "Faithful": 3067, - "Zakarumite": 3068, - "BlackSoul": 3069, - "BurningSoul": 3070, - "SwampGhost": 3071, - "Gloam": 3072, - "WarpedShaman": 3073, - "DarkShaman": 3074, - "DevilkinShaman": 3075, - "CarverShaman": 3076, - "FallenShaman": 3077, - "WarpedFallen": 3078, - "DarkOne": 3079, - "Devilkin": 3080, - "Carver": 3081, - "Fallen": 3082, - "ReturnedArcher": 3083, - "HorrorArcher": 3084, - "BurningDeadArcher": 3085, - "BoneArcher": 3086, - "CorpseArcher": 3087, - "SkeletonArcher": 3088, - "FleshLancer": 3089, - "BlackLancer": 3090, - "DarkLancer": 3091, - "VileLancer": 3092, - "DarkSpearwoman": 3093, - "FleshArcher": 3094, - "BlackArcher": 3095, - "DarkRanger": 3096, - "VileArcher": 3097, - "DarkArcher": 3098, - "Summoner": 3099, - "StygianDollShaman": 3100, - "SoulKillerShaman": 3101, - "FlayerShaman": 3102, - "FetishShaman": 3103, - "RatManShaman": 3104, - "HorrorMage": 3105, - "BurningDeadMage": 3106, - "BoneMage": 3107, - "CorpseMage": 3108, - "ReturnedMage": 3109, - "GargoyleTrap": 3110, - "Bloodraven": 3111, - "navi": 3112, - "Kaelan": 3113, - "meshif": 3114, - "StygianWatcherHead": 3115, - "RiverStalkerHead": 3116, - "WaterWatcherHead": 3117, - "StygianWatcherLimb": 3118, - "RiverStalkerLimb": 3119, - "WaterWatcherLimb": 3120, - "NightMarauder": 3121, - "FireGolem": 3122, - "IronGolem": 3123, - "BloodGolem": 3124, - "ClayGolem": 3125, - "WorldKillerQueen": 3126, - "GiantLampreyQueen": 3127, - "DevourerQueen": 3128, - "RockWormQueen": 3129, - "SandMaggotQueen": 3130, - "Slime Prince": 3131, - "Bog Creature": 3132, - "Swamp Dweller": 3133, - "GiantUrchin": 3134, - "RazorBeast": 3135, - "ThornBrute": 3136, - "SpikeGiant": 3137, - "QuillBear": 3138, - "Council Member": 3139, - "youngdiablo": 3140, - "darkwanderer": 3141, - "HellSlinger": 3142, - "NightSlinger": 3143, - "SpearCat": 3144, - "Slinger": 3145, - "FireTower": 3146, - "LightningSpire": 3147, - "PitLord": 3148, - "Balrog": 3149, - "VenomLord": 3150, - "Iron Wolf": 3151, - "InvisoSpawner": 3152, - "OblivionKnight": 3153, - "Mage": 3154, - "AbyssKnight": 3155, - "Fighter Mage": 3156, - "DoomKnight": 3157, - "Fighter": 3158, - "MawFiend": 3159, - "CorpseSpitter": 3160, - "Corpulent": 3161, - "StormCaster": 3162, - "Strangler": 3163, - "Groper": 3164, - "GrotesqueWyrm": 3165, - "StygianDog": 3166, - "FleshBeast": 3167, - "Grotesque": 3168, - "StygianHag": 3169, - "FleshSpawner": 3170, - "RogueScout": 3171, - "BloodWingNest": 3172, - "BloodHookNest": 3173, - "FeederNest": 3174, - "SuckerNest": 3175, - "NecroMage": 3176, - "NecroSkeleton": 3177, - "TrappedSoul": 3178, - "Valkyrie": 3179, - "Dopplezon": 3180, - "Raises Fetishes": 3181, - "Raises Undead": 3182, - "Lays Eggs": 3183, - "Raises Fallen": 3184, - "heals Zealots and Cantors": 3185, - "drains mana and stamina": 3186, - "drains mana": 3187, - "drains stamina": 3188, - "stun attack": 3189, - "eats and spits corspes": 3190, - "homing missiles": 3191, - "raises Stygian Dolls": 3192, - "raises Soul Killers": 3193, - "raises Flayers": 3194, - "raises Fetishes": 3195, - "raises Ratmen": 3196, - "steals life": 3197, - "raises undead": 3198, - "raises Dark Ones": 3199, - "raises Devilkin": 3200, - "raises Carvers": 3201, - "raises Fallen": 3202, - "raises Warped Fallen": 3203, - "shocking hit": 3204, - "uniquextrastrong": 3205, - "uniqueextrafast": 3206, - "uniquecursed": 3207, - "uniquemagicresistance": 3208, - "uniquefireenchanted": 3209, - "monsteruniqueprop1": 3210, - "monsteruniqueprop2": 3211, - "monsteruniqueprop3": 3212, - "monsteruniqueprop4": 3213, - "monsteruniqueprop5": 3214, - "monsteruniqueprop6": 3215, - "monsteruniqueprop7": 3216, - "monsteruniqueprop8": 3217, - "monsteruniqueprop9": 3218, - "This Cow Bites": 3219, - "Champion": 3220, - "minion": 3221, - "Barrel": 3222, - "Lever1": 3223, - "BarrelEx": 3224, - "Door": 3225, - "Portal": 3226, - "ODoor": 3227, - "BlockedDoor": 3228, - "LockedDoor": 3229, - "StoneAlpha": 3230, - "StoneBeta": 3231, - "StoneDelta": 3232, - "StoneGamma": 3233, - "StoneLambda": 3234, - "StoneTheta": 3235, - "Crate": 3236, - "Casket": 3237, - "Cabinet": 3238, - "Vase": 3239, - "Inifuss": 3240, - "corpse": 3241, - "RogueCorpse": 3242, - "CorpseOnStick": 3243, - "TowerTome": 3244, - "Gibbet": 3245, - "MummyGenerator": 3246, - "ArmorStand": 3247, - "WeaponRack": 3248, - "Sarcophagus": 3249, - "Trap Door": 3250, - "LargeUrn": 3251, - "CanopicJar": 3252, - "Obelisk": 3253, - "HoleAnim": 3254, - "Shrine": 3255, - "Urn": 3256, - "Waypoint": 22526, - "Well": 3258, - "bag": 3259, - "Chest": 3260, - "chest": 3261, - "lockedchest": 3262, - "HorazonsJournal": 3263, - "templeshrine": 3264, - "stair": 3265, - "coffin": 3266, - "bookshelf": 3267, - "loose boulder": 3268, - "loose rock": 3269, - "hollow log": 3270, - "hiding spot": 3271, - "fire": 3328, - "Chest3": 3273, - "hidden stash": 3274, - "GuardCorpse": 3275, - "bowl": 3276, - "jug": 3277, - "AmbientSound": 3278, - "ratnest": 3279, - "burning body": 3280, - "well": 22525, - "door": 3282, - "skeleton": 3283, - "skullpile": 3284, - "cocoon": 3285, - "gidbinn altar": 3286, - "cowa": 3287, - "manashrine": 3288, - "bed": 3289, - "ratchest": 3290, - "bank": 3292, - "goo pile": 3293, - "holyshrine": 3294, - "teleportation pad": 3295, - "ratchest-r": 3296, - "skull pile": 3297, - "body": 3298, - "hell bridge": 3299, - "compellingorb": 3300, - "basket": 3301, - "Basket": 3302, - "RockPIle": 3303, - "Tome": 3304, - "dead body": 3305, - "eunuch": 3306, - "dead guard": 3307, - "portal": 3308, - "sarcophagus": 3309, - "dead villager": 3310, - "sewer lever": 3311, - "sewer stairs": 3312, - "magic shrine": 3313, - "wirt's body": 3314, - "stash": 3315, - "guyq": 3316, - "taintedsunaltar": 3317, - "Hellforge": 3318, - "Corpsefire": 3319, - "fissure": 3320, - "BoneChest": 3321, - "casket": 3322, - "HungSkeleton": 3323, - "pillar": 3324, - "Hydra": 3325, - "Turret": 3326, - "a trap": 3327, - "cost": 3329, - "Repair": 3330, - "Sell": 3331, - "Identify": 3332, - "priceless": 3333, - "NPCMenuTradeRepair": 3334, - "NPCPurchaseItems": 3335, - "NPCSellItems": 3336, - "NPCHeal": 3337, - "NPCRepairItems": 3338, - "NPCNextPage": 3339, - "NPCPreviousPage": 3340, - "strUiMenu2": 4131, - "TransactionMenu1a": 3342, - "TransactionMenu1f": 3343, - "VerifyTransaction1": 3344, - "VerifyTransaction2": 3345, - "VerifyTransaction3": 3346, - "VerifyTransaction4": 3347, - "VerifyTransaction5": 3348, - "VerifyTransaction6": 3349, - "VerifyTransaction7": 3350, - "VerifyTransaction8": 3351, - "VerifyTransaction9": 3352, - "TransactionResults1": 3353, - "TransactionResults2": 3354, - "TransactionResults3": 3355, - "TransactionResults4": 3356, - "TransactionResults5": 3357, - "TransactionResults6": 3358, - "TransactionResults7": 3359, - "TransactionResults8": 3360, - "TransactionResults9": 3361, - "TransactionResults10": 3362, - "TransactionResults11": 3363, - "ItemDesc1s": 3364, - "ItemDesc1t": 3365, - "HP": 3366, - "AC": 3367, - "Level": 3368, - "Cost": 3369, - "Damage": 3370, - "strhirespecial1": 3371, - "strhirespecial2": 3372, - "strhirespecial3": 3373, - "strhirespecial4": 3374, - "strhirespecial5": 3375, - "strhirespecial6": 3376, - "strhirespecial7": 3377, - "strhirespecial8": 3378, - "strhirespecial9": 3379, - "strhirespecial10": 3380, - "TalkMenu": 3381, - "WarrivMenu1b": 3382, - "WarrivMenu1c": 3383, - "MeshifMenuEast": 3384, - "MeshifMenuWest": 3385, - "NPCMenuNews0": 3386, - "NPCMenuNews1": 3387, - "NPCMenuNews2": 3388, - "NPCMenuNews3": 3389, - "NPCMenuNews4": 3390, - "NPCMenuTalkMore": 3391, - "NPCTownMore0": 3392, - "NPCTownMore1": 3393, - "NPCMenuLeave": 3394, - "NPCGossipMenu": 3395, - "NPCMenuTrade": 3396, - "NPCMenuHire": 3397, - "gamble": 3398, - "Intro": 3399, - "Back": 3400, - "ok": 3401, - "cancel": 3402, - "Continue": 3403, - "strMenuMain15": 3404, - "strOptMusic": 3405, - "strOptSound": 3406, - "strOptGamma": 3407, - "strOptRender": 3408, - "strOptPrevious": 3409, - "cfgCtrl": 3410, - "merc01": 3411, - "merc02": 3412, - "merc03": 3413, - "merc04": 3414, - "merc05": 3415, - "merc06": 3416, - "merc07": 3417, - "merc08": 3418, - "merc09": 3419, - "merc10": 3420, - "merc11": 3421, - "merc12": 3422, - "merc13": 3423, - "merc14": 3424, - "merc15": 3425, - "merc16": 3426, - "merc17": 3427, - "merc18": 3428, - "merc19": 3429, - "merc20": 3430, - "merc21": 3431, - "merc22": 3432, - "merc23": 3433, - "merc24": 3434, - "merc25": 3435, - "merc26": 3436, - "merc27": 3437, - "merc28": 3438, - "merc29": 3439, - "merc30": 3440, - "merc31": 3441, - "merc32": 3442, - "merc33": 3443, - "merc34": 3444, - "merc35": 3445, - "merc36": 3446, - "merc37": 3447, - "merc38": 3448, - "merc39": 3449, - "merc40": 3450, - "merc41": 3451, - "merclevelup": 3452, - "Socketable": 3453, - "ItemStats1a": 3454, - "ItemStats1b": 3455, - "ItemStats1c": 3456, - "ItemStats1d": 3457, - "ItemStats1e": 3458, - "ItemStats1f": 3459, - "ItemStats1g": 3460, - "ItemStats1h": 3461, - "ItemStats1i": 3462, - "ItemStats1j": 3463, - "ItemStast1k": 3464, - "ItemStats1l": 3465, - "ItemStats1m": 3466, - "ItemStats1n": 3467, - "ItemStats1o": 3468, - "ItemStats1p": 3469, - "ItemStats1q": 3470, - "ItemStatsrejuv1": 3471, - "ItemStatsrejuv2": 3472, - "ModStr1a": 3473, - "ModStr1b": 3474, - "ModStr1c": 3475, - "ModStr1d": 3476, - "ModStr1e": 3477, - "ModStr1f": 3478, - "ModStr1g": 3479, - "ModStr1h": 3480, - "ModStr1i": 3481, - "ModStr1j": 3482, - "ModStr1k": 3483, - "ModStr1l": 3484, - "ModStr1m": 3485, - "ModStr1n": 3486, - "ModStr1o": 3487, - "ModStr1p": 3488, - "ModStr1q": 3489, - "ModStr1r": 3490, - "ModStr1s": 3491, - "ModStr1t": 3492, - "ModStr1u": 3493, - "ModStr1v": 3494, - "ModStr1w": 3495, - "ModStr1x": 3496, - "ModStr1y": 3497, - "ModStr1z": 3498, - "ModStr2a": 3499, - "ModStr2b": 3500, - "ModStr2c": 3501, - "ModStr2d": 3502, - "ModStr2e": 3503, - "ModStr2f": 3504, - "ModStr2g": 3505, - "ModStr2h": 3506, - "ModStr2i": 3507, - "ModStr2j": 3508, - "ModStr2k": 3509, - "ModStr2l": 3510, - "ModStr2m": 3511, - "ModStr2n": 3512, - "ModStr2o": 3513, - "ModStr2p": 3514, - "ModStr2q": 3515, - "ModStr2r": 3516, - "ModStr2s": 3517, - "ModStr2t": 3518, - "ModStr2u": 3519, - "Modstr2v": 3520, - "ModStr2w": 3521, - "ModStr2x": 3522, - "ModStr2y": 3523, - "ModStr2z": 3524, - "ModStr3a": 3525, - "ModStr3b": 3526, - "ModStr3c": 3527, - "ModStr3d": 3528, - "ModStr3e": 3529, - "ModStr3f": 3530, - "ModStr3g": 3531, - "ModStr3h": 3532, - "ModStr3i": 3533, - "ModStr3j": 3534, - "ModStr3k": 3535, - "ModStr3l": 3536, - "ModStr3m": 3537, - "ModStr3n": 3538, - "ModStr3o": 3539, - "ModStr3p": 3540, - "ModStr3q": 3541, - "ModStr3r": 3542, - "ModStr3u": 3543, - "ModStr3v": 3544, - "ModStr3w": 3545, - "ModStr3x": 3546, - "ModStr3y": 3547, - "ModStr3z": 3548, - "ModStr4a": 3549, - "ModStr4b": 3550, - "ModStr4c": 3551, - "ModStr4d": 3552, - "ModStr4e": 3553, - "ModStr4f": 3554, - "ModStr4g": 3555, - "ModStr4h": 3556, - "ModStr4i": 3557, - "ModStr4j": 3558, - "ModStr4k": 3559, - "ModStr4l": 3560, - "ModStr4m": 3561, - "ModStr4n": 3562, - "ModStr4o": 3563, - "ModStr4p": 3564, - "ModStr4q": 3565, - "ModStr4r": 3566, - "ModStr4s": 3567, - "ModStr4t": 3568, - "ModStr4u": 3569, - "ModStr4v": 3570, - "ModStr4w": 3571, - "ModStr4x": 3572, - "ModStr4y": 3573, - "ModStr4z": 3574, - "ModStr5a": 3575, - "ModStr5b": 3576, - "ModStr5c": 3577, - "ModStr5d": 3578, - "ModStr5e": 3579, - "ModStr5f": 3580, - "ModStr5g": 3581, - "ModStr5h": 3582, - "ModStr5i": 3583, - "ModStr5j": 3584, - "ModStr5k": 3585, - "ModStr5l": 3586, - "ModStr5m": 3587, - "ModStr5n": 3588, - "ModStr5o": 3589, - "ModStr5p": 3590, - "ModStr5q": 3591, - "ModStr5r": 3592, - "ModStr5s": 3593, - "ModStr5t": 3594, - "ModStr5u": 3595, - "ModStr5v": 3596, - "ModStr5w": 3597, - "ModStr5x": 3598, - "ModStr5y": 3599, - "ModStr5z": 3600, - "ModStr6a": 3601, - "ModStr6b": 3602, - "ModStr6c": 3603, - "ModStr6d": 3604, - "ModStr6e": 3605, - "ModStr6f": 3606, - "ModStr6g": 3607, - "ModStr6h": 3608, - "ModStr6i": 3609, - "strModAllResistances": 3610, - "strModAllSkillLevels": 3611, - "strModFireDamage": 3612, - "strModFireDamageRange": 3613, - "strModColdDamage": 3614, - "strModColdDamageRange": 3615, - "strModLightningDamage": 3616, - "strModLightningDamageRange": 3617, - "strModMagicDamage": 3618, - "strModMagicDamageRange": 3619, - "strModPoisonDamage": 3620, - "strModPoisonDamageRange": 3621, - "strModMinDamage": 3622, - "strModMinDamageRange": 3623, - "strModEnhancedDamage": 3624, - "improved damage": 3625, - "improved to hit": 3626, - "improved armor class": 3627, - "improved durability": 3628, - "Quick Strike": 3629, - "strGemPlace1": 3630, - "strGemPlace2": 3631, - "gemeffect1": 3632, - "gemeffect2": 3633, - "gemeffect3": 3634, - "gemeffect4": 3635, - "gemeffect5": 3636, - "gemeffect6": 3637, - "gemeffect7": 3638, - "sysmsg1": 3639, - "sysmsg2": 3640, - "sysmsg3": 3641, - "sysmsg4": 3642, - "sysmsg3a": 3643, - "sysmsg4a": 3644, - "sysmsg5": 3645, - "sysmsg6": 3646, - "sysmsg7": 3647, - "sysmsg8": 3648, - "sysmsg9": 3649, - "sysmsg10": 3650, - "sysmsg11": 3651, - "sysmsg12": 3652, - "sysmsgPlayer": 3653, - "chatmsg1": 3654, - "chatmsg2": 3655, - "chatmsg3": 3657, - "strwhisperworked": 3658, - "syswork": 3659, - "ShrId0": 3660, - "ShrId1": 3661, - "ShrId2": 3662, - "ShrId3": 3663, - "ShrId4": 3664, - "ShrId5": 3665, - "ShrId6": 3666, - "ShrId7": 3667, - "ShrId8": 3668, - "ShrId9": 3669, - "ShrId10": 3670, - "ShrId11": 3671, - "ShrId12": 3672, - "ShrId13": 3673, - "ShrId14": 3674, - "ShrId15": 3675, - "ShrId16": 3676, - "ShrId17": 3677, - "ShrId18": 3678, - "ShrId19": 3679, - "ShrId20": 3680, - "ShrId21": 3681, - "ShrId22": 3682, - "ShrMsg0": 3683, - "ShrMsg1": 3684, - "ShrMsg2": 3685, - "ShrMsg3": 3686, - "ShrMsg4": 3687, - "ShrMsg5": 3688, - "ShrMsg6": 3689, - "ShrMsg7": 3690, - "ShrMsg8": 3691, - "ShrMsg9": 3692, - "ShrMsg10": 3693, - "ShrMsg11": 3694, - "ShrMsg12": 3695, - "ShrMsg13": 3696, - "ShrMsg14": 3697, - "ShrMsg15": 3698, - "ShrMsg16": 3699, - "ShrMsg17": 3700, - "ShrMsg18": 3701, - "ShrMsg19": 3702, - "ShrMsg20": 3703, - "ShrMsg21": 3704, - "ShrMsg22": 3705, - "strqi1": 3706, - "strqi2": 3707, - "stsa1q3alert": 3708, - "stsa1q4alert": 3709, - "stsa3q1alert": 3710, - "qstsa1qt": 3711, - "qstsa1qt0": 3712, - "qstsa1q0": 3713, - "qstsa1q1": 3714, - "qstsa1q2": 3715, - "qstsa1q3": 3716, - "qstsa1q4": 3717, - "qstsa1q5": 3718, - "qstsa1q6": 3719, - "strplaylast": 3720, - "newquestlog": 3721, - "qsts": 3722, - "noactivequest": 3723, - "qstsxxx": 3724, - "qstsnull": 3725, - "qstsComplete": 3726, - "qstsother": 3727, - "qstsprevious": 3728, - "qstsThankYouComeAgain": 3729, - "qstsThankYouComeAgainMulti": 3730, - "qstsThankYouComeAgainSingle": 3731, - "Qstsyouarenot8": 3732, - "qstsa1q3x": 3733, - "qstsa1q4x": 3734, - "qstsa1q11": 3735, - "qstsa1q12": 3736, - "qstsa1q13": 3737, - "qstsa1q14": 3738, - "qstsa1q140": 3739, - "qstsa1q15": 3740, - "qstsa1q21": 3741, - "qstsa1q22": 3742, - "qstsa1q23": 3743, - "qstsa1q41": 3744, - "qstsa1q42": 3745, - "qstsa1q43": 3746, - "qstsa1q44": 3747, - "qstsa1q45": 3748, - "qstsa1q46": 3749, - "qstsa1q46b": 3750, - "qstsa1q51": 3751, - "qstsa1q51a": 3752, - "qstsa1q51b": 3753, - "qstsa1q52": 3754, - "qstsa1q31": 3755, - "qstsa1q32": 3756, - "qstsa1q32b": 3757, - "qstsa1q61": 3758, - "qstsa1q62": 3759, - "qstsa1q62b": 3760, - "qstsa1q63": 3761, - "KeyNone": 3762, - "KeyLButton": 3763, - "KeyRButton": 3764, - "KeyCancel": 3765, - "KeyMButton": 3766, - "Key4Button": 3767, - "Key5Button": 3768, - "KeyWheelUp": 3769, - "KeyWheelDown": 3770, - "KeyKana": 3771, - "KeyJunja": 3772, - "KeyFinal": 3773, - "KeyKanji": 3774, - "KeyEscape": 3775, - "KeyConvert": 3776, - "KeyNonConvert": 3777, - "KeyAccept": 3778, - "KeyModeChange": 3779, - "KeyLeft": 3780, - "KeyUp": 3781, - "KeyRight": 3782, - "KeyDown": 3783, - "KeySelect": 3784, - "KeyExecute": 3785, - "KeyLWin": 3786, - "KeyRWin": 3787, - "KeyApps": 3788, - "KeyNumLock": 3789, - "KeyBack": 3790, - "KeyTab": 3791, - "KeyClear": 3792, - "KeyReturn": 3793, - "KeyShift": 3794, - "KeyControl": 3795, - "KeyMenu": 3796, - "KeyPause": 3797, - "KeyCapital": 3798, - "KeySpace": 3799, - "KeyPrior": 3800, - "KeyNext": 3801, - "KeyEnd": 3802, - "KeyHome": 3803, - "KeyPrint": 3804, - "KeySnapshot": 3805, - "KeyInsert": 3806, - "KeyDelete": 3807, - "KeyHelp": 3808, - "KeyNumPad0": 3809, - "KeyNumPad1": 3810, - "KeyNumPad2": 3811, - "KeyNumPad3": 3812, - "KeyNumPad4": 3813, - "KeyNumPad5": 3814, - "KeyNumPad6": 3815, - "KeyNumPad7": 3816, - "KeyNumPad8": 3817, - "KeyNumPad9": 3818, - "KeyMultiply": 3819, - "KeyAdd": 3820, - "KeySeparator": 3821, - "KeySubtract": 3822, - "KeyDecimal": 3823, - "KeyDivide": 3824, - "KeyF1": 3825, - "KeyF2": 3826, - "KeyF3": 3827, - "KeyF4": 3828, - "KeyF5": 3829, - "KeyF6": 3830, - "KeyF7": 3831, - "KeyF8": 3832, - "KeyF9": 3833, - "KeyF10": 3834, - "KeyF11": 3835, - "KeyF12": 3836, - "KeyF13": 3837, - "KeyF14": 3838, - "KeyF15": 3839, - "KeyF16": 3840, - "KeyF17": 3841, - "KeyF18": 3842, - "KeyF19": 3843, - "KeyF20": 3844, - "KeyF21": 3845, - "KeyF22": 3846, - "KeyF23": 3847, - "KeyF24": 3848, - "KeyScroll": 3849, - "KeySemicolon": 3850, - "KeyEqual": 3851, - "KeyComma": 3852, - "KeyMinus": 3853, - "KeyPeriod": 3854, - "KeySlash": 3855, - "KeyTilde": 3856, - "KeyLBracket": 3857, - "KeyBackslash": 3858, - "KeyRBracket": 3859, - "KeyApostrophe": 3860, - "ShorthandKeyMButton": 3861, - "ShorthandKey4Button": 3862, - "ShorthandKey5Button": 3863, - "ShorthandKeyWheelUp": 3864, - "ShorthandKeyWheelDown": 3865, - "ShorthandKeyKana": 3866, - "ShorthandKeyJunja": 3867, - "ShorthandKeyFinal": 3868, - "ShorthandKeyKanji": 3869, - "ShorthandKeyEscape": 3870, - "ShorthandKeyConvert": 3871, - "ShorthandKeyNonConvert": 3872, - "ShorthandKeyAccept": 3873, - "ShorthandKeyModeChange": 3874, - "ShorthandKeyLeft": 3875, - "ShorthandKeyRight": 3876, - "ShorthandKeyDown": 3877, - "ShorthandKeySelect": 3878, - "ShorthandKeyExecute": 3879, - "ShorthandKeyLeftWindows": 3880, - "ShorthandKeyRightWindows": 3881, - "ShorthandKeyApps": 3882, - "ShorthandKeyNumLock": 3883, - "ShorthandKeyBackspace": 3884, - "ShorthandKeyClear": 3885, - "ShorthandKeyEnter": 3886, - "ShorthandKeyShift": 3887, - "ShorthandKeyControl": 3888, - "ShorthandKeyPause": 3889, - "ShorthandKeyCapsLock": 3890, - "ShorthandKeySpace": 3891, - "ShorthandKeyPageUp": 3892, - "ShorthandKeyPageDown": 3893, - "ShorthandKeyHome": 3894, - "ShorthandKeyPrintScreen": 3895, - "ShorthandKeyInsert": 3896, - "ShorthandKeyDelete": 3897, - "ShorthandKeyHelp": 3898, - "ShorthandKeyNumPad0": 3899, - "ShorthandKeyNumPad1": 3900, - "ShorthandKeyNumPad2": 3901, - "ShorthandKeyNumPad3": 3902, - "ShorthandKeyNumPad4": 3903, - "ShorthandKeyNumPad5": 3904, - "ShorthandKeyNumPad6": 3905, - "ShorthandKeyNumPad7": 3906, - "ShorthandKeyNumPad8": 3907, - "ShorthandKeyNumPad9": 3908, - "ShorthandKeyNumPad*\tnp*": 3909, - "ShorthandKeyNumPad+\tnp+": 3910, - "ShorthandKeyNumPad-\tnp-": 3911, - "ShorthandKeyNumPad.\tnp.": 3912, - "ShorthandKeyNumPad/\tnp/": 3913, - "ShorthandKeyScroll": 3914, - "KeyMacOption": 3915, - "KeyMacCommand": 3916, - "KeyMacNumPad=\tNum Pad =": 3917, - "ShorthandKeyMacOption": 3918, - "ShorthandKeyMacCommand": 3919, - "ShorthandKeyMacNumPad=\tNP=": 3920, - "CfgFunction": 3921, - "CfgPrimaryKey": 3922, - "CfgSecondaryKey": 3923, - "CfgCharacter": 3924, - "CfgInventory": 3925, - "CfgParty": 3926, - "CfgMessageLog": 3927, - "CfgQuestLog": 3928, - "CfgChat": 3929, - "CfgAutoMap": 3930, - "CfgAutoMapCenter": 3931, - "CfgMiniMap": 3932, - "CfgHelp": 3933, - "CfgSkillTree": 3934, - "CfgSkillPick": 3935, - "CfgSkill1": 3936, - "CfgSkill2": 3937, - "CfgSkill3": 3938, - "CfgSkill4": 3939, - "CfgSkill5": 3940, - "CfgSkill6": 3941, - "CfgSkill7": 3942, - "CfgSkill8": 3943, - "Cfgskillup": 3944, - "Cfgskilldown": 3945, - "CfgBeltShow": 3946, - "CfgBelt1": 3947, - "CfgBelt2": 3948, - "CfgBelt3": 3949, - "CfgBelt4": 3950, - "CfgBelt5": 3951, - "CfgBelt6": 3952, - "CfgBelt7": 3953, - "CfgBelt8": 3954, - "CfgBelt9": 3955, - "CfgBelt10": 3956, - "CfgBelt11": 3957, - "CfgBelt12": 3958, - "CfgSay0": 3959, - "CfgSay1": 3960, - "CfgSay2": 3961, - "CfgSay3": 3962, - "CfgSay4": 3963, - "CfgSay5": 3964, - "CfgSay6": 3965, - "CfgRun": 3966, - "CfgRunLock": 3967, - "CfgStandStill": 3968, - "CfgShowItems": 3969, - "CfgClearScreen": 3970, - "CfgSnapshot": 3971, - "CfgDefault": 3972, - "CfgAccept": 3973, - "CfgCancel": 3974, - "strNoKeysAssigned": 3975, - "KeysAssigned": 3976, - "CantAssignMB": 3977, - "CantAssignMW": 3978, - "CantAssignKey": 3979, - "CfgClearKey": 3980, - "Cfgcleartextmsg": 3981, - "CfgTogglePortraits": 3982, - "CfgAutoMapFade": 3983, - "CfgAutoMapNames": 3984, - "CfgAutoMapParty": 3985, - "strlvlup": 3986, - "strnewskl": 3987, - "warpsheader": 3988, - "nowarps": 3989, - "waypointsheader": 3990, - "nowaypoints": 3991, - "max": 3992, - "MAX": 3993, - "colorcode": 3994, - "space": 3995, - "dash": 3996, - "colon": 3997, - "newline": 3998, - "pipe": 3999, - "slash": 4000, - "percent": 4001, - "plus": 4002, - "to": 4003, - "srostertitle": 4004, - "dwell": 4005, - "larva": 4006, - "Barbarian": 4007, - "Paladin": 4008, - "Necromancer": 4009, - "Sorceress": 4010, - "Amazon": 4011, - "druidstr \tDruid": 4012, - "assassinstr": 4013, - "Nest": 4014, - "NoParty": 4015, - "ItsMyParty": 4016, - "Upgrade": 4017, - "upgraderestrict": 4018, - "Use": 4019, - "NPCIdentify1": 4020, - "NPCIdentify2": 4021, - "strCannotDoThisToUnknown": 4022, - "Body Looted": 4023, - "Party1": 4024, - "Party2": 4025, - "Party3": 4026, - "Party4": 4027, - "Party5": 4028, - "Party6": 4029, - "Party7": 4030, - "Party8": 4031, - "Party9": 4032, - "strDropGoldHowMuch": 4033, - "strDropGoldInfo": 4034, - "strMsgLog": 4035, - "strBSArmor": 4036, - "strBSWeapons": 4037, - "strBSMagic": 4038, - "strBSMisc": 4039, - "strTrade": 4040, - "strTradeAccept": 4041, - "strTradeAgreeTo": 4042, - "strWaitingForOtherPlayer": 4043, - "strTradeBusy": 4044, - "strTradeTooFull": 4045, - "strTradeGoldHowMuch": 4046, - "strTradeTimeout": 4047, - "SysmsgPlayer1": 4048, - "strBankGoldDeposit": 4049, - "strBankGoldWithdraw": 4050, - "GoldMax": 4051, - "StrUI0": 4052, - "StrUI1": 4053, - "StrUI2": 4054, - "StrUI3": 4055, - "StrUI4": 4056, - "strchrlvl": 4057, - "strchrexp": 4058, - "strchrnxtlvl": 4059, - "strchrstr": 4060, - "strchrskm": 4061, - "strchrdex": 4062, - "strchratr": 4063, - "strchrdef": 4064, - "strchrrat": 4065, - "strchrvit": 4066, - "strchrstm": 4067, - "strchrlif": 4068, - "strchreng": 4069, - "strchrman": 4070, - "strchrfir": 4071, - "strchrcol": 4072, - "strchrlit": 4073, - "strchrpos": 4074, - "strchrstat": 4075, - "strchrrema": 4076, - "WeaponDescMace": 4077, - "WeaponDescAxe": 4078, - "WeaponDescSword": 4079, - "WeaponDescDagger": 4080, - "WeaponDescThrownPotion": 4081, - "WeaponDescJavelin": 4082, - "WeaponDescSpear": 4083, - "WeaponDescBow": 4084, - "WeaponDescStaff": 4085, - "WeaponDescPoleArm": 4086, - "WeaponDescCrossBow": 4087, - "WeaponAttackFastest": 4088, - "WeaponAttackVeryFast": 4089, - "WeaponAttackFast": 4090, - "WeaponAttackNormal": 4091, - "WeaponAttackSlow": 4092, - "WeaponAttackVerySlow": 4093, - "WeaponAttackSlowest": 4094, - "strNecromanerOnly": 4095, - "strPaladinOnly": 4096, - "strSorceressOnly": 4097, - "strMaceSpecialDamage": 4098, - "strGoldLabel": 4099, - "strParty1": 4100, - "strParty2": 4101, - "strParty3": 4102, - "strParty4": 4103, - "strParty5": 4104, - "strParty6": 4105, - "strParty7": 4106, - "strParty8": 4107, - "strParty9": 4108, - "strParty10": 4109, - "strParty11": 4110, - "strParty12": 4111, - "strParty13": 4112, - "strParty14": 4113, - "strParty15": 4114, - "strParty16": 4115, - "strParty17": 4116, - "strParty18": 4117, - "strParty19": 4118, - "strParty22": 4119, - "strParty24": 4120, - "strParty25": 4121, - "StrParty26": 4122, - "StrParty27": 4123, - "strGoldWithdraw": 4124, - "strGoldDrop": 4125, - "strGoldDeposit": 4126, - "strGoldTrade": 4127, - "strGoldInStash": 4128, - "strGoldTradepup": 4129, - "strUiMenu1": 4130, - "strUiBank": 4132, - "strUnknownTomb": 4133, - "strTradeOtherBox": 4134, - "strTradeBox": 4135, - "strFree": 4136, - "act1": 4137, - "act2": 4138, - "act3": 4139, - "act4": 4140, - "level": 4141, - "lowercasecancel": 4142, - "close": 4143, - "strClose": 4144, - "Lightning Spell": 4145, - "Fire Spell": 4146, - "Cold Spell": 4147, - "Yourparty": 4148, - "Inparty": 4149, - "Invite": 4150, - "Accept": 4151, - "Leave": 4152, - "Partyclose": 4153, - "partycharama": 4154, - "partycharsor": 4155, - "partycharbar": 4156, - "partycharnec": 4157, - "partycharpal": 4158, - "charavghit": 4159, - "charmonster": 4160, - "charmontohit1": 4161, - "charmontohit2": 4162, - "panelexp": 4163, - "panelstamina": 4164, - "panelhealth": 4165, - "panelmana": 4166, - "panelmini": 4167, - "panelcmini": 4168, - "minipanelchar": 4169, - "minipanelinv": 4170, - "minipaneltree": 4171, - "minipanelparty": 4172, - "minipanelautomap": 4173, - "minipanelmessage": 4174, - "minipanelquest": 4175, - "minipanelmenubtn": 4176, - "minipanelHelp": 4177, - "minipanelspecial": 4178, - "RunOn": 4179, - "RunOff": 4180, - "automapgame": 4181, - "automappw": 4182, - "automapdif": 4183, - "scrollbooktext": 4184, - "skilldesc1": 4185, - "skilldesc2": 4186, - "skilldesc3": 4187, - "skilldesc4": 4188, - "strpanel1": 4189, - "strpanel2": 4190, - "strpanel3": 4191, - "strpanel4": 4192, - "strpanel5": 4193, - "strpanel6": 4194, - "strpanel7": 4195, - "strpanel8": 4196, - "stashfull": 4197, - "Strhelp1": 4198, - "StrHelp2": 4199, - "StrHelp3": 4200, - "StrHelp4": 4201, - "StrHelp5": 4202, - "StrHelp6": 4203, - "StrHelp7": 4204, - "StrHelp8": 4205, - "StrHelp8a": 4206, - "StrHelp9": 4207, - "StrHelp10": 4208, - "StrHelp11": 4209, - "StrHelp12": 4210, - "StrHelp13": 4211, - "StrHelp14": 4212, - "StrHelp14a": 4213, - "StrHelp15": 4214, - "StrHelp16": 4215, - "StrHelp16a": 4216, - "StrHelp17": 4217, - "StrHelp18": 4218, - "StrHelp19": 4219, - "StrHelp20": 4220, - "StrHelp21": 4221, - "StrHelp22": 4222, - "strSklTree": 4223, - "StrSklTreea": 4224, - "StrSklTreeb": 4225, - "StrSklTreec": 4226, - "StrSklTree1": 4227, - "StrSklTree2": 4228, - "StrSklTree3": 4229, - "StrSklTree4": 4230, - "StrSklTree5": 4231, - "StrSklTree6": 4232, - "StrSklTree7": 4233, - "StrSklTree8": 4234, - "StrSklTree9": 4235, - "StrSklTree10": 4236, - "StrSklTree11": 4237, - "StrSklTree12": 4238, - "StrSklTree13": 4239, - "StrSklTree14": 4240, - "StrSklTree15": 4241, - "StrSklTree16": 4242, - "StrSklTree17": 4243, - "StrSklTree18": 4244, - "StrSklTree19": 4245, - "StrSklTree20": 4246, - "StrSklTree21": 4247, - "StrSklTree22": 4248, - "StrSklTree23": 4249, - "StrSklTree24": 4250, - "StrSklTree25": 4251, - "StrSkill0": 4252, - "StrSkill1": 4253, - "StrSkill2": 4254, - "StrSkill3": 4255, - "StrSkill4": 4256, - "StrSkill5": 4257, - "StrSkill6": 4258, - "StrSkill7": 4259, - "StrSkill8": 4260, - "StrSkill9": 4261, - "StrSkill10": 4262, - "StrSkill11": 4263, - "StrSkill12": 4264, - "StrSkill13": 4265, - "StrSkill14": 4266, - "StrSkill15": 4267, - "StrSkill16": 4268, - "StrSkill17": 4269, - "StrSkill18": 4270, - "StrSkill19": 4271, - "StrSkill20": 4272, - "StrSkill21": 4273, - "StrSkill22": 4274, - "StrSkill23": 4275, - "StrSkill24": 4276, - "StrSkill25": 4277, - "StrSkill26": 4278, - "StrSkill27": 4279, - "StrSkill28": 4280, - "StrSkill29": 4281, - "StrSkill30": 4282, - "StrSkill31": 4283, - "StrSkill32": 4284, - "StrSkill33": 4285, - "StrSkill34": 4286, - "StrSkill35": 4287, - "StrSkill36": 4288, - "StrSkill37": 4289, - "StrSkill38": 4290, - "StrSkill39": 4291, - "StrSkill40": 4292, - "StrSkill41": 4293, - "StrSkill42": 4294, - "StrSkill43": 4297, - "StrSkill44": 4298, - "StrSkill45": 4299, - "StrSkill46": 4300, - "StrSkill47": 4301, - "StrSkill48": 4302, - "StrSkill49": 4303, - "StrSkill50": 4304, - "StrSkill51": 4305, - "StrSkill52": 4306, - "StrSkill53": 4307, - "StrSkill54": 4308, - "StrSkill55": 4309, - "StrSkill56": 4310, - "StrSkill57": 4311, - "StrSkill58": 4312, - "StrSkill59": 4313, - "StrSkill60": 4314, - "StrSkill61": 4315, - "StrSkill62": 4316, - "StrSkill63": 4317, - "StrSkill64": 4318, - "StrSkill65": 4319, - "StrSkill66": 4320, - "StrSkill67": 4321, - "StrSkill68": 4322, - "StrSkill69": 4323, - "StrSkill70": 4324, - "StrSkill71": 4325, - "StrSkill72": 4326, - "StrSkill73": 4327, - "StrSkill74": 4328, - "StrSkill75": 4329, - "StrSkill76": 4330, - "StrSkill77": 4331, - "StrSkill78": 4332, - "StrSkill79": 4333, - "StrSkill80": 4334, - "StrSkill81": 4335, - "StrSkill82": 4336, - "StrSkill83": 4337, - "StrSkill84": 4338, - "StrSkill85": 4339, - "StrSkill86": 4340, - "StrSkill87": 4341, - "StrSkill88": 4342, - "StrSkill89": 4343, - "StrSkill90": 4344, - "StrSkill91": 4345, - "StrSkill92": 4346, - "StrSkill94": 4347, - "StrSkill95": 4348, - "StrSkill96": 4349, - "StrSkill97": 4350, - "StrSkill98": 4351, - "StrSkill99": 4352, - "StrSkill100": 4353, - "StrSkill101": 4354, - "StrSkill102": 4355, - "StrSkill103": 4356, - "StrSkill104": 4357, - "StrSkill105": 4358, - "StrSkill106": 4359, - "StrSkill107": 4360, - "StrSkill108": 4361, - "StrSkill109": 4362, - "StrSkill110": 4363, - "StrSkill111": 4364, - "StrSkill112": 4365, - "StrSkill113": 4366, - "StrSkill114": 4367, - "StrSkill115": 4368, - "StrSkill116": 4369, - "StrSkill117": 4370, - "StrSkill118": 4371, - "StrSkill119": 4372, - "skillname0": 4373, - "skillsd0": 4374, - "skillld0": 4375, - "skillan0": 4376, - "skillname1": 4377, - "skillsd1": 4378, - "skillld1": 4379, - "skillan1": 4380, - "skillname2": 4381, - "skillsd2": 4382, - "skillld2": 4383, - "skillan2": 4384, - "skillname3": 4385, - "skillsd3": 4386, - "skillld3": 4387, - "skillan3": 4388, - "skillname4": 4389, - "skillsd4": 4390, - "skillld4": 4391, - "skillan4": 4392, - "skillname5": 4393, - "skillsd5": 4394, - "skillld5": 4395, - "skillan5": 4396, - "skillname6": 4397, - "skillsd6": 4398, - "skillld6": 4399, - "skillan6": 4400, - "skillname7": 4401, - "skillsd7": 4402, - "skillld7": 4403, - "skillan7": 4404, - "skillname8": 4405, - "skillsd8": 4406, - "skillld8": 4407, - "skillan8": 4408, - "skillname9": 4409, - "skillsd9": 4410, - "skillld9": 4411, - "skillan9": 4412, - "skillname10": 4413, - "skillsd10": 4414, - "skillld10": 4415, - "skillan10": 4416, - "skillname11": 4417, - "skillsd11": 4418, - "skillld11": 4419, - "skillan11": 4420, - "skillname12": 4421, - "skillsd12": 4422, - "skillld12": 4423, - "skillan12": 4424, - "skillname13": 4425, - "skillsd13": 4426, - "skillld13": 4427, - "skillan13": 4428, - "skillname14": 4429, - "skillsd14": 4430, - "skillld14": 4431, - "skillan14": 4432, - "skillname15": 4433, - "skillsd15": 4434, - "skillld15": 4435, - "skillan15": 4436, - "skillname16": 4437, - "skillsd16": 4438, - "skillld16": 4439, - "skillan16": 4440, - "skillname17": 4441, - "skillsd17": 4442, - "skillld17": 4443, - "skillan17": 4444, - "skillname18": 4445, - "skillsd18": 4446, - "skillld18": 4447, - "skillan18": 4448, - "skillname19": 4449, - "skillsd19": 4450, - "skillld19": 4451, - "skillan19": 4452, - "skillname20": 4453, - "skillsd20": 4454, - "skillld20": 4455, - "skillan20": 4456, - "skillname21": 4457, - "skillsd21": 4458, - "skillld21": 4459, - "skillan21": 4460, - "skillname22": 4461, - "skillsd22": 4462, - "skillld22": 4463, - "skillan22": 4464, - "skillname23": 4465, - "skillsd23": 4466, - "skillld23": 4467, - "skillan23": 4468, - "skillname24": 4469, - "skillsd24": 4470, - "skillld24": 4471, - "skillan24": 4472, - "skillname25": 4473, - "skillsd25": 4474, - "skillld25": 4475, - "skillan25": 4476, - "skillname26": 4477, - "skillsd26": 4478, - "skillld26": 4479, - "skillan26": 4480, - "skillname27": 4481, - "skillsd27": 4482, - "skillld27": 4483, - "skillan27": 4484, - "skillname28": 4485, - "skillsd28": 4486, - "skillld28": 4487, - "skillan28": 4488, - "skillname29": 4489, - "skillsd29": 4490, - "skillld29": 4491, - "skillan29": 4492, - "skillname30": 4493, - "skillsd30": 4494, - "skillld30": 4495, - "skillan30": 4496, - "skillname31": 4497, - "skillsd31": 4498, - "skillld31": 4499, - "skillan31": 4500, - "skillname32": 4501, - "skillsd32": 4502, - "skillld32": 4503, - "skillan32": 4504, - "skillname33": 4505, - "skillsd33": 4506, - "skillld33": 4507, - "skillan33": 4508, - "skillname34": 4509, - "skillsd34": 4510, - "skillld34": 4511, - "skillan34": 4512, - "skillname35": 4513, - "skillsd35": 4514, - "skillld35": 4515, - "skillan35": 4516, - "skillname36": 4517, - "skillsd36": 4518, - "skillld36": 4519, - "skillan36": 4520, - "skillname37": 4521, - "skillsd37": 4522, - "skillld37": 4523, - "skillan37": 4524, - "skillname38": 4525, - "skillsd38": 4526, - "skillld38": 4527, - "skillan38": 4528, - "skillname39": 4529, - "skillsd39": 4530, - "skillld39": 4531, - "skillan39": 4532, - "skillname40": 4533, - "skillsd40": 4534, - "skillld40": 4535, - "skillan40": 4536, - "skillname41": 4537, - "skillsd41": 4538, - "skillld41": 4539, - "skillan41": 4540, - "skillname42": 4541, - "skillsd42": 4542, - "skillld42": 4543, - "skillan42": 4544, - "skillname43": 4545, - "skillsd43": 4546, - "skillld43": 4547, - "skillan43": 4548, - "skillname44": 4549, - "skillsd44": 4550, - "skillld44": 4551, - "skillan44": 4552, - "skillname45": 4553, - "skillsd45": 4554, - "skillld45": 4555, - "skillan45": 4556, - "skillname46": 4557, - "skillsd46": 4558, - "skillld46": 4559, - "skillan46": 4560, - "skillname47": 4561, - "skillsd47": 4562, - "skillld47": 4563, - "skillan47": 4564, - "skillname48": 4565, - "skillsd48": 4566, - "skillld48": 4567, - "skillan48": 4568, - "skillname49": 4569, - "skillsd49": 4570, - "skillld49": 4571, - "skillan49": 4572, - "skillname50": 4573, - "skillsd50": 4574, - "skillld50": 4575, - "skillan50": 4576, - "skillname51": 4577, - "skillsd51": 4578, - "skillld51": 4579, - "skillan51": 4580, - "skillname52": 4581, - "skillsd52": 4582, - "skillld52": 4583, - "skillan52": 4584, - "skillname53": 4585, - "skillsd53": 4586, - "skillld53": 4587, - "skillan53": 4588, - "skillname54": 4589, - "skillsd54": 4590, - "skillld54": 4591, - "skillan54": 4592, - "skillname55": 4593, - "skillsd55": 4594, - "skillld55": 4595, - "skillan55": 4596, - "skillname56": 4597, - "skillsd56": 4598, - "skillld56": 4599, - "skillan56": 4600, - "skillname57": 4601, - "skillsd57": 4602, - "skillld57": 4603, - "skillan57": 4604, - "skillname58": 4605, - "skillsd58": 4606, - "skillld58": 4607, - "skillan58": 4608, - "skillname59": 4609, - "skillsd59": 4610, - "skillld59": 4611, - "skillan59": 4612, - "skillname60": 4613, - "skillsd60": 4614, - "skillld60": 4615, - "skillan60": 4616, - "skillsname61": 4617, - "skillsd61": 4618, - "skillld61": 4619, - "skillan61": 4620, - "skillname62": 4621, - "skillsd62": 4622, - "skillld62": 4623, - "skillan62": 4624, - "skillname63": 4625, - "skillsd63": 4626, - "skillld63": 4627, - "skillan63": 4628, - "skillname64": 4629, - "skillsd64": 4630, - "skillld64": 4631, - "skillan64": 4632, - "skillname65": 4633, - "skillsd65": 4634, - "skillld65": 4635, - "skillan65": 4636, - "skillname66": 4637, - "skillsd66": 4638, - "skillld66": 4639, - "skillan66": 4640, - "skillname67": 4641, - "skillsd67": 4642, - "skillld67": 4643, - "skillan67": 4644, - "skillname68": 4645, - "skillsd68": 4646, - "skillld68": 4647, - "skillan68": 4648, - "skillname69": 4649, - "skillsd69": 4650, - "skillld69": 4651, - "skillan69": 4652, - "skillname70": 4653, - "skillsd70": 4654, - "skillld70": 4655, - "skillan70": 4656, - "skillname71": 4657, - "skillsd71": 4658, - "skillld71": 4659, - "skillan71": 4660, - "skillname72": 4661, - "skillsd72": 4662, - "skillld72": 4663, - "skillan72": 4664, - "skillname73": 4665, - "skillsd73": 4666, - "skillld73": 4667, - "skillan73": 4668, - "skillname74": 4669, - "skillsd74": 4670, - "skillld74": 4671, - "skillan74": 4672, - "skillname75": 4673, - "skillsd75": 4674, - "skillld75": 4675, - "skillan75": 4676, - "skillname76": 4677, - "skillsd76": 4678, - "skillld76": 4679, - "skillan76": 4680, - "skillname77": 4681, - "skillsd77": 4682, - "skillld77": 4683, - "skillan77": 4684, - "skillname78": 4685, - "skillsd78": 4686, - "skillld78": 4687, - "skillan78": 4688, - "skillname79": 4689, - "skillsd79": 4690, - "skillld79": 4691, - "skillan79": 4692, - "skillname80": 4693, - "skillsd80": 4694, - "skillld80": 4695, - "skillan80": 4696, - "skillname81": 4697, - "skillsd81": 4698, - "skillld81": 4699, - "skillan81": 4700, - "skillname82": 4701, - "skillsd82": 4702, - "skillld82": 4703, - "skillan82": 4704, - "skillname83": 4705, - "skillsd83": 4706, - "skillld83": 4707, - "skillan83": 4708, - "skillname84": 4709, - "skillsd84": 4710, - "skillld84": 4711, - "skillan84": 4712, - "skillname85": 4713, - "skillsd85": 4714, - "skillld85": 4715, - "skillan85": 4716, - "skillname86": 4717, - "skillsd86": 4718, - "skillld86": 4719, - "skillan86": 4720, - "skillname87": 4721, - "skillsd87": 4722, - "skillld87": 4723, - "skillan87": 4724, - "skillname88": 4725, - "skillsd88": 4726, - "skillld88": 4727, - "skillan88": 4728, - "skillname89": 4729, - "skillsd89": 4730, - "skillld89": 4731, - "skillan89": 4732, - "skillname90": 4733, - "skillsd90": 4734, - "skillld90": 4735, - "skillan90": 4736, - "skillname91": 4737, - "skillsd91": 4738, - "skillld91": 4739, - "skillan91": 4740, - "skillname92": 4741, - "skillsd92": 4742, - "skillld92": 4743, - "skillan92": 4744, - "skillname93": 4745, - "skillsd93": 4746, - "skillld93": 4747, - "skillan93": 4748, - "skillname94": 4749, - "skillsd94": 4750, - "skillld94": 4751, - "skillan94": 4752, - "skillname95": 4753, - "skillsd95": 4754, - "skillld95": 4755, - "skillan95": 4756, - "skillname96": 4757, - "skillsd96": 4758, - "skillld96": 4759, - "skillan96": 4760, - "skillname97": 4761, - "skillsd97": 4762, - "skillld97": 4763, - "skillan97": 4764, - "skillname98": 4765, - "skillsd98": 4766, - "skillld98": 4767, - "skillan98": 4768, - "skillname99": 4769, - "skillsd99": 4770, - "skillld99": 4771, - "skillan99": 4772, - "skillname100": 4773, - "skillsd100": 4774, - "skillld100": 4775, - "skillan100": 4776, - "skillname101": 4777, - "skillsd101": 4778, - "skillld101": 4779, - "skillan101": 4780, - "skillname102": 4781, - "skillsd102": 4782, - "skillld102": 4783, - "skillan102": 4784, - "skillname103": 4785, - "skillsd103": 4786, - "skillld103": 4787, - "skillan103": 4788, - "skillname104": 4789, - "skillsd104": 4790, - "skillld104": 4791, - "skillan104": 4792, - "skillname105": 4793, - "skillsd105": 4794, - "skillld105": 4795, - "skillan105": 4796, - "skillname106": 4797, - "skillsd106": 4798, - "skillld106": 4799, - "skillan106": 4800, - "skillname107": 4801, - "skillsd107": 4802, - "skillld107": 4803, - "skillan107": 4804, - "skillname108": 4805, - "skillsd108": 4806, - "skillld108": 4807, - "skillan108": 4808, - "skillname109": 4809, - "skillsd109": 4810, - "skillld109": 4811, - "skillan109": 4812, - "skillname110": 4813, - "skillsd110": 4814, - "skillld110": 4815, - "skillan110": 4816, - "skillname111": 4817, - "skillsd111": 4818, - "skillld111": 4819, - "skillan111": 4820, - "skillname112": 4821, - "skillsd112": 4822, - "skillld112": 4823, - "skillan112": 4824, - "skillname113": 4825, - "skillsd113": 4826, - "skillld113": 4827, - "skillan113": 4828, - "skillname114": 4829, - "skillsd114": 4830, - "skillld114": 4831, - "skillan114": 4832, - "skillname115": 4833, - "skillsd115": 4834, - "skillld115": 4835, - "skillan115": 4836, - "skillname116": 4837, - "skillsd116": 4838, - "skillld116": 4839, - "skillan116": 4840, - "skillname117": 4841, - "skillsd117": 4842, - "skillld117": 4843, - "skillan117": 4844, - "skillname118": 4845, - "skillsd118": 4846, - "skillld118": 4847, - "skillan118": 4848, - "skillname119": 4849, - "skillsd119": 4850, - "skillld119": 4851, - "skillan119": 4852, - "skillname120": 4853, - "skillsd120": 4854, - "skillld120": 4855, - "skillan120": 4856, - "skillname121": 4857, - "skillsd121": 4858, - "skillld121": 4859, - "skillan121": 4860, - "skillname122": 4861, - "skillsd122": 4862, - "skillld122": 4863, - "skillan122": 4864, - "skillname123": 4865, - "skillsd123": 4866, - "skillld123": 4867, - "skillan123": 4868, - "skillname124": 4869, - "skillsd124": 4870, - "skillld124": 4871, - "skillan124": 4872, - "skillname125": 4873, - "skillsd125": 4874, - "skillld125": 4875, - "skillan125": 4876, - "skillname126": 4877, - "skillsd126": 4878, - "skillld126": 4879, - "skillan126": 4880, - "skillname127": 4881, - "skillsd127": 4882, - "skillld127": 4883, - "skillan127": 4884, - "skillname128": 4885, - "skillsd128": 4886, - "skillld128": 4887, - "skillan128": 4888, - "skillname129": 4889, - "skillsd129": 4890, - "skillld129": 4891, - "skillan129": 4892, - "skillname130": 4893, - "skillsd130": 4894, - "skillld130": 4895, - "skillan130": 4896, - "skillname131": 4897, - "skillsd131": 4898, - "skillld131": 4899, - "skillan131": 4900, - "skillname132": 4901, - "skillsd132": 4902, - "skillld132": 4903, - "skillan132": 4904, - "skillname133": 4905, - "skillsd133": 4906, - "skillld133": 4907, - "skillan133": 4908, - "skillname134": 4909, - "skillsd134": 4910, - "skillld134": 4911, - "skillan134": 4912, - "skillname135": 4913, - "skillsd135": 4914, - "skillld135": 4915, - "skillan135": 4916, - "skillname136": 4917, - "skillsd136": 4918, - "skillld136": 4919, - "skillan136": 4920, - "skillname137": 4921, - "skillsd137": 4922, - "skillld137": 4923, - "skillan137": 4924, - "skillname138": 4925, - "skillsd138": 4926, - "skillld138": 4927, - "skillan138": 4928, - "skillname139": 4929, - "skillsd139": 4930, - "skillld139": 4931, - "skillan139": 4932, - "skillname140": 4933, - "skillsd140": 4934, - "skillld140": 4935, - "skillan140": 4936, - "skillname141": 4937, - "skillsd141": 4938, - "skillld141": 4939, - "skillan141": 4940, - "skillname142": 4941, - "skillsd142": 4942, - "skillld142": 4943, - "skillan142": 4944, - "skillname143": 4945, - "skillsd143": 4946, - "skillld143": 4947, - "skillan143": 4948, - "skillname144": 4949, - "skillsd144": 4950, - "skillld144": 4951, - "skillan144": 4952, - "skillname145": 4953, - "skillsd145": 4954, - "skillld145": 4955, - "skillan145": 4956, - "skillname146": 4957, - "skillsd146": 4958, - "skillld146": 4959, - "skillan146": 4960, - "skillname147": 4961, - "skillsd147": 4962, - "skillld147": 4963, - "skillan147": 4964, - "skillname148": 4965, - "skillsd148": 4966, - "skillld148": 4967, - "skillan148": 4968, - "skillname149": 4969, - "skillsd149": 4970, - "skillld149": 4971, - "skillan149": 4972, - "skillname150": 4973, - "skillsd150": 4974, - "skillld150": 4975, - "skillan150": 4976, - "skillname151": 4977, - "skillsd151": 4978, - "skillld151": 4979, - "skillan151": 4980, - "skillname152": 4981, - "skillsd152": 4982, - "skillld152": 4983, - "skillan152": 4984, - "skillname153": 4985, - "skillsd153": 4986, - "skillld153": 4987, - "skillan153": 4988, - "skillname154": 4989, - "skillsd154": 4990, - "skillld154": 4991, - "skillan154": 4992, - "skillname155": 4993, - "skillsd155": 4994, - "skillld155": 4995, - "skillan155": 4996, - "skillname217": 4997, - "skillsd217": 4998, - "skillld217": 4999, - "skillan217": 5000, - "skillname218": 5001, - "skillsd218": 5002, - "skillld218": 5003, - "skillan218": 5004, - "skillname219": 5005, - "skillsd219": 5006, - "skillld219": 5007, - "skillan219": 5008, - "skillname220": 5009, - "skillsd220": 5010, - "skillld220": 5011, - "skillan220": 5012, - "strMephistoDoorLocked": 5013, - "strTitleFeminine": 5014, - "strTitleMasculine": 5015, - "strChatHardcore": 5016, - "strChatLevel": 5017, - "Tristram": 5018, - "Catacombs Level 4": 5019, - "Catacombs Level 3": 5020, - "Catacombs Level 2": 5021, - "Catacombs Level 1": 5022, - "Cathedral": 5023, - "Inner Cloister": 5024, - "Jail Level 3": 5025, - "Jail Level 2": 5026, - "Jail Level 1": 5027, - "Barracks": 5028, - "Outer Cloister": 5029, - "Monastery Gate": 5030, - "Tower Cellar Level 5": 5031, - "Tower Cellar Level 4": 5032, - "Tower Cellar Level 3": 5033, - "Tower Cellar Level 2": 5034, - "Tower Cellar Level 1": 5035, - "Forgotten Tower": 5036, - "Mausoleum": 5037, - "Crypt": 5038, - "Burial Grounds": 5039, - "Pit Level 2": 5040, - "Hole Level 2": 5041, - "Underground Passage Level 2": 5042, - "Cave Level 2": 5043, - "Pit Level 1": 5044, - "Hole Level 1": 5045, - "Underground Passage Level 1": 5046, - "Cave Level 1": 5047, - "Den of Evil": 5048, - "Tamoe Highland": 5049, - "Black Marsh": 5050, - "Dark Wood": 5051, - "Stony Field": 5052, - "Cold Plains": 5053, - "Blood Moor": 5054, - "Rogue Encampment": 5055, - "To Tristram": 5056, - "To The Catacombs Level 4": 5057, - "To The Catacombs Level 3": 5058, - "To The Catacombs Level 2": 5059, - "To The Catacombs Level 1": 5060, - "To The Cathedral": 5061, - "To The Inner Cloister": 5062, - "To The Jail Level 3": 5063, - "To The Jail Level 2": 5064, - "To The Jail Level 1": 5065, - "To The Barracks": 5066, - "To The Outer Cloister": 5067, - "To The Monastery Gate": 5068, - "To The Tower Cellar Level 5": 5069, - "To The Tower Cellar Level 4": 5070, - "To The Tower Cellar Level 3": 5071, - "To The Tower Cellar Level 2": 5072, - "To The Tower Cellar Level 1": 5073, - "To The Forgotten Tower": 5074, - "To The Mausoleum": 5075, - "To The Crypt": 5076, - "To The Burial Grounds": 5077, - "To The Pit Level 2": 5078, - "To The Hole Level 2": 5079, - "To Underground Passage Level 2": 5080, - "To The Cave Level 2": 5081, - "To The Pit Level 1": 5082, - "To The Hole Level 1": 5083, - "To Underground Passage Level 1": 5084, - "To The Cave Level 1": 5085, - "To The Den of Evil": 5086, - "To The Tamoe Highland": 5087, - "To The Black Marsh": 5088, - "To The Dark Wood": 5089, - "To The Stony Field": 5090, - "To The Cold Plains": 5091, - "To The Blood Moor": 5092, - "To The Rogue Encampment": 5093, - "Deathmessage": 5094, - "Deathmessnight": 5095, - "Harddeathmessage": 5096, - "LordofTerrordied": 5097, - "Killdiablo1": 5098, - "KillDiablo2": 5099, - "KillDiablo3": 5100, - "x": 22741, - "X": 22746, - "Gem Activated": 5334, - "Gem Deactivated": 5335, - "Perfect Gem Activated": 5336, - "dummy": 5382, - "Dummy": 5383, - "not used": 5384, - "unused": 5385, - "Not used": 5386, - "convertsto": 5387, - "strNotInBeta": 5388, - "strLevelLoadFailed": 5389, - "Endthispuppy": 5390, - "A4Q2ExpansionSuccessTyrael": 20000, - "A4Q2ExpansionSuccessCain": 20001, - "AncientsAct5IntroGossip1": 20002, - "CainAct5IntroGossip1": 20003, - "CainAct5Gossip1": 20004, - "CainAct5Gossip2": 20005, - "CainAct5Gossip3": 20006, - "CainAct5Gossip4": 20007, - "CainAct5Gossip5": 20008, - "CainAct5Gossip6": 20009, - "CainAct5Gossip7": 20010, - "CainAct5Gossip8": 20011, - "CainAct5Gossip9": 20012, - "CainAct5Gossip10": 20013, - "AnyaAct5IntroGossip1": 20014, - "AnyaGossip1": 20015, - "AnyaGossip2": 20016, - "AnyaGossip3": 20017, - "AnyaGossip4": 20018, - "AnyaGossip5": 20019, - "AnyaGossip6": 20020, - "AnyaGossip7": 20021, - "AnyaGossip8": 20022, - "AnyaGossip9": 20023, - "AnyaGossip10": 20024, - "LarzukAct5IntroGossip1": 20025, - "LarzukAct5IntroAmaGossip1": 20026, - "LarzukGossip1": 20027, - "LarzukGossip2": 20028, - "LarzukGossip3": 20029, - "LarzukGossip4": 20030, - "LarzukGossip5": 20031, - "LarzukGossip6": 20032, - "LarzukGossip7": 20033, - "LarzukGossip8": 20034, - "LarzukGossip9": 20035, - "LarzukGossip10": 20036, - "MalahAct5IntroGossip1": 20037, - "MalahAct5IntroSorGossip1": 20038, - "MalahAct5IntroBarGossip1": 20039, - "MalahGossip1": 20040, - "MalahGossip2": 20041, - "MalahGossip3": 20042, - "MalahGossip4": 20043, - "MalahGossip5": 20044, - "MalahGossip6": 20045, - "MalahGossip7": 20046, - "MalahGossip8": 20047, - "MalahGossip9": 20048, - "MalahGossip10": 20049, - "MalahGossip11": 20050, - "MalahGossip12": 20051, - "MalahGossip13": 20052, - "NihlathakAct5IntroGossip1": 20053, - "NihlathakAct5IntroAssGossip1": 20054, - "NihlathakAct5IntroNecGossip1": 20055, - "NihlathakGossip1": 20056, - "NihlathakGossip2": 20057, - "NihlathakGossip3": 20058, - "NihlathakGossip4": 20059, - "NihlathakGossip5": 20060, - "NihlathakGossip6": 20061, - "NihlathakGossip7": 20062, - "NihlathakGossip8": 20063, - "NihlathakGossip9": 20064, - "QualKehkAct5IntroGossip1": 20065, - "QualKehkAct5IntroPalGossip1": 20066, - "QualKehkAct5IntroDruGossip1": 20067, - "QualKehkGossip1": 20068, - "QualKehkGossip2": 20069, - "QualKehkGossip3": 20070, - "QualKehkGossip4": 20071, - "QualKehkGossip5": 20072, - "QualKehkGossip6": 20073, - "QualKehkGossip7": 20074, - "QualKehkGossip8": 20075, - "QualKehkGossip9": 20076, - "A5Q1InitLarzuk": 20077, - "A5Q1AfterInitLarzuk": 20078, - "A5Q1AfterInitCain": 20079, - "A5Q1AfterInitAnya": 20080, - "A5Q1AfterInitMalah": 20081, - "A5Q1AfterInitNihlathak": 20082, - "A5Q1AfterInitQualKehk": 20083, - "A5Q1EarlyReturnLarzuk": 20084, - "A5Q1EarlyReturnCain": 20085, - "A5Q1EarlyReturnAnya": 20086, - "A5Q1EarlyReturnMalah": 20087, - "A5Q1EarlyReturnNihlathak": 20088, - "A5Q1EarlyReturnQualKehk": 20089, - "A5Q1SuccessfulLarzuk": 20090, - "A5Q1SuccessfulCain": 20091, - "A5Q1SuccessfulAnya": 20092, - "A5Q1SuccessfulMalah": 20093, - "A5Q1SuccessfulNihlathak": 20094, - "A5Q1SuccessfulQualKehk": 20095, - "A5Q2InitQualKehk": 20096, - "A5Q2AfterInitQualKehk": 20097, - "A5Q2AfterInitCain": 20098, - "A5Q2AfterInitAnya": 20099, - "A5Q2AfterInitLarzuk": 20100, - "A5Q2AfterInitMalah": 20101, - "A5Q2AfterInitNihlathak": 20102, - "A5Q2EarlyReturnQualKehk": 20103, - "A5Q2EarlyReturnQualKehkMan": 20104, - "A5Q2EarlyReturnCain": 20105, - "A5Q2EarlyReturnAnya": 20106, - "A5Q2EarlyReturnLarzuk": 20107, - "A5Q2EarlyReturnMalah": 20108, - "A5Q2EarlyReturnNihlathak": 20109, - "A5Q2SuccessfulQualKehk": 20110, - "A5Q2SuccessfulCain": 20111, - "A5Q2SuccessfulAnya": 20112, - "A5Q2SuccessfulLarzuk": 20113, - "A5Q2SuccessfulMalah": 20114, - "A5Q2SuccessfulNihlathak": 20115, - "A5Q3InitMalah": 20116, - "A5Q3AfterInitMalah": 20117, - "A5Q3AfterInitCain": 20118, - "A5Q3AfterInitLarzuk": 20119, - "A5Q3AfterInitNihlathak": 20120, - "A5Q3AfterInitQualKehk": 20121, - "A5Q3EarlyReturnMalah": 20122, - "A5Q3EarlyReturnCain": 20123, - "A5Q3EarlyReturnLarzuk": 20124, - "A5Q3EarlyReturnNihlathak": 20125, - "A5Q3EarlyReturnQualKehk": 20126, - "A5Q3FoundAnyaMalah": 20127, - "A5Q3FoundAnyaCain": 20128, - "A5Q3FoundAnyaLarzuk": 20129, - "A5Q3FoundAnyaQualKehk": 20130, - "A5Q3FoundAnyaAnya": 20131, - "A5Q3SuccessfulMalah": 20132, - "A5Q3SuccessfulCain": 20133, - "A5Q3SuccessfulLarzuk": 20134, - "A5Q3SuccessfulQualKehk": 20135, - "A5Q3SuccessfulAnya": 20136, - "A5Q4InitAnya": 20137, - "A5Q4AfterInitAnya": 20138, - "A5Q4AfterInitCain": 20139, - "A5Q4AfterInitMalah": 20140, - "A5Q4AfterInitLarzuk": 20141, - "A5Q4AfterInitQualKehk": 20142, - "A5Q4EarlyReturnAnya": 20143, - "A5Q4EarlyReturnCain": 20144, - "A5Q4EarlyReturnLarzuk": 20145, - "A5Q4EarlyReturnMalah": 20146, - "A5Q4EarlyReturnQualKehk": 20147, - "A5Q4SuccessfulAnya": 20148, - "A5Q4SuccessfulCain": 20149, - "A5Q4SuccessfulLarzuk": 20150, - "A5Q4SuccessfulMalah": 20151, - "A5Q4SuccessfulQualKehk": 20152, - "A5Q5InitQualKehk": 20153, - "A5Q5AfterInitQualKehk": 20154, - "A5Q5AfterInitCain": 20155, - "A5Q5AfterInitAnya": 20156, - "A5Q5AfterInitLarzuk": 20157, - "A5Q5AfterInitMalah": 20158, - "A5Q5EarlyReturnQualKehk": 20159, - "A5Q5EarlyReturnCain": 20160, - "A5Q5EarlyReturnAnya": 20161, - "A5Q5EarlyReturnLarzuk": 20162, - "A5Q5EarlyReturnMalah": 20163, - "A5Q5SuccessfulQualKehk": 20164, - "A5Q5SuccessfulCain": 20165, - "A5Q5SuccessfulAnya": 20166, - "A5Q5SuccessfulLarzuk": 20167, - "A5Q5SuccessfulMalah": 20168, - "A5Q6InitAncients": 20169, - "A5Q6EarlyReturnCain": 20170, - "A5Q6EarlyReturnLarzuk": 20171, - "A5Q6EarlyReturnMalah": 20172, - "A5Q6EarlyReturnAnya": 20173, - "A5Q6EarlyReturnQualKehk": 20174, - "A5Q6SuccessfulTyrael": 20175, - "A5Q6SuccessfulAnya": 20176, - "A5Q6SuccessfulCain": 20177, - "A5Q6SuccessfulLarzuk": 20178, - "A5Q6SuccessfulMalah": 20179, - "A5Q6SuccessfulQualKehk": 20180, - "ktr": 20181, - "wrb": 20182, - "ces": 20183, - "clw": 20184, - "btl": 20185, - "skr": 20186, - "9ar": 20187, - "9wb": 20188, - "9xf": 20189, - "9cs": 20190, - "9lw": 20191, - "9tw": 20192, - "9qr": 20193, - "7ar": 20194, - "7wb": 20195, - "7xf": 20196, - "7cs": 20197, - "7lw": 20198, - "7tw": 20199, - "7qr": 20200, - "7ha": 20201, - "7ax": 20202, - "72a": 20203, - "7mp": 20204, - "7wa": 20205, - "7la": 20206, - "7ba": 20207, - "7bt": 20208, - "7ga": 20209, - "7gi": 20210, - "7wn": 20211, - "7yw": 20212, - "7bw": 20213, - "7gw": 20214, - "7cl": 20215, - "7sc": 20216, - "7qs": 20217, - "7ws": 20218, - "7sp": 20219, - "7ma": 20220, - "7mt": 20221, - "7fl": 20222, - "7wh": 20223, - "7m7": 20224, - "7gm": 20225, - "7ss": 20226, - "7sm": 20227, - "7sb": 20228, - "7fc": 20229, - "7cr": 20230, - "7bs": 20231, - "7ls": 20232, - "7wd": 20233, - "72h": 20234, - "7cm": 20235, - "7gs": 20236, - "7b7": 20237, - "7fb": 20238, - "7gd": 20239, - "7dg": 20240, - "7di": 20241, - "7kr": 20242, - "7bl": 20243, - "7tk": 20244, - "7ta": 20245, - "7bk": 20246, - "7b8": 20247, - "7ja": 20248, - "7pi": 20249, - "7s7": 20250, - "7gl": 20251, - "7ts": 20252, - "7sr": 20253, - "7tr": 20254, - "7br": 20255, - "7st": 20256, - "7p7": 20257, - "7o7": 20258, - "7vo": 20259, - "7s8": 20260, - "7pa": 20261, - "7h7": 20262, - "7wc": 20263, - "6ss": 20264, - "6ls": 20265, - "6cs": 20266, - "6bs": 20267, - "6ws": 20268, - "6sb": 20269, - "6hb": 20270, - "6lb": 20271, - "6cb": 20272, - "6s7": 20273, - "6l7": 20274, - "6sw": 20275, - "6lw": 20276, - "6lx": 20277, - "6mx": 20278, - "6hx": 20279, - "6rx": 20280, - "am1": 20292, - "am2": 20293, - "am3": 20294, - "am4": 20295, - "am5": 20296, - "ob6": 20297, - "ob7": 20298, - "ob8": 20299, - "ob9": 20300, - "oba": 20301, - "am6": 20302, - "am7": 20303, - "am8": 20304, - "am9": 20305, - "ama": 20306, - "obb": 20307, - "obc": 20308, - "obd": 20309, - "obe": 20310, - "obf": 20311, - "amb": 20312, - "amc": 20313, - "amd": 20314, - "ame": 20315, - "amf": 20316, - "ba1": 20322, - "ba2": 20323, - "ba3": 20324, - "ba4": 20325, - "ba5": 20326, - "pa1": 20327, - "pa2": 20328, - "pa3": 20329, - "pa4": 20330, - "pa5": 20331, - "ci0": 20337, - "ci1": 20338, - "ci2": 20339, - "ci3": 20340, - "uap": 20341, - "ukp": 20342, - "ulm": 20343, - "uhl": 20344, - "uhm": 20345, - "urn": 20346, - "usk": 20347, - "uui": 20348, - "uea": 20349, - "ula": 20350, - "utu": 20351, - "ung": 20352, - "ucl": 20353, - "uhn": 20354, - "urs": 20355, - "upl": 20356, - "ult": 20357, - "uld": 20358, - "uth": 20359, - "uul": 20360, - "uar": 20361, - "utp": 20362, - "uuc": 20363, - "uml": 20364, - "urg": 20365, - "uit": 20366, - "uow": 20367, - "uts": 20368, - "ulg": 20369, - "uvg": 20370, - "umg": 20371, - "utg": 20372, - "uhg": 20373, - "ulb": 20374, - "uvb": 20375, - "umb": 20376, - "utb": 20377, - "uhb": 20378, - "ulc": 20379, - "uvc": 20380, - "umc": 20381, - "utc": 20382, - "uhc": 20383, - "uh9": 20384, - "ush": 20385, - "upk": 20386, - "dr9": 20387, - "dr7": 20388, - "dr8": 20389, - "dr6": 20390, - "dra": 20391, - "ba6": 20392, - "ba7": 20393, - "ba8": 20394, - "ba9": 20395, - "baa": 20396, - "pa6": 20397, - "pa7": 20398, - "pa8": 20399, - "pa9": 20400, - "paa": 20401, - "ne6": 20402, - "ne7": 20403, - "ne8": 20404, - "ne9": 20405, - "nea": 20406, - "dre": 20407, - "drc": 20408, - "drd": 20409, - "drb": 20410, - "drf": 20411, - "bab": 20412, - "bac": 20413, - "bad": 20414, - "bae": 20415, - "baf": 20416, - "pab": 20417, - "pac": 20418, - "pae": 20419, - "paf": 20420, - "neb": 20421, - "nec": 20422, - "ned": 20423, - "nee": 20424, - "nef": 20425, - "jew": 20433, - "cm1": 20435, - "cm2": 20436, - "cm3": 20437, - "Charmdes": 20438, - "ice": 20439, - "r33": 20440, - "r32": 20441, - "r31": 20442, - "r30": 20443, - "r29": 20444, - "r28": 20445, - "r27": 20446, - "r26": 20447, - "r25": 20448, - "r24": 20449, - "r23": 20450, - "r22": 20451, - "r21": 20452, - "r20": 20453, - "r19": 20454, - "r18": 20455, - "r17": 20456, - "r16": 20457, - "r15": 20458, - "r14": 20459, - "r13": 20460, - "r12": 20461, - "r11": 20462, - "r10": 20463, - "r09": 20464, - "r08": 20465, - "r07": 20466, - "r06": 20467, - "r05": 20468, - "r04": 20469, - "r03": 20470, - "r02": 20471, - "r01": 20472, - "r33L": 20473, - "r32L": 20474, - "r31L": 20475, - "r30L": 20476, - "r29L": 20477, - "r28L": 20478, - "r27L": 20479, - "r26L": 20480, - "r25L": 20481, - "r24L": 20482, - "r23L": 20483, - "r22L": 20484, - "r21L": 20485, - "r20L": 20486, - "r19L": 20487, - "r18L": 20488, - "r17L": 20489, - "r16L": 20490, - "r15L": 20491, - "r14L": 20492, - "r13L": 20493, - "r12L": 20494, - "r11L": 20495, - "r10L": 20496, - "r09L": 20497, - "r08L": 20498, - "r07L": 20499, - "r06L": 20500, - "r05L": 20501, - "r04L": 20502, - "r03L": 20503, - "r02L": 20504, - "r01L": 20505, - "RuneQuote": 20506, - "Runeword1": 20507, - "Runeword2": 20508, - "Runeword3": 20509, - "Runeword4": 20510, - "Runeword5": 20511, - "Runeword6": 20512, - "Runeword7": 20513, - "Runeword8": 20514, - "Runeword9": 20515, - "Runeword10": 20516, - "Runeword11": 20517, - "Runeword12": 20518, - "Runeword13": 20519, - "Runeword14": 20520, - "Runeword15": 20521, - "Runeword16": 20522, - "Runeword17": 20523, - "Runeword18": 20524, - "Runeword19": 20525, - "Runeword20": 20526, - "Runeword21": 20527, - "Runeword22": 20528, - "Runeword23": 20529, - "Runeword24": 20530, - "Runeword25": 20531, - "Runeword26": 20532, - "Runeword27": 20533, - "Runeword28": 20534, - "Runeword29": 20535, - "Runeword30": 20536, - "Runeword31": 20537, - "Runeword32": 20538, - "Runeword33": 20539, - "Runeword34": 20540, - "Runeword35": 20541, - "Runeword36": 20542, - "Runeword37": 20543, - "Runeword38": 20544, - "Runeword39": 20545, - "Runeword40": 20546, - "Runeword41": 20547, - "Runeword42": 20548, - "Runeword43": 20549, - "Runeword44": 20550, - "Runeword45": 20551, - "Runeword46": 20552, - "Runeword47": 20553, - "Runeword48": 20554, - "Runeword49": 20555, - "Runeword50": 20556, - "Runeword51": 20557, - "Runeword52": 20558, - "Runeword53": 20559, - "Runeword54": 20560, - "Runeword55": 20561, - "Runeword56": 20562, - "Runeword57": 20563, - "Runeword58": 20564, - "Runeword59": 20565, - "Runeword60": 20566, - "Runeword61": 20567, - "Runeword62": 20568, - "Runeword63": 20569, - "Runeword64": 20570, - "Runeword65": 20571, - "Runeword66": 20572, - "Runeword67": 20573, - "Runeword68": 20574, - "Runeword69": 20575, - "Runeword70": 20576, - "Runeword71": 20577, - "Runeword72": 20578, - "Runeword73": 20579, - "Runeword74": 20580, - "Runeword75": 20581, - "Runeword76": 20582, - "Runeword77": 20583, - "Runeword78": 20584, - "Runeword79": 20585, - "Runeword81": 20586, - "Runeword82": 20587, - "Runeword83": 20588, - "Runeword84": 20589, - "Runeword85": 20590, - "Runeword86": 20591, - "Runeword87": 20592, - "Runeword88": 20593, - "Runeword89": 20594, - "Runeword90": 20595, - "Runeword91": 20596, - "Runeword92": 20597, - "Runeword93": 20598, - "Runeword94": 20599, - "Runeword95": 20600, - "Runeword96": 20601, - "Runeword97": 20602, - "Runeword98": 20603, - "Runeword99": 20604, - "Runeword100": 20605, - "Runeword101": 20606, - "Runeword102": 20607, - "Runeword103": 20608, - "Runeword104": 20609, - "Runeword105": 20610, - "Runeword106": 20611, - "Runeword107": 20612, - "Runeword108": 20613, - "Runeword109": 20614, - "Runeword110": 20615, - "Runeword111": 20616, - "Runeword112": 20617, - "Runeword113": 20618, - "Runeword114": 20619, - "Runeword115": 20620, - "Runeword116": 20621, - "Runeword117": 20622, - "Runeword118": 20623, - "Runeword119": 20624, - "Runeword120": 20625, - "Runeword121": 20626, - "Runeword122": 20627, - "Runeword123": 20628, - "Runeword124": 20629, - "Runeword125": 20630, - "Runeword126": 20631, - "Runeword127": 20632, - "Runeword128": 20633, - "Runeword129": 20634, - "Runeword130": 20635, - "Runeword131": 20636, - "Runeword132": 20637, - "Runeword133": 20638, - "Runeword134": 20639, - "Runeword135": 20640, - "Runeword136": 20641, - "Runeword137": 20642, - "Runeword138": 20643, - "Runeword139": 20644, - "Runeword140": 20645, - "Runeword141": 20646, - "Runeword142": 20647, - "Runeword143": 20648, - "Runeword144": 20649, - "Runeword145": 20650, - "Runeword146": 20651, - "Runeword147": 20652, - "Runeword148": 20653, - "Runeword149": 20654, - "Runeword150": 20655, - "Runeword151": 20656, - "Runeword152": 20657, - "Runeword153": 20658, - "Runeword154": 20659, - "Runeword155": 20660, - "Runeword156": 20661, - "Runeword157": 20662, - "Runeword158": 20663, - "Runeword159": 20664, - "Runeword160": 20665, - "Runeword161": 20666, - "Runeword162": 20667, - "Runeword163": 20668, - "Runeword164": 20669, - "Runeword165": 20670, - "Runeword166": 20671, - "Runeword167": 20672, - "Runeword168": 20673, - "Runeword169": 20674, - "Runeword170": 20675, - "spe": 20676, - "scz": 20677, - "sol": 20678, - "qll": 20679, - "fng": 20680, - "flg": 20681, - "tal": 20682, - "hrn": 20683, - "eyz": 20684, - "jaw": 20685, - "brz": 20686, - "hrt": 20687, - "Stout": 20688, - "Antimagic": 20689, - "Null": 20690, - "Godly": 20691, - "Ivory": 20692, - "Eburin": 20693, - "Blanched": 20694, - "Stalwart": 20695, - "Burly": 20696, - "Dense": 20697, - "Thin": 20698, - "Compact": 20699, - "Witch-hunter's": 20700, - "Magekiller's": 20701, - "Hierophant's": 20702, - "Shaman's": 20703, - "Pestilent": 20704, - "Toxic": 20705, - "Corosive": 20706, - "Envenomed": 20707, - "Septic": 20708, - "Shocking": 20709, - "Arcing": 20710, - "Buzzing": 20711, - "Static": 20712, - "Scorching": 20713, - "Flaming": 20714, - "Smoking": 20715, - "Smoldering": 20716, - "Ember": 20717, - "Hibernal": 20718, - "Boreal": 20719, - "Shivering": 20720, - "Snowflake": 20721, - "Mnemonic": 20722, - "Visionary": 20723, - "Eagleeye": 20724, - "Hawkeye": 20725, - "Falconeye": 20726, - "Sparroweye": 20727, - "Robineye": 20728, - "Paradox": 20729, - "Shouting": 20730, - "Yelling": 20731, - "Calling": 20732, - "Loud": 20733, - "Trump": 20734, - "Joker's": 20735, - "Jester's": 20736, - "Jack's": 20737, - "Knave's": 20738, - "Paleocene": 20739, - "Eocene": 20740, - "Oligocene": 20741, - "Miocene": 20742, - "Kenshi's": 20743, - "Sensei's": 20744, - "Shogukusha's": 20745, - "Psychic": 20746, - "Mentalist's": 20747, - "Cunning": 20748, - "Trickster's": 20749, - "Entrapping": 20750, - "Gaea's": 20751, - "Terra's": 20752, - "Nature's": 20753, - "Communal": 20754, - "Feral": 20755, - "Spiritual": 20756, - "Keeper's": 20757, - "Caretaker's": 20758, - "Trainer's": 20759, - "Veteran's": 20760, - "Expert's": 20761, - "Furious": 20762, - "Raging": 20763, - "Echoing": 20764, - "Resonant": 20765, - "Sounding": 20766, - "Guardian's": 20767, - "Warder's": 20768, - "Preserver's": 20769, - "Marshal's": 20770, - "Commander's": 20771, - "Captain's": 20772, - "Rose Branded": 20773, - "Hawk Branded": 20774, - "Lion Branded": 20775, - "Golemlord's": 20776, - "Vodoun": 20777, - "Graverobber's": 20778, - "Venomous": 20779, - "Noxious": 20780, - "Fungal": 20781, - "Accursed": 20782, - "Blighting": 20783, - "Hexing": 20784, - "Glacial": 20785, - "Freezing": 20786, - "Chilling": 20787, - "Powered": 20788, - "Charged": 20789, - "Sparking": 20790, - "Volcanic": 20791, - "Blazing": 20792, - "Burning": 20793, - "Lancer's": 20794, - "Spearmaiden's": 20795, - "Harpoonist's": 20796, - "Athlete's": 20797, - "Gymnast's": 20798, - "Acrobat's": 20799, - "Bowyer's": 20800, - "Diamond": 20801, - "Celestial": 20802, - "Elysian": 20803, - "Astral": 20804, - "Unearthly": 20805, - "Arcadian": 20806, - "Jeweler's": 20807, - "Artificer's": 20808, - "Mechanist's": 20809, - "Aureolin": 20810, - "Victorious": 20811, - "Ambergris": 20812, - "Camphor": 20813, - "Lapis Lazuli": 20814, - "Chromatic": 20815, - "Scintillating": 20816, - "Turquoise": 20817, - "Jacinth": 20818, - "Zircon": 20819, - "Bahamut's": 20820, - "Great Wyrm's": 20821, - "Felicitous": 20822, - "Lucky": 20823, - "Wailing": 20824, - "Screaming": 20825, - "Grandmaster's": 20826, - "Master's": 20827, - "Argent": 20828, - "Tin": 20829, - "Nickel": 20830, - "Maroon": 20831, - "Chestnut": 20832, - "Vigorous": 20833, - "Brown": 20834, - "Dun": 20835, - "Realgar": 20836, - "Rusty": 20837, - "Cinnabar": 20838, - "Vermillion": 20839, - "Carmine": 20840, - "Carbuncle": 20841, - "Serrated": 20842, - "Scarlet": 20843, - "Bloody": 20844, - "Sanguinary": 20845, - "Pearl": 20846, - "Divine": 20847, - "Hallowed": 20848, - "Sacred": 20849, - "Pure": 20850, - "Consecrated": 20851, - "Assamic": 20852, - "Frantic": 20853, - "Hellatial": 20854, - "Quixotic": 20855, - "Smiting": 20856, - "Steller": 20857, - "Stinging": 20858, - "Singing": 20859, - "Timeless": 20860, - "Original": 20861, - "Corporal": 20862, - "Lawful": 20863, - "Chaotic": 20864, - "Fierce": 20865, - "Ferocious": 20866, - "Perpetual": 20867, - "Continuous": 20868, - "Laden": 20869, - "Pernicious": 20870, - "Harmful": 20871, - "Evil": 20872, - "Insidious": 20873, - "Malicious": 20874, - "Spiteful": 20875, - "Precocious": 20876, - "Majestic": 20877, - "Sanguine": 20878, - "Monumental": 20879, - "Irresistible": 20880, - "Festering": 20881, - "Musty": 20882, - "Dusty": 20883, - "Decaying": 20884, - "Rotting": 20885, - "Infectious": 20886, - "Foggy": 20887, - "Cloudy": 20888, - "Hazy": 20889, - "Punishing": 20890, - "Obsidian": 20891, - "Royal": 20892, - "Frigid": 20893, - "Moldy": 20894, - "Gaudy": 20895, - "Impecable": 20896, - "Soulless": 20897, - "Heated": 20898, - "Lasting": 20899, - "Scorched": 20900, - "Marred": 20901, - "Lilac": 20902, - "Rose": 20903, - "Shimmering": 20904, - "Wicked": 20906, - "Strange": 20907, - "Repulsive": 20908, - "Reclusive": 20909, - "Rude": 20911, - "Hermetic": 20912, - "Rainbow": 20913, - "Colorful": 20914, - "Stinky": 20915, - "Gritty": 20916, - "of Warming": 20917, - "of Stoicism": 20918, - "of the Dynamo": 20919, - "of Grounding": 20920, - "of Insulation": 20921, - "of Resistance": 20922, - "of Faith": 20923, - "of Fire Quenching": 20924, - "of Amianthus": 20925, - "of Incombustibility": 20926, - "of Coolness": 20927, - "of Anima": 20928, - "of Life Everlasting": 20929, - "of Sunlight": 20930, - "of Frozen Orb": 20931, - "of Hydra Shield": 20932, - "of Chilling Armor": 20933, - "of Blizzard": 20934, - "of Energy Shield": 20935, - "of Thunder Storm": 20936, - "of Meteor": 20937, - "of Glacial Spike": 20938, - "of Teleport Shield": 20939, - "of Chain Lightning": 20940, - "of Enchant": 20941, - "of Fire Wall": 20942, - "of Shiver Armor": 20943, - "of Nova Shield": 20944, - "of Nova": 20945, - "of Fire Ball": 20946, - "of Blaze": 20947, - "of Ice Blast": 20948, - "of Frost Shield": 20949, - "of Telekinesis": 20950, - "of Static Field": 20951, - "of Frozen Armor": 20952, - "of Icebolt": 20953, - "of Charged Shield": 20954, - "of Firebolts": 20955, - "of the Elements": 20956, - "of the Cobra": 20957, - "of the Efreeti": 20958, - "of the Phoenix": 20959, - "of the Yeti": 20960, - "of Grace and Power": 20961, - "of Grace": 20962, - "of Power": 20963, - "of the Elephant": 20964, - "of Memory": 20965, - "of the Kraken1": 20966, - "of Propogation": 20967, - "of Replenishing": 20968, - "of Ages": 20969, - "of Fast Repair": 20970, - "of Self-Repair": 20971, - "of Acceleration": 20972, - "of Traveling": 20973, - "of Virility": 20974, - "of Atlus": 20975, - "of Freedom": 20976, - "of the Lamprey": 20977, - "of Hope": 20978, - "of Spirit": 20979, - "of Vita": 20980, - "of Substinence": 20981, - "of the Whale": 20982, - "of the Squid": 20983, - "of the Colossus1": 20984, - "of Knowledge": 20985, - "of Enlightenment": 20986, - "of Prosperity": 20987, - "of Good Luck": 20988, - "of Luck": 20989, - "of Avarice": 20990, - "of Honor": 20991, - "of Revivification": 20992, - "of Truth": 20993, - "of Daring": 20994, - "of Nirvana": 20995, - "of Envy": 20996, - "of Anthrax": 20997, - "of Bliss": 20998, - "of Joy": 20999, - "of Transcendence": 21000, - "of Wrath": 21001, - "of Ire": 21002, - "of Evisceration": 21003, - "of Butchery": 21004, - "of Ennui": 21005, - "of Storms": 21006, - "of Passion": 21007, - "of Incineration": 21008, - "of Frigidity": 21009, - "of Winter": 21010, - "of the Icicle": 21011, - "of Fervor": 21012, - "of Malice": 21013, - "of Swords": 21014, - "of Razors": 21015, - "of Desire": 21016, - "of the Sirocco": 21017, - "of the Dunes": 21018, - "of Thawing": 21019, - "Of the Choir": 21020, - "Of the Sniper": 21021, - "Of the Stiletto": 21022, - "Of Bile": 21023, - "Of Blitzen": 21024, - "Of Cremation": 21025, - "Of Darkness": 21026, - "Of Disease": 21027, - "Of Remorse": 21028, - "Of Terror": 21029, - "Of the Sky": 21030, - "Of Valhalla": 21031, - "Of Waste": 21032, - "Of Nobility": 21033, - "Of Karma": 21034, - "Of Grounding": 21035, - "Of the River": 21036, - "Of the Lake": 21037, - "Of the Ocean": 21038, - "Of the Bayou": 21039, - "Of the Stream": 21040, - "Of the Lady": 21041, - "Of the Maiden": 21042, - "Of the Virgin": 21043, - "Of the Hag": 21044, - "Of the Witch": 21045, - "Of Judgement": 21046, - "Of Illusion": 21047, - "Of Elusion": 21048, - "Of Combat": 21049, - "Of Attrition": 21050, - "Of Abrasion": 21051, - "Of Erosion": 21052, - "Of Searing": 21053, - "Of Stone": 21054, - "Of Stature": 21055, - "Of Fortication": 21056, - "Of Quickening": 21057, - "Of Dispatch": 21058, - "Of Daring": 21059, - "Of Dread": 21060, - "Of Suffering": 21061, - "Of Doom": 21062, - "Of Vengence": 21063, - "Of Redemption": 21064, - "Of Luck": 21065, - "Of the Avenger": 21066, - "Of the Specter": 21067, - "Of the Ghost": 21068, - "Of the Infantry": 21069, - "Of the Mosquito": 21070, - "Of the Gnat": 21071, - "Of the Fly": 21072, - "Of the Plague": 21073, - "Of Twilight": 21074, - "Of Dusk": 21075, - "Of Dawn": 21076, - "Of the Imbecile": 21077, - "Of the Idiot": 21078, - "Of the Retard": 21079, - "Of the Jujube": 21080, - "Of the Obscenity": 21081, - "Of Quota": 21082, - "Of the Maggot": 21083, - "Of Horror": 21084, - "Of Baddass": 21085, - "Of the Beast": 21086, - "Of Cruelty": 21087, - "Of Badness": 21088, - "Of the Horde": 21089, - "Of the Forest": 21090, - "Of the Lilly": 21091, - "Of the Grassy Gnoll": 21092, - "Of the Stars": 21093, - "Of the Moon": 21094, - "Of Love": 21095, - "Of the Unicorn": 21096, - "Of the Walrus": 21097, - "Of the Earth": 21098, - "Of Vines": 21099, - "Of Honor": 21100, - "Of Tribute": 21101, - "Of Credit": 21102, - "Of Admiration": 21103, - "Of Sweetness": 21104, - "Of Beauty": 21105, - "Of Pilfering": 21106, - "of Damage Amplification": 21107, - "of Hurricane": 21108, - "of Armageddon": 21109, - "of Tornado": 21110, - "of Volcano": 21111, - "of Twister": 21112, - "of Cyclone Armor": 21113, - "of Eruption": 21114, - "of Molten Boulders": 21115, - "of Firestorms": 21116, - "of Battle Command": 21117, - "of War Cry": 21118, - "of Grim Ward": 21119, - "of Battle Orders": 21120, - "of Battle Cry": 21121, - "of Concentration": 21122, - "of Item Finding": 21123, - "of Stunning": 21124, - "of Shouting": 21125, - "of Taunting": 21126, - "of Potion Finding": 21127, - "of Howling": 21128, - "of Fist of the Heavens": 21129, - "of Holy Shield": 21130, - "of Conversion": 21131, - "of Blessed Hammers": 21132, - "of Vengeance": 21133, - "of Charging": 21134, - "of Zeal": 21135, - "of Holy Bolts": 21136, - "of Sacrifice": 21137, - "of Fire Golem Summoning": 21138, - "of Bone Spirits": 21139, - "of Poison Novas": 21140, - "of Lower Resistance": 21141, - "of Iron Golem Creation": 21142, - "of Bone Imprisonment": 21143, - "of Decrepification": 21144, - "of Attraction": 21145, - "of Blood Golem Summoning": 21146, - "of Bone Spears": 21147, - "of Poison Explosion": 21148, - "of Life Tap": 21149, - "of Confusion": 21150, - "of Raise Skeletal Mages": 21151, - "of Bone Walls": 21152, - "of Terror": 21153, - "of Iron Maiden": 21154, - "of Clay Golem Summoning": 21155, - "of Corpse Explosions": 21156, - "of Poison Dagger": 21157, - "of Weaken": 21158, - "of Dim Vision": 21159, - "of Raise Skeletons": 21160, - "of Bone Armor": 21161, - "of Teeth": 21162, - "of Amplify Damage": 21163, - "of Frozen Orbs": 21164, - "of Hydras": 21165, - "of Blizzards": 21166, - "of Meteors": 21167, - "of Glacial Spikes": 21168, - "of Teleportation": 21169, - "of Enchantment": 21170, - "of Fire Walls": 21171, - "of Novas": 21172, - "of Fire Balls": 21173, - "of Blazing": 21174, - "of Ice Blasts": 21175, - "of Frost Novas": 21176, - "of Ice Bolts": 21177, - "of Charged Bolts": 21178, - "of Fire Bolts": 21179, - "of Lightning Fury": 21180, - "of Lightning Spear": 21181, - "of Freezing Arrows": 21182, - "of Fending": 21183, - "of Immolating Arrows": 21184, - "of Plague Javelin": 21185, - "of Charged Spear": 21186, - "of Guided Arrows": 21187, - "of Ice Arrows": 21188, - "of Lightning Javelin": 21189, - "of Impaling Spear": 21190, - "of Slow Missiles": 21191, - "of Exploding Arrows": 21192, - "of Poison Javelin": 21193, - "of Power Spear": 21194, - "of Multiple Shot": 21195, - "of Cold Arrows": 21196, - "of Jabbing": 21197, - "of Inner Sight": 21198, - "of Fire Arrows": 21199, - "of Magic Arrows": 21200, - "Of self-repair": 21201, - "of Dawn": 21202, - "of Inertia": 21203, - "of Joyfulness": 21204, - "ModStre8a": 21205, - "ModStre8b": 21206, - "ModStre8c": 21207, - "ModStre8d": 21208, - "ModStre8e": 21209, - "ModStre8f": 21210, - "ModStre8g": 21211, - "ModStre8h": 21212, - "ModStre8i": 21213, - "ModStre8j": 21214, - "ModStre8k": 21215, - "ModStre8l": 21216, - "ModStre8m": 21217, - "ModStre8n": 21218, - "ModStre8o": 21219, - "ModStre8p": 21220, - "ModStre8q": 21221, - "ModStre8r": 21222, - "ModStre8s": 21223, - "ModStre8t": 21224, - "ModStre8u": 21225, - "ModStre8v": 21226, - "ModStre8w": 21227, - "ModStre8x": 21228, - "ModStre8y": 21229, - "ModStre8z": 21230, - "ModStre9a": 21231, - "ModStre9b": 21232, - "ModStre9c": 21233, - "ModStre9d": 21234, - "ModStre9e": 21235, - "ModStre9f": 21236, - "ModStre9g": 21237, - "ModStre9h": 21238, - "ModStre9i": 21239, - "ModStre9s": 21240, - "ModStre9t": 21241, - "ModStre9u": 21242, - "ModStre9v": 21243, - "ModStre9w": 21244, - "ModStre9x": 21245, - "ModStre9y": 21246, - "ModStre9z": 21247, - "ModStre10a": 21248, - "ModStre10b": 21249, - "ModStre10c": 21250, - "ModStre10d": 21251, - "ModStre10e": 21252, - "ModStre10f": 21253, - "ModStre10g": 21254, - "ModStre10h": 21255, - "ModStre10i": 21256, - "ModStre10j": 21257, - "WeaponDescOrb": 21259, - "ItemexpED": 21260, - "StrGemX1": 21261, - "StrGemX2": 21262, - "StrGemX3": 21263, - "StrGemX4": 21264, - "GemeffectX11": 21265, - "GemeffectX12": 21266, - "GemeffectX13": 21267, - "GemeffectX21": 21268, - "GemeffectX22": 21269, - "GemeffectX23": 21270, - "GemeffectX31": 21271, - "GemeffectX32": 21272, - "GemeffectX33": 21273, - "GemeffectX41": 21274, - "GemeffectX42": 21275, - "GemeffectX43": 21276, - "GemeffectX51": 21277, - "GemeffectX52": 21278, - "GemeffectX53": 21279, - "GemeffectX61": 21280, - "GemeffectX62": 21281, - "GemeffectX63": 21282, - "GemeffectX71": 21283, - "GemeffectX72": 21284, - "GemeffectX73": 21285, - "Coldkill": 21286, - "Butchers Cleaver": 21287, - "Butcher's Pupil": 21288, - "Islestrike": 21289, - "Pompe's Wrath": 21290, - "Guardian Naga": 21291, - "Warlord's Trust": 21292, - "Spellsteel": 21293, - "Stormrider": 21294, - "Boneslayer Blade": 21295, - "The Minotaur": 21296, - "Suicide Branch": 21297, - "Cairn Shard": 21298, - "Arm of King Leoric": 21299, - "Blackhand Key": 21300, - "Dark Clan Crusher": 21301, - "Drulan's Tongue": 21302, - "Zakrum's Hand": 21303, - "The Fetid Sprinkler": 21304, - "Hand of Blessed Light": 21305, - "Fleshrender": 21306, - "Sureshrill Frost": 21307, - "Moonfall": 21308, - "Baezils Vortex": 21309, - "Earthshaker": 21310, - "Bloodtree Stump": 21311, - "The Gavel of Pain": 21312, - "Bloodletter": 21313, - "Coldsteal Eye": 21314, - "Hexfire": 21315, - "Blade of Ali Baba": 21316, - "Riftslash": 21317, - "Headstriker": 21318, - "Plague Bearer": 21319, - "The Atlantien": 21320, - "Crainte Vomir": 21321, - "Bing Sz Wang": 21322, - "The Vile Husk": 21323, - "Cloudcrack": 21324, - "Todesfaelle Flamme": 21325, - "Swordguard": 21326, - "Spineripper": 21327, - "Heart Carver": 21328, - "Blackbog's Sharp": 21329, - "Stormspike": 21330, - "The Impaler": 21331, - "Kelpie Snare": 21332, - "Soulfeast Tine": 21333, - "Hone Sundan": 21334, - "Spire of Honor": 21335, - "The Meat Scraper": 21336, - "Blackleach Blade": 21337, - "Athena's Wrath": 21338, - "Pierre Tombale Couant": 21339, - "Husoldal Evo": 21340, - "Grim's Burning Dead": 21341, - "Ribcracker": 21342, - "Chromatic Ire": 21343, - "Warpspear": 21344, - "Skullcollector": 21345, - "Skystrike": 21346, - "Kuko Shakaku": 21347, - "Endlessshail": 21348, - "Whichwild String": 21349, - "Godstrike Arch": 21350, - "Langer Briser": 21351, - "Pus Spiter": 21352, - "Buriza-Do Kyanon": 21353, - "Vampiregaze": 21354, - "String of Ears": 21355, - "Gorerider": 21356, - "Lavagout": 21357, - "Venom Grip": 21358, - "Visceratuant": 21359, - "Guardian Angle": 21360, - "Shaftstop": 21361, - "Skin of the Vipermagi": 21362, - "Blackhorn": 21363, - "Valkiry Wing": 21364, - "Peasent Crown": 21365, - "Demon Machine": 21366, - "Magewrath": 21367, - "Cliffkiller": 21368, - "Riphook": 21369, - "Razorswitch": 21370, - "Meatscrape": 21371, - "Coldsteel Eye": 21372, - "Pitblood Thirst": 21373, - "Gaya Wand": 21374, - "Ondal's Wisdom": 21375, - "Geronimo's Fury": 21376, - "Charsi's Favor": 21377, - "Doppleganger's Shadow": 21378, - "Deathbit": 21379, - "Warshrike": 21380, - "Gutsiphon": 21381, - "Razoredge": 21382, - "Stonerattle": 21383, - "Marrowgrinder": 21384, - "Gore Ripper": 21385, - "Bush Wacker": 21386, - "Demonlimb": 21387, - "Steelshade": 21388, - "Tomb Reaver": 21389, - "Death's Web": 21390, - "Gaia's Wrath": 21391, - "Khalim's Vengance": 21392, - "Angel's Song": 21393, - "The Reedeemer": 21394, - "Fleshbone": 21395, - "Odium": 21396, - "Blood Comet": 21397, - "Bonehew": 21398, - "Steelrend": 21399, - "Stone Crusher": 21400, - "Bul-Kathos' Might": 21401, - "Arioc's Needle": 21402, - "Shadowdancer": 21403, - "Indiego's Fancy": 21404, - "Aladdin's Eviserator": 21405, - "Tyrael's Mercy": 21406, - "Souldrain": 21407, - "Runemaster": 21408, - "Deathcleaver": 21409, - "Executioner's Justice": 21410, - "Wallace's Tear": 21411, - "Leviathan": 21412, - "The Wanderer's Blade": 21413, - "Qual'Kek's Enforcer": 21414, - "Dawnbringer": 21415, - "Dragontooth": 21416, - "Wisp": 21417, - "Gargoyle's Bite": 21418, - "Lacerator": 21419, - "Mang Song's Lesson": 21420, - "Viperfork": 21421, - "Blood Chalice": 21422, - "El Espiritu": 21423, - "The Long Rod": 21424, - "Demonhorn's Edge": 21425, - "The Ensanguinator": 21426, - "The Reaper's Toll": 21427, - "Spiritkeeper": 21428, - "Hellrack": 21429, - "Alma Negra": 21430, - "Darkforge Spawn": 21431, - "Rockhew": 21432, - "Sankenkur's Resurrection": 21433, - "Erion's Bonehandle": 21434, - "The Archon Magus": 21435, - "Widow maker": 21436, - "Catgut": 21437, - "Ghostflame": 21438, - "Shadowkiller": 21439, - "Bling Bling": 21440, - "Nebucaneezer's Storm": 21441, - "Griffon's Eye": 21442, - "Eaglewind": 21443, - "Windhammer": 21444, - "Thunderstroke": 21445, - "Giantmaimer": 21446, - "Demon's Arch": 21447, - "The Scalper": 21448, - "Bloodmoon": 21449, - "Djinnslayer": 21450, - "Cranebeak": 21451, - "Iansang's Frenzy": 21452, - "Warhound": 21453, - "Gulletwound": 21454, - "Headhunter's Glory": 21455, - "Mordoc's marauder": 21456, - "Talberd's Law": 21457, - "Amodeus's Manipulator": 21458, - "Darksoul": 21459, - "The Black Adder": 21460, - "Earthshifter": 21461, - "Nature's Peace": 21462, - "Horazon's Chalice": 21463, - "Seraph's Hymn": 21464, - "Zakarum's Salvation": 21465, - "Fleshripper": 21466, - "Stonerage": 21467, - "Blood Rain": 21468, - "Horizon's Tornado": 21469, - "Nord's Tenderizer": 21470, - "Wrath of Cain": 21471, - "Siren's call": 21472, - "Jadetalon": 21473, - "Wraithfang": 21474, - "Blademaster": 21475, - "Cerebus": 21476, - "Archangel's Deliverance": 21477, - "Sinblade": 21478, - "Runeslayer": 21479, - "Excalibur": 21480, - "Fuego Del Sol": 21481, - "Stoneraven": 21482, - "El Infierno": 21483, - "Moonrend": 21484, - "Larzuk's Champion": 21485, - "Nightsummon": 21486, - "Bonescapel": 21487, - "Rabbit Slayer": 21488, - "Pagan's Athame": 21489, - "The Swashbuckler": 21490, - "Kang's Virtue": 21491, - "Snaketongue": 21492, - "Lifechoke": 21493, - "Ethereal edge": 21494, - "Palo Grande": 21495, - "Carnageleaver": 21496, - "Ghostleach": 21497, - "Soulreaper": 21498, - "Samual's Caretaker": 21499, - "Hell's Whisper": 21500, - "The Harvester": 21501, - "Raiden's Crutch": 21502, - "The TreeEnt": 21503, - "Stormwillow": 21504, - "Moonshadow": 21505, - "Strongoak": 21506, - "Demonweb": 21507, - "Bloodraven's Charge": 21508, - "Shadefalcon": 21509, - "Robin's Yolk": 21510, - "Glimmershred": 21511, - "Wraithflight": 21512, - "Lestron's Mark": 21513, - "Banshee's Wail": 21514, - "Windstrike": 21515, - "Medusa's Gaze": 21516, - "Titanfist": 21517, - "Hadeshorn": 21518, - "Rockstopper": 21519, - "Stealskull": 21520, - "Darksight Helm": 21521, - "Crown of Thieves": 21522, - "Blackhorn's Face": 21523, - "The Spirit Shroud": 21524, - "Skin of the Flayed One": 21525, - "Ironpelt": 21526, - "Spiritforge": 21527, - "Crow Caw": 21528, - "Duriel's Shell": 21529, - "Skullder's Ire": 21530, - "Toothrow": 21531, - "Atma's Wail": 21532, - "Black Hades": 21533, - "Corpsemourn": 21534, - "Que-hegan's Wisdom": 21535, - "Moser's Blessed Circle": 21536, - "Stormchaser": 21537, - "Tiamat's Rebuke": 21538, - "Gerke's Sanctuary": 21539, - "Radimant's Sphere": 21540, - "Gravepalm": 21541, - "Ghoulhide": 21542, - "Hellmouth": 21543, - "Infernostride": 21544, - "Waterwalk": 21545, - "Silkweave": 21546, - "Wartraveler": 21547, - "Razortail": 21548, - "Gloomstrap": 21549, - "Snowclash": 21550, - "Thudergod's Vigor": 21551, - "Lidless Wall": 21552, - "Lanceguard": 21553, - "Squire's Cover": 21554, - "Boneflame": 21555, - "Steelpillar": 21556, - "Nightwing's Veil": 21557, - "Hightower's Watch": 21558, - "Crown of Ages": 21559, - "Andariel's Visage": 21560, - "Darkfear": 21561, - "Dragonscale": 21562, - "Steel Carapice": 21563, - "Ashrera's Wired Frame": 21564, - "Rainbow Facet": 21565, - "Ravenlore": 21566, - "Boneshade": 21567, - "Nethercrow": 21568, - "Hellwarden's Husk": 21569, - "Flamebellow": 21570, - "Fathom": 21571, - "Wolfhowl": 21572, - "Spirit Ward": 21573, - "Kira's Guardian": 21574, - "Orumus' Robes": 21575, - "Gheed's Fortune": 21576, - "The Vicar": 21577, - "Stormlash": 21578, - "Halaberd's Reign": 21579, - "Parkersor's Calm": 21580, - "Warriv's Warder": 21581, - "Spike Thorn": 21582, - "Dracul's Grasp": 21583, - "Frostwind": 21584, - "Templar's Might": 21585, - "Eschuta's temper": 21620, - "Firelizard's Talons": 21587, - "Sandstorm Trek": 21588, - "Marrowwalk": 21589, - "Heaven's Light": 21590, - "Merman's Speed": 21591, - "Arachnid Mesh": 21592, - "Nosferatu's Coil": 21593, - "Metalgird": 21594, - "Verdugo's Hearty Cord": 21595, - "Sigurd's Staunch": 21596, - "Carrion Wind": 21597, - "Giantskull": 21598, - "Ironward": 21599, - "Gillian's Brazier": 21600, - "Drakeflame": 21601, - "Dust Storm": 21602, - "Skulltred": 21603, - "Alma's Reflection": 21604, - "Drulan's Tounge": 21605, - "Sacred Charge": 21606, - "Bul-Kathos": 21607, - "Saracen's Chance": 21608, - "Highlord's Wrath": 21609, - "Raven Frost": 21610, - "Dwarf Star": 21611, - "Atma's Scarab": 21612, - "Mara's Kaleidoscope": 21613, - "Crescent Moon": 21614, - "The Rising Sun": 21615, - "The Cat's Eye": 21616, - "Bul Katho's Wedding Band": 21617, - "Rings": 21618, - "Metalgrid": 21619, - "Stormshield": 21621, - "Blackoak Shield": 21622, - "Ormus' Robes": 21623, - "Arkaine's Valor": 21624, - "The Gladiator's Bane": 21625, - "Veil of Steel": 21626, - "Harlequin Crest": 21627, - "Lance Guard": 21628, - "Kerke's Sanctuary": 21629, - "Mosers Blessed Circle": 21630, - "Que-Hegan's Wisdon": 21631, - "Guardian Angel": 21632, - "Skin of the Flayerd One": 21633, - "Armor": 21634, - "Windforce": 21635, - "Eaglehorn": 21636, - "Gimmershred": 21637, - "Widowmaker": 21638, - "Stormspire": 21639, - "Naj's Puzzler": 21640, - "Ethereal Edge": 21641, - "Wizardspike": 21642, - "The Grandfather": 21643, - "Doombringer": 21644, - "Tyrael's Might": 21645, - "Lightsabre": 21646, - "The Cranium Basher": 21647, - "Schaefer's Hammer": 21648, - "Baranar's Star": 21649, - "Deaths's Web": 21650, - "Messerschmidt's Reaver": 21651, - "Hellslayer": 21652, - "Endlesshail": 21653, - "The Atlantian": 21654, - "Riftlash": 21655, - "Baezil's Vortex": 21656, - "Zakarum's Hand": 21657, - "Carin Shard": 21658, - "The Minataur": 21659, - "Trang-Oul's Avatar": 21660, - "Trang-Oul's Guise": 21661, - "Trang-Oul's Wing": 21662, - "Trang-Oul's Mask": 21663, - "Trang-Oul's Scales": 21664, - "Trang-Oul's Claws": 21665, - "Trang-Oul's Girth": 21666, - "Natalya's Odium": 21667, - "Natalya's Totem": 21668, - "Natalya's Mark": 21669, - "Natalya's Shadow": 21670, - "Natalya's Soul": 21671, - "Griswold's Legacy": 21672, - "Griswolds's Redemption": 21673, - "Griswold's Honor": 21674, - "Griswold's Heart": 21675, - "Griswold's Valor": 21676, - "Tang's Imperial Robes": 21677, - "Tang's Fore-Fathers": 21678, - "Tang's Rule": 21679, - "Tang's Throne": 21680, - "Tang's Battle Standard": 21681, - "Ogun's Fierce Visage": 21682, - "Ogun's Shadow": 21683, - "Ogun's Lash": 21684, - "Ogun's Vengeance": 21685, - "Bul-Kathos' Warden": 21686, - "Bul-Kathos' Children": 21687, - "Bul-Kathos' Sacred Charge": 21688, - "Bul-Kathos' Tribal Guardian": 21689, - "Bul-Kathos' Custodian": 21690, - "Flowkrad's Howl": 21691, - "Flowkrad's Grin": 21692, - "Flowkrad's Fur": 21693, - "Flowkrad's Paws": 21694, - "Flowkrad's Sinew": 21695, - "Aldur's Watchtower": 21696, - "Aldur's Stony Gaze": 21697, - "Aldur's Deception": 21698, - "Aldur's Guantlet": 21699, - "Aldur's Advance": 21700, - "M'avina's Battle Hymn": 21701, - "M'avina's True Sight": 21702, - "M'avina's Embrace": 21703, - "M'avina's Icy Clutch": 21704, - "M'avina's Tenet": 21705, - "M'avina's Caster": 21706, - "Sazabi's Grand Tribute": 21707, - "Sazabi's Cobalt Redeemer": 21708, - "Sazabi's Ghost Liberator": 21709, - "Sazabi's Mental Sheath": 21710, - "Hwanin's Majesty": 21711, - "Hwanin's Justice": 21712, - "Hwanin's Splendor": 21713, - "Hwanin's Refuge": 21714, - "Hwanin's Cordon": 21715, - "The Disciple": 21716, - "Telling of Beads": 21717, - "Laying of Hands": 21718, - "Rite of Passage": 21719, - "Spiritual Custodian": 21720, - "Credendum": 21721, - "Cow King's Leathers": 21722, - "Cow King's Horns": 21723, - "Cow King's Hide": 21724, - "Cow King's Hoofs": 21725, - "Aragon's Masterpiece": 21726, - "Aragon's Sunfire": 21727, - "Aragon's Icy Stare": 21728, - "Aragon's Storm Cloud": 21729, - "Orphan's Call": 21730, - "Guillaume's Face": 21731, - "Willhelm's Pride": 21732, - "Magnus' Skin": 21733, - "Wihtstan's Guard": 21734, - "Titan's Revenge": 21735, - "Shakabra's Crux": 21736, - "Lycander's Aim": 21737, - "Shadow's Touch": 21738, - "The Prowler": 21739, - "Mortal Crescent": 21740, - "Cutthroat": 21741, - "Sarmichian Justice": 21742, - "Annihilus": 21743, - "Arreat's Face": 21744, - "The Harbinger": 21745, - "Doomseer": 21746, - "Howling Visage": 21747, - "Terra": 21748, - "Syrian": 21749, - "Jalal's Mane": 21750, - "Malignant": 21751, - "Apothecary's Tote": 21752, - "Apocrypha": 21753, - "Foci of Visjerei": 21754, - "Homunculus": 21755, - "Aurora's Guard": 21756, - "Crest of Morn": 21757, - "Herald of Zakarum": 21758, - "Akarat's Protector": 21759, - "Ancient Eye": 21760, - "Globe of Visjerei": 21761, - "The Oculus": 21762, - "Phoenix Egg": 21763, - "Xenos": 21764, - "Nagas": 21765, - "Wyvern's Head": 21766, - "Sightless Veil": 21767, - "ChampionFormatX": 21768, - "EskillKickSing": 21769, - "EskillKickPlur": 21770, - "EskillPetLife": 21771, - "EskillWolfDef": 21772, - "EskillPassiveFeral": 21773, - "Eskillperhit12": 21774, - "Eskillincasehit": 21775, - "Eskillincasemastery": 21776, - "Eskillincaseraven": 21777, - "pad": 21779, - "axf": 21780, - "Eskillkickdamage": 21781, - "ModStre10k": 21782, - "ModStre10L": 21783, - "Class Specific": 21784, - "fana": 21785, - "qsta5q14": 21786, - "qstsa5q42a": 21787, - "qstsa5q31a": 21788, - "qstsa5q21a": 21789, - "qstsa5q43a": 21790, - "qstsa5q62a": 21791, - "qstsa5q61a": 21792, - "act1X": 21797, - "act2X": 21798, - "act3X": 21799, - "act4X": 21800, - "strepilogueX": 21801, - "act5X": 21802, - "strlastcinematic": 21803, - "CfgSay7": 21804, - "0sc": 21805, - "tr2": 21806, - "of Lightning Strike": 21807, - "of Plague Jab": 21808, - "of Charged Strike": 21809, - "of Impaling Strike": 21810, - "of Poison Jab": 21811, - "of Power Strike": 21812, - "of the Colossus": 21813, - "of the Kraken": 21814, - "Tal Rasha's Wrappings": 21815, - "Tal Rasha's Fire-Spun Cloth": 21816, - "Tal Rasha's Adjudication": 21817, - "Tal Rasha's Howling Wind": 21818, - "Tal Rasha's Lidless Eye": 21819, - "Tal Rasha's Horadric Crest": 21820, - "Hwanin's Seal": 21821, - "Heaven's Brethren": 21822, - "Dangoon's Teaching": 21823, - "Ondal's Almighty": 21824, - "Heaven's Taebaek": 21825, - "Haemosu's Adament": 21826, - "Lycander's Flank": 21827, - "Constricting Ring": 21828, - "Ginther's Rift": 21829, - "Naj's Ancient Set": 21830, - "Naj's Light Plate": 21831, - "Naj's Circlet": 21832, - "Sander's Superstition": 21833, - "Sander's Taboo": 21834, - "Sander's Basis": 21835, - "Sander's Derby": 21836, - "Sander's Court Jester": 21837, - "Ghost Liberator": 21838, - "Wilhelm's Pride": 21839, - "Immortal King's Stone Crusher": 21840, - "Immortal King's Pillar": 21841, - "Immortal King's Forge": 21842, - "Immortal King's Detail": 21843, - "Immortal King's Soul Cage \tImmortal King's Soul Cage": 21844, - "Immortal King's Will": 21845, - "Immortal King": 21846, - "Aldur's Gauntlet": 21847, - "Ancient Statue 3": 21848, - "Ancient Statue 2": 21849, - "Ancient Statue 1": 21850, - "Baal Subject 1": 21851, - "Baal Subject 2": 21852, - "Baal Subject 3": 21853, - "Baal Subject 4": 21854, - "Baal Subject 5": 21855, - "Baal Subject 6": 21856, - "Baal Subject 6a": 21857, - "Baal Subject 6b": 21858, - "Baal Crab Clone": 21859, - "Baal Crab to Stairs": 21860, - "BaalColdMage": 21861, - "Baal Subject Mummy": 21862, - "Baal Tentacle": 21863, - "Baals Minion": 21864, - "Hell1": 21865, - "Hell2": 21866, - "Hell3": 21867, - "To Hell1": 21868, - "To Hell2": 21869, - "To Hell3": 21870, - "Lord of Destruction": 21871, - "EskillPerBlade": 21873, - "ExInsertSockets": 21874, - "McAuley's Superstition": 21875, - "McAuley's Taboo": 21876, - "McAuley's Riprap": 21877, - "McAuley's Paragon": 21878, - "McAuley's Folly": 21879, - "qstsa5q62b": 21881, - "of the Plague": 21883, - "Go South": 21884, - "ItemExpansiveChancX": 21885, - "ItemExpansiveChanc1": 21886, - "ItemExpansiveChanc2": 21887, - "ItemExpcharmdesc": 21888, - "StrMercEx12": 21889, - "StrMercEx14": 21890, - "StrMercEx15": 21891, - "Eskillelementaldmg": 21892, - "Playersubtitles29": 21893, - "Playersubtitles30": 21894, - "LeaveCampDru": 21895, - "LeaveCampAss": 21896, - "EnterDOEAss": 21897, - "EnterDOEDru": 21898, - "EnterBurialAss": 21899, - "EnterBurialDru": 21900, - "EnterMonasteryAss": 21901, - "EnterMonasteryDru": 21902, - "EnterForgottenTAss": 21903, - "EnterForgottenTDru": 21904, - "EnterJailAss": 21905, - "EnterJailDru": 21906, - "EnterCatacombsAss": 21907, - "EnterCatacombsDru": 21908, - "CompletingDOEAss": 21909, - "CompletingDOEDru": 21910, - "CompletingBurialAss": 21911, - "CompletingBurialDru": 21912, - "FindingInifusAss": 21913, - "FindingInifusDru": 21914, - "FindingCairnAss": 21915, - "FindingCairnDru": 21916, - "FindingTristramAss": 21917, - "FindingTristramDru": 21918, - "RescueCainAss": 21919, - "RescueCainDru": 21920, - "HoradricMalusAss": 21921, - "HoradricMalusDru": 21922, - "CompletingAndarielAss": 21925, - "CompletingAndarielDru": 21926, - "EnteringRadamentAss": 21927, - "EnteringRadamentDru": 21928, - "CompletingRadamentAss": 21929, - "CompletingRadamentDru": 21930, - "BeginTaintedSunAss": 21931, - "BeginTaintedSunDru": 21932, - "EnteringClawViperAss": 21933, - "EnteringClawViperDru": 21934, - "CompletingTaintedSunAss": 21935, - "CompletingTaintedSunDru": 21936, - "EnteringArcaneAss": 21937, - "EnteringArcaneDru": 21938, - "FindingSummonerAss": 21939, - "FindingSummonerDru": 21940, - "CompletingSummonerAss": 21941, - "CompletingSummonerDru": 21942, - "FindingdecoyTombAss": 21943, - "FindingdecoyTombDru": 21944, - "FindingTrueTombAss": 21945, - "FindingTrueTombDru": 21946, - "CompletingTombAss": 21947, - "CompletingTombDru": 21948, - "FindingLamEsenAss": 21949, - "FindingLamEsenDru": 21950, - "CompletingLamEsenAss": 21952, - "CompletingLamEsenDru": 21953, - "FindingBeneathCityAss": 21954, - "FindingBeneathCityDru": 21955, - "FindingDrainLeverAss": 21956, - "FindingDrainLeverDru": 21957, - "CompletingBeneathCityAss": 21958, - "CompletingBeneathCityDru": 21959, - "CompletingBladeAss": 21960, - "CompletingBladeDru": 21961, - "FindingJadeFigAss": 21962, - "FindingJadeFigDru": 21963, - "FindingTempleAss": 21964, - "FindingTempleDru": 21965, - "CompletingTempleAss": 21966, - "CompletingTempleDru": 21967, - "FindingGuardianTowerAss": 21968, - "FindingGuardianTowerDru": 21969, - "CompletingGuardianTowerAss": 21971, - "FreezingIzualAss": 21973, - "FreezingIzualDru": 21974, - "KillingdDiabloSor": 21975, - "KillingdDiabloBar": 21976, - "KillingdDiabloNec": 21977, - "KillingdDiabloPal": 21978, - "KillingdDiabloAms": 21979, - "KillingdDiabloAss": 21980, - "KillingdDiabloDru": 21981, - "LeavingTownAct5Sor": 21982, - "LeavingTownAct5Bar": 21983, - "LeavingTownAct5Nec": 21984, - "LeavingTownAct5Pal": 21985, - "LeavingTownAct5Ams": 21986, - "LeavingTownAct5Ass": 21987, - "LeavingTownAct5Dru": 21988, - "CompletingStopSiegeSor": 21989, - "CompletingStopSiegeBar": 21990, - "CompletingStopSiegeNec": 21991, - "CompletingStopSiegePal": 21992, - "CompletingStopSiegeAms": 21993, - "CompletingStopSiegeAss": 21994, - "CompletingStopSiegeDru": 21995, - "RescueQual-KehkAct5Sor": 21996, - "RescueQual-KehkAct5Bar": 21997, - "RescueQual-KehkAct5Nec": 21998, - "RescueQual-KehkAct5Pal": 21999, - "RescueQual-KehkAct5Ams": 22000, - "RescueQual-KehkAct5Ass": 22001, - "RescueQual-KehkAct5Dru": 22002, - "EnteringNihlathakAct5Sor": 22003, - "EnteringNihlathakAct5Bar": 22004, - "EnteringNihlathakAct5Nec": 22005, - "EnteringNihlathakAct5Pal": 22006, - "EnteringNihlathakAct5Ams": 22007, - "EnteringNihlathakAct5Ass": 22008, - "EnteringNihlathakAct5Dru": 22009, - "CompletingNihlathakAct5Sor": 22010, - "CompletingNihlathakAct5Bar": 22011, - "CompletingNihlathakAct5Nec": 22012, - "CompletingNihlathakAct5Pal": 22013, - "CompletingNihlathakAct5Ams": 22014, - "CompletingNihlathakAct5Ass": 22015, - "CompletingNihlathakAct5Dru": 22016, - "EnteringTopMountAct5Sor": 22017, - "EnteringTopMountAct5Bar": 22018, - "EnteringTopMountAct5Nec": 22019, - "EnteringTopMountAct5Pal": 22020, - "EnteringTopMountAct5Ams": 22021, - "EnteringTopMountAct5Ass": 22022, - "EnteringTopMountAct5Dru": 22023, - "EnteringWorldstoneAct5Sor": 22024, - "EnteringWorldstoneAct5Bar": 22025, - "EnteringWorldstoneAct5Nec": 22026, - "EnteringWorldstoneAct5Pal": 22027, - "EnteringWorldstoneAct5Ams": 22028, - "EnteringWorldstoneAct5Ass": 22029, - "EnteringWorldstoneAct5Dru": 22030, - "CompletingDefeatBaalAct5Sor": 22031, - "CompletingDefeatBaalAct5Bar": 22032, - "CompletingDefeatBaalAct5Nec": 22033, - "CompletingDefeatBaalAct5Pal": 22034, - "CompletingDefeatBaalAct5Ams": 22035, - "CompletingDefeatBaalAct5Ass": 22036, - "CompletingDefeatBaalAct5Dru": 22037, - "Skillname222": 22038, - "Skillsd222": 22039, - "Skillld222": 22040, - "Skillan222": 22041, - "Skillname223": 22046, - "Skillsd223": 22047, - "Skillld223": 22048, - "Skillan223": 22049, - "Skillname225": 22050, - "Skillsd225": 22051, - "Skillld225": 22052, - "Skillan225": 22053, - "Skillname226": 22054, - "Skillsd226": 22055, - "Skillld226": 22056, - "Skillan226": 22057, - "Skillname227": 22058, - "Skillsd227": 22059, - "Skillld227": 22060, - "Skillan227": 22061, - "Skillname228": 22062, - "Skillsd228": 22063, - "Skillld228": 22064, - "Skillan228": 22065, - "Skillname229": 22066, - "Skillsd229": 22067, - "Skillld229": 22068, - "Skillan229": 22069, - "Skillname230": 22070, - "Skillsd230": 22071, - "Skillld230": 22072, - "Skillan230": 22073, - "Skillname231": 22074, - "Skillsd231": 22075, - "Skillld231": 22076, - "Skillan231": 22077, - "Skillname232": 22078, - "Skillsd232": 22079, - "Skillld232": 22080, - "Skillan232": 22081, - "Skillname233": 22082, - "Skillsd233": 22083, - "Skillld233": 22084, - "Skillan233": 22085, - "Skillname234": 22086, - "Skillsd234": 22087, - "Skillld234": 22088, - "Skillan234": 22089, - "Skillname235": 22090, - "Skillsd235": 22091, - "Skillld235": 22092, - "Skillan235": 22093, - "Skillname236": 22094, - "Skillsd236": 22095, - "Skillld236": 22096, - "Skillan236": 22097, - "Skillname237": 22098, - "Skillsd237": 22099, - "Skillld237": 22100, - "Skillan237": 22101, - "Skillname238": 22102, - "Skillsd238": 22103, - "Skillld238": 22104, - "Skillan238": 22105, - "Skillname239": 22106, - "Skillsd239": 22107, - "Skillld239": 22108, - "Skillan239": 22109, - "Skillname240": 22110, - "Skillsd240": 22111, - "Skillld240": 22112, - "Skillan240": 22113, - "Skillname241": 22114, - "Skillsd241": 22115, - "Skillld241": 22116, - "Skillan241": 22117, - "Skillname242": 22118, - "Skillsd242": 22119, - "Skillld242": 22120, - "Skillan242": 22121, - "Skillname243": 22122, - "Skillsd243": 22123, - "Skillld243": 22124, - "Skillan243": 22125, - "Skillname244": 22126, - "Skillsd244": 22127, - "Skillld244": 22128, - "Skillan244": 22129, - "Skillname245": 22130, - "Skillsd245": 22131, - "Skillld245": 22132, - "Skillan245": 22133, - "Skillname246": 22134, - "Skillsd246": 22135, - "Skillld246": 22136, - "Skillan246": 22137, - "Skillname247": 22138, - "Skillsd247": 22139, - "Skillld247": 22140, - "Skillan247": 22141, - "Skillname248": 22142, - "Skillsd248": 22143, - "Skillld248": 22144, - "Skillan248": 22145, - "Skillname249": 22146, - "Skillsd249": 22147, - "Skillld249": 22148, - "Skillan249": 22149, - "Skillname250": 22150, - "Skillsd250": 22151, - "Skillld250": 22152, - "Skillan250": 22153, - "Skillname251": 22154, - "Skillsd251": 22155, - "Skillld251": 22156, - "Skillan251": 22157, - "Skillname252": 22158, - "Skillsd252": 22159, - "Skillld252": 22160, - "Skillan252": 22161, - "Skillname253": 22162, - "Skillsd253": 22163, - "Skillld253": 22164, - "Skillan253": 22165, - "Skillname254": 22166, - "Skillsd254": 22167, - "Skillld254": 22168, - "Skillan254": 22169, - "Skillname255": 22170, - "Skillsd255": 22171, - "Skillld255": 22172, - "Skillan255": 22173, - "Skillname256": 22174, - "Skillsd256": 22175, - "Skillld256": 22176, - "Skillan256": 22177, - "Skillname257": 22178, - "Skillsd257": 22179, - "Skillld257": 22180, - "Skillan257": 22181, - "Skillname258": 22182, - "Skillsd258": 22183, - "Skillld258": 22184, - "Skillan258": 22185, - "Skillname259": 22186, - "Skillsd259": 22187, - "Skillld259": 22188, - "Skillan259": 22189, - "Skillname260": 22190, - "Skillsd260": 22191, - "Skillld260": 22192, - "Skillan260": 22193, - "Skillname261": 22194, - "Skillsd261": 22195, - "Skillld261": 22196, - "Skillan261": 22197, - "Skillname262": 22198, - "Skillsd262": 22199, - "Skillld262": 22200, - "Skillan262": 22201, - "Skillname263": 22202, - "Skillsd263": 22203, - "Skillld263": 22204, - "Skillan263": 22205, - "Skillname264": 22206, - "Skillsd264": 22207, - "Skillld264": 22208, - "Skillan264": 22209, - "Skillname265": 22210, - "Skillsd265": 22211, - "Skillld265": 22212, - "Skillan265": 22213, - "Skillname266": 22214, - "Skillsd266": 22215, - "Skillld266": 22216, - "Skillan266": 22217, - "Skillname267": 22218, - "Skillsd267": 22219, - "Skillld267": 22220, - "Skillan267": 22221, - "Skillname268": 22222, - "Skillsd268": 22223, - "Skillld268": 22224, - "Skillan268": 22225, - "Skillname269": 22226, - "Skillsd269": 22227, - "Skillld269": 22228, - "Skillan269": 22229, - "Skillname270": 22230, - "Skillsd270": 22231, - "Skillld270": 22232, - "Skillan270": 22233, - "Skillname271": 22234, - "Skillsd271": 22235, - "Skillld271": 22236, - "Skillan271": 22237, - "Skillname272": 22238, - "Skillsd272": 22239, - "Skillld272": 22240, - "Skillan272": 22241, - "Skillname273": 22242, - "Skillsd273": 22243, - "Skillld273": 22244, - "Skillan273": 22245, - "Skillname274": 22246, - "Skillsd274": 22247, - "Skillld274": 22248, - "Skillan274": 22249, - "Skillname275": 22250, - "Skillsd275": 22251, - "Skillld275": 22252, - "Skillan275": 22253, - "Skillname276": 22254, - "Skillsd276": 22255, - "Skillld276": 22256, - "Skillan276": 22257, - "Skillname277": 22258, - "Skillsd277": 22259, - "Skillld277": 22260, - "Skillan277": 22261, - "Skillname278": 22262, - "Skillsd278": 22263, - "Skillld278": 22264, - "Skillan278": 22265, - "Skillname279": 22266, - "Skillsd279": 22267, - "Skillld279": 22268, - "Skillan279": 22269, - "Skillname280": 22270, - "Skillsd280": 22271, - "Skillld280": 22272, - "Skillan280": 22273, - "Skillname281": 22274, - "Skillsd281": 22275, - "Skillld281": 22276, - "Skillan281": 22277, - "ESkillPerKick": 22286, - "EskillLifeSteal": 22287, - "Eskillchancetostun": 22288, - "Eskillchancetoafflict": 22289, - "Eskillpowerup1": 22290, - "Eskillpowerup2": 22291, - "Eskillpowerup3": 22292, - "Eskillpowerupadd": 22293, - "Eskillsinishup": 22294, - "Eskillpudlife": 22295, - "Eskillpudmana": 22296, - "Eskillpudburning": 22297, - "Eskillpuddgmper": 22298, - "Eskilllowerresis": 22299, - "Eskilltomeleeattacks": 22300, - "EskillManaSteal": 22301, - "Eskillferalpets": 22302, - "Eskillpercentatt": 22303, - "Eskillpercentlif": 22304, - "Eskillpercentdmg": 22305, - "Eskillfinishmove": 22306, - "Eskillmanarecov": 22307, - "Eskillphoenix1": 22308, - "Eskillphoenix2": 22309, - "Eskillphoenix3": 22310, - "Eskillthunder1": 22311, - "Eskillthunder2": 22312, - "Eskillthunder3": 22313, - "Eskillfistsoffire1": 22314, - "Eskillfistsoffire2": 22315, - "Eskillfistsoffire3": 22316, - "Eskillbladesofice1": 22317, - "Eskillbladesofice2": 22318, - "Eskillbladesofice3": 22319, - "strUI5": 22320, - "strUI6": 22321, - "strUI7": 22322, - "strUI8": 22323, - "strUI9": 22324, - "strUI10": 22325, - "strUI11": 22326, - "strUI12": 22327, - "strUI13": 22328, - "strUI14": 22329, - "UIFenirsui": 22330, - "UiRescuedBarUI": 22331, - "UiShadowUI": 22332, - "StrUI18": 22333, - "Spike Generator": 22334, - "Charged Bolt Sentry": 22335, - "Lightning Sentry": 22336, - "Blade Creeper": 22337, - "Invis Pet": 22338, - "Druid Hawk": 22339, - "Druid Wolf": 22340, - "Druid Totem": 22341, - "Druid Fenris": 22342, - "Druid Spirit Wolf": 22343, - "Druid Bear": 22344, - "Druid Plague Poppy": 22345, - "Druid Cycle of Life": 22346, - "Vine Creature": 22347, - "Eagleexp": 22348, - "Wolf": 22349, - "Bear": 22350, - "Siege Door": 22351, - "Siege Beast": 22358, - "Hell Temptress": 22389, - "Blood Temptress": 22390, - "Blood Witch": 22394, - "Hell Witch": 22395, - "CatapultN": 22411, - "CatapultS": 22412, - "CatapultE": 22413, - "CatapultW": 22414, - "Frozen Horror1": 22415, - "Frozen Horror2": 22416, - "Frozen Horror3": 22417, - "Frozen Horror4": 22418, - "Frozen Horror5": 22419, - "Blood Lord1": 22420, - "Blood Lord2": 22421, - "Blood Lord3": 22422, - "Blood Lord4": 22423, - "Blood Lord5": 22424, - "Catapult Spotter N": 22425, - "Catapult Spotter S": 22426, - "Catapult Spotter E": 22427, - "Catapult Spotter W": 22428, - "Catapult Spotter Siege": 22429, - "CatapultSiege": 22430, - "Barricade Wall Right": 22431, - "Barricade Wall Left": 22432, - "Barricade Door": 22433, - "Barricade Tower": 22434, - "Siege Boss": 22435, // shenk the overseer - "Evil hut": 22436, - "Death Mauler1": 22437, - "Death Mauler2": 22438, - "Death Mauler3": 22439, - "Death Mauler4": 22440, - "Death Mauler5": 22441, - "SnowYeti1": 22442, - "SnowYeti2": 22443, - "SnowYeti3": 22444, - "SnowYeti4": 22445, - "Baal Throne": 22446, - "Baal Crab": 22447, - "Baal Taunt": 22448, - "Putrid Defiler1": 22449, - "Putrid Defiler2": 22450, - "Putrid Defiler3": 22451, - "Putrid Defiler4": 22452, - "Putrid Defiler5": 22453, - "Pain Worm1": 22454, - "Pain Worm2": 22455, - "Pain Worm3": 22456, - "Pain Worm4": 22457, - "Pain Worm5": 22458, - "WolfRider5": 22459, - "WolfRider4": 22460, - "WolfRider3": 22461, - "WolfRider2": 22462, - "WolfRider1": 22463, - "Oak Sage": 22464, - "Heart of Wolverine": 22465, - "Spirit of Barbs": 22466, - "Shadow Warrior": 22467, - "Death Sentry": 22468, - "Inferno Sentry": 22469, - "Shadow Master": 22470, - "Wake of Destruction": 22471, - "Ghostly": 22472, - "Fanatic": 22473, - "Possessed": 22474, - "Berserk": 22475, - "Larzuk": 22476, - "Drehya": 22477, - "Malah": 22478, - "Nihlathak Town": 22479, - "Qual-Kehk": 22480, - "Act 5 Townguard": 22481, - "Act 5 Combatant": 22482, - "Nihlathak": 22483, - "POW": 22484, - "Moe": 22485, - "Curly": 22486, - "Larry": 22487, - "Ancient Barbarian 3": 22488, - "Ancient Barbarian 2": 22489, - "Ancient Barbarian 1": 22490, - "Blaze Ripper": 22491, - "Magma Torquer": 22492, - "Sharp Tooth Sayer": 22493, - "Vinvear Molech": 22494, - "Anodized Elite": 22495, - "Snapchip Shatter": 22496, - "Pindleskin": 22497, - "Threash Socket": 22498, - "Eyeback Unleashed": 22499, - "Megaflow Rectifier": 22500, // eldritch the rectifier - "Dac Farren": 22501, - "Bonesaw Breaker": 22502, - "Axe Dweller": 22503, - "Frozenstein": 22504, - "strDruidOnly": 22505, - "strAssassinOnly": 22506, - "strAmazonOnly": 22507, - "strBarbarianOnly": 22508, - "StrSklTree26": 22509, - "StrSklTree27": 22510, - "StrSklTree28": 22511, - "StrSklTree29": 22512, - "StrSklTree30": 22513, - "StrSklTree31": 22514, - "StrSklTree32": 22515, - "StrSklTree33": 22516, - "StrSklTree34": 22517, - "chestr": 22520, - "barrel wilderness": 22521, - "woodchestL": 22522, - "burialchestL": 22523, - "burialchestR": 22524, - "ChestL": 22527, - "ChestSL": 22528, - "ChestSR": 22529, - "woodchestR": 22530, - "chestR": 22531, - "burningbodies": 22532, - "burningpit": 22533, - "tribal flag": 22534, - "flag widlerness": 22535, - "eflg": 22536, - "chan": 22537, - "jar": 22538, - "jar2": 22539, - "jar3": 22540, - "swingingheads": 22541, - "pole": 22542, - "animatedskullsandrocks": 22543, - "hellgate": 22544, - "gate": 22545, - "banner1": 22546, - "banner2": 22547, - "mrpole": 22548, - "pene": 22549, - "debris": 22550, - "woodchest2R": 22551, - "woodchest2L": 22552, - "object1": 22553, - "magic shrine2": 22554, - "torch2": 22555, - "torch1": 22556, - "tomb3": 22557, - "tomb2": 22558, - "tomb1": 22559, - "ttor": 22560, - "icecave_torch2": 22561, - "icecave_torch1": 22562, - "clientsmoke": 22563, - "deadbarbarian": 22564, - "deadbarbarian18": 22565, - "uncle f#%* comedy central(c)\tMoe": 22566, - "cagedwussie1": 22567, - "icecaveshrine2": 22568, - "icecavejar4": 22569, - "icecavejar3": 22570, - "icecavejar2": 22571, - "icecavejar1": 22572, - "evilurn": 22573, - "secret object": 22574, - "Altar": 22575, - "Ldeathpole": 22576, - "deathpole": 22577, - "explodingchest": 22578, - "banner 2": 22579, - "banner 1": 22580, - "pileofskullsandrocks": 22581, - "animated skulland rockpile": 22582, - "jar1": 22583, - "etorch2": 22584, - "ettr": 22585, - "ecfra": 22586, - "etorch1": 22587, - "healthshrine": 22588, - "explodingbarrel": 22589, - "flag wilderness": 22590, - "object": 22591, - "Shrine2wilderness": 22592, - "Shrine3wilderness": 22593, - "pyox": 22594, - "ptox": 22595, - "Siege Control": 22596, - "mrjar": 22597, - "object2": 22598, - "mrbox": 22599, - "tomb3L": 22600, - "tomb2L": 22601, - "tomb1L": 22602, - "red light": 22603, - "groundtombL": 22604, - "groundtomb": 22605, - "deadperson": 22606, - "candles": 22607, - "sbub": 22608, - "ubub": 22609, - "deadperson2": 22610, - "Prison Door": 22611, - "ancientsaltar": 22612, - "hiddenstash": 22613, - "eweaponrackL": 22614, - "eweaponrackR": 22615, - "earmorstandL": 22616, - "earmorstandR": 22617, - "qstsa5q1": 22618, - "qsta5q11": 22619, - "qsta5q12": 22620, - "qsta5q13": 22621, - "qstsa5q2": 22622, - "qstsa5q21": 22623, - "qstsa5q22": 22624, - "qstsa5q23": 22625, - "qstsa5q24": 22626, - "qstsa5q3": 22627, - "qstsa5q31": 22628, - "qstsa5q32": 22629, - "qstsa5q33": 22630, - "qstsa5q34": 22631, - "qstsa5q35": 22632, - "qstsa5q4": 22633, - "qstsa5q41": 22634, - "qstsa5q42": 22635, - "qstsa5q43": 22636, - "qstsa5q5": 22637, - "qstsa5q51": 22638, - "qstsa5q52": 22639, - "qstsa5q53": 22640, - "qstsa5q6": 22641, - "qstsa5q61": 22642, - "qstsa5q62": 22643, - "qstsa5q63": 22644, - "qstsa5q64": 22645, - "Harrogath": 22646, - "Bloody Foothills": 22647, - "Rigid Highlands": 22648, - "Arreat Plateau": 22649, - "Crystalized Cavern Level 1": 22650, - "Cellar of Pity": 22651, - "Crystalized Cavern Level 2": 22652, - "Echo Chamber": 22653, - "Tundra Wastelands": 22654, - "Glacial Caves Level 1": 22655, - "Glacial Caves Level 2": 22656, - "Rocky Summit": 22657, - "Nihlathaks Temple": 22658, - "Halls of Anguish": 22659, - "Halls of Death's Calling": 22660, - "Halls of Tormented Insanity": 22661, - "Halls of Vaught": 22662, - "The Worldstone Keep Level 1": 22663, - "The Worldstone Keep Level 2": 22664, - "The Worldstone Keep Level 3": 22665, - "The Worldstone Chamber": 22666, - "Throne of Destruction": 22667, - "To Harrogath": 22668, - "To The Bloody Foothills": 22669, - "To The Rigid Highlands": 22670, - "To The Arreat Plateau": 22671, - "To The Crystalized Cavern Level 1": 22672, - "To The Cellar of Pity": 22673, - "To The Crystalized Cavern Level 2": 22674, - "To The Echo Chamber": 22675, - "To The Tundra Wastelands": 22676, - "To The Glacier Caves Level 1": 22677, - "To The Glacier Caves Level 2": 22678, - "To The Rocky Summit": 22679, - "To Nihlathaks Temple": 22680, - "To The Halls of Anguish": 22681, - "To The Halls of Death's Calling": 22682, - "To The Halls of Tormented Insanity": 22683, - "To The Halls of Vaught": 22684, - "To The Worldstone Keep Level 1": 22685, - "To The Worldstone Keep Level 2": 22686, - "To The Worldstone Keep Level 3": 22687, - "To The Worldstone Chamber": 22688, - "To The Throne of Destruction": 22689, - "hireiconinfo1": 22690, - "hireiconinfo2": 22691, - "hiredismiss": 22692, - "hiredismisshire": 22693, - "hirerehire": 22694, - "hireresurrect": 22695, - "hireresurrect2": 22696, - "hirechat1": 22697, - "hirechat2": 22698, - "hirechat3": 22699, - "hirepraise1": 22700, - "hirepraise2": 22701, - "hiredanger1": 22702, - "hiredanger2": 22703, - "hiredanger3": 22704, - "hiredanger4": 22705, - "hiredanger5": 22706, - "hiredanger6": 22707, - "hirefeelstronger2": 22708, - "hirehelp1": 22709, - "hirehelp2": 22710, - "hirehelp3": 22711, - "hirehelp4": 22712, - "hiregreets1": 22713, - "hiregreets2": 22714, - "hiregreets3": 22715, - "hiregreets4": 22716, - "CfgSkill9": 22717, - "CfgSkill10": 22718, - "CfgSkill11": 22719, - "CfgSkill12": 22720, - "CfgSkill13": 22721, - "CfgSkill14": 22722, - "CfgSkill15": 22723, - "CfgSkill16": 22724, - "CfgToggleminimap": 22725, - "Cfgswapweapons": 22726, - "Cfghireling": 22727, - "MiniPanelHireinv": 22728, - "MiniPanelHire": 22729, - "Go North": 22737, - "Travel To Harrogath": 22738, - "Rename Instruct": 22747, - "Addsocketsui": 22748, - "Personalizeui": 22749, - "Addsocketsui2": 22750, - "MercX101": 22751, - "MercX102": 22752, - "MercX103": 22753, - "MercX104": 22754, - "MercX105": 22755, - "MercX106": 22756, - "MercX107": 22757, - "MercX108": 22758, - "MercX109": 22759, - "MercX110": 22760, - "MercX111": 22761, - "MercX112": 22762, - "MercX113": 22763, - "MercX114": 22764, - "MercX115": 22765, - "MercX116": 22766, - "MercX117": 22767, - "MercX118": 22768, - "MercX119": 22769, - "MercX120": 22770, - "MercX121": 22771, - "MercX122": 22772, - "MercX123": 22773, - "MercX124": 22774, - "MercX125": 22775, - "MercX126": 22776, - "MercX127": 22777, - "MercX128": 22778, - "MercX129": 22779, - "MercX130": 22780, - "MercX131": 22781, - "MercX132": 22782, - "MercX133": 22783, - "MercX134": 22784, - "MercX135": 22785, - "MercX136": 22786, - "MercX137": 22787, - "MercX138": 22788, - "MercX139": 22789, - "MercX140": 22790, - "MercX141": 22791, - "MercX142": 22792, - "MercX143": 22793, - "MercX144": 22794, - "MercX145": 22795, - "MercX146": 22796, - "MercX147": 22797, - "MercX148": 22798, - "MercX149": 22799, - "MercX150": 22800, - "MercX151": 22801, - "MercX152": 22802, - "MercX153": 22803, - "MercX154": 22804, - "MercX155": 22805, - "MercX156": 22806, - "MercX157": 22807, - "MercX158": 22808, - "MercX159": 22809, - "MercX160": 22810, - "MercX161": 22811, - "MercX162": 22812, - "MercX163": 22813, - "MercX164": 22814, - "MercX165": 22815, - "MercX166": 22816, - "MercX167": 22817 - }; + let LocaleStringID = { + "WarrivAct1IntroGossip1": 0, + "WarrivAct1IntroPalGossip1": 1, + "WarrivGossip1": 2, + "WarrivGossip2": 3, + "WarrivGossip3": 4, + "WarrivGossip4": 5, + "WarrivGossip5": 6, + "WarrivGossip6": 7, + "WarrivGossip7": 8, + "WarrivGossip8": 9, + "WarrivGossip9": 10, + "AkaraIntroGossip1": 11, + "AkaraIntroSorGossip1": 12, + "AkaraGossip1": 13, + "AkaraGossip2": 14, + "AkaraGossip3": 15, + "AkaraGossip4": 16, + "AkaraGossip5": 17, + "AkaraGossip6": 18, + "AkaraGossip7": 19, + "AkaraGossip8": 20, + "AkaraGossip9": 21, + "AkaraGossip10": 22, + "AkaraGossip11": 23, + "KashyaIntroGossip1": 24, + "KashyaIntroAmaGossip1": 25, + "KashyaGossip1": 26, + "KashyaGossip2": 27, + "KashyaGossip3": 28, + "KashyaGossip4": 29, + "KashyaGossip5": 30, + "KashyaGossip6": 31, + "KashyaGossip7": 32, + "KashyaGossip8": 33, + "KashyaGossip9": 34, + "KashyaGossip10": 35, + "CharsiIntroGossip1": 36, + "CharsiIntroBarGossip1": 37, + "CharsiGossip1": 38, + "CharsiGossip2": 39, + "CharsiGossip3": 40, + "CharsiGossip4": 41, + "CharsiGossip5": 42, + "CharsiGossip6": 43, + "CharsiGossip7": 44, + "GheedIntroGossip1": 45, + "GheedIntroNecGossip1": 46, + "GheedGossip1": 47, + "GheedGossip2": 48, + "GheedGossip3": 49, + "GheedGossip4": 50, + "GheedGossip5": 51, + "GheedGossip6": 52, + "GheedGossip7": 53, + "CainGossip1": 54, + "CainGossip2": 55, + "CainGossip3": 56, + "CainGossip4": 57, + "CainGossip5": 58, + "RogueSignpostGossip1": 59, + "RogueSignpostGossip2": 60, + "RogueSignpostGossip3": 61, + "RogueSignpostGossip4": 62, + "RogueSignpostGossip5": 63, + "A1Q1InitAkara": 64, + "A1Q1AfterInitAkara": 65, + "A1Q1AfterInitKashya": 66, + "A1Q1AfterInitCharsiMain": 67, + "A1Q1AfterInitCharsiAlt": 68, + "A1Q1AfterInitGheed": 69, + "A1Q1AfterInitWarriv": 70, + "A1Q1EarlyReturnAkara": 71, + "A1Q1EarlyReturnKashya": 72, + "A1Q1EarlyReturnCharsi": 73, + "A1Q1EarlyReturnGheed": 74, + "A1Q1EarlyReturnWarriv": 75, + "A1Q1SuccessfulAkara": 76, + "A1Q1SuccessfulKashya": 77, + "A1Q1SuccessfulCharsi": 78, + "A1Q1SuccessfulGheed": 79, + "A1Q1SuccessfulWarriv": 80, + "A1Q2InitKashya": 81, + "A1Q2AfterInitKashya": 82, + "A1Q2AfterInitCharsi": 83, + "A1Q2AfterInitGheed": 84, + "A1Q2AfterInitAkara": 85, + "A1Q2AfterInitWarriv": 86, + "A1Q2EarlyReturnKashya": 87, + "A1Q2EarlyReturnAkara": 88, + "A1Q2EarlyReturnCharsi": 89, + "A1Q2EarlyReturnGheed": 90, + "A1Q2EarlyReturnWarriv": 91, + "A1Q2SuccessfulKashya": 92, + "A1Q2SuccessfulAkara": 93, + "A1Q2SuccessfulCharsi": 94, + "A1Q2SuccessfulGheed": 95, + "A1Q2SuccessfulWarriv": 96, + "A1Q4InitAkara": 97, + "A1Q4AfterInitScrollKashya": 98, + "A1Q4AfterInitScrollAkara": 99, + "A1Q4AfterInitScrollCharsi": 100, + "A1Q4AfterInitScrollWarriv": 101, + "A1Q4AfterInitScrollGheed": 102, + "A1Q4InstructionsCharsi": 103, + "A1Q4EarlyReturnSAkara": 104, + "A1Q4EarlyReturnSKashya": 105, + "A1Q4EarlyReturnSGheed": 106, + "A1Q4EarlyReturnSWarriv": 107, + "A1Q4SuccessfulScrollKashya": 108, + "A1Q4SuccessfulScrollCharsi": 109, + "A1Q4SuccessfulScrollGheed": 110, + "A1Q4SuccessfulScrollWarriv": 111, + "A1Q4InstructionsAkara": 112, + "A1Q4EarlyReturnKashya": 113, + "A1Q4EarlyReturnCharsi": 114, + "A1Q4EarlyReturnGheed": 115, + "A1Q4EarlyReturnWarriv": 116, + "A1Q4EarlyReturnAkara": 117, + "A1Q4QuestSuccessfulAkara": 118, + "A1Q4QuestSuccessfulKashya": 119, + "A1Q4QuestSuccessfulGheed": 120, + "A1Q4QuestSuccessfulCharsi": 121, + "A1Q4QuestSuccessfulWarriv": 122, + "A1Q4QuestSuccessfulCain": 123, + "A1Q4RescuedByHeroCain": 124, + "A1Q4RescuedByRoguesCain": 125, + "A1Q4TragedyOfTristramCain": 126, + "A1Q5InitQuestTome": 127, + "A1Q5AfterInitGheed": 128, + "A1Q5AfterInitCharsi": 129, + "A1Q5AfterInitAkara": 130, + "A1Q5AfterInitCain": 131, + "A1Q5AfterInitWarriv": 132, + "A1Q5AfterInitKashya": 133, + "A1Q5EarlyReturnKashya": 134, + "A1Q5EarlyReturnCain": 135, + "A1Q5EarlyReturnWarriv": 136, + "A1Q5EarlyReturnCharsi": 137, + "A1Q5EarlyReturnAkara": 138, + "A1Q5EarlyReturnGheed": 139, + "A1Q5SuccessfulKashya": 140, + "A1Q5SuccessfulWarriv": 141, + "A1Q5SuccessfulGheed": 142, + "A1Q5SuccessfulAkara": 143, + "A1Q5SuccessfulCharsi": 144, + "A1Q5SuccessfulCain": 145, + "A1Q3InitCharsi": 146, + "A1Q3AfterInitCain": 147, + "A1Q3AfterInitAkara": 148, + "A1Q3AfterInitKashya": 149, + "A1Q3AfterInitCharsi": 150, + "A1Q3AfterInitGheed": 151, + "A1Q3AfterInitGheedAlt": 152, + "A1Q3AfterInitWarriv": 153, + "A1Q3EarlyReturnCain": 154, + "A1Q3EarlyReturnAkara": 155, + "A1Q3EarlyReturnKashya": 156, + "A1Q3EarlyReturnCharsi": 157, + "A1Q3EarlyReturnGheed": 158, + "A1Q3EarlyReturnWarriv": 159, + "A1Q3SuccessfulCain": 160, + "A1Q3SuccessfulAkara": 161, + "A1Q3SuccessfulKashya": 162, + "A1Q3SuccessfulCharsi": 163, + "A1Q3SuccessfulGheed": 164, + "A1Q3SuccessfulWarriv": 165, + "A1Q6InitCain": 166, + "A1Q6AfterInitCain": 167, + "A1Q6AfterInitAkara": 168, + "A1Q6AfterInitCharsi": 169, + "A1Q6AfterInitGheed": 170, + "A1Q6AfterInitWarriv": 171, + "A1Q6AfterInitKashya": 172, + "A1Q6EarlyReturnCain": 173, + "A1Q6EarlyReturnAkara": 174, + "A1Q6EarlyReturnGheed": 175, + "A1Q6EarlyReturnCharsi": 176, + "A1Q6EarlyReturnWarriv": 177, + "A1Q6EarlyReturn2Kashya": 178, + "A1Q6SuccessfulAkara": 179, + "A1Q6SuccessfulCharsi": 180, + "A1Q6SuccessfulKashya": 181, + "A1Q6SuccessfulGheed": 182, + "A1Q6SuccessfulWarriv": 183, + "A1Q6SuccessfulCain": 184, + "PalaceGuardGossip1": 185, + "PalaceGuardGossip2": 186, + "PalaceGuardGossip3": 187, + "PalaceGuardGossip4": 188, + "PalaceGuardGossip5": 189, + "GriezIntroGossip1": 190, + "GriezGossip1": 191, + "GriezGossip2": 192, + "GriezGossip3": 193, + "GriezGossip4": 194, + "GriezGossip5": 195, + "GriezGossip6": 196, + "GriezGossip7": 197, + "GriezGossip8": 198, + "GriezGossip9": 199, + "GriezGossip10": 200, + "GriezGossip11": 201, + "GriezGossip12": 202, + "ElzixIntroGossip1": 203, + "ElzixIntroNecGossip1": 204, + "ElzixGossip1": 205, + "ElzixGossip2": 206, + "ElzixGossip3": 207, + "ElzixGossip4": 208, + "ElzixGossip5": 209, + "ElzixGossip6": 210, + "ElzixGossip7": 211, + "ElzixGossip8": 212, + "ElzixGossip9": 213, + "ElzixGossip10": 214, + "WarrivAct2IntroGossip1": 215, + "WarrivAct2Gossip1": 216, + "WarrivAct2Gossip2": 217, + "WarrivAct2Gossip3": 218, + "WarrivAct2Gossip4": 219, + "WarrivAct2Gossip5": 220, + "AtmaIntroGossip1": 221, + "AtmaGossip1": 222, + "AtmaGossip2": 223, + "AtmaGossip3": 224, + "AtmaGossip4": 225, + "AtmaGossip5": 226, + "AtmaGossip6": 227, + "AtmaGossip7": 228, + "AtmaGossip8": 229, + "GeglashIntroGossip1": 230, + "GeglashIntroBarGossip1": 231, + "GeglashGossip1": 232, + "GeglashGossip2": 233, + "GeglashGossip3": 234, + "GeglashGossip4": 235, + "GeglashGossip5": 236, + "GeglashGossip6": 237, + "GeglashGossip7": 238, + "GeglashGossip8": 239, + "GeglashGossip9": 240, + "MeshifIntroGossip1": 241, + "MeshifIntroAmaGossip1": 242, + "MeshifGossip1": 243, + "MeshifGossip2": 244, + "MeshifGossip3": 245, + "MeshifGossip4": 246, + "MeshifGossip5": 247, + "MeshifGossip6": 248, + "MeshifGossip7": 249, + "MeshifGossip8": 250, + "MeshifGossip9": 251, + "MeshifGossip10": 252, + "JerhynActIntroGossip1": 253, + "JerhynActIntroMoreGossip1": 254, + "JerhynIntroGossip1": 255, + "JerhynGossip1": 256, + "JerhynGossip2": 257, + "JerhynGossip3": 258, + "JerhynGossip4": 259, + "JerhynGossip5": 260, + "JerhynGossip6": 261, + "JerhynGossip7": 262, + "FaraIntroGossip1": 263, + "FaraIntroPalGossip1": 264, + "FaraGossip1": 265, + "FaraGossip2": 266, + "FaraGossip3": 267, + "FaraGossip4": 268, + "FaraGossip5": 269, + "FaraGossip6": 270, + "FaraGossip7": 271, + "FaraGossip8": 272, + "FaraGossip9": 273, + "LysanderIntroGossip1": 274, + "LysanderGossip1": 275, + "LysanderGossip2": 276, + "LysanderGossip3": 277, + "LysanderGossip4": 278, + "LysanderGossip5": 279, + "LysanderGossip6": 280, + "LysanderGossip7": 281, + "LysanderGossip8": 282, + "LysanderGossip9": 283, + "LysanderGossip10": 284, + "DrognanIntroGossip1": 285, + "DrognanIntroSorGossip1": 286, + "DrognanGossip1": 287, + "DrognanGossip2": 288, + "DrognanGossip3": 289, + "DrognanGossip4": 290, + "DrognanGossip5": 291, + "DrognanGossip6": 292, + "DrognanGossip7": 293, + "DrognanGossip8": 294, + "DrognanGossip9": 295, + "DrognanGossip10": 296, + "CainAct2Gossip1": 297, + "CainAct2Gossip2": 298, + "CainAct2Gossip3": 299, + "CainAct2Gossip4": 300, + "CainAct2Gossip5": 301, + "TyraelGossip1": 302, + "Desert2GuardGossip1": 303, + "A2Q1InitAtma": 304, + "A2Q1AfterInitGreiz": 305, + "A2Q1AfterInitElzix": 306, + "A2Q1AfterInitWarrivAct2": 307, + "A2Q1AfterInitGeglash": 308, + "A2Q1AfterInitFara": 309, + "A2Q1AfterInitAtma": 310, + "A2Q1AfterInitMeshif": 311, + "A2Q1AfterInitDrognan": 312, + "A2Q1AfterInitLysander": 313, + "A2Q1AfterInitCain": 314, + "A2Q1EarlyReturnWarrivAct2": 315, + "A2Q1EarlyReturnMeshif": 316, + "A2Q1EarlyReturnAtma": 317, + "A2Q1EarlyReturnGreiz": 318, + "A2Q1EarlyReturnGeglash": 319, + "A2Q1EarlyReturnElzix": 320, + "A2Q1EarlyReturnLysander": 321, + "A2Q1EarlyReturnDrognan": 322, + "A2Q1EarlyReturnFara": 323, + "A2Q1EarlyReturnCain": 324, + "A2Q1SuccessfulGreiz": 325, + "A2Q1SuccessfulDrognan": 326, + "A2Q1SuccessfulLysander": 327, + "A2Q1SuccessfulMeshif": 328, + "A2Q1SuccessfulGeglash": 329, + "A2Q1SuccessfulElzix": 330, + "A2Q1SuccessfulWarrivAct2": 331, + "A2Q1SuccessfulFara": 332, + "A2Q1SuccessfulCain": 333, + "A2Q1SuccessfulAtma": 334, + "A2Q2EarlyReturnScrollCain": 335, + "A2Q2EarlyReturnCapCain": 336, + "A2Q2EarlyReturnStaveCain": 337, + "A2Q2EarlyReturnCubeCain": 338, + "A2Q2SuccessfulStaffCain": 339, + "A2Q3AfterInitJerhyn": 340, + "A2Q3AfterInitGreiz": 341, + "A2Q3AfterInitElzix": 342, + "A2Q3AfterInitWarrivAct2": 343, + "A2Q3AfterInitAtma": 344, + "A2Q3AfterInitGeglash": 345, + "A2Q3AfterInitFara": 346, + "A2Q3AfterInitLysander": 347, + "A2Q3AfterInitDrognan": 348, + "A2Q3AfterInitMeshif": 349, + "A2Q3AfterInitCain": 350, + "A2Q3EarlyReturnJerhyn": 351, + "A2Q3EarlyReturnGreiz": 352, + "A2Q3EarlyReturnWarrivAct2": 353, + "A2Q3EarlyReturnGeglash": 354, + "A2Q3EarlyReturnMeshif": 355, + "A2Q3EarlyReturnFara": 356, + "A2Q3EarlyReturnLysander": 357, + "A2Q3EarlyReturnDrognan": 358, + "A2Q3EarlyReturnElzix": 359, + "A2Q3EarlyReturnCain": 360, + "A2Q3EarlyReturnAtma": 361, + "A2Q3SuccessfulJerhyn": 362, + "A2Q3SuccessfulGreiz": 363, + "A2Q3SuccessfulElzix": 364, + "A2Q3SuccessfulGeglash": 365, + "A2Q3SuccessfulWarrivAct2": 366, + "A2Q3SuccessfulMeshif": 367, + "A2Q3SuccessfulAtma": 368, + "A2Q3SuccessfulFara": 369, + "A2Q3SuccessfulLysander": 370, + "A2Q3SuccessfulDrognan": 371, + "A2Q3SuccessfulCain": 372, + "A2Q4InitDrognan": 373, + "A2Q4AfterInitFara": 374, + "A2Q4AfterInitGreiz": 375, + "A2Q4AfterInitElzix": 376, + "A2Q4AfterInitJerhyn": 377, + "A2Q4AfterInitCain": 378, + "A2Q4AfterInitGeglash": 379, + "A2Q4AfterInitAtma": 380, + "A2Q4AfterInitWarrivAct2": 381, + "A2Q4AfterInitLysander": 382, + "A2Q4AfterInitDrognan": 383, + "A2Q4AfterInitMeshif": 384, + "A2Q4EarlyReturnElzix": 385, + "A2Q4EarlyReturnJerhyn": 386, + "A2Q4EarlyReturnGreiz": 387, + "A2Q4EarlyReturnDrognan": 388, + "A2Q4EarlyReturnLysander": 389, + "A2Q4EarlyReturnFara": 390, + "A2Q4EarlyReturnGeglash": 391, + "A2Q4EarlyReturnMeshif": 392, + "A2Q4EarlyReturnAtma": 393, + "A2Q4EarlyReturnWarrivAct2": 394, + "A2Q4EarlyReturnCain": 395, + "A2Q4SuccessfulNarrator": 396, + "A2Q4SuccessfulGriez": 397, + "A2Q4SuccessfulJerhyn": 398, + "A2Q4SuccessfulDrognan": 399, + "A2Q4SuccessfulElzix": 400, + "A2Q4SuccessfulGeglash": 401, + "A2Q4SuccessfulMeshif": 402, + "A2Q4SuccessfulWarrivAct2": 403, + "A2Q4SuccessfulFara": 404, + "A2Q4SuccessfulLysander": 405, + "A2Q4SuccessfulAtma": 406, + "A2Q4SuccessfulCain": 407, + "A2Q5EarlyReturnGreiz": 408, + "A2Q5EarlyReturnJerhyn": 409, + "A2Q5EarlyReturnDrognan": 410, + "A2Q5EarlyReturnLysander": 411, + "A2Q5EarlyReturnMeshif": 412, + "A2Q5EarlyReturnWarrivAct2": 413, + "A2Q5EarlyReturnAtma": 414, + "A2Q5EarlyReturnGeglash": 415, + "A2Q5EarlyReturnFara": 416, + "A2Q5EarlyReturnElzix": 417, + "A2Q5EarlyReturnCain": 418, + "A2Q5SuccessfulGreiz": 419, + "A2Q5SuccessfulGeglash": 420, + "A2Q5SuccessfulJerhyn": 421, + "A2Q5SuccessfulDrognan": 422, + "A2Q5SuccessfulElzix": 423, + "A2Q5SuccessfulWarrivAct2": 424, + "A2Q5SuccessfulMeshif": 425, + "A2Q5SuccessfulLysander": 426, + "A2Q5SuccessfulAtma": 427, + "A2Q5SuccessfulFara": 428, + "A2Q5SuccessfulCain": 429, + "A2Q6InitJerhyn": 430, + "A2Q6AfterInitJerhyn": 431, + "A2Q6AfterInitElzix": 432, + "A2Q6AfterInitWarrivAct2": 433, + "A2Q6AfterInitAtma": 434, + "A2Q6AfterInitGeglash": 435, + "A2Q6AfterInitMeshif": 436, + "A2Q6AfterInitFara": 437, + "A2Q6AfterInitLysander": 438, + "A2Q6AfterInitDrognan": 439, + "A2Q6AfterInitCain": 440, + "A2Q6AfterInitGreiz": 441, + "A2Q6SuccessfulJerhyn": 442, + "A2Q6SuccessfulElzix": 443, + "A2Q6SuccessfulLysander": 444, + "A2Q6SuccessfulAtma": 445, + "A2Q6SuccessfulWarrivAct2": 446, + "A2Q6SuccessfulFara": 447, + "A2Q6SuccessfulGeglash": 448, + "A2Q6SuccessfulDrognan": 449, + "A2Q6SuccessfulMeshif": 450, + "A2Q6SuccessfulGreiz": 451, + "A2Q6SuccessfulCain": 452, + "NatalyaIntroGossip1": 453, + "NatalyaGossip1": 454, + "NatalyaGossip2": 455, + "NatalyaGossip3": 456, + "NatalyaGossip4": 457, + "CainAct3IntroGossip1": 458, + "CainAct3Gossip1": 459, + "CainAct3Gossip2": 460, + "CainAct3Gossip3": 461, + "CainAct3Gossip4": 462, + "CainAct3Gossip5": 463, + "CainAct3Gossip6": 464, + "HratliActIntroGossip1": 465, + "HratliActIntroSorGossip1": 466, + "HratliGossip1": 467, + "HratliGossip2": 468, + "HratliGossip3": 469, + "HratliGossip4": 470, + "HratliGossip5": 471, + "HratliGossip6": 472, + "HratliGossip7": 473, + "HratliGossip8": 474, + "HratliGossip9": 475, + "HratliGossip10": 476, + "HratliGossip11": 477, + "MeshifAct3IntroGossip1": 478, + "MeshifAct3IntroBarGossip1": 479, + "MeshifAct3Gossip1": 480, + "MeshifAct3Gossip2": 481, + "MeshifAct3Gossip3": 482, + "MeshifAct3Gossip4": 483, + "MeshifAct3Gossip5": 484, + "MeshifAct3Gossip6": 485, + "MeshifAct3Gossip7": 486, + "MeshifAct3Gossip8": 487, + "MeshifAct3Gossip9": 488, + "MeshifAct3Gossip10": 489, + "AshearaIntroGossip1": 490, + "AshearaIntroAmaGossip1": 491, + "AshearaGossip1": 492, + "AshearaGossip2": 493, + "AshearaGossip3": 494, + "AshearaGossip4": 495, + "AshearaGossip5": 496, + "AshearaGossip6": 497, + "AshearaGossip7": 498, + "AshearaGossip8": 499, + "AshearaGossip9": 500, + "AlkorIntroGossip1": 501, + "AlkorIntroNecGossip1": 502, + "AlkorGossip1": 503, + "AlkorGossip2": 504, + "AlkorGossip3": 505, + "AlkorGossip4": 506, + "AlkorGossip5": 507, + "AlkorGossip6": 508, + "AlkorGossip7": 509, + "AlkorGossip8": 510, + "AlkorGossip9": 511, + "AlkorGossip10": 512, + "AlkorGossip11": 513, + "OrmusIntroGossip1": 514, + "OrmusIntroPalGossip1": 515, + "OrmusGossip1": 516, + "OrmusGossip2": 517, + "OrmusGossip3": 518, + "OrmusGossip4": 519, + "OrmusGossip5": 520, + "OrmusGossip6": 521, + "OrmusGossip7": 522, + "OrmusGossip8": 523, + "OrmusGossip9": 524, + "OrmusGossip10": 525, + "OrmusGossip11": 526, + "A3Q4Init1CainAct3": 527, + "A3Q4Init1Asheara": 528, + "A3Q4Init2MeshifAct3": 529, + "A3Q4Init2Natalya": 530, + "A3Q4Init3CainAct3": 531, + "A3Q4Init3Hratli": 532, + "A3Q4Init3Asheara": 533, + "A3Q4AfterInitAlkor": 534, + "A3Q4AfterInitOrmus": 535, + "A3Q4AfterInitHratli": 536, + "A3Q4AfterInitNatalya": 537, + "A3Q4SuccessfulAlkor": 538, + "A3Q4SuccessfulMeshifAct3": 539, + "A3Q4SuccessfulCainAct3": 540, + "A3Q4SuccessfulOrmus": 541, + "A3Q4SuccessfulNatalya": 542, + "A3Q2InitCain": 543, + "A3Q2EarlyReturnHeartCain": 544, + "A3Q2EarlyReturnEyeCain": 545, + "A3Q2EarlyReturnBrainCain": 546, + "A3Q2EarlyReturnFlailCain": 547, + "A3Q2SuccessfulCain": 548, + "A3Q1InitAlkor": 549, + "A3Q1AfterInitAlkor": 550, + "A3Q1AfterInitOrmus": 551, + "A3Q1AfterInitMeshifAct3": 552, + "A3Q1AfterInitAsheara": 553, + "A3Q1AfterInitHratli": 554, + "A3Q1AfterInitCainAct3": 555, + "A3Q1AfterInitNatalya": 556, + "A3Q1EarlyReturnAlkor": 557, + "A3Q1EarlyReturnOrmus": 558, + "A3Q1EarlyReturnMeshifAct3": 559, + "A3Q1EarlyReturnAsheara": 560, + "A3Q1EarlyReturnHratli": 561, + "A3Q1EarlyReturnCainAct3": 562, + "A3Q1EarlyReturnNatalya": 563, + "A3Q1SuccessfulAlkor": 564, + "A3Q1SuccessfulOrmus": 565, + "A3Q1SuccessfulMeshifAct3": 566, + "A3Q1SuccessfulAsheara": 567, + "A3Q1SuccessfulHratli": 568, + "A3Q1SuccessfulCainAct3": 569, + "A3Q1SuccessfulNatalya": 570, + "A3Q3InitHratli": 571, + "A3Q3AfterInitAlkor": 572, + "A3Q3AfterInitOrmus": 573, + "A3Q3AfterInitMeshifAct3": 574, + "A3Q3AfterInitAsheara": 575, + "A3Q3AfterInitHratli": 576, + "A3Q3AfterInitCainAct3": 577, + "A3Q3AfterInitNatalya": 578, + "A3Q3EarlyReturnAlkor": 579, + "A3Q3EarlyReturnOrmus": 580, + "A3Q3EarlyReturnMeshifAct3": 581, + "A3Q3EarlyReturnAsheara": 582, + "A3Q3EarlyReturnHratli": 583, + "A3Q3EarlyReturnCainAct3": 584, + "A3Q3EarlyReturnNatalya": 585, + "A3Q3SuccessfulAlkor": 586, + "A3Q3SuccessfulOrmus": 587, + "A3Q3SuccessfulMeshifAct3": 588, + "A3Q3SuccessfulAsheara": 589, + "A3Q3SuccessfulHratli": 590, + "A3Q3SuccessfulCainAct3": 591, + "A3Q3SuccessfulNatalya": 592, + "A3Q3RewardOrmus": 593, + "A3Q5InitOrmus": 594, + "A3Q5AfterInitAlkor": 595, + "A3Q5AfterInitAlkorVA": 596, + "A3Q5AfterInitOrmus": 597, + "A3Q5AfterInitOrmusVA": 598, + "A3Q5AfterInitMeshifAct3": 599, + "A3Q5AfterInitMeshifAct3VA": 600, + "A3Q5AfterInitAsheara": 601, + "A3Q5AfterInitAshearaVA": 602, + "A3Q5AfterInitHratli": 603, + "A3Q5AfterInitHratliVA": 604, + "A3Q5AfterInitCainAct3": 605, + "A3Q5AfterInitCainAct3VA": 606, + "A3Q5AfterInitNatalya": 607, + "A3Q5AfterInitNatalyaVA": 608, + "A3Q5EarlyReturnAlkor": 609, + "A3Q5EarlyReturnAlkorVA": 610, + "A3Q5EarlyReturnOrmus": 611, + "A3Q5EarlyReturnMeshifAct3": 612, + "A3Q5EarlyReturnMeshifAct3VA": 613, + "A3Q5EarlyReturnAsheara": 614, + "A3Q5EarlyReturnAshearaVA": 615, + "A3Q5EarlyReturnHratli": 616, + "A3Q5EarlyReturnHratliVA": 617, + "A3Q5EarlyReturnCainAct3": 618, + "A3Q5EarlyReturnNatalya": 619, + "A3Q5EarlyReturnNatalyaVA": 620, + "A3Q5SuccessfulAlkor": 621, + "A3Q5SuccessfulOrmus": 622, + "A3Q5SuccessfulMeshifAct3": 623, + "A3Q5SuccessfulAsheara": 624, + "A3Q5SuccessfulHratli": 625, + "A3Q5SuccessfulCainAct3": 626, + "A3Q5SuccessfulNatalya": 627, + "A3Q6InitOrmus": 628, + "A3Q6AfterInitAlkor": 629, + "A3Q6AfterInitAlkorVA": 630, + "A3Q6AfterInitOrmus": 631, + "A3Q6AfterInitOrmusVA": 632, + "A3Q6AfterInitMeshifAct3": 633, + "A3Q6AfterInitMeshifAct3VA": 634, + "A3Q6AfterInitAsheara": 635, + "A3Q6AfterInitAshearaVA": 636, + "A3Q6AfterInitHratli": 637, + "A3Q6AfterInitHratliVA": 638, + "A3Q6AfterInitCainAct3": 639, + "A3Q6AfterInitCainAct3VA": 640, + "A3Q6AfterInitNatalya": 641, + "A3Q6AfterInitNatalyaVA": 642, + "A3Q6EarlyReturnAlkor": 643, + "A3Q6EarlyReturnAlkorVA": 644, + "A3Q6EarlyReturnOrmus": 645, + "A3Q6EarlyReturnOrmusVA": 646, + "A3Q6EarlyReturnMeshifAct3": 647, + "A3Q6EarlyReturnMeshifAct3VA": 648, + "A3Q6EarlyReturnAsheara": 649, + "A3Q6EarlyReturnAshearaVA": 650, + "A3Q6EarlyReturnHratli": 651, + "A3Q6EarlyReturnHratliVA": 652, + "A3Q6EarlyReturnCainAct3": 653, + "A3Q6EarlyReturnCainAct3VA": 654, + "A3Q6EarlyReturnNatalya": 655, + "A3Q6EarlyReturnNatalyaVA": 656, + "A3Q6SuccessfulAlkor": 657, + "A3Q6SuccessfulOrmus": 658, + "A3Q6SuccessfulMeshifAct3": 659, + "A3Q6SuccessfulAsheara": 660, + "A3Q6SuccessfulHratli": 661, + "A3Q6SuccessfulCainAct3": 662, + "A3Q6SuccessfulNatalya": 663, + "TyraelActIntroGossip1": 664, + "TyraelAct4Gossip1": 665, + "CainAct4IntroGossip1": 666, + "CainAct4Gossip1": 667, + "HellsAngelGossip1": 668, + "HellsAngelGossip2": 669, + "A4Q1InitTyrael": 670, + "A4Q1AfterInitTyrael": 671, + "A4Q1AfterInitCain": 672, + "A4Q1EarlyReturnTyrael": 673, + "A4Q1EarlyReturnCain": 674, + "A4Q1SuccessfulIzual": 675, + "A4Q1SuccessfulTyrael": 676, + "A4Q1SuccessfulCain": 677, + "A4Q3InitHasStoneCain": 678, + "A4Q3InitNoStoneCain": 679, + "A4Q3SuccessfulCain": 680, + "A4Q2InitTyrael": 681, + "A4Q2AfterInitCain": 682, + "A4Q2AfterInitTyrael": 683, + "A4Q2SuccessfulTyrael": 684, + "A4Q2SuccessfulCain": 685, + "D2bnetHelp50": 686, + "D2bnetHelp": 687, + "D2bnetHelp2a": 688, + "D2bnetHelpa": 689, + "D2bnetHelp1": 690, + "D2bnetHelp2": 691, + "D2bnetHelp3": 692, + "D2bnetHelp4": 693, + "D2bnetHelp5": 694, + "D2bnetHelp5a": 695, + "D2bnetHelp6": 696, + "D2bnetHelp7": 697, + "D2bnetHelp8": 698, + "D2bnetHelp9": 699, + "D2bnetHelp10": 700, + "D2bnetHelp11": 701, + "D2bnetHelp36": 702, + "D2bnetHelp36a": 703, + "D2bnetHelp37": 704, + "D2bnetHelp37a": 705, + "D2bnetHelp38": 706, + "D2bnetHelp39": 707, + "D2bnetHelp40": 708, + "D2bnetHelp41": 709, + "D2bnetHelp42": 710, + "D2bnetHelp42a": 711, + "D2bnetHelp43": 712, + "D2bnetHelp44": 713, + "D2bnetHelp44ab": 714, + "D2bnetHelp44a": 715, + "D2bnetHelp45": 716, + "D2bnetHelp45b": 717, + "D2bnetHelp45a": 718, + "D2bnetHel46": 719, + "D2bnetHelp46a": 720, + "D2bnetHelp47": 721, + "D2bnetHelp48": 722, + "D2bnetHelp49": 723, + "D2bnetHelp12": 724, + "D2bnetHelp12c": 725, + "D2bnetHelp12b": 726, + "D2bnetHelp12a": 727, + "D2bnetHelp13": 728, + "D2bnetHelp13b": 729, + "D2bnetHelp13a": 730, + "D2bnetHelp14": 731, + "D2bnetHelp14a": 732, + "D2bnetHelp15": 733, + "D2bnetHelp15b": 734, + "D2bnetHelp15a": 735, + "D2bnetHelp16": 736, + "D2bnetHelp16b": 737, + "D2bnetHelp16a": 738, + "D2bnetHelp17": 739, + "D2bnetHelp17a": 740, + "D2bnetHelp18": 741, + "D2bnetHelp18a": 742, + "D2bnetHelp19": 743, + "D2bnetHelp19a": 744, + "D2bnetHelp20": 745, + "D2bnetHelp20a": 746, + "D2bnetHelp21": 747, + "D2bnetHelp21a": 748, + "D2bnetHelp22": 749, + "D2bnetHelp22a": 750, + "D2bnetHelp23": 751, + "D2bnetHelp23a": 752, + "D2bnetHelp24": 753, + "D2bnetHelp24a": 754, + "D2bnetHelp25": 755, + "D2bnetHelp25a": 756, + "D2bnetHelp26": 757, + "D2bnetHelp26b": 758, + "D2bnetHelp26a": 759, + "D2bnetHelp27": 760, + "D2bnetHelp27a": 761, + "D2bnetHelp28": 762, + "D2bnetHelp28a": 763, + "D2bnetHelp29": 764, + "D2bnetHelp29a": 765, + "D2bnetHelp30": 766, + "D2bnetHelp30a": 767, + "D2bnetHelp31": 768, + "D2bnetHelp31a": 769, + "D2bnetHelp32": 770, + "D2bnetHelp32a": 771, + "D2bnetHelp33": 772, + "D2bnetHelp34": 773, + "D2bnetHelp35": 774, + "D2bnetHelp51": 775, + "D2bnetHelp52": 776, + "D2bnetHelp53": 777, + "D2bnetHelp54": 778, + "D2bnetHelp55": 779, + "D2bnetHelp56": 780, + "D2bnetHelp57": 781, + "D2bnetHelp58": 782, + "D2bnetHelp59": 783, + "D2bnetHelp60": 784, + "D2bnetHelp61": 785, + "D2bnetHelp62": 786, + "D2bnetHelp63": 787, + "Moo Moo Farm": 788, + "Chaos Sanctum": 789, + "The Pandemonium Fortress": 790, + "River of Flame": 791, + "Outer Steppes": 792, + "Plains of Despair": 793, + "City of the Damned": 794, + "Durance of Hate Level 3": 795, + "Durance of Hate Level 2": 796, + "Durance of Hate Level 1": 797, + "Disused Reliquary": 798, + "Ruined Fane": 799, + "Forgotten Temple": 800, + "Forgotten Reliquary": 801, + "Disused Fane": 802, + "Ruined Temple": 803, + "Flayer Dungeon Level 3": 804, + "Flayer Dungeon Level 2": 805, + "Flayer Dungeon Level 1": 806, + "Swampy Pit Level 3": 807, + "Swampy Pit Level 2": 808, + "Swampy Pit Level 1": 809, + "Spider Cave": 810, + "Spider Cavern": 811, + "Travincal": 812, + "Kurast Causeway": 813, + "Upper Kurast": 814, + "Kurast Bazaar": 815, + "Lower Kurast": 816, + "Flayer Jungle": 817, + "Great Marsh": 818, + "Spider Forest": 819, + "Kurast Docktown": 820, + "Durance of Hate": 821, + "Flayer Dungeon": 822, + "Swampy Pit": 823, + "Arcane Sanctuary": 824, + "Duriel's Lair": 825, + "Tal Rasha's Tomb": 826, + "Ancient Tunnels": 827, + "Maggot Lair Level 3": 828, + "Maggot Lair Level 2": 829, + "Maggot Lair Level 1": 830, + "Claw Viper Temple Level 2": 831, + "Halls of the Dead Level 3": 832, + "Stony Tomb Level 2": 833, + "Claw Viper Temple Level 1": 834, + "Halls of the Dead Level 2": 835, + "Halls of the Dead Level 1": 836, + "Stony Tomb Level 1": 837, + "Palace Cellar Level 3": 838, + "Palace Cellar Level 2": 839, + "Palace Cellar Level 1 \tPalace Cellar Level 1": 840, + "Harem Level 2": 841, + "Harem Level 1": 842, + "Sewers Level 3": 843, + "Sewers Level 2": 844, + "Sewers Level 1": 845, + "Canyon of the Magi": 846, + "Valley of Snakes": 847, + "Lost City": 848, + "Far Oasis": 849, + "Dry Hills": 850, + "Rocky Waste": 851, + "Lut Gholein": 852, + "Maggot Lair": 853, + "Claw Viper Temple": 854, + "Halls of the Dead": 855, + "Stony Tomb": 856, + "Palace Cellar": 857, + "Harem": 858, + "Sewers": 859, + "To The Moo Moo Farm": 860, + "To Chaos Sanctum": 861, + "To The River of Flame": 862, + "To The Outer Steppes": 863, + "To The Plains of Despair": 864, + "To The City of the Damned": 865, + "To The Pandemonium Fortress": 866, + "To The Durance of Hate Level 3": 867, + "To The Durance of Hate Level 2": 868, + "To The Durance of Hate Level 1": 869, + "To The Disused Reliquary": 870, + "To The Ruined Fane": 871, + "To The Forgotten Temple": 872, + "To The Forgotten Reliquary": 873, + "To The Disused Fane": 874, + "To The Ruined Temple": 875, + "To The Flayer Dungeon Level 1": 876, + "To The Flayer Dungeon Level 2": 877, + "To The Flayer Dungeon Level 3": 878, + "To The Swampy Pit Level 3": 879, + "To The Swampy Pit Level 2": 880, + "To The Swampy Pit Level 1": 881, + "To The Spider Cave": 882, + "To The Spider Cavern": 883, + "To Travincal": 884, + "To The Kurast Causeway": 885, + "To Upper Kurast": 886, + "To The Kurast Bazaar": 887, + "To Lower Kurast": 888, + "To The Flayer Jungle": 889, + "To The Great Marsh": 890, + "To The Spider Forest": 891, + "To The Kurast Docktown": 892, + "To The Arcane Sanctuary": 893, + "To Duriel's Lair": 894, + "To Tal Rasha's Tomb": 895, + "To The Ancient Tunnels": 896, + "To The Maggot Lair Level 3": 897, + "To The Maggot Lair Level 2": 898, + "To The Maggot Lair Level 1": 899, + "To The Claw Viper Temple Level 2": 900, + "To The Halls of the Dead Level 3": 901, + "To The Stony Tomb Level 2": 902, + "To The Claw Viper Temple Level 1": 903, + "To The Halls of the Dead Level 2": 904, + "To The Halls of the Dead Level 1": 905, + "To The Stony Tomb Level 1": 906, + "To The Palace Cellar Level 3": 907, + "To The Palace Cellar Level 2": 908, + "To The Palace Cellar Level 1 \tTo The Palace Cellar Level 1 ": 909, + "To The Harem Level 2": 910, + "To The Harem Level 1": 911, + "To The Sewers Level 3": 912, + "To The Sewers Level 2": 913, + "To The Sewers Level 1": 914, + "To The Canyon of the Magi": 915, + "To The Valley of Snakes": 916, + "To The Lost City": 917, + "To The Far Oasis": 918, + "To The Dry Hills": 919, + "To The Rocky Waste": 920, + "To Lut Gholein": 921, + "qstsa2q0": 922, + "qstsa2q1": 923, + "qstsa2q2": 924, + "qstsa2q3": 925, + "qstsa2q4": 926, + "qstsa2q5": 927, + "qstsa2q6": 928, + "qstsa3q0": 929, + "qstsa3q1": 930, + "qstsa3q2": 931, + "qstsa3q3": 932, + "qstsa3q4": 933, + "qstsa3q5": 934, + "qstsa3q6": 935, + "qstsa4q0": 936, + "qstsa4q1": 937, + "qstsa4q2": 938, + "qstsa4q3": 939, + "qstsa2q01": 940, + "qstsa2q11": 941, + "qstsa2q12": 942, + "qstsa2q13": 943, + "qstsa2q21": 944, + "qstsa2q22": 945, + "qstsa2q23": 946, + "qstsa2q24": 947, + "qstsa2q25": 948, + "qstsa2q31": 949, + "qstsa2q31a": 950, + "qstsa2q32": 951, + "qstsa2q33": 952, + "qstsa2q41": 953, + "qstsa2q41a": 954, + "qstsa2q42": 955, + "qstsa2q43": 956, + "qstsa2q51": 957, + "qstsa2q52": 958, + "qstsa2q53": 959, + "qstsa2q61": 960, + "qstsa2q61a": 961, + "qstsa2q62": 962, + "qstsa2q63": 963, + "qstsa2q63a": 964, + "qstsa2q64": 965, + "qstsa2q65": 966, + "qstsa3q01": 967, + "qstsa3q11": 968, + "qstsa3q12": 969, + "qstsa3q21": 970, + "qstsa3q22": 971, + "qstsa3q23": 972, + "qstsa3q24": 973, + "qstsa3q25": 974, + "qstsa3q26": 975, + "qstsa3q21a": 976, + "qstsa3q31": 977, + "qstsa3q32": 978, + "qstsa3q33": 979, + "qstsa3q34": 980, + "qstsa3q35": 981, + "qstsa3q41": 982, + "qstsa3q42": 983, + "qstsa3q43": 984, + "qstsa3q44": 985, + "qstsa3q45": 986, + "qstsa3q51": 987, + "qstsa3q52": 988, + "qstsa3q53": 989, + "qstsa3q61": 990, + "qstsa3q62": 991, + "qstsa3q63": 992, + "qstsa3q31a": 993, + "qstsa3q51a": 994, + "qstsa3q61a": 995, + "qstsa4q11": 996, + "qstsa4q12": 997, + "qstsa4q13a": 998, + "qstsa4q13": 999, + "qstsa4q31": 1000, + "qstsa4q32": 1001, + "qstsa4q33": 1002, + "qstsa4q34": 1003, + "qstsa4q21": 1004, + "qstsa4q22": 1005, + "qstsa4q23": 1006, + "qstsa4q24": 1007, + "asheara": 1008, + "hratli": 1009, + "alkor": 1010, + "ormus": 1011, + "nikita": 1012, + "tyrael": 1013, + "Izual": 1014, + "izual": 1015, + "Jamella": 1016, + "halbu": 1017, + "Malachai": 1018, + "merca201": 1019, + "merca202": 1020, + "merca203": 1021, + "merca204": 1022, + "merca205": 1023, + "merca206": 1024, + "merca207": 1025, + "merca208": 1026, + "merca209": 1027, + "merca210": 1028, + "merca211": 1029, + "merca212": 1030, + "merca213": 1031, + "merca214": 1032, + "merca215": 1033, + "merca216": 1034, + "merca217": 1035, + "merca218": 1036, + "merca219": 1037, + "merca220": 1038, + "merca221": 1039, + "merca222": 1040, + "merca223": 1041, + "merca224": 1042, + "merca225": 1043, + "merca226": 1044, + "merca227": 1045, + "merca228": 1046, + "merca229": 1047, + "merca230": 1048, + "merca231": 1049, + "merca232": 1050, + "merca233": 1051, + "merca234": 1052, + "merca235": 1053, + "merca236": 1054, + "merca237": 1055, + "merca238": 1056, + "merca239": 1057, + "merca240": 1058, + "merca241": 1059, + "qf1": 1060, + "qf2": 1061, + "KhalimFlail": 1062, + "SuperKhalimFlail": 1063, + "qey": 1064, + "qbr": 1065, + "qhr": 1066, + "The Feature Creep": 1067, + "Hell Bovine": 1068, + "Playersubtitles00": 1069, + "Playersubtitles01": 1070, + "Playersubtitles02": 1071, + "Playersubtitles03": 1072, + "Playersubtitles04": 1073, + "Playersubtitles05": 1074, + "Playersubtitles06": 1075, + "Playersubtitles07": 1076, + "Playersubtitles09": 1077, + "Playersubtitles10": 1078, + "Playersubtitles11": 1079, + "Playersubtitles12": 1080, + "Playersubtitles13": 1081, + "Playersubtitles14": 1082, + "Playersubtitles15": 1083, + "Playersubtitles16": 1084, + "Playersubtitles17": 1085, + "Playersubtitles18": 1086, + "Playersubtitles21": 1087, + "Playersubtitles22": 1088, + "Playersubtitles23": 1089, + "Playersubtitles24": 1090, + "Playersubtitles25": 1091, + "Playersubtitles26": 1092, + "Playersubtitles27": 1093, + "Playersubtitles28": 1094, + "LeaveCampAma": 1095, + "LeaveCampBar": 1096, + "LeaveCampPal": 1097, + "LeaveCampSor": 1098, + "LeaveCampNec": 1099, + "EnterDOEAma": 1100, + "EnterDOEBar": 1101, + "EnterDOEPal": 1102, + "EnterDOESor": 1103, + "EnterDOENec": 1104, + "EnterBurialAma": 1105, + "EnterBurialBar": 1106, + "EnterBurialPal": 1107, + "EnterBurialSor": 1108, + "EnterBurialNec": 1109, + "EnterMonasteryAma": 1110, + "EnterMonasteryBar": 1111, + "EnterMonasteryPal": 1112, + "EnterMonasterySor": 1113, + "EnterMonasteryNec": 1114, + "EnterForgottenTAma": 1115, + "EnterForgottenTBar": 1116, + "EnterForgottenTPal": 1117, + "EnterForgottenTSor": 1118, + "EnterForgottenTNec": 1119, + "EnterJailAma": 1120, + "EnterJailBar": 1121, + "EnterJailPal": 1122, + "EnterJailSor": 1123, + "EnterJailNec": 1124, + "Barracksremoved": 1129, + "EnterCatacombsAma": 1130, + "EnterCatacombsBar": 1131, + "EnterCatacombsPal": 1132, + "EnterCatacombsSor": 1133, + "EnterCatacombsNec": 1134, + "CompletingDOEAma": 1135, + "CompletingDOEBar": 1136, + "CompletingDOEPal": 1137, + "CompletingDOESor": 1138, + "CompletingDOENec": 1139, + "CompletingBurialAma": 1140, + "CompletingBurialBar": 1141, + "CompletingBurialPal": 1142, + "CompletingBurialSor": 1143, + "CompletingBurialNec": 1144, + "FindingInifusAma": 1145, + "FindingInifusBar": 1146, + "FindingInifusPal": 1147, + "FindingInifusSor": 1148, + "FindingInifusNec": 1149, + "FindingCairnAma": 1150, + "FindingCairnBar": 1151, + "FindingCairnPal": 1152, + "FindingCairnSor": 1153, + "FindingCairnNec": 1154, + "FindingTristramAma": 1155, + "FindingTristramBar": 1156, + "FindingTristramPal": 1157, + "FindingTristramSor": 1158, + "FindingTristramNec": 1159, + "RescueCainAma": 1160, + "RescueCainBar": 1161, + "RescueCainPal": 1162, + "RescueCainSor": 1163, + "RescueCainNec": 1164, + "HoradricMalusAma": 1165, + "HoradricMalusBar": 1166, + "HoradricMalusPal": 1167, + "HoradricMalusSor": 1168, + "HoradricMalusNec": 1169, + "CompletingForgottenTAma": 1170, + "CompletingForgottenTBar": 1171, + "CompletingForgottenTPal": 21924, + "CompletingForgottenTSor": 1173, + "CompletingForgottenTNec": 1174, + "CompletingAndarielAma": 1175, + "CompletingAndarielBar": 1176, + "CompletingAndarielPal": 1177, + "CompletingAndarielSor": 1178, + "CompletingAndarielNec": 1179, + "EnteringRadamentAma": 1180, + "EnteringRadamentBar": 1181, + "EnteringRadamentPal": 1182, + "EnteringRadamentSor": 1183, + "EnteringRadamentNec": 1184, + "CompletingRadamentAma": 1185, + "CompletingRadamentBar": 1186, + "CompletingRadamentPal": 1187, + "CompletingRadamentSor": 1188, + "CompletingRadamentNec": 1189, + "BeginTaintedSunAma": 1190, + "BeginTaintedSunBar": 1191, + "BeginTaintedSunPal": 1192, + "BeginTaintedSunSor": 1193, + "BeginTaintedSunNec": 1194, + "EnteringClawViperAma": 1195, + "EnteringClawViperBar": 1196, + "EnteringClawViperPal": 1197, + "EnteringClawViperSor": 1198, + "EnteringClawViperNec": 1199, + "CompletingTaintedSunAma": 1200, + "CompletingTaintedSunBar": 1201, + "CompletingTaintedSunPal": 1202, + "CompletingTaintedSunSor": 1203, + "CompletingTaintedSunNec": 1204, + "EnteringArcaneAma": 1205, + "EnteringArcaneBar": 1206, + "EnteringArcanePal": 1207, + "EnteringArcaneSor": 1208, + "EnteringArcaneNec": 1209, + "FindingSummonerAma": 1210, + "FindingSummonerBar": 1211, + "FindingSummonerPal": 1212, + "FindingSummonerSor": 1213, + "FindingSummonerNec": 1214, + "CompletingSummonerAma": 1215, + "CompletingSummonerBar": 1216, + "CompletingSummonerPal": 1217, + "CompletingSummonerSor": 1218, + "CompletingSummonerNec": 1219, + "FindingdecoyTombAma": 1220, + "FindingdecoyTombBar": 1221, + "FindingdecoyTombPal": 1222, + "FindingdecoyTombSor": 1223, + "FindingdecoyTombNec": 1224, + "FindingTrueTombAma": 1225, + "FindingTrueTombBar": 1226, + "FindingTrueTombPal": 1227, + "FindingTrueTombSor": 1228, + "FindingTrueTombNec": 1229, + "CompletingTombAma": 1230, + "CompletingTombBar": 1231, + "CompletingTombPal": 1232, + "CompletingTombSor": 1233, + "CompletingTombNec": 1234, + "nodarkwanderer": 1235, + "FindingLamEsenAma": 1236, + "FindingLamEsenBar": 1237, + "FindingLamEsenPal": 1238, + "FindingLamEsenSor": 1239, + "FindingLamEsenNec": 1240, + "CompletingLamEsenAma": 1241, + "CompletingLamEsenBar": 1242, + "CompletingLamEsenPal": 1243, + "CompletingLamEsenSor": 1244, + "CompletingLamEsenNec": 1245, + "FindingBeneathCityAma": 1246, + "FindingBeneathCityBar": 1247, + "FindingBeneathCityPal": 1248, + "FindingBeneathCitySor": 1249, + "FindingBeneathCityNec": 1250, + "FindingDrainLeverAma": 1251, + "FindingDrainLeverBar": 1252, + "FindingDrainLeverPal": 1253, + "FindingDrainLeverSor": 1254, + "FindingDrainLeverNec": 1255, + "CompletingBeneathCityAma": 1256, + "CompletingBeneathCityBar": 1257, + "CompletingBeneathCityPal": 1258, + "CompletingBeneathCitySor": 1259, + "CompletingBeneathCityNec": 1260, + "CompletingBladeAma": 1261, + "CompletingBladeBar": 1262, + "CompletingBladePal": 1263, + "CompletingBladeSor": 1264, + "CompletingBladeNec": 1265, + "FindingJadeFigAma": 1270, + "FindingTempleAma": 1271, + "FindingTempleBar": 1272, + "FindingTemplePal": 1273, + "FindingTempleSor": 1274, + "FindingTempleNec": 1275, + "CompletingTempleAma": 1276, + "CompletingTempleBar": 1277, + "CompletingTemplePal": 1278, + "CompletingTempleSor": 1279, + "CompletingTempleNec": 1280, + "FindingGuardianTowerAma": 1281, + "FindingGuardianTowerBar": 1282, + "FindingGuardianTowerPal": 1283, + "FindingGuardianTowerSor": 1284, + "FindingGuardianTowerNec": 1285, + "CompletingGuardianTowerAma": 1286, + "CompletingGuardianTowerBar": 1287, + "CompletingGuardianTowerPal": 1288, + "CompletingGuardianTowerSor": 1289, + "CompletingGuardianTowerNec": 1290, + "FreezingIzualAma": 21972, + "FreezingIzualBar": 1292, + "FreezingIzualPal": 1293, + "FreezingIzualSor": 1294, + "FreezingIzualNec": 1295, + "Eskillname0": 1296, + "Eskillsd0": 1297, + "Eskillld0": 1298, + "Eskillan0": 1299, + "EskillnameExp1": 1300, + "EskillsExpd1": 1301, + "EskilllExpd1": 1302, + "EskillExpan1": 1303, + "Eskillname2": 1304, + "Eskillsd2": 1305, + "Eskillld2": 1306, + "Eskillan2": 1307, + "Eskillname3": 1308, + "Eskillsd3": 1309, + "Eskillld3": 1310, + "Eskillan3": 1311, + "Eskillname4": 1312, + "Eskillsd4": 1313, + "Eskillld4": 1314, + "Eskillan4": 1315, + "Eskillname5": 1316, + "Eskillsd5": 1317, + "Eskillld5": 1318, + "Eskillan5": 1319, + "Eskillname6": 1320, + "Eskillsd6": 1321, + "Eskillld6": 1322, + "Eskillan6": 1323, + "Eskillname7": 1324, + "Eskillsd7": 1325, + "Eskillld7": 1326, + "Eskillan7": 1327, + "Eskillname8": 1328, + "Eskillsd8": 1329, + "Eskillld8": 1330, + "Eskillan8": 1331, + "Eskillname9": 1332, + "Eskillsd9": 1333, + "Eskillld9": 1334, + "Eskillan9": 1335, + "Eskillname10": 1336, + "Eskillsd10": 1337, + "Eskillld10": 1338, + "Eskillan10": 1339, + "Eskillname11": 1340, + "Eskillsd11": 1341, + "Eskillld11": 1342, + "Eskillan11": 1343, + "Eskillname12": 1344, + "Eskillsd12": 1345, + "Eskillld12": 1346, + "Eskillan12": 1347, + "Eskillname13": 1348, + "Eskillsd13": 1349, + "Eskillld13": 1350, + "Eskillan13": 1351, + "Eskillname14": 1352, + "Eskillsd14": 1353, + "Eskillld14": 1354, + "Eskillan14": 1355, + "Eskillname15": 1356, + "Eskillsd15": 1357, + "Eskillld15": 1358, + "Eskillan15": 1359, + "Eskillname16": 1360, + "Eskillsd16": 1361, + "Eskillld16": 1362, + "Eskillan16": 1363, + "Eskillname17": 1364, + "Eskillsd17": 1365, + "Eskillld17": 1366, + "Eskillan17": 1367, + "Eskillname18": 1368, + "Eskillsd18": 1369, + "Eskillld18": 1370, + "Eskillan18": 1371, + "Eskillname19": 1372, + "Eskillsd19": 1373, + "Eskillld19": 1374, + "Eskillan19": 1375, + "Eskillname20": 1376, + "Eskillsd20": 1377, + "Eskillld20": 1378, + "Eskillan20": 1379, + "Eskillname21": 1380, + "Eskillsd21": 1381, + "Eskillld21": 1382, + "Eskillan21": 1383, + "Eskillname22": 1384, + "Eskillsd22": 1385, + "Eskillld22": 1386, + "Eskillan22": 1387, + "Eskillname23": 1388, + "Eskillsd23": 1389, + "Eskillld23": 1390, + "Eskillan23": 1391, + "Eskillname24": 1392, + "Eskillsd24": 1393, + "Eskillld24": 1394, + "Eskillan24": 1395, + "Eskillname25": 1396, + "Eskillsd25": 1397, + "Eskillld25": 1398, + "Eskillan25": 1399, + "Eskillname26": 1400, + "Eskillsd26": 1401, + "Eskillld26": 1402, + "Eskillan26": 1403, + "Eskillname27": 1404, + "Eskillsd27": 1405, + "Eskillld27": 1406, + "Eskillan27": 1407, + "Eskillname28": 1408, + "Eskillsd28": 1409, + "Eskillld28": 1410, + "Eskillan28": 1411, + "Eskillname29": 1412, + "Eskillsd29": 1413, + "Eskillld29": 1414, + "Eskillan29": 1415, + "Eskillname30": 1416, + "Eskillsd30": 1417, + "Eskillld30": 1418, + "Eskillan30": 1419, + "Eskillname31": 1420, + "Eskillsd31": 1421, + "Eskillld31": 1422, + "Eskillan31": 1423, + "Eskillname32": 1424, + "Eskillsd32": 1425, + "Eskillld32": 1426, + "Eskillan32": 1427, + "Eskillname33": 1428, + "Eskillsd33": 1429, + "Eskillld33": 1430, + "Eskillan33": 1431, + "Eskillname34": 1432, + "Eskillsd34": 1433, + "Eskillld34": 1434, + "Eskillan34": 1435, + "Eskillname35": 1436, + "Eskillsd35": 1437, + "Eskillld35": 1438, + "Eskillan35": 1439, + "Eskillname36": 1440, + "Eskillsd36": 1441, + "Eskillld36": 1442, + "Eskillan36": 1443, + "Eskillname37": 1444, + "Eskillsd37": 1445, + "Eskillld37": 1446, + "Eskillan37": 1447, + "Eskillname38": 1448, + "Eskillsd38": 1449, + "Eskillld38": 1450, + "Eskillan38": 1451, + "Eskillname39": 1452, + "Eskillsd39": 1453, + "Eskillld39": 1454, + "Eskillan39": 1455, + "Eskillname40": 1456, + "Eskillsd40": 1457, + "Eskillld40": 1458, + "Eskillan40": 1459, + "Eskillname41": 1460, + "Eskillsd41": 1461, + "Eskillld41": 1462, + "Eskillan41": 1463, + "Eskillname42": 1464, + "Eskillsd42": 1465, + "Eskillld42": 1466, + "Eskillan42": 1467, + "Eskillname43": 1468, + "Eskillsd43": 1469, + "Eskillld43": 1470, + "Eskillan43": 1471, + "Eskillname44": 1472, + "Eskillsd44": 1473, + "Eskillld44": 1474, + "Eskillan44": 1475, + "Eskillname45": 1476, + "Eskillsd45": 1477, + "Eskillld45": 1478, + "Eskillan45": 1479, + "Eskillname46": 1480, + "Eskillsd46": 1481, + "Eskillld46": 1482, + "Eskillan46": 1483, + "Eskillname47": 1484, + "Eskillsd47": 1485, + "Eskillld47": 1486, + "Eskillan47": 1487, + "Eskillname48": 1488, + "Eskillsd48": 1489, + "Eskillld48": 1490, + "Eskillan48": 1491, + "Eskillname49": 1492, + "Eskillsd49": 1493, + "Eskillld49": 1494, + "Eskillan49": 1495, + "Eskillname50": 1496, + "Eskillsd50": 1497, + "Eskillld50": 1498, + "Eskillan50": 1499, + "Eskillname51": 1500, + "Eskillsd51": 1501, + "Eskillld51": 1502, + "Eskillan51": 1503, + "Eskillname52": 1504, + "Eskillsd52": 1505, + "Eskillld52": 1506, + "Eskillan52": 1507, + "Eskillname53": 1508, + "Eskillsd53": 1509, + "Eskillld53": 1510, + "Eskillan53": 1511, + "Eskillname54": 1512, + "Eskillsd54": 1513, + "Eskillld54": 1514, + "Eskillan54": 1515, + "Eskillname55": 1516, + "Eskillsd55": 1517, + "Eskillld55": 1518, + "Eskillan55": 1519, + "Eskillname56": 1520, + "Eskillsd56": 1521, + "Eskillld56": 1522, + "Eskillan56": 1523, + "Eskillname57": 1524, + "Eskillsd57": 1525, + "Eskillld57": 1526, + "Eskillan57": 1527, + "Eskillname58": 1528, + "Eskillsd58": 1529, + "Eskillld58": 1530, + "Eskillan58": 1531, + "Eskillname59": 1532, + "Eskillsd59": 1533, + "Eskillld59": 1534, + "Eskillan59": 1535, + "ESkillHawk": 22278, + "ESkillSpikes": 22279, + "ESkillStars": 22280, + "ESkillWolf": 22281, + "ESkillWolves": 22282, + "ESkillShoots": 22283, + "ESkillTimes": 22284, + "ESkillSpikes2": 22285, + "ob1": 20281, + "ob2": 20282, + "ob3": 20283, + "ob4": 20284, + "ob5": 21778, + "ne1": 20332, + "ne2": 20333, + "ne3": 20334, + "ne4": 20335, + "ne5": 20336, + "dr1": 20320, + "dr2": 20318, + "dr3": 20319, + "dr4": 20317, + "dr5": 20321, + "as1": 20285, + "as2": 20286, + "as3": 20287, + "as4": 20288, + "as5": 20289, + "as6": 20290, + "as7": 20291, + "AmaOnly": 20426, + "SorOnly": 20427, + "NecOnly": 20428, + "PalOnly": 20429, + "BarOnly": 20430, + "DruOnly": 20431, + "AssOnly": 20432, + "WeaponDescH2H": 21258, + "Seige Tower": 22352, + "RotWalker": 22353, + "ReanimatedHorde": 22354, + "ProwlingDead": 22355, + "UnholyCorpse": 22356, + "DefiledWarrior": 22357, + "Seige Beast": 1580, + "CrushBiest": 22359, + "BloodBringer": 22360, + "GoreBearer": 22361, + "DeamonSteed": 22362, + "WailingSpirit": 22363, + "LifeSeeker": 22364, + "LifeStealer": 22365, + "DeathlyVisage": 22366, + "BoundSpirit": 22367, + "BanishedSoul": 22368, + "Deathexp": 22369, + "Minionexp": 22370, + "Slayerexp": 22371, + "IceBoar": 22372, + "FireBoar": 22373, + "HellSpawn": 22374, + "IceSpawn": 22375, + "GreaterHellSpawn": 22376, + "GreaterIceSpawn": 22377, + "FanaticMinion": 22378, + "BerserkSlayer": 22379, + "ConsumedFireBoar": 22380, + "ConsumedIceBoar": 22381, + "FrenziedHellSpawn": 22382, + "FrenziedIceSpawn": 22383, + "InsaneHellSpawn": 22384, + "InsaneIceSpawn": 22385, + "Succubusexp": 22386, + "VileTemptress": 22387, + "StygianHarlot": 22388, + "BlightWing": 1611, + "BloodWitch": 1612, + "Dominus": 22391, + "VileWitch": 22392, + "StygianFury": 22393, + "MageWing": 1616, + "HellWitch": 1617, + "OverSeer": 22396, + "Lasher": 22397, + "OverLord": 22398, + "BloodBoss": 22399, + "HellWhip": 22400, + "MinionSpawner": 22401, + "MinionSlayerSpawner": 22402, + "MinionIce/fireBoarSpawner": 22403, + "Minionice/hellSpawnSpawner": 22404, + "MinionGreaterIce/hellSpawnSpawner": 22405, + "Imp1": 22406, + "Imp2": 22407, + "Imp3": 22408, + "Imp4": 22409, + "Imp5": 22410, + "CapsJoinMenu4": 1633, + "CapsJoinMenu5": 1634, + "Guild 1": 1635, + "Guild 2": 1636, + "Guild 3": 1637, + "Guild 4": 1638, + "Guild 5": 1639, + "To Guild 5": 1640, + "To Guild 4": 1641, + "To Guild 3": 1642, + "To Guild 2": 1643, + "To Guild 1": 1644, + "CapsBnet9": 1645, + "CapsBnet10": 1646, + "CapsBnet11": 1647, + "CapsBnet12": 1648, + "CapsBnet13": 1649, + "CapsBnet14": 1650, + "CapsBnet15": 1651, + "CapsGuildName": 1652, + "CapsGuildTag": 1653, + "GuildText1": 1654, + "GuildText2": 1655, + "Ladder3": 1656, + "Ladder7": 1657, + "gmGuildTitle": 1658, + "gmGuildName": 1659, + "gmGuildTag": 1660, + "gmWWW": 1661, + "gmGuildCharter": 1662, + "gmGuildCurrentGolds": 1663, + "gmGuildNextLevel": 1664, + "gmGuildMaster": 1665, + "gmOfficer": 1666, + "gmName": 1667, + "gmClass": 1668, + "gmLevel": 1669, + "gmDonate": 1670, + "gmRemove": 1671, + "gmPal": 1672, + "gmSor": 1673, + "gmAma": 1674, + "gmNec": 1675, + "gmBar": 1676, + "gmChangeSym": 1677, + "gmChangeCharter": 1678, + "gmChangeWebLink": 1679, + "Guild Portal": 1680, + "createdguildsuccess": 1681, + "createdguildfailure": 1682, + "inviteguildsuccess": 1683, + "inviteguildfailure": 1684, + "inviteguildins": 1685, + "joinedguildsuccess": 1686, + "joinedguildfailure": 1687, + "quitguildsuccess": 1688, + "quitguildfailure": 1689, + "guildentererror": 1690, + "strGuildMasterKicked": 1691, + "strGuildPerk1": 1692, + "strGuildPerk2": 1693, + "strGuildPerk3": 1694, + "strGuildPerk4": 1695, + "strGuildPerk5": 1696, + "strGuildPerk6": 1697, + "strGuildGoldDonated": 1698, + "strGuildDonateGold": 1699, + "gmGuildCurrentGoldPopup": 1700, + "gmGuildNextLevelPopup": 1701, + "gmGuildDonateGoldPopup": 1702, + "Message Board": 1703, + "Trophy Case": 1704, + "Guild Vault": 1705, + "Steeg Stone": 1706, + "guildaccepticon": 1707, + "guildmsgtext": 1708, + "ScrollFormat": 1709, + "BookFormat": 1710, + "HiqualityFormat": 1711, + "LowqualityFormat": 1712, + "HerbFormat": 1713, + "MagicFormat": 1714, + "GemmedNormalName": 1715, + "BodyPartsFormat": 1716, + "PlayerBodyPartFormat": 1717, + "RareFormat": 1718, + "SetItemFormat": 1719, + "ChampionFormat": 1720, + "Monster1Format": 1721, + "Monster2Format": 1722, + "Low Quality": 1723, + "Damaged": 1724, + "Cracked": 1725, + "Crude": 20910, + "Hiquality": 1727, + "Gemmed": 1728, + "Resiliant": 1729, + "Sturdy": 1730, + "Strong": 1731, + "Glorious": 1732, + "Blessed": 1733, + "Saintly": 1734, + "Holy": 1735, + "Devious": 1736, + "Fortified": 1737, + "Urgent": 1738, + "Fleet": 1739, + "Muscular": 1740, + "Jagged": 1741, + "Deadly": 1742, + "Vicious": 1743, + "Brutal": 1744, + "Massive": 1745, + "Savage": 1746, + "Merciless": 1747, + "Vulpine": 1748, + "Swift": 1749, + "Artful": 1750, + "Skillful": 1751, + "Adroit": 1752, + "Tireless": 1753, + "Rugged": 1754, + "Bronze": 1755, + "Iron": 1756, + "Steel": 1757, + "Silver": 1758, + "Gold": 1759, + "Platinum": 1760, + "Meteoric": 1761, + "Sharp": 1762, + "Fine": 1763, + "Warrior's": 1764, + "Soldier's": 1765, + "Knight's": 1766, + "Lord's": 1767, + "King's": 1768, + "Howling": 1769, + "Fortuitous": 1770, + "Brilliant": 1771, + "Omniscient": 1772, + "Sage": 1773, + "Shrewd": 1774, + "Vivid": 1775, + "Glimmering": 1776, + "Glowing": 1777, + "Bright": 1778, + "Solar": 1779, + "Lizard's": 1780, + "Forceful": 1781, + "Snake's": 1782, + "Serpent's": 1783, + "Drake's": 1784, + "Dragon's": 1785, + "Wyrm's": 1786, + "Dazzling": 1787, + "Facinating": 1788, + "Prismatic": 1789, + "Azure": 1790, + "Lapis": 1791, + "Cobalt": 1792, + "Indigo": 1793, + "Sapphire": 1794, + "Cerulean": 1795, + "Red": 1796, + "Crimson": 1797, + "Burgundy": 1798, + "Garnet": 1799, + "Russet": 1800, + "Ruby": 1801, + "Vermilion": 1802, + "Orange": 1803, + "Ocher": 1804, + "Tangerine": 1805, + "Coral": 1806, + "Crackling": 1807, + "Amber": 1808, + "Forked": 1809, + "Green": 20905, + "Beryl": 1811, + "Jade": 1812, + "Viridian": 1813, + "Vital": 1814, + "Emerald": 1815, + "Enduring": 1816, + "Fletcher's": 1817, + "Archer's": 1818, + "Monk's": 1819, + "Priest's": 1820, + "Summoner's": 1821, + "Necromancer's": 1822, + "Angel's": 1823, + "Arch-Angel's": 1824, + "Slayer's": 1825, + "Berserker's": 2507, + "Kicking": 1827, + "Triumphant": 1828, + "Mighty": 1829, + "Energizing": 1830, + "Strengthening": 1831, + "Empowering": 1832, + "Brisk": 1833, + "Tough": 1834, + "Hardy": 1835, + "Robust": 1836, + "of Health": 1837, + "of Protection": 1838, + "of Absorption": 1839, + "of Warding": 1840, + "of the Sentinel": 1841, + "of Guarding": 1842, + "of Negation": 1843, + "of Piercing": 1844, + "of Bashing": 1845, + "of Puncturing": 1846, + "of Thorns": 1847, + "of Spikes": 1848, + "of Readiness": 1849, + "of Alacrity": 1850, + "of Swiftness": 1851, + "of Quickness": 1852, + "of Blocking": 1853, + "of Deflecting": 1854, + "of the Apprentice": 1855, + "of the Magus": 1856, + "of Frost": 1857, + "of the Glacier": 1858, + "of Warmth": 1859, + "of Flame": 1860, + "of Fire": 1861, + "of Burning": 1862, + "of Shock": 1863, + "of Lightning": 1864, + "of Thunder": 1865, + "of Craftsmanship": 1866, + "of Quality": 1867, + "of Maiming": 1868, + "of Slaying": 1869, + "of Gore": 1870, + "of Carnage": 1871, + "of Slaughter": 1872, + "of Worth": 1873, + "of Measure": 1874, + "of Excellence": 1875, + "of Performance": 1876, + "of Blight": 1877, + "of Venom": 1878, + "of Pestilence": 1879, + "of Dexterity": 1880, + "of Skill": 1881, + "of Accuracy": 1882, + "of Precision": 1883, + "of Perfection": 1884, + "of Balance": 1885, + "of Stability": 1886, + "of the Horse": 1887, + "of Regeneration": 1888, + "of Regrowth": 1889, + "of Vileness": 1890, + "of Greed": 1891, + "of Wealth": 1892, + "of Chance": 1893, + "of Fortune": 1894, + "of Energy": 1895, + "of the Mind": 1896, + "of Brilliance": 1897, + "of Sorcery": 1898, + "of Wizardry": 1899, + "of the Bear": 1900, + "of Light": 1901, + "of Radiance": 1902, + "of the Sun": 1903, + "of Life": 1904, + "of the Jackal": 1905, + "of the Fox": 1906, + "of the Wolf": 1907, + "of the Tiger": 1908, + "of the Mammoth": 1909, + "of the Colosuss": 1910, + "of the Leech": 1911, + "of the Locust": 1912, + "of the Bat": 1913, + "of the Vampire": 1914, + "of Defiance": 1915, + "of Remedy": 1916, + "of Amelioration": 1917, + "of Ice": 1918, + "of Simplicity": 1919, + "of Ease": 1920, + "of the Mule": 1921, + "of Strength": 1922, + "of Might": 1923, + "of the Ox": 1924, + "of the Giant": 1925, + "of the Titan": 1926, + "of Pacing": 1927, + "of Haste": 1928, + "of Speed": 1929, + "cap": 1930, + "skp": 1931, + "hlm": 1932, + "fhl": 1933, + "ghm": 1934, + "crn": 1935, + "msk": 1936, + "qui": 1937, + "lea": 1938, + "hla": 1939, + "stu": 1940, + "rng": 1941, + "scl": 1942, + "chn": 1943, + "brs": 1944, + "spl": 1945, + "plt": 1946, + "fld": 1947, + "gth": 1948, + "ful": 1949, + "aar": 1950, + "ltp": 1951, + "buc": 1952, + "sml": 1953, + "lrg": 1954, + "kit": 1955, + "tow": 1956, + "gts": 1957, + "lgl": 1958, + "vgl": 1959, + "mgl": 1960, + "tgl": 1961, + "hgl": 1962, + "lbt": 1963, + "vbt": 1964, + "mbt": 1965, + "tbt": 1966, + "hbt": 1967, + "lbl": 1968, + "vbl": 1969, + "mbl": 1970, + "tbl": 1971, + "hbl": 1972, + "bhm": 1973, + "bsh": 1974, + "spk": 1975, + "hax": 1976, + "axe": 1977, + "2ax": 1978, + "mpi": 1979, + "wax": 1980, + "lax": 1981, + "bax": 1982, + "btx": 1983, + "gax": 1984, + "gix": 1985, + "wnd": 1986, + "ywn": 1987, + "bwn": 1988, + "gwn": 1989, + "clb": 1990, + "scp": 1991, + "gsc": 1992, + "wsp": 1993, + "spc": 1994, + "mac": 1995, + "mst": 1996, + "fla": 1997, + "whm": 1998, + "mau": 1999, + "gma": 2000, + "ssd": 2001, + "scm": 2002, + "sbr": 2003, + "flc": 2004, + "crs": 2005, + "bsd": 2006, + "lsd": 2007, + "wsd": 2008, + "2hs": 2009, + "clm": 2010, + "gis": 2011, + "bsw": 2012, + "flb": 2013, + "gsd": 2014, + "dgr": 2015, + "dir": 2016, + "kri": 2017, + "bld": 2018, + "tkf": 2019, + "tax": 2020, + "bkf": 2021, + "bal": 2022, + "jav": 2023, + "pil": 2024, + "ssp": 2025, + "glv": 2026, + "tsp": 2027, + "spr": 2028, + "tri": 2029, + "brn": 2030, + "spt": 2031, + "pik": 2032, + "bar": 2033, + "vou": 2034, + "scy": 2035, + "pax": 2036, + "hal": 2037, + "wsc": 2038, + "sst": 2039, + "lst": 2040, + "cst": 2041, + "bst": 2042, + "wst": 2043, + "sbw": 2044, + "hbw": 2045, + "lbw": 2046, + "cbw": 2047, + "sbb": 2048, + "lbb": 2049, + "swb": 2050, + "lwb": 2051, + "lxb": 2052, + "mxb": 2053, + "hxb": 2054, + "rxb": 2055, + "xpk": 2056, + "xsh": 2057, + "xh9": 2058, + "zhb": 2059, + "ztb": 2060, + "zmb": 2061, + "zvb": 2062, + "zlb": 2063, + "xhb": 2064, + "xtb": 2065, + "xmb": 2066, + "xvb": 2067, + "xlb": 2068, + "xhg": 2069, + "xtg": 2070, + "xmg": 2071, + "xvg": 2072, + "xlg": 2073, + "xts": 2074, + "xow": 2075, + "xit": 2076, + "xrg": 2077, + "xml": 2078, + "xuc": 2079, + "xtp": 2080, + "xar": 2081, + "xul": 2082, + "xth": 2083, + "xld": 2084, + "xlt": 2085, + "xpl": 2086, + "xrs": 2087, + "xhn": 2088, + "xcl": 2089, + "xng": 2090, + "xtu": 2091, + "xla": 2092, + "xea": 2093, + "xui": 2094, + "xsk": 2095, + "xrn": 2096, + "xhm": 2097, + "xhl": 2098, + "xlm": 2099, + "xkp": 2100, + "xap": 2101, + "8rx": 2102, + "8hx": 2103, + "8mx": 2104, + "8lx": 2105, + "8lw": 2106, + "8sw": 2107, + "8l8": 2108, + "8s8": 2109, + "8cb": 2110, + "8lb": 2111, + "8hb": 2112, + "8sb": 2113, + "8ws": 2114, + "8bs": 2115, + "8cs": 2116, + "8ls": 2117, + "8ss": 2118, + "9wc": 2119, + "9h9": 2120, + "9pa": 2121, + "9s8": 2122, + "9vo": 2123, + "9b7": 2124, + "9p9": 2125, + "9st": 2126, + "9br": 2127, + "9tr": 2128, + "9sr": 2129, + "9ts": 2130, + "9gl": 2131, + "9s9": 2132, + "9pi": 2133, + "9ja": 2134, + "9b8": 2135, + "9bk": 2136, + "9ta": 2137, + "9tk": 2138, + "9bl": 2139, + "9kr": 2140, + "9di": 2141, + "9dg": 2142, + "9gd": 2143, + "9fb": 2144, + "9gs": 2145, + "9cm": 2146, + "92h": 2147, + "9wd": 2148, + "9ls": 2149, + "9bs": 2150, + "9cr": 2151, + "9fc": 2152, + "9sb": 2153, + "9sm": 2154, + "9ss": 2155, + "9gm": 2156, + "9m9": 2157, + "9wh": 2158, + "9fl": 2159, + "9mt": 2160, + "9ma": 2161, + "9sp": 2162, + "9ws": 2163, + "9qs": 2164, + "9sc": 2165, + "9cl": 2166, + "9gw": 2167, + "9bw": 2168, + "9yw": 2169, + "9wn": 2170, + "9gi": 2171, + "9ga": 2172, + "9bt": 2173, + "9ba": 2174, + "9la": 2175, + "9wa": 2176, + "9mp": 2177, + "92a": 2178, + "9ax": 2179, + "9ha": 2180, + "9b9": 2181, + "gpl": 2182, + "opl": 2183, + "gpm": 2184, + "opm": 2185, + "gps": 2186, + "ops": 2187, + "gidbinn": 2188, + "g33": 2189, + "d33": 2190, + "leg": 2191, + "Malus": 2192, + "hdm": 2193, + "hfh": 2194, + "hst": 2195, + "msf": 2196, + "orifice": 2197, + "elx": 2198, + "tbk": 2199, + "tsc": 2200, + "ibk": 2201, + "isc": 2202, + "RightClicktoUse": 2203, + "RightClicktoOpen": 2204, + "RightClicktoRead": 2205, + "InsertScrolls": 2206, + "vps": 2207, + "yps": 2208, + "rvs": 2209, + "rvl": 2210, + "wms": 2211, + "amu": 2212, + "vip": 2213, + "rin": 2214, + "gld": 2215, + "bks": 2216, + "bkd": 2217, + "aqv": 2218, + "tch": 2219, + "cqv": 2220, + "Key": 2221, + "key": 2222, + "luv": 2223, + "xyz": 2224, + "shrine": 2225, + "teleport pad": 2226, + "j34": 2227, + "g34": 2228, + "bbb": 2229, + "LamTome": 2230, + "box": 2231, + "tr1": 2232, + "mss": 2233, + "ass": 2234, + "ear": 2235, + "gcv": 2236, + "gfv": 2237, + "gsv": 2238, + "gzv": 2239, + "gpv": 2240, + "gcy": 2241, + "gfy": 2242, + "gsy": 2243, + "gly": 2244, + "gpy": 2245, + "gcb": 2246, + "gfb": 2247, + "gsb": 2248, + "glb": 2249, + "gpb": 2250, + "gcg": 2251, + "gfg": 2252, + "glg": 2253, + "gsg": 2254, + "gpg": 2255, + "gcr": 2256, + "gfr": 2257, + "gsr": 2258, + "glr": 2259, + "gpr": 2260, + "gcw": 2261, + "gfw": 2262, + "gsw": 2263, + "glw": 2264, + "gpw": 2265, + "hp1": 2266, + "hp2": 2267, + "hp3": 2268, + "hp4": 2269, + "hp5": 2270, + "mp1": 2271, + "mp2": 2272, + "mp3": 2273, + "mp4": 2274, + "mp5": 2275, + "hrb": 20434, + "skc": 2277, + "skf": 2278, + "sku": 2279, + "skl": 2280, + "skz": 2281, + "Beast": 2282, + "Eagle": 2283, + "Raven": 2284, + "Viper": 2285, + "GhoulRI": 2286, + "Skull": 2287, + "Blood": 2288, + "Dread": 2289, + "Doom": 2290, + "Grim": 2291, + "Bone": 2292, + "Death": 2293, + "Shadow": 2294, + "Storm": 2295, + "Rune": 2296, + "PlagueRI": 2297, + "Stone": 2298, + "Wraith": 2989, + "Spirit": 2300, + "Demon": 2301, + "Cruel": 2302, + "Empyrion": 2303, + "Bramble": 2304, + "Pain": 2305, + "Loath": 2306, + "Glyph": 2307, + "Imp": 2308, + "Fiend": 2309, + "Hailstone": 2310, + "Gale": 2311, + "Dire": 2312, + "Soul": 2313, + "Brimstone": 2314, + "Corpse": 2315, + "Carrion": 2316, + "Holocaust": 2317, + "Havoc": 2318, + "Bitter": 2319, + "Entropy": 2320, + "Chaos": 2321, + "Order": 2322, + "Rift": 2323, + "Corruption": 2324, + "bite": 2325, + "scratch": 2326, + "scalpel": 2327, + "fang": 2328, + "gutter": 2329, + "thirst": 2330, + "razor": 2331, + "scythe": 2332, + "edge": 2333, + "saw": 2334, + "splitter": 2335, + "cleaver": 2336, + "sever": 2337, + "sunder": 2338, + "rend": 2339, + "mangler": 2340, + "slayer": 2341, + "reaver": 2342, + "Spawn": 2343, + "gnash": 2344, + "star": 2345, + "blow": 2346, + "smasher": 2347, + "Bane": 2348, + "crusher": 2349, + "breaker": 2350, + "grinder": 2351, + "crack": 2352, + "mallet": 2353, + "knell": 2354, + "lance": 2355, + "spike": 2356, + "impaler": 2357, + "skewer": 2358, + "prod": 2359, + "scourge": 2360, + "wand": 2361, + "wrack": 2362, + "barb": 2363, + "needle": 2364, + "dart": 2365, + "bolt": 2366, + "quarrel": 2367, + "fletch": 2368, + "flight": 2369, + "nock": 2370, + "horn": 2371, + "stinger": 2372, + "quill": 2373, + "goad": 2374, + "branch": 2375, + "spire": 2376, + "song": 2377, + "call": 2378, + "cry": 2379, + "spell": 2380, + "chant": 2381, + "weaver": 2382, + "gnarl": 2383, + "visage": 2384, + "crest": 2385, + "circlet": 2386, + "veil": 2387, + "hood": 2388, + "mask": 2389, + "brow": 2390, + "casque": 2391, + "visor": 2392, + "cowl": 2393, + "hide": 2394, + "Pelt": 2395, + "carapace": 2396, + "coat": 2397, + "wrap": 2398, + "suit": 2399, + "cloak": 2400, + "shroud": 2401, + "jack": 2402, + "mantle": 2403, + "guard": 2404, + "badge": 2405, + "rock": 2406, + "aegis": 2407, + "ward": 2408, + "tower": 2409, + "shield": 2410, + "wing": 2411, + "mark": 2412, + "emblem": 2413, + "hand": 2414, + "fist": 2415, + "claw": 2416, + "clutches": 2417, + "grip": 2418, + "grasp": 2419, + "hold": 2420, + "touch": 2421, + "finger": 2422, + "knuckle": 2423, + "shank": 2424, + "spur": 2425, + "tread": 2426, + "stalker": 2427, + "greave": 2428, + "blazer": 2429, + "nails": 2430, + "trample": 2431, + "Brogues": 2432, + "track": 2433, + "slippers": 2434, + "clasp": 2435, + "buckle": 2436, + "harness": 2437, + "lock": 2438, + "fringe": 2439, + "winding": 2440, + "chain": 2441, + "strap": 2442, + "lash": 2443, + "cord": 2444, + "knot": 2445, + "circle": 2446, + "loop": 2447, + "eye": 2448, + "turn": 2449, + "spiral": 2450, + "coil": 2451, + "gyre": 2452, + "band": 2453, + "whorl": 2454, + "talisman": 2455, + "heart": 2456, + "noose": 2457, + "necklace": 2458, + "collar": 2459, + "beads": 2460, + "torc": 2461, + "gorget": 2462, + "scarab": 2463, + "wood": 2464, + "brand": 2465, + "bludgeon": 2466, + "cudgel": 2467, + "loom": 2468, + "harp": 2469, + "master": 2470, + "barRI": 2471, + "hew": 2472, + "crook": 2473, + "mar": 2474, + "shell": 2475, + "stake": 2476, + "picket": 2477, + "pale": 2478, + "flange": 2479, + "Civerb's Vestments": 2480, + "Hsarus' Trim": 2481, + "Cleglaw's Brace": 2482, + "Iratha's Finery": 2483, + "Isenhart's Armory": 2484, + "Vidala's Rig": 2485, + "Milabrega's Regalia": 2486, + "Cathan's Traps": 2487, + "Tancred's Battlegear": 2488, + "Sigon's Complete Steel": 2489, + "Infernal Tools": 2490, + "Berserker's Garb": 2491, + "Death's Disguise": 2492, + "Angelical Raiment": 2493, + "Arctic Gear": 2494, + "Arcanna's Tricks": 2495, + "Civerb's": 2496, + "Hsarus'\tHsaru's": 2497, + "Cleglaw's": 2498, + "Iratha's": 2499, + "Isenhart's": 2500, + "Vidala's": 2501, + "Milabrega's": 2502, + "Cathan's": 2503, + "Tancred's": 2504, + "Sigon's": 2505, + "Infernal": 2506, + "Death's": 2508, + "Angelical": 2509, + "Arctic": 2510, + "Arcanna's": 2511, + "Ward": 2512, + "Iron Heel": 2513, + "Tooth": 2514, + "Collar": 2515, + "Lightbrand": 2516, + "Barb": 2517, + "Orb": 2518, + "Rule": 2519, + "Crowbill": 2520, + "Visor": 2521, + "Cranium": 2522, + "Headgear": 2523, + "Hand": 2524, + "Sickle": 2525, + "Horn": 2526, + "Sign": 2527, + "Icon": 2528, + "Iron Fist": 2529, + "Claw": 2530, + "Cuff": 2531, + "Parry": 2532, + "Fetlock": 2533, + "Rod": 2534, + "Mesh": 2535, + "Spine": 2536, + "Shelter": 2537, + "Torch": 2538, + "Hauberk": 2539, + "Guard": 2540, + "Mantle": 2541, + "Furs": 2542, + "Deathwand": 2543, + "CudgelSI3S": 2544, + "Iron Stay": 2545, + "Pincers": 2546, + "Coil": 2547, + "Case": 2548, + "Ambush": 2549, + "Diadem": 2550, + "Visage": 2551, + "Hobnails": 2552, + "Gage": 2553, + "SignSI3S": 2554, + "Hatchet": 2555, + "Touch": 2556, + "Halo": 2557, + "Binding": 2558, + "Head": 2559, + "Horns": 2560, + "Snare": 2561, + "Robe": 2562, + "Sigil": 2563, + "Weird": 2564, + "Sabot": 2565, + "Wings": 2566, + "Mitts": 2567, + "Flesh": 2568, + "Cord": 2569, + "Seal": 2570, + "SkullSI5S": 2571, + "Wrap": 2572, + "GuardSI6S": 2573, + "The Gnasher": 2574, + "Deathspade": 2575, + "Bladebone": 2576, + "Mindrend": 2577, + "Rakescar": 2578, + "Fechmars Axe": 2579, + "Goreshovel": 2580, + "The Chieftan": 2581, + "Brainhew": 2582, + "The Humongous": 2583, + "Iros Torch": 2584, + "Maelstromwrath": 2585, + "Gravenspine": 2586, + "Umes Lament": 2587, + "Felloak": 2588, + "Knell Striker": 2589, + "Rusthandle": 2590, + "Stormeye": 2591, + "Stoutnail": 2592, + "Crushflange": 2593, + "Bloodrise": 2594, + "The Generals Tan Do Li Ga": 2595, + "Ironstone": 2596, + "Bonesob": 2597, + "Steeldriver": 2598, + "Rixots Keen": 2599, + "Blood Crescent": 2600, + "Krintizs Skewer": 2601, + "Gleamscythe": 2602, + "Azurewrath": 2603, + "Griswolds Edge": 2604, + "Hellplague": 2605, + "Culwens Point": 2606, + "Shadowfang": 2607, + "Soulflay": 2608, + "Kinemils Awl": 2609, + "Blacktongue": 2610, + "Ripsaw": 2611, + "The Patriarch": 2612, + "Gull": 2613, + "The Diggler": 2614, + "The Jade Tan Do": 2615, + "Irices Shard": 2616, + "The Dragon Chang": 2617, + "Razortine": 2618, + "Bloodthief": 2619, + "Lance of Yaggai": 2620, + "The Tannr Gorerod": 2621, + "Dimoaks Hew": 2622, + "Steelgoad": 2623, + "Soul Harvest": 2624, + "The Battlebranch": 2625, + "Woestave": 2626, + "The Grim Reaper": 2627, + "Bane Ash": 2628, + "Serpent Lord": 2629, + "Lazarus Spire": 2630, + "The Salamander": 2631, + "The Iron Jang Bong": 2632, + "Pluckeye": 2633, + "Witherstring": 2634, + "Rimeraven": 2635, + "Piercerib": 2636, + "Pullspite": 2637, + "Wizendraw": 2638, + "Hellclap": 2639, + "Blastbark": 2640, + "Leadcrow": 2641, + "Ichorsting": 2642, + "Hellcast": 2643, + "Doomspittle": 2644, + "War Bonnet": 2645, + "Tarnhelm": 2646, + "Coif of Glory": 2647, + "Duskdeep": 2648, + "Wormskull": 2649, + "Howltusk": 2650, + "Undead Crown": 2651, + "The Face of Horror": 2652, + "Greyform": 2653, + "Blinkbats Form": 2654, + "The Centurion": 2655, + "Twitchthroe": 2656, + "Darkglow": 2657, + "Hawkmail": 2658, + "Sparking Mail": 2659, + "Venomsward": 2660, + "Iceblink": 2661, + "Boneflesh": 2662, + "Rockfleece": 2663, + "Rattlecage": 2664, + "Goldskin": 2665, + "Victors Silk": 2666, + "Heavenly Garb": 2667, + "Pelta Lunata": 2668, + "Umbral Disk": 2669, + "Stormguild": 2670, + "Wall of the Eyeless": 2671, + "Swordback Hold": 2672, + "Steelclash": 2673, + "Bverrit Keep": 2674, + "The Ward": 2675, + "The Hand of Broc": 2676, + "Bloodfist": 2677, + "Chance Guards": 2678, + "Magefist": 2679, + "Frostburn": 2680, + "Hotspur": 2681, + "Gorefoot": 2682, + "Treads of Cthon": 2683, + "Goblin Toe": 2684, + "Tearhaunch": 2685, + "Lenyms Cord": 2686, + "Snakecord": 2687, + "Nightsmoke": 2688, + "Goldwrap": 2689, + "Bladebuckle": 2690, + "Nokozan Relic": 2691, + "The Eye of Etlich": 2692, + "The Mahim-Oak Curio": 2693, + "Nagelring": 2694, + "Manald Heal": 2695, + "Gorgethroat": 2696, + "Amulet of the Viper": 2697, + "Staff of Kings": 2698, + "Horadric Staff": 2699, + "Hell Forge Hammer": 2700, + "The Stone of Jordan": 2701, + "GloomUM": 2702, + "Gray": 2703, + "DireUM": 2704, + "Black": 2705, + "ShadowUM": 2706, + "Haze": 2707, + "Wind": 2708, + "StormUM": 2709, + "Warp": 2710, + "Night": 2711, + "Moon": 2712, + "Star": 2713, + "Pit": 2714, + "Fire": 2715, + "Cold": 2716, + "Seethe": 2717, + "SharpUM": 2718, + "AshUM": 2719, + "Blade": 2720, + "SteelUM": 2721, + "StoneUM": 2722, + "Rust": 2723, + "Mold": 2724, + "Blight": 2725, + "Plague": 2726, + "Rot": 2727, + "Ooze": 2728, + "Puke": 2729, + "Snot": 2730, + "Bile": 2731, + "BloodUM": 2732, + "Pulse": 2733, + "Gut": 2734, + "Gore": 2735, + "FleshUM": 2736, + "BoneUM": 2737, + "SpineUM": 2738, + "Mind": 2739, + "SpiritUM": 2740, + "SoulUM": 2741, + "Wrath": 2742, + "GriefUM": 2743, + "Foul": 2744, + "Vile": 2745, + "Sin": 2746, + "ChaosUM": 2747, + "DreadUM": 2748, + "DoomUM": 2749, + "BaneUM": 2750, + "DeathUM": 2751, + "ViperUM": 2752, + "Dragon": 2753, + "Devil": 2754, + "touchUM": 2755, + "spellUM": 2756, + "feast": 2757, + "wound": 2758, + "grin": 2759, + "maim": 2760, + "hack": 2761, + "biteUM": 2762, + "rendUM": 2763, + "burn": 2764, + "rip": 2765, + "kill": 2766, + "callUM": 2767, + "vex": 2768, + "jade": 2769, + "web": 2770, + "shieldUM": 2771, + "KillerUM": 2772, + "RazorUM": 2773, + "drinker": 2774, + "shifter": 2775, + "crawler": 2776, + "dancer": 2777, + "bender": 2778, + "weaverUM": 2779, + "eater": 2780, + "widow": 2781, + "maggot": 2782, + "spawn": 2783, + "wight": 2784, + "GrumbleUM": 2785, + "GrowlerUM": 2786, + "SnarlUM": 2787, + "wolf": 2788, + "crow": 2789, + "raven": 2790, + "hawk": 2791, + "cloud": 2792, + "BangUM": 2793, + "head": 2794, + "skullUM": 2795, + "browUM": 2796, + "eyeUM": 2797, + "maw": 2798, + "tongue": 2799, + "fangUM": 2800, + "hornUM": 2801, + "thorn": 2802, + "clawUM": 2803, + "fistUM": 2804, + "heartUM": 2805, + "shankUM": 2806, + "skinUM": 2807, + "wingUM": 2808, + "pox": 2809, + "fester": 2810, + "blister": 3291, + "pus": 2812, + "SlimeUM": 2813, + "drool": 2814, + "froth": 2815, + "sludge": 2816, + "venom": 2817, + "poison": 2818, + "break": 2819, + "shard": 2820, + "flame": 2821, + "maul": 2822, + "thirstUM": 2823, + "lust": 2824, + "the Hammer": 2825, + "the Axe": 2826, + "the Sharp": 2827, + "the Jagged": 2828, + "the Flayer": 2829, + "the Slasher": 2830, + "the Impaler": 2831, + "the Hunter": 2832, + "the Slayer": 2833, + "the Mauler": 2834, + "the Destroyer": 2835, + "theQuick": 2836, + "the Witch": 2837, + "the Mad": 2838, + "the Wraith": 2839, + "the Shade": 2840, + "the Dead": 2841, + "the Unholy": 2842, + "the Howler": 2843, + "the Grim": 2844, + "the Dark": 2845, + "the Tainted": 2846, + "the Unclean": 2847, + "the Hungry": 2848, + "the Cold": 2849, + "The Cow King": 2850, + "Grand Vizier of Chaos": 2851, + "Lord De Seis": 2852, + "Infector of Souls": 2853, + "Riftwraith the Cannibal": 2854, + "Taintbreeder": 2855, + "The Tormentor": 2856, + "Winged Death": 2857, + "Maffer Dragonhand": 2858, + "Wyand Voidfinger": 2859, + "Toorc Icefist": 2860, + "Bremm Sparkfist": 2861, + "Geleb Flamefinger": 2862, + "Ismail Vilehand": 2863, + "Icehawk Riftwing": 2864, + "Sarina the Battlemaid": 2865, + "Stormtree": 2866, + "Witch Doctor Endugu": 2867, + "Web Mage the Burning": 2868, + "Bishibosh": 2869, + "Bonebreak": 2870, + "Coldcrow": 2871, + "Rakanishu": 2872, + "Treehead WoodFist": 2873, + "Griswold": 2874, + "The Countess": 2875, + "Pitspawn Fouldog": 2876, + "Flamespike the Crawler": 2877, + "Boneash": 2878, + "Radament": 2879, + "Bloodwitch the Wild": 2880, + "Fangskin": 2881, + "Beetleburst": 2882, + "Leatherarm": 2883, + "Coldworm the Burrower": 2884, + "Fire Eye": 2885, + "Dark Elder": 2886, + "The Summoner": 2887, + "Ancient Kaa the Soulless": 2888, + "The Smith": 2889, + "DeckardCain": 2890, + "Gheed": 2891, + "Akara": 2892, + "Kashya": 2893, + "Charsi": 2894, + "Wariv": 2895, + "Warriv": 2896, + "Rogue": 2897, + "StygianDoll": 2898, + "SoulKiller": 2899, + "Flayer": 2900, + "Fetish": 2901, + "RatMan": 2902, + "Undead StygianDoll": 2903, + "Undead SoulKiller": 2904, + "Undead Flayer": 2905, + "Undead Fetish": 2906, + "Undead RatMan": 2907, + "DarkFamiliar": 2908, + "BloodDiver": 2909, + "Gloombat": 2910, + "DesertWing": 2911, + "Banished": 2912, + "BloodLord": 2913, + "DarkLord": 2914, + "NightLord": 2915, + "GhoulLord": 2916, + "Spikefist": 2917, + "Thrasher": 2918, + "BrambleHulk": 2919, + "ThornedHulk": 2920, + "SpiderMagus": 2921, + "FlameSpider": 2922, + "PoisonSpinner": 2923, + "SandFisher": 2924, + "Arach": 2925, + "BloodWing": 2926, + "BloodHook": 2927, + "Feeder": 2928, + "Sucker": 2929, + "WingedNightmare": 2930, + "HellBuzzard": 2931, + "UndeadScavenger": 2932, + "CarrionBird": 2933, + "Unraveler": 2934, + "Guardian": 2935, + "HollowOne": 2936, + "Horadrim Ancient": 2937, + "AlbinoRoach": 2938, + "SteelWeevil": 2939, + "Scarab": 2940, + "SandWarrior": 2941, + "DungSoldier": 2942, + "HellSwarm": 2943, + "PlagueBugs": 2944, + "BlackLocusts": 2945, + "Itchies": 2946, + "HellCat": 2947, + "NightTiger": 2948, + "SaberCat": 2949, + "Huntress": 2950, + "RazorPitDemon": 2951, + "TreeLurker": 2952, + "CaveLeaper": 2953, + "TombCreeper": 2954, + "SandLeaper": 2955, + "TombViper": 2956, + "PitViper": 2957, + "Salamander": 2958, + "ClawViper": 2959, + "SerpentMagus": 2960, + "WorldKiller": 2961, + "GiantLamprey": 2962, + "Devourer": 2963, + "RockWorm": 2964, + "SandMaggot": 2965, + "JungleUrchin": 2966, + "RazorSpine": 2967, + "ThornBeast": 2968, + "SpikeFiend": 2969, + "QuillRat": 2970, + "HellClan": 2971, + "MoonClan": 2972, + "NightClan": 2973, + "DeathClan": 2974, + "BloodClan": 2975, + "TempleGuard": 2976, + "DoomApe": 2977, + "JungleHunter": 2978, + "RockDweller": 2979, + "DuneBeast": 2980, + "FleshHunter": 2981, + "BlackRogue": 2982, + "DarkStalker": 2983, + "VileHunter": 2984, + "DarkHunter": 2985, + "DarkShape": 2986, + "Apparition": 2987, + "Specter": 2988, + "Ghost": 2990, + "Assailant": 2991, + "Infidel": 2992, + "Invader": 2993, + "Marauder": 2994, + "SandRaider": 2995, + "GargantuanBeast": 2996, + "WailingBeast": 2997, + "Yeti": 2998, + "Crusher": 2999, + "Brute": 3000, + "CloudStalker": 3001, + "BlackVulture": 3002, + "BlackRaptor": 3003, + "BloodHawk": 3004, + "FoulCrow": 3005, + "PlagueBearer": 3006, + "Ghoul": 3007, + "DrownedCarcass": 3008, + "HungryDead": 3009, + "Zombie": 3010, + "Skeleton": 3011, + "Horror": 3012, + "Returned": 3013, + "BurningDead": 3014, + "BoneWarrior": 3015, + "Damned": 3016, + "Disfigured": 3017, + "Misshapen": 3018, + "Tainted": 3019, + "Afflicted": 3020, + "Andariel": 3021, + "Natalya": 3022, + "Drognan": 3023, + "Atma": 3024, + "Fara": 3025, + "Lysander": 3026, + "Jerhyn": 3027, + "jerhyn": 3028, + "Geglash": 3029, + "Elzix": 3030, + "Greiz": 3031, + "Meshif": 3032, + "Camel": 3033, + "Cadaver": 3034, + "PreservedDead": 3035, + "Embalmed": 3036, + "DriedCorpse": 3037, + "Decayed": 3038, + "Urdar": 3039, + "Mauler": 3040, + "Gorbelly": 3041, + "Blunderbore": 3042, + "WorldKillerYoung": 3043, + "GiantLampreyYoung": 3044, + "DevourerYoung": 3045, + "RockWormYoung": 3046, + "SandMaggotYoung": 3047, + "WorldKillerEgg": 3048, + "GiantLampreyEgg": 3049, + "DevourerEgg": 3050, + "RockWormEgg": 3051, + "SandMaggotEgg": 3052, + "Maggot": 3053, + "Duriel": 3054, + "BloodHawkNest": 3055, + "FlyingScimitar": 3056, + "CloudStalkerNest": 3057, + "BlackVultureNest": 3058, + "FoulCrowNest": 3059, + "Diablo": 3060, + "Baal": 3061, + "Mephisto": 3062, + "Cantor": 3063, + "Heirophant": 3064, + "Sexton": 3065, + "Zealot": 3066, + "Faithful": 3067, + "Zakarumite": 3068, + "BlackSoul": 3069, + "BurningSoul": 3070, + "SwampGhost": 3071, + "Gloam": 3072, + "WarpedShaman": 3073, + "DarkShaman": 3074, + "DevilkinShaman": 3075, + "CarverShaman": 3076, + "FallenShaman": 3077, + "WarpedFallen": 3078, + "DarkOne": 3079, + "Devilkin": 3080, + "Carver": 3081, + "Fallen": 3082, + "ReturnedArcher": 3083, + "HorrorArcher": 3084, + "BurningDeadArcher": 3085, + "BoneArcher": 3086, + "CorpseArcher": 3087, + "SkeletonArcher": 3088, + "FleshLancer": 3089, + "BlackLancer": 3090, + "DarkLancer": 3091, + "VileLancer": 3092, + "DarkSpearwoman": 3093, + "FleshArcher": 3094, + "BlackArcher": 3095, + "DarkRanger": 3096, + "VileArcher": 3097, + "DarkArcher": 3098, + "Summoner": 3099, + "StygianDollShaman": 3100, + "SoulKillerShaman": 3101, + "FlayerShaman": 3102, + "FetishShaman": 3103, + "RatManShaman": 3104, + "HorrorMage": 3105, + "BurningDeadMage": 3106, + "BoneMage": 3107, + "CorpseMage": 3108, + "ReturnedMage": 3109, + "GargoyleTrap": 3110, + "Bloodraven": 3111, + "navi": 3112, + "Kaelan": 3113, + "meshif": 3114, + "StygianWatcherHead": 3115, + "RiverStalkerHead": 3116, + "WaterWatcherHead": 3117, + "StygianWatcherLimb": 3118, + "RiverStalkerLimb": 3119, + "WaterWatcherLimb": 3120, + "NightMarauder": 3121, + "FireGolem": 3122, + "IronGolem": 3123, + "BloodGolem": 3124, + "ClayGolem": 3125, + "WorldKillerQueen": 3126, + "GiantLampreyQueen": 3127, + "DevourerQueen": 3128, + "RockWormQueen": 3129, + "SandMaggotQueen": 3130, + "Slime Prince": 3131, + "Bog Creature": 3132, + "Swamp Dweller": 3133, + "GiantUrchin": 3134, + "RazorBeast": 3135, + "ThornBrute": 3136, + "SpikeGiant": 3137, + "QuillBear": 3138, + "Council Member": 3139, + "youngdiablo": 3140, + "darkwanderer": 3141, + "HellSlinger": 3142, + "NightSlinger": 3143, + "SpearCat": 3144, + "Slinger": 3145, + "FireTower": 3146, + "LightningSpire": 3147, + "PitLord": 3148, + "Balrog": 3149, + "VenomLord": 3150, + "Iron Wolf": 3151, + "InvisoSpawner": 3152, + "OblivionKnight": 3153, + "Mage": 3154, + "AbyssKnight": 3155, + "Fighter Mage": 3156, + "DoomKnight": 3157, + "Fighter": 3158, + "MawFiend": 3159, + "CorpseSpitter": 3160, + "Corpulent": 3161, + "StormCaster": 3162, + "Strangler": 3163, + "Groper": 3164, + "GrotesqueWyrm": 3165, + "StygianDog": 3166, + "FleshBeast": 3167, + "Grotesque": 3168, + "StygianHag": 3169, + "FleshSpawner": 3170, + "RogueScout": 3171, + "BloodWingNest": 3172, + "BloodHookNest": 3173, + "FeederNest": 3174, + "SuckerNest": 3175, + "NecroMage": 3176, + "NecroSkeleton": 3177, + "TrappedSoul": 3178, + "Valkyrie": 3179, + "Dopplezon": 3180, + "Raises Fetishes": 3181, + "Raises Undead": 3182, + "Lays Eggs": 3183, + "Raises Fallen": 3184, + "heals Zealots and Cantors": 3185, + "drains mana and stamina": 3186, + "drains mana": 3187, + "drains stamina": 3188, + "stun attack": 3189, + "eats and spits corspes": 3190, + "homing missiles": 3191, + "raises Stygian Dolls": 3192, + "raises Soul Killers": 3193, + "raises Flayers": 3194, + "raises Fetishes": 3195, + "raises Ratmen": 3196, + "steals life": 3197, + "raises undead": 3198, + "raises Dark Ones": 3199, + "raises Devilkin": 3200, + "raises Carvers": 3201, + "raises Fallen": 3202, + "raises Warped Fallen": 3203, + "shocking hit": 3204, + "uniquextrastrong": 3205, + "uniqueextrafast": 3206, + "uniquecursed": 3207, + "uniquemagicresistance": 3208, + "uniquefireenchanted": 3209, + "monsteruniqueprop1": 3210, + "monsteruniqueprop2": 3211, + "monsteruniqueprop3": 3212, + "monsteruniqueprop4": 3213, + "monsteruniqueprop5": 3214, + "monsteruniqueprop6": 3215, + "monsteruniqueprop7": 3216, + "monsteruniqueprop8": 3217, + "monsteruniqueprop9": 3218, + "This Cow Bites": 3219, + "Champion": 3220, + "minion": 3221, + "Barrel": 3222, + "Lever1": 3223, + "BarrelEx": 3224, + "Door": 3225, + "Portal": 3226, + "ODoor": 3227, + "BlockedDoor": 3228, + "LockedDoor": 3229, + "StoneAlpha": 3230, + "StoneBeta": 3231, + "StoneDelta": 3232, + "StoneGamma": 3233, + "StoneLambda": 3234, + "StoneTheta": 3235, + "Crate": 3236, + "Casket": 3237, + "Cabinet": 3238, + "Vase": 3239, + "Inifuss": 3240, + "corpse": 3241, + "RogueCorpse": 3242, + "CorpseOnStick": 3243, + "TowerTome": 3244, + "Gibbet": 3245, + "MummyGenerator": 3246, + "ArmorStand": 3247, + "WeaponRack": 3248, + "Sarcophagus": 3249, + "Trap Door": 3250, + "LargeUrn": 3251, + "CanopicJar": 3252, + "Obelisk": 3253, + "HoleAnim": 3254, + "Shrine": 3255, + "Urn": 3256, + "Waypoint": 22526, + "Well": 3258, + "bag": 3259, + "Chest": 3260, + "chest": 3261, + "lockedchest": 3262, + "HorazonsJournal": 3263, + "templeshrine": 3264, + "stair": 3265, + "coffin": 3266, + "bookshelf": 3267, + "loose boulder": 3268, + "loose rock": 3269, + "hollow log": 3270, + "hiding spot": 3271, + "fire": 3328, + "Chest3": 3273, + "hidden stash": 3274, + "GuardCorpse": 3275, + "bowl": 3276, + "jug": 3277, + "AmbientSound": 3278, + "ratnest": 3279, + "burning body": 3280, + "well": 22525, + "door": 3282, + "skeleton": 3283, + "skullpile": 3284, + "cocoon": 3285, + "gidbinn altar": 3286, + "cowa": 3287, + "manashrine": 3288, + "bed": 3289, + "ratchest": 3290, + "bank": 3292, + "goo pile": 3293, + "holyshrine": 3294, + "teleportation pad": 3295, + "ratchest-r": 3296, + "skull pile": 3297, + "body": 3298, + "hell bridge": 3299, + "compellingorb": 3300, + "basket": 3301, + "Basket": 3302, + "RockPIle": 3303, + "Tome": 3304, + "dead body": 3305, + "eunuch": 3306, + "dead guard": 3307, + "portal": 3308, + "sarcophagus": 3309, + "dead villager": 3310, + "sewer lever": 3311, + "sewer stairs": 3312, + "magic shrine": 3313, + "wirt's body": 3314, + "stash": 3315, + "guyq": 3316, + "taintedsunaltar": 3317, + "Hellforge": 3318, + "Corpsefire": 3319, + "fissure": 3320, + "BoneChest": 3321, + "casket": 3322, + "HungSkeleton": 3323, + "pillar": 3324, + "Hydra": 3325, + "Turret": 3326, + "a trap": 3327, + "cost": 3329, + "Repair": 3330, + "Sell": 3331, + "Identify": 3332, + "priceless": 3333, + "NPCMenuTradeRepair": 3334, + "NPCPurchaseItems": 3335, + "NPCSellItems": 3336, + "NPCHeal": 3337, + "NPCRepairItems": 3338, + "NPCNextPage": 3339, + "NPCPreviousPage": 3340, + "strUiMenu2": 4131, + "TransactionMenu1a": 3342, + "TransactionMenu1f": 3343, + "VerifyTransaction1": 3344, + "VerifyTransaction2": 3345, + "VerifyTransaction3": 3346, + "VerifyTransaction4": 3347, + "VerifyTransaction5": 3348, + "VerifyTransaction6": 3349, + "VerifyTransaction7": 3350, + "VerifyTransaction8": 3351, + "VerifyTransaction9": 3352, + "TransactionResults1": 3353, + "TransactionResults2": 3354, + "TransactionResults3": 3355, + "TransactionResults4": 3356, + "TransactionResults5": 3357, + "TransactionResults6": 3358, + "TransactionResults7": 3359, + "TransactionResults8": 3360, + "TransactionResults9": 3361, + "TransactionResults10": 3362, + "TransactionResults11": 3363, + "ItemDesc1s": 3364, + "ItemDesc1t": 3365, + "HP": 3366, + "AC": 3367, + "Level": 3368, + "Cost": 3369, + "Damage": 3370, + "strhirespecial1": 3371, + "strhirespecial2": 3372, + "strhirespecial3": 3373, + "strhirespecial4": 3374, + "strhirespecial5": 3375, + "strhirespecial6": 3376, + "strhirespecial7": 3377, + "strhirespecial8": 3378, + "strhirespecial9": 3379, + "strhirespecial10": 3380, + "TalkMenu": 3381, + "WarrivMenu1b": 3382, + "WarrivMenu1c": 3383, + "MeshifMenuEast": 3384, + "MeshifMenuWest": 3385, + "NPCMenuNews0": 3386, + "NPCMenuNews1": 3387, + "NPCMenuNews2": 3388, + "NPCMenuNews3": 3389, + "NPCMenuNews4": 3390, + "NPCMenuTalkMore": 3391, + "NPCTownMore0": 3392, + "NPCTownMore1": 3393, + "NPCMenuLeave": 3394, + "NPCGossipMenu": 3395, + "NPCMenuTrade": 3396, + "NPCMenuHire": 3397, + "gamble": 3398, + "Intro": 3399, + "Back": 3400, + "ok": 3401, + "cancel": 3402, + "Continue": 3403, + "strMenuMain15": 3404, + "strOptMusic": 3405, + "strOptSound": 3406, + "strOptGamma": 3407, + "strOptRender": 3408, + "strOptPrevious": 3409, + "cfgCtrl": 3410, + "merc01": 3411, + "merc02": 3412, + "merc03": 3413, + "merc04": 3414, + "merc05": 3415, + "merc06": 3416, + "merc07": 3417, + "merc08": 3418, + "merc09": 3419, + "merc10": 3420, + "merc11": 3421, + "merc12": 3422, + "merc13": 3423, + "merc14": 3424, + "merc15": 3425, + "merc16": 3426, + "merc17": 3427, + "merc18": 3428, + "merc19": 3429, + "merc20": 3430, + "merc21": 3431, + "merc22": 3432, + "merc23": 3433, + "merc24": 3434, + "merc25": 3435, + "merc26": 3436, + "merc27": 3437, + "merc28": 3438, + "merc29": 3439, + "merc30": 3440, + "merc31": 3441, + "merc32": 3442, + "merc33": 3443, + "merc34": 3444, + "merc35": 3445, + "merc36": 3446, + "merc37": 3447, + "merc38": 3448, + "merc39": 3449, + "merc40": 3450, + "merc41": 3451, + "merclevelup": 3452, + "Socketable": 3453, + "ItemStats1a": 3454, + "ItemStats1b": 3455, + "ItemStats1c": 3456, + "ItemStats1d": 3457, + "ItemStats1e": 3458, + "ItemStats1f": 3459, + "ItemStats1g": 3460, + "ItemStats1h": 3461, + "ItemStats1i": 3462, + "ItemStats1j": 3463, + "ItemStast1k": 3464, + "ItemStats1l": 3465, + "ItemStats1m": 3466, + "ItemStats1n": 3467, + "ItemStats1o": 3468, + "ItemStats1p": 3469, + "ItemStats1q": 3470, + "ItemStatsrejuv1": 3471, + "ItemStatsrejuv2": 3472, + "ModStr1a": 3473, + "ModStr1b": 3474, + "ModStr1c": 3475, + "ModStr1d": 3476, + "ModStr1e": 3477, + "ModStr1f": 3478, + "ModStr1g": 3479, + "ModStr1h": 3480, + "ModStr1i": 3481, + "ModStr1j": 3482, + "ModStr1k": 3483, + "ModStr1l": 3484, + "ModStr1m": 3485, + "ModStr1n": 3486, + "ModStr1o": 3487, + "ModStr1p": 3488, + "ModStr1q": 3489, + "ModStr1r": 3490, + "ModStr1s": 3491, + "ModStr1t": 3492, + "ModStr1u": 3493, + "ModStr1v": 3494, + "ModStr1w": 3495, + "ModStr1x": 3496, + "ModStr1y": 3497, + "ModStr1z": 3498, + "ModStr2a": 3499, + "ModStr2b": 3500, + "ModStr2c": 3501, + "ModStr2d": 3502, + "ModStr2e": 3503, + "ModStr2f": 3504, + "ModStr2g": 3505, + "ModStr2h": 3506, + "ModStr2i": 3507, + "ModStr2j": 3508, + "ModStr2k": 3509, + "ModStr2l": 3510, + "ModStr2m": 3511, + "ModStr2n": 3512, + "ModStr2o": 3513, + "ModStr2p": 3514, + "ModStr2q": 3515, + "ModStr2r": 3516, + "ModStr2s": 3517, + "ModStr2t": 3518, + "ModStr2u": 3519, + "Modstr2v": 3520, + "ModStr2w": 3521, + "ModStr2x": 3522, + "ModStr2y": 3523, + "ModStr2z": 3524, + "ModStr3a": 3525, + "ModStr3b": 3526, + "ModStr3c": 3527, + "ModStr3d": 3528, + "ModStr3e": 3529, + "ModStr3f": 3530, + "ModStr3g": 3531, + "ModStr3h": 3532, + "ModStr3i": 3533, + "ModStr3j": 3534, + "ModStr3k": 3535, + "ModStr3l": 3536, + "ModStr3m": 3537, + "ModStr3n": 3538, + "ModStr3o": 3539, + "ModStr3p": 3540, + "ModStr3q": 3541, + "ModStr3r": 3542, + "ModStr3u": 3543, + "ModStr3v": 3544, + "ModStr3w": 3545, + "ModStr3x": 3546, + "ModStr3y": 3547, + "ModStr3z": 3548, + "ModStr4a": 3549, + "ModStr4b": 3550, + "ModStr4c": 3551, + "ModStr4d": 3552, + "ModStr4e": 3553, + "ModStr4f": 3554, + "ModStr4g": 3555, + "ModStr4h": 3556, + "ModStr4i": 3557, + "ModStr4j": 3558, + "ModStr4k": 3559, + "ModStr4l": 3560, + "ModStr4m": 3561, + "ModStr4n": 3562, + "ModStr4o": 3563, + "ModStr4p": 3564, + "ModStr4q": 3565, + "ModStr4r": 3566, + "ModStr4s": 3567, + "ModStr4t": 3568, + "ModStr4u": 3569, + "ModStr4v": 3570, + "ModStr4w": 3571, + "ModStr4x": 3572, + "ModStr4y": 3573, + "ModStr4z": 3574, + "ModStr5a": 3575, + "ModStr5b": 3576, + "ModStr5c": 3577, + "ModStr5d": 3578, + "ModStr5e": 3579, + "ModStr5f": 3580, + "ModStr5g": 3581, + "ModStr5h": 3582, + "ModStr5i": 3583, + "ModStr5j": 3584, + "ModStr5k": 3585, + "ModStr5l": 3586, + "ModStr5m": 3587, + "ModStr5n": 3588, + "ModStr5o": 3589, + "ModStr5p": 3590, + "ModStr5q": 3591, + "ModStr5r": 3592, + "ModStr5s": 3593, + "ModStr5t": 3594, + "ModStr5u": 3595, + "ModStr5v": 3596, + "ModStr5w": 3597, + "ModStr5x": 3598, + "ModStr5y": 3599, + "ModStr5z": 3600, + "ModStr6a": 3601, + "ModStr6b": 3602, + "ModStr6c": 3603, + "ModStr6d": 3604, + "ModStr6e": 3605, + "ModStr6f": 3606, + "ModStr6g": 3607, + "ModStr6h": 3608, + "ModStr6i": 3609, + "strModAllResistances": 3610, + "strModAllSkillLevels": 3611, + "strModFireDamage": 3612, + "strModFireDamageRange": 3613, + "strModColdDamage": 3614, + "strModColdDamageRange": 3615, + "strModLightningDamage": 3616, + "strModLightningDamageRange": 3617, + "strModMagicDamage": 3618, + "strModMagicDamageRange": 3619, + "strModPoisonDamage": 3620, + "strModPoisonDamageRange": 3621, + "strModMinDamage": 3622, + "strModMinDamageRange": 3623, + "strModEnhancedDamage": 3624, + "improved damage": 3625, + "improved to hit": 3626, + "improved armor class": 3627, + "improved durability": 3628, + "Quick Strike": 3629, + "strGemPlace1": 3630, + "strGemPlace2": 3631, + "gemeffect1": 3632, + "gemeffect2": 3633, + "gemeffect3": 3634, + "gemeffect4": 3635, + "gemeffect5": 3636, + "gemeffect6": 3637, + "gemeffect7": 3638, + "sysmsg1": 3639, + "sysmsg2": 3640, + "sysmsg3": 3641, + "sysmsg4": 3642, + "sysmsg3a": 3643, + "sysmsg4a": 3644, + "sysmsg5": 3645, + "sysmsg6": 3646, + "sysmsg7": 3647, + "sysmsg8": 3648, + "sysmsg9": 3649, + "sysmsg10": 3650, + "sysmsg11": 3651, + "sysmsg12": 3652, + "sysmsgPlayer": 3653, + "chatmsg1": 3654, + "chatmsg2": 3655, + "chatmsg3": 3657, + "strwhisperworked": 3658, + "syswork": 3659, + "ShrId0": 3660, + "ShrId1": 3661, + "ShrId2": 3662, + "ShrId3": 3663, + "ShrId4": 3664, + "ShrId5": 3665, + "ShrId6": 3666, + "ShrId7": 3667, + "ShrId8": 3668, + "ShrId9": 3669, + "ShrId10": 3670, + "ShrId11": 3671, + "ShrId12": 3672, + "ShrId13": 3673, + "ShrId14": 3674, + "ShrId15": 3675, + "ShrId16": 3676, + "ShrId17": 3677, + "ShrId18": 3678, + "ShrId19": 3679, + "ShrId20": 3680, + "ShrId21": 3681, + "ShrId22": 3682, + "ShrMsg0": 3683, + "ShrMsg1": 3684, + "ShrMsg2": 3685, + "ShrMsg3": 3686, + "ShrMsg4": 3687, + "ShrMsg5": 3688, + "ShrMsg6": 3689, + "ShrMsg7": 3690, + "ShrMsg8": 3691, + "ShrMsg9": 3692, + "ShrMsg10": 3693, + "ShrMsg11": 3694, + "ShrMsg12": 3695, + "ShrMsg13": 3696, + "ShrMsg14": 3697, + "ShrMsg15": 3698, + "ShrMsg16": 3699, + "ShrMsg17": 3700, + "ShrMsg18": 3701, + "ShrMsg19": 3702, + "ShrMsg20": 3703, + "ShrMsg21": 3704, + "ShrMsg22": 3705, + "strqi1": 3706, + "strqi2": 3707, + "stsa1q3alert": 3708, + "stsa1q4alert": 3709, + "stsa3q1alert": 3710, + "qstsa1qt": 3711, + "qstsa1qt0": 3712, + "qstsa1q0": 3713, + "qstsa1q1": 3714, + "qstsa1q2": 3715, + "qstsa1q3": 3716, + "qstsa1q4": 3717, + "qstsa1q5": 3718, + "qstsa1q6": 3719, + "strplaylast": 3720, + "newquestlog": 3721, + "qsts": 3722, + "noactivequest": 3723, + "qstsxxx": 3724, + "qstsnull": 3725, + "qstsComplete": 3726, + "qstsother": 3727, + "qstsprevious": 3728, + "qstsThankYouComeAgain": 3729, + "qstsThankYouComeAgainMulti": 3730, + "qstsThankYouComeAgainSingle": 3731, + "Qstsyouarenot8": 3732, + "qstsa1q3x": 3733, + "qstsa1q4x": 3734, + "qstsa1q11": 3735, + "qstsa1q12": 3736, + "qstsa1q13": 3737, + "qstsa1q14": 3738, + "qstsa1q140": 3739, + "qstsa1q15": 3740, + "qstsa1q21": 3741, + "qstsa1q22": 3742, + "qstsa1q23": 3743, + "qstsa1q41": 3744, + "qstsa1q42": 3745, + "qstsa1q43": 3746, + "qstsa1q44": 3747, + "qstsa1q45": 3748, + "qstsa1q46": 3749, + "qstsa1q46b": 3750, + "qstsa1q51": 3751, + "qstsa1q51a": 3752, + "qstsa1q51b": 3753, + "qstsa1q52": 3754, + "qstsa1q31": 3755, + "qstsa1q32": 3756, + "qstsa1q32b": 3757, + "qstsa1q61": 3758, + "qstsa1q62": 3759, + "qstsa1q62b": 3760, + "qstsa1q63": 3761, + "KeyNone": 3762, + "KeyLButton": 3763, + "KeyRButton": 3764, + "KeyCancel": 3765, + "KeyMButton": 3766, + "Key4Button": 3767, + "Key5Button": 3768, + "KeyWheelUp": 3769, + "KeyWheelDown": 3770, + "KeyKana": 3771, + "KeyJunja": 3772, + "KeyFinal": 3773, + "KeyKanji": 3774, + "KeyEscape": 3775, + "KeyConvert": 3776, + "KeyNonConvert": 3777, + "KeyAccept": 3778, + "KeyModeChange": 3779, + "KeyLeft": 3780, + "KeyUp": 3781, + "KeyRight": 3782, + "KeyDown": 3783, + "KeySelect": 3784, + "KeyExecute": 3785, + "KeyLWin": 3786, + "KeyRWin": 3787, + "KeyApps": 3788, + "KeyNumLock": 3789, + "KeyBack": 3790, + "KeyTab": 3791, + "KeyClear": 3792, + "KeyReturn": 3793, + "KeyShift": 3794, + "KeyControl": 3795, + "KeyMenu": 3796, + "KeyPause": 3797, + "KeyCapital": 3798, + "KeySpace": 3799, + "KeyPrior": 3800, + "KeyNext": 3801, + "KeyEnd": 3802, + "KeyHome": 3803, + "KeyPrint": 3804, + "KeySnapshot": 3805, + "KeyInsert": 3806, + "KeyDelete": 3807, + "KeyHelp": 3808, + "KeyNumPad0": 3809, + "KeyNumPad1": 3810, + "KeyNumPad2": 3811, + "KeyNumPad3": 3812, + "KeyNumPad4": 3813, + "KeyNumPad5": 3814, + "KeyNumPad6": 3815, + "KeyNumPad7": 3816, + "KeyNumPad8": 3817, + "KeyNumPad9": 3818, + "KeyMultiply": 3819, + "KeyAdd": 3820, + "KeySeparator": 3821, + "KeySubtract": 3822, + "KeyDecimal": 3823, + "KeyDivide": 3824, + "KeyF1": 3825, + "KeyF2": 3826, + "KeyF3": 3827, + "KeyF4": 3828, + "KeyF5": 3829, + "KeyF6": 3830, + "KeyF7": 3831, + "KeyF8": 3832, + "KeyF9": 3833, + "KeyF10": 3834, + "KeyF11": 3835, + "KeyF12": 3836, + "KeyF13": 3837, + "KeyF14": 3838, + "KeyF15": 3839, + "KeyF16": 3840, + "KeyF17": 3841, + "KeyF18": 3842, + "KeyF19": 3843, + "KeyF20": 3844, + "KeyF21": 3845, + "KeyF22": 3846, + "KeyF23": 3847, + "KeyF24": 3848, + "KeyScroll": 3849, + "KeySemicolon": 3850, + "KeyEqual": 3851, + "KeyComma": 3852, + "KeyMinus": 3853, + "KeyPeriod": 3854, + "KeySlash": 3855, + "KeyTilde": 3856, + "KeyLBracket": 3857, + "KeyBackslash": 3858, + "KeyRBracket": 3859, + "KeyApostrophe": 3860, + "ShorthandKeyMButton": 3861, + "ShorthandKey4Button": 3862, + "ShorthandKey5Button": 3863, + "ShorthandKeyWheelUp": 3864, + "ShorthandKeyWheelDown": 3865, + "ShorthandKeyKana": 3866, + "ShorthandKeyJunja": 3867, + "ShorthandKeyFinal": 3868, + "ShorthandKeyKanji": 3869, + "ShorthandKeyEscape": 3870, + "ShorthandKeyConvert": 3871, + "ShorthandKeyNonConvert": 3872, + "ShorthandKeyAccept": 3873, + "ShorthandKeyModeChange": 3874, + "ShorthandKeyLeft": 3875, + "ShorthandKeyRight": 3876, + "ShorthandKeyDown": 3877, + "ShorthandKeySelect": 3878, + "ShorthandKeyExecute": 3879, + "ShorthandKeyLeftWindows": 3880, + "ShorthandKeyRightWindows": 3881, + "ShorthandKeyApps": 3882, + "ShorthandKeyNumLock": 3883, + "ShorthandKeyBackspace": 3884, + "ShorthandKeyClear": 3885, + "ShorthandKeyEnter": 3886, + "ShorthandKeyShift": 3887, + "ShorthandKeyControl": 3888, + "ShorthandKeyPause": 3889, + "ShorthandKeyCapsLock": 3890, + "ShorthandKeySpace": 3891, + "ShorthandKeyPageUp": 3892, + "ShorthandKeyPageDown": 3893, + "ShorthandKeyHome": 3894, + "ShorthandKeyPrintScreen": 3895, + "ShorthandKeyInsert": 3896, + "ShorthandKeyDelete": 3897, + "ShorthandKeyHelp": 3898, + "ShorthandKeyNumPad0": 3899, + "ShorthandKeyNumPad1": 3900, + "ShorthandKeyNumPad2": 3901, + "ShorthandKeyNumPad3": 3902, + "ShorthandKeyNumPad4": 3903, + "ShorthandKeyNumPad5": 3904, + "ShorthandKeyNumPad6": 3905, + "ShorthandKeyNumPad7": 3906, + "ShorthandKeyNumPad8": 3907, + "ShorthandKeyNumPad9": 3908, + "ShorthandKeyNumPad*\tnp*": 3909, + "ShorthandKeyNumPad+\tnp+": 3910, + "ShorthandKeyNumPad-\tnp-": 3911, + "ShorthandKeyNumPad.\tnp.": 3912, + "ShorthandKeyNumPad/\tnp/": 3913, + "ShorthandKeyScroll": 3914, + "KeyMacOption": 3915, + "KeyMacCommand": 3916, + "KeyMacNumPad=\tNum Pad =": 3917, + "ShorthandKeyMacOption": 3918, + "ShorthandKeyMacCommand": 3919, + "ShorthandKeyMacNumPad=\tNP=": 3920, + "CfgFunction": 3921, + "CfgPrimaryKey": 3922, + "CfgSecondaryKey": 3923, + "CfgCharacter": 3924, + "CfgInventory": 3925, + "CfgParty": 3926, + "CfgMessageLog": 3927, + "CfgQuestLog": 3928, + "CfgChat": 3929, + "CfgAutoMap": 3930, + "CfgAutoMapCenter": 3931, + "CfgMiniMap": 3932, + "CfgHelp": 3933, + "CfgSkillTree": 3934, + "CfgSkillPick": 3935, + "CfgSkill1": 3936, + "CfgSkill2": 3937, + "CfgSkill3": 3938, + "CfgSkill4": 3939, + "CfgSkill5": 3940, + "CfgSkill6": 3941, + "CfgSkill7": 3942, + "CfgSkill8": 3943, + "Cfgskillup": 3944, + "Cfgskilldown": 3945, + "CfgBeltShow": 3946, + "CfgBelt1": 3947, + "CfgBelt2": 3948, + "CfgBelt3": 3949, + "CfgBelt4": 3950, + "CfgBelt5": 3951, + "CfgBelt6": 3952, + "CfgBelt7": 3953, + "CfgBelt8": 3954, + "CfgBelt9": 3955, + "CfgBelt10": 3956, + "CfgBelt11": 3957, + "CfgBelt12": 3958, + "CfgSay0": 3959, + "CfgSay1": 3960, + "CfgSay2": 3961, + "CfgSay3": 3962, + "CfgSay4": 3963, + "CfgSay5": 3964, + "CfgSay6": 3965, + "CfgRun": 3966, + "CfgRunLock": 3967, + "CfgStandStill": 3968, + "CfgShowItems": 3969, + "CfgClearScreen": 3970, + "CfgSnapshot": 3971, + "CfgDefault": 3972, + "CfgAccept": 3973, + "CfgCancel": 3974, + "strNoKeysAssigned": 3975, + "KeysAssigned": 3976, + "CantAssignMB": 3977, + "CantAssignMW": 3978, + "CantAssignKey": 3979, + "CfgClearKey": 3980, + "Cfgcleartextmsg": 3981, + "CfgTogglePortraits": 3982, + "CfgAutoMapFade": 3983, + "CfgAutoMapNames": 3984, + "CfgAutoMapParty": 3985, + "strlvlup": 3986, + "strnewskl": 3987, + "warpsheader": 3988, + "nowarps": 3989, + "waypointsheader": 3990, + "nowaypoints": 3991, + "max": 3992, + "MAX": 3993, + "colorcode": 3994, + "space": 3995, + "dash": 3996, + "colon": 3997, + "newline": 3998, + "pipe": 3999, + "slash": 4000, + "percent": 4001, + "plus": 4002, + "to": 4003, + "srostertitle": 4004, + "dwell": 4005, + "larva": 4006, + "Barbarian": 4007, + "Paladin": 4008, + "Necromancer": 4009, + "Sorceress": 4010, + "Amazon": 4011, + "druidstr \tDruid": 4012, + "assassinstr": 4013, + "Nest": 4014, + "NoParty": 4015, + "ItsMyParty": 4016, + "Upgrade": 4017, + "upgraderestrict": 4018, + "Use": 4019, + "NPCIdentify1": 4020, + "NPCIdentify2": 4021, + "strCannotDoThisToUnknown": 4022, + "Body Looted": 4023, + "Party1": 4024, + "Party2": 4025, + "Party3": 4026, + "Party4": 4027, + "Party5": 4028, + "Party6": 4029, + "Party7": 4030, + "Party8": 4031, + "Party9": 4032, + "strDropGoldHowMuch": 4033, + "strDropGoldInfo": 4034, + "strMsgLog": 4035, + "strBSArmor": 4036, + "strBSWeapons": 4037, + "strBSMagic": 4038, + "strBSMisc": 4039, + "strTrade": 4040, + "strTradeAccept": 4041, + "strTradeAgreeTo": 4042, + "strWaitingForOtherPlayer": 4043, + "strTradeBusy": 4044, + "strTradeTooFull": 4045, + "strTradeGoldHowMuch": 4046, + "strTradeTimeout": 4047, + "SysmsgPlayer1": 4048, + "strBankGoldDeposit": 4049, + "strBankGoldWithdraw": 4050, + "GoldMax": 4051, + "StrUI0": 4052, + "StrUI1": 4053, + "StrUI2": 4054, + "StrUI3": 4055, + "StrUI4": 4056, + "strchrlvl": 4057, + "strchrexp": 4058, + "strchrnxtlvl": 4059, + "strchrstr": 4060, + "strchrskm": 4061, + "strchrdex": 4062, + "strchratr": 4063, + "strchrdef": 4064, + "strchrrat": 4065, + "strchrvit": 4066, + "strchrstm": 4067, + "strchrlif": 4068, + "strchreng": 4069, + "strchrman": 4070, + "strchrfir": 4071, + "strchrcol": 4072, + "strchrlit": 4073, + "strchrpos": 4074, + "strchrstat": 4075, + "strchrrema": 4076, + "WeaponDescMace": 4077, + "WeaponDescAxe": 4078, + "WeaponDescSword": 4079, + "WeaponDescDagger": 4080, + "WeaponDescThrownPotion": 4081, + "WeaponDescJavelin": 4082, + "WeaponDescSpear": 4083, + "WeaponDescBow": 4084, + "WeaponDescStaff": 4085, + "WeaponDescPoleArm": 4086, + "WeaponDescCrossBow": 4087, + "WeaponAttackFastest": 4088, + "WeaponAttackVeryFast": 4089, + "WeaponAttackFast": 4090, + "WeaponAttackNormal": 4091, + "WeaponAttackSlow": 4092, + "WeaponAttackVerySlow": 4093, + "WeaponAttackSlowest": 4094, + "strNecromanerOnly": 4095, + "strPaladinOnly": 4096, + "strSorceressOnly": 4097, + "strMaceSpecialDamage": 4098, + "strGoldLabel": 4099, + "strParty1": 4100, + "strParty2": 4101, + "strParty3": 4102, + "strParty4": 4103, + "strParty5": 4104, + "strParty6": 4105, + "strParty7": 4106, + "strParty8": 4107, + "strParty9": 4108, + "strParty10": 4109, + "strParty11": 4110, + "strParty12": 4111, + "strParty13": 4112, + "strParty14": 4113, + "strParty15": 4114, + "strParty16": 4115, + "strParty17": 4116, + "strParty18": 4117, + "strParty19": 4118, + "strParty22": 4119, + "strParty24": 4120, + "strParty25": 4121, + "StrParty26": 4122, + "StrParty27": 4123, + "strGoldWithdraw": 4124, + "strGoldDrop": 4125, + "strGoldDeposit": 4126, + "strGoldTrade": 4127, + "strGoldInStash": 4128, + "strGoldTradepup": 4129, + "strUiMenu1": 4130, + "strUiBank": 4132, + "strUnknownTomb": 4133, + "strTradeOtherBox": 4134, + "strTradeBox": 4135, + "strFree": 4136, + "act1": 4137, + "act2": 4138, + "act3": 4139, + "act4": 4140, + "level": 4141, + "lowercasecancel": 4142, + "close": 4143, + "strClose": 4144, + "Lightning Spell": 4145, + "Fire Spell": 4146, + "Cold Spell": 4147, + "Yourparty": 4148, + "Inparty": 4149, + "Invite": 4150, + "Accept": 4151, + "Leave": 4152, + "Partyclose": 4153, + "partycharama": 4154, + "partycharsor": 4155, + "partycharbar": 4156, + "partycharnec": 4157, + "partycharpal": 4158, + "charavghit": 4159, + "charmonster": 4160, + "charmontohit1": 4161, + "charmontohit2": 4162, + "panelexp": 4163, + "panelstamina": 4164, + "panelhealth": 4165, + "panelmana": 4166, + "panelmini": 4167, + "panelcmini": 4168, + "minipanelchar": 4169, + "minipanelinv": 4170, + "minipaneltree": 4171, + "minipanelparty": 4172, + "minipanelautomap": 4173, + "minipanelmessage": 4174, + "minipanelquest": 4175, + "minipanelmenubtn": 4176, + "minipanelHelp": 4177, + "minipanelspecial": 4178, + "RunOn": 4179, + "RunOff": 4180, + "automapgame": 4181, + "automappw": 4182, + "automapdif": 4183, + "scrollbooktext": 4184, + "skilldesc1": 4185, + "skilldesc2": 4186, + "skilldesc3": 4187, + "skilldesc4": 4188, + "strpanel1": 4189, + "strpanel2": 4190, + "strpanel3": 4191, + "strpanel4": 4192, + "strpanel5": 4193, + "strpanel6": 4194, + "strpanel7": 4195, + "strpanel8": 4196, + "stashfull": 4197, + "Strhelp1": 4198, + "StrHelp2": 4199, + "StrHelp3": 4200, + "StrHelp4": 4201, + "StrHelp5": 4202, + "StrHelp6": 4203, + "StrHelp7": 4204, + "StrHelp8": 4205, + "StrHelp8a": 4206, + "StrHelp9": 4207, + "StrHelp10": 4208, + "StrHelp11": 4209, + "StrHelp12": 4210, + "StrHelp13": 4211, + "StrHelp14": 4212, + "StrHelp14a": 4213, + "StrHelp15": 4214, + "StrHelp16": 4215, + "StrHelp16a": 4216, + "StrHelp17": 4217, + "StrHelp18": 4218, + "StrHelp19": 4219, + "StrHelp20": 4220, + "StrHelp21": 4221, + "StrHelp22": 4222, + "strSklTree": 4223, + "StrSklTreea": 4224, + "StrSklTreeb": 4225, + "StrSklTreec": 4226, + "StrSklTree1": 4227, + "StrSklTree2": 4228, + "StrSklTree3": 4229, + "StrSklTree4": 4230, + "StrSklTree5": 4231, + "StrSklTree6": 4232, + "StrSklTree7": 4233, + "StrSklTree8": 4234, + "StrSklTree9": 4235, + "StrSklTree10": 4236, + "StrSklTree11": 4237, + "StrSklTree12": 4238, + "StrSklTree13": 4239, + "StrSklTree14": 4240, + "StrSklTree15": 4241, + "StrSklTree16": 4242, + "StrSklTree17": 4243, + "StrSklTree18": 4244, + "StrSklTree19": 4245, + "StrSklTree20": 4246, + "StrSklTree21": 4247, + "StrSklTree22": 4248, + "StrSklTree23": 4249, + "StrSklTree24": 4250, + "StrSklTree25": 4251, + "StrSkill0": 4252, + "StrSkill1": 4253, + "StrSkill2": 4254, + "StrSkill3": 4255, + "StrSkill4": 4256, + "StrSkill5": 4257, + "StrSkill6": 4258, + "StrSkill7": 4259, + "StrSkill8": 4260, + "StrSkill9": 4261, + "StrSkill10": 4262, + "StrSkill11": 4263, + "StrSkill12": 4264, + "StrSkill13": 4265, + "StrSkill14": 4266, + "StrSkill15": 4267, + "StrSkill16": 4268, + "StrSkill17": 4269, + "StrSkill18": 4270, + "StrSkill19": 4271, + "StrSkill20": 4272, + "StrSkill21": 4273, + "StrSkill22": 4274, + "StrSkill23": 4275, + "StrSkill24": 4276, + "StrSkill25": 4277, + "StrSkill26": 4278, + "StrSkill27": 4279, + "StrSkill28": 4280, + "StrSkill29": 4281, + "StrSkill30": 4282, + "StrSkill31": 4283, + "StrSkill32": 4284, + "StrSkill33": 4285, + "StrSkill34": 4286, + "StrSkill35": 4287, + "StrSkill36": 4288, + "StrSkill37": 4289, + "StrSkill38": 4290, + "StrSkill39": 4291, + "StrSkill40": 4292, + "StrSkill41": 4293, + "StrSkill42": 4294, + "StrSkill43": 4297, + "StrSkill44": 4298, + "StrSkill45": 4299, + "StrSkill46": 4300, + "StrSkill47": 4301, + "StrSkill48": 4302, + "StrSkill49": 4303, + "StrSkill50": 4304, + "StrSkill51": 4305, + "StrSkill52": 4306, + "StrSkill53": 4307, + "StrSkill54": 4308, + "StrSkill55": 4309, + "StrSkill56": 4310, + "StrSkill57": 4311, + "StrSkill58": 4312, + "StrSkill59": 4313, + "StrSkill60": 4314, + "StrSkill61": 4315, + "StrSkill62": 4316, + "StrSkill63": 4317, + "StrSkill64": 4318, + "StrSkill65": 4319, + "StrSkill66": 4320, + "StrSkill67": 4321, + "StrSkill68": 4322, + "StrSkill69": 4323, + "StrSkill70": 4324, + "StrSkill71": 4325, + "StrSkill72": 4326, + "StrSkill73": 4327, + "StrSkill74": 4328, + "StrSkill75": 4329, + "StrSkill76": 4330, + "StrSkill77": 4331, + "StrSkill78": 4332, + "StrSkill79": 4333, + "StrSkill80": 4334, + "StrSkill81": 4335, + "StrSkill82": 4336, + "StrSkill83": 4337, + "StrSkill84": 4338, + "StrSkill85": 4339, + "StrSkill86": 4340, + "StrSkill87": 4341, + "StrSkill88": 4342, + "StrSkill89": 4343, + "StrSkill90": 4344, + "StrSkill91": 4345, + "StrSkill92": 4346, + "StrSkill94": 4347, + "StrSkill95": 4348, + "StrSkill96": 4349, + "StrSkill97": 4350, + "StrSkill98": 4351, + "StrSkill99": 4352, + "StrSkill100": 4353, + "StrSkill101": 4354, + "StrSkill102": 4355, + "StrSkill103": 4356, + "StrSkill104": 4357, + "StrSkill105": 4358, + "StrSkill106": 4359, + "StrSkill107": 4360, + "StrSkill108": 4361, + "StrSkill109": 4362, + "StrSkill110": 4363, + "StrSkill111": 4364, + "StrSkill112": 4365, + "StrSkill113": 4366, + "StrSkill114": 4367, + "StrSkill115": 4368, + "StrSkill116": 4369, + "StrSkill117": 4370, + "StrSkill118": 4371, + "StrSkill119": 4372, + "skillname0": 4373, + "skillsd0": 4374, + "skillld0": 4375, + "skillan0": 4376, + "skillname1": 4377, + "skillsd1": 4378, + "skillld1": 4379, + "skillan1": 4380, + "skillname2": 4381, + "skillsd2": 4382, + "skillld2": 4383, + "skillan2": 4384, + "skillname3": 4385, + "skillsd3": 4386, + "skillld3": 4387, + "skillan3": 4388, + "skillname4": 4389, + "skillsd4": 4390, + "skillld4": 4391, + "skillan4": 4392, + "skillname5": 4393, + "skillsd5": 4394, + "skillld5": 4395, + "skillan5": 4396, + "skillname6": 4397, + "skillsd6": 4398, + "skillld6": 4399, + "skillan6": 4400, + "skillname7": 4401, + "skillsd7": 4402, + "skillld7": 4403, + "skillan7": 4404, + "skillname8": 4405, + "skillsd8": 4406, + "skillld8": 4407, + "skillan8": 4408, + "skillname9": 4409, + "skillsd9": 4410, + "skillld9": 4411, + "skillan9": 4412, + "skillname10": 4413, + "skillsd10": 4414, + "skillld10": 4415, + "skillan10": 4416, + "skillname11": 4417, + "skillsd11": 4418, + "skillld11": 4419, + "skillan11": 4420, + "skillname12": 4421, + "skillsd12": 4422, + "skillld12": 4423, + "skillan12": 4424, + "skillname13": 4425, + "skillsd13": 4426, + "skillld13": 4427, + "skillan13": 4428, + "skillname14": 4429, + "skillsd14": 4430, + "skillld14": 4431, + "skillan14": 4432, + "skillname15": 4433, + "skillsd15": 4434, + "skillld15": 4435, + "skillan15": 4436, + "skillname16": 4437, + "skillsd16": 4438, + "skillld16": 4439, + "skillan16": 4440, + "skillname17": 4441, + "skillsd17": 4442, + "skillld17": 4443, + "skillan17": 4444, + "skillname18": 4445, + "skillsd18": 4446, + "skillld18": 4447, + "skillan18": 4448, + "skillname19": 4449, + "skillsd19": 4450, + "skillld19": 4451, + "skillan19": 4452, + "skillname20": 4453, + "skillsd20": 4454, + "skillld20": 4455, + "skillan20": 4456, + "skillname21": 4457, + "skillsd21": 4458, + "skillld21": 4459, + "skillan21": 4460, + "skillname22": 4461, + "skillsd22": 4462, + "skillld22": 4463, + "skillan22": 4464, + "skillname23": 4465, + "skillsd23": 4466, + "skillld23": 4467, + "skillan23": 4468, + "skillname24": 4469, + "skillsd24": 4470, + "skillld24": 4471, + "skillan24": 4472, + "skillname25": 4473, + "skillsd25": 4474, + "skillld25": 4475, + "skillan25": 4476, + "skillname26": 4477, + "skillsd26": 4478, + "skillld26": 4479, + "skillan26": 4480, + "skillname27": 4481, + "skillsd27": 4482, + "skillld27": 4483, + "skillan27": 4484, + "skillname28": 4485, + "skillsd28": 4486, + "skillld28": 4487, + "skillan28": 4488, + "skillname29": 4489, + "skillsd29": 4490, + "skillld29": 4491, + "skillan29": 4492, + "skillname30": 4493, + "skillsd30": 4494, + "skillld30": 4495, + "skillan30": 4496, + "skillname31": 4497, + "skillsd31": 4498, + "skillld31": 4499, + "skillan31": 4500, + "skillname32": 4501, + "skillsd32": 4502, + "skillld32": 4503, + "skillan32": 4504, + "skillname33": 4505, + "skillsd33": 4506, + "skillld33": 4507, + "skillan33": 4508, + "skillname34": 4509, + "skillsd34": 4510, + "skillld34": 4511, + "skillan34": 4512, + "skillname35": 4513, + "skillsd35": 4514, + "skillld35": 4515, + "skillan35": 4516, + "skillname36": 4517, + "skillsd36": 4518, + "skillld36": 4519, + "skillan36": 4520, + "skillname37": 4521, + "skillsd37": 4522, + "skillld37": 4523, + "skillan37": 4524, + "skillname38": 4525, + "skillsd38": 4526, + "skillld38": 4527, + "skillan38": 4528, + "skillname39": 4529, + "skillsd39": 4530, + "skillld39": 4531, + "skillan39": 4532, + "skillname40": 4533, + "skillsd40": 4534, + "skillld40": 4535, + "skillan40": 4536, + "skillname41": 4537, + "skillsd41": 4538, + "skillld41": 4539, + "skillan41": 4540, + "skillname42": 4541, + "skillsd42": 4542, + "skillld42": 4543, + "skillan42": 4544, + "skillname43": 4545, + "skillsd43": 4546, + "skillld43": 4547, + "skillan43": 4548, + "skillname44": 4549, + "skillsd44": 4550, + "skillld44": 4551, + "skillan44": 4552, + "skillname45": 4553, + "skillsd45": 4554, + "skillld45": 4555, + "skillan45": 4556, + "skillname46": 4557, + "skillsd46": 4558, + "skillld46": 4559, + "skillan46": 4560, + "skillname47": 4561, + "skillsd47": 4562, + "skillld47": 4563, + "skillan47": 4564, + "skillname48": 4565, + "skillsd48": 4566, + "skillld48": 4567, + "skillan48": 4568, + "skillname49": 4569, + "skillsd49": 4570, + "skillld49": 4571, + "skillan49": 4572, + "skillname50": 4573, + "skillsd50": 4574, + "skillld50": 4575, + "skillan50": 4576, + "skillname51": 4577, + "skillsd51": 4578, + "skillld51": 4579, + "skillan51": 4580, + "skillname52": 4581, + "skillsd52": 4582, + "skillld52": 4583, + "skillan52": 4584, + "skillname53": 4585, + "skillsd53": 4586, + "skillld53": 4587, + "skillan53": 4588, + "skillname54": 4589, + "skillsd54": 4590, + "skillld54": 4591, + "skillan54": 4592, + "skillname55": 4593, + "skillsd55": 4594, + "skillld55": 4595, + "skillan55": 4596, + "skillname56": 4597, + "skillsd56": 4598, + "skillld56": 4599, + "skillan56": 4600, + "skillname57": 4601, + "skillsd57": 4602, + "skillld57": 4603, + "skillan57": 4604, + "skillname58": 4605, + "skillsd58": 4606, + "skillld58": 4607, + "skillan58": 4608, + "skillname59": 4609, + "skillsd59": 4610, + "skillld59": 4611, + "skillan59": 4612, + "skillname60": 4613, + "skillsd60": 4614, + "skillld60": 4615, + "skillan60": 4616, + "skillsname61": 4617, + "skillsd61": 4618, + "skillld61": 4619, + "skillan61": 4620, + "skillname62": 4621, + "skillsd62": 4622, + "skillld62": 4623, + "skillan62": 4624, + "skillname63": 4625, + "skillsd63": 4626, + "skillld63": 4627, + "skillan63": 4628, + "skillname64": 4629, + "skillsd64": 4630, + "skillld64": 4631, + "skillan64": 4632, + "skillname65": 4633, + "skillsd65": 4634, + "skillld65": 4635, + "skillan65": 4636, + "skillname66": 4637, + "skillsd66": 4638, + "skillld66": 4639, + "skillan66": 4640, + "skillname67": 4641, + "skillsd67": 4642, + "skillld67": 4643, + "skillan67": 4644, + "skillname68": 4645, + "skillsd68": 4646, + "skillld68": 4647, + "skillan68": 4648, + "skillname69": 4649, + "skillsd69": 4650, + "skillld69": 4651, + "skillan69": 4652, + "skillname70": 4653, + "skillsd70": 4654, + "skillld70": 4655, + "skillan70": 4656, + "skillname71": 4657, + "skillsd71": 4658, + "skillld71": 4659, + "skillan71": 4660, + "skillname72": 4661, + "skillsd72": 4662, + "skillld72": 4663, + "skillan72": 4664, + "skillname73": 4665, + "skillsd73": 4666, + "skillld73": 4667, + "skillan73": 4668, + "skillname74": 4669, + "skillsd74": 4670, + "skillld74": 4671, + "skillan74": 4672, + "skillname75": 4673, + "skillsd75": 4674, + "skillld75": 4675, + "skillan75": 4676, + "skillname76": 4677, + "skillsd76": 4678, + "skillld76": 4679, + "skillan76": 4680, + "skillname77": 4681, + "skillsd77": 4682, + "skillld77": 4683, + "skillan77": 4684, + "skillname78": 4685, + "skillsd78": 4686, + "skillld78": 4687, + "skillan78": 4688, + "skillname79": 4689, + "skillsd79": 4690, + "skillld79": 4691, + "skillan79": 4692, + "skillname80": 4693, + "skillsd80": 4694, + "skillld80": 4695, + "skillan80": 4696, + "skillname81": 4697, + "skillsd81": 4698, + "skillld81": 4699, + "skillan81": 4700, + "skillname82": 4701, + "skillsd82": 4702, + "skillld82": 4703, + "skillan82": 4704, + "skillname83": 4705, + "skillsd83": 4706, + "skillld83": 4707, + "skillan83": 4708, + "skillname84": 4709, + "skillsd84": 4710, + "skillld84": 4711, + "skillan84": 4712, + "skillname85": 4713, + "skillsd85": 4714, + "skillld85": 4715, + "skillan85": 4716, + "skillname86": 4717, + "skillsd86": 4718, + "skillld86": 4719, + "skillan86": 4720, + "skillname87": 4721, + "skillsd87": 4722, + "skillld87": 4723, + "skillan87": 4724, + "skillname88": 4725, + "skillsd88": 4726, + "skillld88": 4727, + "skillan88": 4728, + "skillname89": 4729, + "skillsd89": 4730, + "skillld89": 4731, + "skillan89": 4732, + "skillname90": 4733, + "skillsd90": 4734, + "skillld90": 4735, + "skillan90": 4736, + "skillname91": 4737, + "skillsd91": 4738, + "skillld91": 4739, + "skillan91": 4740, + "skillname92": 4741, + "skillsd92": 4742, + "skillld92": 4743, + "skillan92": 4744, + "skillname93": 4745, + "skillsd93": 4746, + "skillld93": 4747, + "skillan93": 4748, + "skillname94": 4749, + "skillsd94": 4750, + "skillld94": 4751, + "skillan94": 4752, + "skillname95": 4753, + "skillsd95": 4754, + "skillld95": 4755, + "skillan95": 4756, + "skillname96": 4757, + "skillsd96": 4758, + "skillld96": 4759, + "skillan96": 4760, + "skillname97": 4761, + "skillsd97": 4762, + "skillld97": 4763, + "skillan97": 4764, + "skillname98": 4765, + "skillsd98": 4766, + "skillld98": 4767, + "skillan98": 4768, + "skillname99": 4769, + "skillsd99": 4770, + "skillld99": 4771, + "skillan99": 4772, + "skillname100": 4773, + "skillsd100": 4774, + "skillld100": 4775, + "skillan100": 4776, + "skillname101": 4777, + "skillsd101": 4778, + "skillld101": 4779, + "skillan101": 4780, + "skillname102": 4781, + "skillsd102": 4782, + "skillld102": 4783, + "skillan102": 4784, + "skillname103": 4785, + "skillsd103": 4786, + "skillld103": 4787, + "skillan103": 4788, + "skillname104": 4789, + "skillsd104": 4790, + "skillld104": 4791, + "skillan104": 4792, + "skillname105": 4793, + "skillsd105": 4794, + "skillld105": 4795, + "skillan105": 4796, + "skillname106": 4797, + "skillsd106": 4798, + "skillld106": 4799, + "skillan106": 4800, + "skillname107": 4801, + "skillsd107": 4802, + "skillld107": 4803, + "skillan107": 4804, + "skillname108": 4805, + "skillsd108": 4806, + "skillld108": 4807, + "skillan108": 4808, + "skillname109": 4809, + "skillsd109": 4810, + "skillld109": 4811, + "skillan109": 4812, + "skillname110": 4813, + "skillsd110": 4814, + "skillld110": 4815, + "skillan110": 4816, + "skillname111": 4817, + "skillsd111": 4818, + "skillld111": 4819, + "skillan111": 4820, + "skillname112": 4821, + "skillsd112": 4822, + "skillld112": 4823, + "skillan112": 4824, + "skillname113": 4825, + "skillsd113": 4826, + "skillld113": 4827, + "skillan113": 4828, + "skillname114": 4829, + "skillsd114": 4830, + "skillld114": 4831, + "skillan114": 4832, + "skillname115": 4833, + "skillsd115": 4834, + "skillld115": 4835, + "skillan115": 4836, + "skillname116": 4837, + "skillsd116": 4838, + "skillld116": 4839, + "skillan116": 4840, + "skillname117": 4841, + "skillsd117": 4842, + "skillld117": 4843, + "skillan117": 4844, + "skillname118": 4845, + "skillsd118": 4846, + "skillld118": 4847, + "skillan118": 4848, + "skillname119": 4849, + "skillsd119": 4850, + "skillld119": 4851, + "skillan119": 4852, + "skillname120": 4853, + "skillsd120": 4854, + "skillld120": 4855, + "skillan120": 4856, + "skillname121": 4857, + "skillsd121": 4858, + "skillld121": 4859, + "skillan121": 4860, + "skillname122": 4861, + "skillsd122": 4862, + "skillld122": 4863, + "skillan122": 4864, + "skillname123": 4865, + "skillsd123": 4866, + "skillld123": 4867, + "skillan123": 4868, + "skillname124": 4869, + "skillsd124": 4870, + "skillld124": 4871, + "skillan124": 4872, + "skillname125": 4873, + "skillsd125": 4874, + "skillld125": 4875, + "skillan125": 4876, + "skillname126": 4877, + "skillsd126": 4878, + "skillld126": 4879, + "skillan126": 4880, + "skillname127": 4881, + "skillsd127": 4882, + "skillld127": 4883, + "skillan127": 4884, + "skillname128": 4885, + "skillsd128": 4886, + "skillld128": 4887, + "skillan128": 4888, + "skillname129": 4889, + "skillsd129": 4890, + "skillld129": 4891, + "skillan129": 4892, + "skillname130": 4893, + "skillsd130": 4894, + "skillld130": 4895, + "skillan130": 4896, + "skillname131": 4897, + "skillsd131": 4898, + "skillld131": 4899, + "skillan131": 4900, + "skillname132": 4901, + "skillsd132": 4902, + "skillld132": 4903, + "skillan132": 4904, + "skillname133": 4905, + "skillsd133": 4906, + "skillld133": 4907, + "skillan133": 4908, + "skillname134": 4909, + "skillsd134": 4910, + "skillld134": 4911, + "skillan134": 4912, + "skillname135": 4913, + "skillsd135": 4914, + "skillld135": 4915, + "skillan135": 4916, + "skillname136": 4917, + "skillsd136": 4918, + "skillld136": 4919, + "skillan136": 4920, + "skillname137": 4921, + "skillsd137": 4922, + "skillld137": 4923, + "skillan137": 4924, + "skillname138": 4925, + "skillsd138": 4926, + "skillld138": 4927, + "skillan138": 4928, + "skillname139": 4929, + "skillsd139": 4930, + "skillld139": 4931, + "skillan139": 4932, + "skillname140": 4933, + "skillsd140": 4934, + "skillld140": 4935, + "skillan140": 4936, + "skillname141": 4937, + "skillsd141": 4938, + "skillld141": 4939, + "skillan141": 4940, + "skillname142": 4941, + "skillsd142": 4942, + "skillld142": 4943, + "skillan142": 4944, + "skillname143": 4945, + "skillsd143": 4946, + "skillld143": 4947, + "skillan143": 4948, + "skillname144": 4949, + "skillsd144": 4950, + "skillld144": 4951, + "skillan144": 4952, + "skillname145": 4953, + "skillsd145": 4954, + "skillld145": 4955, + "skillan145": 4956, + "skillname146": 4957, + "skillsd146": 4958, + "skillld146": 4959, + "skillan146": 4960, + "skillname147": 4961, + "skillsd147": 4962, + "skillld147": 4963, + "skillan147": 4964, + "skillname148": 4965, + "skillsd148": 4966, + "skillld148": 4967, + "skillan148": 4968, + "skillname149": 4969, + "skillsd149": 4970, + "skillld149": 4971, + "skillan149": 4972, + "skillname150": 4973, + "skillsd150": 4974, + "skillld150": 4975, + "skillan150": 4976, + "skillname151": 4977, + "skillsd151": 4978, + "skillld151": 4979, + "skillan151": 4980, + "skillname152": 4981, + "skillsd152": 4982, + "skillld152": 4983, + "skillan152": 4984, + "skillname153": 4985, + "skillsd153": 4986, + "skillld153": 4987, + "skillan153": 4988, + "skillname154": 4989, + "skillsd154": 4990, + "skillld154": 4991, + "skillan154": 4992, + "skillname155": 4993, + "skillsd155": 4994, + "skillld155": 4995, + "skillan155": 4996, + "skillname217": 4997, + "skillsd217": 4998, + "skillld217": 4999, + "skillan217": 5000, + "skillname218": 5001, + "skillsd218": 5002, + "skillld218": 5003, + "skillan218": 5004, + "skillname219": 5005, + "skillsd219": 5006, + "skillld219": 5007, + "skillan219": 5008, + "skillname220": 5009, + "skillsd220": 5010, + "skillld220": 5011, + "skillan220": 5012, + "strMephistoDoorLocked": 5013, + "strTitleFeminine": 5014, + "strTitleMasculine": 5015, + "strChatHardcore": 5016, + "strChatLevel": 5017, + "Tristram": 5018, + "Catacombs Level 4": 5019, + "Catacombs Level 3": 5020, + "Catacombs Level 2": 5021, + "Catacombs Level 1": 5022, + "Cathedral": 5023, + "Inner Cloister": 5024, + "Jail Level 3": 5025, + "Jail Level 2": 5026, + "Jail Level 1": 5027, + "Barracks": 5028, + "Outer Cloister": 5029, + "Monastery Gate": 5030, + "Tower Cellar Level 5": 5031, + "Tower Cellar Level 4": 5032, + "Tower Cellar Level 3": 5033, + "Tower Cellar Level 2": 5034, + "Tower Cellar Level 1": 5035, + "Forgotten Tower": 5036, + "Mausoleum": 5037, + "Crypt": 5038, + "Burial Grounds": 5039, + "Pit Level 2": 5040, + "Hole Level 2": 5041, + "Underground Passage Level 2": 5042, + "Cave Level 2": 5043, + "Pit Level 1": 5044, + "Hole Level 1": 5045, + "Underground Passage Level 1": 5046, + "Cave Level 1": 5047, + "Den of Evil": 5048, + "Tamoe Highland": 5049, + "Black Marsh": 5050, + "Dark Wood": 5051, + "Stony Field": 5052, + "Cold Plains": 5053, + "Blood Moor": 5054, + "Rogue Encampment": 5055, + "To Tristram": 5056, + "To The Catacombs Level 4": 5057, + "To The Catacombs Level 3": 5058, + "To The Catacombs Level 2": 5059, + "To The Catacombs Level 1": 5060, + "To The Cathedral": 5061, + "To The Inner Cloister": 5062, + "To The Jail Level 3": 5063, + "To The Jail Level 2": 5064, + "To The Jail Level 1": 5065, + "To The Barracks": 5066, + "To The Outer Cloister": 5067, + "To The Monastery Gate": 5068, + "To The Tower Cellar Level 5": 5069, + "To The Tower Cellar Level 4": 5070, + "To The Tower Cellar Level 3": 5071, + "To The Tower Cellar Level 2": 5072, + "To The Tower Cellar Level 1": 5073, + "To The Forgotten Tower": 5074, + "To The Mausoleum": 5075, + "To The Crypt": 5076, + "To The Burial Grounds": 5077, + "To The Pit Level 2": 5078, + "To The Hole Level 2": 5079, + "To Underground Passage Level 2": 5080, + "To The Cave Level 2": 5081, + "To The Pit Level 1": 5082, + "To The Hole Level 1": 5083, + "To Underground Passage Level 1": 5084, + "To The Cave Level 1": 5085, + "To The Den of Evil": 5086, + "To The Tamoe Highland": 5087, + "To The Black Marsh": 5088, + "To The Dark Wood": 5089, + "To The Stony Field": 5090, + "To The Cold Plains": 5091, + "To The Blood Moor": 5092, + "To The Rogue Encampment": 5093, + "Deathmessage": 5094, + "Deathmessnight": 5095, + "Harddeathmessage": 5096, + "LordofTerrordied": 5097, + "Killdiablo1": 5098, + "KillDiablo2": 5099, + "KillDiablo3": 5100, + "x": 22741, + "X": 22746, + "Gem Activated": 5334, + "Gem Deactivated": 5335, + "Perfect Gem Activated": 5336, + "dummy": 5382, + "Dummy": 5383, + "not used": 5384, + "unused": 5385, + "Not used": 5386, + "convertsto": 5387, + "strNotInBeta": 5388, + "strLevelLoadFailed": 5389, + "Endthispuppy": 5390, + "A4Q2ExpansionSuccessTyrael": 20000, + "A4Q2ExpansionSuccessCain": 20001, + "AncientsAct5IntroGossip1": 20002, + "CainAct5IntroGossip1": 20003, + "CainAct5Gossip1": 20004, + "CainAct5Gossip2": 20005, + "CainAct5Gossip3": 20006, + "CainAct5Gossip4": 20007, + "CainAct5Gossip5": 20008, + "CainAct5Gossip6": 20009, + "CainAct5Gossip7": 20010, + "CainAct5Gossip8": 20011, + "CainAct5Gossip9": 20012, + "CainAct5Gossip10": 20013, + "AnyaAct5IntroGossip1": 20014, + "AnyaGossip1": 20015, + "AnyaGossip2": 20016, + "AnyaGossip3": 20017, + "AnyaGossip4": 20018, + "AnyaGossip5": 20019, + "AnyaGossip6": 20020, + "AnyaGossip7": 20021, + "AnyaGossip8": 20022, + "AnyaGossip9": 20023, + "AnyaGossip10": 20024, + "LarzukAct5IntroGossip1": 20025, + "LarzukAct5IntroAmaGossip1": 20026, + "LarzukGossip1": 20027, + "LarzukGossip2": 20028, + "LarzukGossip3": 20029, + "LarzukGossip4": 20030, + "LarzukGossip5": 20031, + "LarzukGossip6": 20032, + "LarzukGossip7": 20033, + "LarzukGossip8": 20034, + "LarzukGossip9": 20035, + "LarzukGossip10": 20036, + "MalahAct5IntroGossip1": 20037, + "MalahAct5IntroSorGossip1": 20038, + "MalahAct5IntroBarGossip1": 20039, + "MalahGossip1": 20040, + "MalahGossip2": 20041, + "MalahGossip3": 20042, + "MalahGossip4": 20043, + "MalahGossip5": 20044, + "MalahGossip6": 20045, + "MalahGossip7": 20046, + "MalahGossip8": 20047, + "MalahGossip9": 20048, + "MalahGossip10": 20049, + "MalahGossip11": 20050, + "MalahGossip12": 20051, + "MalahGossip13": 20052, + "NihlathakAct5IntroGossip1": 20053, + "NihlathakAct5IntroAssGossip1": 20054, + "NihlathakAct5IntroNecGossip1": 20055, + "NihlathakGossip1": 20056, + "NihlathakGossip2": 20057, + "NihlathakGossip3": 20058, + "NihlathakGossip4": 20059, + "NihlathakGossip5": 20060, + "NihlathakGossip6": 20061, + "NihlathakGossip7": 20062, + "NihlathakGossip8": 20063, + "NihlathakGossip9": 20064, + "QualKehkAct5IntroGossip1": 20065, + "QualKehkAct5IntroPalGossip1": 20066, + "QualKehkAct5IntroDruGossip1": 20067, + "QualKehkGossip1": 20068, + "QualKehkGossip2": 20069, + "QualKehkGossip3": 20070, + "QualKehkGossip4": 20071, + "QualKehkGossip5": 20072, + "QualKehkGossip6": 20073, + "QualKehkGossip7": 20074, + "QualKehkGossip8": 20075, + "QualKehkGossip9": 20076, + "A5Q1InitLarzuk": 20077, + "A5Q1AfterInitLarzuk": 20078, + "A5Q1AfterInitCain": 20079, + "A5Q1AfterInitAnya": 20080, + "A5Q1AfterInitMalah": 20081, + "A5Q1AfterInitNihlathak": 20082, + "A5Q1AfterInitQualKehk": 20083, + "A5Q1EarlyReturnLarzuk": 20084, + "A5Q1EarlyReturnCain": 20085, + "A5Q1EarlyReturnAnya": 20086, + "A5Q1EarlyReturnMalah": 20087, + "A5Q1EarlyReturnNihlathak": 20088, + "A5Q1EarlyReturnQualKehk": 20089, + "A5Q1SuccessfulLarzuk": 20090, + "A5Q1SuccessfulCain": 20091, + "A5Q1SuccessfulAnya": 20092, + "A5Q1SuccessfulMalah": 20093, + "A5Q1SuccessfulNihlathak": 20094, + "A5Q1SuccessfulQualKehk": 20095, + "A5Q2InitQualKehk": 20096, + "A5Q2AfterInitQualKehk": 20097, + "A5Q2AfterInitCain": 20098, + "A5Q2AfterInitAnya": 20099, + "A5Q2AfterInitLarzuk": 20100, + "A5Q2AfterInitMalah": 20101, + "A5Q2AfterInitNihlathak": 20102, + "A5Q2EarlyReturnQualKehk": 20103, + "A5Q2EarlyReturnQualKehkMan": 20104, + "A5Q2EarlyReturnCain": 20105, + "A5Q2EarlyReturnAnya": 20106, + "A5Q2EarlyReturnLarzuk": 20107, + "A5Q2EarlyReturnMalah": 20108, + "A5Q2EarlyReturnNihlathak": 20109, + "A5Q2SuccessfulQualKehk": 20110, + "A5Q2SuccessfulCain": 20111, + "A5Q2SuccessfulAnya": 20112, + "A5Q2SuccessfulLarzuk": 20113, + "A5Q2SuccessfulMalah": 20114, + "A5Q2SuccessfulNihlathak": 20115, + "A5Q3InitMalah": 20116, + "A5Q3AfterInitMalah": 20117, + "A5Q3AfterInitCain": 20118, + "A5Q3AfterInitLarzuk": 20119, + "A5Q3AfterInitNihlathak": 20120, + "A5Q3AfterInitQualKehk": 20121, + "A5Q3EarlyReturnMalah": 20122, + "A5Q3EarlyReturnCain": 20123, + "A5Q3EarlyReturnLarzuk": 20124, + "A5Q3EarlyReturnNihlathak": 20125, + "A5Q3EarlyReturnQualKehk": 20126, + "A5Q3FoundAnyaMalah": 20127, + "A5Q3FoundAnyaCain": 20128, + "A5Q3FoundAnyaLarzuk": 20129, + "A5Q3FoundAnyaQualKehk": 20130, + "A5Q3FoundAnyaAnya": 20131, + "A5Q3SuccessfulMalah": 20132, + "A5Q3SuccessfulCain": 20133, + "A5Q3SuccessfulLarzuk": 20134, + "A5Q3SuccessfulQualKehk": 20135, + "A5Q3SuccessfulAnya": 20136, + "A5Q4InitAnya": 20137, + "A5Q4AfterInitAnya": 20138, + "A5Q4AfterInitCain": 20139, + "A5Q4AfterInitMalah": 20140, + "A5Q4AfterInitLarzuk": 20141, + "A5Q4AfterInitQualKehk": 20142, + "A5Q4EarlyReturnAnya": 20143, + "A5Q4EarlyReturnCain": 20144, + "A5Q4EarlyReturnLarzuk": 20145, + "A5Q4EarlyReturnMalah": 20146, + "A5Q4EarlyReturnQualKehk": 20147, + "A5Q4SuccessfulAnya": 20148, + "A5Q4SuccessfulCain": 20149, + "A5Q4SuccessfulLarzuk": 20150, + "A5Q4SuccessfulMalah": 20151, + "A5Q4SuccessfulQualKehk": 20152, + "A5Q5InitQualKehk": 20153, + "A5Q5AfterInitQualKehk": 20154, + "A5Q5AfterInitCain": 20155, + "A5Q5AfterInitAnya": 20156, + "A5Q5AfterInitLarzuk": 20157, + "A5Q5AfterInitMalah": 20158, + "A5Q5EarlyReturnQualKehk": 20159, + "A5Q5EarlyReturnCain": 20160, + "A5Q5EarlyReturnAnya": 20161, + "A5Q5EarlyReturnLarzuk": 20162, + "A5Q5EarlyReturnMalah": 20163, + "A5Q5SuccessfulQualKehk": 20164, + "A5Q5SuccessfulCain": 20165, + "A5Q5SuccessfulAnya": 20166, + "A5Q5SuccessfulLarzuk": 20167, + "A5Q5SuccessfulMalah": 20168, + "A5Q6InitAncients": 20169, + "A5Q6EarlyReturnCain": 20170, + "A5Q6EarlyReturnLarzuk": 20171, + "A5Q6EarlyReturnMalah": 20172, + "A5Q6EarlyReturnAnya": 20173, + "A5Q6EarlyReturnQualKehk": 20174, + "A5Q6SuccessfulTyrael": 20175, + "A5Q6SuccessfulAnya": 20176, + "A5Q6SuccessfulCain": 20177, + "A5Q6SuccessfulLarzuk": 20178, + "A5Q6SuccessfulMalah": 20179, + "A5Q6SuccessfulQualKehk": 20180, + "ktr": 20181, + "wrb": 20182, + "ces": 20183, + "clw": 20184, + "btl": 20185, + "skr": 20186, + "9ar": 20187, + "9wb": 20188, + "9xf": 20189, + "9cs": 20190, + "9lw": 20191, + "9tw": 20192, + "9qr": 20193, + "7ar": 20194, + "7wb": 20195, + "7xf": 20196, + "7cs": 20197, + "7lw": 20198, + "7tw": 20199, + "7qr": 20200, + "7ha": 20201, + "7ax": 20202, + "72a": 20203, + "7mp": 20204, + "7wa": 20205, + "7la": 20206, + "7ba": 20207, + "7bt": 20208, + "7ga": 20209, + "7gi": 20210, + "7wn": 20211, + "7yw": 20212, + "7bw": 20213, + "7gw": 20214, + "7cl": 20215, + "7sc": 20216, + "7qs": 20217, + "7ws": 20218, + "7sp": 20219, + "7ma": 20220, + "7mt": 20221, + "7fl": 20222, + "7wh": 20223, + "7m7": 20224, + "7gm": 20225, + "7ss": 20226, + "7sm": 20227, + "7sb": 20228, + "7fc": 20229, + "7cr": 20230, + "7bs": 20231, + "7ls": 20232, + "7wd": 20233, + "72h": 20234, + "7cm": 20235, + "7gs": 20236, + "7b7": 20237, + "7fb": 20238, + "7gd": 20239, + "7dg": 20240, + "7di": 20241, + "7kr": 20242, + "7bl": 20243, + "7tk": 20244, + "7ta": 20245, + "7bk": 20246, + "7b8": 20247, + "7ja": 20248, + "7pi": 20249, + "7s7": 20250, + "7gl": 20251, + "7ts": 20252, + "7sr": 20253, + "7tr": 20254, + "7br": 20255, + "7st": 20256, + "7p7": 20257, + "7o7": 20258, + "7vo": 20259, + "7s8": 20260, + "7pa": 20261, + "7h7": 20262, + "7wc": 20263, + "6ss": 20264, + "6ls": 20265, + "6cs": 20266, + "6bs": 20267, + "6ws": 20268, + "6sb": 20269, + "6hb": 20270, + "6lb": 20271, + "6cb": 20272, + "6s7": 20273, + "6l7": 20274, + "6sw": 20275, + "6lw": 20276, + "6lx": 20277, + "6mx": 20278, + "6hx": 20279, + "6rx": 20280, + "am1": 20292, + "am2": 20293, + "am3": 20294, + "am4": 20295, + "am5": 20296, + "ob6": 20297, + "ob7": 20298, + "ob8": 20299, + "ob9": 20300, + "oba": 20301, + "am6": 20302, + "am7": 20303, + "am8": 20304, + "am9": 20305, + "ama": 20306, + "obb": 20307, + "obc": 20308, + "obd": 20309, + "obe": 20310, + "obf": 20311, + "amb": 20312, + "amc": 20313, + "amd": 20314, + "ame": 20315, + "amf": 20316, + "ba1": 20322, + "ba2": 20323, + "ba3": 20324, + "ba4": 20325, + "ba5": 20326, + "pa1": 20327, + "pa2": 20328, + "pa3": 20329, + "pa4": 20330, + "pa5": 20331, + "ci0": 20337, + "ci1": 20338, + "ci2": 20339, + "ci3": 20340, + "uap": 20341, + "ukp": 20342, + "ulm": 20343, + "uhl": 20344, + "uhm": 20345, + "urn": 20346, + "usk": 20347, + "uui": 20348, + "uea": 20349, + "ula": 20350, + "utu": 20351, + "ung": 20352, + "ucl": 20353, + "uhn": 20354, + "urs": 20355, + "upl": 20356, + "ult": 20357, + "uld": 20358, + "uth": 20359, + "uul": 20360, + "uar": 20361, + "utp": 20362, + "uuc": 20363, + "uml": 20364, + "urg": 20365, + "uit": 20366, + "uow": 20367, + "uts": 20368, + "ulg": 20369, + "uvg": 20370, + "umg": 20371, + "utg": 20372, + "uhg": 20373, + "ulb": 20374, + "uvb": 20375, + "umb": 20376, + "utb": 20377, + "uhb": 20378, + "ulc": 20379, + "uvc": 20380, + "umc": 20381, + "utc": 20382, + "uhc": 20383, + "uh9": 20384, + "ush": 20385, + "upk": 20386, + "dr9": 20387, + "dr7": 20388, + "dr8": 20389, + "dr6": 20390, + "dra": 20391, + "ba6": 20392, + "ba7": 20393, + "ba8": 20394, + "ba9": 20395, + "baa": 20396, + "pa6": 20397, + "pa7": 20398, + "pa8": 20399, + "pa9": 20400, + "paa": 20401, + "ne6": 20402, + "ne7": 20403, + "ne8": 20404, + "ne9": 20405, + "nea": 20406, + "dre": 20407, + "drc": 20408, + "drd": 20409, + "drb": 20410, + "drf": 20411, + "bab": 20412, + "bac": 20413, + "bad": 20414, + "bae": 20415, + "baf": 20416, + "pab": 20417, + "pac": 20418, + "pae": 20419, + "paf": 20420, + "neb": 20421, + "nec": 20422, + "ned": 20423, + "nee": 20424, + "nef": 20425, + "jew": 20433, + "cm1": 20435, + "cm2": 20436, + "cm3": 20437, + "Charmdes": 20438, + "ice": 20439, + "r33": 20440, + "r32": 20441, + "r31": 20442, + "r30": 20443, + "r29": 20444, + "r28": 20445, + "r27": 20446, + "r26": 20447, + "r25": 20448, + "r24": 20449, + "r23": 20450, + "r22": 20451, + "r21": 20452, + "r20": 20453, + "r19": 20454, + "r18": 20455, + "r17": 20456, + "r16": 20457, + "r15": 20458, + "r14": 20459, + "r13": 20460, + "r12": 20461, + "r11": 20462, + "r10": 20463, + "r09": 20464, + "r08": 20465, + "r07": 20466, + "r06": 20467, + "r05": 20468, + "r04": 20469, + "r03": 20470, + "r02": 20471, + "r01": 20472, + "r33L": 20473, + "r32L": 20474, + "r31L": 20475, + "r30L": 20476, + "r29L": 20477, + "r28L": 20478, + "r27L": 20479, + "r26L": 20480, + "r25L": 20481, + "r24L": 20482, + "r23L": 20483, + "r22L": 20484, + "r21L": 20485, + "r20L": 20486, + "r19L": 20487, + "r18L": 20488, + "r17L": 20489, + "r16L": 20490, + "r15L": 20491, + "r14L": 20492, + "r13L": 20493, + "r12L": 20494, + "r11L": 20495, + "r10L": 20496, + "r09L": 20497, + "r08L": 20498, + "r07L": 20499, + "r06L": 20500, + "r05L": 20501, + "r04L": 20502, + "r03L": 20503, + "r02L": 20504, + "r01L": 20505, + "RuneQuote": 20506, + "Runeword1": 20507, + "Runeword2": 20508, + "Runeword3": 20509, + "Runeword4": 20510, + "Runeword5": 20511, + "Runeword6": 20512, + "Runeword7": 20513, + "Runeword8": 20514, + "Runeword9": 20515, + "Runeword10": 20516, + "Runeword11": 20517, + "Runeword12": 20518, + "Runeword13": 20519, + "Runeword14": 20520, + "Runeword15": 20521, + "Runeword16": 20522, + "Runeword17": 20523, + "Runeword18": 20524, + "Runeword19": 20525, + "Runeword20": 20526, + "Runeword21": 20527, + "Runeword22": 20528, + "Runeword23": 20529, + "Runeword24": 20530, + "Runeword25": 20531, + "Runeword26": 20532, + "Runeword27": 20533, + "Runeword28": 20534, + "Runeword29": 20535, + "Runeword30": 20536, + "Runeword31": 20537, + "Runeword32": 20538, + "Runeword33": 20539, + "Runeword34": 20540, + "Runeword35": 20541, + "Runeword36": 20542, + "Runeword37": 20543, + "Runeword38": 20544, + "Runeword39": 20545, + "Runeword40": 20546, + "Runeword41": 20547, + "Runeword42": 20548, + "Runeword43": 20549, + "Runeword44": 20550, + "Runeword45": 20551, + "Runeword46": 20552, + "Runeword47": 20553, + "Runeword48": 20554, + "Runeword49": 20555, + "Runeword50": 20556, + "Runeword51": 20557, + "Runeword52": 20558, + "Runeword53": 20559, + "Runeword54": 20560, + "Runeword55": 20561, + "Runeword56": 20562, + "Runeword57": 20563, + "Runeword58": 20564, + "Runeword59": 20565, + "Runeword60": 20566, + "Runeword61": 20567, + "Runeword62": 20568, + "Runeword63": 20569, + "Runeword64": 20570, + "Runeword65": 20571, + "Runeword66": 20572, + "Runeword67": 20573, + "Runeword68": 20574, + "Runeword69": 20575, + "Runeword70": 20576, + "Runeword71": 20577, + "Runeword72": 20578, + "Runeword73": 20579, + "Runeword74": 20580, + "Runeword75": 20581, + "Runeword76": 20582, + "Runeword77": 20583, + "Runeword78": 20584, + "Runeword79": 20585, + "Runeword81": 20586, + "Runeword82": 20587, + "Runeword83": 20588, + "Runeword84": 20589, + "Runeword85": 20590, + "Runeword86": 20591, + "Runeword87": 20592, + "Runeword88": 20593, + "Runeword89": 20594, + "Runeword90": 20595, + "Runeword91": 20596, + "Runeword92": 20597, + "Runeword93": 20598, + "Runeword94": 20599, + "Runeword95": 20600, + "Runeword96": 20601, + "Runeword97": 20602, + "Runeword98": 20603, + "Runeword99": 20604, + "Runeword100": 20605, + "Runeword101": 20606, + "Runeword102": 20607, + "Runeword103": 20608, + "Runeword104": 20609, + "Runeword105": 20610, + "Runeword106": 20611, + "Runeword107": 20612, + "Runeword108": 20613, + "Runeword109": 20614, + "Runeword110": 20615, + "Runeword111": 20616, + "Runeword112": 20617, + "Runeword113": 20618, + "Runeword114": 20619, + "Runeword115": 20620, + "Runeword116": 20621, + "Runeword117": 20622, + "Runeword118": 20623, + "Runeword119": 20624, + "Runeword120": 20625, + "Runeword121": 20626, + "Runeword122": 20627, + "Runeword123": 20628, + "Runeword124": 20629, + "Runeword125": 20630, + "Runeword126": 20631, + "Runeword127": 20632, + "Runeword128": 20633, + "Runeword129": 20634, + "Runeword130": 20635, + "Runeword131": 20636, + "Runeword132": 20637, + "Runeword133": 20638, + "Runeword134": 20639, + "Runeword135": 20640, + "Runeword136": 20641, + "Runeword137": 20642, + "Runeword138": 20643, + "Runeword139": 20644, + "Runeword140": 20645, + "Runeword141": 20646, + "Runeword142": 20647, + "Runeword143": 20648, + "Runeword144": 20649, + "Runeword145": 20650, + "Runeword146": 20651, + "Runeword147": 20652, + "Runeword148": 20653, + "Runeword149": 20654, + "Runeword150": 20655, + "Runeword151": 20656, + "Runeword152": 20657, + "Runeword153": 20658, + "Runeword154": 20659, + "Runeword155": 20660, + "Runeword156": 20661, + "Runeword157": 20662, + "Runeword158": 20663, + "Runeword159": 20664, + "Runeword160": 20665, + "Runeword161": 20666, + "Runeword162": 20667, + "Runeword163": 20668, + "Runeword164": 20669, + "Runeword165": 20670, + "Runeword166": 20671, + "Runeword167": 20672, + "Runeword168": 20673, + "Runeword169": 20674, + "Runeword170": 20675, + "spe": 20676, + "scz": 20677, + "sol": 20678, + "qll": 20679, + "fng": 20680, + "flg": 20681, + "tal": 20682, + "hrn": 20683, + "eyz": 20684, + "jaw": 20685, + "brz": 20686, + "hrt": 20687, + "Stout": 20688, + "Antimagic": 20689, + "Null": 20690, + "Godly": 20691, + "Ivory": 20692, + "Eburin": 20693, + "Blanched": 20694, + "Stalwart": 20695, + "Burly": 20696, + "Dense": 20697, + "Thin": 20698, + "Compact": 20699, + "Witch-hunter's": 20700, + "Magekiller's": 20701, + "Hierophant's": 20702, + "Shaman's": 20703, + "Pestilent": 20704, + "Toxic": 20705, + "Corosive": 20706, + "Envenomed": 20707, + "Septic": 20708, + "Shocking": 20709, + "Arcing": 20710, + "Buzzing": 20711, + "Static": 20712, + "Scorching": 20713, + "Flaming": 20714, + "Smoking": 20715, + "Smoldering": 20716, + "Ember": 20717, + "Hibernal": 20718, + "Boreal": 20719, + "Shivering": 20720, + "Snowflake": 20721, + "Mnemonic": 20722, + "Visionary": 20723, + "Eagleeye": 20724, + "Hawkeye": 20725, + "Falconeye": 20726, + "Sparroweye": 20727, + "Robineye": 20728, + "Paradox": 20729, + "Shouting": 20730, + "Yelling": 20731, + "Calling": 20732, + "Loud": 20733, + "Trump": 20734, + "Joker's": 20735, + "Jester's": 20736, + "Jack's": 20737, + "Knave's": 20738, + "Paleocene": 20739, + "Eocene": 20740, + "Oligocene": 20741, + "Miocene": 20742, + "Kenshi's": 20743, + "Sensei's": 20744, + "Shogukusha's": 20745, + "Psychic": 20746, + "Mentalist's": 20747, + "Cunning": 20748, + "Trickster's": 20749, + "Entrapping": 20750, + "Gaea's": 20751, + "Terra's": 20752, + "Nature's": 20753, + "Communal": 20754, + "Feral": 20755, + "Spiritual": 20756, + "Keeper's": 20757, + "Caretaker's": 20758, + "Trainer's": 20759, + "Veteran's": 20760, + "Expert's": 20761, + "Furious": 20762, + "Raging": 20763, + "Echoing": 20764, + "Resonant": 20765, + "Sounding": 20766, + "Guardian's": 20767, + "Warder's": 20768, + "Preserver's": 20769, + "Marshal's": 20770, + "Commander's": 20771, + "Captain's": 20772, + "Rose Branded": 20773, + "Hawk Branded": 20774, + "Lion Branded": 20775, + "Golemlord's": 20776, + "Vodoun": 20777, + "Graverobber's": 20778, + "Venomous": 20779, + "Noxious": 20780, + "Fungal": 20781, + "Accursed": 20782, + "Blighting": 20783, + "Hexing": 20784, + "Glacial": 20785, + "Freezing": 20786, + "Chilling": 20787, + "Powered": 20788, + "Charged": 20789, + "Sparking": 20790, + "Volcanic": 20791, + "Blazing": 20792, + "Burning": 20793, + "Lancer's": 20794, + "Spearmaiden's": 20795, + "Harpoonist's": 20796, + "Athlete's": 20797, + "Gymnast's": 20798, + "Acrobat's": 20799, + "Bowyer's": 20800, + "Diamond": 20801, + "Celestial": 20802, + "Elysian": 20803, + "Astral": 20804, + "Unearthly": 20805, + "Arcadian": 20806, + "Jeweler's": 20807, + "Artificer's": 20808, + "Mechanist's": 20809, + "Aureolin": 20810, + "Victorious": 20811, + "Ambergris": 20812, + "Camphor": 20813, + "Lapis Lazuli": 20814, + "Chromatic": 20815, + "Scintillating": 20816, + "Turquoise": 20817, + "Jacinth": 20818, + "Zircon": 20819, + "Bahamut's": 20820, + "Great Wyrm's": 20821, + "Felicitous": 20822, + "Lucky": 20823, + "Wailing": 20824, + "Screaming": 20825, + "Grandmaster's": 20826, + "Master's": 20827, + "Argent": 20828, + "Tin": 20829, + "Nickel": 20830, + "Maroon": 20831, + "Chestnut": 20832, + "Vigorous": 20833, + "Brown": 20834, + "Dun": 20835, + "Realgar": 20836, + "Rusty": 20837, + "Cinnabar": 20838, + "Vermillion": 20839, + "Carmine": 20840, + "Carbuncle": 20841, + "Serrated": 20842, + "Scarlet": 20843, + "Bloody": 20844, + "Sanguinary": 20845, + "Pearl": 20846, + "Divine": 20847, + "Hallowed": 20848, + "Sacred": 20849, + "Pure": 20850, + "Consecrated": 20851, + "Assamic": 20852, + "Frantic": 20853, + "Hellatial": 20854, + "Quixotic": 20855, + "Smiting": 20856, + "Steller": 20857, + "Stinging": 20858, + "Singing": 20859, + "Timeless": 20860, + "Original": 20861, + "Corporal": 20862, + "Lawful": 20863, + "Chaotic": 20864, + "Fierce": 20865, + "Ferocious": 20866, + "Perpetual": 20867, + "Continuous": 20868, + "Laden": 20869, + "Pernicious": 20870, + "Harmful": 20871, + "Evil": 20872, + "Insidious": 20873, + "Malicious": 20874, + "Spiteful": 20875, + "Precocious": 20876, + "Majestic": 20877, + "Sanguine": 20878, + "Monumental": 20879, + "Irresistible": 20880, + "Festering": 20881, + "Musty": 20882, + "Dusty": 20883, + "Decaying": 20884, + "Rotting": 20885, + "Infectious": 20886, + "Foggy": 20887, + "Cloudy": 20888, + "Hazy": 20889, + "Punishing": 20890, + "Obsidian": 20891, + "Royal": 20892, + "Frigid": 20893, + "Moldy": 20894, + "Gaudy": 20895, + "Impecable": 20896, + "Soulless": 20897, + "Heated": 20898, + "Lasting": 20899, + "Scorched": 20900, + "Marred": 20901, + "Lilac": 20902, + "Rose": 20903, + "Shimmering": 20904, + "Wicked": 20906, + "Strange": 20907, + "Repulsive": 20908, + "Reclusive": 20909, + "Rude": 20911, + "Hermetic": 20912, + "Rainbow": 20913, + "Colorful": 20914, + "Stinky": 20915, + "Gritty": 20916, + "of Warming": 20917, + "of Stoicism": 20918, + "of the Dynamo": 20919, + "of Grounding": 20920, + "of Insulation": 20921, + "of Resistance": 20922, + "of Faith": 20923, + "of Fire Quenching": 20924, + "of Amianthus": 20925, + "of Incombustibility": 20926, + "of Coolness": 20927, + "of Anima": 20928, + "of Life Everlasting": 20929, + "of Sunlight": 20930, + "of Frozen Orb": 20931, + "of Hydra Shield": 20932, + "of Chilling Armor": 20933, + "of Blizzard": 20934, + "of Energy Shield": 20935, + "of Thunder Storm": 20936, + "of Meteor": 20937, + "of Glacial Spike": 20938, + "of Teleport Shield": 20939, + "of Chain Lightning": 20940, + "of Enchant": 20941, + "of Fire Wall": 20942, + "of Shiver Armor": 20943, + "of Nova Shield": 20944, + "of Nova": 20945, + "of Fire Ball": 20946, + "of Blaze": 20947, + "of Ice Blast": 20948, + "of Frost Shield": 20949, + "of Telekinesis": 20950, + "of Static Field": 20951, + "of Frozen Armor": 20952, + "of Icebolt": 20953, + "of Charged Shield": 20954, + "of Firebolts": 20955, + "of the Elements": 20956, + "of the Cobra": 20957, + "of the Efreeti": 20958, + "of the Phoenix": 20959, + "of the Yeti": 20960, + "of Grace and Power": 20961, + "of Grace": 20962, + "of Power": 20963, + "of the Elephant": 20964, + "of Memory": 20965, + "of the Kraken1": 20966, + "of Propogation": 20967, + "of Replenishing": 20968, + "of Ages": 20969, + "of Fast Repair": 20970, + "of Self-Repair": 20971, + "of Acceleration": 20972, + "of Traveling": 20973, + "of Virility": 20974, + "of Atlus": 20975, + "of Freedom": 20976, + "of the Lamprey": 20977, + "of Hope": 20978, + "of Spirit": 20979, + "of Vita": 20980, + "of Substinence": 20981, + "of the Whale": 20982, + "of the Squid": 20983, + "of the Colossus1": 20984, + "of Knowledge": 20985, + "of Enlightenment": 20986, + "of Prosperity": 20987, + "of Good Luck": 20988, + "of Luck": 20989, + "of Avarice": 20990, + "of Honor": 20991, + "of Revivification": 20992, + "of Truth": 20993, + "of Daring": 20994, + "of Nirvana": 20995, + "of Envy": 20996, + "of Anthrax": 20997, + "of Bliss": 20998, + "of Joy": 20999, + "of Transcendence": 21000, + "of Wrath": 21001, + "of Ire": 21002, + "of Evisceration": 21003, + "of Butchery": 21004, + "of Ennui": 21005, + "of Storms": 21006, + "of Passion": 21007, + "of Incineration": 21008, + "of Frigidity": 21009, + "of Winter": 21010, + "of the Icicle": 21011, + "of Fervor": 21012, + "of Malice": 21013, + "of Swords": 21014, + "of Razors": 21015, + "of Desire": 21016, + "of the Sirocco": 21017, + "of the Dunes": 21018, + "of Thawing": 21019, + "Of the Choir": 21020, + "Of the Sniper": 21021, + "Of the Stiletto": 21022, + "Of Bile": 21023, + "Of Blitzen": 21024, + "Of Cremation": 21025, + "Of Darkness": 21026, + "Of Disease": 21027, + "Of Remorse": 21028, + "Of Terror": 21029, + "Of the Sky": 21030, + "Of Valhalla": 21031, + "Of Waste": 21032, + "Of Nobility": 21033, + "Of Karma": 21034, + "Of Grounding": 21035, + "Of the River": 21036, + "Of the Lake": 21037, + "Of the Ocean": 21038, + "Of the Bayou": 21039, + "Of the Stream": 21040, + "Of the Lady": 21041, + "Of the Maiden": 21042, + "Of the Virgin": 21043, + "Of the Hag": 21044, + "Of the Witch": 21045, + "Of Judgement": 21046, + "Of Illusion": 21047, + "Of Elusion": 21048, + "Of Combat": 21049, + "Of Attrition": 21050, + "Of Abrasion": 21051, + "Of Erosion": 21052, + "Of Searing": 21053, + "Of Stone": 21054, + "Of Stature": 21055, + "Of Fortication": 21056, + "Of Quickening": 21057, + "Of Dispatch": 21058, + "Of Daring": 21059, + "Of Dread": 21060, + "Of Suffering": 21061, + "Of Doom": 21062, + "Of Vengence": 21063, + "Of Redemption": 21064, + "Of Luck": 21065, + "Of the Avenger": 21066, + "Of the Specter": 21067, + "Of the Ghost": 21068, + "Of the Infantry": 21069, + "Of the Mosquito": 21070, + "Of the Gnat": 21071, + "Of the Fly": 21072, + "Of the Plague": 21073, + "Of Twilight": 21074, + "Of Dusk": 21075, + "Of Dawn": 21076, + "Of the Imbecile": 21077, + "Of the Idiot": 21078, + "Of the Retard": 21079, + "Of the Jujube": 21080, + "Of the Obscenity": 21081, + "Of Quota": 21082, + "Of the Maggot": 21083, + "Of Horror": 21084, + "Of Baddass": 21085, + "Of the Beast": 21086, + "Of Cruelty": 21087, + "Of Badness": 21088, + "Of the Horde": 21089, + "Of the Forest": 21090, + "Of the Lilly": 21091, + "Of the Grassy Gnoll": 21092, + "Of the Stars": 21093, + "Of the Moon": 21094, + "Of Love": 21095, + "Of the Unicorn": 21096, + "Of the Walrus": 21097, + "Of the Earth": 21098, + "Of Vines": 21099, + "Of Honor": 21100, + "Of Tribute": 21101, + "Of Credit": 21102, + "Of Admiration": 21103, + "Of Sweetness": 21104, + "Of Beauty": 21105, + "Of Pilfering": 21106, + "of Damage Amplification": 21107, + "of Hurricane": 21108, + "of Armageddon": 21109, + "of Tornado": 21110, + "of Volcano": 21111, + "of Twister": 21112, + "of Cyclone Armor": 21113, + "of Eruption": 21114, + "of Molten Boulders": 21115, + "of Firestorms": 21116, + "of Battle Command": 21117, + "of War Cry": 21118, + "of Grim Ward": 21119, + "of Battle Orders": 21120, + "of Battle Cry": 21121, + "of Concentration": 21122, + "of Item Finding": 21123, + "of Stunning": 21124, + "of Shouting": 21125, + "of Taunting": 21126, + "of Potion Finding": 21127, + "of Howling": 21128, + "of Fist of the Heavens": 21129, + "of Holy Shield": 21130, + "of Conversion": 21131, + "of Blessed Hammers": 21132, + "of Vengeance": 21133, + "of Charging": 21134, + "of Zeal": 21135, + "of Holy Bolts": 21136, + "of Sacrifice": 21137, + "of Fire Golem Summoning": 21138, + "of Bone Spirits": 21139, + "of Poison Novas": 21140, + "of Lower Resistance": 21141, + "of Iron Golem Creation": 21142, + "of Bone Imprisonment": 21143, + "of Decrepification": 21144, + "of Attraction": 21145, + "of Blood Golem Summoning": 21146, + "of Bone Spears": 21147, + "of Poison Explosion": 21148, + "of Life Tap": 21149, + "of Confusion": 21150, + "of Raise Skeletal Mages": 21151, + "of Bone Walls": 21152, + "of Terror": 21153, + "of Iron Maiden": 21154, + "of Clay Golem Summoning": 21155, + "of Corpse Explosions": 21156, + "of Poison Dagger": 21157, + "of Weaken": 21158, + "of Dim Vision": 21159, + "of Raise Skeletons": 21160, + "of Bone Armor": 21161, + "of Teeth": 21162, + "of Amplify Damage": 21163, + "of Frozen Orbs": 21164, + "of Hydras": 21165, + "of Blizzards": 21166, + "of Meteors": 21167, + "of Glacial Spikes": 21168, + "of Teleportation": 21169, + "of Enchantment": 21170, + "of Fire Walls": 21171, + "of Novas": 21172, + "of Fire Balls": 21173, + "of Blazing": 21174, + "of Ice Blasts": 21175, + "of Frost Novas": 21176, + "of Ice Bolts": 21177, + "of Charged Bolts": 21178, + "of Fire Bolts": 21179, + "of Lightning Fury": 21180, + "of Lightning Spear": 21181, + "of Freezing Arrows": 21182, + "of Fending": 21183, + "of Immolating Arrows": 21184, + "of Plague Javelin": 21185, + "of Charged Spear": 21186, + "of Guided Arrows": 21187, + "of Ice Arrows": 21188, + "of Lightning Javelin": 21189, + "of Impaling Spear": 21190, + "of Slow Missiles": 21191, + "of Exploding Arrows": 21192, + "of Poison Javelin": 21193, + "of Power Spear": 21194, + "of Multiple Shot": 21195, + "of Cold Arrows": 21196, + "of Jabbing": 21197, + "of Inner Sight": 21198, + "of Fire Arrows": 21199, + "of Magic Arrows": 21200, + "Of self-repair": 21201, + "of Dawn": 21202, + "of Inertia": 21203, + "of Joyfulness": 21204, + "ModStre8a": 21205, + "ModStre8b": 21206, + "ModStre8c": 21207, + "ModStre8d": 21208, + "ModStre8e": 21209, + "ModStre8f": 21210, + "ModStre8g": 21211, + "ModStre8h": 21212, + "ModStre8i": 21213, + "ModStre8j": 21214, + "ModStre8k": 21215, + "ModStre8l": 21216, + "ModStre8m": 21217, + "ModStre8n": 21218, + "ModStre8o": 21219, + "ModStre8p": 21220, + "ModStre8q": 21221, + "ModStre8r": 21222, + "ModStre8s": 21223, + "ModStre8t": 21224, + "ModStre8u": 21225, + "ModStre8v": 21226, + "ModStre8w": 21227, + "ModStre8x": 21228, + "ModStre8y": 21229, + "ModStre8z": 21230, + "ModStre9a": 21231, + "ModStre9b": 21232, + "ModStre9c": 21233, + "ModStre9d": 21234, + "ModStre9e": 21235, + "ModStre9f": 21236, + "ModStre9g": 21237, + "ModStre9h": 21238, + "ModStre9i": 21239, + "ModStre9s": 21240, + "ModStre9t": 21241, + "ModStre9u": 21242, + "ModStre9v": 21243, + "ModStre9w": 21244, + "ModStre9x": 21245, + "ModStre9y": 21246, + "ModStre9z": 21247, + "ModStre10a": 21248, + "ModStre10b": 21249, + "ModStre10c": 21250, + "ModStre10d": 21251, + "ModStre10e": 21252, + "ModStre10f": 21253, + "ModStre10g": 21254, + "ModStre10h": 21255, + "ModStre10i": 21256, + "ModStre10j": 21257, + "WeaponDescOrb": 21259, + "ItemexpED": 21260, + "StrGemX1": 21261, + "StrGemX2": 21262, + "StrGemX3": 21263, + "StrGemX4": 21264, + "GemeffectX11": 21265, + "GemeffectX12": 21266, + "GemeffectX13": 21267, + "GemeffectX21": 21268, + "GemeffectX22": 21269, + "GemeffectX23": 21270, + "GemeffectX31": 21271, + "GemeffectX32": 21272, + "GemeffectX33": 21273, + "GemeffectX41": 21274, + "GemeffectX42": 21275, + "GemeffectX43": 21276, + "GemeffectX51": 21277, + "GemeffectX52": 21278, + "GemeffectX53": 21279, + "GemeffectX61": 21280, + "GemeffectX62": 21281, + "GemeffectX63": 21282, + "GemeffectX71": 21283, + "GemeffectX72": 21284, + "GemeffectX73": 21285, + "Coldkill": 21286, + "Butchers Cleaver": 21287, + "Butcher's Pupil": 21288, + "Islestrike": 21289, + "Pompe's Wrath": 21290, + "Guardian Naga": 21291, + "Warlord's Trust": 21292, + "Spellsteel": 21293, + "Stormrider": 21294, + "Boneslayer Blade": 21295, + "The Minotaur": 21296, + "Suicide Branch": 21297, + "Cairn Shard": 21298, + "Arm of King Leoric": 21299, + "Blackhand Key": 21300, + "Dark Clan Crusher": 21301, + "Drulan's Tongue": 21302, + "Zakrum's Hand": 21303, + "The Fetid Sprinkler": 21304, + "Hand of Blessed Light": 21305, + "Fleshrender": 21306, + "Sureshrill Frost": 21307, + "Moonfall": 21308, + "Baezils Vortex": 21309, + "Earthshaker": 21310, + "Bloodtree Stump": 21311, + "The Gavel of Pain": 21312, + "Bloodletter": 21313, + "Coldsteal Eye": 21314, + "Hexfire": 21315, + "Blade of Ali Baba": 21316, + "Riftslash": 21317, + "Headstriker": 21318, + "Plague Bearer": 21319, + "The Atlantien": 21320, + "Crainte Vomir": 21321, + "Bing Sz Wang": 21322, + "The Vile Husk": 21323, + "Cloudcrack": 21324, + "Todesfaelle Flamme": 21325, + "Swordguard": 21326, + "Spineripper": 21327, + "Heart Carver": 21328, + "Blackbog's Sharp": 21329, + "Stormspike": 21330, + "The Impaler": 21331, + "Kelpie Snare": 21332, + "Soulfeast Tine": 21333, + "Hone Sundan": 21334, + "Spire of Honor": 21335, + "The Meat Scraper": 21336, + "Blackleach Blade": 21337, + "Athena's Wrath": 21338, + "Pierre Tombale Couant": 21339, + "Husoldal Evo": 21340, + "Grim's Burning Dead": 21341, + "Ribcracker": 21342, + "Chromatic Ire": 21343, + "Warpspear": 21344, + "Skullcollector": 21345, + "Skystrike": 21346, + "Kuko Shakaku": 21347, + "Endlessshail": 21348, + "Whichwild String": 21349, + "Godstrike Arch": 21350, + "Langer Briser": 21351, + "Pus Spiter": 21352, + "Buriza-Do Kyanon": 21353, + "Vampiregaze": 21354, + "String of Ears": 21355, + "Gorerider": 21356, + "Lavagout": 21357, + "Venom Grip": 21358, + "Visceratuant": 21359, + "Guardian Angle": 21360, + "Shaftstop": 21361, + "Skin of the Vipermagi": 21362, + "Blackhorn": 21363, + "Valkiry Wing": 21364, + "Peasent Crown": 21365, + "Demon Machine": 21366, + "Magewrath": 21367, + "Cliffkiller": 21368, + "Riphook": 21369, + "Razorswitch": 21370, + "Meatscrape": 21371, + "Coldsteel Eye": 21372, + "Pitblood Thirst": 21373, + "Gaya Wand": 21374, + "Ondal's Wisdom": 21375, + "Geronimo's Fury": 21376, + "Charsi's Favor": 21377, + "Doppleganger's Shadow": 21378, + "Deathbit": 21379, + "Warshrike": 21380, + "Gutsiphon": 21381, + "Razoredge": 21382, + "Stonerattle": 21383, + "Marrowgrinder": 21384, + "Gore Ripper": 21385, + "Bush Wacker": 21386, + "Demonlimb": 21387, + "Steelshade": 21388, + "Tomb Reaver": 21389, + "Death's Web": 21390, + "Gaia's Wrath": 21391, + "Khalim's Vengance": 21392, + "Angel's Song": 21393, + "The Reedeemer": 21394, + "Fleshbone": 21395, + "Odium": 21396, + "Blood Comet": 21397, + "Bonehew": 21398, + "Steelrend": 21399, + "Stone Crusher": 21400, + "Bul-Kathos' Might": 21401, + "Arioc's Needle": 21402, + "Shadowdancer": 21403, + "Indiego's Fancy": 21404, + "Aladdin's Eviserator": 21405, + "Tyrael's Mercy": 21406, + "Souldrain": 21407, + "Runemaster": 21408, + "Deathcleaver": 21409, + "Executioner's Justice": 21410, + "Wallace's Tear": 21411, + "Leviathan": 21412, + "The Wanderer's Blade": 21413, + "Qual'Kek's Enforcer": 21414, + "Dawnbringer": 21415, + "Dragontooth": 21416, + "Wisp": 21417, + "Gargoyle's Bite": 21418, + "Lacerator": 21419, + "Mang Song's Lesson": 21420, + "Viperfork": 21421, + "Blood Chalice": 21422, + "El Espiritu": 21423, + "The Long Rod": 21424, + "Demonhorn's Edge": 21425, + "The Ensanguinator": 21426, + "The Reaper's Toll": 21427, + "Spiritkeeper": 21428, + "Hellrack": 21429, + "Alma Negra": 21430, + "Darkforge Spawn": 21431, + "Rockhew": 21432, + "Sankenkur's Resurrection": 21433, + "Erion's Bonehandle": 21434, + "The Archon Magus": 21435, + "Widow maker": 21436, + "Catgut": 21437, + "Ghostflame": 21438, + "Shadowkiller": 21439, + "Bling Bling": 21440, + "Nebucaneezer's Storm": 21441, + "Griffon's Eye": 21442, + "Eaglewind": 21443, + "Windhammer": 21444, + "Thunderstroke": 21445, + "Giantmaimer": 21446, + "Demon's Arch": 21447, + "The Scalper": 21448, + "Bloodmoon": 21449, + "Djinnslayer": 21450, + "Cranebeak": 21451, + "Iansang's Frenzy": 21452, + "Warhound": 21453, + "Gulletwound": 21454, + "Headhunter's Glory": 21455, + "Mordoc's marauder": 21456, + "Talberd's Law": 21457, + "Amodeus's Manipulator": 21458, + "Darksoul": 21459, + "The Black Adder": 21460, + "Earthshifter": 21461, + "Nature's Peace": 21462, + "Horazon's Chalice": 21463, + "Seraph's Hymn": 21464, + "Zakarum's Salvation": 21465, + "Fleshripper": 21466, + "Stonerage": 21467, + "Blood Rain": 21468, + "Horizon's Tornado": 21469, + "Nord's Tenderizer": 21470, + "Wrath of Cain": 21471, + "Siren's call": 21472, + "Jadetalon": 21473, + "Wraithfang": 21474, + "Blademaster": 21475, + "Cerebus": 21476, + "Archangel's Deliverance": 21477, + "Sinblade": 21478, + "Runeslayer": 21479, + "Excalibur": 21480, + "Fuego Del Sol": 21481, + "Stoneraven": 21482, + "El Infierno": 21483, + "Moonrend": 21484, + "Larzuk's Champion": 21485, + "Nightsummon": 21486, + "Bonescapel": 21487, + "Rabbit Slayer": 21488, + "Pagan's Athame": 21489, + "The Swashbuckler": 21490, + "Kang's Virtue": 21491, + "Snaketongue": 21492, + "Lifechoke": 21493, + "Ethereal edge": 21494, + "Palo Grande": 21495, + "Carnageleaver": 21496, + "Ghostleach": 21497, + "Soulreaper": 21498, + "Samual's Caretaker": 21499, + "Hell's Whisper": 21500, + "The Harvester": 21501, + "Raiden's Crutch": 21502, + "The TreeEnt": 21503, + "Stormwillow": 21504, + "Moonshadow": 21505, + "Strongoak": 21506, + "Demonweb": 21507, + "Bloodraven's Charge": 21508, + "Shadefalcon": 21509, + "Robin's Yolk": 21510, + "Glimmershred": 21511, + "Wraithflight": 21512, + "Lestron's Mark": 21513, + "Banshee's Wail": 21514, + "Windstrike": 21515, + "Medusa's Gaze": 21516, + "Titanfist": 21517, + "Hadeshorn": 21518, + "Rockstopper": 21519, + "Stealskull": 21520, + "Darksight Helm": 21521, + "Crown of Thieves": 21522, + "Blackhorn's Face": 21523, + "The Spirit Shroud": 21524, + "Skin of the Flayed One": 21525, + "Ironpelt": 21526, + "Spiritforge": 21527, + "Crow Caw": 21528, + "Duriel's Shell": 21529, + "Skullder's Ire": 21530, + "Toothrow": 21531, + "Atma's Wail": 21532, + "Black Hades": 21533, + "Corpsemourn": 21534, + "Que-hegan's Wisdom": 21535, + "Moser's Blessed Circle": 21536, + "Stormchaser": 21537, + "Tiamat's Rebuke": 21538, + "Gerke's Sanctuary": 21539, + "Radimant's Sphere": 21540, + "Gravepalm": 21541, + "Ghoulhide": 21542, + "Hellmouth": 21543, + "Infernostride": 21544, + "Waterwalk": 21545, + "Silkweave": 21546, + "Wartraveler": 21547, + "Razortail": 21548, + "Gloomstrap": 21549, + "Snowclash": 21550, + "Thudergod's Vigor": 21551, + "Lidless Wall": 21552, + "Lanceguard": 21553, + "Squire's Cover": 21554, + "Boneflame": 21555, + "Steelpillar": 21556, + "Nightwing's Veil": 21557, + "Hightower's Watch": 21558, + "Crown of Ages": 21559, + "Andariel's Visage": 21560, + "Darkfear": 21561, + "Dragonscale": 21562, + "Steel Carapice": 21563, + "Ashrera's Wired Frame": 21564, + "Rainbow Facet": 21565, + "Ravenlore": 21566, + "Boneshade": 21567, + "Nethercrow": 21568, + "Hellwarden's Husk": 21569, + "Flamebellow": 21570, + "Fathom": 21571, + "Wolfhowl": 21572, + "Spirit Ward": 21573, + "Kira's Guardian": 21574, + "Orumus' Robes": 21575, + "Gheed's Fortune": 21576, + "The Vicar": 21577, + "Stormlash": 21578, + "Halaberd's Reign": 21579, + "Parkersor's Calm": 21580, + "Warriv's Warder": 21581, + "Spike Thorn": 21582, + "Dracul's Grasp": 21583, + "Frostwind": 21584, + "Templar's Might": 21585, + "Eschuta's temper": 21620, + "Firelizard's Talons": 21587, + "Sandstorm Trek": 21588, + "Marrowwalk": 21589, + "Heaven's Light": 21590, + "Merman's Speed": 21591, + "Arachnid Mesh": 21592, + "Nosferatu's Coil": 21593, + "Metalgird": 21594, + "Verdugo's Hearty Cord": 21595, + "Sigurd's Staunch": 21596, + "Carrion Wind": 21597, + "Giantskull": 21598, + "Ironward": 21599, + "Gillian's Brazier": 21600, + "Drakeflame": 21601, + "Dust Storm": 21602, + "Skulltred": 21603, + "Alma's Reflection": 21604, + "Drulan's Tounge": 21605, + "Sacred Charge": 21606, + "Bul-Kathos": 21607, + "Saracen's Chance": 21608, + "Highlord's Wrath": 21609, + "Raven Frost": 21610, + "Dwarf Star": 21611, + "Atma's Scarab": 21612, + "Mara's Kaleidoscope": 21613, + "Crescent Moon": 21614, + "The Rising Sun": 21615, + "The Cat's Eye": 21616, + "Bul Katho's Wedding Band": 21617, + "Rings": 21618, + "Metalgrid": 21619, + "Stormshield": 21621, + "Blackoak Shield": 21622, + "Ormus' Robes": 21623, + "Arkaine's Valor": 21624, + "The Gladiator's Bane": 21625, + "Veil of Steel": 21626, + "Harlequin Crest": 21627, + "Lance Guard": 21628, + "Kerke's Sanctuary": 21629, + "Mosers Blessed Circle": 21630, + "Que-Hegan's Wisdon": 21631, + "Guardian Angel": 21632, + "Skin of the Flayerd One": 21633, + "Armor": 21634, + "Windforce": 21635, + "Eaglehorn": 21636, + "Gimmershred": 21637, + "Widowmaker": 21638, + "Stormspire": 21639, + "Naj's Puzzler": 21640, + "Ethereal Edge": 21641, + "Wizardspike": 21642, + "The Grandfather": 21643, + "Doombringer": 21644, + "Tyrael's Might": 21645, + "Lightsabre": 21646, + "The Cranium Basher": 21647, + "Schaefer's Hammer": 21648, + "Baranar's Star": 21649, + "Deaths's Web": 21650, + "Messerschmidt's Reaver": 21651, + "Hellslayer": 21652, + "Endlesshail": 21653, + "The Atlantian": 21654, + "Riftlash": 21655, + "Baezil's Vortex": 21656, + "Zakarum's Hand": 21657, + "Carin Shard": 21658, + "The Minataur": 21659, + "Trang-Oul's Avatar": 21660, + "Trang-Oul's Guise": 21661, + "Trang-Oul's Wing": 21662, + "Trang-Oul's Mask": 21663, + "Trang-Oul's Scales": 21664, + "Trang-Oul's Claws": 21665, + "Trang-Oul's Girth": 21666, + "Natalya's Odium": 21667, + "Natalya's Totem": 21668, + "Natalya's Mark": 21669, + "Natalya's Shadow": 21670, + "Natalya's Soul": 21671, + "Griswold's Legacy": 21672, + "Griswolds's Redemption": 21673, + "Griswold's Honor": 21674, + "Griswold's Heart": 21675, + "Griswold's Valor": 21676, + "Tang's Imperial Robes": 21677, + "Tang's Fore-Fathers": 21678, + "Tang's Rule": 21679, + "Tang's Throne": 21680, + "Tang's Battle Standard": 21681, + "Ogun's Fierce Visage": 21682, + "Ogun's Shadow": 21683, + "Ogun's Lash": 21684, + "Ogun's Vengeance": 21685, + "Bul-Kathos' Warden": 21686, + "Bul-Kathos' Children": 21687, + "Bul-Kathos' Sacred Charge": 21688, + "Bul-Kathos' Tribal Guardian": 21689, + "Bul-Kathos' Custodian": 21690, + "Flowkrad's Howl": 21691, + "Flowkrad's Grin": 21692, + "Flowkrad's Fur": 21693, + "Flowkrad's Paws": 21694, + "Flowkrad's Sinew": 21695, + "Aldur's Watchtower": 21696, + "Aldur's Stony Gaze": 21697, + "Aldur's Deception": 21698, + "Aldur's Guantlet": 21699, + "Aldur's Advance": 21700, + "M'avina's Battle Hymn": 21701, + "M'avina's True Sight": 21702, + "M'avina's Embrace": 21703, + "M'avina's Icy Clutch": 21704, + "M'avina's Tenet": 21705, + "M'avina's Caster": 21706, + "Sazabi's Grand Tribute": 21707, + "Sazabi's Cobalt Redeemer": 21708, + "Sazabi's Ghost Liberator": 21709, + "Sazabi's Mental Sheath": 21710, + "Hwanin's Majesty": 21711, + "Hwanin's Justice": 21712, + "Hwanin's Splendor": 21713, + "Hwanin's Refuge": 21714, + "Hwanin's Cordon": 21715, + "The Disciple": 21716, + "Telling of Beads": 21717, + "Laying of Hands": 21718, + "Rite of Passage": 21719, + "Spiritual Custodian": 21720, + "Credendum": 21721, + "Cow King's Leathers": 21722, + "Cow King's Horns": 21723, + "Cow King's Hide": 21724, + "Cow King's Hoofs": 21725, + "Aragon's Masterpiece": 21726, + "Aragon's Sunfire": 21727, + "Aragon's Icy Stare": 21728, + "Aragon's Storm Cloud": 21729, + "Orphan's Call": 21730, + "Guillaume's Face": 21731, + "Willhelm's Pride": 21732, + "Magnus' Skin": 21733, + "Wihtstan's Guard": 21734, + "Titan's Revenge": 21735, + "Shakabra's Crux": 21736, + "Lycander's Aim": 21737, + "Shadow's Touch": 21738, + "The Prowler": 21739, + "Mortal Crescent": 21740, + "Cutthroat": 21741, + "Sarmichian Justice": 21742, + "Annihilus": 21743, + "Arreat's Face": 21744, + "The Harbinger": 21745, + "Doomseer": 21746, + "Howling Visage": 21747, + "Terra": 21748, + "Syrian": 21749, + "Jalal's Mane": 21750, + "Malignant": 21751, + "Apothecary's Tote": 21752, + "Apocrypha": 21753, + "Foci of Visjerei": 21754, + "Homunculus": 21755, + "Aurora's Guard": 21756, + "Crest of Morn": 21757, + "Herald of Zakarum": 21758, + "Akarat's Protector": 21759, + "Ancient Eye": 21760, + "Globe of Visjerei": 21761, + "The Oculus": 21762, + "Phoenix Egg": 21763, + "Xenos": 21764, + "Nagas": 21765, + "Wyvern's Head": 21766, + "Sightless Veil": 21767, + "ChampionFormatX": 21768, + "EskillKickSing": 21769, + "EskillKickPlur": 21770, + "EskillPetLife": 21771, + "EskillWolfDef": 21772, + "EskillPassiveFeral": 21773, + "Eskillperhit12": 21774, + "Eskillincasehit": 21775, + "Eskillincasemastery": 21776, + "Eskillincaseraven": 21777, + "pad": 21779, + "axf": 21780, + "Eskillkickdamage": 21781, + "ModStre10k": 21782, + "ModStre10L": 21783, + "Class Specific": 21784, + "fana": 21785, + "qsta5q14": 21786, + "qstsa5q42a": 21787, + "qstsa5q31a": 21788, + "qstsa5q21a": 21789, + "qstsa5q43a": 21790, + "qstsa5q62a": 21791, + "qstsa5q61a": 21792, + "act1X": 21797, + "act2X": 21798, + "act3X": 21799, + "act4X": 21800, + "strepilogueX": 21801, + "act5X": 21802, + "strlastcinematic": 21803, + "CfgSay7": 21804, + "0sc": 21805, + "tr2": 21806, + "of Lightning Strike": 21807, + "of Plague Jab": 21808, + "of Charged Strike": 21809, + "of Impaling Strike": 21810, + "of Poison Jab": 21811, + "of Power Strike": 21812, + "of the Colossus": 21813, + "of the Kraken": 21814, + "Tal Rasha's Wrappings": 21815, + "Tal Rasha's Fire-Spun Cloth": 21816, + "Tal Rasha's Adjudication": 21817, + "Tal Rasha's Howling Wind": 21818, + "Tal Rasha's Lidless Eye": 21819, + "Tal Rasha's Horadric Crest": 21820, + "Hwanin's Seal": 21821, + "Heaven's Brethren": 21822, + "Dangoon's Teaching": 21823, + "Ondal's Almighty": 21824, + "Heaven's Taebaek": 21825, + "Haemosu's Adament": 21826, + "Lycander's Flank": 21827, + "Constricting Ring": 21828, + "Ginther's Rift": 21829, + "Naj's Ancient Set": 21830, + "Naj's Light Plate": 21831, + "Naj's Circlet": 21832, + "Sander's Superstition": 21833, + "Sander's Taboo": 21834, + "Sander's Basis": 21835, + "Sander's Derby": 21836, + "Sander's Court Jester": 21837, + "Ghost Liberator": 21838, + "Wilhelm's Pride": 21839, + "Immortal King's Stone Crusher": 21840, + "Immortal King's Pillar": 21841, + "Immortal King's Forge": 21842, + "Immortal King's Detail": 21843, + "Immortal King's Soul Cage \tImmortal King's Soul Cage": 21844, + "Immortal King's Will": 21845, + "Immortal King": 21846, + "Aldur's Gauntlet": 21847, + "Ancient Statue 3": 21848, + "Ancient Statue 2": 21849, + "Ancient Statue 1": 21850, + "Baal Subject 1": 21851, + "Baal Subject 2": 21852, + "Baal Subject 3": 21853, + "Baal Subject 4": 21854, + "Baal Subject 5": 21855, + "Baal Subject 6": 21856, + "Baal Subject 6a": 21857, + "Baal Subject 6b": 21858, + "Baal Crab Clone": 21859, + "Baal Crab to Stairs": 21860, + "BaalColdMage": 21861, + "Baal Subject Mummy": 21862, + "Baal Tentacle": 21863, + "Baals Minion": 21864, + "Hell1": 21865, + "Hell2": 21866, + "Hell3": 21867, + "To Hell1": 21868, + "To Hell2": 21869, + "To Hell3": 21870, + "Lord of Destruction": 21871, + "EskillPerBlade": 21873, + "ExInsertSockets": 21874, + "McAuley's Superstition": 21875, + "McAuley's Taboo": 21876, + "McAuley's Riprap": 21877, + "McAuley's Paragon": 21878, + "McAuley's Folly": 21879, + "qstsa5q62b": 21881, + "of the Plague": 21883, + "Go South": 21884, + "ItemExpansiveChancX": 21885, + "ItemExpansiveChanc1": 21886, + "ItemExpansiveChanc2": 21887, + "ItemExpcharmdesc": 21888, + "StrMercEx12": 21889, + "StrMercEx14": 21890, + "StrMercEx15": 21891, + "Eskillelementaldmg": 21892, + "Playersubtitles29": 21893, + "Playersubtitles30": 21894, + "LeaveCampDru": 21895, + "LeaveCampAss": 21896, + "EnterDOEAss": 21897, + "EnterDOEDru": 21898, + "EnterBurialAss": 21899, + "EnterBurialDru": 21900, + "EnterMonasteryAss": 21901, + "EnterMonasteryDru": 21902, + "EnterForgottenTAss": 21903, + "EnterForgottenTDru": 21904, + "EnterJailAss": 21905, + "EnterJailDru": 21906, + "EnterCatacombsAss": 21907, + "EnterCatacombsDru": 21908, + "CompletingDOEAss": 21909, + "CompletingDOEDru": 21910, + "CompletingBurialAss": 21911, + "CompletingBurialDru": 21912, + "FindingInifusAss": 21913, + "FindingInifusDru": 21914, + "FindingCairnAss": 21915, + "FindingCairnDru": 21916, + "FindingTristramAss": 21917, + "FindingTristramDru": 21918, + "RescueCainAss": 21919, + "RescueCainDru": 21920, + "HoradricMalusAss": 21921, + "HoradricMalusDru": 21922, + "CompletingAndarielAss": 21925, + "CompletingAndarielDru": 21926, + "EnteringRadamentAss": 21927, + "EnteringRadamentDru": 21928, + "CompletingRadamentAss": 21929, + "CompletingRadamentDru": 21930, + "BeginTaintedSunAss": 21931, + "BeginTaintedSunDru": 21932, + "EnteringClawViperAss": 21933, + "EnteringClawViperDru": 21934, + "CompletingTaintedSunAss": 21935, + "CompletingTaintedSunDru": 21936, + "EnteringArcaneAss": 21937, + "EnteringArcaneDru": 21938, + "FindingSummonerAss": 21939, + "FindingSummonerDru": 21940, + "CompletingSummonerAss": 21941, + "CompletingSummonerDru": 21942, + "FindingdecoyTombAss": 21943, + "FindingdecoyTombDru": 21944, + "FindingTrueTombAss": 21945, + "FindingTrueTombDru": 21946, + "CompletingTombAss": 21947, + "CompletingTombDru": 21948, + "FindingLamEsenAss": 21949, + "FindingLamEsenDru": 21950, + "CompletingLamEsenAss": 21952, + "CompletingLamEsenDru": 21953, + "FindingBeneathCityAss": 21954, + "FindingBeneathCityDru": 21955, + "FindingDrainLeverAss": 21956, + "FindingDrainLeverDru": 21957, + "CompletingBeneathCityAss": 21958, + "CompletingBeneathCityDru": 21959, + "CompletingBladeAss": 21960, + "CompletingBladeDru": 21961, + "FindingJadeFigAss": 21962, + "FindingJadeFigDru": 21963, + "FindingTempleAss": 21964, + "FindingTempleDru": 21965, + "CompletingTempleAss": 21966, + "CompletingTempleDru": 21967, + "FindingGuardianTowerAss": 21968, + "FindingGuardianTowerDru": 21969, + "CompletingGuardianTowerAss": 21971, + "FreezingIzualAss": 21973, + "FreezingIzualDru": 21974, + "KillingdDiabloSor": 21975, + "KillingdDiabloBar": 21976, + "KillingdDiabloNec": 21977, + "KillingdDiabloPal": 21978, + "KillingdDiabloAms": 21979, + "KillingdDiabloAss": 21980, + "KillingdDiabloDru": 21981, + "LeavingTownAct5Sor": 21982, + "LeavingTownAct5Bar": 21983, + "LeavingTownAct5Nec": 21984, + "LeavingTownAct5Pal": 21985, + "LeavingTownAct5Ams": 21986, + "LeavingTownAct5Ass": 21987, + "LeavingTownAct5Dru": 21988, + "CompletingStopSiegeSor": 21989, + "CompletingStopSiegeBar": 21990, + "CompletingStopSiegeNec": 21991, + "CompletingStopSiegePal": 21992, + "CompletingStopSiegeAms": 21993, + "CompletingStopSiegeAss": 21994, + "CompletingStopSiegeDru": 21995, + "RescueQual-KehkAct5Sor": 21996, + "RescueQual-KehkAct5Bar": 21997, + "RescueQual-KehkAct5Nec": 21998, + "RescueQual-KehkAct5Pal": 21999, + "RescueQual-KehkAct5Ams": 22000, + "RescueQual-KehkAct5Ass": 22001, + "RescueQual-KehkAct5Dru": 22002, + "EnteringNihlathakAct5Sor": 22003, + "EnteringNihlathakAct5Bar": 22004, + "EnteringNihlathakAct5Nec": 22005, + "EnteringNihlathakAct5Pal": 22006, + "EnteringNihlathakAct5Ams": 22007, + "EnteringNihlathakAct5Ass": 22008, + "EnteringNihlathakAct5Dru": 22009, + "CompletingNihlathakAct5Sor": 22010, + "CompletingNihlathakAct5Bar": 22011, + "CompletingNihlathakAct5Nec": 22012, + "CompletingNihlathakAct5Pal": 22013, + "CompletingNihlathakAct5Ams": 22014, + "CompletingNihlathakAct5Ass": 22015, + "CompletingNihlathakAct5Dru": 22016, + "EnteringTopMountAct5Sor": 22017, + "EnteringTopMountAct5Bar": 22018, + "EnteringTopMountAct5Nec": 22019, + "EnteringTopMountAct5Pal": 22020, + "EnteringTopMountAct5Ams": 22021, + "EnteringTopMountAct5Ass": 22022, + "EnteringTopMountAct5Dru": 22023, + "EnteringWorldstoneAct5Sor": 22024, + "EnteringWorldstoneAct5Bar": 22025, + "EnteringWorldstoneAct5Nec": 22026, + "EnteringWorldstoneAct5Pal": 22027, + "EnteringWorldstoneAct5Ams": 22028, + "EnteringWorldstoneAct5Ass": 22029, + "EnteringWorldstoneAct5Dru": 22030, + "CompletingDefeatBaalAct5Sor": 22031, + "CompletingDefeatBaalAct5Bar": 22032, + "CompletingDefeatBaalAct5Nec": 22033, + "CompletingDefeatBaalAct5Pal": 22034, + "CompletingDefeatBaalAct5Ams": 22035, + "CompletingDefeatBaalAct5Ass": 22036, + "CompletingDefeatBaalAct5Dru": 22037, + "Skillname222": 22038, + "Skillsd222": 22039, + "Skillld222": 22040, + "Skillan222": 22041, + "Skillname223": 22046, + "Skillsd223": 22047, + "Skillld223": 22048, + "Skillan223": 22049, + "Skillname225": 22050, + "Skillsd225": 22051, + "Skillld225": 22052, + "Skillan225": 22053, + "Skillname226": 22054, + "Skillsd226": 22055, + "Skillld226": 22056, + "Skillan226": 22057, + "Skillname227": 22058, + "Skillsd227": 22059, + "Skillld227": 22060, + "Skillan227": 22061, + "Skillname228": 22062, + "Skillsd228": 22063, + "Skillld228": 22064, + "Skillan228": 22065, + "Skillname229": 22066, + "Skillsd229": 22067, + "Skillld229": 22068, + "Skillan229": 22069, + "Skillname230": 22070, + "Skillsd230": 22071, + "Skillld230": 22072, + "Skillan230": 22073, + "Skillname231": 22074, + "Skillsd231": 22075, + "Skillld231": 22076, + "Skillan231": 22077, + "Skillname232": 22078, + "Skillsd232": 22079, + "Skillld232": 22080, + "Skillan232": 22081, + "Skillname233": 22082, + "Skillsd233": 22083, + "Skillld233": 22084, + "Skillan233": 22085, + "Skillname234": 22086, + "Skillsd234": 22087, + "Skillld234": 22088, + "Skillan234": 22089, + "Skillname235": 22090, + "Skillsd235": 22091, + "Skillld235": 22092, + "Skillan235": 22093, + "Skillname236": 22094, + "Skillsd236": 22095, + "Skillld236": 22096, + "Skillan236": 22097, + "Skillname237": 22098, + "Skillsd237": 22099, + "Skillld237": 22100, + "Skillan237": 22101, + "Skillname238": 22102, + "Skillsd238": 22103, + "Skillld238": 22104, + "Skillan238": 22105, + "Skillname239": 22106, + "Skillsd239": 22107, + "Skillld239": 22108, + "Skillan239": 22109, + "Skillname240": 22110, + "Skillsd240": 22111, + "Skillld240": 22112, + "Skillan240": 22113, + "Skillname241": 22114, + "Skillsd241": 22115, + "Skillld241": 22116, + "Skillan241": 22117, + "Skillname242": 22118, + "Skillsd242": 22119, + "Skillld242": 22120, + "Skillan242": 22121, + "Skillname243": 22122, + "Skillsd243": 22123, + "Skillld243": 22124, + "Skillan243": 22125, + "Skillname244": 22126, + "Skillsd244": 22127, + "Skillld244": 22128, + "Skillan244": 22129, + "Skillname245": 22130, + "Skillsd245": 22131, + "Skillld245": 22132, + "Skillan245": 22133, + "Skillname246": 22134, + "Skillsd246": 22135, + "Skillld246": 22136, + "Skillan246": 22137, + "Skillname247": 22138, + "Skillsd247": 22139, + "Skillld247": 22140, + "Skillan247": 22141, + "Skillname248": 22142, + "Skillsd248": 22143, + "Skillld248": 22144, + "Skillan248": 22145, + "Skillname249": 22146, + "Skillsd249": 22147, + "Skillld249": 22148, + "Skillan249": 22149, + "Skillname250": 22150, + "Skillsd250": 22151, + "Skillld250": 22152, + "Skillan250": 22153, + "Skillname251": 22154, + "Skillsd251": 22155, + "Skillld251": 22156, + "Skillan251": 22157, + "Skillname252": 22158, + "Skillsd252": 22159, + "Skillld252": 22160, + "Skillan252": 22161, + "Skillname253": 22162, + "Skillsd253": 22163, + "Skillld253": 22164, + "Skillan253": 22165, + "Skillname254": 22166, + "Skillsd254": 22167, + "Skillld254": 22168, + "Skillan254": 22169, + "Skillname255": 22170, + "Skillsd255": 22171, + "Skillld255": 22172, + "Skillan255": 22173, + "Skillname256": 22174, + "Skillsd256": 22175, + "Skillld256": 22176, + "Skillan256": 22177, + "Skillname257": 22178, + "Skillsd257": 22179, + "Skillld257": 22180, + "Skillan257": 22181, + "Skillname258": 22182, + "Skillsd258": 22183, + "Skillld258": 22184, + "Skillan258": 22185, + "Skillname259": 22186, + "Skillsd259": 22187, + "Skillld259": 22188, + "Skillan259": 22189, + "Skillname260": 22190, + "Skillsd260": 22191, + "Skillld260": 22192, + "Skillan260": 22193, + "Skillname261": 22194, + "Skillsd261": 22195, + "Skillld261": 22196, + "Skillan261": 22197, + "Skillname262": 22198, + "Skillsd262": 22199, + "Skillld262": 22200, + "Skillan262": 22201, + "Skillname263": 22202, + "Skillsd263": 22203, + "Skillld263": 22204, + "Skillan263": 22205, + "Skillname264": 22206, + "Skillsd264": 22207, + "Skillld264": 22208, + "Skillan264": 22209, + "Skillname265": 22210, + "Skillsd265": 22211, + "Skillld265": 22212, + "Skillan265": 22213, + "Skillname266": 22214, + "Skillsd266": 22215, + "Skillld266": 22216, + "Skillan266": 22217, + "Skillname267": 22218, + "Skillsd267": 22219, + "Skillld267": 22220, + "Skillan267": 22221, + "Skillname268": 22222, + "Skillsd268": 22223, + "Skillld268": 22224, + "Skillan268": 22225, + "Skillname269": 22226, + "Skillsd269": 22227, + "Skillld269": 22228, + "Skillan269": 22229, + "Skillname270": 22230, + "Skillsd270": 22231, + "Skillld270": 22232, + "Skillan270": 22233, + "Skillname271": 22234, + "Skillsd271": 22235, + "Skillld271": 22236, + "Skillan271": 22237, + "Skillname272": 22238, + "Skillsd272": 22239, + "Skillld272": 22240, + "Skillan272": 22241, + "Skillname273": 22242, + "Skillsd273": 22243, + "Skillld273": 22244, + "Skillan273": 22245, + "Skillname274": 22246, + "Skillsd274": 22247, + "Skillld274": 22248, + "Skillan274": 22249, + "Skillname275": 22250, + "Skillsd275": 22251, + "Skillld275": 22252, + "Skillan275": 22253, + "Skillname276": 22254, + "Skillsd276": 22255, + "Skillld276": 22256, + "Skillan276": 22257, + "Skillname277": 22258, + "Skillsd277": 22259, + "Skillld277": 22260, + "Skillan277": 22261, + "Skillname278": 22262, + "Skillsd278": 22263, + "Skillld278": 22264, + "Skillan278": 22265, + "Skillname279": 22266, + "Skillsd279": 22267, + "Skillld279": 22268, + "Skillan279": 22269, + "Skillname280": 22270, + "Skillsd280": 22271, + "Skillld280": 22272, + "Skillan280": 22273, + "Skillname281": 22274, + "Skillsd281": 22275, + "Skillld281": 22276, + "Skillan281": 22277, + "ESkillPerKick": 22286, + "EskillLifeSteal": 22287, + "Eskillchancetostun": 22288, + "Eskillchancetoafflict": 22289, + "Eskillpowerup1": 22290, + "Eskillpowerup2": 22291, + "Eskillpowerup3": 22292, + "Eskillpowerupadd": 22293, + "Eskillsinishup": 22294, + "Eskillpudlife": 22295, + "Eskillpudmana": 22296, + "Eskillpudburning": 22297, + "Eskillpuddgmper": 22298, + "Eskilllowerresis": 22299, + "Eskilltomeleeattacks": 22300, + "EskillManaSteal": 22301, + "Eskillferalpets": 22302, + "Eskillpercentatt": 22303, + "Eskillpercentlif": 22304, + "Eskillpercentdmg": 22305, + "Eskillfinishmove": 22306, + "Eskillmanarecov": 22307, + "Eskillphoenix1": 22308, + "Eskillphoenix2": 22309, + "Eskillphoenix3": 22310, + "Eskillthunder1": 22311, + "Eskillthunder2": 22312, + "Eskillthunder3": 22313, + "Eskillfistsoffire1": 22314, + "Eskillfistsoffire2": 22315, + "Eskillfistsoffire3": 22316, + "Eskillbladesofice1": 22317, + "Eskillbladesofice2": 22318, + "Eskillbladesofice3": 22319, + "strUI5": 22320, + "strUI6": 22321, + "strUI7": 22322, + "strUI8": 22323, + "strUI9": 22324, + "strUI10": 22325, + "strUI11": 22326, + "strUI12": 22327, + "strUI13": 22328, + "strUI14": 22329, + "UIFenirsui": 22330, + "UiRescuedBarUI": 22331, + "UiShadowUI": 22332, + "StrUI18": 22333, + "Spike Generator": 22334, + "Charged Bolt Sentry": 22335, + "Lightning Sentry": 22336, + "Blade Creeper": 22337, + "Invis Pet": 22338, + "Druid Hawk": 22339, + "Druid Wolf": 22340, + "Druid Totem": 22341, + "Druid Fenris": 22342, + "Druid Spirit Wolf": 22343, + "Druid Bear": 22344, + "Druid Plague Poppy": 22345, + "Druid Cycle of Life": 22346, + "Vine Creature": 22347, + "Eagleexp": 22348, + "Wolf": 22349, + "Bear": 22350, + "Siege Door": 22351, + "Siege Beast": 22358, + "Hell Temptress": 22389, + "Blood Temptress": 22390, + "Blood Witch": 22394, + "Hell Witch": 22395, + "CatapultN": 22411, + "CatapultS": 22412, + "CatapultE": 22413, + "CatapultW": 22414, + "Frozen Horror1": 22415, + "Frozen Horror2": 22416, + "Frozen Horror3": 22417, + "Frozen Horror4": 22418, + "Frozen Horror5": 22419, + "Blood Lord1": 22420, + "Blood Lord2": 22421, + "Blood Lord3": 22422, + "Blood Lord4": 22423, + "Blood Lord5": 22424, + "Catapult Spotter N": 22425, + "Catapult Spotter S": 22426, + "Catapult Spotter E": 22427, + "Catapult Spotter W": 22428, + "Catapult Spotter Siege": 22429, + "CatapultSiege": 22430, + "Barricade Wall Right": 22431, + "Barricade Wall Left": 22432, + "Barricade Door": 22433, + "Barricade Tower": 22434, + "Siege Boss": 22435, // shenk the overseer + "Evil hut": 22436, + "Death Mauler1": 22437, + "Death Mauler2": 22438, + "Death Mauler3": 22439, + "Death Mauler4": 22440, + "Death Mauler5": 22441, + "SnowYeti1": 22442, + "SnowYeti2": 22443, + "SnowYeti3": 22444, + "SnowYeti4": 22445, + "Baal Throne": 22446, + "Baal Crab": 22447, + "Baal Taunt": 22448, + "Putrid Defiler1": 22449, + "Putrid Defiler2": 22450, + "Putrid Defiler3": 22451, + "Putrid Defiler4": 22452, + "Putrid Defiler5": 22453, + "Pain Worm1": 22454, + "Pain Worm2": 22455, + "Pain Worm3": 22456, + "Pain Worm4": 22457, + "Pain Worm5": 22458, + "WolfRider5": 22459, + "WolfRider4": 22460, + "WolfRider3": 22461, + "WolfRider2": 22462, + "WolfRider1": 22463, + "Oak Sage": 22464, + "Heart of Wolverine": 22465, + "Spirit of Barbs": 22466, + "Shadow Warrior": 22467, + "Death Sentry": 22468, + "Inferno Sentry": 22469, + "Shadow Master": 22470, + "Wake of Destruction": 22471, + "Ghostly": 22472, + "Fanatic": 22473, + "Possessed": 22474, + "Berserk": 22475, + "Larzuk": 22476, + "Drehya": 22477, + "Malah": 22478, + "Nihlathak Town": 22479, + "Qual-Kehk": 22480, + "Act 5 Townguard": 22481, + "Act 5 Combatant": 22482, + "Nihlathak": 22483, + "POW": 22484, + "Moe": 22485, + "Curly": 22486, + "Larry": 22487, + "Ancient Barbarian 3": 22488, + "Ancient Barbarian 2": 22489, + "Ancient Barbarian 1": 22490, + "Blaze Ripper": 22491, + "Magma Torquer": 22492, + "Sharp Tooth Sayer": 22493, + "Vinvear Molech": 22494, + "Anodized Elite": 22495, + "Snapchip Shatter": 22496, + "Pindleskin": 22497, + "Threash Socket": 22498, + "Eyeback Unleashed": 22499, + "Megaflow Rectifier": 22500, // eldritch the rectifier + "Dac Farren": 22501, + "Bonesaw Breaker": 22502, + "Axe Dweller": 22503, + "Frozenstein": 22504, + "strDruidOnly": 22505, + "strAssassinOnly": 22506, + "strAmazonOnly": 22507, + "strBarbarianOnly": 22508, + "StrSklTree26": 22509, + "StrSklTree27": 22510, + "StrSklTree28": 22511, + "StrSklTree29": 22512, + "StrSklTree30": 22513, + "StrSklTree31": 22514, + "StrSklTree32": 22515, + "StrSklTree33": 22516, + "StrSklTree34": 22517, + "chestr": 22520, + "barrel wilderness": 22521, + "woodchestL": 22522, + "burialchestL": 22523, + "burialchestR": 22524, + "ChestL": 22527, + "ChestSL": 22528, + "ChestSR": 22529, + "woodchestR": 22530, + "chestR": 22531, + "burningbodies": 22532, + "burningpit": 22533, + "tribal flag": 22534, + "flag widlerness": 22535, + "eflg": 22536, + "chan": 22537, + "jar": 22538, + "jar2": 22539, + "jar3": 22540, + "swingingheads": 22541, + "pole": 22542, + "animatedskullsandrocks": 22543, + "hellgate": 22544, + "gate": 22545, + "banner1": 22546, + "banner2": 22547, + "mrpole": 22548, + "pene": 22549, + "debris": 22550, + "woodchest2R": 22551, + "woodchest2L": 22552, + "object1": 22553, + "magic shrine2": 22554, + "torch2": 22555, + "torch1": 22556, + "tomb3": 22557, + "tomb2": 22558, + "tomb1": 22559, + "ttor": 22560, + "icecave_torch2": 22561, + "icecave_torch1": 22562, + "clientsmoke": 22563, + "deadbarbarian": 22564, + "deadbarbarian18": 22565, + "uncle f#%* comedy central(c)\tMoe": 22566, + "cagedwussie1": 22567, + "icecaveshrine2": 22568, + "icecavejar4": 22569, + "icecavejar3": 22570, + "icecavejar2": 22571, + "icecavejar1": 22572, + "evilurn": 22573, + "secret object": 22574, + "Altar": 22575, + "Ldeathpole": 22576, + "deathpole": 22577, + "explodingchest": 22578, + "banner 2": 22579, + "banner 1": 22580, + "pileofskullsandrocks": 22581, + "animated skulland rockpile": 22582, + "jar1": 22583, + "etorch2": 22584, + "ettr": 22585, + "ecfra": 22586, + "etorch1": 22587, + "healthshrine": 22588, + "explodingbarrel": 22589, + "flag wilderness": 22590, + "object": 22591, + "Shrine2wilderness": 22592, + "Shrine3wilderness": 22593, + "pyox": 22594, + "ptox": 22595, + "Siege Control": 22596, + "mrjar": 22597, + "object2": 22598, + "mrbox": 22599, + "tomb3L": 22600, + "tomb2L": 22601, + "tomb1L": 22602, + "red light": 22603, + "groundtombL": 22604, + "groundtomb": 22605, + "deadperson": 22606, + "candles": 22607, + "sbub": 22608, + "ubub": 22609, + "deadperson2": 22610, + "Prison Door": 22611, + "ancientsaltar": 22612, + "hiddenstash": 22613, + "eweaponrackL": 22614, + "eweaponrackR": 22615, + "earmorstandL": 22616, + "earmorstandR": 22617, + "qstsa5q1": 22618, + "qsta5q11": 22619, + "qsta5q12": 22620, + "qsta5q13": 22621, + "qstsa5q2": 22622, + "qstsa5q21": 22623, + "qstsa5q22": 22624, + "qstsa5q23": 22625, + "qstsa5q24": 22626, + "qstsa5q3": 22627, + "qstsa5q31": 22628, + "qstsa5q32": 22629, + "qstsa5q33": 22630, + "qstsa5q34": 22631, + "qstsa5q35": 22632, + "qstsa5q4": 22633, + "qstsa5q41": 22634, + "qstsa5q42": 22635, + "qstsa5q43": 22636, + "qstsa5q5": 22637, + "qstsa5q51": 22638, + "qstsa5q52": 22639, + "qstsa5q53": 22640, + "qstsa5q6": 22641, + "qstsa5q61": 22642, + "qstsa5q62": 22643, + "qstsa5q63": 22644, + "qstsa5q64": 22645, + "Harrogath": 22646, + "Bloody Foothills": 22647, + "Rigid Highlands": 22648, + "Arreat Plateau": 22649, + "Crystalized Cavern Level 1": 22650, + "Cellar of Pity": 22651, + "Crystalized Cavern Level 2": 22652, + "Echo Chamber": 22653, + "Tundra Wastelands": 22654, + "Glacial Caves Level 1": 22655, + "Glacial Caves Level 2": 22656, + "Rocky Summit": 22657, + "Nihlathaks Temple": 22658, + "Halls of Anguish": 22659, + "Halls of Death's Calling": 22660, + "Halls of Tormented Insanity": 22661, + "Halls of Vaught": 22662, + "The Worldstone Keep Level 1": 22663, + "The Worldstone Keep Level 2": 22664, + "The Worldstone Keep Level 3": 22665, + "The Worldstone Chamber": 22666, + "Throne of Destruction": 22667, + "To Harrogath": 22668, + "To The Bloody Foothills": 22669, + "To The Rigid Highlands": 22670, + "To The Arreat Plateau": 22671, + "To The Crystalized Cavern Level 1": 22672, + "To The Cellar of Pity": 22673, + "To The Crystalized Cavern Level 2": 22674, + "To The Echo Chamber": 22675, + "To The Tundra Wastelands": 22676, + "To The Glacier Caves Level 1": 22677, + "To The Glacier Caves Level 2": 22678, + "To The Rocky Summit": 22679, + "To Nihlathaks Temple": 22680, + "To The Halls of Anguish": 22681, + "To The Halls of Death's Calling": 22682, + "To The Halls of Tormented Insanity": 22683, + "To The Halls of Vaught": 22684, + "To The Worldstone Keep Level 1": 22685, + "To The Worldstone Keep Level 2": 22686, + "To The Worldstone Keep Level 3": 22687, + "To The Worldstone Chamber": 22688, + "To The Throne of Destruction": 22689, + "hireiconinfo1": 22690, + "hireiconinfo2": 22691, + "hiredismiss": 22692, + "hiredismisshire": 22693, + "hirerehire": 22694, + "hireresurrect": 22695, + "hireresurrect2": 22696, + "hirechat1": 22697, + "hirechat2": 22698, + "hirechat3": 22699, + "hirepraise1": 22700, + "hirepraise2": 22701, + "hiredanger1": 22702, + "hiredanger2": 22703, + "hiredanger3": 22704, + "hiredanger4": 22705, + "hiredanger5": 22706, + "hiredanger6": 22707, + "hirefeelstronger2": 22708, + "hirehelp1": 22709, + "hirehelp2": 22710, + "hirehelp3": 22711, + "hirehelp4": 22712, + "hiregreets1": 22713, + "hiregreets2": 22714, + "hiregreets3": 22715, + "hiregreets4": 22716, + "CfgSkill9": 22717, + "CfgSkill10": 22718, + "CfgSkill11": 22719, + "CfgSkill12": 22720, + "CfgSkill13": 22721, + "CfgSkill14": 22722, + "CfgSkill15": 22723, + "CfgSkill16": 22724, + "CfgToggleminimap": 22725, + "Cfgswapweapons": 22726, + "Cfghireling": 22727, + "MiniPanelHireinv": 22728, + "MiniPanelHire": 22729, + "Go North": 22737, + "Travel To Harrogath": 22738, + "Rename Instruct": 22747, + "Addsocketsui": 22748, + "Personalizeui": 22749, + "Addsocketsui2": 22750, + "MercX101": 22751, + "MercX102": 22752, + "MercX103": 22753, + "MercX104": 22754, + "MercX105": 22755, + "MercX106": 22756, + "MercX107": 22757, + "MercX108": 22758, + "MercX109": 22759, + "MercX110": 22760, + "MercX111": 22761, + "MercX112": 22762, + "MercX113": 22763, + "MercX114": 22764, + "MercX115": 22765, + "MercX116": 22766, + "MercX117": 22767, + "MercX118": 22768, + "MercX119": 22769, + "MercX120": 22770, + "MercX121": 22771, + "MercX122": 22772, + "MercX123": 22773, + "MercX124": 22774, + "MercX125": 22775, + "MercX126": 22776, + "MercX127": 22777, + "MercX128": 22778, + "MercX129": 22779, + "MercX130": 22780, + "MercX131": 22781, + "MercX132": 22782, + "MercX133": 22783, + "MercX134": 22784, + "MercX135": 22785, + "MercX136": 22786, + "MercX137": 22787, + "MercX138": 22788, + "MercX139": 22789, + "MercX140": 22790, + "MercX141": 22791, + "MercX142": 22792, + "MercX143": 22793, + "MercX144": 22794, + "MercX145": 22795, + "MercX146": 22796, + "MercX147": 22797, + "MercX148": 22798, + "MercX149": 22799, + "MercX150": 22800, + "MercX151": 22801, + "MercX152": 22802, + "MercX153": 22803, + "MercX154": 22804, + "MercX155": 22805, + "MercX156": 22806, + "MercX157": 22807, + "MercX158": 22808, + "MercX159": 22809, + "MercX160": 22810, + "MercX161": 22811, + "MercX162": 22812, + "MercX163": 22813, + "MercX164": 22814, + "MercX165": 22815, + "MercX166": 22816, + "MercX167": 22817 + }; - let LocaleStringName = {}; + let LocaleStringName = {}; - for (let k in LocaleStringID) { - LocaleStringName[LocaleStringID[k]] = k; - } + for (let k in LocaleStringID) { + LocaleStringName[LocaleStringID[k]] = k; + } - module.exports = { - LocaleStringName: LocaleStringName, - LocaleStringID: LocaleStringID - }; + module.exports = { + LocaleStringName: LocaleStringName, + LocaleStringID: LocaleStringID + }; })(module); diff --git a/libs/SoloPlay/Modules/GameData/MissileData.js b/libs/SoloPlay/Modules/GameData/MissileData.js index 7ddf9996..fb1d0c16 100644 --- a/libs/SoloPlay/Modules/GameData/MissileData.js +++ b/libs/SoloPlay/Modules/GameData/MissileData.js @@ -1,28 +1,28 @@ (function (module, require) { - /** - * MissilesData - */ - const MISSILES_COUNT = 385; - const MissilesData = Array(MISSILES_COUNT); + /** + * MissilesData + */ + const MISSILES_COUNT = 385; + const MissilesData = Array(MISSILES_COUNT); - for (let i = 0; i < MissilesData.length; i++) { - let index = i; - MissilesData[i] = ({ - index: index, - classID: index, - internalName: getBaseStat("missiles", index, "Missile"), - velocity: getBaseStat("missiles", index, "Vel"), - velocityMax: getBaseStat("missiles", index, "MaxVel"), - acceleration: getBaseStat("missiles", index, "Accel"), - range: getBaseStat("missiles", index, "Range"), - size: getBaseStat("missiles", index, "Size"), - minDamage: getBaseStat("missiles", index, "MinDamage"), - maxDamage: getBaseStat("missiles", index, "MaxDamage"), - eType: getBaseStat("missiles", index, "EType"), - eMin: getBaseStat("missiles", index, "EMin"), - eMax: getBaseStat("missiles", index, "EMax"), - cltSubMissiles: [getBaseStat("missiles", index, "CltSubMissile1"), getBaseStat("missiles", index, "CltSubMissile2"), getBaseStat("missiles", index, "CltSubMissile3")], - }); - } - module.exports = MissilesData; + for (let i = 0; i < MissilesData.length; i++) { + let index = i; + MissilesData[i] = ({ + index: index, + classID: index, + internalName: getBaseStat("missiles", index, "Missile"), + velocity: getBaseStat("missiles", index, "Vel"), + velocityMax: getBaseStat("missiles", index, "MaxVel"), + acceleration: getBaseStat("missiles", index, "Accel"), + range: getBaseStat("missiles", index, "Range"), + size: getBaseStat("missiles", index, "Size"), + minDamage: getBaseStat("missiles", index, "MinDamage"), + maxDamage: getBaseStat("missiles", index, "MaxDamage"), + eType: getBaseStat("missiles", index, "EType"), + eMin: getBaseStat("missiles", index, "EMin"), + eMax: getBaseStat("missiles", index, "EMax"), + cltSubMissiles: [getBaseStat("missiles", index, "CltSubMissile1"), getBaseStat("missiles", index, "CltSubMissile2"), getBaseStat("missiles", index, "CltSubMissile3")], + }); + } + module.exports = MissilesData; })(module, require); diff --git a/libs/SoloPlay/Modules/GameData/MonsterData.js b/libs/SoloPlay/Modules/GameData/MonsterData.js index 322b7977..9cb9f401 100644 --- a/libs/SoloPlay/Modules/GameData/MonsterData.js +++ b/libs/SoloPlay/Modules/GameData/MonsterData.js @@ -1,109 +1,109 @@ (function (module, require) { - const LocaleStringName = require("./LocaleStringID").LocaleStringName; - const MONSTER_INDEX_COUNT = 770; - /** - * @typedef MonsterDataObj - * @type {object} - * @property {number} Index = Index of this monster - * @property {number} ClassID = classid of this monster - * @property {number} Type = Type of monster - * @property {number} Level = Level of this monster in normal (use GameData.monsterLevel to find monster levels) - * @property {boolean} Ranged = if monster is ranged - * @property {number} Rarity = weight of this monster in level generation - * @property {number} Threat = threat level used by mercs - * @property {number} Align = alignment of unit (determines what it will attack) - * @property {boolean} Melee = if monster is melee - * @property {boolean} NPC = if unit is NPC - * @property {boolean} Demon = if monster is demon - * @property {boolean} Flying = if monster is flying - * @property {boolean} Boss = if monster is a boss - * @property {boolean} ActBoss = if monster is act boss - * @property {boolean} Killable = if monster can be killed - * @property {boolean} Convertable = if monster is affected by convert or mind blast - * @property {boolean} NeverCount = if not counted as a minion - * @property {number} DeathDamage = explodes on death - * @property {number} Regeneration = hp regeneration - * @property {number} LocaleString = locale string index for getLocaleString - * @property {number} ExperienceModifier = percent of base monster exp this unit rewards when killed - * @property {number} Undead = 2 if greater undead, 1 if lesser undead, 0 if neither - * @property {number} Drain = drain effectiveness percent - * @property {number} Block = block percent - * @property {number} Physical = physical resist - * @property {number} Magic = magic resist - * @property {number} Fire = fire resist - * @property {number} Lightning = lightning resist - * @property {number} Poison = poison resist - * @property {number[]} Minions = array of minions that can spawn with this unit - * @property {number} MinionCount.Min = minimum number of minions that can spawn with this unit - * @property {number} MinionCount.Max = maximum number of minions that can spawn with this unit - */ - - /** @type {MonsterDataObj[]} */ - const MonsterData = Array(MONSTER_INDEX_COUNT); + const LocaleStringName = require("./LocaleStringID").LocaleStringName; + const MONSTER_INDEX_COUNT = 770; + /** + * @typedef MonsterDataObj + * @type {object} + * @property {number} Index = Index of this monster + * @property {number} ClassID = classid of this monster + * @property {number} Type = Type of monster + * @property {number} Level = Level of this monster in normal (use GameData.monsterLevel to find monster levels) + * @property {boolean} Ranged = if monster is ranged + * @property {number} Rarity = weight of this monster in level generation + * @property {number} Threat = threat level used by mercs + * @property {number} Align = alignment of unit (determines what it will attack) + * @property {boolean} Melee = if monster is melee + * @property {boolean} NPC = if unit is NPC + * @property {boolean} Demon = if monster is demon + * @property {boolean} Flying = if monster is flying + * @property {boolean} Boss = if monster is a boss + * @property {boolean} ActBoss = if monster is act boss + * @property {boolean} Killable = if monster can be killed + * @property {boolean} Convertable = if monster is affected by convert or mind blast + * @property {boolean} NeverCount = if not counted as a minion + * @property {number} DeathDamage = explodes on death + * @property {number} Regeneration = hp regeneration + * @property {number} LocaleString = locale string index for getLocaleString + * @property {number} ExperienceModifier = percent of base monster exp this unit rewards when killed + * @property {number} Undead = 2 if greater undead, 1 if lesser undead, 0 if neither + * @property {number} Drain = drain effectiveness percent + * @property {number} Block = block percent + * @property {number} Physical = physical resist + * @property {number} Magic = magic resist + * @property {number} Fire = fire resist + * @property {number} Lightning = lightning resist + * @property {number} Poison = poison resist + * @property {number[]} Minions = array of minions that can spawn with this unit + * @property {number} MinionCount.Min = minimum number of minions that can spawn with this unit + * @property {number} MinionCount.Max = maximum number of minions that can spawn with this unit + */ + + /** @type {MonsterDataObj[]} */ + const MonsterData = Array(MONSTER_INDEX_COUNT); - for (let i = 0; i < MonsterData.length; i++) { - let index = i; - - MonsterData[i] = ({ - Index: index, - ClassID: index, - Type: getBaseStat("monstats", index, "MonType"), - Level: getBaseStat("monstats", index, "Level"), // normal only, nm/hell are determined by area's LevelEx - Ranged: getBaseStat("monstats", index, "RangedType"), - Rarity: getBaseStat("monstats", index, "Rarity"), - Threat: getBaseStat("monstats", index, "threat"), - PetIgnore: getBaseStat("monstats", index, "petignore"), - Align: getBaseStat("monstats", index, "Align"), - Melee: getBaseStat("monstats", index, "isMelee"), - NPC: getBaseStat("monstats", index, "npc"), - Demon: getBaseStat("monstats", index, "demon"), - Flying: getBaseStat("monstats", index, "flying"), - Boss: getBaseStat("monstats", index, "boss"), - ActBoss: getBaseStat("monstats", index, "primeevil"), - Killable: getBaseStat("monstats", index, "killable"), - Convertable: getBaseStat("monstats", index, "switchai"), - NeverCount: getBaseStat("monstats", index, "neverCount"), - DeathDamage: getBaseStat("monstats", index, "deathDmg"), - Regeneration: getBaseStat("monstats", index, "DamageRegen"), - LocaleString: getLocaleString(getBaseStat("monstats", index, "NameStr")), - InternalName: LocaleStringName[getBaseStat("monstats", index, "NameStr")], - ExperienceModifier: getBaseStat("monstats", index, ["Exp", "Exp(N)", "Exp(H)"][me.diff]), - Undead: (getBaseStat("monstats", index, "hUndead") && 2) | (getBaseStat("monstats", index, "lUndead") && 1), - Drain: getBaseStat("monstats", index, ["Drain", "Drain(N)", "Drain(H)"][me.diff]), - Block: getBaseStat("monstats", index, ["ToBlock", "ToBlock(N)", "ToBlock(H)"][me.diff]), - Physical: getBaseStat("monstats", index, ["ResDm", "ResDm(N)", "ResDm(H)"][me.diff]), - Magic: getBaseStat("monstats", index, ["ResMa", "ResMa(N)", "ResMa(H)"][me.diff]), - Fire: getBaseStat("monstats", index, ["ResFi", "ResFi(N)", "ResFi(H)"][me.diff]), - Lightning: getBaseStat("monstats", index, ["ResLi", "ResLi(N)", "ResLi(H)"][me.diff]), - Cold: getBaseStat("monstats", index, ["ResCo", "ResCo(N)", "ResCo(H)"][me.diff]), - Poison: getBaseStat("monstats", index, ["ResPo", "ResPo(N)", "ResPo(H)"][me.diff]), - Minions: ([getBaseStat("monstats", index, "minion1"), getBaseStat("monstats", index, "minion2")].filter(mon => mon !== 65535)), - GroupCount: ({ - Min: getBaseStat("monstats", index, "MinGrp"), - Max: getBaseStat("monstats", index, "MaxGrp") - }), - MinionCount: ({ - Min: getBaseStat("monstats", index, "PartyMin"), - Max: getBaseStat("monstats", index, "PartyMax") - }), - Velocity: getBaseStat("monstats", index, "Velocity"), - Run: getBaseStat("monstats", index, "Run"), - SizeX: getBaseStat("monstats", index, "SizeX"), - SizeY: getBaseStat("monstats", index, "SizeY"), - Attack1MinDmg: getBaseStat("monstats", index, ["A1MinD", "A1MinD(N)", "A1MinD(H)"][me.diff]), - Attack1MaxDmg: getBaseStat("monstats", index, ["A1MaxD", "A1MaxD(N)", "A1MaxD(H)"][me.diff]), - Attack2MinDmg: getBaseStat("monstats", index, ["A2MinD", "A2MinD(N)", "A2MinD(H)"][me.diff]), - Attack2MaxDmg: getBaseStat("monstats", index, ["A2MaxD", "A2MaxD(N)", "A2MaxD(H)"][me.diff]), - Skill1MinDmg: getBaseStat("monstats", index, ["S1MinD", "S1MinD(N)", "S1MinD(H)"][me.diff]), - Skill1MaxDmg: getBaseStat("monstats", index, ["S1MaxD", "S1MaxD(N)", "S1MaxD(H)"][me.diff]), - }); - } + for (let i = 0; i < MonsterData.length; i++) { + let index = i; + + MonsterData[i] = ({ + Index: index, + ClassID: index, + Type: getBaseStat("monstats", index, "MonType"), + Level: getBaseStat("monstats", index, "Level"), // normal only, nm/hell are determined by area's LevelEx + Ranged: getBaseStat("monstats", index, "RangedType"), + Rarity: getBaseStat("monstats", index, "Rarity"), + Threat: getBaseStat("monstats", index, "threat"), + PetIgnore: getBaseStat("monstats", index, "petignore"), + Align: getBaseStat("monstats", index, "Align"), + Melee: getBaseStat("monstats", index, "isMelee"), + NPC: getBaseStat("monstats", index, "npc"), + Demon: getBaseStat("monstats", index, "demon"), + Flying: getBaseStat("monstats", index, "flying"), + Boss: getBaseStat("monstats", index, "boss"), + ActBoss: getBaseStat("monstats", index, "primeevil"), + Killable: getBaseStat("monstats", index, "killable"), + Convertable: getBaseStat("monstats", index, "switchai"), + NeverCount: getBaseStat("monstats", index, "neverCount"), + DeathDamage: getBaseStat("monstats", index, "deathDmg"), + Regeneration: getBaseStat("monstats", index, "DamageRegen"), + LocaleString: getLocaleString(getBaseStat("monstats", index, "NameStr")), + InternalName: LocaleStringName[getBaseStat("monstats", index, "NameStr")], + ExperienceModifier: getBaseStat("monstats", index, ["Exp", "Exp(N)", "Exp(H)"][me.diff]), + Undead: (getBaseStat("monstats", index, "hUndead") && 2) | (getBaseStat("monstats", index, "lUndead") && 1), + Drain: getBaseStat("monstats", index, ["Drain", "Drain(N)", "Drain(H)"][me.diff]), + Block: getBaseStat("monstats", index, ["ToBlock", "ToBlock(N)", "ToBlock(H)"][me.diff]), + Physical: getBaseStat("monstats", index, ["ResDm", "ResDm(N)", "ResDm(H)"][me.diff]), + Magic: getBaseStat("monstats", index, ["ResMa", "ResMa(N)", "ResMa(H)"][me.diff]), + Fire: getBaseStat("monstats", index, ["ResFi", "ResFi(N)", "ResFi(H)"][me.diff]), + Lightning: getBaseStat("monstats", index, ["ResLi", "ResLi(N)", "ResLi(H)"][me.diff]), + Cold: getBaseStat("monstats", index, ["ResCo", "ResCo(N)", "ResCo(H)"][me.diff]), + Poison: getBaseStat("monstats", index, ["ResPo", "ResPo(N)", "ResPo(H)"][me.diff]), + Minions: ([getBaseStat("monstats", index, "minion1"), getBaseStat("monstats", index, "minion2")].filter(mon => mon !== 65535)), + GroupCount: ({ + Min: getBaseStat("monstats", index, "MinGrp"), + Max: getBaseStat("monstats", index, "MaxGrp") + }), + MinionCount: ({ + Min: getBaseStat("monstats", index, "PartyMin"), + Max: getBaseStat("monstats", index, "PartyMax") + }), + Velocity: getBaseStat("monstats", index, "Velocity"), + Run: getBaseStat("monstats", index, "Run"), + SizeX: getBaseStat("monstats", index, "SizeX"), + SizeY: getBaseStat("monstats", index, "SizeY"), + Attack1MinDmg: getBaseStat("monstats", index, ["A1MinD", "A1MinD(N)", "A1MinD(H)"][me.diff]), + Attack1MaxDmg: getBaseStat("monstats", index, ["A1MaxD", "A1MaxD(N)", "A1MaxD(H)"][me.diff]), + Attack2MinDmg: getBaseStat("monstats", index, ["A2MinD", "A2MinD(N)", "A2MinD(H)"][me.diff]), + Attack2MaxDmg: getBaseStat("monstats", index, ["A2MaxD", "A2MaxD(N)", "A2MaxD(H)"][me.diff]), + Skill1MinDmg: getBaseStat("monstats", index, ["S1MinD", "S1MinD(N)", "S1MinD(H)"][me.diff]), + Skill1MaxDmg: getBaseStat("monstats", index, ["S1MaxD", "S1MaxD(N)", "S1MaxD(H)"][me.diff]), + }); + } - MonsterData.findByName = function (whatToFind) { - let matches = MonsterData.map(mon => [Math.min(whatToFind.diffCount(mon.LocaleString), whatToFind.diffCount(mon.InternalName)), mon]).sort((a, b) => a[0] - b[0]); + MonsterData.findByName = function (whatToFind) { + let matches = MonsterData.map(mon => [Math.min(whatToFind.diffCount(mon.LocaleString), whatToFind.diffCount(mon.InternalName)), mon]).sort((a, b) => a[0] - b[0]); - return matches[0][1]; - }; + return matches[0][1]; + }; - module.exports = MonsterData; + module.exports = MonsterData; })(module, require); diff --git a/libs/SoloPlay/Modules/GameData/PotData.js b/libs/SoloPlay/Modules/GameData/PotData.js index 6ad3f5c3..acdc4de0 100644 --- a/libs/SoloPlay/Modules/GameData/PotData.js +++ b/libs/SoloPlay/Modules/GameData/PotData.js @@ -5,151 +5,151 @@ */ (function (root, factory) { - if (typeof module === "object" && typeof module.exports === "object") { - const v = factory(); - if (v !== undefined) module.exports = v; - } else if (typeof define === "function" && define.amd) { - define([], factory); - } else { - root.PotData = factory(); - } + if (typeof module === "object" && typeof module.exports === "object") { + const v = factory(); + if (v !== undefined) module.exports = v; + } else if (typeof define === "function" && define.amd) { + define([], factory); + } else { + root.PotData = factory(); + } }(this, function () { - "use strict"; - const PotData = (function () { - /** - * @typedef {Object} Pot - * @property {string} type - * @property {number[]} effect - * @property {number} cost - * @property {number} duration - * @property {Array} [recipe] - */ + "use strict"; + const PotData = (function () { + /** + * @typedef {Object} Pot + * @property {string} type + * @property {number[]} effect + * @property {number} cost + * @property {number} duration + * @property {Array} [recipe] + */ - /** @type {Object.} */ - const _pots = {}; + /** @type {Object.} */ + const _pots = {}; - _pots[sdk.items.MinorHealingPotion] = { - type: "hp", - effect: [45, 30, 30, 45, 60, 30, 45], - cost: 30, - duration: 7.68 - }; - _pots[sdk.items.LightHealingPotion] = { - type: "hp", - effect: [90, 60, 60, 90, 120, 60, 90], - cost: 67, - duration: 6.4 - }; - _pots[sdk.items.HealingPotion] = { - type: "hp", - effect: [150, 100, 100, 150, 200, 100, 150], - cost: 112, - duration: 6.84 - }; - _pots[sdk.items.GreaterHealingPotion] = { - type: "hp", - effect: [270, 180, 180, 270, 360, 180, 270], - cost: 225, - duration: 7.68 - }; - _pots[sdk.items.SuperHealingPotion] = { - type: "hp", - effect: [480, 320, 320, 480, 640, 320, 480], - cost: undefined, - duration: 10.24 - }; - _pots[sdk.items.MinorManaPotion] = { - type: "mp", - effect: [30, 40, 40, 30, 20, 40, 30], - cost: 60, - duration: 5.12 - }; - _pots[sdk.items.LightManaPotion] = { - type: "mp", - effect: [60, 80, 80, 60, 40, 80, 60], - cost: 135, - duration: 5.12 - }; - _pots[sdk.items.ManaPotion] = { - type: "mp", - effect: [120, 160, 160, 120, 80, 160, 120], - cost: 270, - duration: 5.12 - }; - _pots[sdk.items.GreaterManaPotion] = { - type: "mp", - effect: [225, 300, 300, 225, 150, 300, 225], - cost: 450, - duration: 5.12 - }; - _pots[sdk.items.SuperManaPotion] = { - type: "mp", - effect: [375, 500, 500, 375, 250, 500, 375], - cost: undefined, - duration: 5.12 - }; - _pots[sdk.items.RejuvenationPotion] = { - type: "rv", - effect: [35, 35, 35, 35, 35, 35, 35], - cost: undefined, - duration: 0.04, - recipe: [ - [ - sdk.items.HealingPotion, sdk.items.HealingPotion, sdk.items.HealingPotion, - sdk.items.ManaPotion, sdk.items.ManaPotion, sdk.items.ManaPotion, - function (item) { - return item.itemType === sdk.items.type.ChippedGem; - } - ] - ] - }; - _pots[sdk.items.FullRejuvenationPotion] = { - type: "rv", - effect: [100, 100, 100, 100, 100, 100, 100], - cost: undefined, - duration: 0.04, - recipe: [ - // Recipe is either an classid, or an function that returns true on the correct item - [ - sdk.items.RejuvenationPotion, sdk.items.RejuvenationPotion, sdk.items.RejuvenationPotion // 3 normal rv's - ], - [ - sdk.items.HealingPotion, sdk.items.HealingPotion, sdk.items.HealingPotion, - sdk.items.ManaPotion, sdk.items.ManaPotion, sdk.items.ManaPotion, - function (item) { - return item.itemType === sdk.items.type.Gem; - } - ], - ] - }; + _pots[sdk.items.MinorHealingPotion] = { + type: "hp", + effect: [45, 30, 30, 45, 60, 30, 45], + cost: 30, + duration: 7.68 + }; + _pots[sdk.items.LightHealingPotion] = { + type: "hp", + effect: [90, 60, 60, 90, 120, 60, 90], + cost: 67, + duration: 6.4 + }; + _pots[sdk.items.HealingPotion] = { + type: "hp", + effect: [150, 100, 100, 150, 200, 100, 150], + cost: 112, + duration: 6.84 + }; + _pots[sdk.items.GreaterHealingPotion] = { + type: "hp", + effect: [270, 180, 180, 270, 360, 180, 270], + cost: 225, + duration: 7.68 + }; + _pots[sdk.items.SuperHealingPotion] = { + type: "hp", + effect: [480, 320, 320, 480, 640, 320, 480], + cost: undefined, + duration: 10.24 + }; + _pots[sdk.items.MinorManaPotion] = { + type: "mp", + effect: [30, 40, 40, 30, 20, 40, 30], + cost: 60, + duration: 5.12 + }; + _pots[sdk.items.LightManaPotion] = { + type: "mp", + effect: [60, 80, 80, 60, 40, 80, 60], + cost: 135, + duration: 5.12 + }; + _pots[sdk.items.ManaPotion] = { + type: "mp", + effect: [120, 160, 160, 120, 80, 160, 120], + cost: 270, + duration: 5.12 + }; + _pots[sdk.items.GreaterManaPotion] = { + type: "mp", + effect: [225, 300, 300, 225, 150, 300, 225], + cost: 450, + duration: 5.12 + }; + _pots[sdk.items.SuperManaPotion] = { + type: "mp", + effect: [375, 500, 500, 375, 250, 500, 375], + cost: undefined, + duration: 5.12 + }; + _pots[sdk.items.RejuvenationPotion] = { + type: "rv", + effect: [35, 35, 35, 35, 35, 35, 35], + cost: undefined, + duration: 0.04, + recipe: [ + [ + sdk.items.HealingPotion, sdk.items.HealingPotion, sdk.items.HealingPotion, + sdk.items.ManaPotion, sdk.items.ManaPotion, sdk.items.ManaPotion, + function (item) { + return item.itemType === sdk.items.type.ChippedGem; + } + ] + ] + }; + _pots[sdk.items.FullRejuvenationPotion] = { + type: "rv", + effect: [100, 100, 100, 100, 100, 100, 100], + cost: undefined, + duration: 0.04, + recipe: [ + // Recipe is either an classid, or an function that returns true on the correct item + [ + sdk.items.RejuvenationPotion, sdk.items.RejuvenationPotion, sdk.items.RejuvenationPotion // 3 normal rv's + ], + [ + sdk.items.HealingPotion, sdk.items.HealingPotion, sdk.items.HealingPotion, + sdk.items.ManaPotion, sdk.items.ManaPotion, sdk.items.ManaPotion, + function (item) { + return item.itemType === sdk.items.type.Gem; + } + ], + ] + }; - /** @type {Pot[]} */ - const _mpPots = [ - _pots[sdk.items.MinorManaPotion], _pots[sdk.items.LightManaPotion], _pots[sdk.items.ManaPotion], - _pots[sdk.items.GreaterManaPotion], _pots[sdk.items.SuperManaPotion] - ]; - /** @type {Pot[]} */ - const _hpPots = [ - _pots[sdk.items.MinorHealingPotion], _pots[sdk.items.LightHealingPotion], _pots[sdk.items.HealingPotion], - _pots[sdk.items.GreaterHealingPotion], _pots[sdk.items.SuperHealingPotion] - ]; + /** @type {Pot[]} */ + const _mpPots = [ + _pots[sdk.items.MinorManaPotion], _pots[sdk.items.LightManaPotion], _pots[sdk.items.ManaPotion], + _pots[sdk.items.GreaterManaPotion], _pots[sdk.items.SuperManaPotion] + ]; + /** @type {Pot[]} */ + const _hpPots = [ + _pots[sdk.items.MinorHealingPotion], _pots[sdk.items.LightHealingPotion], _pots[sdk.items.HealingPotion], + _pots[sdk.items.GreaterHealingPotion], _pots[sdk.items.SuperHealingPotion] + ]; - Object.keys(_pots) - .forEach(key => Object.freeze(_pots[key])); - Object.freeze(_mpPots); - Object.freeze(_hpPots); + Object.keys(_pots) + .forEach(key => Object.freeze(_pots[key])); + Object.freeze(_mpPots); + Object.freeze(_hpPots); - return { - pots: _pots, - - getMpPots: function () { - return _mpPots; - }, - getHpPots: function () { - return _hpPots; - }, - }; - })(); + return { + pots: _pots, + + getMpPots: function () { + return _mpPots; + }, + getHpPots: function () { + return _hpPots; + }, + }; + })(); - return PotData; + return PotData; })); diff --git a/libs/SoloPlay/Modules/Guard.js b/libs/SoloPlay/Modules/Guard.js index 2ce60486..6c9bd54d 100644 --- a/libs/SoloPlay/Modules/Guard.js +++ b/libs/SoloPlay/Modules/Guard.js @@ -1,83 +1,83 @@ (function (module, require, thread) { - "use strict"; - const _Messaging = require("../../modules/Messaging"); - const Worker = require("../../modules/Worker"); - const sdk = require("../../modules/sdk"); + "use strict"; + const _Messaging = require("../../modules/Messaging"); + const Worker = require("../../modules/Worker"); + const sdk = require("../../modules/sdk"); - switch (thread) { - case "thread": { - Worker.runInBackground.stackTrace = (new function () { - let self = this; - let stack; + switch (thread) { + case "thread": { + Worker.runInBackground.stackTrace = (new function () { + let self = this; + let stack; - let myStack = ""; + let myStack = ""; - // recv stack - _Messaging.on("Guard", (data => typeof data === "object" && data && data.hasOwnProperty("stack") && (myStack = data.stack))); + // recv stack + _Messaging.on("Guard", (data => typeof data === "object" && data && data.hasOwnProperty("stack") && (myStack = data.stack))); - /** - * @constructor - * @param {function():string} callback - */ - function UpdateableText(callback) { - let element = new Text(callback(), self.x + 15, self.y + (7 * self.hooks.length), 0, 12, 0); - self.hooks.push(element); - this.update = () => { - element.text = callback(); - element.visible = [sdk.uiflags.Inventory, - sdk.uiflags.SkillWindow, - sdk.uiflags.TradePrompt, - sdk.uiflags.QuickSkill].every(f => !getUIFlag(f)); - }; - } + /** + * @constructor + * @param {function():string} callback + */ + function UpdateableText(callback) { + let element = new Text(callback(), self.x + 15, self.y + (7 * self.hooks.length), 0, 12, 0); + self.hooks.push(element); + this.update = () => { + element.text = callback(); + element.visible = [sdk.uiflags.Inventory, + sdk.uiflags.SkillWindow, + sdk.uiflags.TradePrompt, + sdk.uiflags.QuickSkill].every(f => !getUIFlag(f)); + }; + } - this.hooks = []; - this.x = 500; - this.y = 600 - (400 + (self.hooks.length * 15)); - // this.box = new Box(this.x-2, this.y-20, 250, (self.hooks.length * 15), 0, 0.2); + this.hooks = []; + this.x = 500; + this.y = 600 - (400 + (self.hooks.length * 15)); + // this.box = new Box(this.x-2, this.y-20, 250, (self.hooks.length * 15), 0, 0.2); - for (let i = 0; i < 20; i++) { - (i => this.hooks.push(new UpdateableText(() => stack && stack.length > i && stack[i] || "")))(i); - } + for (let i = 0; i < 20; i++) { + (i => this.hooks.push(new UpdateableText(() => stack && stack.length > i && stack[i] || "")))(i); + } - this.update = () => { - stack = myStack.match(/[^\r\n]+/g); - stack = stack && stack.slice(6/*skip path to here*/).map(el => { - let line = el.substr(el.lastIndexOf(":") + 1); - let functionName = el.substr(0, el.indexOf("@")); - let filename = el.substr(el.lastIndexOf("\\") + 1); + this.update = () => { + stack = myStack.match(/[^\r\n]+/g); + stack = stack && stack.slice(6/*skip path to here*/).map(el => { + let line = el.substr(el.lastIndexOf(":") + 1); + let functionName = el.substr(0, el.indexOf("@")); + let filename = el.substr(el.lastIndexOf("\\") + 1); - filename = filename.substr(0, filename.indexOf(".")); + filename = filename.substr(0, filename.indexOf(".")); - return filename + "ÿc::ÿc0" + line + "ÿc:@ÿc0" + functionName; - }).reverse(); - this.hooks.filter(hook => hook.hasOwnProperty("update") && typeof hook.update === "function" && hook.update()); - return true; - }; + return filename + "ÿc::ÿc0" + line + "ÿc:@ÿc0" + functionName; + }).reverse(); + this.hooks.filter(hook => hook.hasOwnProperty("update") && typeof hook.update === "function" && hook.update()); + return true; + }; - }).update; + }).update; - let quiting = false; - addEventListener("scriptmsg", data => data === "quit" && (quiting = true)); + let quiting = false; + addEventListener("scriptmsg", data => data === "quit" && (quiting = true)); - while (!quiting) delay(1000); - break; - } - case "started": { - let sendStack = getTickCount(); - Worker.push(function highPrio() { - Worker.push(highPrio); - if ((getTickCount() - sendStack) < 200 || (sendStack = getTickCount()) && false) return true; - _Messaging.send({ Guard: { stack: (new Error).stack } }); - return true; - }); + while (!quiting) delay(1000); + break; + } + case "started": { + let sendStack = getTickCount(); + Worker.push(function highPrio() { + Worker.push(highPrio); + if ((getTickCount() - sendStack) < 200 || (sendStack = getTickCount()) && false) return true; + _Messaging.send({ Guard: { stack: (new Error).stack } }); + return true; + }); - break; - } - case "loaded": { - break; - } - } + break; + } + case "loaded": { + break; + } + } }).call(null, typeof module === "object" && module || {}, typeof require === "undefined" && (include("require.js") && require) || require, getScript.startAsThread()); diff --git a/libs/SoloPlay/Modules/MercLib.js b/libs/SoloPlay/Modules/MercLib.js index 1d8b0375..623ed821 100644 --- a/libs/SoloPlay/Modules/MercLib.js +++ b/libs/SoloPlay/Modules/MercLib.js @@ -14,332 +14,332 @@ * + proper modern classes */ (function (factory) { - if (typeof module === "object" && typeof module.exports === "object") { - let v = factory(require, exports); - if (v !== undefined) module.exports = v; - } else if (typeof define === "function" && define.amd) { - define(["require", "exports", "./bigInt"], factory); - } + if (typeof module === "object" && typeof module.exports === "object") { + let v = factory(require, exports); + if (v !== undefined) module.exports = v; + } else if (typeof define === "function" && define.amd) { + define(["require", "exports", "./bigInt"], factory); + } })(function (require, exports) { - "use strict"; - Object.defineProperty(exports, "__esModule", { value: true }); - exports.mercPacket = exports.Merc = exports.Rnd = void 0; - let bigInt_1 = require("./bigInt"); - /// The actual Merc.js code of dzik - function leftShift(val, shift) { - let l; - while (shift >= bigInt_1.bpe) { - l = Math.ceil((bigInt_1.bitSize(val) + bigInt_1.bpe - 1) / bigInt_1.bpe) + 1; - if (val.length < l) { - val = bigInt_1.expand(val, l); - } - bigInt_1.leftShift_(val, bigInt_1.bpe - 1); - shift -= bigInt_1.bpe - 1; - } - if (shift > 0) { - l = Math.ceil((bigInt_1.bitSize(val) + bigInt_1.bpe - 1) / bigInt_1.bpe) + 1; - if (val.length < l) { - val = bigInt_1.expand(val, l); - } - bigInt_1.leftShift_(val, shift); - } - return val; - } - function rightShift(val, shift) { - while (shift >= bigInt_1.bpe) { - bigInt_1.rightShift_(val, bigInt_1.bpe - 1); - shift -= bigInt_1.bpe - 1; - } - if (shift > 0) { - bigInt_1.rightShift_(val, shift); - } - } - let Rnd = /** @class */ (function () { - function Rnd(seed) { - let tmp = bigInt_1.int2bigInt(666, 16, 0); - tmp = leftShift(tmp, 32); - this.val = bigInt_1.add(tmp, bigInt_1.str2bigInt(seed.toString(16), 16, 33)); - } - Rnd.prototype.roll = function () { - let tmp = bigInt_1.modInt(this.val, 0x100000000); - let tmp2 = bigInt_1.mult(bigInt_1.str2bigInt(tmp.toString(16), 16, 33), bigInt_1.int2bigInt(1791398085, 33, 0)); - rightShift(this.val, 32); - let res = bigInt_1.add(tmp2, this.val); - let rescopy = bigInt_1.dup(res); - rightShift(rescopy, 64); - res = bigInt_1.sub(res, rescopy); - this.val = res; - }; - Rnd.prototype.get = function () { - return bigInt_1.modInt(this.val, 0x100000000); - }; - return Rnd; - }()); - exports.Rnd = Rnd; - let Merc = /** @class */ (function () { - function Merc(name, seed) { - this.id = name; - this.name = getLocaleString(name); - const getTable = function () { - let list = []; - let level = 0; - let act = checkActByName(name); - for (let i = 0; i < MercTable.length; i++) { - if (MercTable[i][5] == act && MercTable[i][6] == me.diff + 1 && MercTable[i][2] == me.gametype * 100) { - if (level == 0 || MercTable[i][7] == level) { - list.push(MercTable[i]); - level = MercTable[i][7]; - } - } - } - return list; - }; - const getMercInfo = function (index, infoindex) { - if (validMercTable[index].hasOwnProperty(infoindex)) { - return validMercTable[index][infoindex]; - } + "use strict"; + Object.defineProperty(exports, "__esModule", { value: true }); + exports.mercPacket = exports.Merc = exports.Rnd = void 0; + let bigInt_1 = require("./bigInt"); + /// The actual Merc.js code of dzik + function leftShift(val, shift) { + let l; + while (shift >= bigInt_1.bpe) { + l = Math.ceil((bigInt_1.bitSize(val) + bigInt_1.bpe - 1) / bigInt_1.bpe) + 1; + if (val.length < l) { + val = bigInt_1.expand(val, l); + } + bigInt_1.leftShift_(val, bigInt_1.bpe - 1); + shift -= bigInt_1.bpe - 1; + } + if (shift > 0) { + l = Math.ceil((bigInt_1.bitSize(val) + bigInt_1.bpe - 1) / bigInt_1.bpe) + 1; + if (val.length < l) { + val = bigInt_1.expand(val, l); + } + bigInt_1.leftShift_(val, shift); + } + return val; + } + function rightShift(val, shift) { + while (shift >= bigInt_1.bpe) { + bigInt_1.rightShift_(val, bigInt_1.bpe - 1); + shift -= bigInt_1.bpe - 1; + } + if (shift > 0) { + bigInt_1.rightShift_(val, shift); + } + } + let Rnd = /** @class */ (function () { + function Rnd(seed) { + let tmp = bigInt_1.int2bigInt(666, 16, 0); + tmp = leftShift(tmp, 32); + this.val = bigInt_1.add(tmp, bigInt_1.str2bigInt(seed.toString(16), 16, 33)); + } + Rnd.prototype.roll = function () { + let tmp = bigInt_1.modInt(this.val, 0x100000000); + let tmp2 = bigInt_1.mult(bigInt_1.str2bigInt(tmp.toString(16), 16, 33), bigInt_1.int2bigInt(1791398085, 33, 0)); + rightShift(this.val, 32); + let res = bigInt_1.add(tmp2, this.val); + let rescopy = bigInt_1.dup(res); + rightShift(rescopy, 64); + res = bigInt_1.sub(res, rescopy); + this.val = res; + }; + Rnd.prototype.get = function () { + return bigInt_1.modInt(this.val, 0x100000000); + }; + return Rnd; + }()); + exports.Rnd = Rnd; + let Merc = /** @class */ (function () { + function Merc(name, seed) { + this.id = name; + this.name = getLocaleString(name); + const getTable = function () { + let list = []; + let level = 0; + let act = checkActByName(name); + for (let i = 0; i < MercTable.length; i++) { + if (MercTable[i][5] == act && MercTable[i][6] == me.diff + 1 && MercTable[i][2] == me.gametype * 100) { + if (level == 0 || MercTable[i][7] == level) { + list.push(MercTable[i]); + level = MercTable[i][7]; + } + } + } + return list; + }; + const getMercInfo = function (index, infoindex) { + if (validMercTable[index].hasOwnProperty(infoindex)) { + return validMercTable[index][infoindex]; + } - return false; - }; - const checkActByName = function (name) { - if (name >= 3411 && name <= 3451) { - return 1; + return false; + }; + const checkActByName = function (name) { + if (name >= 3411 && name <= 3451) { + return 1; - } else if (name >= 1019 && name <= 1039) { - return 2; - } else if (name >= 1040 && name <= 1059) { - return 3; - } else if (name >= 10835 && name <= 10901) { - return 5; - } else { - return false; - } - }; - const validMercTable = getTable(); - let newseed = new Rnd(seed); - newseed.roll(); - let index = (newseed.get()) % validMercTable.length; - newseed.roll(); - this.level = (newseed.get()) % 5 + me.charlvl - 5; - let lvl = this.level; - let difference = this.level - getMercInfo(index, 7); - this.hireling = getMercInfo(index, 0); - this.typeid = getMercInfo(index, 3); - this.subtype = getMercInfo(index, 1); - this.skills = []; - if (getMercInfo(index, 33) !== undefined) { - this.skills.push({ - name: getMercInfo(index, 33), - lvl: Math.floor(lvl * 10 / 32) // experimental - }); - } - if (getMercInfo(index, 39) !== undefined) { - this.skills.push({ - name: getMercInfo(index, 39), - lvl: Math.floor(lvl * 10 / 32) // experimental - }); - } - if (getMercInfo(index, 45) !== undefined) { - this.skills.push({ - name: getMercInfo(index, 45), - lvl: Math.floor(lvl * 10 / 32) // experimental - }); - } - this.cost = getMercInfo(index, 11) * (15 * difference + 100) / 100; - if (this.cost < getMercInfo(index, 11)) { - this.cost = getMercInfo(index, 11); - } - this.cost = Math.floor(this.cost); - this.hp = getMercInfo(index, 13) + getMercInfo(index, 14) * difference; - if (this.hp < 40) { - this.hp = 40; - } - this.defense = getMercInfo(index, 15) + getMercInfo(index, 16) * difference; - if (this.defense < 0) { - this.defense = 0; - } - this.strength = getMercInfo(index, 17) + getMercInfo(index, 18) * difference; - if (this.strength < 10) { - this.strength = 10; - } - this.dexterity = getMercInfo(index, 19) + getMercInfo(index, 20) * difference; - if (this.dexterity < 10) { - this.dexterity = 10; - } - this.experience = this.level * this.level * (this.level + 1) * getMercInfo(index, 12); - if (this.experience < 0) { - this.experience = 0; - } - this.attackrating = getMercInfo(index, 21) + getMercInfo(index, 22) * difference; - if (this.attackrating < 1) { - this.attackrating = 1; - } - this.dmg_min = getMercInfo(index, 24) + getMercInfo(index, 26) * difference; - if (this.dmg_min < 0) { - this.dmg_min = 0; - } - this.dmg_max = getMercInfo(index, 25) + getMercInfo(index, 26) * difference; - if (this.dmg_max < 1) { - this.dmg_max = 1; - } - this.resists = getMercInfo(index, 27) + getMercInfo(index, 28) * difference; - if (this.resists < 1) { - this.resists = 1; - } - } - Merc.prototype.hire = function () { - let npc = getInteractedNPC(); - if (!npc) throw new Error("To buy merc you need to interact with npc first"); - if (typeof this.cost !== "number") throw new Error("Invalid cost"); - if (me.gold < this.cost) throw new Error("Merc is too expensive to buy."); - let before = me.gold; - while (before === me.gold) { - sendPacket(1, sdk.packets.send.HireMerc, 4, npc.gid, 4, this.id); - delay((me.ping || 1) * 5); - } - return true; - }; - return Merc; - }()); - exports.Merc = Merc; - let Mercs = []; - exports.default = Mercs; - function mercPacket(pByte) { - switch (pByte[0]) { - case 0x4f: - // Clear mercenary list - Mercs.splice(0, Mercs.length); - break; - case 0x4e: - let name = ((pByte[1]) | (pByte[2] << 8)), seed = ((pByte[3]) | (pByte[4] << 8) | (pByte[5] << 16) | (pByte[6] << 24)) >>> 0; - Mercs.push(new Merc(name, seed)); - break; - } - } - exports.mercPacket = mercPacket; - //addEventListener("gamepacket", mercPacket); - //removeEventListener("gamepacket", mercPacket); - const MercTable = [ - // 0.Hireling , 1.SubType , 2.Version , 3.Id , 4.Class , 5.Act , 6.Diff , 7.Level , 8.Seller , 9.NameFirst , 10.NameLast , 11.Gold , 12.Exp/Lvl , 13.HP , 14.HP/Lvl , 15.Def , 16.Def/Lvl , Str , Str/Lvl , Dex , Dex/Lvl , AR , AR/Lvl , Share , Dmg-Min , Dmg-Max , Dmg/Lvl , Resist , Resist/Lvl , WType1 , WType2 , HireDesc , DefaultChance , Skill1 , Mode1 , Chance1 , ChancePerLvl1 , Level1 , LvlPerLvl1 , Skill2 , Mode2 , Chance2 , ChancePerLvl2 , Level2 , LvlPerLvl2 , Skill3 , Mode3 , Chance3 , ChancePerLvl3 , Level3 , LvlPerLvl3 , Skill4 , Mode4 , Chance4 , ChancePerLvl4 , Level4 , LvlPerLvl4 , Skill5 , Mode5 , Chance5 , ChancePerLvl5 , Level5 , LvlPerLvl5 , Skill6 , Mode6 , Chance6 , ChancePerLvl6 , Level6 , LvlPerLvl6 , Head , Torso , Weapon , Shield - ["Rogue Scout", "Fire - Normal", 0, 0, 271, 1, 1, 3, 150, "merc01", "merc41", 100, 100, 45, 8, 15, 6, 35, 10, 45, 16, 10, 10, , 1, 3, 2, 0, 8, "bow", , "farw", 75, "Inner Sight", 4, 10, 0, 1, 10, "Fire Arrow", 4, 25, 8, 1, 10, , , , , , , , , , , , , , , , , , , , , , , , , 2, 1, 2, 0], - ["Rogue Scout", "Fire - Normal", 0, 0, 271, 1, 1, 25, 150, "merc01", "merc41", 100, 100, 221, 13, 147, 12, 63, 10, 89, 16, 230, 20, 1, 7, 9, 4, 44, 7, "bow", , "farw", 75, "Inner Sight", 4, 10, 0, 8, 10, "Fire Arrow", 4, 69, 0, 8, 10, , , , , , , , , , , , , , , , , , , , , , , , , 2, 1, 2, 0], - ["Rogue Scout", "Fire - Normal", 0, 0, 271, 1, 1, 49, 150, "merc01", "merc41", 100, 100, 533, 25, 435, 18, 93, 10, 137, 16, 710, 30, 2, 19, 21, 6, 86, 5, "bow", , "farw", 75, "Inner Sight", 4, 10, 0, 16, 10, "Fire Arrow", 4, 69, 0, 16, 10, , , , , , , , , , , , , , , , , , , , , , , , , 2, 1, 2, 0], - ["Rogue Scout", "Ice - Normal", 0, 1, 271, 1, 1, 3, 150, "merc01", "merc41", 150, 105, 45, 8, 15, 6, 35, 10, 45, 16, 10, 10, , 1, 3, 2, 0, 8, "bow", , "carw", 75, "Inner Sight", 4, 10, 0, 1, 10, "Cold Arrow", 4, 25, 2, 1, 10, , , , , , , , , , , , , , , , , , , , , , , , , 2, 1, 2, 0], - ["Rogue Scout", "Ice - Normal", 0, 1, 271, 1, 1, 25, 150, "merc01", "merc41", 150, 105, 221, 12, 147, 12, 63, 10, 89, 16, 230, 20, 1, 7, 9, 4, 44, 7, "bow", , "carw", 75, "Inner Sight", 4, 10, 0, 8, 10, "Cold Arrow", 4, 36, 0, 8, 10, , , , , , , , , , , , , , , , , , , , , , , , , 2, 1, 2, 0], - ["Rogue Scout", "Ice - Normal", 0, 1, 271, 1, 1, 49, 150, "merc01", "merc41", 150, 105, 509, 16, 435, 18, 93, 10, 137, 16, 710, 30, 2, 19, 21, 6, 86, 5, "bow", , "carw", 75, "Inner Sight", 4, 10, 0, 16, 10, "Cold Arrow", 4, 36, 0, 16, 10, , , , , , , , , , , , , , , , , , , , , , , , , 2, 1, 2, 0], - ["Rogue Scout", "Fire - Nightmare", 0, 2, 271, 1, 2, 25, 150, "merc01", "merc41", 6000, 110, 199, 12, 132, 12, 60, 10, 84, 16, 207, 20, 1, 6, 8, 4, 41, 7, "bow", , "farw", 75, "Inner Sight", 4, 10, 0, 7, 10, "Fire Arrow", 4, 69, 0, 7, 10, , , , , , , , , , , , , , , , , , , , , , , , , 2, 1, 2, 0], - ["Rogue Scout", "Fire - Nightmare", 0, 2, 271, 1, 2, 49, 150, "merc01", "merc41", 6000, 110, 487, 16, 420, 18, 90, 10, 132, 16, 687, 30, 2, 18, 20, 6, 83, 5, "bow", , "farw", 75, "Inner Sight", 4, 10, 0, 15, 10, "Fire Arrow", 4, 69, 0, 15, 10, , , , , , , , , , , , , , , , , , , , , , , , , 2, 1, 2, 0], - ["Rogue Scout", "Ice - Nightmare", 0, 3, 271, 1, 2, 25, 150, "merc01", "merc41", 7500, 115, 199, 12, 132, 12, 60, 10, 84, 16, 207, 20, 1, 6, 8, 4, 41, 7, "bow", , "carw", 75, "Inner Sight", 4, 10, 0, 7, 10, "Cold Arrow", 4, 69, 0, 7, 10, , , , , , , , , , , , , , , , , , , , , , , , , 2, 1, 2, 0], - ["Rogue Scout", "Ice - Nightmare", 0, 3, 271, 1, 2, 49, 150, "merc01", "merc41", 7500, 115, 487, 16, 420, 18, 90, 10, 132, 16, 687, 30, 2, 18, 20, 6, 83, 5, "bow", , "carw", 75, "Inner Sight", 4, 10, 0, 15, 10, "Cold Arrow", 4, 69, 0, 15, 10, , , , , , , , , , , , , , , , , , , , , , , , , 2, 1, 2, 0], - ["Rogue Scout", "Fire - Hell", 0, 4, 271, 1, 3, 49, 150, "merc01", "merc41", 12500, 120, 438, 16, 378, 18, 87, 10, 127, 16, 618, 30, 2, 17, 19, 6, 80, 5, "bow", , "farw", 75, "Inner Sight", 4, 10, 0, 14, 10, "Fire Arrow", 4, 69, 0, 14, 10, , , , , , , , , , , , , , , , , , , , , , , , , 2, 1, 2, 0], - ["Rogue Scout", "Ice - Hell", 0, 5, 271, 1, 3, 49, 150, "merc01", "merc41", 14000, 125, 438, 16, 378, 18, 87, 10, 127, 16, 618, 30, 2, 17, 19, 6, 80, 5, "bow", , "carw", 75, "Inner Sight", 4, 10, 0, 14, 10, "Cold Arrow", 4, 69, 0, 14, 10, , , , , , , , , , , , , , , , , , , , , , , , , 2, 1, 2, 0], - ["Rogue Scout", "Fire - Normal", 100, 0, 271, 1, 1, 3, 150, "merc01", "merc41", 100, 100, 45, 9, 15, 8, 35, 10, 45, 16, 10, 12, , 1, 3, 2, 0, 8, "bow", , "farw", 75, "Inner Sight", 4, 10, 0, 1, 10, "Fire Arrow", 4, 25, 5, 1, 10, , , , , , , , , , , , , , , , , , , , , , , , , 2, 1, 2, 0], - ["Rogue Scout", "Fire - Normal", 100, 0, 271, 1, 1, 36, 150, "merc01", "merc41", 100, 100, 342, 18, 279, 15, 77, 10, 111, 16, 406, 24, 1, 9, 11, 4, 66, 7, "bow", , "farw", 75, "Inner Sight", 4, 10, 0, 12, 10, "Fire Arrow", 4, 67, 0, 12, 10, , , , , , , , , , , , , , , , , , , , , , , , , 2, 1, 2, 0], - ["Rogue Scout", "Fire - Normal", 100, 0, 271, 1, 1, 67, 150, "merc01", "merc41", 100, 100, 900, 30, 744, 22, 116, 10, 173, 16, 1150, 36, 2, 25, 27, 6, 121, 5, "bow", , "farw", 75, "Inner Sight", 4, 10, 0, 22, 10, "Fire Arrow", 4, 67, 0, 22, 10, , , , , , , , , , , , , , , , , , , , , , , , , 2, 1, 2, 0], - ["Rogue Scout", "Ice - Normal", 100, 1, 271, 1, 1, 3, 150, "merc01", "merc41", 150, 105, 45, 9, 15, 8, 35, 10, 45, 16, 10, 12, , 1, 3, 2, 0, 8, "bow", , "carw", 75, "Inner Sight", 4, 10, 0, 1, 10, "Cold Arrow", 4, 25, 5, 1, 10, , , , , , , , , , , , , , , , , , , , , , , , , 2, 1, 2, 0], - ["Rogue Scout", "Ice - Normal", 100, 1, 271, 1, 1, 36, 150, "merc01", "merc41", 150, 105, 342, 18, 279, 15, 77, 10, 111, 16, 406, 24, 1, 9, 11, 4, 66, 7, "bow", , "carw", 75, "Inner Sight", 4, 10, 0, 12, 10, "Cold Arrow", 4, 67, 0, 12, 10, , , , , , , , , , , , , , , , , , , , , , , , , 2, 1, 2, 0], - ["Rogue Scout", "Ice - Normal", 100, 1, 271, 1, 1, 67, 150, "merc01", "merc41", 150, 105, 900, 30, 744, 22, 116, 10, 173, 16, 1150, 36, 2, 25, 27, 6, 121, 5, "bow", , "carw", 75, "Inner Sight", 4, 10, 0, 22, 10, "Cold Arrow", 4, 67, 0, 22, 10, , , , , , , , , , , , , , , , , , , , , , , , , 2, 1, 2, 0], - ["Rogue Scout", "Fire - Nightmare", 100, 2, 271, 1, 2, 36, 150, "merc01", "merc41", 6000, 110, 308, 18, 251, 15, 74, 10, 106, 16, 365, 24, 1, 8, 10, 4, 63, 7, "bow", , "farw", 75, "Inner Sight", 4, 10, 0, 11, 10, "Fire Arrow", 4, 69, 0, 11, 10, , , , , , , , , , , , , , , , , , , , , , , , , 2, 1, 2, 0], - ["Rogue Scout", "Fire - Nightmare", 100, 2, 271, 1, 2, 67, 150, "merc01", "merc41", 6000, 110, 866, 30, 716, 22, 113, 10, 168, 16, 1109, 36, 2, 24, 26, 6, 118, 5, "bow", , "farw", 75, "Inner Sight", 4, 10, 0, 21, 10, "Fire Arrow", 4, 69, 0, 21, 10, , , , , , , , , , , , , , , , , , , , , , , , , 2, 1, 2, 0], - ["Rogue Scout", "Ice - Nightmare", 100, 3, 271, 1, 2, 36, 150, "merc01", "merc41", 7500, 115, 308, 18, 251, 15, 74, 10, 106, 16, 365, 24, 1, 8, 10, 4, 63, 7, "bow", , "carw", 75, "Inner Sight", 4, 10, 0, 11, 10, "Cold Arrow", 4, 69, 0, 11, 10, , , , , , , , , , , , , , , , , , , , , , , , , 2, 1, 2, 0], - ["Rogue Scout", "Ice - Nightmare", 100, 3, 271, 1, 2, 67, 150, "merc01", "merc41", 7500, 115, 866, 30, 716, 22, 113, 10, 168, 16, 1109, 36, 2, 24, 26, 6, 118, 5, "bow", , "carw", 75, "Inner Sight", 4, 10, 0, 21, 10, "Cold Arrow", 4, 69, 0, 21, 10, , , , , , , , , , , , , , , , , , , , , , , , , 2, 1, 2, 0], - ["Rogue Scout", "Fire - Hell", 100, 4, 271, 1, 3, 67, 150, "merc01", "merc41", 12500, 120, 779, 30, 644, 22, 110, 10, 163, 16, 998, 36, 2, 23, 25, 6, 115, 5, "bow", , "farw", 75, "Inner Sight", 4, 10, 0, 20, 10, "Fire Arrow", 4, 69, 0, 20, 10, , , , , , , , , , , , , , , , , , , , , , , , , 2, 1, 2, 0], - ["Rogue Scout", "Ice - Hell", 100, 5, 271, 1, 3, 67, 150, "merc01", "merc41", 14000, 125, 779, 30, 644, 22, 110, 10, 163, 16, 998, 36, 2, 23, 25, 6, 115, 5, "bow", , "carw", 75, "Inner Sight", 4, 10, 0, 20, 10, "Cold Arrow", 4, 69, 0, 20, 10, , , , , , , , , , , , , , , , , , , , , , , , , 2, 1, 2, 0], - ["Desert Mercenary", "Comb-Normal", 0, 6, 338, 2, 1, 9, 198, "merca201", "merca221", 350, 110, 120, 10, 45, 9, 57, 14, 40, 12, 20, 10, 1, 7, 14, 4, 18, 8, "pole", "spea", "comb", 30, "Jab", 14, 70, 4, 3, 10, "Prayer", 1, 10, 0, 3, 10, , , , , , , , , , , , , , , , , , , , , , , , , 2, 2, 2, 0], - ["Desert Mercenary", "Comb-Normal", 0, 6, 338, 2, 1, 31, 198, "merca201", "merca221", 350, 110, 340, 20, 243, 16, 96, 14, 73, 12, 240, 20, 3, 18, 25, 6, 62, 7, "pole", "spea", "comb", 30, "Jab", 14, 92, 4, 10, 10, "Prayer", 1, 10, 0, 10, 10, , , , , , , , , , , , , , , , , , , , , , , , , 2, 2, 2, 0], - ["Desert Mercenary", "Comb-Normal", 0, 6, 338, 2, 1, 55, 198, "merca201", "merca221", 350, 110, 820, 35, 627, 24, 138, 14, 109, 12, 720, 30, 4, 36, 43, 8, 104, 4, "pole", "spea", "comb", 30, "Jab", 14, 116, 4, 18, 10, "Prayer", 1, 10, 0, 18, 0, , , , , , , , , , , , , , , , , , , , , , , , , 2, 2, 2, 0], - ["Desert Mercenary", "Def-Normal", 0, 7, 338, 2, 1, 9, 198, "merca201", "merca221", 350, 110, 120, 10, 45, 9, 57, 14, 40, 12, 20, 10, 1, 7, 14, 4, 18, 8, "pole", "spea", "def", 30, "Jab", 14, 70, 4, 3, 10, "Defiance", 1, 10, 0, 3, 10, , , , , , , , , , , , , , , , , , , , , , , , , 2, 2, 2, 0], - ["Desert Mercenary", "Def-Normal", 0, 7, 338, 2, 1, 31, 198, "merca201", "merca221", 350, 110, 340, 20, 243, 16, 96, 14, 73, 12, 240, 20, 3, 18, 25, 6, 62, 7, "pole", "spea", "def", 30, "Jab", 14, 92, 4, 10, 10, "Defiance", 1, 10, 0, 10, 10, , , , , , , , , , , , , , , , , , , , , , , , , 2, 2, 2, 0], - ["Desert Mercenary", "Def-Normal", 0, 7, 338, 2, 1, 55, 198, "merca201", "merca221", 350, 110, 820, 35, 627, 24, 138, 14, 109, 12, 720, 30, 4, 36, 43, 8, 104, 4, "pole", "spea", "def", 30, "Jab", 14, 116, 4, 18, 0, "Defiance", 1, 10, 0, 18, 0, , , , , , , , , , , , , , , , , , , , , , , , , 2, 2, 2, 0], - ["Desert Mercenary", "Off-Normal", 0, 8, 338, 2, 1, 9, 198, "merca201", "merca221", 350, 110, 120, 10, 45, 9, 57, 14, 40, 12, 20, 10, 1, 7, 14, 4, 18, 8, "pole", "spea", "off", 30, "Jab", 14, 70, 4, 3, 10, "Blessed Aim", 1, 10, 0, 3, 10, , , , , , , , , , , , , , , , , , , , , , , , , 2, 2, 2, 0], - ["Desert Mercenary", "Off-Normal", 0, 8, 338, 2, 1, 31, 198, "merca201", "merca221", 350, 110, 340, 20, 243, 16, 96, 14, 73, 12, 240, 20, 3, 18, 25, 6, 62, 7, "pole", "spea", "off", 30, "Jab", 14, 92, 4, 10, 10, "Blessed Aim", 1, 10, 0, 10, 10, , , , , , , , , , , , , , , , , , , , , , , , , 2, 2, 2, 0], - ["Desert Mercenary", "Off-Normal", 0, 8, 338, 2, 1, 55, 198, "merca201", "merca221", 350, 110, 820, 35, 627, 24, 138, 14, 109, 12, 720, 30, 4, 36, 43, 8, 104, 4, "pole", "spea", "off", 30, "Jab", 14, 116, 4, 18, 0, "Blessed Aim", 1, 10, 0, 18, 0, , , , , , , , , , , , , , , , , , , , , , , , , 2, 2, 2, 0], - ["Desert Mercenary", "Comb-Nightmare", 0, 9, 338, 2, 2, 31, 198, "merca201", "merca221", 7900, 120, 306, 20, 219, 16, 92, 14, 69, 12, 216, 20, 3, 16, 23, 6, 59, 7, "pole", "spea", "comb", 30, "Jab", 14, 120, 4, 9, 10, "Thorns", 1, 10, 0, 5, 10, , , , , , , , , , , , , , , , , , , , , , , , , 2, 2, 2, 0], - ["Desert Mercenary", "Comb-Nightmare", 0, 9, 338, 2, 2, 55, 198, "merca201", "merca221", 7900, 120, 786, 35, 603, 24, 134, 14, 105, 12, 696, 30, 4, 34, 41, 8, 101, 4, "pole", "spea", "comb", 30, "Jab", 14, 144, 4, 17, 10, "Thorns", 1, 10, 0, 13, 0, , , , , , , , , , , , , , , , , , , , , , , , , 2, 2, 2, 0], - ["Desert Mercenary", "Def-Nightmare", 0, 10, 338, 2, 2, 31, 198, "merca201", "merca221", 7900, 120, 306, 20, 219, 16, 92, 14, 69, 12, 216, 20, 3, 16, 23, 6, 59, 7, "pole", "spea", "def", 30, "Jab", 14, 120, 4, 9, 10, "Holy Freeze", 1, 10, 0, 6, 10, , , , , , , , , , , , , , , , , , , , , , , , , 2, 2, 2, 0], - ["Desert Mercenary", "Def-Nightmare", 0, 10, 338, 2, 2, 55, 198, "merca201", "merca221", 7900, 120, 786, 35, 603, 24, 134, 14, 105, 12, 696, 30, 4, 34, 41, 8, 101, 4, "pole", "spea", "def", 30, "Jab", 14, 144, 4, 17, 0, "Holy Freeze", 1, 10, 0, 14, 0, , , , , , , , , , , , , , , , , , , , , , , , , 2, 2, 2, 0], - ["Desert Mercenary", "Off-Nightmare", 0, 11, 338, 2, 2, 31, 198, "merca201", "merca221", 7900, 120, 306, 20, 219, 16, 92, 14, 69, 12, 216, 20, 3, 16, 23, 6, 59, 7, "pole", "spea", "off", 30, "Jab", 14, 120, 4, 9, 10, "Might", 1, 10, 0, 7, 8, , , , , , , , , , , , , , , , , , , , , , , , , 2, 2, 2, 0], - ["Desert Mercenary", "Off-Nightmare", 0, 11, 338, 2, 2, 55, 198, "merca201", "merca221", 7900, 120, 786, 35, 603, 24, 134, 14, 105, 12, 696, 30, 4, 34, 41, 8, 101, 4, "pole", "spea", "off", 30, "Jab", 14, 144, 4, 17, 0, "Might", 1, 10, 0, 13, 8, , , , , , , , , , , , , , , , , , , , , , , , , 2, 2, 2, 0], - ["Desert Mercenary", "Comb-Hell", 0, 12, 338, 2, 3, 55, 198, "merca201", "merca221", 15000, 130, 707, 35, 543, 24, 130, 14, 101, 12, 626, 30, 4, 32, 39, 8, 98, 4, "pole", "spea", "comb", 30, "Jab", 14, 104, 4, 16, 10, "Prayer", 1, 10, 0, 16, 0, , , , , , , , , , , , , , , , , , , , , , , , , 2, 2, 2, 0], - ["Desert Mercenary", "Def-Hell", 0, 13, 338, 2, 3, 55, 198, "merca201", "merca221", 15000, 130, 707, 35, 543, 24, 130, 14, 101, 12, 626, 30, 4, 32, 39, 8, 98, 4, "pole", "spea", "def", 30, "Jab", 14, 104, 4, 16, 0, "Defiance", 1, 10, 0, 16, 0, , , , , , , , , , , , , , , , , , , , , , , , , 2, 2, 2, 0], - ["Desert Mercenary", "Off-Hell", 0, 14, 338, 2, 3, 55, 198, "merca201", "merca221", 15000, 130, 707, 35, 543, 24, 130, 14, 101, 12, 626, 30, 4, 32, 39, 8, 98, 4, "pole", "spea", "off", 30, "Jab", 14, 104, 4, 16, 0, "Blessed Aim", 1, 10, 0, 16, 0, , , , , , , , , , , , , , , , , , , , , , , , , 2, 2, 2, 0], - ["Desert Mercenary", "Comb-Normal", 100, 6, 338, 2, 1, 9, 198, "merca201", "merca221", 350, 110, 120, 15, 45, 11, 57, 14, 40, 12, 20, 12, 1, 7, 14, 4, 18, 8, "pole", "spea", "comb", 30, "Jab", 14, 70, 4, 3, 10, "Prayer", 1, 10, 0, 3, 7, , , , , , , , , , , , , , , , , , , , , , , , , 2, 2, 2, 0], - ["Desert Mercenary", "Comb-Normal", 100, 6, 338, 2, 1, 43, 198, "merca201", "merca221", 350, 110, 630, 25, 419, 19, 117, 14, 91, 12, 428, 24, 3, 24, 31, 6, 86, 7, "pole", "spea", "comb", 30, "Jab", 14, 104, 4, 14, 10, "Prayer", 1, 10, 0, 11, 7, , , , , , , , , , , , , , , , , , , , , , , , , 2, 2, 2, 0], - ["Desert Mercenary", "Comb-Normal", 100, 6, 338, 2, 1, 75, 198, "merca201", "merca221", 350, 110, 1430, 40, 1027, 28, 173, 14, 139, 12, 1196, 36, 4, 48, 55, 8, 142, 4, "pole", "spea", "comb", 30, "Jab", 14, 136, 4, 24, 10, "Prayer", 1, 10, 0, 18, 0, , , , , , , , , , , , , , , , , , , , , , , , , 2, 2, 2, 0], - ["Desert Mercenary", "Def-Normal", 100, 7, 338, 2, 1, 9, 198, "merca201", "merca221", 350, 110, 120, 15, 45, 11, 57, 14, 40, 12, 20, 12, 1, 7, 14, 4, 18, 8, "pole", "spea", "def", 30, "Jab", 14, 70, 4, 3, 10, "Defiance", 1, 10, 0, 3, 7, , , , , , , , , , , , , , , , , , , , , , , , , 2, 2, 2, 0], - ["Desert Mercenary", "Def-Normal", 100, 7, 338, 2, 1, 43, 198, "merca201", "merca221", 350, 110, 630, 25, 419, 19, 117, 14, 91, 12, 428, 24, 3, 24, 31, 6, 86, 7, "pole", "spea", "def", 30, "Jab", 14, 104, 4, 14, 10, "Defiance", 1, 10, 0, 11, 7, , , , , , , , , , , , , , , , , , , , , , , , , 2, 2, 2, 0], - ["Desert Mercenary", "Def-Normal", 100, 7, 338, 2, 1, 75, 198, "merca201", "merca221", 350, 110, 1430, 40, 1027, 28, 173, 14, 139, 12, 1196, 36, 4, 48, 55, 8, 142, 4, "pole", "spea", "def", 30, "Jab", 14, 136, 4, 24, 0, "Defiance", 1, 10, 0, 18, 0, , , , , , , , , , , , , , , , , , , , , , , , , 2, 2, 2, 0], - ["Desert Mercenary", "Off-Normal", 100, 8, 338, 2, 1, 9, 198, "merca201", "merca221", 350, 110, 120, 15, 45, 11, 57, 14, 40, 12, 20, 12, 1, 7, 14, 4, 18, 8, "pole", "spea", "off", 30, "Jab", 14, 70, 4, 3, 10, "Blessed Aim", 1, 10, 0, 3, 7, , , , , , , , , , , , , , , , , , , , , , , , , 2, 2, 2, 0], - ["Desert Mercenary", "Off-Normal", 100, 8, 338, 2, 1, 43, 198, "merca201", "merca221", 350, 110, 630, 25, 419, 19, 117, 14, 91, 12, 428, 24, 3, 24, 31, 6, 86, 7, "pole", "spea", "off", 30, "Jab", 14, 104, 4, 14, 10, "Blessed Aim", 1, 10, 0, 11, 7, , , , , , , , , , , , , , , , , , , , , , , , , 2, 2, 2, 0], - ["Desert Mercenary", "Off-Normal", 100, 8, 338, 2, 1, 75, 198, "merca201", "merca221", 350, 110, 1430, 40, 1027, 28, 173, 14, 139, 12, 1196, 36, 4, 48, 55, 8, 142, 4, "pole", "spea", "off", 30, "Jab", 14, 136, 4, 24, 0, "Blessed Aim", 1, 10, 0, 18, 0, , , , , , , , , , , , , , , , , , , , , , , , , 2, 2, 2, 0], - ["Desert Mercenary", "Comb-Nightmare", 100, 9, 338, 2, 2, 43, 198, "merca201", "merca221", 7900, 120, 567, 25, 377, 19, 113, 14, 87, 12, 385, 24, 3, 22, 29, 6, 83, 7, "pole", "spea", "comb", 30, "Jab", 14, 120, 4, 13, 10, "Thorns", 1, 10, 0, 5, 10, , , , , , , , , , , , , , , , , , , , , , , , , 2, 2, 2, 0], - ["Desert Mercenary", "Comb-Nightmare", 100, 9, 338, 2, 2, 75, 198, "merca201", "merca221", 7900, 120, 1367, 40, 985, 28, 169, 14, 135, 12, 1153, 36, 4, 46, 53, 8, 139, 4, "pole", "spea", "comb", 30, "Jab", 14, 152, 4, 23, 10, "Thorns", 1, 10, 0, 15, 0, , , , , , , , , , , , , , , , , , , , , , , , , 2, 2, 2, 0], - ["Desert Mercenary", "Def-Nightmare", 100, 10, 338, 2, 2, 43, 198, "merca201", "merca221", 7900, 120, 567, 25, 377, 19, 113, 14, 87, 12, 385, 24, 3, 22, 29, 6, 83, 7, "pole", "spea", "def", 30, "Jab", 14, 120, 4, 13, 10, "Holy Freeze", 1, 10, 0, 6, 10, , , , , , , , , , , , , , , , , , , , , , , , , 2, 2, 2, 0], - ["Desert Mercenary", "Def-Nightmare", 100, 10, 338, 2, 2, 75, 198, "merca201", "merca221", 7900, 120, 1367, 40, 985, 28, 169, 14, 135, 12, 1153, 36, 4, 46, 53, 8, 139, 4, "pole", "spea", "def", 30, "Jab", 14, 152, 4, 23, 0, "Holy Freeze", 1, 10, 0, 16, 0, , , , , , , , , , , , , , , , , , , , , , , , , 2, 2, 2, 0], - ["Desert Mercenary", "Off-Nightmare", 100, 11, 338, 2, 2, 43, 198, "merca201", "merca221", 7900, 120, 567, 25, 377, 19, 113, 14, 87, 12, 385, 24, 3, 22, 29, 6, 83, 7, "pole", "spea", "off", 30, "Jab", 14, 120, 4, 13, 10, "Might", 1, 10, 0, 7, 8, , , , , , , , , , , , , , , , , , , , , , , , , 2, 2, 2, 0], - ["Desert Mercenary", "Off-Nightmare", 100, 11, 338, 2, 2, 75, 198, "merca201", "merca221", 7900, 120, 1367, 40, 985, 28, 169, 14, 135, 12, 1153, 36, 4, 46, 53, 8, 139, 4, "pole", "spea", "off", 30, "Jab", 14, 152, 4, 23, 0, "Might", 1, 10, 0, 15, 8, , , , , , , , , , , , , , , , , , , , , , , , , 2, 2, 2, 0], - ["Desert Mercenary", "Comb-Hell", 100, 12, 338, 2, 3, 75, 198, "merca201", "merca221", 15000, 130, 1230, 40, 887, 28, 165, 14, 131, 12, 1038, 36, 4, 44, 51, 8, 136, 4, "pole", "spea", "comb", 30, "Jab", 14, 104, 4, 22, 10, "Prayer", 1, 10, 0, 16, 0, , , , , , , , , , , , , , , , , , , , , , , , , 2, 2, 2, 0], - ["Desert Mercenary", "Def-Hell", 100, 13, 338, 2, 3, 75, 198, "merca201", "merca221", 15000, 130, 1230, 40, 887, 28, 165, 14, 131, 12, 1038, 36, 4, 44, 51, 8, 136, 4, "pole", "spea", "def", 30, "Jab", 14, 104, 4, 22, 0, "Defiance", 1, 10, 0, 16, 0, , , , , , , , , , , , , , , , , , , , , , , , , 2, 2, 2, 0], - ["Desert Mercenary", "Off-Hell", 100, 14, 338, 2, 3, 75, 198, "merca201", "merca221", 15000, 130, 1230, 40, 887, 28, 165, 14, 131, 12, 1038, 36, 4, 44, 51, 8, 136, 4, "pole", "spea", "off", 30, "Jab", 14, 104, 4, 22, 0, "Blessed Aim", 1, 10, 0, 16, 0, , , , , , , , , , , , , , , , , , , , , , , , , 2, 2, 2, 0], - ["Eastern Sorceror", "Fire-Normal", 0, 15, 359, 3, 1, 15, 252, "merca222", "merca241", 1000, 110, 160, 8, 80, 4, 49, 10, 40, 8, 15, 10, , 1, 7, 4, 25, 7, "swor", "shie", "fire", 10, "Inferno", 7, 60, 0, 6, 10, "Fire Ball", 7, 30, 0, 4, 10, , , , , , , , , , , , , , , , , , , , , , , , , 2, 2, 2, 1], - ["Eastern Sorceror", "Fire-Normal", 0, 15, 359, 3, 1, 37, 252, "merca222", "merca241", 1000, 110, 336, 14, 168, 10, 77, 10, 62, 8, 235, 20, 3, 12, 18, 4, 64, 7, "swor", "shie", "fire", 10, "Inferno", 7, 60, 0, 13, 10, "Fire Ball", 7, 30, 0, 11, 10, , , , , , , , , , , , , , , , , , , , , , , , , 2, 2, 2, 1], - ["Eastern Sorceror", "Fire-Normal", 0, 15, 359, 3, 1, 61, 252, "merca222", "merca241", 1000, 110, 672, 20, 408, 18, 107, 10, 86, 8, 715, 30, 5, 24, 30, 4, 106, 6, "swor", "shie", "fire", 10, "Inferno", 7, 60, 0, 21, 10, "Fire Ball", 7, 30, 0, 19, 10, , , , , , , , , , , , , , , , , , , , , , , , , 2, 2, 2, 1], - ["Eastern Sorceror", "Cold-Normal", 0, 16, 359, 3, 1, 15, 252, "merca222", "merca241", 1500, 120, 160, 8, 80, 4, 49, 10, 40, 8, 15, 10, , 1, 7, 4, 25, 7, "swor", "shie", "cold", 10, "Glacial Spike", 7, 60, 0, 1, 5, "Frozen Armor", 7, 1000, 0, 2, 10, "Ice Blast", 7, 240, 0, 6, 10, , , , , , , , , , , , , , , , , , , 2, 2, 2, 1], - ["Eastern Sorceror", "Cold-Normal", 0, 16, 359, 3, 1, 37, 252, "merca222", "merca241", 1500, 120, 336, 14, 168, 10, 77, 10, 62, 8, 235, 20, 3, 12, 18, 4, 64, 7, "swor", "shie", "cold", 10, "Glacial Spike", 7, 60, 0, 5, 5, "Frozen Armor", 7, 1000, 0, 9, 10, "Ice Blast", 7, 240, 0, 13, 10, , , , , , , , , , , , , , , , , , , 2, 2, 2, 1], - ["Eastern Sorceror", "Cold-Normal", 0, 16, 359, 3, 1, 61, 252, "merca222", "merca241", 1500, 120, 672, 20, 408, 16, 107, 10, 86, 8, 715, 30, 5, 24, 30, 4, 106, 4, "swor", "shie", "cold", 10, "Glacial Spike", 7, 60, 0, 9, 5, "Frozen Armor", 7, 1000, 0, 17, 10, "Ice Blast", 7, 240, 0, 21, 10, , , , , , , , , , , , , , , , , , , 2, 2, 2, 1], - ["Eastern Sorceror", "Ltng-Normal", 0, 17, 359, 3, 1, 15, 252, "merca222", "merca241", 1000, 110, 160, 8, 80, 4, 49, 10, 40, 8, 15, 10, , 1, 7, 4, 25, 7, "swor", "shie", "ltng", 10, "Charged Bolt", 7, 60, 0, 4, 5, "Lightning", 7, 30, 0, 3, 10, , , , , , , , , , , , , , , , , , , , , , , , , 2, 2, 2, 1], - ["Eastern Sorceror", "Ltng-Normal", 0, 17, 359, 3, 1, 37, 252, "merca222", "merca241", 1000, 110, 336, 14, 168, 10, 77, 10, 62, 8, 235, 20, 3, 12, 18, 4, 64, 7, "swor", "shie", "ltng", 10, "Charged Bolt", 7, 60, 0, 8, 5, "Lightning", 7, 30, 0, 10, 10, , , , , , , , , , , , , , , , , , , , , , , , , 2, 2, 2, 1], - ["Eastern Sorceror", "Ltng-Normal", 0, 17, 359, 3, 1, 61, 252, "merca222", "merca241", 1000, 110, 672, 20, 408, 16, 107, 10, 86, 8, 715, 30, 5, 24, 30, 4, 106, 4, "swor", "shie", "ltng", 10, "Charged Bolt", 7, 60, 0, 12, 5, "Lightning", 7, 30, 0, 18, 10, , , , , , , , , , , , , , , , , , , , , , , , , 2, 2, 2, 1], - ["Eastern Sorceror", "Fire-Nightmare", 0, 18, 359, 3, 2, 37, 252, "merca222", "merca241", 9500, 120, 302, 14, 151, 10, 73, 10, 58, 8, 212, 20, 3, 10, 16, 4, 61, 7, "swor", "shie", "fire", 10, "Inferno", 7, 60, 0, 12, 10, "Fire Ball", 7, 30, 0, 10, 10, , , , , , , , , , , , , , , , , , , , , , , , , 2, 2, 2, 1], - ["Eastern Sorceror", "Fire-Nightmare", 0, 18, 359, 3, 2, 61, 252, "merca222", "merca241", 9500, 120, 638, 20, 391, 16, 103, 10, 82, 8, 692, 30, 5, 22, 28, 4, 103, 4, "swor", "shie", "fire", 10, "Inferno", 7, 60, 0, 20, 10, "Fire Ball", 7, 30, 0, 18, 10, , , , , , , , , , , , , , , , , , , , , , , , , 2, 2, 2, 1], - ["Eastern Sorceror", "Cold-Nightmare", 0, 19, 359, 3, 2, 37, 252, "merca222", "merca241", 12000, 130, 302, 14, 151, 10, 73, 10, 58, 8, 212, 20, 3, 10, 16, 4, 61, 7, "swor", "shie", "cold", 10, "Glacial Spike", 7, 60, 0, 4, 5, "Frozen Armor", 7, 1000, 0, 8, 10, "Ice Blast", 7, 240, 0, 12, 10, , , , , , , , , , , , , , , , , , , 2, 2, 2, 1], - ["Eastern Sorceror", "Cold-Nightmare", 0, 19, 359, 3, 2, 61, 252, "merca222", "merca241", 12000, 130, 638, 20, 391, 16, 103, 10, 82, 8, 692, 30, 5, 22, 28, 4, 103, 4, "swor", "shie", "cold", 10, "Glacial Spike", 7, 60, 0, 8, 5, "Frozen Armor", 7, 1000, 0, 16, 10, "Ice Blast", 7, 240, 0, 20, 10, , , , , , , , , , , , , , , , , , , 2, 2, 2, 1], - ["Eastern Sorceror", "Ltng-Nightmare", 0, 20, 359, 3, 2, 37, 252, "merca222", "merca241", 10000, 120, 302, 14, 151, 10, 73, 10, 58, 8, 212, 20, 3, 10, 16, 4, 61, 7, "swor", "shie", "ltng", 10, "Charged Bolt", 7, 60, 0, 7, 5, "Lightning", 7, 30, 0, 9, 10, , , , , , , , , , , , , , , , , , , , , , , , , 2, 2, 2, 1], - ["Eastern Sorceror", "Ltng-Nightmare", 0, 20, 359, 3, 2, 61, 252, "merca222", "merca241", 10000, 120, 638, 20, 391, 16, 103, 10, 82, 8, 692, 30, 5, 22, 28, 4, 103, 4, "swor", "shie", "ltng", 10, "Charged Bolt", 7, 60, 0, 11, 5, "Lightning", 7, 30, 0, 17, 10, , , , , , , , , , , , , , , , , , , , , , , , , 2, 2, 2, 1], - ["Eastern Sorceror", "Fire-Hell", 0, 21, 359, 3, 3, 61, 252, "merca222", "merca241", 21000, 130, 574, 20, 352, 16, 99, 10, 78, 8, 623, 30, 5, 20, 26, 4, 100, 4, "swor", "shie", "fire", 10, "Inferno", 7, 60, 0, 19, 10, "Fire Ball", 7, 30, 0, 17, 10, , , , , , , , , , , , , , , , , , , , , , , , , 2, 2, 2, 1], - ["Eastern Sorceror", "Cold-Hell", 0, 22, 359, 3, 3, 61, 252, "merca222", "merca241", 27000, 140, 574, 20, 352, 16, 99, 10, 78, 8, 623, 30, 5, 20, 26, 4, 100, 4, "swor", "shie", "cold", 10, "Glacial Spike", 7, 60, 0, 7, 5, "Frozen Armor", 7, 1000, 0, 15, 10, "Ice Blast", 7, 240, 0, 19, 10, , , , , , , , , , , , , , , , , , , 2, 2, 2, 1], - ["Eastern Sorceror", "Ltng-Hell", 0, 23, 359, 3, 3, 61, 252, "merca222", "merca241", 21000, 130, 574, 20, 352, 16, 99, 10, 78, 8, 623, 30, 5, 20, 27, 4, 100, 4, "swor", "shie", "ltng", 10, "Charged Bolt", 7, 60, 0, 10, 5, "Lightning", 7, 30, 0, 16, 10, , , , , , , , , , , , , , , , , , , , , , , , , 2, 2, 2, 1], - ["Eastern Sorceror", "Fire-Normal", 100, 15, 359, 3, 1, 15, 252, "merca222", "merca241", 1000, 110, 160, 9, 80, 5, 49, 10, 40, 8, 15, 12, , 1, 7, 4, 25, 7, "swor", "shie", "fire", 10, "Inferno", 7, 60, 0, 6, 10, "Fire Ball", 7, 30, 0, 4, 10, , , , , , , , , , , , , , , , , , , , , , , , , 2, 2, 2, 1], - ["Eastern Sorceror", "Fire-Normal", 100, 15, 359, 3, 1, 49, 252, "merca222", "merca241", 1000, 110, 466, 18, 250, 13, 92, 10, 74, 8, 423, 24, 3, 18, 24, 4, 85, 7, "swor", "shie", "fire", 10, "Inferno", 7, 60, 0, 17, 10, "Fire Ball", 7, 30, 0, 15, 10, , , , , , , , , , , , , , , , , , , , , , , , , 2, 2, 2, 1], - ["Eastern Sorceror", "Fire-Normal", 100, 15, 359, 3, 1, 79, 252, "merca222", "merca241", 1000, 110, 1006, 27, 640, 25, 130, 10, 104, 8, 1143, 36, 5, 33, 39, 4, 138, 6, "swor", "shie", "fire", 10, "Inferno", 7, 60, 0, 27, 10, "Fire Ball", 7, 30, 0, 25, 10, , , , , , , , , , , , , , , , , , , , , , , , , 2, 2, 2, 1], - ["Eastern Sorceror", "Cold-Normal", 100, 16, 359, 3, 1, 15, 252, "merca222", "merca241", 1500, 120, 160, 9, 80, 5, 49, 10, 40, 8, 15, 12, , 1, 7, 4, 25, 7, "swor", "shie", "cold", 10, "Glacial Spike", 7, 60, 0, 1, 5, "Frozen Armor", 7, 1000, 0, 2, 10, "Ice Blast", 7, 240, 0, 6, 10, , , , , , , , , , , , , , , , , , , 2, 2, 2, 1], - ["Eastern Sorceror", "Cold-Normal", 100, 16, 359, 3, 1, 49, 252, "merca222", "merca241", 1500, 120, 466, 18, 250, 13, 92, 10, 74, 8, 423, 24, 3, 18, 24, 4, 85, 7, "swor", "shie", "cold", 10, "Glacial Spike", 7, 60, 0, 7, 5, "Frozen Armor", 7, 1000, 0, 13, 10, "Ice Blast", 7, 240, 0, 17, 10, , , , , , , , , , , , , , , , , , , 2, 2, 2, 1], - ["Eastern Sorceror", "Cold-Normal", 100, 16, 359, 3, 1, 79, 252, "merca222", "merca241", 1500, 120, 1006, 27, 640, 25, 130, 10, 104, 8, 1143, 36, 5, 33, 39, 4, 138, 4, "swor", "shie", "cold", 10, "Glacial Spike", 7, 60, 0, 12, 5, "Frozen Armor", 7, 1000, 0, 23, 10, "Ice Blast", 7, 240, 0, 27, 10, , , , , , , , , , , , , , , , , , , 2, 2, 2, 1], - ["Eastern Sorceror", "Ltng-Normal", 100, 17, 359, 3, 1, 15, 252, "merca222", "merca241", 1000, 110, 160, 9, 80, 5, 49, 10, 40, 8, 15, 12, , 1, 7, 4, 25, 7, "swor", "shie", "ltng", 10, "Charged Bolt", 7, 60, 0, 4, 5, "Lightning", 7, 30, 0, 3, 10, , , , , , , , , , , , , , , , , , , , , , , , , 2, 2, 2, 1], - ["Eastern Sorceror", "Ltng-Normal", 100, 17, 359, 3, 1, 49, 252, "merca222", "merca241", 1000, 110, 466, 18, 250, 13, 92, 10, 74, 8, 423, 24, 3, 18, 24, 4, 85, 7, "swor", "shie", "ltng", 10, "Charged Bolt", 7, 60, 0, 10, 5, "Lightning", 7, 30, 0, 14, 10, , , , , , , , , , , , , , , , , , , , , , , , , 2, 2, 2, 1], - ["Eastern Sorceror", "Ltng-Normal", 100, 17, 359, 3, 1, 79, 252, "merca222", "merca241", 1000, 110, 1006, 27, 640, 25, 130, 10, 104, 8, 1143, 36, 5, 33, 39, 4, 138, 4, "swor", "shie", "ltng", 10, "Charged Bolt", 7, 60, 0, 15, 5, "Lightning", 7, 30, 0, 24, 10, , , , , , , , , , , , , , , , , , , , , , , , , 2, 2, 2, 1], - ["Eastern Sorceror", "Fire-Nightmare", 100, 18, 359, 3, 2, 49, 252, "merca222", "merca241", 9500, 120, 419, 18, 225, 13, 88, 10, 70, 8, 381, 24, 3, 16, 22, 4, 82, 7, "swor", "shie", "fire", 10, "Inferno", 7, 60, 0, 16, 10, "Fire Ball", 7, 30, 0, 14, 10, , , , , , , , , , , , , , , , , , , , , , , , , 2, 2, 2, 1], - ["Eastern Sorceror", "Fire-Nightmare", 100, 18, 359, 3, 2, 79, 252, "merca222", "merca241", 9500, 120, 959, 27, 615, 25, 126, 10, 100, 8, 1101, 36, 5, 31, 37, 4, 135, 4, "swor", "shie", "fire", 10, "Inferno", 7, 60, 0, 26, 10, "Fire Ball", 7, 30, 0, 24, 10, , , , , , , , , , , , , , , , , , , , , , , , , 2, 2, 2, 1], - ["Eastern Sorceror", "Cold-Nightmare", 100, 19, 359, 3, 2, 49, 252, "merca222", "merca241", 12000, 130, 419, 18, 225, 13, 88, 10, 70, 8, 381, 24, 3, 16, 22, 4, 82, 7, "swor", "shie", "cold", 10, "Glacial Spike", 7, 60, 0, 6, 5, "Frozen Armor", 7, 1000, 0, 12, 10, "Ice Blast", 7, 240, 0, 16, 10, , , , , , , , , , , , , , , , , , , 2, 2, 2, 1], - ["Eastern Sorceror", "Cold-Nightmare", 100, 19, 359, 3, 2, 79, 252, "merca222", "merca241", 12000, 130, 959, 27, 615, 25, 126, 10, 100, 8, 1101, 36, 5, 31, 37, 4, 135, 4, "swor", "shie", "cold", 10, "Glacial Spike", 7, 60, 0, 11, 5, "Frozen Armor", 7, 1000, 0, 22, 10, "Ice Blast", 7, 240, 0, 26, 10, , , , , , , , , , , , , , , , , , , 2, 2, 2, 1], - ["Eastern Sorceror", "Ltng-Nightmare", 100, 20, 359, 3, 2, 49, 252, "merca222", "merca241", 10000, 120, 419, 18, 225, 13, 88, 10, 70, 8, 381, 24, 3, 16, 22, 4, 82, 7, "swor", "shie", "ltng", 10, "Charged Bolt", 7, 60, 0, 9, 5, "Lightning", 7, 30, 0, 13, 10, , , , , , , , , , , , , , , , , , , , , , , , , 2, 2, 2, 1], - ["Eastern Sorceror", "Ltng-Nightmare", 100, 20, 359, 3, 2, 79, 252, "merca222", "merca241", 10000, 120, 959, 27, 615, 25, 126, 10, 100, 8, 1101, 36, 5, 31, 37, 4, 135, 4, "swor", "shie", "ltng", 10, "Charged Bolt", 7, 60, 0, 14, 5, "Lightning", 7, 30, 0, 23, 10, , , , , , , , , , , , , , , , , , , , , , , , , 2, 2, 2, 1], - ["Eastern Sorceror", "Fire-Hell", 100, 21, 359, 3, 3, 79, 252, "merca222", "merca241", 21000, 130, 863, 27, 554, 25, 122, 10, 96, 8, 991, 36, 5, 29, 35, 4, 132, 4, "swor", "shie", "fire", 10, "Inferno", 7, 60, 0, 25, 10, "Fire Ball", 7, 30, 0, 23, 10, , , , , , , , , , , , , , , , , , , , , , , , , 2, 2, 2, 1], - ["Eastern Sorceror", "Cold-Hell", 100, 22, 359, 3, 3, 79, 252, "merca222", "merca241", 27000, 140, 863, 27, 554, 25, 122, 10, 96, 8, 991, 36, 5, 29, 35, 4, 132, 4, "swor", "shie", "cold", 10, "Glacial Spike", 7, 60, 0, 10, 5, "Frozen Armor", 7, 1000, 0, 21, 10, "Ice Blast", 7, 240, 0, 25, 10, , , , , , , , , , , , , , , , , , , 2, 2, 2, 1], - ["Eastern Sorceror", "Ltng-Hell", 100, 23, 359, 3, 3, 79, 252, "merca222", "merca241", 21000, 130, 863, 27, 554, 25, 122, 10, 96, 8, 991, 36, 5, 29, 36, 4, 132, 4, "swor", "shie", "ltng", 10, "Charged Bolt", 7, 60, 0, 13, 5, "Lightning", 7, 30, 0, 22, 10, , , , , , , , , , , , , , , , , , , , , , , , , 2, 2, 2, 1], - ["Barbarian", "1hs-Normal", 0, 24, 561, 5, 1, 28, 515, "mercX101", "mercX167", 9000, 120, 288, 14, 180, 7, 101, 15, 63, 10, 150, 17, 1, 16, 20, 6, 56, 7, "swor", , , 50, "Bash", 5, 15, 0, 4, 10, "Stun", 5, 15, 0, 3, 8, , , , , , , , , , , , , , , , , , , , , , , , , 0, 0, 0, 0], - ["Barbarian", "1hs-Normal", 0, 24, 561, 5, 1, 42, 515, "mercX101", "mercX167", 9000, 120, 484, 21, 278, 20, 128, 15, 81, 10, 388, 27, 1, 27, 31, 8, 81, 7, "swor", , , 50, "Bash", 5, 50, 0, 9, 10, "Stun", 5, 50, 0, 7, 8, , , , , , , , , , , , , , , , , , , , , , , , , 0, 0, 0, 0], - ["Barbarian", "1hs-Normal", 0, 24, 561, 5, 1, 75, 515, "mercX101", "mercX167", 9000, 120, 1177, 35, 938, 40, 190, 15, 123, 10, 1279, 37, 1, 60, 64, 8, 139, 4, "swor", , , 50, "Bash", 5, 75, 0, 20, 0, "Stun", 5, 75, 0, 16, 0, , , , , , , , , , , , , , , , , , , , , , , , , 0, 0, 0, 0], - ["Barbarian", "1hs-Normal", 0, 25, 561, 5, 1, 28, 515, "mercX101", "mercX167", 9000, 120, 288, 14, 180, 7, 101, 15, 63, 10, 150, 17, 1, 16, 20, 6, 56, 7, "swor", , , 50, "Bash", 5, 15, 0, 4, 10, "Stun", 5, 15, 0, 3, 8, , , , , , , , , , , , , , , , , , , , , , , , , 0, 0, 0, 0], - ["Barbarian", "1hs-Normal", 0, 25, 561, 5, 1, 42, 515, "mercX101", "mercX167", 9000, 120, 484, 21, 278, 20, 128, 15, 81, 10, 388, 27, 1, 27, 31, 8, 81, 7, "swor", , , 50, "Bash", 5, 50, 0, 9, 10, "Stun", 5, 50, 0, 7, 8, , , , , , , , , , , , , , , , , , , , , , , , , 0, 0, 0, 0], - ["Barbarian", "1hs-Normal", 0, 25, 561, 5, 1, 75, 515, "mercX101", "mercX167", 9000, 120, 1177, 35, 938, 40, 190, 15, 123, 10, 1279, 37, 1, 60, 64, 8, 139, 4, "swor", , , 50, "Bash", 5, 75, 0, 20, 0, "Stun", 5, 75, 0, 16, 0, , , , , , , , , , , , , , , , , , , , , , , , , 0, 0, 0, 0], - ["Barbarian", "1hs-Nightmare", 0, 26, 561, 5, 2, 42, 515, "mercX101", "mercX167", 18000, 130, 436, 21, 250, 20, 125, 15, 76, 10, 349, 27, 4, 25, 29, 8, 78, 7, "swor", , , 50, "Bash", 5, 50, 0, 8, 10, "Stun", 5, 50, 0, 6, 8, , , , , , , , , , , , , , , , , , , , , , , , , 0, 0, 0, 0], - ["Barbarian", "1hs-Nightmare", 0, 26, 561, 5, 2, 75, 515, "mercX101", "mercX167", 18000, 130, 1129, 35, 910, 40, 187, 15, 118, 10, 1240, 37, 4, 58, 62, 8, 136, 4, "swor", , , 50, "Bash", 5, 75, 0, 19, 0, "Stun", 5, 75, 0, 15, 0, , , , , , , , , , , , , , , , , , , , , , , , , 0, 0, 0, 0], - ["Barbarian", "1hs-Nightmare", 0, 27, 561, 5, 2, 42, 515, "mercX101", "mercX167", 18000, 130, 436, 21, 250, 20, 125, 15, 76, 10, 349, 27, 4, 25, 29, 8, 78, 7, "swor", , , 50, "Bash", 5, 50, 0, 8, 10, "Stun", 5, 50, 0, 6, 8, , , , , , , , , , , , , , , , , , , , , , , , , 0, 0, 0, 0], - ["Barbarian", "1hs-Nightmare", 0, 27, 561, 5, 2, 75, 515, "mercX101", "mercX167", 18000, 130, 1129, 35, 910, 40, 187, 15, 118, 10, 1240, 37, 4, 58, 62, 8, 136, 4, "swor", , , 50, "Bash", 5, 75, 0, 19, 0, "Stun", 5, 75, 0, 15, 0, , , , , , , , , , , , , , , , , , , , , , , , , 0, 0, 0, 0], - ["Barbarian", "1hs-Hell", 0, 28, 561, 5, 3, 75, 515, "mercX101", "mercX167", 32000, 140, 1016, 35, 819, 40, 184, 15, 113, 10, 1116, 37, 6, 56, 60, 8, 133, 4, "swor", , , 50, "Bash", 5, 70, 0, 18, 0, "Stun", 5, 70, 0, 14, 0, , , , , , , , , , , , , , , , , , , , , , , , , 0, 0, 0, 0], - ["Barbarian", "1hs-Hell", 0, 29, 561, 5, 3, 75, 515, "mercX101", "mercX167", 32000, 140, 1016, 35, 819, 40, 184, 15, 113, 10, 1116, 37, 6, 56, 60, 8, 133, 4, "swor", , , 50, "Bash", 5, 70, 0, 18, 0, "Stun", 5, 70, 0, 14, 0, , , , , , , , , , , , , , , , , , , , , , , , , 0, 0, 0, 0], - ["Barbarian", "1hs-Normal", 100, 24, 561, 5, 1, 28, 515, "mercX101", "mercX167", 9000, 120, 288, 18, 180, 10, 101, 15, 63, 10, 150, 20, 1, 16, 20, 6, 56, 7, "swor", , , 50, "Bash", 5, 15, 0, 4, 10, "Stun", 5, 15, 0, 3, 8, , , , , , , , , , , , , , , , , , , , , , , , , 0, 0, 0, 0], - ["Barbarian", "1hs-Normal", 100, 24, 561, 5, 1, 58, 515, "mercX101", "mercX167", 9000, 120, 828, 27, 480, 35, 158, 15, 101, 10, 750, 35, 1, 39, 43, 8, 109, 7, "swor", , , 50, "Bash", 5, 50, 0, 14, 10, "Stun", 5, 50, 0, 11, 8, , , , , , , , , , , , , , , , , , , , , , , , , 0, 0, 0, 0], - ["Barbarian", "1hs-Normal", 100, 24, 561, 5, 1, 80, 515, "mercX101", "mercX167", 9000, 120, 1422, 45, 1250, 50, 200, 15, 129, 10, 1520, 45, 1, 61, 65, 8, 148, 4, "swor", , , 50, "Bash", 5, 75, 0, 21, 0, "Stun", 5, 75, 0, 17, 0, , , , , , , , , , , , , , , , , , , , , , , , , 0, 0, 0, 0], - ["Barbarian", "1hs-Normal", 100, 25, 561, 5, 1, 28, 515, "mercX101", "mercX167", 9000, 120, 288, 18, 180, 10, 101, 15, 63, 10, 150, 20, 1, 16, 20, 6, 56, 7, "swor", , , 50, "Bash", 5, 15, 0, 4, 10, "Stun", 5, 15, 0, 3, 8, , , , , , , , , , , , , , , , , , , , , , , , , 0, 0, 0, 0], - ["Barbarian", "1hs-Normal", 100, 25, 561, 5, 1, 58, 515, "mercX101", "mercX167", 9000, 120, 828, 27, 480, 35, 158, 15, 101, 10, 750, 35, 1, 39, 43, 8, 109, 7, "swor", , , 50, "Bash", 5, 50, 0, 14, 10, "Stun", 5, 50, 0, 11, 8, , , , , , , , , , , , , , , , , , , , , , , , , 0, 0, 0, 0], - ["Barbarian", "1hs-Normal", 100, 25, 561, 5, 1, 80, 515, "mercX101", "mercX167", 9000, 120, 1422, 45, 1250, 50, 200, 15, 129, 10, 1520, 45, 1, 61, 65, 8, 148, 4, "swor", , , 50, "Bash", 5, 75, 0, 21, 0, "Stun", 5, 75, 0, 17, 0, , , , , , , , , , , , , , , , , , , , , , , , , 0, 0, 0, 0], - ["Barbarian", "1hs-Nightmare", 100, 26, 561, 5, 2, 58, 515, "mercX101", "mercX167", 18000, 130, 745, 27, 432, 35, 155, 15, 96, 10, 675, 35, 4, 37, 41, 8, 106, 7, "swor", , , 50, "Bash", 5, 50, 0, 13, 10, "Stun", 5, 50, 0, 10, 8, , , , , , , , , , , , , , , , , , , , , , , , , 0, 0, 0, 0], - ["Barbarian", "1hs-Nightmare", 100, 26, 561, 5, 2, 80, 515, "mercX101", "mercX167", 18000, 130, 1339, 45, 1202, 50, 197, 15, 124, 10, 1445, 45, 4, 59, 63, 8, 145, 4, "swor", , , 50, "Bash", 5, 75, 0, 20, 0, "Stun", 5, 75, 0, 16, 0, , , , , , , , , , , , , , , , , , , , , , , , , 0, 0, 0, 0], - ["Barbarian", "1hs-Nightmare", 100, 27, 561, 5, 2, 58, 515, "mercX101", "mercX167", 18000, 130, 745, 27, 432, 35, 155, 15, 96, 10, 675, 35, 4, 37, 41, 8, 106, 7, "swor", , , 50, "Bash", 5, 50, 0, 13, 10, "Stun", 5, 50, 0, 10, 8, , , , , , , , , , , , , , , , , , , , , , , , , 0, 0, 0, 0], - ["Barbarian", "1hs-Nightmare", 100, 27, 561, 5, 2, 80, 515, "mercX101", "mercX167", 18000, 130, 1339, 45, 1202, 50, 197, 15, 124, 10, 1445, 45, 4, 59, 63, 8, 145, 4, "swor", , , 50, "Bash", 5, 75, 0, 20, 0, "Stun", 5, 75, 0, 16, 0, , , , , , , , , , , , , , , , , , , , , , , , , 0, 0, 0, 0], - ["Barbarian", "1hs-Hell", 100, 28, 561, 5, 3, 80, 515, "mercX101", "mercX167", 32000, 140, 1205, 45, 1082, 50, 194, 15, 119, 10, 1301, 45, 6, 57, 61, 8, 142, 4, "swor", , , 50, "Bash", 5, 70, 0, 19, 0, "Stun", 5, 70, 0, 15, 0, , , , , , , , , , , , , , , , , , , , , , , , , 0, 0, 0, 0], - ["Barbarian", "1hs-Hell", 100, 29, 561, 5, 3, 80, 515, "mercX101", "mercX167", 32000, 140, 1205, 45, 1082, 50, 194, 15, 119, 10, 1301, 45, 6, 57, 61, 8, 142, 4, "swor", , , 50, "Bash", 5, 70, 0, 19, 0, "Stun", 5, 70, 0, 15, 0, , , , , , , , , , , , , , , , , , , , , , , , , 0, 0, 0, 0] - ]; + } else if (name >= 1019 && name <= 1039) { + return 2; + } else if (name >= 1040 && name <= 1059) { + return 3; + } else if (name >= 10835 && name <= 10901) { + return 5; + } else { + return false; + } + }; + const validMercTable = getTable(); + let newseed = new Rnd(seed); + newseed.roll(); + let index = (newseed.get()) % validMercTable.length; + newseed.roll(); + this.level = (newseed.get()) % 5 + me.charlvl - 5; + let lvl = this.level; + let difference = this.level - getMercInfo(index, 7); + this.hireling = getMercInfo(index, 0); + this.typeid = getMercInfo(index, 3); + this.subtype = getMercInfo(index, 1); + this.skills = []; + if (getMercInfo(index, 33) !== undefined) { + this.skills.push({ + name: getMercInfo(index, 33), + lvl: Math.floor(lvl * 10 / 32) // experimental + }); + } + if (getMercInfo(index, 39) !== undefined) { + this.skills.push({ + name: getMercInfo(index, 39), + lvl: Math.floor(lvl * 10 / 32) // experimental + }); + } + if (getMercInfo(index, 45) !== undefined) { + this.skills.push({ + name: getMercInfo(index, 45), + lvl: Math.floor(lvl * 10 / 32) // experimental + }); + } + this.cost = getMercInfo(index, 11) * (15 * difference + 100) / 100; + if (this.cost < getMercInfo(index, 11)) { + this.cost = getMercInfo(index, 11); + } + this.cost = Math.floor(this.cost); + this.hp = getMercInfo(index, 13) + getMercInfo(index, 14) * difference; + if (this.hp < 40) { + this.hp = 40; + } + this.defense = getMercInfo(index, 15) + getMercInfo(index, 16) * difference; + if (this.defense < 0) { + this.defense = 0; + } + this.strength = getMercInfo(index, 17) + getMercInfo(index, 18) * difference; + if (this.strength < 10) { + this.strength = 10; + } + this.dexterity = getMercInfo(index, 19) + getMercInfo(index, 20) * difference; + if (this.dexterity < 10) { + this.dexterity = 10; + } + this.experience = this.level * this.level * (this.level + 1) * getMercInfo(index, 12); + if (this.experience < 0) { + this.experience = 0; + } + this.attackrating = getMercInfo(index, 21) + getMercInfo(index, 22) * difference; + if (this.attackrating < 1) { + this.attackrating = 1; + } + this.dmg_min = getMercInfo(index, 24) + getMercInfo(index, 26) * difference; + if (this.dmg_min < 0) { + this.dmg_min = 0; + } + this.dmg_max = getMercInfo(index, 25) + getMercInfo(index, 26) * difference; + if (this.dmg_max < 1) { + this.dmg_max = 1; + } + this.resists = getMercInfo(index, 27) + getMercInfo(index, 28) * difference; + if (this.resists < 1) { + this.resists = 1; + } + } + Merc.prototype.hire = function () { + let npc = getInteractedNPC(); + if (!npc) throw new Error("To buy merc you need to interact with npc first"); + if (typeof this.cost !== "number") throw new Error("Invalid cost"); + if (me.gold < this.cost) throw new Error("Merc is too expensive to buy."); + let before = me.gold; + while (before === me.gold) { + sendPacket(1, sdk.packets.send.HireMerc, 4, npc.gid, 4, this.id); + delay((me.ping || 1) * 5); + } + return true; + }; + return Merc; + }()); + exports.Merc = Merc; + let Mercs = []; + exports.default = Mercs; + function mercPacket(pByte) { + switch (pByte[0]) { + case 0x4f: + // Clear mercenary list + Mercs.splice(0, Mercs.length); + break; + case 0x4e: + let name = ((pByte[1]) | (pByte[2] << 8)), seed = ((pByte[3]) | (pByte[4] << 8) | (pByte[5] << 16) | (pByte[6] << 24)) >>> 0; + Mercs.push(new Merc(name, seed)); + break; + } + } + exports.mercPacket = mercPacket; + //addEventListener("gamepacket", mercPacket); + //removeEventListener("gamepacket", mercPacket); + const MercTable = [ + // 0.Hireling , 1.SubType , 2.Version , 3.Id , 4.Class , 5.Act , 6.Diff , 7.Level , 8.Seller , 9.NameFirst , 10.NameLast , 11.Gold , 12.Exp/Lvl , 13.HP , 14.HP/Lvl , 15.Def , 16.Def/Lvl , Str , Str/Lvl , Dex , Dex/Lvl , AR , AR/Lvl , Share , Dmg-Min , Dmg-Max , Dmg/Lvl , Resist , Resist/Lvl , WType1 , WType2 , HireDesc , DefaultChance , Skill1 , Mode1 , Chance1 , ChancePerLvl1 , Level1 , LvlPerLvl1 , Skill2 , Mode2 , Chance2 , ChancePerLvl2 , Level2 , LvlPerLvl2 , Skill3 , Mode3 , Chance3 , ChancePerLvl3 , Level3 , LvlPerLvl3 , Skill4 , Mode4 , Chance4 , ChancePerLvl4 , Level4 , LvlPerLvl4 , Skill5 , Mode5 , Chance5 , ChancePerLvl5 , Level5 , LvlPerLvl5 , Skill6 , Mode6 , Chance6 , ChancePerLvl6 , Level6 , LvlPerLvl6 , Head , Torso , Weapon , Shield + ["Rogue Scout", "Fire - Normal", 0, 0, 271, 1, 1, 3, 150, "merc01", "merc41", 100, 100, 45, 8, 15, 6, 35, 10, 45, 16, 10, 10, , 1, 3, 2, 0, 8, "bow", , "farw", 75, "Inner Sight", 4, 10, 0, 1, 10, "Fire Arrow", 4, 25, 8, 1, 10, , , , , , , , , , , , , , , , , , , , , , , , , 2, 1, 2, 0], + ["Rogue Scout", "Fire - Normal", 0, 0, 271, 1, 1, 25, 150, "merc01", "merc41", 100, 100, 221, 13, 147, 12, 63, 10, 89, 16, 230, 20, 1, 7, 9, 4, 44, 7, "bow", , "farw", 75, "Inner Sight", 4, 10, 0, 8, 10, "Fire Arrow", 4, 69, 0, 8, 10, , , , , , , , , , , , , , , , , , , , , , , , , 2, 1, 2, 0], + ["Rogue Scout", "Fire - Normal", 0, 0, 271, 1, 1, 49, 150, "merc01", "merc41", 100, 100, 533, 25, 435, 18, 93, 10, 137, 16, 710, 30, 2, 19, 21, 6, 86, 5, "bow", , "farw", 75, "Inner Sight", 4, 10, 0, 16, 10, "Fire Arrow", 4, 69, 0, 16, 10, , , , , , , , , , , , , , , , , , , , , , , , , 2, 1, 2, 0], + ["Rogue Scout", "Ice - Normal", 0, 1, 271, 1, 1, 3, 150, "merc01", "merc41", 150, 105, 45, 8, 15, 6, 35, 10, 45, 16, 10, 10, , 1, 3, 2, 0, 8, "bow", , "carw", 75, "Inner Sight", 4, 10, 0, 1, 10, "Cold Arrow", 4, 25, 2, 1, 10, , , , , , , , , , , , , , , , , , , , , , , , , 2, 1, 2, 0], + ["Rogue Scout", "Ice - Normal", 0, 1, 271, 1, 1, 25, 150, "merc01", "merc41", 150, 105, 221, 12, 147, 12, 63, 10, 89, 16, 230, 20, 1, 7, 9, 4, 44, 7, "bow", , "carw", 75, "Inner Sight", 4, 10, 0, 8, 10, "Cold Arrow", 4, 36, 0, 8, 10, , , , , , , , , , , , , , , , , , , , , , , , , 2, 1, 2, 0], + ["Rogue Scout", "Ice - Normal", 0, 1, 271, 1, 1, 49, 150, "merc01", "merc41", 150, 105, 509, 16, 435, 18, 93, 10, 137, 16, 710, 30, 2, 19, 21, 6, 86, 5, "bow", , "carw", 75, "Inner Sight", 4, 10, 0, 16, 10, "Cold Arrow", 4, 36, 0, 16, 10, , , , , , , , , , , , , , , , , , , , , , , , , 2, 1, 2, 0], + ["Rogue Scout", "Fire - Nightmare", 0, 2, 271, 1, 2, 25, 150, "merc01", "merc41", 6000, 110, 199, 12, 132, 12, 60, 10, 84, 16, 207, 20, 1, 6, 8, 4, 41, 7, "bow", , "farw", 75, "Inner Sight", 4, 10, 0, 7, 10, "Fire Arrow", 4, 69, 0, 7, 10, , , , , , , , , , , , , , , , , , , , , , , , , 2, 1, 2, 0], + ["Rogue Scout", "Fire - Nightmare", 0, 2, 271, 1, 2, 49, 150, "merc01", "merc41", 6000, 110, 487, 16, 420, 18, 90, 10, 132, 16, 687, 30, 2, 18, 20, 6, 83, 5, "bow", , "farw", 75, "Inner Sight", 4, 10, 0, 15, 10, "Fire Arrow", 4, 69, 0, 15, 10, , , , , , , , , , , , , , , , , , , , , , , , , 2, 1, 2, 0], + ["Rogue Scout", "Ice - Nightmare", 0, 3, 271, 1, 2, 25, 150, "merc01", "merc41", 7500, 115, 199, 12, 132, 12, 60, 10, 84, 16, 207, 20, 1, 6, 8, 4, 41, 7, "bow", , "carw", 75, "Inner Sight", 4, 10, 0, 7, 10, "Cold Arrow", 4, 69, 0, 7, 10, , , , , , , , , , , , , , , , , , , , , , , , , 2, 1, 2, 0], + ["Rogue Scout", "Ice - Nightmare", 0, 3, 271, 1, 2, 49, 150, "merc01", "merc41", 7500, 115, 487, 16, 420, 18, 90, 10, 132, 16, 687, 30, 2, 18, 20, 6, 83, 5, "bow", , "carw", 75, "Inner Sight", 4, 10, 0, 15, 10, "Cold Arrow", 4, 69, 0, 15, 10, , , , , , , , , , , , , , , , , , , , , , , , , 2, 1, 2, 0], + ["Rogue Scout", "Fire - Hell", 0, 4, 271, 1, 3, 49, 150, "merc01", "merc41", 12500, 120, 438, 16, 378, 18, 87, 10, 127, 16, 618, 30, 2, 17, 19, 6, 80, 5, "bow", , "farw", 75, "Inner Sight", 4, 10, 0, 14, 10, "Fire Arrow", 4, 69, 0, 14, 10, , , , , , , , , , , , , , , , , , , , , , , , , 2, 1, 2, 0], + ["Rogue Scout", "Ice - Hell", 0, 5, 271, 1, 3, 49, 150, "merc01", "merc41", 14000, 125, 438, 16, 378, 18, 87, 10, 127, 16, 618, 30, 2, 17, 19, 6, 80, 5, "bow", , "carw", 75, "Inner Sight", 4, 10, 0, 14, 10, "Cold Arrow", 4, 69, 0, 14, 10, , , , , , , , , , , , , , , , , , , , , , , , , 2, 1, 2, 0], + ["Rogue Scout", "Fire - Normal", 100, 0, 271, 1, 1, 3, 150, "merc01", "merc41", 100, 100, 45, 9, 15, 8, 35, 10, 45, 16, 10, 12, , 1, 3, 2, 0, 8, "bow", , "farw", 75, "Inner Sight", 4, 10, 0, 1, 10, "Fire Arrow", 4, 25, 5, 1, 10, , , , , , , , , , , , , , , , , , , , , , , , , 2, 1, 2, 0], + ["Rogue Scout", "Fire - Normal", 100, 0, 271, 1, 1, 36, 150, "merc01", "merc41", 100, 100, 342, 18, 279, 15, 77, 10, 111, 16, 406, 24, 1, 9, 11, 4, 66, 7, "bow", , "farw", 75, "Inner Sight", 4, 10, 0, 12, 10, "Fire Arrow", 4, 67, 0, 12, 10, , , , , , , , , , , , , , , , , , , , , , , , , 2, 1, 2, 0], + ["Rogue Scout", "Fire - Normal", 100, 0, 271, 1, 1, 67, 150, "merc01", "merc41", 100, 100, 900, 30, 744, 22, 116, 10, 173, 16, 1150, 36, 2, 25, 27, 6, 121, 5, "bow", , "farw", 75, "Inner Sight", 4, 10, 0, 22, 10, "Fire Arrow", 4, 67, 0, 22, 10, , , , , , , , , , , , , , , , , , , , , , , , , 2, 1, 2, 0], + ["Rogue Scout", "Ice - Normal", 100, 1, 271, 1, 1, 3, 150, "merc01", "merc41", 150, 105, 45, 9, 15, 8, 35, 10, 45, 16, 10, 12, , 1, 3, 2, 0, 8, "bow", , "carw", 75, "Inner Sight", 4, 10, 0, 1, 10, "Cold Arrow", 4, 25, 5, 1, 10, , , , , , , , , , , , , , , , , , , , , , , , , 2, 1, 2, 0], + ["Rogue Scout", "Ice - Normal", 100, 1, 271, 1, 1, 36, 150, "merc01", "merc41", 150, 105, 342, 18, 279, 15, 77, 10, 111, 16, 406, 24, 1, 9, 11, 4, 66, 7, "bow", , "carw", 75, "Inner Sight", 4, 10, 0, 12, 10, "Cold Arrow", 4, 67, 0, 12, 10, , , , , , , , , , , , , , , , , , , , , , , , , 2, 1, 2, 0], + ["Rogue Scout", "Ice - Normal", 100, 1, 271, 1, 1, 67, 150, "merc01", "merc41", 150, 105, 900, 30, 744, 22, 116, 10, 173, 16, 1150, 36, 2, 25, 27, 6, 121, 5, "bow", , "carw", 75, "Inner Sight", 4, 10, 0, 22, 10, "Cold Arrow", 4, 67, 0, 22, 10, , , , , , , , , , , , , , , , , , , , , , , , , 2, 1, 2, 0], + ["Rogue Scout", "Fire - Nightmare", 100, 2, 271, 1, 2, 36, 150, "merc01", "merc41", 6000, 110, 308, 18, 251, 15, 74, 10, 106, 16, 365, 24, 1, 8, 10, 4, 63, 7, "bow", , "farw", 75, "Inner Sight", 4, 10, 0, 11, 10, "Fire Arrow", 4, 69, 0, 11, 10, , , , , , , , , , , , , , , , , , , , , , , , , 2, 1, 2, 0], + ["Rogue Scout", "Fire - Nightmare", 100, 2, 271, 1, 2, 67, 150, "merc01", "merc41", 6000, 110, 866, 30, 716, 22, 113, 10, 168, 16, 1109, 36, 2, 24, 26, 6, 118, 5, "bow", , "farw", 75, "Inner Sight", 4, 10, 0, 21, 10, "Fire Arrow", 4, 69, 0, 21, 10, , , , , , , , , , , , , , , , , , , , , , , , , 2, 1, 2, 0], + ["Rogue Scout", "Ice - Nightmare", 100, 3, 271, 1, 2, 36, 150, "merc01", "merc41", 7500, 115, 308, 18, 251, 15, 74, 10, 106, 16, 365, 24, 1, 8, 10, 4, 63, 7, "bow", , "carw", 75, "Inner Sight", 4, 10, 0, 11, 10, "Cold Arrow", 4, 69, 0, 11, 10, , , , , , , , , , , , , , , , , , , , , , , , , 2, 1, 2, 0], + ["Rogue Scout", "Ice - Nightmare", 100, 3, 271, 1, 2, 67, 150, "merc01", "merc41", 7500, 115, 866, 30, 716, 22, 113, 10, 168, 16, 1109, 36, 2, 24, 26, 6, 118, 5, "bow", , "carw", 75, "Inner Sight", 4, 10, 0, 21, 10, "Cold Arrow", 4, 69, 0, 21, 10, , , , , , , , , , , , , , , , , , , , , , , , , 2, 1, 2, 0], + ["Rogue Scout", "Fire - Hell", 100, 4, 271, 1, 3, 67, 150, "merc01", "merc41", 12500, 120, 779, 30, 644, 22, 110, 10, 163, 16, 998, 36, 2, 23, 25, 6, 115, 5, "bow", , "farw", 75, "Inner Sight", 4, 10, 0, 20, 10, "Fire Arrow", 4, 69, 0, 20, 10, , , , , , , , , , , , , , , , , , , , , , , , , 2, 1, 2, 0], + ["Rogue Scout", "Ice - Hell", 100, 5, 271, 1, 3, 67, 150, "merc01", "merc41", 14000, 125, 779, 30, 644, 22, 110, 10, 163, 16, 998, 36, 2, 23, 25, 6, 115, 5, "bow", , "carw", 75, "Inner Sight", 4, 10, 0, 20, 10, "Cold Arrow", 4, 69, 0, 20, 10, , , , , , , , , , , , , , , , , , , , , , , , , 2, 1, 2, 0], + ["Desert Mercenary", "Comb-Normal", 0, 6, 338, 2, 1, 9, 198, "merca201", "merca221", 350, 110, 120, 10, 45, 9, 57, 14, 40, 12, 20, 10, 1, 7, 14, 4, 18, 8, "pole", "spea", "comb", 30, "Jab", 14, 70, 4, 3, 10, "Prayer", 1, 10, 0, 3, 10, , , , , , , , , , , , , , , , , , , , , , , , , 2, 2, 2, 0], + ["Desert Mercenary", "Comb-Normal", 0, 6, 338, 2, 1, 31, 198, "merca201", "merca221", 350, 110, 340, 20, 243, 16, 96, 14, 73, 12, 240, 20, 3, 18, 25, 6, 62, 7, "pole", "spea", "comb", 30, "Jab", 14, 92, 4, 10, 10, "Prayer", 1, 10, 0, 10, 10, , , , , , , , , , , , , , , , , , , , , , , , , 2, 2, 2, 0], + ["Desert Mercenary", "Comb-Normal", 0, 6, 338, 2, 1, 55, 198, "merca201", "merca221", 350, 110, 820, 35, 627, 24, 138, 14, 109, 12, 720, 30, 4, 36, 43, 8, 104, 4, "pole", "spea", "comb", 30, "Jab", 14, 116, 4, 18, 10, "Prayer", 1, 10, 0, 18, 0, , , , , , , , , , , , , , , , , , , , , , , , , 2, 2, 2, 0], + ["Desert Mercenary", "Def-Normal", 0, 7, 338, 2, 1, 9, 198, "merca201", "merca221", 350, 110, 120, 10, 45, 9, 57, 14, 40, 12, 20, 10, 1, 7, 14, 4, 18, 8, "pole", "spea", "def", 30, "Jab", 14, 70, 4, 3, 10, "Defiance", 1, 10, 0, 3, 10, , , , , , , , , , , , , , , , , , , , , , , , , 2, 2, 2, 0], + ["Desert Mercenary", "Def-Normal", 0, 7, 338, 2, 1, 31, 198, "merca201", "merca221", 350, 110, 340, 20, 243, 16, 96, 14, 73, 12, 240, 20, 3, 18, 25, 6, 62, 7, "pole", "spea", "def", 30, "Jab", 14, 92, 4, 10, 10, "Defiance", 1, 10, 0, 10, 10, , , , , , , , , , , , , , , , , , , , , , , , , 2, 2, 2, 0], + ["Desert Mercenary", "Def-Normal", 0, 7, 338, 2, 1, 55, 198, "merca201", "merca221", 350, 110, 820, 35, 627, 24, 138, 14, 109, 12, 720, 30, 4, 36, 43, 8, 104, 4, "pole", "spea", "def", 30, "Jab", 14, 116, 4, 18, 0, "Defiance", 1, 10, 0, 18, 0, , , , , , , , , , , , , , , , , , , , , , , , , 2, 2, 2, 0], + ["Desert Mercenary", "Off-Normal", 0, 8, 338, 2, 1, 9, 198, "merca201", "merca221", 350, 110, 120, 10, 45, 9, 57, 14, 40, 12, 20, 10, 1, 7, 14, 4, 18, 8, "pole", "spea", "off", 30, "Jab", 14, 70, 4, 3, 10, "Blessed Aim", 1, 10, 0, 3, 10, , , , , , , , , , , , , , , , , , , , , , , , , 2, 2, 2, 0], + ["Desert Mercenary", "Off-Normal", 0, 8, 338, 2, 1, 31, 198, "merca201", "merca221", 350, 110, 340, 20, 243, 16, 96, 14, 73, 12, 240, 20, 3, 18, 25, 6, 62, 7, "pole", "spea", "off", 30, "Jab", 14, 92, 4, 10, 10, "Blessed Aim", 1, 10, 0, 10, 10, , , , , , , , , , , , , , , , , , , , , , , , , 2, 2, 2, 0], + ["Desert Mercenary", "Off-Normal", 0, 8, 338, 2, 1, 55, 198, "merca201", "merca221", 350, 110, 820, 35, 627, 24, 138, 14, 109, 12, 720, 30, 4, 36, 43, 8, 104, 4, "pole", "spea", "off", 30, "Jab", 14, 116, 4, 18, 0, "Blessed Aim", 1, 10, 0, 18, 0, , , , , , , , , , , , , , , , , , , , , , , , , 2, 2, 2, 0], + ["Desert Mercenary", "Comb-Nightmare", 0, 9, 338, 2, 2, 31, 198, "merca201", "merca221", 7900, 120, 306, 20, 219, 16, 92, 14, 69, 12, 216, 20, 3, 16, 23, 6, 59, 7, "pole", "spea", "comb", 30, "Jab", 14, 120, 4, 9, 10, "Thorns", 1, 10, 0, 5, 10, , , , , , , , , , , , , , , , , , , , , , , , , 2, 2, 2, 0], + ["Desert Mercenary", "Comb-Nightmare", 0, 9, 338, 2, 2, 55, 198, "merca201", "merca221", 7900, 120, 786, 35, 603, 24, 134, 14, 105, 12, 696, 30, 4, 34, 41, 8, 101, 4, "pole", "spea", "comb", 30, "Jab", 14, 144, 4, 17, 10, "Thorns", 1, 10, 0, 13, 0, , , , , , , , , , , , , , , , , , , , , , , , , 2, 2, 2, 0], + ["Desert Mercenary", "Def-Nightmare", 0, 10, 338, 2, 2, 31, 198, "merca201", "merca221", 7900, 120, 306, 20, 219, 16, 92, 14, 69, 12, 216, 20, 3, 16, 23, 6, 59, 7, "pole", "spea", "def", 30, "Jab", 14, 120, 4, 9, 10, "Holy Freeze", 1, 10, 0, 6, 10, , , , , , , , , , , , , , , , , , , , , , , , , 2, 2, 2, 0], + ["Desert Mercenary", "Def-Nightmare", 0, 10, 338, 2, 2, 55, 198, "merca201", "merca221", 7900, 120, 786, 35, 603, 24, 134, 14, 105, 12, 696, 30, 4, 34, 41, 8, 101, 4, "pole", "spea", "def", 30, "Jab", 14, 144, 4, 17, 0, "Holy Freeze", 1, 10, 0, 14, 0, , , , , , , , , , , , , , , , , , , , , , , , , 2, 2, 2, 0], + ["Desert Mercenary", "Off-Nightmare", 0, 11, 338, 2, 2, 31, 198, "merca201", "merca221", 7900, 120, 306, 20, 219, 16, 92, 14, 69, 12, 216, 20, 3, 16, 23, 6, 59, 7, "pole", "spea", "off", 30, "Jab", 14, 120, 4, 9, 10, "Might", 1, 10, 0, 7, 8, , , , , , , , , , , , , , , , , , , , , , , , , 2, 2, 2, 0], + ["Desert Mercenary", "Off-Nightmare", 0, 11, 338, 2, 2, 55, 198, "merca201", "merca221", 7900, 120, 786, 35, 603, 24, 134, 14, 105, 12, 696, 30, 4, 34, 41, 8, 101, 4, "pole", "spea", "off", 30, "Jab", 14, 144, 4, 17, 0, "Might", 1, 10, 0, 13, 8, , , , , , , , , , , , , , , , , , , , , , , , , 2, 2, 2, 0], + ["Desert Mercenary", "Comb-Hell", 0, 12, 338, 2, 3, 55, 198, "merca201", "merca221", 15000, 130, 707, 35, 543, 24, 130, 14, 101, 12, 626, 30, 4, 32, 39, 8, 98, 4, "pole", "spea", "comb", 30, "Jab", 14, 104, 4, 16, 10, "Prayer", 1, 10, 0, 16, 0, , , , , , , , , , , , , , , , , , , , , , , , , 2, 2, 2, 0], + ["Desert Mercenary", "Def-Hell", 0, 13, 338, 2, 3, 55, 198, "merca201", "merca221", 15000, 130, 707, 35, 543, 24, 130, 14, 101, 12, 626, 30, 4, 32, 39, 8, 98, 4, "pole", "spea", "def", 30, "Jab", 14, 104, 4, 16, 0, "Defiance", 1, 10, 0, 16, 0, , , , , , , , , , , , , , , , , , , , , , , , , 2, 2, 2, 0], + ["Desert Mercenary", "Off-Hell", 0, 14, 338, 2, 3, 55, 198, "merca201", "merca221", 15000, 130, 707, 35, 543, 24, 130, 14, 101, 12, 626, 30, 4, 32, 39, 8, 98, 4, "pole", "spea", "off", 30, "Jab", 14, 104, 4, 16, 0, "Blessed Aim", 1, 10, 0, 16, 0, , , , , , , , , , , , , , , , , , , , , , , , , 2, 2, 2, 0], + ["Desert Mercenary", "Comb-Normal", 100, 6, 338, 2, 1, 9, 198, "merca201", "merca221", 350, 110, 120, 15, 45, 11, 57, 14, 40, 12, 20, 12, 1, 7, 14, 4, 18, 8, "pole", "spea", "comb", 30, "Jab", 14, 70, 4, 3, 10, "Prayer", 1, 10, 0, 3, 7, , , , , , , , , , , , , , , , , , , , , , , , , 2, 2, 2, 0], + ["Desert Mercenary", "Comb-Normal", 100, 6, 338, 2, 1, 43, 198, "merca201", "merca221", 350, 110, 630, 25, 419, 19, 117, 14, 91, 12, 428, 24, 3, 24, 31, 6, 86, 7, "pole", "spea", "comb", 30, "Jab", 14, 104, 4, 14, 10, "Prayer", 1, 10, 0, 11, 7, , , , , , , , , , , , , , , , , , , , , , , , , 2, 2, 2, 0], + ["Desert Mercenary", "Comb-Normal", 100, 6, 338, 2, 1, 75, 198, "merca201", "merca221", 350, 110, 1430, 40, 1027, 28, 173, 14, 139, 12, 1196, 36, 4, 48, 55, 8, 142, 4, "pole", "spea", "comb", 30, "Jab", 14, 136, 4, 24, 10, "Prayer", 1, 10, 0, 18, 0, , , , , , , , , , , , , , , , , , , , , , , , , 2, 2, 2, 0], + ["Desert Mercenary", "Def-Normal", 100, 7, 338, 2, 1, 9, 198, "merca201", "merca221", 350, 110, 120, 15, 45, 11, 57, 14, 40, 12, 20, 12, 1, 7, 14, 4, 18, 8, "pole", "spea", "def", 30, "Jab", 14, 70, 4, 3, 10, "Defiance", 1, 10, 0, 3, 7, , , , , , , , , , , , , , , , , , , , , , , , , 2, 2, 2, 0], + ["Desert Mercenary", "Def-Normal", 100, 7, 338, 2, 1, 43, 198, "merca201", "merca221", 350, 110, 630, 25, 419, 19, 117, 14, 91, 12, 428, 24, 3, 24, 31, 6, 86, 7, "pole", "spea", "def", 30, "Jab", 14, 104, 4, 14, 10, "Defiance", 1, 10, 0, 11, 7, , , , , , , , , , , , , , , , , , , , , , , , , 2, 2, 2, 0], + ["Desert Mercenary", "Def-Normal", 100, 7, 338, 2, 1, 75, 198, "merca201", "merca221", 350, 110, 1430, 40, 1027, 28, 173, 14, 139, 12, 1196, 36, 4, 48, 55, 8, 142, 4, "pole", "spea", "def", 30, "Jab", 14, 136, 4, 24, 0, "Defiance", 1, 10, 0, 18, 0, , , , , , , , , , , , , , , , , , , , , , , , , 2, 2, 2, 0], + ["Desert Mercenary", "Off-Normal", 100, 8, 338, 2, 1, 9, 198, "merca201", "merca221", 350, 110, 120, 15, 45, 11, 57, 14, 40, 12, 20, 12, 1, 7, 14, 4, 18, 8, "pole", "spea", "off", 30, "Jab", 14, 70, 4, 3, 10, "Blessed Aim", 1, 10, 0, 3, 7, , , , , , , , , , , , , , , , , , , , , , , , , 2, 2, 2, 0], + ["Desert Mercenary", "Off-Normal", 100, 8, 338, 2, 1, 43, 198, "merca201", "merca221", 350, 110, 630, 25, 419, 19, 117, 14, 91, 12, 428, 24, 3, 24, 31, 6, 86, 7, "pole", "spea", "off", 30, "Jab", 14, 104, 4, 14, 10, "Blessed Aim", 1, 10, 0, 11, 7, , , , , , , , , , , , , , , , , , , , , , , , , 2, 2, 2, 0], + ["Desert Mercenary", "Off-Normal", 100, 8, 338, 2, 1, 75, 198, "merca201", "merca221", 350, 110, 1430, 40, 1027, 28, 173, 14, 139, 12, 1196, 36, 4, 48, 55, 8, 142, 4, "pole", "spea", "off", 30, "Jab", 14, 136, 4, 24, 0, "Blessed Aim", 1, 10, 0, 18, 0, , , , , , , , , , , , , , , , , , , , , , , , , 2, 2, 2, 0], + ["Desert Mercenary", "Comb-Nightmare", 100, 9, 338, 2, 2, 43, 198, "merca201", "merca221", 7900, 120, 567, 25, 377, 19, 113, 14, 87, 12, 385, 24, 3, 22, 29, 6, 83, 7, "pole", "spea", "comb", 30, "Jab", 14, 120, 4, 13, 10, "Thorns", 1, 10, 0, 5, 10, , , , , , , , , , , , , , , , , , , , , , , , , 2, 2, 2, 0], + ["Desert Mercenary", "Comb-Nightmare", 100, 9, 338, 2, 2, 75, 198, "merca201", "merca221", 7900, 120, 1367, 40, 985, 28, 169, 14, 135, 12, 1153, 36, 4, 46, 53, 8, 139, 4, "pole", "spea", "comb", 30, "Jab", 14, 152, 4, 23, 10, "Thorns", 1, 10, 0, 15, 0, , , , , , , , , , , , , , , , , , , , , , , , , 2, 2, 2, 0], + ["Desert Mercenary", "Def-Nightmare", 100, 10, 338, 2, 2, 43, 198, "merca201", "merca221", 7900, 120, 567, 25, 377, 19, 113, 14, 87, 12, 385, 24, 3, 22, 29, 6, 83, 7, "pole", "spea", "def", 30, "Jab", 14, 120, 4, 13, 10, "Holy Freeze", 1, 10, 0, 6, 10, , , , , , , , , , , , , , , , , , , , , , , , , 2, 2, 2, 0], + ["Desert Mercenary", "Def-Nightmare", 100, 10, 338, 2, 2, 75, 198, "merca201", "merca221", 7900, 120, 1367, 40, 985, 28, 169, 14, 135, 12, 1153, 36, 4, 46, 53, 8, 139, 4, "pole", "spea", "def", 30, "Jab", 14, 152, 4, 23, 0, "Holy Freeze", 1, 10, 0, 16, 0, , , , , , , , , , , , , , , , , , , , , , , , , 2, 2, 2, 0], + ["Desert Mercenary", "Off-Nightmare", 100, 11, 338, 2, 2, 43, 198, "merca201", "merca221", 7900, 120, 567, 25, 377, 19, 113, 14, 87, 12, 385, 24, 3, 22, 29, 6, 83, 7, "pole", "spea", "off", 30, "Jab", 14, 120, 4, 13, 10, "Might", 1, 10, 0, 7, 8, , , , , , , , , , , , , , , , , , , , , , , , , 2, 2, 2, 0], + ["Desert Mercenary", "Off-Nightmare", 100, 11, 338, 2, 2, 75, 198, "merca201", "merca221", 7900, 120, 1367, 40, 985, 28, 169, 14, 135, 12, 1153, 36, 4, 46, 53, 8, 139, 4, "pole", "spea", "off", 30, "Jab", 14, 152, 4, 23, 0, "Might", 1, 10, 0, 15, 8, , , , , , , , , , , , , , , , , , , , , , , , , 2, 2, 2, 0], + ["Desert Mercenary", "Comb-Hell", 100, 12, 338, 2, 3, 75, 198, "merca201", "merca221", 15000, 130, 1230, 40, 887, 28, 165, 14, 131, 12, 1038, 36, 4, 44, 51, 8, 136, 4, "pole", "spea", "comb", 30, "Jab", 14, 104, 4, 22, 10, "Prayer", 1, 10, 0, 16, 0, , , , , , , , , , , , , , , , , , , , , , , , , 2, 2, 2, 0], + ["Desert Mercenary", "Def-Hell", 100, 13, 338, 2, 3, 75, 198, "merca201", "merca221", 15000, 130, 1230, 40, 887, 28, 165, 14, 131, 12, 1038, 36, 4, 44, 51, 8, 136, 4, "pole", "spea", "def", 30, "Jab", 14, 104, 4, 22, 0, "Defiance", 1, 10, 0, 16, 0, , , , , , , , , , , , , , , , , , , , , , , , , 2, 2, 2, 0], + ["Desert Mercenary", "Off-Hell", 100, 14, 338, 2, 3, 75, 198, "merca201", "merca221", 15000, 130, 1230, 40, 887, 28, 165, 14, 131, 12, 1038, 36, 4, 44, 51, 8, 136, 4, "pole", "spea", "off", 30, "Jab", 14, 104, 4, 22, 0, "Blessed Aim", 1, 10, 0, 16, 0, , , , , , , , , , , , , , , , , , , , , , , , , 2, 2, 2, 0], + ["Eastern Sorceror", "Fire-Normal", 0, 15, 359, 3, 1, 15, 252, "merca222", "merca241", 1000, 110, 160, 8, 80, 4, 49, 10, 40, 8, 15, 10, , 1, 7, 4, 25, 7, "swor", "shie", "fire", 10, "Inferno", 7, 60, 0, 6, 10, "Fire Ball", 7, 30, 0, 4, 10, , , , , , , , , , , , , , , , , , , , , , , , , 2, 2, 2, 1], + ["Eastern Sorceror", "Fire-Normal", 0, 15, 359, 3, 1, 37, 252, "merca222", "merca241", 1000, 110, 336, 14, 168, 10, 77, 10, 62, 8, 235, 20, 3, 12, 18, 4, 64, 7, "swor", "shie", "fire", 10, "Inferno", 7, 60, 0, 13, 10, "Fire Ball", 7, 30, 0, 11, 10, , , , , , , , , , , , , , , , , , , , , , , , , 2, 2, 2, 1], + ["Eastern Sorceror", "Fire-Normal", 0, 15, 359, 3, 1, 61, 252, "merca222", "merca241", 1000, 110, 672, 20, 408, 18, 107, 10, 86, 8, 715, 30, 5, 24, 30, 4, 106, 6, "swor", "shie", "fire", 10, "Inferno", 7, 60, 0, 21, 10, "Fire Ball", 7, 30, 0, 19, 10, , , , , , , , , , , , , , , , , , , , , , , , , 2, 2, 2, 1], + ["Eastern Sorceror", "Cold-Normal", 0, 16, 359, 3, 1, 15, 252, "merca222", "merca241", 1500, 120, 160, 8, 80, 4, 49, 10, 40, 8, 15, 10, , 1, 7, 4, 25, 7, "swor", "shie", "cold", 10, "Glacial Spike", 7, 60, 0, 1, 5, "Frozen Armor", 7, 1000, 0, 2, 10, "Ice Blast", 7, 240, 0, 6, 10, , , , , , , , , , , , , , , , , , , 2, 2, 2, 1], + ["Eastern Sorceror", "Cold-Normal", 0, 16, 359, 3, 1, 37, 252, "merca222", "merca241", 1500, 120, 336, 14, 168, 10, 77, 10, 62, 8, 235, 20, 3, 12, 18, 4, 64, 7, "swor", "shie", "cold", 10, "Glacial Spike", 7, 60, 0, 5, 5, "Frozen Armor", 7, 1000, 0, 9, 10, "Ice Blast", 7, 240, 0, 13, 10, , , , , , , , , , , , , , , , , , , 2, 2, 2, 1], + ["Eastern Sorceror", "Cold-Normal", 0, 16, 359, 3, 1, 61, 252, "merca222", "merca241", 1500, 120, 672, 20, 408, 16, 107, 10, 86, 8, 715, 30, 5, 24, 30, 4, 106, 4, "swor", "shie", "cold", 10, "Glacial Spike", 7, 60, 0, 9, 5, "Frozen Armor", 7, 1000, 0, 17, 10, "Ice Blast", 7, 240, 0, 21, 10, , , , , , , , , , , , , , , , , , , 2, 2, 2, 1], + ["Eastern Sorceror", "Ltng-Normal", 0, 17, 359, 3, 1, 15, 252, "merca222", "merca241", 1000, 110, 160, 8, 80, 4, 49, 10, 40, 8, 15, 10, , 1, 7, 4, 25, 7, "swor", "shie", "ltng", 10, "Charged Bolt", 7, 60, 0, 4, 5, "Lightning", 7, 30, 0, 3, 10, , , , , , , , , , , , , , , , , , , , , , , , , 2, 2, 2, 1], + ["Eastern Sorceror", "Ltng-Normal", 0, 17, 359, 3, 1, 37, 252, "merca222", "merca241", 1000, 110, 336, 14, 168, 10, 77, 10, 62, 8, 235, 20, 3, 12, 18, 4, 64, 7, "swor", "shie", "ltng", 10, "Charged Bolt", 7, 60, 0, 8, 5, "Lightning", 7, 30, 0, 10, 10, , , , , , , , , , , , , , , , , , , , , , , , , 2, 2, 2, 1], + ["Eastern Sorceror", "Ltng-Normal", 0, 17, 359, 3, 1, 61, 252, "merca222", "merca241", 1000, 110, 672, 20, 408, 16, 107, 10, 86, 8, 715, 30, 5, 24, 30, 4, 106, 4, "swor", "shie", "ltng", 10, "Charged Bolt", 7, 60, 0, 12, 5, "Lightning", 7, 30, 0, 18, 10, , , , , , , , , , , , , , , , , , , , , , , , , 2, 2, 2, 1], + ["Eastern Sorceror", "Fire-Nightmare", 0, 18, 359, 3, 2, 37, 252, "merca222", "merca241", 9500, 120, 302, 14, 151, 10, 73, 10, 58, 8, 212, 20, 3, 10, 16, 4, 61, 7, "swor", "shie", "fire", 10, "Inferno", 7, 60, 0, 12, 10, "Fire Ball", 7, 30, 0, 10, 10, , , , , , , , , , , , , , , , , , , , , , , , , 2, 2, 2, 1], + ["Eastern Sorceror", "Fire-Nightmare", 0, 18, 359, 3, 2, 61, 252, "merca222", "merca241", 9500, 120, 638, 20, 391, 16, 103, 10, 82, 8, 692, 30, 5, 22, 28, 4, 103, 4, "swor", "shie", "fire", 10, "Inferno", 7, 60, 0, 20, 10, "Fire Ball", 7, 30, 0, 18, 10, , , , , , , , , , , , , , , , , , , , , , , , , 2, 2, 2, 1], + ["Eastern Sorceror", "Cold-Nightmare", 0, 19, 359, 3, 2, 37, 252, "merca222", "merca241", 12000, 130, 302, 14, 151, 10, 73, 10, 58, 8, 212, 20, 3, 10, 16, 4, 61, 7, "swor", "shie", "cold", 10, "Glacial Spike", 7, 60, 0, 4, 5, "Frozen Armor", 7, 1000, 0, 8, 10, "Ice Blast", 7, 240, 0, 12, 10, , , , , , , , , , , , , , , , , , , 2, 2, 2, 1], + ["Eastern Sorceror", "Cold-Nightmare", 0, 19, 359, 3, 2, 61, 252, "merca222", "merca241", 12000, 130, 638, 20, 391, 16, 103, 10, 82, 8, 692, 30, 5, 22, 28, 4, 103, 4, "swor", "shie", "cold", 10, "Glacial Spike", 7, 60, 0, 8, 5, "Frozen Armor", 7, 1000, 0, 16, 10, "Ice Blast", 7, 240, 0, 20, 10, , , , , , , , , , , , , , , , , , , 2, 2, 2, 1], + ["Eastern Sorceror", "Ltng-Nightmare", 0, 20, 359, 3, 2, 37, 252, "merca222", "merca241", 10000, 120, 302, 14, 151, 10, 73, 10, 58, 8, 212, 20, 3, 10, 16, 4, 61, 7, "swor", "shie", "ltng", 10, "Charged Bolt", 7, 60, 0, 7, 5, "Lightning", 7, 30, 0, 9, 10, , , , , , , , , , , , , , , , , , , , , , , , , 2, 2, 2, 1], + ["Eastern Sorceror", "Ltng-Nightmare", 0, 20, 359, 3, 2, 61, 252, "merca222", "merca241", 10000, 120, 638, 20, 391, 16, 103, 10, 82, 8, 692, 30, 5, 22, 28, 4, 103, 4, "swor", "shie", "ltng", 10, "Charged Bolt", 7, 60, 0, 11, 5, "Lightning", 7, 30, 0, 17, 10, , , , , , , , , , , , , , , , , , , , , , , , , 2, 2, 2, 1], + ["Eastern Sorceror", "Fire-Hell", 0, 21, 359, 3, 3, 61, 252, "merca222", "merca241", 21000, 130, 574, 20, 352, 16, 99, 10, 78, 8, 623, 30, 5, 20, 26, 4, 100, 4, "swor", "shie", "fire", 10, "Inferno", 7, 60, 0, 19, 10, "Fire Ball", 7, 30, 0, 17, 10, , , , , , , , , , , , , , , , , , , , , , , , , 2, 2, 2, 1], + ["Eastern Sorceror", "Cold-Hell", 0, 22, 359, 3, 3, 61, 252, "merca222", "merca241", 27000, 140, 574, 20, 352, 16, 99, 10, 78, 8, 623, 30, 5, 20, 26, 4, 100, 4, "swor", "shie", "cold", 10, "Glacial Spike", 7, 60, 0, 7, 5, "Frozen Armor", 7, 1000, 0, 15, 10, "Ice Blast", 7, 240, 0, 19, 10, , , , , , , , , , , , , , , , , , , 2, 2, 2, 1], + ["Eastern Sorceror", "Ltng-Hell", 0, 23, 359, 3, 3, 61, 252, "merca222", "merca241", 21000, 130, 574, 20, 352, 16, 99, 10, 78, 8, 623, 30, 5, 20, 27, 4, 100, 4, "swor", "shie", "ltng", 10, "Charged Bolt", 7, 60, 0, 10, 5, "Lightning", 7, 30, 0, 16, 10, , , , , , , , , , , , , , , , , , , , , , , , , 2, 2, 2, 1], + ["Eastern Sorceror", "Fire-Normal", 100, 15, 359, 3, 1, 15, 252, "merca222", "merca241", 1000, 110, 160, 9, 80, 5, 49, 10, 40, 8, 15, 12, , 1, 7, 4, 25, 7, "swor", "shie", "fire", 10, "Inferno", 7, 60, 0, 6, 10, "Fire Ball", 7, 30, 0, 4, 10, , , , , , , , , , , , , , , , , , , , , , , , , 2, 2, 2, 1], + ["Eastern Sorceror", "Fire-Normal", 100, 15, 359, 3, 1, 49, 252, "merca222", "merca241", 1000, 110, 466, 18, 250, 13, 92, 10, 74, 8, 423, 24, 3, 18, 24, 4, 85, 7, "swor", "shie", "fire", 10, "Inferno", 7, 60, 0, 17, 10, "Fire Ball", 7, 30, 0, 15, 10, , , , , , , , , , , , , , , , , , , , , , , , , 2, 2, 2, 1], + ["Eastern Sorceror", "Fire-Normal", 100, 15, 359, 3, 1, 79, 252, "merca222", "merca241", 1000, 110, 1006, 27, 640, 25, 130, 10, 104, 8, 1143, 36, 5, 33, 39, 4, 138, 6, "swor", "shie", "fire", 10, "Inferno", 7, 60, 0, 27, 10, "Fire Ball", 7, 30, 0, 25, 10, , , , , , , , , , , , , , , , , , , , , , , , , 2, 2, 2, 1], + ["Eastern Sorceror", "Cold-Normal", 100, 16, 359, 3, 1, 15, 252, "merca222", "merca241", 1500, 120, 160, 9, 80, 5, 49, 10, 40, 8, 15, 12, , 1, 7, 4, 25, 7, "swor", "shie", "cold", 10, "Glacial Spike", 7, 60, 0, 1, 5, "Frozen Armor", 7, 1000, 0, 2, 10, "Ice Blast", 7, 240, 0, 6, 10, , , , , , , , , , , , , , , , , , , 2, 2, 2, 1], + ["Eastern Sorceror", "Cold-Normal", 100, 16, 359, 3, 1, 49, 252, "merca222", "merca241", 1500, 120, 466, 18, 250, 13, 92, 10, 74, 8, 423, 24, 3, 18, 24, 4, 85, 7, "swor", "shie", "cold", 10, "Glacial Spike", 7, 60, 0, 7, 5, "Frozen Armor", 7, 1000, 0, 13, 10, "Ice Blast", 7, 240, 0, 17, 10, , , , , , , , , , , , , , , , , , , 2, 2, 2, 1], + ["Eastern Sorceror", "Cold-Normal", 100, 16, 359, 3, 1, 79, 252, "merca222", "merca241", 1500, 120, 1006, 27, 640, 25, 130, 10, 104, 8, 1143, 36, 5, 33, 39, 4, 138, 4, "swor", "shie", "cold", 10, "Glacial Spike", 7, 60, 0, 12, 5, "Frozen Armor", 7, 1000, 0, 23, 10, "Ice Blast", 7, 240, 0, 27, 10, , , , , , , , , , , , , , , , , , , 2, 2, 2, 1], + ["Eastern Sorceror", "Ltng-Normal", 100, 17, 359, 3, 1, 15, 252, "merca222", "merca241", 1000, 110, 160, 9, 80, 5, 49, 10, 40, 8, 15, 12, , 1, 7, 4, 25, 7, "swor", "shie", "ltng", 10, "Charged Bolt", 7, 60, 0, 4, 5, "Lightning", 7, 30, 0, 3, 10, , , , , , , , , , , , , , , , , , , , , , , , , 2, 2, 2, 1], + ["Eastern Sorceror", "Ltng-Normal", 100, 17, 359, 3, 1, 49, 252, "merca222", "merca241", 1000, 110, 466, 18, 250, 13, 92, 10, 74, 8, 423, 24, 3, 18, 24, 4, 85, 7, "swor", "shie", "ltng", 10, "Charged Bolt", 7, 60, 0, 10, 5, "Lightning", 7, 30, 0, 14, 10, , , , , , , , , , , , , , , , , , , , , , , , , 2, 2, 2, 1], + ["Eastern Sorceror", "Ltng-Normal", 100, 17, 359, 3, 1, 79, 252, "merca222", "merca241", 1000, 110, 1006, 27, 640, 25, 130, 10, 104, 8, 1143, 36, 5, 33, 39, 4, 138, 4, "swor", "shie", "ltng", 10, "Charged Bolt", 7, 60, 0, 15, 5, "Lightning", 7, 30, 0, 24, 10, , , , , , , , , , , , , , , , , , , , , , , , , 2, 2, 2, 1], + ["Eastern Sorceror", "Fire-Nightmare", 100, 18, 359, 3, 2, 49, 252, "merca222", "merca241", 9500, 120, 419, 18, 225, 13, 88, 10, 70, 8, 381, 24, 3, 16, 22, 4, 82, 7, "swor", "shie", "fire", 10, "Inferno", 7, 60, 0, 16, 10, "Fire Ball", 7, 30, 0, 14, 10, , , , , , , , , , , , , , , , , , , , , , , , , 2, 2, 2, 1], + ["Eastern Sorceror", "Fire-Nightmare", 100, 18, 359, 3, 2, 79, 252, "merca222", "merca241", 9500, 120, 959, 27, 615, 25, 126, 10, 100, 8, 1101, 36, 5, 31, 37, 4, 135, 4, "swor", "shie", "fire", 10, "Inferno", 7, 60, 0, 26, 10, "Fire Ball", 7, 30, 0, 24, 10, , , , , , , , , , , , , , , , , , , , , , , , , 2, 2, 2, 1], + ["Eastern Sorceror", "Cold-Nightmare", 100, 19, 359, 3, 2, 49, 252, "merca222", "merca241", 12000, 130, 419, 18, 225, 13, 88, 10, 70, 8, 381, 24, 3, 16, 22, 4, 82, 7, "swor", "shie", "cold", 10, "Glacial Spike", 7, 60, 0, 6, 5, "Frozen Armor", 7, 1000, 0, 12, 10, "Ice Blast", 7, 240, 0, 16, 10, , , , , , , , , , , , , , , , , , , 2, 2, 2, 1], + ["Eastern Sorceror", "Cold-Nightmare", 100, 19, 359, 3, 2, 79, 252, "merca222", "merca241", 12000, 130, 959, 27, 615, 25, 126, 10, 100, 8, 1101, 36, 5, 31, 37, 4, 135, 4, "swor", "shie", "cold", 10, "Glacial Spike", 7, 60, 0, 11, 5, "Frozen Armor", 7, 1000, 0, 22, 10, "Ice Blast", 7, 240, 0, 26, 10, , , , , , , , , , , , , , , , , , , 2, 2, 2, 1], + ["Eastern Sorceror", "Ltng-Nightmare", 100, 20, 359, 3, 2, 49, 252, "merca222", "merca241", 10000, 120, 419, 18, 225, 13, 88, 10, 70, 8, 381, 24, 3, 16, 22, 4, 82, 7, "swor", "shie", "ltng", 10, "Charged Bolt", 7, 60, 0, 9, 5, "Lightning", 7, 30, 0, 13, 10, , , , , , , , , , , , , , , , , , , , , , , , , 2, 2, 2, 1], + ["Eastern Sorceror", "Ltng-Nightmare", 100, 20, 359, 3, 2, 79, 252, "merca222", "merca241", 10000, 120, 959, 27, 615, 25, 126, 10, 100, 8, 1101, 36, 5, 31, 37, 4, 135, 4, "swor", "shie", "ltng", 10, "Charged Bolt", 7, 60, 0, 14, 5, "Lightning", 7, 30, 0, 23, 10, , , , , , , , , , , , , , , , , , , , , , , , , 2, 2, 2, 1], + ["Eastern Sorceror", "Fire-Hell", 100, 21, 359, 3, 3, 79, 252, "merca222", "merca241", 21000, 130, 863, 27, 554, 25, 122, 10, 96, 8, 991, 36, 5, 29, 35, 4, 132, 4, "swor", "shie", "fire", 10, "Inferno", 7, 60, 0, 25, 10, "Fire Ball", 7, 30, 0, 23, 10, , , , , , , , , , , , , , , , , , , , , , , , , 2, 2, 2, 1], + ["Eastern Sorceror", "Cold-Hell", 100, 22, 359, 3, 3, 79, 252, "merca222", "merca241", 27000, 140, 863, 27, 554, 25, 122, 10, 96, 8, 991, 36, 5, 29, 35, 4, 132, 4, "swor", "shie", "cold", 10, "Glacial Spike", 7, 60, 0, 10, 5, "Frozen Armor", 7, 1000, 0, 21, 10, "Ice Blast", 7, 240, 0, 25, 10, , , , , , , , , , , , , , , , , , , 2, 2, 2, 1], + ["Eastern Sorceror", "Ltng-Hell", 100, 23, 359, 3, 3, 79, 252, "merca222", "merca241", 21000, 130, 863, 27, 554, 25, 122, 10, 96, 8, 991, 36, 5, 29, 36, 4, 132, 4, "swor", "shie", "ltng", 10, "Charged Bolt", 7, 60, 0, 13, 5, "Lightning", 7, 30, 0, 22, 10, , , , , , , , , , , , , , , , , , , , , , , , , 2, 2, 2, 1], + ["Barbarian", "1hs-Normal", 0, 24, 561, 5, 1, 28, 515, "mercX101", "mercX167", 9000, 120, 288, 14, 180, 7, 101, 15, 63, 10, 150, 17, 1, 16, 20, 6, 56, 7, "swor", , , 50, "Bash", 5, 15, 0, 4, 10, "Stun", 5, 15, 0, 3, 8, , , , , , , , , , , , , , , , , , , , , , , , , 0, 0, 0, 0], + ["Barbarian", "1hs-Normal", 0, 24, 561, 5, 1, 42, 515, "mercX101", "mercX167", 9000, 120, 484, 21, 278, 20, 128, 15, 81, 10, 388, 27, 1, 27, 31, 8, 81, 7, "swor", , , 50, "Bash", 5, 50, 0, 9, 10, "Stun", 5, 50, 0, 7, 8, , , , , , , , , , , , , , , , , , , , , , , , , 0, 0, 0, 0], + ["Barbarian", "1hs-Normal", 0, 24, 561, 5, 1, 75, 515, "mercX101", "mercX167", 9000, 120, 1177, 35, 938, 40, 190, 15, 123, 10, 1279, 37, 1, 60, 64, 8, 139, 4, "swor", , , 50, "Bash", 5, 75, 0, 20, 0, "Stun", 5, 75, 0, 16, 0, , , , , , , , , , , , , , , , , , , , , , , , , 0, 0, 0, 0], + ["Barbarian", "1hs-Normal", 0, 25, 561, 5, 1, 28, 515, "mercX101", "mercX167", 9000, 120, 288, 14, 180, 7, 101, 15, 63, 10, 150, 17, 1, 16, 20, 6, 56, 7, "swor", , , 50, "Bash", 5, 15, 0, 4, 10, "Stun", 5, 15, 0, 3, 8, , , , , , , , , , , , , , , , , , , , , , , , , 0, 0, 0, 0], + ["Barbarian", "1hs-Normal", 0, 25, 561, 5, 1, 42, 515, "mercX101", "mercX167", 9000, 120, 484, 21, 278, 20, 128, 15, 81, 10, 388, 27, 1, 27, 31, 8, 81, 7, "swor", , , 50, "Bash", 5, 50, 0, 9, 10, "Stun", 5, 50, 0, 7, 8, , , , , , , , , , , , , , , , , , , , , , , , , 0, 0, 0, 0], + ["Barbarian", "1hs-Normal", 0, 25, 561, 5, 1, 75, 515, "mercX101", "mercX167", 9000, 120, 1177, 35, 938, 40, 190, 15, 123, 10, 1279, 37, 1, 60, 64, 8, 139, 4, "swor", , , 50, "Bash", 5, 75, 0, 20, 0, "Stun", 5, 75, 0, 16, 0, , , , , , , , , , , , , , , , , , , , , , , , , 0, 0, 0, 0], + ["Barbarian", "1hs-Nightmare", 0, 26, 561, 5, 2, 42, 515, "mercX101", "mercX167", 18000, 130, 436, 21, 250, 20, 125, 15, 76, 10, 349, 27, 4, 25, 29, 8, 78, 7, "swor", , , 50, "Bash", 5, 50, 0, 8, 10, "Stun", 5, 50, 0, 6, 8, , , , , , , , , , , , , , , , , , , , , , , , , 0, 0, 0, 0], + ["Barbarian", "1hs-Nightmare", 0, 26, 561, 5, 2, 75, 515, "mercX101", "mercX167", 18000, 130, 1129, 35, 910, 40, 187, 15, 118, 10, 1240, 37, 4, 58, 62, 8, 136, 4, "swor", , , 50, "Bash", 5, 75, 0, 19, 0, "Stun", 5, 75, 0, 15, 0, , , , , , , , , , , , , , , , , , , , , , , , , 0, 0, 0, 0], + ["Barbarian", "1hs-Nightmare", 0, 27, 561, 5, 2, 42, 515, "mercX101", "mercX167", 18000, 130, 436, 21, 250, 20, 125, 15, 76, 10, 349, 27, 4, 25, 29, 8, 78, 7, "swor", , , 50, "Bash", 5, 50, 0, 8, 10, "Stun", 5, 50, 0, 6, 8, , , , , , , , , , , , , , , , , , , , , , , , , 0, 0, 0, 0], + ["Barbarian", "1hs-Nightmare", 0, 27, 561, 5, 2, 75, 515, "mercX101", "mercX167", 18000, 130, 1129, 35, 910, 40, 187, 15, 118, 10, 1240, 37, 4, 58, 62, 8, 136, 4, "swor", , , 50, "Bash", 5, 75, 0, 19, 0, "Stun", 5, 75, 0, 15, 0, , , , , , , , , , , , , , , , , , , , , , , , , 0, 0, 0, 0], + ["Barbarian", "1hs-Hell", 0, 28, 561, 5, 3, 75, 515, "mercX101", "mercX167", 32000, 140, 1016, 35, 819, 40, 184, 15, 113, 10, 1116, 37, 6, 56, 60, 8, 133, 4, "swor", , , 50, "Bash", 5, 70, 0, 18, 0, "Stun", 5, 70, 0, 14, 0, , , , , , , , , , , , , , , , , , , , , , , , , 0, 0, 0, 0], + ["Barbarian", "1hs-Hell", 0, 29, 561, 5, 3, 75, 515, "mercX101", "mercX167", 32000, 140, 1016, 35, 819, 40, 184, 15, 113, 10, 1116, 37, 6, 56, 60, 8, 133, 4, "swor", , , 50, "Bash", 5, 70, 0, 18, 0, "Stun", 5, 70, 0, 14, 0, , , , , , , , , , , , , , , , , , , , , , , , , 0, 0, 0, 0], + ["Barbarian", "1hs-Normal", 100, 24, 561, 5, 1, 28, 515, "mercX101", "mercX167", 9000, 120, 288, 18, 180, 10, 101, 15, 63, 10, 150, 20, 1, 16, 20, 6, 56, 7, "swor", , , 50, "Bash", 5, 15, 0, 4, 10, "Stun", 5, 15, 0, 3, 8, , , , , , , , , , , , , , , , , , , , , , , , , 0, 0, 0, 0], + ["Barbarian", "1hs-Normal", 100, 24, 561, 5, 1, 58, 515, "mercX101", "mercX167", 9000, 120, 828, 27, 480, 35, 158, 15, 101, 10, 750, 35, 1, 39, 43, 8, 109, 7, "swor", , , 50, "Bash", 5, 50, 0, 14, 10, "Stun", 5, 50, 0, 11, 8, , , , , , , , , , , , , , , , , , , , , , , , , 0, 0, 0, 0], + ["Barbarian", "1hs-Normal", 100, 24, 561, 5, 1, 80, 515, "mercX101", "mercX167", 9000, 120, 1422, 45, 1250, 50, 200, 15, 129, 10, 1520, 45, 1, 61, 65, 8, 148, 4, "swor", , , 50, "Bash", 5, 75, 0, 21, 0, "Stun", 5, 75, 0, 17, 0, , , , , , , , , , , , , , , , , , , , , , , , , 0, 0, 0, 0], + ["Barbarian", "1hs-Normal", 100, 25, 561, 5, 1, 28, 515, "mercX101", "mercX167", 9000, 120, 288, 18, 180, 10, 101, 15, 63, 10, 150, 20, 1, 16, 20, 6, 56, 7, "swor", , , 50, "Bash", 5, 15, 0, 4, 10, "Stun", 5, 15, 0, 3, 8, , , , , , , , , , , , , , , , , , , , , , , , , 0, 0, 0, 0], + ["Barbarian", "1hs-Normal", 100, 25, 561, 5, 1, 58, 515, "mercX101", "mercX167", 9000, 120, 828, 27, 480, 35, 158, 15, 101, 10, 750, 35, 1, 39, 43, 8, 109, 7, "swor", , , 50, "Bash", 5, 50, 0, 14, 10, "Stun", 5, 50, 0, 11, 8, , , , , , , , , , , , , , , , , , , , , , , , , 0, 0, 0, 0], + ["Barbarian", "1hs-Normal", 100, 25, 561, 5, 1, 80, 515, "mercX101", "mercX167", 9000, 120, 1422, 45, 1250, 50, 200, 15, 129, 10, 1520, 45, 1, 61, 65, 8, 148, 4, "swor", , , 50, "Bash", 5, 75, 0, 21, 0, "Stun", 5, 75, 0, 17, 0, , , , , , , , , , , , , , , , , , , , , , , , , 0, 0, 0, 0], + ["Barbarian", "1hs-Nightmare", 100, 26, 561, 5, 2, 58, 515, "mercX101", "mercX167", 18000, 130, 745, 27, 432, 35, 155, 15, 96, 10, 675, 35, 4, 37, 41, 8, 106, 7, "swor", , , 50, "Bash", 5, 50, 0, 13, 10, "Stun", 5, 50, 0, 10, 8, , , , , , , , , , , , , , , , , , , , , , , , , 0, 0, 0, 0], + ["Barbarian", "1hs-Nightmare", 100, 26, 561, 5, 2, 80, 515, "mercX101", "mercX167", 18000, 130, 1339, 45, 1202, 50, 197, 15, 124, 10, 1445, 45, 4, 59, 63, 8, 145, 4, "swor", , , 50, "Bash", 5, 75, 0, 20, 0, "Stun", 5, 75, 0, 16, 0, , , , , , , , , , , , , , , , , , , , , , , , , 0, 0, 0, 0], + ["Barbarian", "1hs-Nightmare", 100, 27, 561, 5, 2, 58, 515, "mercX101", "mercX167", 18000, 130, 745, 27, 432, 35, 155, 15, 96, 10, 675, 35, 4, 37, 41, 8, 106, 7, "swor", , , 50, "Bash", 5, 50, 0, 13, 10, "Stun", 5, 50, 0, 10, 8, , , , , , , , , , , , , , , , , , , , , , , , , 0, 0, 0, 0], + ["Barbarian", "1hs-Nightmare", 100, 27, 561, 5, 2, 80, 515, "mercX101", "mercX167", 18000, 130, 1339, 45, 1202, 50, 197, 15, 124, 10, 1445, 45, 4, 59, 63, 8, 145, 4, "swor", , , 50, "Bash", 5, 75, 0, 20, 0, "Stun", 5, 75, 0, 16, 0, , , , , , , , , , , , , , , , , , , , , , , , , 0, 0, 0, 0], + ["Barbarian", "1hs-Hell", 100, 28, 561, 5, 3, 80, 515, "mercX101", "mercX167", 32000, 140, 1205, 45, 1082, 50, 194, 15, 119, 10, 1301, 45, 6, 57, 61, 8, 142, 4, "swor", , , 50, "Bash", 5, 70, 0, 19, 0, "Stun", 5, 70, 0, 15, 0, , , , , , , , , , , , , , , , , , , , , , , , , 0, 0, 0, 0], + ["Barbarian", "1hs-Hell", 100, 29, 561, 5, 3, 80, 515, "mercX101", "mercX167", 32000, 140, 1205, 45, 1082, 50, 194, 15, 119, 10, 1301, 45, 6, 57, 61, 8, 142, 4, "swor", , , 50, "Bash", 5, 70, 0, 19, 0, "Stun", 5, 70, 0, 15, 0, , , , , , , , , , , , , , , , , , , , , , , , , 0, 0, 0, 0] + ]; }); diff --git a/libs/SoloPlay/Modules/Mock.js b/libs/SoloPlay/Modules/Mock.js index 8ed9dacf..caeebb0a 100644 --- a/libs/SoloPlay/Modules/Mock.js +++ b/libs/SoloPlay/Modules/Mock.js @@ -1,196 +1,196 @@ /* eslint-disable no-var */ var __extends = (this && this.__extends) || (function () { - var extendStatics = function (d, b) { - extendStatics = Object.setPrototypeOf || + var extendStatics = function (d, b) { + extendStatics = Object.setPrototypeOf || ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; }; - return extendStatics(d, b); - }; - return function (d, b) { - if (typeof b !== "function" && b !== null) throw new TypeError("Class extends value " + String(b) + " is not a constructor or null"); - extendStatics(d, b); - function __() { this.constructor = d; } - d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); - }; + return extendStatics(d, b); + }; + return function (d, b) { + if (typeof b !== "function" && b !== null) throw new TypeError("Class extends value " + String(b) + " is not a constructor or null"); + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; })(); var __spreadArray = (this && this.__spreadArray) || function (to, from) { - for (var i = 0, il = from.length, j = to.length; i < il; i++, j++) { - to[j] = from[i]; - } - return to; + for (var i = 0, il = from.length, j = to.length; i < il; i++, j++) { + to[j] = from[i]; + } + return to; }; var __importDefault = (this && this.__importDefault) || function (mod) { - return (mod && mod.__esModule) ? mod : { "default": mod }; + return (mod && mod.__esModule) ? mod : { "default": mod }; }; (function (factory) { - if (typeof module === "object" && typeof module.exports === "object") { - var v = factory(require, exports); - if (v !== undefined) module.exports = v; - } else if (typeof define === "function" && define.amd) { - define(["require", "exports", "./utilities", "../../modules/sdk"], factory); - } + if (typeof module === "object" && typeof module.exports === "object") { + var v = factory(require, exports); + if (v !== undefined) module.exports = v; + } else if (typeof define === "function" && define.amd) { + define(["require", "exports", "./utilities", "../../modules/sdk"], factory); + } })(function (require, exports) { - "use strict"; - Object.defineProperty(exports, "__esModule", { value: true }); - exports.MockMonster = exports.MockPlayer = exports.MockItem = exports.Mockable = void 0; - /** + "use strict"; + Object.defineProperty(exports, "__esModule", { value: true }); + exports.MockMonster = exports.MockPlayer = exports.MockItem = exports.Mockable = void 0; + /** * @description An savable/transferable item you can test with as if it where real * @author Jaenster * */ - var utilities_1 = require("./utilities"); - var sdk_1 = __importDefault(require("../../modules/sdk")); - var Mockable = /** @class */ (function () { - function Mockable(settings) { - if (settings === void 0) { settings = {}; } - this.overrides = { stats: [], skills: [], flags: 0, items: [], states: {} }; - this.settingKeys = []; - this.settingKeys = Object.keys(settings); - Object.assign(this, settings); - } - Mockable.prototype.internalGetStat = function (major, minor) { - var _a; - var stat = ((_a = this.overrides.stats) !== null && _a !== void 0 ? _a : []).find(function (_a) { - var main = _a[0], sub = _a[1]; - return main === major && sub === (minor | 0); - }); - return (stat === null || stat === void 0 ? void 0 : stat[2]) || 0; - }; - Mockable.prototype.getStat = function (major, minor, extra) { - var selfValue = this.internalGetStat(major, minor); - var inventory = (this.getItems() || undefined); - // Level requirement is the max of all items (so including sockets) - if (major === sdk.stats.Levelreq) { - return Math.max.apply(Math, __spreadArray([selfValue], inventory.map(function (el) { return el.getStat(sdk.stats.Levelreq); }))); - } - var socketedStats = inventory.reduce(function (a, c) { return a + c.getStat.call(c, major, minor, extra); }, 0); - return selfValue + socketedStats; - }; - Mockable.prototype.getItems = function () { - return this.overrides.items || []; - }; - Mockable.prototype.toJSON = function () { - var _this = this; - var obj = {}; - this.settingKeys.forEach(function (key) { - obj[key] = _this[key]; - }); - return JSON.stringify(obj); - }; - Mockable.prototype.getState = function (id) { - var _a; - return ((_a = this.overrides.states) === null || _a === void 0 ? void 0 : _a[id]) || 0; - }; - Mockable.prototype.getFlag = function (flags) { - var _a; - return !!(((_a = this.overrides.flags) !== null && _a !== void 0 ? _a : 0) & (flags | 0)); - }; - return Mockable; - }()); - exports.Mockable = Mockable; - // Put entire prototype of Unit in Mockable - utilities_1.mixinFunctions(Mockable, Unit); - var MockItem = /** @class */ (function (_super) { - __extends(MockItem, _super); - function MockItem() { - return _super !== null && _super.apply(this, arguments) || this; - } - MockItem.getAllItemStats = function (item) { - var stats = []; - if (!item.getFlag(sdk.items.flags.Runeword)) { - // since getStat(-1) is a perfect copy from item.getStat(major, minor), loop over it and get the real value - // example, item.getStat(7, 0) != item.getStat(-1).find(([major])=> major === 7)[2] - // its shifted with 8 bytes - return (item.getStat(-1) || []) - .map(function (_a) { - // eslint-disable-next-line no-unused-vars - var major = _a[0], minor = _a[1], value = _a[2]; - return [major, minor, item.getStat(major, minor)]; - }); - } - for (var x = 0; x < 358; x++) { - var zero = item.getStatEx(x, 0); - zero && stats.push([x, 0, zero]); - for (var y = 1; y < 281; y++) { - var second = item.getStatEx(x, y); - second && second !== zero && stats.push([x, y, second]); - } - } - return stats; - }; - MockItem.settingsGenerator = function (item, settings) { - if (settings === void 0) { settings = {}; } - // Add to settings - var initializer = Object.keys(item) - .filter(function (key) { return typeof item[key] !== "function"; }) - .reduce(function (acc, key) { - acc[key] = item[key]; - return acc; - }, {}); - var stats = MockItem.getAllItemStats(item); - initializer.overrides = { - stats: stats.reduce(function (accumulator, _a) { - var major = _a[0], minor = _a[1], value = _a[2]; - var socketable = item.getItemsEx().map(function (item) { return item.getStat(major, minor); }).reduce(function (a, c) { return a + c; }, 0) || 0; - var realValue = value; - if (major !== sdk.stats.Levelreq) { - realValue = value - socketable; - } - if (realValue > 0) { // Only if this stat isn't given by a socketable - accumulator.push([major, minor, value]); - } - return accumulator; - }, []), - flags: item.getFlags(), - }; - return initializer; - }; - MockItem.fromItem = function (item, settings) { - if (settings === void 0) { settings = {}; } - var initializer = this.settingsGenerator(item, settings); - initializer.overrides.items = item.getItemsEx().map(function (item) { return MockItem.fromItem(item); }); - return new MockItem(initializer); - }; - MockItem.fromPlayer = function (from) { - if (from === void 0) { from = me; } - return from.getItemsEx() - .filter(function (item) { - return item.location === sdk_1.default.storage.Equipment + var utilities_1 = require("./utilities"); + var sdk_1 = __importDefault(require("../../modules/sdk")); + var Mockable = /** @class */ (function () { + function Mockable(settings) { + if (settings === void 0) { settings = {}; } + this.overrides = { stats: [], skills: [], flags: 0, items: [], states: {} }; + this.settingKeys = []; + this.settingKeys = Object.keys(settings); + Object.assign(this, settings); + } + Mockable.prototype.internalGetStat = function (major, minor) { + var _a; + var stat = ((_a = this.overrides.stats) !== null && _a !== void 0 ? _a : []).find(function (_a) { + var main = _a[0], sub = _a[1]; + return main === major && sub === (minor | 0); + }); + return (stat === null || stat === void 0 ? void 0 : stat[2]) || 0; + }; + Mockable.prototype.getStat = function (major, minor, extra) { + var selfValue = this.internalGetStat(major, minor); + var inventory = (this.getItems() || undefined); + // Level requirement is the max of all items (so including sockets) + if (major === sdk.stats.Levelreq) { + return Math.max.apply(Math, __spreadArray([selfValue], inventory.map(function (el) { return el.getStat(sdk.stats.Levelreq); }))); + } + var socketedStats = inventory.reduce(function (a, c) { return a + c.getStat.call(c, major, minor, extra); }, 0); + return selfValue + socketedStats; + }; + Mockable.prototype.getItems = function () { + return this.overrides.items || []; + }; + Mockable.prototype.toJSON = function () { + var _this = this; + var obj = {}; + this.settingKeys.forEach(function (key) { + obj[key] = _this[key]; + }); + return JSON.stringify(obj); + }; + Mockable.prototype.getState = function (id) { + var _a; + return ((_a = this.overrides.states) === null || _a === void 0 ? void 0 : _a[id]) || 0; + }; + Mockable.prototype.getFlag = function (flags) { + var _a; + return !!(((_a = this.overrides.flags) !== null && _a !== void 0 ? _a : 0) & (flags | 0)); + }; + return Mockable; + }()); + exports.Mockable = Mockable; + // Put entire prototype of Unit in Mockable + utilities_1.mixinFunctions(Mockable, Unit); + var MockItem = /** @class */ (function (_super) { + __extends(MockItem, _super); + function MockItem() { + return _super !== null && _super.apply(this, arguments) || this; + } + MockItem.getAllItemStats = function (item) { + var stats = []; + if (!item.getFlag(sdk.items.flags.Runeword)) { + // since getStat(-1) is a perfect copy from item.getStat(major, minor), loop over it and get the real value + // example, item.getStat(7, 0) != item.getStat(-1).find(([major])=> major === 7)[2] + // its shifted with 8 bytes + return (item.getStat(-1) || []) + .map(function (_a) { + // eslint-disable-next-line no-unused-vars + var major = _a[0], minor = _a[1], value = _a[2]; + return [major, minor, item.getStat(major, minor)]; + }); + } + for (var x = 0; x < 358; x++) { + var zero = item.getStatEx(x, 0); + zero && stats.push([x, 0, zero]); + for (var y = 1; y < 281; y++) { + var second = item.getStatEx(x, y); + second && second !== zero && stats.push([x, y, second]); + } + } + return stats; + }; + MockItem.settingsGenerator = function (item, settings) { + if (settings === void 0) { settings = {}; } + // Add to settings + var initializer = Object.keys(item) + .filter(function (key) { return typeof item[key] !== "function"; }) + .reduce(function (acc, key) { + acc[key] = item[key]; + return acc; + }, {}); + var stats = MockItem.getAllItemStats(item); + initializer.overrides = { + stats: stats.reduce(function (accumulator, _a) { + var major = _a[0], minor = _a[1], value = _a[2]; + var socketable = item.getItemsEx().map(function (item) { return item.getStat(major, minor); }).reduce(function (a, c) { return a + c; }, 0) || 0; + var realValue = value; + if (major !== sdk.stats.Levelreq) { + realValue = value - socketable; + } + if (realValue > 0) { // Only if this stat isn't given by a socketable + accumulator.push([major, minor, value]); + } + return accumulator; + }, []), + flags: item.getFlags(), + }; + return initializer; + }; + MockItem.fromItem = function (item, settings) { + if (settings === void 0) { settings = {}; } + var initializer = this.settingsGenerator(item, settings); + initializer.overrides.items = item.getItemsEx().map(function (item) { return MockItem.fromItem(item); }); + return new MockItem(initializer); + }; + MockItem.fromPlayer = function (from) { + if (from === void 0) { from = me; } + return from.getItemsEx() + .filter(function (item) { + return item.location === sdk_1.default.storage.Equipment || (item.location === sdk_1.default.storage.Inventory && [603, 604, 605].indexOf(item.classid) > -1); - }) - .map(function (x) { return MockItem.fromItem(x); }); - }; - return MockItem; - }(Mockable)); - exports.MockItem = MockItem; - var MockPlayer = /** @class */ (function (_super) { - __extends(MockPlayer, _super); - function MockPlayer() { - return _super !== null && _super.apply(this, arguments) || this; - } - Object.defineProperty(MockPlayer.prototype, "maxhp", { - get: function () { - return this.getStat(sdk_1.default.stats.Maxhp) * (1 + (this.getStat(sdk_1.default.stats.MaxhpPercent) / 100)); - }, - enumerable: false, - configurable: true - }); - Object.defineProperty(MockPlayer.prototype, "maxmp", { - get: function () { - return this.getStat(sdk_1.default.stats.Maxhp) * (1 + (this.getStat(sdk_1.default.stats.MaxmanaPercent) / 100)); - }, - enumerable: false, - configurable: true - }); - return MockPlayer; - }(Mockable)); - exports.MockPlayer = MockPlayer; - var MockMonster = /** @class */ (function (_super) { - __extends(MockMonster, _super); - function MockMonster() { - return _super !== null && _super.apply(this, arguments) || this; - } - return MockMonster; - }(Mockable)); - exports.MockMonster = MockMonster; + }) + .map(function (x) { return MockItem.fromItem(x); }); + }; + return MockItem; + }(Mockable)); + exports.MockItem = MockItem; + var MockPlayer = /** @class */ (function (_super) { + __extends(MockPlayer, _super); + function MockPlayer() { + return _super !== null && _super.apply(this, arguments) || this; + } + Object.defineProperty(MockPlayer.prototype, "maxhp", { + get: function () { + return this.getStat(sdk_1.default.stats.Maxhp) * (1 + (this.getStat(sdk_1.default.stats.MaxhpPercent) / 100)); + }, + enumerable: false, + configurable: true + }); + Object.defineProperty(MockPlayer.prototype, "maxmp", { + get: function () { + return this.getStat(sdk_1.default.stats.Maxhp) * (1 + (this.getStat(sdk_1.default.stats.MaxmanaPercent) / 100)); + }, + enumerable: false, + configurable: true + }); + return MockPlayer; + }(Mockable)); + exports.MockPlayer = MockPlayer; + var MockMonster = /** @class */ (function (_super) { + __extends(MockMonster, _super); + function MockMonster() { + return _super !== null && _super.apply(this, arguments) || this; + } + return MockMonster; + }(Mockable)); + exports.MockMonster = MockMonster; }); diff --git a/libs/SoloPlay/Modules/MockItem.js b/libs/SoloPlay/Modules/MockItem.js index 10f19026..b0cb8ede 100644 --- a/libs/SoloPlay/Modules/MockItem.js +++ b/libs/SoloPlay/Modules/MockItem.js @@ -7,203 +7,203 @@ (function (module, require) { - var defaultSettings = { - base: 0, // Can be an item it extends - type: 4, - classid: 0, - mode: 0, - name: 0, - act: 0, - gid: 0, - x: 0, - y: 0, - targetx: 0, - targety: 0, - area: 0, - hp: 0, - hpmax: 0, - mp: 0, - mpmax: 0, - stamina: 0, - staminamax: 0, - charlvl: 0, - itemcount: 0, - owner: 0, - ownertype: 0, - spectype: 0, - direction: 0, - uniqueid: 0, - code: 0, - prefix: 0, - suffix: 0, - prefixes: 0, - suffixes: 0, - prefixnum: 0, - suffixnum: 0, - prefixnums: 0, - suffixnums: 0, - fname: 0, - quality: 0, - node: 0, - location: 0, - sizex: 0, - sizey: 0, - itemType: 0, - description: 0, - bodylocation: 0, - ilvl: 0, - lvlreq: 0, - gfx: 0, - runwalk: 0, - weaponswitch: 0, - objtype: 0, - islocked: 0, - getColor: 0, - socketedWith: [], - - overrides: {stats: {}}, - }; - - /** - * @static fromItem - * @static fromGear - * - * @param settings - * @constructor - */ - function MockItem(settings = {}) { - if (typeof settings !== 'object' && settings) { settings = {}; } - this.overrides = { stats: [], skills: [], flags: 0, items: [], states: {} }; - this.settingKeys = []; - this.settingKeys = Object.keys(settings); - Object.assign(this, settings); - - Object.keys(Unit.prototype).forEach(k => { - typeof Unit.prototype === 'function' && (this[k] = (...args) => { - Unit.prototype.apply(this, args); - }); - }); - - this.internalGetStat = function (major, minor) { - var _a; - var stat = ((_a = this.overrides.stats) !== null && _a !== void 0 ? _a : []).find(function (_a) { - var main = _a[0], sub = _a[1]; - return main === major && sub === (minor | 0); - }); - return (stat === null || stat === void 0 ? void 0 : stat[2]) || 0; - }; - - this.getStat = function (major, minor, extra) { - var selfValue = this.internalGetStat(major, minor); - var inventory = (this.getItems() || undefined); - // Level requirement is the max of all items (so including sockets) - let original = typeof this.base === 'object' && this.base.hasOwnProperty('getStat') && this.base.getStat.apply(this.base, [major, minor]) || 0; - if (major === sdk.stats.Levelreq) { - return Math.max.apply(Math, __spreadArray([selfValue], inventory.map(function (el) { return el.getStat(sdk.stats.Levelreq); }))); - } - var socketedStats = inventory.reduce(function (a, c) { return a + c.getStat.call(c, major, minor, extra); }, 0); - return original + selfValue + socketedStats; - }; - - this.getItems = function () { - return this.overrides.items || []; - }; - - this.toJSON = function () { - var _this = this; - var obj = {}; - this.settingKeys.forEach(function (key) { - obj[key] = _this[key]; - }); - return JSON.stringify(obj); - }; - this.getState = function (id) { - var _a; - return ((_a = this.overrides.states) === null || _a === void 0 ? void 0 : _a[id]) || 0; - }; - this.getFlag = function (flags) { - var _a; - return !!(((_a = this.overrides.flags) !== null && _a !== void 0 ? _a : 0) & (flags | 0)); - }; - - this.getItemsEx = function () { - return this.socketedWith; - }; - - // make it work with pickit lines - this.getStatEx = function (major, minor) { - return Unit.prototype.getStatEx.apply(this, [major, minor]); - }; - - this.store = () => JSON.stringify(Object.keys(settings).reduce((a, key) => a[key] = this[key], {})); - - Object.keys(Unit.prototype) - .filter(key => typeof this[key] === 'undefined') - .forEach(key => this[key] = (...args) => Unit.prototype[key].apply(this, args)); - } - - MockItem.getAllItemStats = function (item) { - var stats = []; - if (!item.getFlag(sdk.items.flags.Runeword)) { - // since getStat(-1) is a perfect copy from item.getStat(major, minor), loop over it and get the real value - // example, item.getStat(7, 0) != item.getStat(-1).find(([major])=> major === 7)[2] - // its shifted with 8 bytes - return (item.getStat(-1) || []) - .map(function (_a) { - var major = _a[0], minor = _a[1], value = _a[2]; - return [major, minor, item.getStat(major, minor)]; - }); - } - for (var x = 0; x < 358; x++) { - var zero = item.getStatEx(x, 0); - zero && stats.push([x, 0, zero]); - for (var y = 1; y < 281; y++) { - var second = item.getStatEx(x, y); - second && second !== zero && stats.push([x, y, second]); - } - } - return stats; - }; - - MockItem.fromItem = function (item, settings) { - if (settings === void 0) { settings = {}; } - console.log(JSON.stringify(settings)); - Object.keys(item).forEach(key => settings[key] = item[key]); - console.log(JSON.stringify(settings)); - settings.socketedWith = item.getItemsEx().map(item => MockItem.fromItem(item)) || []; // Mock its sockets too - var initializer = Object.keys(item) - .filter(function (key) { return typeof item[key] !== 'function'; }) - .reduce(function (acc, key) { - acc[key] = item[key]; - return acc; - }, {}); - var stats = MockItem.getAllItemStats(item); - initializer.overrides = { - stats: stats.reduce(function (accumulator, _a) { - var major = _a[0], minor = _a[1], value = _a[2]; - var socketable = item.getItemsEx().map(item => item.getStat(major, minor)).reduce((a, c) => a + c, 0) || 0; - var realValue = value; - if (major !== sdk.stats.Levelreq) { - realValue = value - socketable; - } - if (realValue > 0) { // Only if this stat isn't given by a socketable - accumulator.push([major, minor, value]); - } - return accumulator; - }, []), - flags: item.getFlag(), - }; - initializer.overrides.items = item.getItemsEx().map(function (item) { return MockItem.fromItem(item); }); - return new MockItem(initializer); - }; - - MockItem.fromGear = function () { - return me.getItemsEx() - .filter(item => item.location === sdk.storage.Equipped - || (item.location === sdk.storage.Inventory && [603, 604, 605].indexOf(item.classid) > -1)) - .map(x => MockItem.fromItem(x)); - }; - - module.exports = MockItem; + var defaultSettings = { + base: 0, // Can be an item it extends + type: 4, + classid: 0, + mode: 0, + name: 0, + act: 0, + gid: 0, + x: 0, + y: 0, + targetx: 0, + targety: 0, + area: 0, + hp: 0, + hpmax: 0, + mp: 0, + mpmax: 0, + stamina: 0, + staminamax: 0, + charlvl: 0, + itemcount: 0, + owner: 0, + ownertype: 0, + spectype: 0, + direction: 0, + uniqueid: 0, + code: 0, + prefix: 0, + suffix: 0, + prefixes: 0, + suffixes: 0, + prefixnum: 0, + suffixnum: 0, + prefixnums: 0, + suffixnums: 0, + fname: 0, + quality: 0, + node: 0, + location: 0, + sizex: 0, + sizey: 0, + itemType: 0, + description: 0, + bodylocation: 0, + ilvl: 0, + lvlreq: 0, + gfx: 0, + runwalk: 0, + weaponswitch: 0, + objtype: 0, + islocked: 0, + getColor: 0, + socketedWith: [], + + overrides: {stats: {}}, + }; + + /** + * @static fromItem + * @static fromGear + * + * @param settings + * @constructor + */ + function MockItem(settings = {}) { + if (typeof settings !== 'object' && settings) { settings = {}; } + this.overrides = { stats: [], skills: [], flags: 0, items: [], states: {} }; + this.settingKeys = []; + this.settingKeys = Object.keys(settings); + Object.assign(this, settings); + + Object.keys(Unit.prototype).forEach(k => { + typeof Unit.prototype === 'function' && (this[k] = (...args) => { + Unit.prototype.apply(this, args); + }); + }); + + this.internalGetStat = function (major, minor) { + var _a; + var stat = ((_a = this.overrides.stats) !== null && _a !== void 0 ? _a : []).find(function (_a) { + var main = _a[0], sub = _a[1]; + return main === major && sub === (minor | 0); + }); + return (stat === null || stat === void 0 ? void 0 : stat[2]) || 0; + }; + + this.getStat = function (major, minor, extra) { + var selfValue = this.internalGetStat(major, minor); + var inventory = (this.getItems() || undefined); + // Level requirement is the max of all items (so including sockets) + let original = typeof this.base === 'object' && this.base.hasOwnProperty('getStat') && this.base.getStat.apply(this.base, [major, minor]) || 0; + if (major === sdk.stats.Levelreq) { + return Math.max.apply(Math, __spreadArray([selfValue], inventory.map(function (el) { return el.getStat(sdk.stats.Levelreq); }))); + } + var socketedStats = inventory.reduce(function (a, c) { return a + c.getStat.call(c, major, minor, extra); }, 0); + return original + selfValue + socketedStats; + }; + + this.getItems = function () { + return this.overrides.items || []; + }; + + this.toJSON = function () { + var _this = this; + var obj = {}; + this.settingKeys.forEach(function (key) { + obj[key] = _this[key]; + }); + return JSON.stringify(obj); + }; + this.getState = function (id) { + var _a; + return ((_a = this.overrides.states) === null || _a === void 0 ? void 0 : _a[id]) || 0; + }; + this.getFlag = function (flags) { + var _a; + return !!(((_a = this.overrides.flags) !== null && _a !== void 0 ? _a : 0) & (flags | 0)); + }; + + this.getItemsEx = function () { + return this.socketedWith; + }; + + // make it work with pickit lines + this.getStatEx = function (major, minor) { + return Unit.prototype.getStatEx.apply(this, [major, minor]); + }; + + this.store = () => JSON.stringify(Object.keys(settings).reduce((a, key) => a[key] = this[key], {})); + + Object.keys(Unit.prototype) + .filter(key => typeof this[key] === 'undefined') + .forEach(key => this[key] = (...args) => Unit.prototype[key].apply(this, args)); + } + + MockItem.getAllItemStats = function (item) { + var stats = []; + if (!item.getFlag(sdk.items.flags.Runeword)) { + // since getStat(-1) is a perfect copy from item.getStat(major, minor), loop over it and get the real value + // example, item.getStat(7, 0) != item.getStat(-1).find(([major])=> major === 7)[2] + // its shifted with 8 bytes + return (item.getStat(-1) || []) + .map(function (_a) { + var major = _a[0], minor = _a[1], value = _a[2]; + return [major, minor, item.getStat(major, minor)]; + }); + } + for (var x = 0; x < 358; x++) { + var zero = item.getStatEx(x, 0); + zero && stats.push([x, 0, zero]); + for (var y = 1; y < 281; y++) { + var second = item.getStatEx(x, y); + second && second !== zero && stats.push([x, y, second]); + } + } + return stats; + }; + + MockItem.fromItem = function (item, settings) { + if (settings === void 0) { settings = {}; } + console.log(JSON.stringify(settings)); + Object.keys(item).forEach(key => settings[key] = item[key]); + console.log(JSON.stringify(settings)); + settings.socketedWith = item.getItemsEx().map(item => MockItem.fromItem(item)) || []; // Mock its sockets too + var initializer = Object.keys(item) + .filter(function (key) { return typeof item[key] !== 'function'; }) + .reduce(function (acc, key) { + acc[key] = item[key]; + return acc; + }, {}); + var stats = MockItem.getAllItemStats(item); + initializer.overrides = { + stats: stats.reduce(function (accumulator, _a) { + var major = _a[0], minor = _a[1], value = _a[2]; + var socketable = item.getItemsEx().map(item => item.getStat(major, minor)).reduce((a, c) => a + c, 0) || 0; + var realValue = value; + if (major !== sdk.stats.Levelreq) { + realValue = value - socketable; + } + if (realValue > 0) { // Only if this stat isn't given by a socketable + accumulator.push([major, minor, value]); + } + return accumulator; + }, []), + flags: item.getFlag(), + }; + initializer.overrides.items = item.getItemsEx().map(function (item) { return MockItem.fromItem(item); }); + return new MockItem(initializer); + }; + + MockItem.fromGear = function () { + return me.getItemsEx() + .filter(item => item.location === sdk.storage.Equipped + || (item.location === sdk.storage.Inventory && [603, 604, 605].indexOf(item.classid) > -1)) + .map(x => MockItem.fromItem(x)); + }; + + module.exports = MockItem; }).call(null, module, require); diff --git a/libs/SoloPlay/Modules/MoveTo.js b/libs/SoloPlay/Modules/MoveTo.js index bc3754de..abb563bd 100644 --- a/libs/SoloPlay/Modules/MoveTo.js +++ b/libs/SoloPlay/Modules/MoveTo.js @@ -1,300 +1,300 @@ // eslint-disable-next-line no-var var __importDefault = (this && this.__importDefault) || function (mod) { - return (mod && mod.__esModule) ? mod : { "default": mod }; + return (mod && mod.__esModule) ? mod : { "default": mod }; }; (function (factory) { - if (typeof module === "object" && typeof module.exports === "object") { - let v = factory(require, exports); - if (v !== undefined) module.exports = v; - } else if (typeof define === "function" && define.amd) { - define(["require", "exports", "./Clear", "../../modules/sdk"], factory); - } + if (typeof module === "object" && typeof module.exports === "object") { + let v = factory(require, exports); + if (v !== undefined) module.exports = v; + } else if (typeof define === "function" && define.amd) { + define(["require", "exports", "./Clear", "../../modules/sdk"], factory); + } })(function (require, exports) { - "use strict"; - Object.defineProperty(exports, "__esModule", { value: true }); - exports.currentWalkingPath = exports.getWalkDistance = void 0; - const clear_1 = __importDefault(require("./Clear")); - const sdk_1 = __importDefault(require("../../modules/sdk")); - const getWalkDistance = function (x, y, area, xx, yy) { - if (area === void 0) { area = me.area; } - if (xx === void 0) { xx = me.x; } - if (yy === void 0) { yy = me.y; } - // distance between node x and x-1 - let path = getPath(area, x, y, xx, yy, 2, 5); - return path && path.map(function (e, i, s) { return i && getDistance(s[i - 1], e) || 0; }) - .reduce(function (acc, cur) { return acc + cur; }, 0) || Infinity; - }; - exports.getWalkDistance = getWalkDistance; + "use strict"; + Object.defineProperty(exports, "__esModule", { value: true }); + exports.currentWalkingPath = exports.getWalkDistance = void 0; + const clear_1 = __importDefault(require("./Clear")); + const sdk_1 = __importDefault(require("../../modules/sdk")); + const getWalkDistance = function (x, y, area, xx, yy) { + if (area === void 0) { area = me.area; } + if (xx === void 0) { xx = me.x; } + if (yy === void 0) { yy = me.y; } + // distance between node x and x-1 + let path = getPath(area, x, y, xx, yy, 2, 5); + return path && path.map(function (e, i, s) { return i && getDistance(s[i - 1], e) || 0; }) + .reduce(function (acc, cur) { return acc + cur; }, 0) || Infinity; + }; + exports.getWalkDistance = getWalkDistance; - let skipShrine = []; - exports.currentWalkingPath = []; - function moveTo(target, givenSettings) { - let _a; - let settings = Object.assign({}, { - allowTeleport: true, - startIndex: 0, - rangeOverride: null, - callback: undefined, - allowClearing: true, - clearFilter: function (m, n) { return getDistance(m, n) <= 14; }, - }, givenSettings); - let stateForShrine = function (id) { - if (id >= sdk_1.default.shrines.Armor && id <= sdk_1.default.shrines.Experience) { - return id + 122; - } - return 0; - }; - let searchShrine = function () { - return getUnits(2, "shrine") - .filter(function (el) { return el.mode === sdk.objects.mode.Inactive && Config.ScanShrines.includes(el.objtype); }) - .filter(function (el) { - // Dont do anything with shrines we already found - if (skipShrine.includes(el.gid)) return false; - let currentIndex = Config.ScanShrines.findIndex((s) => me.getState(stateForShrine(s))); - let index = Config.ScanShrines.indexOf(el.objtype); - if (currentIndex === -1 || index <= currentIndex || stateForShrine(el.objtype) === 0) { - if (el.objtype !== sdk_1.default.shrines.Mana || me.mpPercent >= 50) { - return true; - } else { - return getDistance(me, el) <= 10; - } - } - return false; - }) - .filter(function (el) { return Pather.getWalkDistance(el.x, el.y, el.area, me.x, me.y, 0, 5) <= 40; }) - .sort(function (a, b) { return (Config.ScanShrines.indexOf(a.objtype) - Config.ScanShrines.indexOf(b.objtype)) || a.distance - b.distance; }) - .first(); - }; - // convert presetunit to x,y target - if (target instanceof PresetUnit) { - target = { x: target.roomx * 5 + target.x, y: target.roomy * 5 + target.y }; - } + let skipShrine = []; + exports.currentWalkingPath = []; + function moveTo(target, givenSettings) { + let _a; + let settings = Object.assign({}, { + allowTeleport: true, + startIndex: 0, + rangeOverride: null, + callback: undefined, + allowClearing: true, + clearFilter: function (m, n) { return getDistance(m, n) <= 14; }, + }, givenSettings); + let stateForShrine = function (id) { + if (id >= sdk_1.default.shrines.Armor && id <= sdk_1.default.shrines.Experience) { + return id + 122; + } + return 0; + }; + let searchShrine = function () { + return getUnits(2, "shrine") + .filter(function (el) { return el.mode === sdk.objects.mode.Inactive && Config.ScanShrines.includes(el.objtype); }) + .filter(function (el) { + // Dont do anything with shrines we already found + if (skipShrine.includes(el.gid)) return false; + let currentIndex = Config.ScanShrines.findIndex((s) => me.getState(stateForShrine(s))); + let index = Config.ScanShrines.indexOf(el.objtype); + if (currentIndex === -1 || index <= currentIndex || stateForShrine(el.objtype) === 0) { + if (el.objtype !== sdk_1.default.shrines.Mana || me.mpPercent >= 50) { + return true; + } else { + return getDistance(me, el) <= 10; + } + } + return false; + }) + .filter(function (el) { return Pather.getWalkDistance(el.x, el.y, el.area, me.x, me.y, 0, 5) <= 40; }) + .sort(function (a, b) { return (Config.ScanShrines.indexOf(a.objtype) - Config.ScanShrines.indexOf(b.objtype)) || a.distance - b.distance; }) + .first(); + }; + // convert presetunit to x,y target + if (target instanceof PresetUnit) { + target = { x: target.roomx * 5 + target.x, y: target.roomy * 5 + target.y }; + } - let canTeleport = settings.allowTeleport && Pather.useTeleport(); - let clearPercentage = 100, didSkipTown = false; - // To fix recursion issues - let _prevpath = exports.currentWalkingPath; - try { - if (!Array.isArray(target)) { - target = [target]; - } + let canTeleport = settings.allowTeleport && Pather.useTeleport(); + let clearPercentage = 100, didSkipTown = false; + // To fix recursion issues + let _prevpath = exports.currentWalkingPath; + try { + if (!Array.isArray(target)) { + target = [target]; + } - let path_1 = target.map(function (target, index, self) { - // The next node starts with the last node - let fromx = !index ? me.x : self[index - 1].x, fromy = !index ? me.y : self[index - 1].y; - // avoid d2bs issues - if (typeof target.hook === "undefined") { - target.hook = undefined; - } - let path = (getPath(me.area, target.x, target.y, fromx, fromy, 2, 4) || []); - // sometimes the reduction path messes us that we dont have any path left to take (bugs in arcane) - if (!path.length) { - path = (getPath(me.area, target.x, target.y, fromx, fromy, 0, 4) || []); - } - return path.map(function (el, idx) { - // last index of the path gets the hook. Since path is in reverse order, last node is idx 0 - if (idx === 0 && target.hook) { - console.log("Assign the current hook -> ", target.hook); - return { x: el.x, y: el.y, index: index, hook: target.hook }; - } - // normal ones dont -> hook: undefined to avoid d2bs issues - return { x: el.x, y: el.y, index: index, hook: undefined }; - }); - }).reduce(function (cur, acc) { - // push each node to the list - cur.forEach(function (el) { return acc.push(el); }); - return acc; - }, []); - if (!path_1) { - throw new Error("failed to generate path"); - } + let path_1 = target.map(function (target, index, self) { + // The next node starts with the last node + let fromx = !index ? me.x : self[index - 1].x, fromy = !index ? me.y : self[index - 1].y; + // avoid d2bs issues + if (typeof target.hook === "undefined") { + target.hook = undefined; + } + let path = (getPath(me.area, target.x, target.y, fromx, fromy, 2, 4) || []); + // sometimes the reduction path messes us that we dont have any path left to take (bugs in arcane) + if (!path.length) { + path = (getPath(me.area, target.x, target.y, fromx, fromy, 0, 4) || []); + } + return path.map(function (el, idx) { + // last index of the path gets the hook. Since path is in reverse order, last node is idx 0 + if (idx === 0 && target.hook) { + console.log("Assign the current hook -> ", target.hook); + return { x: el.x, y: el.y, index: index, hook: target.hook }; + } + // normal ones dont -> hook: undefined to avoid d2bs issues + return { x: el.x, y: el.y, index: index, hook: undefined }; + }); + }).reduce(function (cur, acc) { + // push each node to the list + cur.forEach(function (el) { return acc.push(el); }); + return acc; + }, []); + if (!path_1) { + throw new Error("failed to generate path"); + } - path_1.reverse(); - let lines = path_1.map(function (node, i, self) { return i /*skip first*/ && new Line(self[i - 1].x, self[i - 1].y, node.x, node.y, 0x33, true); }); - path_1.forEach(function (el, idx) { - if (el.hook && idx) { - console.log("path ", idx, "has a hook"); - } - }); - exports.currentWalkingPath = path_1; - let pathCopy = path_1.slice(); - // find where to start (usefull to render a long path with nodes to walk back - let startIndex = path_1.findIndex(function (path) { return path.index === settings.startIndex; }); - if (startIndex > -1) { - console.log("start idnex"); - } + path_1.reverse(); + let lines = path_1.map(function (node, i, self) { return i /*skip first*/ && new Line(self[i - 1].x, self[i - 1].y, node.x, node.y, 0x33, true); }); + path_1.forEach(function (el, idx) { + if (el.hook && idx) { + console.log("path ", idx, "has a hook"); + } + }); + exports.currentWalkingPath = path_1; + let pathCopy = path_1.slice(); + // find where to start (usefull to render a long path with nodes to walk back + let startIndex = path_1.findIndex(function (path) { return path.index === settings.startIndex; }); + if (startIndex > -1) { + console.log("start idnex"); + } - let loops = 0, shrine_1; - let _loop_1 = function (i, node, l) { - if (settings.allowClearing && settings.clearFilter && canTeleport) { - j = i + 1; - let monsters = getUnits(sdk_1.default.unittype.Monster) - .filter(function (m) { return m.attackable && settings.clearFilter(m, path_1[j]); }); - while (j < path_1.length && !path_1[j].hook && monsters.length === 0 && exports.getWalkDistance(path_1[j].x, path_1[j].y) < 100 - 14 && settings.allowClearing) { - j += 1; - monsters = getUnits(sdk_1.default.unittype.Monster) - .filter(function (m) { return m.attackable && settings.clearFilter(m, path_1[j]); }); - } - i = Math.min(path_1.length - 1, j - 1); - } + let loops = 0, shrine_1; + let _loop_1 = function (i, node, l) { + if (settings.allowClearing && settings.clearFilter && canTeleport) { + j = i + 1; + let monsters = getUnits(sdk_1.default.unittype.Monster) + .filter(function (m) { return m.attackable && settings.clearFilter(m, path_1[j]); }); + while (j < path_1.length && !path_1[j].hook && monsters.length === 0 && exports.getWalkDistance(path_1[j].x, path_1[j].y) < 100 - 14 && settings.allowClearing) { + j += 1; + monsters = getUnits(sdk_1.default.unittype.Monster) + .filter(function (m) { return m.attackable && settings.clearFilter(m, path_1[j]); }); + } + i = Math.min(path_1.length - 1, j - 1); + } - node = path_1[i]; - path_1.index = i; - lines.forEach(function (line, i) { return line.color = i < path_1.index ? 0x99 : 0x7A; }); - if (me.inTown && !didSkipTown) { - didSkipTown = true; - console.log("Total nodes -> " + path_1.length); - let area = void 0, exits = []; - (area = getArea(me.area)) && (exits = area.exits); - let target_1 = exits.find(function (exit) { - let closeExitNode = path_1.findIndex(function (node) { return getDistance(node, exit) < 10; }); - if (closeExitNode > -1) { - // i = Math.min(closeExitNode-3 , 1); - i = closeExitNode; - return true; - } - return false; - }); - if (!target_1) { - console.log("Walking in town, but cant find any exit to walk to. So, simply walk normally"); - } - } + node = path_1[i]; + path_1.index = i; + lines.forEach(function (line, i) { return line.color = i < path_1.index ? 0x99 : 0x7A; }); + if (me.inTown && !didSkipTown) { + didSkipTown = true; + console.log("Total nodes -> " + path_1.length); + let area = void 0, exits = []; + (area = getArea(me.area)) && (exits = area.exits); + let target_1 = exits.find(function (exit) { + let closeExitNode = path_1.findIndex(function (node) { return getDistance(node, exit) < 10; }); + if (closeExitNode > -1) { + // i = Math.min(closeExitNode-3 , 1); + i = closeExitNode; + return true; + } + return false; + }); + if (!target_1) { + console.log("Walking in town, but cant find any exit to walk to. So, simply walk normally"); + } + } - let hookEvent = node.hook; - me.overhead("Moving to node (" + i + "/" + l + ") -- " + Math.round(node.distance * 100) / 100); - if (node.distance < 5) { - i++; - // console.log('Skipping node as its too nearby -> Hook? ', hookEvent); - hookEvent && hookEvent(); - return out_i_1 = i, out_node_1 = node, "continue"; - } + let hookEvent = node.hook; + me.overhead("Moving to node (" + i + "/" + l + ") -- " + Math.round(node.distance * 100) / 100); + if (node.distance < 5) { + i++; + // console.log('Skipping node as its too nearby -> Hook? ', hookEvent); + hookEvent && hookEvent(); + return out_i_1 = i, out_node_1 = node, "continue"; + } - //ToDo; teleport a part if we have enough mana and it saves us a bunch of nodes - // Like if we can skip by 35 of distance, yet remove a walk path of 60, we rather use a single teleport - // The path generated is long, we want sub nodes - // fixme: this will never be true, because we get a path from target by chunks of distance 4, see line 89-91 - // so the distance to next node is always 4 - if (node.distance > 30) { - let d = exports.getWalkDistance(node.x, node.y); - // If walking to the node is twice as far as teleporting, we teleport - if (canTeleport && d * 2 > node.distance) { - if (node.distance > 35) { - Pather.moveToOverride(node.x, node.y, 4, settings.allowClearing); - } else { - Pather.teleportTo(node.x, node.y); - } - } else { - console.debug("DONT USE RECURSION HERE WTF?"); - Pather.moveToOverride(node.x, node.y); - } - } + //ToDo; teleport a part if we have enough mana and it saves us a bunch of nodes + // Like if we can skip by 35 of distance, yet remove a walk path of 60, we rather use a single teleport + // The path generated is long, we want sub nodes + // fixme: this will never be true, because we get a path from target by chunks of distance 4, see line 89-91 + // so the distance to next node is always 4 + if (node.distance > 30) { + let d = exports.getWalkDistance(node.x, node.y); + // If walking to the node is twice as far as teleporting, we teleport + if (canTeleport && d * 2 > node.distance) { + if (node.distance > 35) { + Pather.moveToOverride(node.x, node.y, 4, settings.allowClearing); + } else { + Pather.teleportTo(node.x, node.y); + } + } else { + console.debug("DONT USE RECURSION HERE WTF?"); + Pather.moveToOverride(node.x, node.y); + } + } - // decent fix for this - me.cancel(0) && me.cancel(0) && me.cancel(0) && me.cancel(0); + // decent fix for this + me.cancel(0) && me.cancel(0) && me.cancel(0) && me.cancel(0); - if (node.distance > 2) { - if (exports.getWalkDistance(node.x, node.y) * 0.9 > node.distance) { - Pather.moveToOverride(node.x, node.y); - } else { - Pather.walkTo(node.x, node.y); - } - } + if (node.distance > 2) { + if (exports.getWalkDistance(node.x, node.y) * 0.9 > node.distance) { + Pather.moveToOverride(node.x, node.y); + } else { + Pather.walkTo(node.x, node.y); + } + } - if (settings.callback && settings.callback()) return { value: void 0 }; - // ToDo; only if clearing makes sense in this area due to effort - let range = 14 / 100 * clearPercentage; - if (settings.allowClearing) { - clear_1.default({ nodes: path_1, range: settings.rangeOverride || Math.max(4, range), callback: settings.callback }); - } - // console.log('after clear'); - // console.log('before pick'); - //Pickit.pickOnPath(path_1); - Misc.openChests(8); - // console.log('after pick'); - // if shrine found, click on it - if ((shrine_1 = searchShrine())) { - skipShrine.push(shrine_1.gid); - let nearestShrine_1 = path_1.slice().sort(function (a, b) { return getDistance(shrine_1, a) - getDistance(shrine_1, b); }).first(); - if (nearestShrine_1) { - (function (originalHook, shrineId) { - // First run original hook on this spot, if it had any - originalHook && originalHook(); - // once we are near - nearestShrine_1.hook = function () { - console.log("Should take shrine"); - let shrine = getUnits(2, "shrine").filter(function (el) { return el.gid === shrineId; }).first(); - if (shrine) { - // ToDo; use walk near / tk if we got it - moveTo([{ - x: shrine.x, - y: shrine.y, - hook: function () { - Misc.getShrine(shrine); - } - }]); - } - }; - })(typeof nearestShrine_1.hook !== "undefined" ? nearestShrine_1.hook : undefined, shrine_1.gid); - } - } + if (settings.callback && settings.callback()) return { value: void 0 }; + // ToDo; only if clearing makes sense in this area due to effort + let range = 14 / 100 * clearPercentage; + if (settings.allowClearing) { + clear_1.default({ nodes: path_1, range: settings.rangeOverride || Math.max(4, range), callback: settings.callback }); + } + // console.log('after clear'); + // console.log('before pick'); + //Pickit.pickOnPath(path_1); + Misc.openChests(8); + // console.log('after pick'); + // if shrine found, click on it + if ((shrine_1 = searchShrine())) { + skipShrine.push(shrine_1.gid); + let nearestShrine_1 = path_1.slice().sort(function (a, b) { return getDistance(shrine_1, a) - getDistance(shrine_1, b); }).first(); + if (nearestShrine_1) { + (function (originalHook, shrineId) { + // First run original hook on this spot, if it had any + originalHook && originalHook(); + // once we are near + nearestShrine_1.hook = function () { + console.log("Should take shrine"); + let shrine = getUnits(2, "shrine").filter(function (el) { return el.gid === shrineId; }).first(); + if (shrine) { + // ToDo; use walk near / tk if we got it + moveTo([{ + x: shrine.x, + y: shrine.y, + hook: function () { + Misc.getShrine(shrine); + } + }]); + } + }; + })(typeof nearestShrine_1.hook !== "undefined" ? nearestShrine_1.hook : undefined, shrine_1.gid); + } + } - // if this wasnt our last node - if (l - 1 !== i) { - if (path_1.index < i) { - console.debug("Walked back?"); - // let nearestNode = pathCopy.filter(el => el.index === node.index).sort((a, b) => a.distance - b.distance).first(); - i = path_1.index; - hookEvent && hookEvent(); - return out_i_1 = i, out_node_1 = node, "continue"; - } else { - // Sometimes we go way out track due to clearing, - // lets find the nearest node on the path and go from there - // but not of the next node path - let nearestNode_1 = pathCopy.filter(function (el) { return el.index === node.index; }).sort(function (a, b) { return a.distance - b.distance; }).first(); - // let nearestNode = path.slice(Math.min(path.index-10,0), path.index + 30).sort((a, b) => a.distance - b.distance).first(); - // if the nearest node is still in 95% of our current node, we dont need to reset - if (nearestNode_1.distance > 5 && node.distance > 5 && 100 / node.distance * nearestNode_1.distance < 95) { - console.debug("reseting path to other node"); - // reset i to the nearest node - let newIndex = path_1.findIndex(function (node) { return nearestNode_1.x === node.x && nearestNode_1.y === node.y; }); - // Move forward - if (newIndex > i) { - // Hook all skipped nodes - for (let j_1 = i; j_1 < newIndex; j_1++) { - let hookEvent_1 = (_a = path_1[i + j_1]) === null || _a === void 0 ? void 0 : _a.hook; - hookEvent_1 && hookEvent_1(); - } - i = newIndex; - } - hookEvent && hookEvent(); - return out_i_1 = i, out_node_1 = node, "continue"; - } - } - hookEvent && hookEvent(); - i++; - } + // if this wasnt our last node + if (l - 1 !== i) { + if (path_1.index < i) { + console.debug("Walked back?"); + // let nearestNode = pathCopy.filter(el => el.index === node.index).sort((a, b) => a.distance - b.distance).first(); + i = path_1.index; + hookEvent && hookEvent(); + return out_i_1 = i, out_node_1 = node, "continue"; + } else { + // Sometimes we go way out track due to clearing, + // lets find the nearest node on the path and go from there + // but not of the next node path + let nearestNode_1 = pathCopy.filter(function (el) { return el.index === node.index; }).sort(function (a, b) { return a.distance - b.distance; }).first(); + // let nearestNode = path.slice(Math.min(path.index-10,0), path.index + 30).sort((a, b) => a.distance - b.distance).first(); + // if the nearest node is still in 95% of our current node, we dont need to reset + if (nearestNode_1.distance > 5 && node.distance > 5 && 100 / node.distance * nearestNode_1.distance < 95) { + console.debug("reseting path to other node"); + // reset i to the nearest node + let newIndex = path_1.findIndex(function (node) { return nearestNode_1.x === node.x && nearestNode_1.y === node.y; }); + // Move forward + if (newIndex > i) { + // Hook all skipped nodes + for (let j_1 = i; j_1 < newIndex; j_1++) { + let hookEvent_1 = (_a = path_1[i + j_1]) === null || _a === void 0 ? void 0 : _a.hook; + hookEvent_1 && hookEvent_1(); + } + i = newIndex; + } + hookEvent && hookEvent(); + return out_i_1 = i, out_node_1 = node, "continue"; + } + } + hookEvent && hookEvent(); + i++; + } - out_i_1 = i; - out_node_1 = node; - }; + out_i_1 = i; + out_node_1 = node; + }; - let j, out_i_1, out_node_1; - for (let i = startIndex > 1 ? startIndex : 0, node = void 0, l = path_1.length; i < l; loops++) { - let state_1 = _loop_1(i, node, l); - i = out_i_1; - node = out_node_1; - if (typeof state_1 === "object") { - return state_1.value; - } - } - } finally { - // reset current path - exports.currentWalkingPath = _prevpath; - recursiveMoveTo--; - } - } + let j, out_i_1, out_node_1; + for (let i = startIndex > 1 ? startIndex : 0, node = void 0, l = path_1.length; i < l; loops++) { + let state_1 = _loop_1(i, node, l); + i = out_i_1; + node = out_node_1; + if (typeof state_1 === "object") { + return state_1.value; + } + } + } finally { + // reset current path + exports.currentWalkingPath = _prevpath; + recursiveMoveTo--; + } + } - exports.default = moveTo; - // eslint-disable-next-line no-var, no-unused-vars - var recursiveMoveTo = 0; + exports.default = moveTo; + // eslint-disable-next-line no-var, no-unused-vars + var recursiveMoveTo = 0; }); diff --git a/libs/SoloPlay/Modules/utilities.js b/libs/SoloPlay/Modules/utilities.js index cad74a80..d4aed495 100644 --- a/libs/SoloPlay/Modules/utilities.js +++ b/libs/SoloPlay/Modules/utilities.js @@ -1,69 +1,69 @@ var __importDefault = (this && this.__importDefault) || function (mod) { - return (mod && mod.__esModule) ? mod : { "default": mod }; + return (mod && mod.__esModule) ? mod : { "default": mod }; }; (function (factory) { - if (typeof module === "object" && typeof module.exports === "object") { - let v = factory(require, exports); - if (v !== undefined) module.exports = v; - } else if (typeof define === "function" && define.amd) { - define(["require", "exports", "../../modules/sdk", ], factory); - } + if (typeof module === "object" && typeof module.exports === "object") { + let v = factory(require, exports); + if (v !== undefined) module.exports = v; + } else if (typeof define === "function" && define.amd) { + define(["require", "exports", "../../modules/sdk", ], factory); + } })(function (require, exports) { - "use strict"; - Object.defineProperty(exports, "__esModule", { value: true }); - exports.randomString = exports.recursiveSearch = exports.mixinFunctions = exports.mixin = void 0; - let sdk_1 = __importDefault(require("../../modules/sdk")); - function mixin(target) { - let sources = []; - for (let _i = 1; _i < arguments.length; _i++) { - sources[_i - 1] = arguments[_i]; - } - sources.forEach(function (source) { - return Object.getOwnPropertyNames(source.prototype) - .forEach(function (key) { return Object.defineProperty(target.prototype, key, Object.getOwnPropertyDescriptor(source.prototype, key)); }); - }); - } - exports.mixin = mixin; - function mixinFunctions(target) { - let sources = []; - for (let _i = 1; _i < arguments.length; _i++) { - sources[_i - 1] = arguments[_i]; - } - sources.forEach(function (source) { - return Object.getOwnPropertyNames(source.prototype) - .forEach(function (key) { - let propertyDescriptor = Object.getOwnPropertyDescriptor(source.prototype, key); - let current = Object.getOwnPropertyDescriptor(target.prototype, key); - if (!current && propertyDescriptor.hasOwnProperty("value") && typeof propertyDescriptor.value === "function") { - Object.defineProperty(target.prototype, key, Object.getOwnPropertyDescriptor(source.prototype, key)); - } - }); - }); - } - exports.mixinFunctions = mixinFunctions; - function recursiveSearch(o, n, changed) { - if (changed === void 0) { changed = {}; } - Object.keys(n).forEach(function (key) { - if (typeof n[key] !== "object") { - if (!o.hasOwnProperty(key) || o[key] !== n[key]) { - changed[key] = n[key]; - } - } else { - if (typeof changed[key] !== "object" || !changed[key]) { - changed[key] = {}; - } - recursiveSearch((o === null || o === void 0 ? void 0 : o[key]) || {}, (n === null || n === void 0 ? void 0 : n[key]) || {}, changed[key]); - if (!Object.keys(changed[key]).length) - delete changed[key]; - } - }); - return changed; - } - exports.recursiveSearch = recursiveSearch; - function randomString(min, max) { - return Array.apply(null, { length: min + ~~(rand(0, max - min)) }) - .map(function (_) { return "abcdefghijklmnopqrstuvwxyz".charAt(Math.floor(Math.random() * 26)); }) - .join(""); - } - exports.randomString = randomString; + "use strict"; + Object.defineProperty(exports, "__esModule", { value: true }); + exports.randomString = exports.recursiveSearch = exports.mixinFunctions = exports.mixin = void 0; + let sdk_1 = __importDefault(require("../../modules/sdk")); + function mixin(target) { + let sources = []; + for (let _i = 1; _i < arguments.length; _i++) { + sources[_i - 1] = arguments[_i]; + } + sources.forEach(function (source) { + return Object.getOwnPropertyNames(source.prototype) + .forEach(function (key) { return Object.defineProperty(target.prototype, key, Object.getOwnPropertyDescriptor(source.prototype, key)); }); + }); + } + exports.mixin = mixin; + function mixinFunctions(target) { + let sources = []; + for (let _i = 1; _i < arguments.length; _i++) { + sources[_i - 1] = arguments[_i]; + } + sources.forEach(function (source) { + return Object.getOwnPropertyNames(source.prototype) + .forEach(function (key) { + let propertyDescriptor = Object.getOwnPropertyDescriptor(source.prototype, key); + let current = Object.getOwnPropertyDescriptor(target.prototype, key); + if (!current && propertyDescriptor.hasOwnProperty("value") && typeof propertyDescriptor.value === "function") { + Object.defineProperty(target.prototype, key, Object.getOwnPropertyDescriptor(source.prototype, key)); + } + }); + }); + } + exports.mixinFunctions = mixinFunctions; + function recursiveSearch(o, n, changed) { + if (changed === void 0) { changed = {}; } + Object.keys(n).forEach(function (key) { + if (typeof n[key] !== "object") { + if (!o.hasOwnProperty(key) || o[key] !== n[key]) { + changed[key] = n[key]; + } + } else { + if (typeof changed[key] !== "object" || !changed[key]) { + changed[key] = {}; + } + recursiveSearch((o === null || o === void 0 ? void 0 : o[key]) || {}, (n === null || n === void 0 ? void 0 : n[key]) || {}, changed[key]); + if (!Object.keys(changed[key]).length) + delete changed[key]; + } + }); + return changed; + } + exports.recursiveSearch = recursiveSearch; + function randomString(min, max) { + return Array.apply(null, { length: min + ~~(rand(0, max - min)) }) + .map(function (_) { return "abcdefghijklmnopqrstuvwxyz".charAt(Math.floor(Math.random() * 26)); }) + .join(""); + } + exports.randomString = randomString; }); diff --git a/libs/SoloPlay/Scripts/a1chests.js b/libs/SoloPlay/Scripts/a1chests.js index 3339e248..69a35e54 100644 --- a/libs/SoloPlay/Scripts/a1chests.js +++ b/libs/SoloPlay/Scripts/a1chests.js @@ -6,28 +6,28 @@ */ function a1chests() { - const areas = [sdk.areas.CaveLvl2, sdk.areas.UndergroundPassageLvl2, sdk.areas.HoleLvl2, sdk.areas.PitLvl2]; - - myPrint("starting a1 chests"); - Town.doChores(); + const areas = [sdk.areas.CaveLvl2, sdk.areas.UndergroundPassageLvl2, sdk.areas.HoleLvl2, sdk.areas.PitLvl2]; + + myPrint("starting a1 chests"); + Town.doChores(); - for (let i = 0; i < areas.length; i++) { - try { - // Don't run pits for its chest, when it was cleared during the pits script - if ((SoloIndex.doneList.includes("pits") || me.barbarian) && areas[i] === sdk.areas.PitLvl2) { - continue; - } + for (let i = 0; i < areas.length; i++) { + try { + // Don't run pits for its chest, when it was cleared during the pits script + if ((SoloIndex.doneList.includes("pits") || me.barbarian) && areas[i] === sdk.areas.PitLvl2) { + continue; + } - myPrint("Moving to " + getAreaName(areas[i])); - Pather.journeyTo(areas[i]); - Precast.doPrecast(); - Misc.openChestsInArea(areas[i]); - Town.doChores(); - } catch (e) { - console.debug("ÿc8Kolbot-SoloPlayÿc0: Failed to move to " + getAreaName(areas[i]), e); - continue; - } - } + myPrint("Moving to " + getAreaName(areas[i])); + Pather.journeyTo(areas[i]); + Precast.doPrecast(); + Misc.openChestsInArea(areas[i]); + Town.doChores(); + } catch (e) { + console.debug("ÿc8Kolbot-SoloPlayÿc0: Failed to move to " + getAreaName(areas[i]), e); + continue; + } + } - return true; + return true; } diff --git a/libs/SoloPlay/Scripts/a5chests.js b/libs/SoloPlay/Scripts/a5chests.js index 8cc16140..f21efd6e 100644 --- a/libs/SoloPlay/Scripts/a5chests.js +++ b/libs/SoloPlay/Scripts/a5chests.js @@ -6,29 +6,29 @@ */ function a5chests() { - const areas = [sdk.areas.Abaddon, sdk.areas.PitofAcheron, sdk.areas.InfernalPit, sdk.areas.GlacialTrail, sdk.areas.DrifterCavern, sdk.areas.IcyCellar]; - - myPrint("starting a5 chests"); - Town.doChores(); + const areas = [sdk.areas.Abaddon, sdk.areas.PitofAcheron, sdk.areas.InfernalPit, sdk.areas.GlacialTrail, sdk.areas.DrifterCavern, sdk.areas.IcyCellar]; + + myPrint("starting a5 chests"); + Town.doChores(); - for (let i = 0; i < areas.length; i++) { - try { - if (!Pather.canTeleport() && me.nightmare && [sdk.areas.Abaddon, sdk.areas.PitofAcheron, sdk.areas.InfernalPit].includes(areas[i])) { - continue; - } else if (!Pather.canTeleport() && me.nightmare && me.charlvl >= 70 && [sdk.areas.Abaddon, sdk.areas.PitofAcheron, sdk.areas.InfernalPit, sdk.areas.GlacialTrail, sdk.areas.DrifterCavern].includes(areas[i])) { - continue; - } + for (let i = 0; i < areas.length; i++) { + try { + if (!Pather.canTeleport() && me.nightmare && [sdk.areas.Abaddon, sdk.areas.PitofAcheron, sdk.areas.InfernalPit].includes(areas[i])) { + continue; + } else if (!Pather.canTeleport() && me.nightmare && me.charlvl >= 70 && [sdk.areas.Abaddon, sdk.areas.PitofAcheron, sdk.areas.InfernalPit, sdk.areas.GlacialTrail, sdk.areas.DrifterCavern].includes(areas[i])) { + continue; + } - myPrint("Moving to " + getAreaName(areas[i])); - Pather.journeyTo(areas[i]); - Precast.doPrecast(); - Misc.openChestsInArea(areas[i]); - Town.doChores(); - } catch (e) { - console.debug("ÿc8Kolbot-SoloPlayÿc0: Failed to move to " + getAreaName(areas[i]), e); - continue; - } - } + myPrint("Moving to " + getAreaName(areas[i])); + Pather.journeyTo(areas[i]); + Precast.doPrecast(); + Misc.openChestsInArea(areas[i]); + Town.doChores(); + } catch (e) { + console.debug("ÿc8Kolbot-SoloPlayÿc0: Failed to move to " + getAreaName(areas[i]), e); + continue; + } + } - return true; + return true; } diff --git a/libs/SoloPlay/Scripts/amulet.js b/libs/SoloPlay/Scripts/amulet.js index 1bd9b44b..faa990fe 100644 --- a/libs/SoloPlay/Scripts/amulet.js +++ b/libs/SoloPlay/Scripts/amulet.js @@ -6,32 +6,32 @@ */ function amulet () { - Town.doChores(false, { fullChores: true }); - myPrint("starting amulet"); + Town.doChores(false, { fullChores: true }); + myPrint("starting amulet"); - Pather.checkWP(sdk.areas.LostCity, true) ? Pather.useWaypoint(sdk.areas.LostCity) : Pather.getWP(sdk.areas.LostCity); - Precast.doPrecast(true); - Pather.moveToExit([sdk.areas.ValleyofSnakes, sdk.areas.ClawViperTempleLvl1, sdk.areas.ClawViperTempleLvl2], true); - Precast.doPrecast(true); + Pather.checkWP(sdk.areas.LostCity, true) ? Pather.useWaypoint(sdk.areas.LostCity) : Pather.getWP(sdk.areas.LostCity); + Precast.doPrecast(true); + Pather.moveToExit([sdk.areas.ValleyofSnakes, sdk.areas.ClawViperTempleLvl1, sdk.areas.ClawViperTempleLvl2], true); + Precast.doPrecast(true); - if (!Pather.useTeleport()) { - // change this to be array loop, sometimes bot gets lucky to have a clearish path to the chest but - // then because Attack.clear on nodeaction we move from the chest even though we were there and the vipers can't get to the altar - Pather.moveTo(15065, 14047); - Pather.moveTo(15063, 14066); - Pather.moveTo(15051, 14066); - Pather.moveTo(15045, 14051); - } else { - Pather.moveTo(15045, 14051, null, false); - } + if (!Pather.useTeleport()) { + // change this to be array loop, sometimes bot gets lucky to have a clearish path to the chest but + // then because Attack.clear on nodeaction we move from the chest even though we were there and the vipers can't get to the altar + Pather.moveTo(15065, 14047); + Pather.moveTo(15063, 14066); + Pather.moveTo(15051, 14066); + Pather.moveTo(15045, 14051); + } else { + Pather.moveTo(15045, 14051, null, false); + } - if (!Quest.collectItem(sdk.quest.item.ViperAmulet, sdk.quest.chest.ViperAmuletChest)) { - myPrint("Failed to collect viper amulet"); - return false; - } + if (!Quest.collectItem(sdk.quest.item.ViperAmulet, sdk.quest.chest.ViperAmuletChest)) { + myPrint("Failed to collect viper amulet"); + return false; + } - Town.npcInteract("drognan"); - Quest.stashItem(sdk.items.quest.ViperAmulet); + Town.npcInteract("drognan"); + Quest.stashItem(sdk.items.quest.ViperAmulet); - return true; + return true; } diff --git a/libs/SoloPlay/Scripts/ancients.js b/libs/SoloPlay/Scripts/ancients.js index 70fb2602..3b610940 100644 --- a/libs/SoloPlay/Scripts/ancients.js +++ b/libs/SoloPlay/Scripts/ancients.js @@ -7,65 +7,65 @@ */ function ancients () { - include("core/Common/Ancients.js"); - Town.doChores(false, { fullChores: true }); - myPrint("starting ancients"); + include("core/Common/Ancients.js"); + Town.doChores(false, { fullChores: true }); + myPrint("starting ancients"); - Pather.checkWP(sdk.areas.AncientsWay) ? Pather.useWaypoint(sdk.areas.AncientsWay) : Pather.getWP(sdk.areas.AncientsWay); - Precast.doPrecast(true); - Pather.clearToExit(sdk.areas.AncientsWay, sdk.areas.ArreatSummit, true); // enter Arreat Summit + Pather.checkWP(sdk.areas.AncientsWay) ? Pather.useWaypoint(sdk.areas.AncientsWay) : Pather.getWP(sdk.areas.AncientsWay); + Precast.doPrecast(true); + Pather.clearToExit(sdk.areas.AncientsWay, sdk.areas.ArreatSummit, true); // enter Arreat Summit - // failed to move to Arreat Summit - if (!me.inArea(sdk.areas.ArreatSummit)) { - return false; - } + // failed to move to Arreat Summit + if (!me.inArea(sdk.areas.ArreatSummit)) { + return false; + } - // ancients prep - Town.doChores(false, { thawing: true, antidote: true, stamina: true, fullChores: true }); + // ancients prep + Town.doChores(false, { thawing: true, antidote: true, stamina: true, fullChores: true }); - let tempConfig = copyObj(Config); // save and update config settings + let tempConfig = copyObj(Config); // save and update config settings - Config.TownCheck = false; - Config.MercWatch = false; - Config.TownHP = 0; - Config.TownMP = 0; - Config.HPBuffer = 15; - Config.MPBuffer = 15; - Config.LifeChicken = 10; - CharData.updateConfig(); - me.overhead("updated settings"); + Config.TownCheck = false; + Config.MercWatch = false; + Config.TownHP = 0; + Config.TownMP = 0; + Config.HPBuffer = 15; + Config.MPBuffer = 15; + Config.LifeChicken = 10; + CharData.updateConfig(); + me.overhead("updated settings"); - NPCAction.buyPotions(); - if (!Pather.usePortal(sdk.areas.ArreatSummit, me.name)) { - console.log("ÿc8Kolbot-SoloPlayÿc0: Failed to take portal back to Arreat Summit"); - Pather.clearToExit(sdk.areas.AncientsWay, sdk.areas.ArreatSummit, true); // enter Arreat Summit - } - - Precast.doPrecast(true); + NPCAction.buyPotions(); + if (!Pather.usePortal(sdk.areas.ArreatSummit, me.name)) { + console.log("ÿc8Kolbot-SoloPlayÿc0: Failed to take portal back to Arreat Summit"); + Pather.clearToExit(sdk.areas.AncientsWay, sdk.areas.ArreatSummit, true); // enter Arreat Summit + } + + Precast.doPrecast(true); - // move to altar - if (!Pather.moveToPreset(sdk.areas.ArreatSummit, sdk.unittype.Object, sdk.quest.chest.AncientsAltar)) { - console.warn("ÿc8Kolbot-SoloPlayÿc0: Failed to move to ancients' altar"); - } + // move to altar + if (!Pather.moveToPreset(sdk.areas.ArreatSummit, sdk.unittype.Object, sdk.quest.chest.AncientsAltar)) { + console.warn("ÿc8Kolbot-SoloPlayÿc0: Failed to move to ancients' altar"); + } - Common.Ancients.touchAltar(); - Common.Ancients.startAncients(true, true); - - me.cancel(); - Config = tempConfig; - CharData.updateConfig(); - me.overhead("restored settings"); - Precast.doPrecast(true); + Common.Ancients.touchAltar(); + Common.Ancients.startAncients(true, true); + + me.cancel(); + Config = tempConfig; + CharData.updateConfig(); + me.overhead("restored settings"); + Precast.doPrecast(true); - try { - if (Misc.checkQuest(sdk.quest.id.RiteofPassage, sdk.quest.states.Completed)) { - Pather.clearToExit(sdk.areas.ArreatSummit, sdk.areas.WorldstoneLvl1, true); - Pather.clearToExit(sdk.areas.WorldstoneLvl1, sdk.areas.WorldstoneLvl2, true); - Pather.getWP(sdk.areas.WorldstoneLvl2); - } - } catch (err) { - console.log("ÿc8Kolbot-SoloPlayÿc0: Cleared Ancients. Failed to get WSK Waypoint"); - } + try { + if (Misc.checkQuest(sdk.quest.id.RiteofPassage, sdk.quest.states.Completed)) { + Pather.clearToExit(sdk.areas.ArreatSummit, sdk.areas.WorldstoneLvl1, true); + Pather.clearToExit(sdk.areas.WorldstoneLvl1, sdk.areas.WorldstoneLvl2, true); + Pather.getWP(sdk.areas.WorldstoneLvl2); + } + } catch (err) { + console.log("ÿc8Kolbot-SoloPlayÿc0: Cleared Ancients. Failed to get WSK Waypoint"); + } - return true; + return true; } diff --git a/libs/SoloPlay/Scripts/ancienttunnels.js b/libs/SoloPlay/Scripts/ancienttunnels.js index aa22f6c9..fb6473ea 100644 --- a/libs/SoloPlay/Scripts/ancienttunnels.js +++ b/libs/SoloPlay/Scripts/ancienttunnels.js @@ -6,34 +6,34 @@ */ function ancienttunnels () { - Town.doChores(false, { fullChores: true }); - myPrint("starting ancient tunnels"); + Town.doChores(false, { fullChores: true }); + myPrint("starting ancient tunnels"); - Pather.checkWP(sdk.areas.LostCity, true) ? Pather.useWaypoint(sdk.areas.LostCity) : Pather.getWP(sdk.areas.LostCity); - Precast.doPrecast(true); + Pather.checkWP(sdk.areas.LostCity, true) ? Pather.useWaypoint(sdk.areas.LostCity) : Pather.getWP(sdk.areas.LostCity); + Precast.doPrecast(true); - if (me.hell && me.classic) { - Attack.clearLevel(); - } else { - try { - Pather.moveToPreset(me.area, sdk.unittype.Object, sdk.objects.SuperChest) && Misc.openChests(5) && Pickit.pickItems(); - } catch (e) { - console.error(e); - } + if (me.hell && me.classic) { + Attack.clearLevel(); + } else { + try { + Pather.moveToPreset(me.area, sdk.unittype.Object, sdk.objects.SuperChest) && Misc.openChests(5) && Pickit.pickItems(); + } catch (e) { + console.error(e); + } - try { - Pather.moveToPreset(me.area, sdk.unittype.Monster, sdk.monsters.preset.DarkElder) && Attack.clear(15, 0, getLocaleString(sdk.locale.monsters.DarkElder)); - } catch (e) { - console.warn("ÿc8Kolbot-SoloPlayÿc0: Failed to kill Dark Elder"); - } - } + try { + Pather.moveToPreset(me.area, sdk.unittype.Monster, sdk.monsters.preset.DarkElder) && Attack.clear(15, 0, getLocaleString(sdk.locale.monsters.DarkElder)); + } catch (e) { + console.warn("ÿc8Kolbot-SoloPlayÿc0: Failed to kill Dark Elder"); + } + } - if (!Pather.moveToExit(sdk.areas.AncientTunnels, true)) { - console.log("ÿc8Kolbot-SoloPlayÿc0: Failed to move to Ancient Tunnels"); - return false; - } + if (!Pather.moveToExit(sdk.areas.AncientTunnels, true)) { + console.log("ÿc8Kolbot-SoloPlayÿc0: Failed to move to Ancient Tunnels"); + return false; + } - Attack.clearLevel(); + Attack.clearLevel(); - return true; + return true; } diff --git a/libs/SoloPlay/Scripts/andariel.js b/libs/SoloPlay/Scripts/andariel.js index 5926885a..044b4d9b 100644 --- a/libs/SoloPlay/Scripts/andariel.js +++ b/libs/SoloPlay/Scripts/andariel.js @@ -7,94 +7,94 @@ // todo: clean this up function andariel () { - Town.doChores(false, { fullChores: true }); - myPrint("starting andy"); - - if (me.normal && Misc.checkQuest(sdk.quest.id.SistersToTheSlaughter, sdk.quest.states.ReqComplete)) { - Pather.changeAct(); - - return true; - } - - let questBug = (!me.normal && !me.andariel); - - Pather.checkWP(sdk.areas.CatacombsLvl2, true) ? Pather.useWaypoint(sdk.areas.CatacombsLvl2) : Pather.getWP(sdk.areas.CatacombsLvl2); - Precast.doPrecast(true); - Pather.moveToExit([sdk.areas.CatacombsLvl3, sdk.areas.CatacombsLvl4], true); - - if (me.poisonRes < 75) { - Town.doChores(true, { thawing: me.coldRes < 75, antidote: true }); - Pather.usePortal(sdk.areas.CatacombsLvl4); - } - - Precast.doPrecast(true); - - let oldPickRange = Config.PickRange; - - if (questBug) { - Config.PickRange = -1; - me.barbarian && (Config.FindItem = false); - } else { - Config.PickRange = 5; // Only pick what is directly around me - } - - let coords = [ - { x: 22572, y: 9635 }, { x: 22554, y: 9618 }, - { x: 22542, y: 9600 }, { x: 22572, y: 9582 }, - { x: 22554, y: 9566 } - ]; - - if (Pather.useTeleport()) { - Pather.moveTo(22571, 9590); - } else { - while (coords.length) { - let andy = Game.getMonster(sdk.monsters.Andariel); - - if (andy && andy.distance < 15) { - break; - } - - Pather.moveToUnit(coords[0]); - Attack.clearClassids(sdk.monsters.DarkShaman1); - coords.shift(); - } - } - - Config.MercWatch = false; - - Attack.killTarget("Andariel"); - - if (questBug) { - Config.TownCheck = false; - Config.MercWatch = false; - Config.HealStatus = false; - Config.UseMerc = false; - Config.TownHP = 0; - Config.TownMP = 0; - Config.PickRange = -1; - SoloEvents.townChicken.disabled = true; - CharData.updateConfig(); - - if (Pather.changeAct()) { - delay(2000 + me.ping); - - // Now check my area - if (me.act === 2) { - // Act change sucessful, Andy has been bugged - // let result = (Misc.checkQuest(sdk.quest.id.SistersToTheSlaughter, 15) ? "Sucessful" : "Unsucessful"); - // myPrint("Andy bugged was " + result); - myPrint("Bugging andy"); - scriptBroadcast("quit"); - } - } - } - - delay(2000 + me.ping); // Wait for minions to die. - Config.PickRange = oldPickRange; // Reset to normal value - Pickit.pickItems(); - Config.MercWatch = true; - - !me.andariel && Pather.changeAct(); - - return true; + Town.doChores(false, { fullChores: true }); + myPrint("starting andy"); + + if (me.normal && Misc.checkQuest(sdk.quest.id.SistersToTheSlaughter, sdk.quest.states.ReqComplete)) { + Pather.changeAct(); + + return true; + } + + let questBug = (!me.normal && !me.andariel); + + Pather.checkWP(sdk.areas.CatacombsLvl2, true) ? Pather.useWaypoint(sdk.areas.CatacombsLvl2) : Pather.getWP(sdk.areas.CatacombsLvl2); + Precast.doPrecast(true); + Pather.moveToExit([sdk.areas.CatacombsLvl3, sdk.areas.CatacombsLvl4], true); + + if (me.poisonRes < 75) { + Town.doChores(true, { thawing: me.coldRes < 75, antidote: true }); + Pather.usePortal(sdk.areas.CatacombsLvl4); + } + + Precast.doPrecast(true); + + let oldPickRange = Config.PickRange; + + if (questBug) { + Config.PickRange = -1; + me.barbarian && (Config.FindItem = false); + } else { + Config.PickRange = 5; // Only pick what is directly around me + } + + let coords = [ + { x: 22572, y: 9635 }, { x: 22554, y: 9618 }, + { x: 22542, y: 9600 }, { x: 22572, y: 9582 }, + { x: 22554, y: 9566 } + ]; + + if (Pather.useTeleport()) { + Pather.moveTo(22571, 9590); + } else { + while (coords.length) { + let andy = Game.getMonster(sdk.monsters.Andariel); + + if (andy && andy.distance < 15) { + break; + } + + Pather.moveToUnit(coords[0]); + Attack.clearClassids(sdk.monsters.DarkShaman1); + coords.shift(); + } + } + + Config.MercWatch = false; + + Attack.killTarget("Andariel"); + + if (questBug) { + Config.TownCheck = false; + Config.MercWatch = false; + Config.HealStatus = false; + Config.UseMerc = false; + Config.TownHP = 0; + Config.TownMP = 0; + Config.PickRange = -1; + SoloEvents.townChicken.disabled = true; + CharData.updateConfig(); + + if (Pather.changeAct()) { + delay(2000 + me.ping); + + // Now check my area + if (me.act === 2) { + // Act change sucessful, Andy has been bugged + // let result = (Misc.checkQuest(sdk.quest.id.SistersToTheSlaughter, 15) ? "Sucessful" : "Unsucessful"); + // myPrint("Andy bugged was " + result); + myPrint("Bugging andy"); + scriptBroadcast("quit"); + } + } + } + + delay(2000 + me.ping); // Wait for minions to die. + Config.PickRange = oldPickRange; // Reset to normal value + Pickit.pickItems(); + Config.MercWatch = true; + + !me.andariel && Pather.changeAct(); + + return true; } diff --git a/libs/SoloPlay/Scripts/anya.js b/libs/SoloPlay/Scripts/anya.js index b6716d12..8dbd6f80 100644 --- a/libs/SoloPlay/Scripts/anya.js +++ b/libs/SoloPlay/Scripts/anya.js @@ -6,120 +6,120 @@ */ function anya () { - Town.doChores(false, { fullChores: true }); - // double check after chores, as that calls Quest.unfinishedQuests - if (Misc.checkQuest(sdk.quest.id.PrisonofIce, sdk.quest.states.Completed)) return true; - - Town.goToTown(5); - myPrint("starting anya"); - - Pather.checkWP(sdk.areas.CrystalizedPassage, true) - ? Pather.useWaypoint(sdk.areas.CrystalizedPassage) - : Pather.getWP(sdk.areas.CrystalizedPassage); - - // Move to anya's platform - Precast.doPrecast(true); - Pather.clearToExit(sdk.areas.CrystalizedPassage, sdk.areas.FrozenRiver, Pather.useTeleport()); - - if (!Pather.moveToPresetObject(me.area, sdk.objects.FrozenAnyasPlatform, { callback: () => { - let fStein = Game.getMonster(getLocaleString(sdk.locale.monsters.Frozenstein)); - // let frozenanya = Game.getObject(sdk.objects.FrozenAnya); - return (fStein && fStein.distance < 30) /*&& /* (frozenanya && frozenanya.distance < 35) */; - } })) { - console.log("ÿc8Kolbot-SoloPlayÿc0: Failed to move to Anya"); - return false; - } - - // Making sure it's safe, needs work - let presetLoc = Game.getPresetObject(me.area, sdk.objects.FrozenAnyasPlatform).realCoords(); - let fStein = Game.getMonster(getLocaleString(sdk.locale.monsters.Frozenstein)); - - if (presetLoc && fStein && getDistance(presetLoc, fStein) < 15) { - // need to write a clearWhile function - Attack.clear(15, sdk.monsters.spectype.All, fStein); - } - - let frozenanya = Game.getObject(sdk.objects.FrozenAnya); - - if (!frozenanya) { - Pather.moveToEx(presetLoc.x, presetLoc.y, { callback: () => Game.getObject(sdk.objects.FrozenAnya) }); - frozenanya = Game.getObject(sdk.objects.FrozenAnya); - } - - /** - * Here we have issues sometimes - * Including a check for her unfreezing in case we already have malah's potion - * @todo - * - tele char can lure frozenstein away from anya as he can be hard to kill - * aggro the pack then move back until there isn't any monster around anya (note) we can only detect mobs around 40 yards of us - * then should use a static location behind anya as our destination to tele to - */ - if (frozenanya) { - if (me.sorceress && Skill.haveTK) { - Attack.getIntoPosition(frozenanya, 15, sdk.collision.LineOfSight, Pather.canTeleport(), true); - Packet.telekinesis(frozenanya); - } else { - Pather.moveToUnit(frozenanya); - Packet.entityInteract(frozenanya); - } - Misc.poll(() => getIsTalkingNPC() || frozenanya.mode, 2000, 50); - me.cancel() && me.cancel(); - } - - Town.npcInteract("malah"); - - /** - * Now this should prevent us from re-entering if we either failed to interact with anya in the first place - * or if we had malah's potion because this is our second attempt and we managed to unfreeze her - */ - if (me.getItem(sdk.quest.item.MalahsPotion)) { - console.log("Got potion, lets go unfreeze anya"); - - if (!Misc.poll(() => { - Pather.usePortal(sdk.areas.FrozenRiver, me.name); - return me.inArea(sdk.areas.FrozenRiver); - }, Time.seconds(30), 1000)) throw new Error("Anya quest failed - Failed to return to frozen river"); - - frozenanya = Game.getObject(sdk.objects.FrozenAnya); // Check again in case she's no longer there from first intereaction - - if (frozenanya) { - for (let i = 0; i < 3; i++) { - frozenanya.distance > 5 && Pather.moveToUnit(frozenanya, 1, 2); - Packet.entityInteract(frozenanya); - if (Misc.poll(() => frozenanya.mode, Time.seconds(2), 50)) { - me.cancel() && me.cancel(); - break; - } - if (getIsTalkingNPC()) { - // in case we failed to interact the first time this prevent us from crashing if her dialog is going - me.cancel() && me.cancel(); - } - Attack.clearPos(frozenanya.x, frozenanya.y, 15); - } - } - } - - /** - * Now lets handle completing the quest as we have freed anya - */ - if (Misc.checkQuest(sdk.quest.id.PrisonofIce, sdk.quest.states.ReqComplete)) { - /** - * Here we haven't talked to malah to recieve the scroll yet so lets do that - */ - if (!Misc.checkQuest(sdk.quest.id.PrisonofIce, 8/** Recieved the scroll */)) { - Town.npcInteract("malah"); - } - - /** - * Here we haven't talked to anya to open the red portal - */ - if (!Misc.checkQuest(sdk.quest.id.PrisonofIce, 9/** Talk to anya in town */)) { - Town.npcInteract("anya"); - } - - /** Handles using the scroll, no need to repeat the same code here */ - Quest.unfinishedQuests(); - } - - return !!Misc.checkQuest(sdk.quest.id.PrisonofIce, sdk.quest.states.Completed); + Town.doChores(false, { fullChores: true }); + // double check after chores, as that calls Quest.unfinishedQuests + if (Misc.checkQuest(sdk.quest.id.PrisonofIce, sdk.quest.states.Completed)) return true; + + Town.goToTown(5); + myPrint("starting anya"); + + Pather.checkWP(sdk.areas.CrystalizedPassage, true) + ? Pather.useWaypoint(sdk.areas.CrystalizedPassage) + : Pather.getWP(sdk.areas.CrystalizedPassage); + + // Move to anya's platform + Precast.doPrecast(true); + Pather.clearToExit(sdk.areas.CrystalizedPassage, sdk.areas.FrozenRiver, Pather.useTeleport()); + + if (!Pather.moveToPresetObject(me.area, sdk.objects.FrozenAnyasPlatform, { callback: () => { + let fStein = Game.getMonster(getLocaleString(sdk.locale.monsters.Frozenstein)); + // let frozenanya = Game.getObject(sdk.objects.FrozenAnya); + return (fStein && fStein.distance < 30) /*&& /* (frozenanya && frozenanya.distance < 35) */; + } })) { + console.log("ÿc8Kolbot-SoloPlayÿc0: Failed to move to Anya"); + return false; + } + + // Making sure it's safe, needs work + let presetLoc = Game.getPresetObject(me.area, sdk.objects.FrozenAnyasPlatform).realCoords(); + let fStein = Game.getMonster(getLocaleString(sdk.locale.monsters.Frozenstein)); + + if (presetLoc && fStein && getDistance(presetLoc, fStein) < 15) { + // need to write a clearWhile function + Attack.clear(15, sdk.monsters.spectype.All, fStein); + } + + let frozenanya = Game.getObject(sdk.objects.FrozenAnya); + + if (!frozenanya) { + Pather.moveToEx(presetLoc.x, presetLoc.y, { callback: () => Game.getObject(sdk.objects.FrozenAnya) }); + frozenanya = Game.getObject(sdk.objects.FrozenAnya); + } + + /** + * Here we have issues sometimes + * Including a check for her unfreezing in case we already have malah's potion + * @todo + * - tele char can lure frozenstein away from anya as he can be hard to kill + * aggro the pack then move back until there isn't any monster around anya (note) we can only detect mobs around 40 yards of us + * then should use a static location behind anya as our destination to tele to + */ + if (frozenanya) { + if (me.sorceress && Skill.haveTK) { + Attack.getIntoPosition(frozenanya, 15, sdk.collision.LineOfSight, Pather.canTeleport(), true); + Packet.telekinesis(frozenanya); + } else { + Pather.moveToUnit(frozenanya); + Packet.entityInteract(frozenanya); + } + Misc.poll(() => getIsTalkingNPC() || frozenanya.mode, 2000, 50); + me.cancel() && me.cancel(); + } + + Town.npcInteract("malah"); + + /** + * Now this should prevent us from re-entering if we either failed to interact with anya in the first place + * or if we had malah's potion because this is our second attempt and we managed to unfreeze her + */ + if (me.getItem(sdk.quest.item.MalahsPotion)) { + console.log("Got potion, lets go unfreeze anya"); + + if (!Misc.poll(() => { + Pather.usePortal(sdk.areas.FrozenRiver, me.name); + return me.inArea(sdk.areas.FrozenRiver); + }, Time.seconds(30), 1000)) throw new Error("Anya quest failed - Failed to return to frozen river"); + + frozenanya = Game.getObject(sdk.objects.FrozenAnya); // Check again in case she's no longer there from first intereaction + + if (frozenanya) { + for (let i = 0; i < 3; i++) { + frozenanya.distance > 5 && Pather.moveToUnit(frozenanya, 1, 2); + Packet.entityInteract(frozenanya); + if (Misc.poll(() => frozenanya.mode, Time.seconds(2), 50)) { + me.cancel() && me.cancel(); + break; + } + if (getIsTalkingNPC()) { + // in case we failed to interact the first time this prevent us from crashing if her dialog is going + me.cancel() && me.cancel(); + } + Attack.clearPos(frozenanya.x, frozenanya.y, 15); + } + } + } + + /** + * Now lets handle completing the quest as we have freed anya + */ + if (Misc.checkQuest(sdk.quest.id.PrisonofIce, sdk.quest.states.ReqComplete)) { + /** + * Here we haven't talked to malah to recieve the scroll yet so lets do that + */ + if (!Misc.checkQuest(sdk.quest.id.PrisonofIce, 8/** Recieved the scroll */)) { + Town.npcInteract("malah"); + } + + /** + * Here we haven't talked to anya to open the red portal + */ + if (!Misc.checkQuest(sdk.quest.id.PrisonofIce, 9/** Talk to anya in town */)) { + Town.npcInteract("anya"); + } + + /** Handles using the scroll, no need to repeat the same code here */ + Quest.unfinishedQuests(); + } + + return !!Misc.checkQuest(sdk.quest.id.PrisonofIce, sdk.quest.states.Completed); } diff --git a/libs/SoloPlay/Scripts/baal.js b/libs/SoloPlay/Scripts/baal.js index 4ef8ec8c..d987e79b 100644 --- a/libs/SoloPlay/Scripts/baal.js +++ b/libs/SoloPlay/Scripts/baal.js @@ -7,295 +7,295 @@ */ function baal () { - include("core/Common/Baal.js"); - Config.BossPriority = false; + include("core/Common/Baal.js"); + Config.BossPriority = false; - let decoyTick = 0; - let decoyDuration = (me.amazon ? Skill.getDuration(sdk.skills.Decoy) : 0); - - const preattack = function () { - switch (me.classid) { - case sdk.player.class.Amazon: - if (Skill.canUse(sdk.skills.Decoy)) { - let decoy = Game.getMonster(sdk.summons.Dopplezon); - - if (!decoy || (getTickCount() - decoyTick >= decoyDuration)) { - Skill.cast(sdk.skills.Decoy, sdk.skills.hand.Right, 15092, 5028); - decoyTick = getTickCount(); - } - } - - break; - case sdk.player.class.Sorceress: - if ([sdk.skills.Meteor, sdk.skills.Blizzard, sdk.skills.FrozenOrb].includes(Config.AttackSkill[1])) { - !me.skillDelay ? Skill.cast(Config.AttackSkill[1], sdk.skills.hand.Right, 15093, 5024) : delay(50); - } - - return true; - case sdk.player.class.Paladin: - if (Config.AttackSkill[3] !== sdk.skills.BlessedHammer) return false; - [15093, 5029].distance > 3 && Pather.moveTo(15093, 5029); - Config.AttackSkill[4] > 0 && Skill.setSkill(Config.AttackSkill[4], sdk.skills.hand.Right); - - Skill.cast(Config.AttackSkill[3], sdk.skills.hand.Left); - - return true; - case sdk.player.class.Druid: - if ([sdk.skills.Tornado, sdk.skills.Fissure, sdk.skills.Volcano].includes(Config.AttackSkill[3])) { - Skill.cast(Config.AttackSkill[3], sdk.skills.hand.Right, 15093, 5029); - - return true; - } - - break; - case sdk.player.class.Assassin: - if (Config.UseTraps) { - let check = ClassAttack.checkTraps({ x: 15093, y: 5029 }); - - if (check) { - ClassAttack.placeTraps({ x: 15093, y: 5029 }, 5); - - return true; - } - } - - if (Config.AttackSkill[3] === sdk.skills.ShockWeb) { - return Skill.cast(Config.AttackSkill[3], sdk.skills.hand.Right, 15094, 5028); - } - - break; - } - - return false; - }; - - const clearWaves = function () { - let boss; - let tick = getTickCount(); - - MainLoop: - while (true) { - if (!Game.getMonster(sdk.monsters.ThroneBaal)) return true; - - switch (Common.Baal.checkThrone()) { - case 1: - Attack.clearClassids(sdk.monsters.WarpedFallen, sdk.monsters.WarpedShaman) && (tick = getTickCount()); - - break; - case 2: - boss = Game.getMonster("Achmel the Cursed"); - - if (boss) { - if (!Attack.canAttack(boss)) throw new Error("Immune boss"); - if (me.paladin && me.hell && Check.currentBuild().caster) throw new Error("Too much effort for hammerdin"); - } - - Attack.clearClassids(sdk.monsters.BaalSubjectMummy, sdk.monsters.BaalColdMage) && (tick = getTickCount()); - - break; - case 3: - Attack.clearClassids(sdk.monsters.Council4) && (tick = getTickCount()); - Common.Baal.checkHydra() && (tick = getTickCount()); - - break; - case 4: - Attack.clearClassids(sdk.monsters.VenomLord2) && (tick = getTickCount()); - - break; - case 5: - boss = Game.getMonster("Lister the Tormentor"); - if (boss && !Attack.canAttack(boss)) throw new Error("Immune boss"); - - Attack.clearClassids(sdk.monsters.ListerTheTormenter, sdk.monsters.Minion1, sdk.monsters.Minion2); - - break MainLoop; - default: - if (getTickCount() - tick < Time.seconds(7)) { - if (Skill.canUse(sdk.skills.Cleansing) && me.getState(sdk.states.Poison)) { - Skill.setSkill(sdk.skills.Cleansing, sdk.skills.hand.Right); - Misc.poll(() => { - if (Config.AttackSkill[3] === sdk.skills.BlessedHammer) { - Skill.cast(Config.AttackSkill[3], sdk.skills.hand.Left); - } - return !me.getState(sdk.states.Poison) || me.mode === sdk.player.mode.GettingHit; - }, Time.seconds(3), 100); - } - } - - if (getTickCount() - tick > Time.seconds(20)) { - tick = getTickCount(); - Common.Baal.clearThrone(); - } - - if (!preattack()) { - delay(50); - } - - break; - } - - switch (me.classid) { - case sdk.player.class.Amazon: - case sdk.player.class.Sorceress: - case sdk.player.class.Necromancer: - case sdk.player.class.Assassin: - [15116, 5026].distance > 3 && Pather.moveTo(15116, 5026); - - break; - case sdk.player.class.Paladin: - if (Config.AttackSkill[3] === sdk.skills.BlessedHammer) { - [15094, 5029].distance > 3 && Pather.moveTo(15094, 5029); - - break; - } - // eslint-disable-next-line no-fallthrough - case sdk.player.class.Druid: - if ([sdk.skills.Fissure, sdk.skills.Volcano].includes(Config.AttackSkill[3])) { - [15116, 5026].distance > 3 && Pather.moveTo(15116, 5026); - - break; - } - - if (Config.AttackSkill[3] === sdk.skills.Tornado) { - [15094, 5029].distance > 3 && Pather.moveTo(15106, 5041); - - break; - } - // eslint-disable-next-line no-fallthrough - case sdk.player.class.Barbarian: - [15101, 5045].distance > 3 && Pather.moveTo(15101, 5045); - - break; - } - - // If we've been in the throne for 30 minutes that's way too long - if (getTickCount() - totalTick > Time.minutes(30)) return false; - - delay(10); - } - - return true; - }; - - const unSafeCheck = function (soulAmount = 0, totalAmount = 0) { - let count = 0; - let soul = Game.getMonster(sdk.monsters.BurningSoul1); - - if (soul) { - do { - if (getDistance(me, soul) < 45) { - count += 1; - } - } while (soul.getNext()); - } - - if (count > soulAmount) return true; - - let monster = Game.getMonster(); - - if (monster) { - do { - if (!monster.getParent() && monster.classid !== sdk.monsters.BurningSoul1 && getDistance(me, monster) < 45) { - count += 1; - } - } while (monster.getNext()); - } - - return count > totalAmount; - }; - - const canClearThrone = function () { - Pather.moveTo(15094, 5029); - let [canAttack, cantAttack] = [[], []]; - getUnits(sdk.unittype.Monster).filter(i => !!i && i.attackable).forEach(mon => { - Attack.canAttack(mon) ? canAttack.push(mon) : cantAttack.push(mon); - }); - - console.debug("Can Attack: " + canAttack.length, " Can't Attack: " + cantAttack.length); - - return ((!canAttack.length && !cantAttack.length) || (canAttack.length > cantAttack.length)); - }; - - // START - Town.doChores(false, { fullChores: true }); - myPrint("starting baal"); - - Pather.checkWP(sdk.areas.WorldstoneLvl2, true) ? Pather.useWaypoint(sdk.areas.WorldstoneLvl2) : Pather.getWP(sdk.areas.WorldstoneLvl2, true); - Precast.doPrecast(true); - const oldCPRange = Config.ClearPath.Range; - const canTele = Pather.canTeleport(); - try { - canTele && (Config.ClearPath.Range = 0); - canTele - ? Pather.moveToExit([sdk.areas.WorldstoneLvl3, sdk.areas.ThroneofDestruction], true, false) - : (Pather.clearToExit(sdk.areas.WorldstoneLvl2, sdk.areas.WorldstoneLvl3, true) && Pather.clearToExit(sdk.areas.WorldstoneLvl3, sdk.areas.ThroneofDestruction, true)); - } finally { - oldCPRange !== Config.ClearPath.Range && (Config.ClearPath.Range = oldCPRange); - } - - // Enter throne room - const dollQuit = me.hardcore; - Pather.moveToEx(15095, 5029, { callback: () => { - if (dollQuit && Game.getMonster(sdk.monsters.SoulKiller)) { - throw new ScriptError("Unsafe for hardcore, dolls found"); - } - } }); - Pather.moveTo(15113, 5040, 5); - - let totalTick = getTickCount(); - - // souls hurt - if (unSafeCheck(8, 20) && me.lightRes < 70 && me.nightmare) throw new Error("Unsafe to clear"); - if (!canClearThrone()) throw new Error("Too many mobs I can't attack"); - - try { - if (((me.hell && me.paladin && !Attack.auradin) || me.barbarian || me.gold < 25000 || (!me.baal && SetUp.finalBuild !== "Bumper"))) { - Messaging.sendToScript(SoloEvents.filePath, "addBaalEvent"); - } - - Attack.clear(15); - Common.Baal.clearThrone(); - - if (me.coldRes < 75 || me.poisonRes < 75) { - Town.doChores(null, { thawing: me.coldRes < 75, antidote: me.poisonRes < 75 }); - Town.move("portalspot"); - Pather.usePortal(sdk.areas.ThroneofDestruction, me.name); - } - - if (!clearWaves()) throw new Error("Can't clear waves"); - - Common.Baal.clearThrone(); // double check - Pather.moveTo(15094, me.paladin ? 5029 : 5038); - Pickit.pickItems(); - Pather.moveTo(15094, me.paladin ? 5029 : 5038); - Pickit.pickItems(); - Pather.moveTo(15090, 5008); - delay(2500 + me.ping); - Precast.doPrecast(true); - - if (SetUp.finalBuild === "Bumper") throw new Error("BUMPER"); - - if (Misc.poll(() => me.getMobCount(15) > 1)) { - clearWaves(); - } - - Config.BossPriority = true; - - if (Common.Baal.killBaal()) { - // Grab static gold - NTIP.addLine("[name] == gold # [gold] >= 25"); - Pather.moveTo(15072, 5894); - Pickit.pickItems(); - Pather.moveTo(15095, 5881); - Pickit.pickItems(); - } else { - console.log("ÿc8Kolbot-SoloPlayÿc0: Couldn't access portal."); - } - } catch (e) { - console.warn(e.message ? e.message : e); - } finally { - Messaging.sendToScript(SoloEvents.filePath, "removeBaalEvent"); - } - - return true; + let decoyTick = 0; + let decoyDuration = (me.amazon ? Skill.getDuration(sdk.skills.Decoy) : 0); + + const preattack = function () { + switch (me.classid) { + case sdk.player.class.Amazon: + if (Skill.canUse(sdk.skills.Decoy)) { + let decoy = Game.getMonster(sdk.summons.Dopplezon); + + if (!decoy || (getTickCount() - decoyTick >= decoyDuration)) { + Skill.cast(sdk.skills.Decoy, sdk.skills.hand.Right, 15092, 5028); + decoyTick = getTickCount(); + } + } + + break; + case sdk.player.class.Sorceress: + if ([sdk.skills.Meteor, sdk.skills.Blizzard, sdk.skills.FrozenOrb].includes(Config.AttackSkill[1])) { + !me.skillDelay ? Skill.cast(Config.AttackSkill[1], sdk.skills.hand.Right, 15093, 5024) : delay(50); + } + + return true; + case sdk.player.class.Paladin: + if (Config.AttackSkill[3] !== sdk.skills.BlessedHammer) return false; + [15093, 5029].distance > 3 && Pather.moveTo(15093, 5029); + Config.AttackSkill[4] > 0 && Skill.setSkill(Config.AttackSkill[4], sdk.skills.hand.Right); + + Skill.cast(Config.AttackSkill[3], sdk.skills.hand.Left); + + return true; + case sdk.player.class.Druid: + if ([sdk.skills.Tornado, sdk.skills.Fissure, sdk.skills.Volcano].includes(Config.AttackSkill[3])) { + Skill.cast(Config.AttackSkill[3], sdk.skills.hand.Right, 15093, 5029); + + return true; + } + + break; + case sdk.player.class.Assassin: + if (Config.UseTraps) { + let check = ClassAttack.checkTraps({ x: 15093, y: 5029 }); + + if (check) { + ClassAttack.placeTraps({ x: 15093, y: 5029 }, 5); + + return true; + } + } + + if (Config.AttackSkill[3] === sdk.skills.ShockWeb) { + return Skill.cast(Config.AttackSkill[3], sdk.skills.hand.Right, 15094, 5028); + } + + break; + } + + return false; + }; + + const clearWaves = function () { + let boss; + let tick = getTickCount(); + + MainLoop: + while (true) { + if (!Game.getMonster(sdk.monsters.ThroneBaal)) return true; + + switch (Common.Baal.checkThrone()) { + case 1: + Attack.clearClassids(sdk.monsters.WarpedFallen, sdk.monsters.WarpedShaman) && (tick = getTickCount()); + + break; + case 2: + boss = Game.getMonster("Achmel the Cursed"); + + if (boss) { + if (!Attack.canAttack(boss)) throw new Error("Immune boss"); + if (me.paladin && me.hell && Check.currentBuild().caster) throw new Error("Too much effort for hammerdin"); + } + + Attack.clearClassids(sdk.monsters.BaalSubjectMummy, sdk.monsters.BaalColdMage) && (tick = getTickCount()); + + break; + case 3: + Attack.clearClassids(sdk.monsters.Council4) && (tick = getTickCount()); + Common.Baal.checkHydra() && (tick = getTickCount()); + + break; + case 4: + Attack.clearClassids(sdk.monsters.VenomLord2) && (tick = getTickCount()); + + break; + case 5: + boss = Game.getMonster("Lister the Tormentor"); + if (boss && !Attack.canAttack(boss)) throw new Error("Immune boss"); + + Attack.clearClassids(sdk.monsters.ListerTheTormenter, sdk.monsters.Minion1, sdk.monsters.Minion2); + + break MainLoop; + default: + if (getTickCount() - tick < Time.seconds(7)) { + if (Skill.canUse(sdk.skills.Cleansing) && me.getState(sdk.states.Poison)) { + Skill.setSkill(sdk.skills.Cleansing, sdk.skills.hand.Right); + Misc.poll(() => { + if (Config.AttackSkill[3] === sdk.skills.BlessedHammer) { + Skill.cast(Config.AttackSkill[3], sdk.skills.hand.Left); + } + return !me.getState(sdk.states.Poison) || me.mode === sdk.player.mode.GettingHit; + }, Time.seconds(3), 100); + } + } + + if (getTickCount() - tick > Time.seconds(20)) { + tick = getTickCount(); + Common.Baal.clearThrone(); + } + + if (!preattack()) { + delay(50); + } + + break; + } + + switch (me.classid) { + case sdk.player.class.Amazon: + case sdk.player.class.Sorceress: + case sdk.player.class.Necromancer: + case sdk.player.class.Assassin: + [15116, 5026].distance > 3 && Pather.moveTo(15116, 5026); + + break; + case sdk.player.class.Paladin: + if (Config.AttackSkill[3] === sdk.skills.BlessedHammer) { + [15094, 5029].distance > 3 && Pather.moveTo(15094, 5029); + + break; + } + // eslint-disable-next-line no-fallthrough + case sdk.player.class.Druid: + if ([sdk.skills.Fissure, sdk.skills.Volcano].includes(Config.AttackSkill[3])) { + [15116, 5026].distance > 3 && Pather.moveTo(15116, 5026); + + break; + } + + if (Config.AttackSkill[3] === sdk.skills.Tornado) { + [15094, 5029].distance > 3 && Pather.moveTo(15106, 5041); + + break; + } + // eslint-disable-next-line no-fallthrough + case sdk.player.class.Barbarian: + [15101, 5045].distance > 3 && Pather.moveTo(15101, 5045); + + break; + } + + // If we've been in the throne for 30 minutes that's way too long + if (getTickCount() - totalTick > Time.minutes(30)) return false; + + delay(10); + } + + return true; + }; + + const unSafeCheck = function (soulAmount = 0, totalAmount = 0) { + let count = 0; + let soul = Game.getMonster(sdk.monsters.BurningSoul1); + + if (soul) { + do { + if (getDistance(me, soul) < 45) { + count += 1; + } + } while (soul.getNext()); + } + + if (count > soulAmount) return true; + + let monster = Game.getMonster(); + + if (monster) { + do { + if (!monster.getParent() && monster.classid !== sdk.monsters.BurningSoul1 && getDistance(me, monster) < 45) { + count += 1; + } + } while (monster.getNext()); + } + + return count > totalAmount; + }; + + const canClearThrone = function () { + Pather.moveTo(15094, 5029); + let [canAttack, cantAttack] = [[], []]; + getUnits(sdk.unittype.Monster).filter(i => !!i && i.attackable).forEach(mon => { + Attack.canAttack(mon) ? canAttack.push(mon) : cantAttack.push(mon); + }); + + console.debug("Can Attack: " + canAttack.length, " Can't Attack: " + cantAttack.length); + + return ((!canAttack.length && !cantAttack.length) || (canAttack.length > cantAttack.length)); + }; + + // START + Town.doChores(false, { fullChores: true }); + myPrint("starting baal"); + + Pather.checkWP(sdk.areas.WorldstoneLvl2, true) ? Pather.useWaypoint(sdk.areas.WorldstoneLvl2) : Pather.getWP(sdk.areas.WorldstoneLvl2, true); + Precast.doPrecast(true); + const oldCPRange = Config.ClearPath.Range; + const canTele = Pather.canTeleport(); + try { + canTele && (Config.ClearPath.Range = 0); + canTele + ? Pather.moveToExit([sdk.areas.WorldstoneLvl3, sdk.areas.ThroneofDestruction], true, false) + : (Pather.clearToExit(sdk.areas.WorldstoneLvl2, sdk.areas.WorldstoneLvl3, true) && Pather.clearToExit(sdk.areas.WorldstoneLvl3, sdk.areas.ThroneofDestruction, true)); + } finally { + oldCPRange !== Config.ClearPath.Range && (Config.ClearPath.Range = oldCPRange); + } + + // Enter throne room + const dollQuit = me.hardcore; + Pather.moveToEx(15095, 5029, { callback: () => { + if (dollQuit && Game.getMonster(sdk.monsters.SoulKiller)) { + throw new ScriptError("Unsafe for hardcore, dolls found"); + } + } }); + Pather.moveTo(15113, 5040, 5); + + let totalTick = getTickCount(); + + // souls hurt + if (unSafeCheck(8, 20) && me.lightRes < 70 && me.nightmare) throw new Error("Unsafe to clear"); + if (!canClearThrone()) throw new Error("Too many mobs I can't attack"); + + try { + if (((me.hell && me.paladin && !Attack.auradin) || me.barbarian || me.gold < 25000 || (!me.baal && SetUp.finalBuild !== "Bumper"))) { + Messaging.sendToScript(SoloEvents.filePath, "addBaalEvent"); + } + + Attack.clear(15); + Common.Baal.clearThrone(); + + if (me.coldRes < 75 || me.poisonRes < 75) { + Town.doChores(null, { thawing: me.coldRes < 75, antidote: me.poisonRes < 75 }); + Town.move("portalspot"); + Pather.usePortal(sdk.areas.ThroneofDestruction, me.name); + } + + if (!clearWaves()) throw new Error("Can't clear waves"); + + Common.Baal.clearThrone(); // double check + Pather.moveTo(15094, me.paladin ? 5029 : 5038); + Pickit.pickItems(); + Pather.moveTo(15094, me.paladin ? 5029 : 5038); + Pickit.pickItems(); + Pather.moveTo(15090, 5008); + delay(2500 + me.ping); + Precast.doPrecast(true); + + if (SetUp.finalBuild === "Bumper") throw new Error("BUMPER"); + + if (Misc.poll(() => me.getMobCount(15) > 1)) { + clearWaves(); + } + + Config.BossPriority = true; + + if (Common.Baal.killBaal()) { + // Grab static gold + NTIP.addLine("[name] == gold # [gold] >= 25"); + Pather.moveTo(15072, 5894); + Pickit.pickItems(); + Pather.moveTo(15095, 5881); + Pickit.pickItems(); + } else { + console.log("ÿc8Kolbot-SoloPlayÿc0: Couldn't access portal."); + } + } catch (e) { + console.warn(e.message ? e.message : e); + } finally { + Messaging.sendToScript(SoloEvents.filePath, "removeBaalEvent"); + } + + return true; } diff --git a/libs/SoloPlay/Scripts/beetleburst.js b/libs/SoloPlay/Scripts/beetleburst.js index 09dcac3c..8c7e93c7 100644 --- a/libs/SoloPlay/Scripts/beetleburst.js +++ b/libs/SoloPlay/Scripts/beetleburst.js @@ -6,13 +6,13 @@ */ function beetleburst () { - Town.doChores(); - myPrint("ÿc8Kolbot-SoloPlayÿc0: starting beetleburst"); + Town.doChores(); + myPrint("ÿc8Kolbot-SoloPlayÿc0: starting beetleburst"); - Pather.checkWP(sdk.areas.FarOasis, true) ? Pather.useWaypoint(sdk.areas.FarOasis) : Pather.getWP(sdk.areas.FarOasis); - Precast.doPrecast(true); - Pather.moveToPreset(me.area, sdk.unittype.Monster, sdk.monsters.preset.Beetleburst); - Attack.clear(15, 0, getLocaleString(sdk.locale.monsters.Beetleburst)); + Pather.checkWP(sdk.areas.FarOasis, true) ? Pather.useWaypoint(sdk.areas.FarOasis) : Pather.getWP(sdk.areas.FarOasis); + Precast.doPrecast(true); + Pather.moveToPreset(me.area, sdk.unittype.Monster, sdk.monsters.preset.Beetleburst); + Attack.clear(15, 0, getLocaleString(sdk.locale.monsters.Beetleburst)); - return true; + return true; } diff --git a/libs/SoloPlay/Scripts/bishibosh.js b/libs/SoloPlay/Scripts/bishibosh.js index 16101d3f..5f90cdd2 100644 --- a/libs/SoloPlay/Scripts/bishibosh.js +++ b/libs/SoloPlay/Scripts/bishibosh.js @@ -6,15 +6,15 @@ */ function bishibosh () { - if (!me.inArea(sdk.areas.ColdPlains)) { - Town.doChores(); - Pather.useWaypoint(sdk.areas.ColdPlains); - } - Precast.doPrecast(true); + if (!me.inArea(sdk.areas.ColdPlains)) { + Town.doChores(); + Pather.useWaypoint(sdk.areas.ColdPlains); + } + Precast.doPrecast(true); - Pather.moveToPreset(sdk.areas.ColdPlains, sdk.unittype.Monster, sdk.monsters.preset.Bishibosh); - Attack.clear(15, 0, getLocaleString(sdk.locale.monsters.Bishibosh)); - Pickit.pickItems(); + Pather.moveToPreset(sdk.areas.ColdPlains, sdk.unittype.Monster, sdk.monsters.preset.Bishibosh); + Attack.clear(15, 0, getLocaleString(sdk.locale.monsters.Bishibosh)); + Pickit.pickItems(); - return true; + return true; } diff --git a/libs/SoloPlay/Scripts/bloodraven.js b/libs/SoloPlay/Scripts/bloodraven.js index abaf1f7f..e30ee9e2 100644 --- a/libs/SoloPlay/Scripts/bloodraven.js +++ b/libs/SoloPlay/Scripts/bloodraven.js @@ -6,75 +6,75 @@ */ function bloodraven () { - Town.doChores(false, { fullChores: true }); - myPrint("ÿc8Kolbot-SoloPlayÿc0: starting blood raven"); + Town.doChores(false, { fullChores: true }); + myPrint("ÿc8Kolbot-SoloPlayÿc0: starting blood raven"); - if (!Pather.checkWP(sdk.areas.StonyField, true)) { - Pather.getWP(sdk.areas.StonyField); - if (me.charlvl < 6) { - Pather.moveToPreset(sdk.areas.StonyField, sdk.unittype.Monster, sdk.monsters.preset.Rakanishu, 10, 10, false, true); - Attack.killTarget(getLocaleString(sdk.locale.monsters.Rakanishu)); - } - } else { - if (me.hell && Pather.canTeleport() && me.charlvl < 74/*xp penalty makes this not worth it after 74*/) { - Misc.getExpShrine([sdk.areas.StonyField, sdk.areas.ColdPlains, sdk.areas.DarkWood, sdk.areas.BloodMoor]); - if (!me.inArea(sdk.areas.ColdPlains)) { - Town.goToTown() && Pather.useWaypoint(sdk.areas.ColdPlains); - } - } else { - Pather.useWaypoint(sdk.areas.ColdPlains); - if (me.charlvl < 5) { - SoloIndex.doneList.includes("cave") ? Attack.clearLevelUntilLevel(5) : Loader.skipTown.push("cave") && Loader.runScript("cave"); - } - } - } + if (!Pather.checkWP(sdk.areas.StonyField, true)) { + Pather.getWP(sdk.areas.StonyField); + if (me.charlvl < 6) { + Pather.moveToPreset(sdk.areas.StonyField, sdk.unittype.Monster, sdk.monsters.preset.Rakanishu, 10, 10, false, true); + Attack.killTarget(getLocaleString(sdk.locale.monsters.Rakanishu)); + } + } else { + if (me.hell && Pather.canTeleport() && me.charlvl < 74/*xp penalty makes this not worth it after 74*/) { + Misc.getExpShrine([sdk.areas.StonyField, sdk.areas.ColdPlains, sdk.areas.DarkWood, sdk.areas.BloodMoor]); + if (!me.inArea(sdk.areas.ColdPlains)) { + Town.goToTown() && Pather.useWaypoint(sdk.areas.ColdPlains); + } + } else { + Pather.useWaypoint(sdk.areas.ColdPlains); + if (me.charlvl < 5) { + SoloIndex.doneList.includes("cave") ? Attack.clearLevelUntilLevel(5) : Loader.skipTown.push("cave") && Loader.runScript("cave"); + } + } + } - Precast.doPrecast(true); + Precast.doPrecast(true); - me.overhead("blood raven"); - Pather.journeyTo(sdk.areas.BurialGrounds); - me.sorceress && !me.normal - ? Pather.moveNearPreset(sdk.areas.BurialGrounds, sdk.unittype.Monster, sdk.monsters.preset.BloodRaven, 20) - : Pather.moveToPreset(sdk.areas.BurialGrounds, sdk.unittype.Monster, sdk.monsters.preset.BloodRaven); - Attack.killTarget("Blood Raven"); - Pickit.pickItems(); + me.overhead("blood raven"); + Pather.journeyTo(sdk.areas.BurialGrounds); + me.sorceress && !me.normal + ? Pather.moveNearPreset(sdk.areas.BurialGrounds, sdk.unittype.Monster, sdk.monsters.preset.BloodRaven, 20) + : Pather.moveToPreset(sdk.areas.BurialGrounds, sdk.unittype.Monster, sdk.monsters.preset.BloodRaven); + Attack.killTarget("Blood Raven"); + Pickit.pickItems(); - if (me.normal && !me.bloodraven && me.canTpToTown()) { - Town.npcInteract("kashya"); - return true; - } else if (me.paladin && Check.currentBuild().caster && !Pather.canTeleport()) { - return true; - } + if (me.normal && !me.bloodraven && me.canTpToTown()) { + Town.npcInteract("kashya"); + return true; + } else if (me.paladin && Check.currentBuild().caster && !Pather.canTeleport()) { + return true; + } - myPrint("blood raven :: starting mausoleum"); + myPrint("blood raven :: starting mausoleum"); - if (!Pather.moveToExit([sdk.areas.BurialGrounds, sdk.areas.Mausoleum], true)) { - console.log("ÿc8Kolbot-SoloPlayÿc0: Failed to move to Mausoleum"); - } + if (!Pather.moveToExit([sdk.areas.BurialGrounds, sdk.areas.Mausoleum], true)) { + console.log("ÿc8Kolbot-SoloPlayÿc0: Failed to move to Mausoleum"); + } - me.inArea(sdk.areas.Mausoleum) && Attack.clearLevel(); + me.inArea(sdk.areas.Mausoleum) && Attack.clearLevel(); - if (me.hell) { - switch (me.gametype) { - case sdk.game.gametype.Classic: - if (me.accessToAct(3)) { - return true; - } + if (me.hell) { + switch (me.gametype) { + case sdk.game.gametype.Classic: + if (me.accessToAct(3)) { + return true; + } - break; - case sdk.game.gametype.Expansion: - if ((me.charlvl < 80 || me.charlvl > 85) && !((me.sorceress || me.druid || me.assassin) && me.equipped.get(sdk.body.RightArm).tier < 100000)) { - return true; - } + break; + case sdk.game.gametype.Expansion: + if ((me.charlvl < 80 || me.charlvl > 85) && !((me.sorceress || me.druid || me.assassin) && me.equipped.get(sdk.body.RightArm).tier < 100000)) { + return true; + } - break; - } - } else { - return true; - } + break; + } + } else { + return true; + } - myPrint("blood raven :: starting crypt"); - Pather.journeyTo(sdk.areas.Crypt) && Attack.clearLevel(); - - return true; + myPrint("blood raven :: starting crypt"); + Pather.journeyTo(sdk.areas.Crypt) && Attack.clearLevel(); + + return true; } diff --git a/libs/SoloPlay/Scripts/boneash.js b/libs/SoloPlay/Scripts/boneash.js index 1e3003ee..125c7e89 100644 --- a/libs/SoloPlay/Scripts/boneash.js +++ b/libs/SoloPlay/Scripts/boneash.js @@ -6,14 +6,14 @@ */ function boneash () { - Town.doChores(false, { fullChores: true }); - myPrint("starting boneash"); + Town.doChores(false, { fullChores: true }); + myPrint("starting boneash"); - Pather.checkWP(sdk.areas.InnerCloister, true) ? Pather.useWaypoint(sdk.areas.InnerCloister) : Pather.getWP(sdk.areas.InnerCloister); - Precast.doPrecast(true); - Pather.clearToExit(sdk.areas.InnerCloister, sdk.areas.Cathedral, true); - Pather.moveTo(20047, 4898); - Attack.killTarget(getLocaleString(sdk.locale.monsters.BoneAsh)); + Pather.checkWP(sdk.areas.InnerCloister, true) ? Pather.useWaypoint(sdk.areas.InnerCloister) : Pather.getWP(sdk.areas.InnerCloister); + Precast.doPrecast(true); + Pather.clearToExit(sdk.areas.InnerCloister, sdk.areas.Cathedral, true); + Pather.moveTo(20047, 4898); + Attack.killTarget(getLocaleString(sdk.locale.monsters.BoneAsh)); - return true; + return true; } diff --git a/libs/SoloPlay/Scripts/brain.js b/libs/SoloPlay/Scripts/brain.js index 30150723..77c3b0bd 100644 --- a/libs/SoloPlay/Scripts/brain.js +++ b/libs/SoloPlay/Scripts/brain.js @@ -6,22 +6,22 @@ */ function brain () { - Town.doChores(false, { fullChores: true }); - myPrint("starting brain"); + Town.doChores(false, { fullChores: true }); + myPrint("starting brain"); - Pather.checkWP(sdk.areas.FlayerJungle, true) ? Pather.useWaypoint(sdk.areas.FlayerJungle) : Pather.getWP(sdk.areas.FlayerJungle); - Precast.doPrecast(true); - Pather.clearToExit(sdk.areas.FlayerJungle, sdk.areas.FlayerDungeonLvl1, Pather.useTeleport()); - Pather.clearToExit(sdk.areas.FlayerDungeonLvl1, sdk.areas.FlayerDungeonLvl2, Pather.useTeleport()); - Pather.clearToExit(sdk.areas.FlayerDungeonLvl2, sdk.areas.FlayerDungeonLvl3, Pather.useTeleport()); + Pather.checkWP(sdk.areas.FlayerJungle, true) ? Pather.useWaypoint(sdk.areas.FlayerJungle) : Pather.getWP(sdk.areas.FlayerJungle); + Precast.doPrecast(true); + Pather.clearToExit(sdk.areas.FlayerJungle, sdk.areas.FlayerDungeonLvl1, Pather.useTeleport()); + Pather.clearToExit(sdk.areas.FlayerDungeonLvl1, sdk.areas.FlayerDungeonLvl2, Pather.useTeleport()); + Pather.clearToExit(sdk.areas.FlayerDungeonLvl2, sdk.areas.FlayerDungeonLvl3, Pather.useTeleport()); - if (!Pather.moveToPreset(me.area, sdk.unittype.Object, sdk.quest.chest.KhalimsBrainChest)) { - console.log("ÿc8Kolbot-SoloPlayÿc0: Failed to get the Brain"); - } + if (!Pather.moveToPreset(me.area, sdk.unittype.Object, sdk.quest.chest.KhalimsBrainChest)) { + console.log("ÿc8Kolbot-SoloPlayÿc0: Failed to get the Brain"); + } - Attack.clear(0x7); - Quest.collectItem(sdk.items.quest.KhalimsBrain, sdk.quest.chest.KhalimsBrainChest); - Quest.stashItem(sdk.items.quest.KhalimsBrain); + Attack.clear(0x7); + Quest.collectItem(sdk.items.quest.KhalimsBrain, sdk.quest.chest.KhalimsBrainChest); + Quest.stashItem(sdk.items.quest.KhalimsBrain); - return true; + return true; } diff --git a/libs/SoloPlay/Scripts/cave.js b/libs/SoloPlay/Scripts/cave.js index 1c8e2162..4e54006e 100644 --- a/libs/SoloPlay/Scripts/cave.js +++ b/libs/SoloPlay/Scripts/cave.js @@ -6,56 +6,56 @@ */ function cave () { - !me.inArea(sdk.areas.ColdPlains) && Pather.journeyTo(sdk.areas.ColdPlains); - Pather.moveToExit([sdk.areas.CaveLvl1, sdk.areas.CaveLvl2], true, true); + !me.inArea(sdk.areas.ColdPlains) && Pather.journeyTo(sdk.areas.ColdPlains); + Pather.moveToExit([sdk.areas.CaveLvl1, sdk.areas.CaveLvl2], true, true); - // coords from sonic - const clearCoords = [ - { "x": 7549, "y": 12554, "radius": 10 }, - { "x": 7560, "y": 12551, "radius": 10 }, - { "x": 7573, "y": 12550, "radius": 10 }, - { "x": 7576, "y": 12563, "radius": 10 }, - { "x": 7586, "y": 12564, "radius": 10 }, - { "x": 7596, "y": 12567, "radius": 10 }, - { "x": 7596, "y": 12578, "radius": 10 }, - { "x": 7606, "y": 12559, "radius": 10 }, - { "x": 7612, "y": 12549, "radius": 10 }, - { "x": 7611, "y": 12540, "radius": 10 }, - { "x": 7608, "y": 12528, "radius": 10 }, - { "x": 7595, "y": 12529, "radius": 10 }, - { "x": 7588, "y": 12519, "radius": 10 }, - { "x": 7574, "y": 12520, "radius": 10 }, - { "x": 7564, "y": 12523, "radius": 10 }, - { "x": 7568, "y": 12567, "radius": 10 }, - { "x": 7565, "y": 12574, "radius": 10 }, - { "x": 7560, "y": 12583, "radius": 10 }, - { "x": 7554, "y": 12578, "radius": 10 }, - { "x": 7546, "y": 12573, "radius": 10 }, - { "x": 7537, "y": 12573, "radius": 10 }, - { "x": 7528, "y": 12574, "radius": 10 }, - { "x": 7519, "y": 12575, "radius": 10 }, - { "x": 7510, "y": 12566, "radius": 10 }, - { "x": 7510, "y": 12584, "radius": 10 }, - { "x": 7514, "y": 12593, "radius": 10 }, - { "x": 7521, "y": 12595, "radius": 10 }, - { "x": 7526, "y": 12600, "radius": 10 }, - { "x": 7525, "y": 12606, "radius": 10 }, - { "x": 7535, "y": 12596, "radius": 10 }, - { "x": 7543, "y": 12596, "radius": 10 }, - { "x": 7550, "y": 12596, "radius": 10 }, - { "x": 7557, "y": 12595, "radius": 10 }, - { "x": 7556, "y": 12605, "radius": 10 }, - { "x": 7556, "y": 12611, "radius": 10 }, - { "x": 7566, "y": 12608, "radius": 10 }, - { "x": 7580, "y": 12613, "radius": 10 }, - { "x": 7589, "y": 12610, "radius": 10 }, - { "x": 7594, "y": 12601, "radius": 10 }, - { "x": 7600, "y": 12601, "radius": 10 } - ]; + // coords from sonic + const clearCoords = [ + { "x": 7549, "y": 12554, "radius": 10 }, + { "x": 7560, "y": 12551, "radius": 10 }, + { "x": 7573, "y": 12550, "radius": 10 }, + { "x": 7576, "y": 12563, "radius": 10 }, + { "x": 7586, "y": 12564, "radius": 10 }, + { "x": 7596, "y": 12567, "radius": 10 }, + { "x": 7596, "y": 12578, "radius": 10 }, + { "x": 7606, "y": 12559, "radius": 10 }, + { "x": 7612, "y": 12549, "radius": 10 }, + { "x": 7611, "y": 12540, "radius": 10 }, + { "x": 7608, "y": 12528, "radius": 10 }, + { "x": 7595, "y": 12529, "radius": 10 }, + { "x": 7588, "y": 12519, "radius": 10 }, + { "x": 7574, "y": 12520, "radius": 10 }, + { "x": 7564, "y": 12523, "radius": 10 }, + { "x": 7568, "y": 12567, "radius": 10 }, + { "x": 7565, "y": 12574, "radius": 10 }, + { "x": 7560, "y": 12583, "radius": 10 }, + { "x": 7554, "y": 12578, "radius": 10 }, + { "x": 7546, "y": 12573, "radius": 10 }, + { "x": 7537, "y": 12573, "radius": 10 }, + { "x": 7528, "y": 12574, "radius": 10 }, + { "x": 7519, "y": 12575, "radius": 10 }, + { "x": 7510, "y": 12566, "radius": 10 }, + { "x": 7510, "y": 12584, "radius": 10 }, + { "x": 7514, "y": 12593, "radius": 10 }, + { "x": 7521, "y": 12595, "radius": 10 }, + { "x": 7526, "y": 12600, "radius": 10 }, + { "x": 7525, "y": 12606, "radius": 10 }, + { "x": 7535, "y": 12596, "radius": 10 }, + { "x": 7543, "y": 12596, "radius": 10 }, + { "x": 7550, "y": 12596, "radius": 10 }, + { "x": 7557, "y": 12595, "radius": 10 }, + { "x": 7556, "y": 12605, "radius": 10 }, + { "x": 7556, "y": 12611, "radius": 10 }, + { "x": 7566, "y": 12608, "radius": 10 }, + { "x": 7580, "y": 12613, "radius": 10 }, + { "x": 7589, "y": 12610, "radius": 10 }, + { "x": 7594, "y": 12601, "radius": 10 }, + { "x": 7600, "y": 12601, "radius": 10 } + ]; - !me.inArea(sdk.areas.CaveLvl2) && Pather.journeyTo(sdk.areas.CaveLvl2); - Attack.clearCoordList(clearCoords, 10); - Town.goToTown(); + !me.inArea(sdk.areas.CaveLvl2) && Pather.journeyTo(sdk.areas.CaveLvl2); + Attack.clearCoordList(clearCoords, 10); + Town.goToTown(); - return true; + return true; } diff --git a/libs/SoloPlay/Scripts/corpsefire.js b/libs/SoloPlay/Scripts/corpsefire.js index 850f6cc1..26dad08d 100644 --- a/libs/SoloPlay/Scripts/corpsefire.js +++ b/libs/SoloPlay/Scripts/corpsefire.js @@ -6,14 +6,14 @@ */ function corpsefire() { - myPrint("starting corpsefire"); - Town.doChores(null, { thawing: me.coldRes < 75, antidote: me.poisonRes < 75 }); + myPrint("starting corpsefire"); + Town.doChores(null, { thawing: me.coldRes < 75, antidote: me.poisonRes < 75 }); - Pather.checkWP(sdk.areas.ColdPlains, true) ? Pather.useWaypoint(sdk.areas.ColdPlains) : Pather.getWP(sdk.areas.ColdPlains); - Precast.doPrecast(true); - Pather.clearToExit(sdk.areas.ColdPlains, sdk.areas.BloodMoor, true); - Pather.clearToExit(sdk.areas.BloodMoor, sdk.areas.DenofEvil, true); - Attack.clearLevel(); + Pather.checkWP(sdk.areas.ColdPlains, true) ? Pather.useWaypoint(sdk.areas.ColdPlains) : Pather.getWP(sdk.areas.ColdPlains); + Precast.doPrecast(true); + Pather.clearToExit(sdk.areas.ColdPlains, sdk.areas.BloodMoor, true); + Pather.clearToExit(sdk.areas.BloodMoor, sdk.areas.DenofEvil, true); + Attack.clearLevel(); - return true; + return true; } diff --git a/libs/SoloPlay/Scripts/countess.js b/libs/SoloPlay/Scripts/countess.js index dec423ed..d2da958b 100644 --- a/libs/SoloPlay/Scripts/countess.js +++ b/libs/SoloPlay/Scripts/countess.js @@ -6,44 +6,44 @@ */ function countess () { - const floors = [ - sdk.areas.ForgottenTower, sdk.areas.TowerCellarLvl1, - sdk.areas.TowerCellarLvl2, sdk.areas.TowerCellarLvl3, - sdk.areas.TowerCellarLvl4, sdk.areas.TowerCellarLvl5 - ]; - Town.doChores(false, { fullChores: true }); - myPrint("starting countess"); - - Pather.checkWP(sdk.areas.BlackMarsh, true) ? Pather.useWaypoint(sdk.areas.BlackMarsh) : Pather.getWP(sdk.areas.BlackMarsh); - Precast.doPrecast(true); - let forQuest = !Misc.checkQuest(sdk.quest.id.ForgottenTower, sdk.quest.states.Completed); - - if (me.charlvl < 12) { - // @todo - low level, lets take a scenic route and kill those hawk nests - } - - try { - if (me.charlvl < 15) { - for (let i = 0; i < floors.length; i += 1) { - Pather.moveToExit(floors[i], true); - Attack.clear(0x7); - } - } else { - Pather.moveToExit(floors, true); - } - - Pather.moveToPreset(me.area, sdk.unittype.Object, sdk.objects.SuperChest); - Attack.killTarget(getLocaleString(sdk.locale.monsters.TheCountess)); - - if (forQuest) { - Pather.moveToPreset(me.area, sdk.unittype.Object, sdk.objects.SuperChest); - delay(3000); - } - } catch (err) { - console.log("ÿc8Kolbot-SoloPlayÿc0: Failed to kill Countess: " + err); - } - - Pickit.pickItems(); - - return true; + const floors = [ + sdk.areas.ForgottenTower, sdk.areas.TowerCellarLvl1, + sdk.areas.TowerCellarLvl2, sdk.areas.TowerCellarLvl3, + sdk.areas.TowerCellarLvl4, sdk.areas.TowerCellarLvl5 + ]; + Town.doChores(false, { fullChores: true }); + myPrint("starting countess"); + + Pather.checkWP(sdk.areas.BlackMarsh, true) ? Pather.useWaypoint(sdk.areas.BlackMarsh) : Pather.getWP(sdk.areas.BlackMarsh); + Precast.doPrecast(true); + let forQuest = !Misc.checkQuest(sdk.quest.id.ForgottenTower, sdk.quest.states.Completed); + + if (me.charlvl < 12) { + // @todo - low level, lets take a scenic route and kill those hawk nests + } + + try { + if (me.charlvl < 15) { + for (let i = 0; i < floors.length; i += 1) { + Pather.moveToExit(floors[i], true); + Attack.clear(0x7); + } + } else { + Pather.moveToExit(floors, true); + } + + Pather.moveToPreset(me.area, sdk.unittype.Object, sdk.objects.SuperChest); + Attack.killTarget(getLocaleString(sdk.locale.monsters.TheCountess)); + + if (forQuest) { + Pather.moveToPreset(me.area, sdk.unittype.Object, sdk.objects.SuperChest); + delay(3000); + } + } catch (err) { + console.log("ÿc8Kolbot-SoloPlayÿc0: Failed to kill Countess: " + err); + } + + Pickit.pickItems(); + + return true; } diff --git a/libs/SoloPlay/Scripts/cows.js b/libs/SoloPlay/Scripts/cows.js index e7dea7fe..23f601c0 100644 --- a/libs/SoloPlay/Scripts/cows.js +++ b/libs/SoloPlay/Scripts/cows.js @@ -6,160 +6,160 @@ */ function cows () { - const getLeg = function () { - if (me.getItem(sdk.items.quest.WirtsLeg)) return me.getItem(sdk.items.quest.WirtsLeg); - - // Cain is incomplete, complete it then continue @isid0re - if (!me.tristram) { - Loader.runScript("tristram"); - includeIfNotIncluded("SoloPlay/Scripts/tristram.js"); - !me.inTown && Town.goToTown(); - } - - Pather.useWaypoint(sdk.areas.StonyField); - Precast.doPrecast(true); - Pather.moveToPreset(sdk.areas.StonyField, sdk.unittype.Monster, sdk.monsters.preset.Rakanishu, 8, 8); - Pather.usePortal(sdk.areas.Tristram); - - if (me.inArea(sdk.areas.Tristram)) { - Pather.moveTo(25048, 5177); - Quest.collectItem(sdk.items.quest.WirtsLeg, sdk.quest.chest.Wirt); - Pickit.pickItems(); - Town.goToTown(); - } else { - return false; - } - - return me.getItem(sdk.items.quest.WirtsLeg); - }; - - const openPortal = function (portalID, ...classIDS) { - !me.inArea(sdk.areas.RogueEncampment) && Town.goToTown(1); - - let npc, tome, scroll; - let tpTome = me.findItems(sdk.items.TomeofTownPortal, sdk.items.mode.inStorage, sdk.storage.Inventory); - - try { - if (tpTome.length < 2) { - npc = Town.initNPC("Shop", "buyTpTome"); - if (!getInteractedNPC()) throw new Error("Failed to find npc"); - - tome = npc.getItem(sdk.items.TomeofTownPortal); - - if (!!tome && tome.getItemCost(sdk.items.cost.ToBuy) < me.gold && tome.buy()) { - delay(500); - tpTome = me.findItems(sdk.items.TomeofTownPortal, sdk.items.mode.inStorage, sdk.storage.Inventory); - scroll = npc.getItem(sdk.items.ScrollofTownPortal); - let scrollCost = scroll.getItemCost(sdk.items.cost.ToBuy); - tpTome.forEach(function (book) { - while (book.getStat(sdk.stats.Quantity) < 20) { - scroll = npc.getItem(sdk.items.ScrollofTownPortal); - - if (!!scroll && scrollCost < me.gold) { - scroll.buy(true); - } else { - break; - } - - delay(20); - } - }); - } - } - } finally { - me.cancelUIFlags(); - } - - !Town.openStash() && console.log("ÿc8Kolbot-SoloPlayÿc0: Failed to open stash. (openPortal)"); - !Cubing.emptyCube() && console.log("ÿc8Kolbot-SoloPlayÿc0: Failed to empty cube. (openPortal)"); - if (!me.getItem(sdk.items.quest.WirtsLeg)) return false; - - for (let classID of classIDS) { - let cubingItem; - if (classID === sdk.items.TomeofTownPortal) { - // select the tome we just bought rather than the one we had by it's position in our invo - cubingItem = me.getItemsEx(sdk.items.TomeofTownPortal).filter(i => i.isInInventory).sort((a, b) => a.x - b.x).first(); - } else { - cubingItem = me.getItem(classID); - } - - if (!cubingItem || !Storage.Cube.MoveTo(cubingItem)) { - return false; - } - } - - Misc.poll(() => Cubing.openCube(), Time.seconds(10), 1000); - - let tick = getTickCount(); - - while (getTickCount() - tick < 5000) { - if (Cubing.openCube()) { - transmute(); - delay(750); - let cowPortal = Pather.getPortal(portalID); - - if (cowPortal) { - break; - } - } - } - - me.cancel(); - me.sortInventory(); - - return true; - }; - - if (!me.diffCompleted) throw new Error("Final quest incomplete, cannot make cows yet"); - - // START - Town.doChores(false, { fullChores: true }); - myPrint("starting cows"); - - if (!Pather.getPortal(sdk.areas.MooMooFarm) && !getLeg()) return true; - - Town.doChores(); - openPortal(sdk.areas.MooMooFarm, sdk.items.quest.WirtsLeg, sdk.items.TomeofTownPortal); - NPCAction.fillTome(sdk.items.TomeofTownPortal); - - // when does this become not worth it - if (Pather.canTeleport()) { - Misc.getExpShrine([sdk.areas.StonyField, sdk.areas.DarkWood, sdk.areas.BlackMarsh]); - } else { - Misc.getExpShrine([sdk.areas.BloodMoor]); - } - - Town.move("stash"); - - if (Misc.poll(() => Pather.usePortal(sdk.areas.MooMooFarm), Time.seconds(30), Time.seconds(1))) { - include("core/Common/Cows.js"); - const Worker = require("../../modules/Worker"); - let kingTick = getTickCount(); - let king; - let kingPreset; - - Worker.runInBackground.kingTracker = function () { - if (me.inArea(sdk.areas.MooMooFarm)) { - if (getTickCount() - kingTick < 1000) return true; - kingTick = getTickCount(); - king = Game.getMonster(getLocaleString(sdk.locale.monsters.TheCowKing)); - // only get the preset unit once - !kingPreset && (kingPreset = Game.getPresetMonster(me.area, sdk.monsters.preset.TheCowKing)); - - if (king && kingPreset) { - if (getDistance(me.x, me.y, getRoom(kingPreset.roomx * 5 + kingPreset.x), getRoom(kingPreset.roomy * 5 + kingPreset.y)) <= 25) { - myPrint("exit cows. Near the king"); - throw new Error("exit cows. Near the king"); - } - } - } - - return true; - }; - - Precast.doPrecast(true); - Common.Cows.clearCowLevel(); - } - - return true; + const getLeg = function () { + if (me.getItem(sdk.items.quest.WirtsLeg)) return me.getItem(sdk.items.quest.WirtsLeg); + + // Cain is incomplete, complete it then continue @isid0re + if (!me.tristram) { + Loader.runScript("tristram"); + includeIfNotIncluded("SoloPlay/Scripts/tristram.js"); + !me.inTown && Town.goToTown(); + } + + Pather.useWaypoint(sdk.areas.StonyField); + Precast.doPrecast(true); + Pather.moveToPreset(sdk.areas.StonyField, sdk.unittype.Monster, sdk.monsters.preset.Rakanishu, 8, 8); + Pather.usePortal(sdk.areas.Tristram); + + if (me.inArea(sdk.areas.Tristram)) { + Pather.moveTo(25048, 5177); + Quest.collectItem(sdk.items.quest.WirtsLeg, sdk.quest.chest.Wirt); + Pickit.pickItems(); + Town.goToTown(); + } else { + return false; + } + + return me.getItem(sdk.items.quest.WirtsLeg); + }; + + const openPortal = function (portalID, ...classIDS) { + !me.inArea(sdk.areas.RogueEncampment) && Town.goToTown(1); + + let npc, tome, scroll; + let tpTome = me.findItems(sdk.items.TomeofTownPortal, sdk.items.mode.inStorage, sdk.storage.Inventory); + + try { + if (tpTome.length < 2) { + npc = Town.initNPC("Shop", "buyTpTome"); + if (!getInteractedNPC()) throw new Error("Failed to find npc"); + + tome = npc.getItem(sdk.items.TomeofTownPortal); + + if (!!tome && tome.getItemCost(sdk.items.cost.ToBuy) < me.gold && tome.buy()) { + delay(500); + tpTome = me.findItems(sdk.items.TomeofTownPortal, sdk.items.mode.inStorage, sdk.storage.Inventory); + scroll = npc.getItem(sdk.items.ScrollofTownPortal); + let scrollCost = scroll.getItemCost(sdk.items.cost.ToBuy); + tpTome.forEach(function (book) { + while (book.getStat(sdk.stats.Quantity) < 20) { + scroll = npc.getItem(sdk.items.ScrollofTownPortal); + + if (!!scroll && scrollCost < me.gold) { + scroll.buy(true); + } else { + break; + } + + delay(20); + } + }); + } + } + } finally { + me.cancelUIFlags(); + } + + !Town.openStash() && console.log("ÿc8Kolbot-SoloPlayÿc0: Failed to open stash. (openPortal)"); + !Cubing.emptyCube() && console.log("ÿc8Kolbot-SoloPlayÿc0: Failed to empty cube. (openPortal)"); + if (!me.getItem(sdk.items.quest.WirtsLeg)) return false; + + for (let classID of classIDS) { + let cubingItem; + if (classID === sdk.items.TomeofTownPortal) { + // select the tome we just bought rather than the one we had by it's position in our invo + cubingItem = me.getItemsEx(sdk.items.TomeofTownPortal).filter(i => i.isInInventory).sort((a, b) => a.x - b.x).first(); + } else { + cubingItem = me.getItem(classID); + } + + if (!cubingItem || !Storage.Cube.MoveTo(cubingItem)) { + return false; + } + } + + Misc.poll(() => Cubing.openCube(), Time.seconds(10), 1000); + + let tick = getTickCount(); + + while (getTickCount() - tick < 5000) { + if (Cubing.openCube()) { + transmute(); + delay(750); + let cowPortal = Pather.getPortal(portalID); + + if (cowPortal) { + break; + } + } + } + + me.cancel(); + me.sortInventory(); + + return true; + }; + + if (!me.diffCompleted) throw new Error("Final quest incomplete, cannot make cows yet"); + + // START + Town.doChores(false, { fullChores: true }); + myPrint("starting cows"); + + if (!Pather.getPortal(sdk.areas.MooMooFarm) && !getLeg()) return true; + + Town.doChores(); + openPortal(sdk.areas.MooMooFarm, sdk.items.quest.WirtsLeg, sdk.items.TomeofTownPortal); + NPCAction.fillTome(sdk.items.TomeofTownPortal); + + // when does this become not worth it + if (Pather.canTeleport()) { + Misc.getExpShrine([sdk.areas.StonyField, sdk.areas.DarkWood, sdk.areas.BlackMarsh]); + } else { + Misc.getExpShrine([sdk.areas.BloodMoor]); + } + + Town.move("stash"); + + if (Misc.poll(() => Pather.usePortal(sdk.areas.MooMooFarm), Time.seconds(30), Time.seconds(1))) { + include("core/Common/Cows.js"); + const Worker = require("../../modules/Worker"); + let kingTick = getTickCount(); + let king; + let kingPreset; + + Worker.runInBackground.kingTracker = function () { + if (me.inArea(sdk.areas.MooMooFarm)) { + if (getTickCount() - kingTick < 1000) return true; + kingTick = getTickCount(); + king = Game.getMonster(getLocaleString(sdk.locale.monsters.TheCowKing)); + // only get the preset unit once + !kingPreset && (kingPreset = Game.getPresetMonster(me.area, sdk.monsters.preset.TheCowKing)); + + if (king && kingPreset) { + if (getDistance(me.x, me.y, getRoom(kingPreset.roomx * 5 + kingPreset.x), getRoom(kingPreset.roomy * 5 + kingPreset.y)) <= 25) { + myPrint("exit cows. Near the king"); + throw new Error("exit cows. Near the king"); + } + } + } + + return true; + }; + + Precast.doPrecast(true); + Common.Cows.clearCowLevel(); + } + + return true; } diff --git a/libs/SoloPlay/Scripts/cube.js b/libs/SoloPlay/Scripts/cube.js index 36df134c..73c60b0f 100644 --- a/libs/SoloPlay/Scripts/cube.js +++ b/libs/SoloPlay/Scripts/cube.js @@ -6,17 +6,17 @@ */ function cube () { - Town.doChores(false, { fullChores: true }); - myPrint("ÿc8Kolbot-SoloPlayÿc0: starting cube"); + Town.doChores(false, { fullChores: true }); + myPrint("ÿc8Kolbot-SoloPlayÿc0: starting cube"); - Pather.checkWP(sdk.areas.HallsoftheDeadLvl2, true) ? Pather.useWaypoint(sdk.areas.HallsoftheDeadLvl2) : Pather.getWP(sdk.areas.HallsoftheDeadLvl2); - Precast.doPrecast(true); - Pather.clearToExit(sdk.areas.HallsoftheDeadLvl2, sdk.areas.HallsoftheDeadLvl3, Pather.useTeleport()); - Pather.moveToPreset(me.area, sdk.unittype.Object, sdk.quest.chest.HoradricCubeChest); - Attack.securePosition(me.x, me.y, 30, 3000, true); - Quest.collectItem(sdk.items.quest.Cube, sdk.quest.chest.HoradricCubeChest); - Quest.stashItem(sdk.items.quest.Cube); - Town.sortStash(true); + Pather.checkWP(sdk.areas.HallsoftheDeadLvl2, true) ? Pather.useWaypoint(sdk.areas.HallsoftheDeadLvl2) : Pather.getWP(sdk.areas.HallsoftheDeadLvl2); + Precast.doPrecast(true); + Pather.clearToExit(sdk.areas.HallsoftheDeadLvl2, sdk.areas.HallsoftheDeadLvl3, Pather.useTeleport()); + Pather.moveToPreset(me.area, sdk.unittype.Object, sdk.quest.chest.HoradricCubeChest); + Attack.securePosition(me.x, me.y, 30, 3000, true); + Quest.collectItem(sdk.items.quest.Cube, sdk.quest.chest.HoradricCubeChest); + Quest.stashItem(sdk.items.quest.Cube); + Town.sortStash(true); - return me.getItem(sdk.items.quest.Cube); + return me.getItem(sdk.items.quest.Cube); } diff --git a/libs/SoloPlay/Scripts/den.js b/libs/SoloPlay/Scripts/den.js index 4f968696..e4d0ceaa 100644 --- a/libs/SoloPlay/Scripts/den.js +++ b/libs/SoloPlay/Scripts/den.js @@ -6,170 +6,170 @@ */ function den () { - const customGoToTown = function () { - if (me.inTown) return; - if (!me.canTpToTown()) { - Pather.moveToExit([sdk.areas.BloodMoor, sdk.areas.ColdPlains], true); - Pather.getWP(sdk.areas.ColdPlains); - Pather.useWaypoint(sdk.areas.RogueEncampment); - } else { - Town.goToTown(); - } - }; - - myPrint("starting den"); - - me.gold > 500 && Town.initNPC("repair", "shopItems") && NPCAction.shopItems(500, [sdk.items.type.Bow, sdk.items.type.Crossbow, sdk.items.type.Belt]); - me.gold > 1000 && Town.buyPots(12, "stamina", true); - - if (!Pather.checkWP(sdk.areas.ColdPlains) || me.charlvl < 4) { - Pather.moveToExit(sdk.areas.BloodMoor, true); - - try { - if (me.charlvl < 2) { - let cPlains = Pather.getExitCoords(me.area, sdk.areas.ColdPlains); - // sort by the ones closest to us but also by distance to cold plains exit so we end up there - Game.getPresetObjects(me.area) - .filter(el => Misc.presetShrineIds.includes(el.id) || Misc.presetChestIds.includes(el.id)) - .sort((a, b) => { - let [aX, aY] = [a.roomx * 5 + a.x, a.roomy * 5 + a.y]; - let [bX, bY] = [b.roomx * 5 + b.x, b.roomy * 5 + b.y]; - if ([aX, aY].distance < [bX, bY].distance && getDistance(aX, aY, cPlains.x, cPlains.y) > getDistance(bX, bY, cPlains.x, cPlains.y)) { - return -1; - } - if ([aX, aY].distance > [bX, bY].distance && getDistance(aX, aY, cPlains.x, cPlains.y) < getDistance(bX, bY, cPlains.x, cPlains.y)) { - return 1; - } - return [aX, aY].distance - [bX, bY].distance; - }) - .forEach(el => Pather.moveNearUnit(el, 7)); - } else { - Pather.moveNearPreset(sdk.areas.BloodMoor, sdk.unittype.Object, sdk.objects.SuperChest, 8) && Misc.openChests(8); - } - } catch (e) { - console.warn(e.message ? e.message : e); - } - Pather.getWP(sdk.areas.ColdPlains); - - // check if we need to do chores - if so use waypoint to town (preserves portal if we made one at den) - return to cold plains using waypoint - Storage.Inventory.UsedSpacePercent() > 50 && Pather.useWaypoint(sdk.areas.RogueEncampment) && Town.doChores() && Pather.useWaypoint(sdk.areas.ColdPlains); - } - - if (me.charlvl < 8) { - me.sorceress && me.charlvl >= 2 && me.charlvl < 8 && Loader.skipTown.push("bishibosh") && Loader.runScript("bishibosh"); - me.charlvl < 6 && Loader.skipTown.push("cave") && Loader.runScript("cave"); - } - - if (me.charlvl < 8 || me.gold < 1000) { - SoloIndex.retryList.push("den"); - return true; - } - - Town.doChores(false, (me.charlvl < 18 ? { stamina: true } : {})); - !me.inArea(sdk.areas.RogueEncampment) && Town.goToTown(1); - Town.move("portalspot"); - - // Check if there are any portals before trying to use one - let p = Game.getObject(sdk.objects.BluePortal); - - if (!!p && [sdk.areas.BloodMoor, sdk.areas.DenofEvil].includes(p.objtype)) { - Pather.usePortal(null, me.name); - } else { - Pather.moveToExit(sdk.areas.BloodMoor, true); - } - - // START - let attempt = 1; - let killTracker = false; - let denLights = false; - Precast.doPrecast(true); - Attack.clear(20); - Pather.moveToExit(sdk.areas.DenofEvil, true); - - const denLightsListener = function (bytes = []) { - if (!bytes.length) return; - // d2gs unique event - den lights - if (bytes[0] === 0x89) { - denLights = true; - } - }; - - if (me.inArea(sdk.areas.DenofEvil)) { - addEventListener("gamepacket", denLightsListener); - const Worker = require("../../modules/Worker"); - let corpsefire; - let corpseTick = getTickCount(); - - try { - if (!me.normal) { - Worker.runInBackground.corpseTracker = function () { - if (killTracker) return false; - if (me.inArea(sdk.areas.DenofEvil)) { - if (getTickCount() - corpseTick < 1000) return true; - corpseTick = getTickCount(); - corpsefire = Game.getMonster(getLocaleString(sdk.locale.monsters.Corpsefire)); - - if (corpsefire) { - if (!Attack.canAttack(corpsefire)) { - killTracker = true; - throw new Error("Exit den. Corpsefire is immune"); - } else { - // we can attack, no need to run this in the background any longer - return false; - } - } - } - - return true; - }; - } - - Worker.runInBackground.denLightsTracker = function () { - if (killTracker) return false; - if (me.inArea(sdk.areas.DenofEvil)) { - if (denLights) { - killTracker = true; - throw new Error("EVENT :: DEN COMPLETE"); - } - } - - return true; - }; - - while (!Misc.checkQuest(sdk.quest.id.DenofEvil, sdk.quest.states.Completed)) { - console.log("ÿc8Kolbot-SoloPlayÿc0: Clearing den attempt: " + attempt); - Attack.clearLevel(); - - if (!me.inArea(sdk.areas.DenofEvil)) { - break; - } - - if (Misc.checkQuest(sdk.quest.id.DenofEvil, sdk.quest.states.PartyMemberComplete)) { - customGoToTown(); - Town.npcInteract("akara"); - - break; - } - - if (attempt >= 5) { - console.log("ÿc8Kolbot-SoloPlayÿc0: Failed to complete den"); - customGoToTown(); - - break; - } - - attempt++; - } - - } catch (e) { - // - } finally { - removeEventListener("gamepacket", denLightsListener); - SoloEvents.finishDen(); - killTracker = true; - me.getStat(sdk.stats.NewSkills) > 0 && AutoSkill.init(Config.AutoSkill.Build, Config.AutoSkill.Save); - } - } - - return true; + const customGoToTown = function () { + if (me.inTown) return; + if (!me.canTpToTown()) { + Pather.moveToExit([sdk.areas.BloodMoor, sdk.areas.ColdPlains], true); + Pather.getWP(sdk.areas.ColdPlains); + Pather.useWaypoint(sdk.areas.RogueEncampment); + } else { + Town.goToTown(); + } + }; + + myPrint("starting den"); + + me.gold > 500 && Town.initNPC("repair", "shopItems") && NPCAction.shopItems(500, [sdk.items.type.Bow, sdk.items.type.Crossbow, sdk.items.type.Belt]); + me.gold > 1000 && Town.buyPots(12, "stamina", true); + + if (!Pather.checkWP(sdk.areas.ColdPlains) || me.charlvl < 4) { + Pather.moveToExit(sdk.areas.BloodMoor, true); + + try { + if (me.charlvl < 2) { + let cPlains = Pather.getExitCoords(me.area, sdk.areas.ColdPlains); + // sort by the ones closest to us but also by distance to cold plains exit so we end up there + Game.getPresetObjects(me.area) + .filter(el => Misc.presetShrineIds.includes(el.id) || Misc.presetChestIds.includes(el.id)) + .sort((a, b) => { + let [aX, aY] = [a.roomx * 5 + a.x, a.roomy * 5 + a.y]; + let [bX, bY] = [b.roomx * 5 + b.x, b.roomy * 5 + b.y]; + if ([aX, aY].distance < [bX, bY].distance && getDistance(aX, aY, cPlains.x, cPlains.y) > getDistance(bX, bY, cPlains.x, cPlains.y)) { + return -1; + } + if ([aX, aY].distance > [bX, bY].distance && getDistance(aX, aY, cPlains.x, cPlains.y) < getDistance(bX, bY, cPlains.x, cPlains.y)) { + return 1; + } + return [aX, aY].distance - [bX, bY].distance; + }) + .forEach(el => Pather.moveNearUnit(el, 7)); + } else { + Pather.moveNearPreset(sdk.areas.BloodMoor, sdk.unittype.Object, sdk.objects.SuperChest, 8) && Misc.openChests(8); + } + } catch (e) { + console.warn(e.message ? e.message : e); + } + Pather.getWP(sdk.areas.ColdPlains); + + // check if we need to do chores - if so use waypoint to town (preserves portal if we made one at den) - return to cold plains using waypoint + Storage.Inventory.UsedSpacePercent() > 50 && Pather.useWaypoint(sdk.areas.RogueEncampment) && Town.doChores() && Pather.useWaypoint(sdk.areas.ColdPlains); + } + + if (me.charlvl < 8) { + me.sorceress && me.charlvl >= 2 && me.charlvl < 8 && Loader.skipTown.push("bishibosh") && Loader.runScript("bishibosh"); + me.charlvl < 6 && Loader.skipTown.push("cave") && Loader.runScript("cave"); + } + + if (me.charlvl < 8 || me.gold < 1000) { + SoloIndex.retryList.push("den"); + return true; + } + + Town.doChores(false, (me.charlvl < 18 ? { stamina: true } : {})); + !me.inArea(sdk.areas.RogueEncampment) && Town.goToTown(1); + Town.move("portalspot"); + + // Check if there are any portals before trying to use one + let p = Game.getObject(sdk.objects.BluePortal); + + if (!!p && [sdk.areas.BloodMoor, sdk.areas.DenofEvil].includes(p.objtype)) { + Pather.usePortal(null, me.name); + } else { + Pather.moveToExit(sdk.areas.BloodMoor, true); + } + + // START + let attempt = 1; + let killTracker = false; + let denLights = false; + Precast.doPrecast(true); + Attack.clear(20); + Pather.moveToExit(sdk.areas.DenofEvil, true); + + const denLightsListener = function (bytes = []) { + if (!bytes.length) return; + // d2gs unique event - den lights + if (bytes[0] === 0x89) { + denLights = true; + } + }; + + if (me.inArea(sdk.areas.DenofEvil)) { + addEventListener("gamepacket", denLightsListener); + const Worker = require("../../modules/Worker"); + let corpsefire; + let corpseTick = getTickCount(); + + try { + if (!me.normal) { + Worker.runInBackground.corpseTracker = function () { + if (killTracker) return false; + if (me.inArea(sdk.areas.DenofEvil)) { + if (getTickCount() - corpseTick < 1000) return true; + corpseTick = getTickCount(); + corpsefire = Game.getMonster(getLocaleString(sdk.locale.monsters.Corpsefire)); + + if (corpsefire) { + if (!Attack.canAttack(corpsefire)) { + killTracker = true; + throw new Error("Exit den. Corpsefire is immune"); + } else { + // we can attack, no need to run this in the background any longer + return false; + } + } + } + + return true; + }; + } + + Worker.runInBackground.denLightsTracker = function () { + if (killTracker) return false; + if (me.inArea(sdk.areas.DenofEvil)) { + if (denLights) { + killTracker = true; + throw new Error("EVENT :: DEN COMPLETE"); + } + } + + return true; + }; + + while (!Misc.checkQuest(sdk.quest.id.DenofEvil, sdk.quest.states.Completed)) { + console.log("ÿc8Kolbot-SoloPlayÿc0: Clearing den attempt: " + attempt); + Attack.clearLevel(); + + if (!me.inArea(sdk.areas.DenofEvil)) { + break; + } + + if (Misc.checkQuest(sdk.quest.id.DenofEvil, sdk.quest.states.PartyMemberComplete)) { + customGoToTown(); + Town.npcInteract("akara"); + + break; + } + + if (attempt >= 5) { + console.log("ÿc8Kolbot-SoloPlayÿc0: Failed to complete den"); + customGoToTown(); + + break; + } + + attempt++; + } + + } catch (e) { + // + } finally { + removeEventListener("gamepacket", denLightsListener); + SoloEvents.finishDen(); + killTracker = true; + me.getStat(sdk.stats.NewSkills) > 0 && AutoSkill.init(Config.AutoSkill.Build, Config.AutoSkill.Save); + } + } + + return true; } diff --git a/libs/SoloPlay/Scripts/developermode.js b/libs/SoloPlay/Scripts/developermode.js index 8aa20888..928f1e74 100644 --- a/libs/SoloPlay/Scripts/developermode.js +++ b/libs/SoloPlay/Scripts/developermode.js @@ -6,267 +6,267 @@ */ function developermode() { - let [done, action, command, userAddon, test] = [false, false, false, false, false]; - let [watchSent, watchRecv, blockSent, blockRecv] = [[], [], [], []]; - const runCommand = function (msg) { - if (msg.length <= 1) return; - - let cmd = msg.split(" ")[0].split(".")[1]; - let msgList = msg.split(" "); - - switch (cmd.toLowerCase()) { - case "me": - myPrint("Character Level: " + me.charlvl + " | Area: " + me.area + " | x: " + me.x + ", y: " + me.y); - - break; - case "useraddon": - userAddon = !userAddon; - me.overhead("userAddon set to " + userAddon); - - break; - case "run": - if (msgList.length < 2) { - console.log("ÿc1Missing arguments"); - break; - } - - action = msgList[1].toLowerCase(); - - break; - case "done": - done = true; - - break; - case "testing": - test = true; - - break; - case "command": - if (msgList.length < 2) { - console.log("ÿc1Missing arguments"); - break; - } - - command = msgList.splice(1).join(" "); - - break; - case "watch": - if (msgList.length < 3) { - console.log("ÿc1Missing arguments"); - break; - } - - switch (msgList[1].toLowerCase()) { - case "sent": - if (msgList[2] === "list") { - console.log("Watching sent packets : ÿc8" + watchSent.join(", ")); - break; - } - - watchSent.push(msgList[2]); - console.log("Added ÿc80x" + msgList[2] + "ÿc0 (sent) to watch list"); - break; - - case "recv": - if (msgList[2] === "list") { - console.log("Watching received packets : ÿc8" + watchRecv.join(", ")); - break; - } - - watchRecv.push(msgList[2]); - console.log("Added ÿc80x" + msgList[2] + "ÿc0 (recv) to watch list"); - break; - - default: - console.log("ÿc1Invalid argument : " + msgList[1]); - break; - } - - break; - - case "!watch": - if (msgList.length < 3) { - console.log("ÿc1Missing arguments"); - break; - } - - switch (msgList[1].toLowerCase()) { - case "sent": - if (watchSent.indexOf(msgList[2]) > -1) watchSent.splice(watchSent.indexOf(msgList[2]), 1); - console.log("Removed packet ÿc80x" + msgList[2] + "ÿc0 (sent) from watch list"); - break; - - case "recv": - if (watchRecv.indexOf(msgList[2]) > -1) watchRecv.splice(watchRecv.indexOf(msgList[2]), 1); - console.log("Removed packet ÿc80x" + msgList[2] + "ÿc0 (recv) from watch list"); - break; - - default: - console.log("ÿc1Invalid argument : " + msgList[1]); - break; - } - - break; - - case "block": - if (msgList.length < 3) { - console.log("ÿc1Missing arguments"); - break; - } - - switch (msgList[1].toLowerCase()) { - case "sent": - if (msgList[2] === "list") { - console.log("Blocking sent packets : ÿc8" + blockSent.join(", ")); - break; - } - - blockSent.push(msgList[2]); - console.log("Added ÿc80x" + msgList[2] + "ÿc0 (sent) to block list"); - break; - - case "recv": - if (msgList[2] === "list") { - console.log("Blocking received packets : ÿc8" + blockRecv.join(", ")); - break; - } - - blockRecv.push(msgList[2]); - console.log("Added ÿc80x" + msgList[2] + "ÿc0 (recv) to block list"); - break; - - default: - console.log("ÿc1Invalid argument : " + msgList[1]); - break; - } - - break; - - case "!block": - if (msgList.length < 3) { - console.log("ÿc1Missing arguments"); - break; - } - - switch (msgList[1].toLowerCase()) { - case "sent": - if (blockSent.indexOf(msgList[2]) > -1) blockSent.splice(blockSent.indexOf(msgList[2]), 1); - console.log("Removed packet ÿc80x" + msgList[2] + "ÿc0 (sent) from block list"); - break; - - case "recv": - if (blockRecv.indexOf(msgList[2]) > -1) blockRecv.splice(blockRecv.indexOf(msgList[2]), 1); - console.log("Removed packet ÿc80x" + msgList[2] + "ÿc0 (recv) from block list"); - break; - - default: - console.log("ÿc1Invalid argument : " + msgList[1]); - break; - } - - break; - } - }; - - /** - * Received packet handler - * @param {number[]} pBytes - */ - const packetReceived = function (pBytes) { - let ID = pBytes[0].toString(16); - - // Block received packets from list - if (blockRecv.includes(ID)) return true; - - if (watchRecv.includes(ID)) { - let size = pBytes.length; - let array = [].slice.call(pBytes).map(pByte => pByte.toString(16)); - array.shift(); - console.log("ÿc2S ÿc8" + ID + "ÿc0 " + array.join(" ") + " ÿc5(" + size + " Bytes)"); - } - - return false; - }; - - // Sent packet handler - const packetSent = function (pBytes) { - let ID = pBytes[0].toString(16); - - // Block all commands or irc chat from being sent to server - if (ID === "15") { - if (pBytes[3] === 46) { - let str = ""; - - for (let b = 3; b < pBytes.length - 3; b++) { - str += String.fromCharCode(pBytes[b]); - } - - if (pBytes[3] === 46) { - runCommand(str); - return true; - } - } - } - - // Block sent packets from list - if (blockSent.includes(ID)) return true; - - if (watchSent.includes(ID)) { - let size = pBytes.length; - let array = [].slice.call(pBytes); - array.shift(); - console.log("ÿc2C ÿc8" + ID + "ÿc0 " + array.join(" ") + " ÿc5(" + size + " Bytes)"); - } - - return false; - }; - - const UnitInfo = new (require("../../modules/UnitInfo")); - - try { - myPrint("ÿc8Kolbot-SoloPlayÿc0: starting developermode"); - addEventListener("gamepacketsent", packetSent); - addEventListener("gamepacket", packetReceived); - Config.Silence = false; - - while (!done) { - UnitInfo.check(); - - if (action) { - Loader.runScript(action); - - me.overhead("Done with action"); - action = false; - } - - if (command) { - try { - eval(command); - } catch (e) { - console.error(e); - } - - me.overhead("Done with action"); - command = false; - } - - if (userAddon) { - UnitInfo.createInfo(Game.getSelectedUnit()); - } - - if (test) { - console.debug("done"); - me.overhead("done"); - test = false; - } - - delay(100); - } - } finally { - removeEventListener("gamepacketsent", packetSent); - removeEventListener("gamepacket", packetReceived); - Config.Silence = true; - } - - return true; + let [done, action, command, userAddon, test] = [false, false, false, false, false]; + let [watchSent, watchRecv, blockSent, blockRecv] = [[], [], [], []]; + const runCommand = function (msg) { + if (msg.length <= 1) return; + + let cmd = msg.split(" ")[0].split(".")[1]; + let msgList = msg.split(" "); + + switch (cmd.toLowerCase()) { + case "me": + myPrint("Character Level: " + me.charlvl + " | Area: " + me.area + " | x: " + me.x + ", y: " + me.y); + + break; + case "useraddon": + userAddon = !userAddon; + me.overhead("userAddon set to " + userAddon); + + break; + case "run": + if (msgList.length < 2) { + console.log("ÿc1Missing arguments"); + break; + } + + action = msgList[1].toLowerCase(); + + break; + case "done": + done = true; + + break; + case "testing": + test = true; + + break; + case "command": + if (msgList.length < 2) { + console.log("ÿc1Missing arguments"); + break; + } + + command = msgList.splice(1).join(" "); + + break; + case "watch": + if (msgList.length < 3) { + console.log("ÿc1Missing arguments"); + break; + } + + switch (msgList[1].toLowerCase()) { + case "sent": + if (msgList[2] === "list") { + console.log("Watching sent packets : ÿc8" + watchSent.join(", ")); + break; + } + + watchSent.push(msgList[2]); + console.log("Added ÿc80x" + msgList[2] + "ÿc0 (sent) to watch list"); + break; + + case "recv": + if (msgList[2] === "list") { + console.log("Watching received packets : ÿc8" + watchRecv.join(", ")); + break; + } + + watchRecv.push(msgList[2]); + console.log("Added ÿc80x" + msgList[2] + "ÿc0 (recv) to watch list"); + break; + + default: + console.log("ÿc1Invalid argument : " + msgList[1]); + break; + } + + break; + + case "!watch": + if (msgList.length < 3) { + console.log("ÿc1Missing arguments"); + break; + } + + switch (msgList[1].toLowerCase()) { + case "sent": + if (watchSent.indexOf(msgList[2]) > -1) watchSent.splice(watchSent.indexOf(msgList[2]), 1); + console.log("Removed packet ÿc80x" + msgList[2] + "ÿc0 (sent) from watch list"); + break; + + case "recv": + if (watchRecv.indexOf(msgList[2]) > -1) watchRecv.splice(watchRecv.indexOf(msgList[2]), 1); + console.log("Removed packet ÿc80x" + msgList[2] + "ÿc0 (recv) from watch list"); + break; + + default: + console.log("ÿc1Invalid argument : " + msgList[1]); + break; + } + + break; + + case "block": + if (msgList.length < 3) { + console.log("ÿc1Missing arguments"); + break; + } + + switch (msgList[1].toLowerCase()) { + case "sent": + if (msgList[2] === "list") { + console.log("Blocking sent packets : ÿc8" + blockSent.join(", ")); + break; + } + + blockSent.push(msgList[2]); + console.log("Added ÿc80x" + msgList[2] + "ÿc0 (sent) to block list"); + break; + + case "recv": + if (msgList[2] === "list") { + console.log("Blocking received packets : ÿc8" + blockRecv.join(", ")); + break; + } + + blockRecv.push(msgList[2]); + console.log("Added ÿc80x" + msgList[2] + "ÿc0 (recv) to block list"); + break; + + default: + console.log("ÿc1Invalid argument : " + msgList[1]); + break; + } + + break; + + case "!block": + if (msgList.length < 3) { + console.log("ÿc1Missing arguments"); + break; + } + + switch (msgList[1].toLowerCase()) { + case "sent": + if (blockSent.indexOf(msgList[2]) > -1) blockSent.splice(blockSent.indexOf(msgList[2]), 1); + console.log("Removed packet ÿc80x" + msgList[2] + "ÿc0 (sent) from block list"); + break; + + case "recv": + if (blockRecv.indexOf(msgList[2]) > -1) blockRecv.splice(blockRecv.indexOf(msgList[2]), 1); + console.log("Removed packet ÿc80x" + msgList[2] + "ÿc0 (recv) from block list"); + break; + + default: + console.log("ÿc1Invalid argument : " + msgList[1]); + break; + } + + break; + } + }; + + /** + * Received packet handler + * @param {number[]} pBytes + */ + const packetReceived = function (pBytes) { + let ID = pBytes[0].toString(16); + + // Block received packets from list + if (blockRecv.includes(ID)) return true; + + if (watchRecv.includes(ID)) { + let size = pBytes.length; + let array = [].slice.call(pBytes).map(pByte => pByte.toString(16)); + array.shift(); + console.log("ÿc2S ÿc8" + ID + "ÿc0 " + array.join(" ") + " ÿc5(" + size + " Bytes)"); + } + + return false; + }; + + // Sent packet handler + const packetSent = function (pBytes) { + let ID = pBytes[0].toString(16); + + // Block all commands or irc chat from being sent to server + if (ID === "15") { + if (pBytes[3] === 46) { + let str = ""; + + for (let b = 3; b < pBytes.length - 3; b++) { + str += String.fromCharCode(pBytes[b]); + } + + if (pBytes[3] === 46) { + runCommand(str); + return true; + } + } + } + + // Block sent packets from list + if (blockSent.includes(ID)) return true; + + if (watchSent.includes(ID)) { + let size = pBytes.length; + let array = [].slice.call(pBytes); + array.shift(); + console.log("ÿc2C ÿc8" + ID + "ÿc0 " + array.join(" ") + " ÿc5(" + size + " Bytes)"); + } + + return false; + }; + + const UnitInfo = new (require("../../modules/UnitInfo")); + + try { + myPrint("ÿc8Kolbot-SoloPlayÿc0: starting developermode"); + addEventListener("gamepacketsent", packetSent); + addEventListener("gamepacket", packetReceived); + Config.Silence = false; + + while (!done) { + UnitInfo.check(); + + if (action) { + Loader.runScript(action); + + me.overhead("Done with action"); + action = false; + } + + if (command) { + try { + eval(command); + } catch (e) { + console.error(e); + } + + me.overhead("Done with action"); + command = false; + } + + if (userAddon) { + UnitInfo.createInfo(Game.getSelectedUnit()); + } + + if (test) { + console.debug("done"); + me.overhead("done"); + test = false; + } + + delay(100); + } + } finally { + removeEventListener("gamepacketsent", packetSent); + removeEventListener("gamepacket", packetReceived); + Config.Silence = true; + } + + return true; } diff --git a/libs/SoloPlay/Scripts/diablo.js b/libs/SoloPlay/Scripts/diablo.js index 55d9cbaa..f72f2186 100644 --- a/libs/SoloPlay/Scripts/diablo.js +++ b/libs/SoloPlay/Scripts/diablo.js @@ -8,177 +8,177 @@ // todo: clean this up, listen for lights game packet while opening/checking seals function diablo () { - include("core/Common/Diablo.js"); - // Start Diablo Quest - const diabloPrep = function () { - let tick = getTickCount(); - let decoyDuration = (me.amazon ? Skill.getDuration(sdk.skills.Decoy) : 0); - - while (getTickCount() - tick < 17500) { - me.getMobCount(20) > 1 && Attack.clear(20); - if (getTickCount() - tick >= 8000) { - switch (me.classid) { - case sdk.player.class.Amazon: - if (me.getSkill(sdk.skills.Decoy, sdk.skills.subindex.SoftPoints)) { - let decoy = Game.getMonster(sdk.summons.Dopplezon); - - if (!decoy || (getTickCount() - tick >= decoyDuration)) { - Skill.cast(sdk.skills.Decoy, sdk.skills.hand.Right, 7793, 5293); - } - } - - break; - case sdk.player.class.Sorceress: - if ([sdk.skills.Meteor, sdk.skills.Blizzard, sdk.skills.FrozenOrb, sdk.skills.FireWall].includes(Config.AttackSkill[1])) { - Skill.cast(Config.AttackSkill[1], sdk.skills.hand.Right, 7793 + rand(-1, 1), 5293); - } - - delay(500); - - break; - case sdk.player.class.Paladin: - Skill.setSkill(Config.AttackSkill[2]); - Config.AttackSkill[1] === sdk.skills.BlessedHammer && Skill.cast(Config.AttackSkill[1], sdk.skills.hand.Left); - - break; - case sdk.player.class.Druid: - if ([sdk.skills.Tornado, sdk.skills.Fissure, sdk.skills.Volcano].includes(Config.AttackSkill[3])) { - Skill.cast(Config.AttackSkill[1], sdk.skills.hand.Right, 7793 + rand(-1, 1), 5293); - - break; - } - - delay(500); - - break; - case sdk.player.class.Assassin: - if (Config.UseTraps) { - let trapCheck = ClassAttack.checkTraps({ x: 7793, y: 5293 }); - trapCheck && ClassAttack.placeTraps({ x: 7793, y: 5293, classid: sdk.monsters.Diablo }, trapCheck); - } - - Config.AttackSkill[1] === sdk.skills.ShockWeb && Skill.cast(Config.AttackSkill[1], sdk.skills.hand.Right, 7793, 5293); - - delay(500); - - break; - default: - delay(500); - } - } else { - delay(500); - } - - if (Game.getMonster(sdk.monsters.Diablo)) { - return true; - } - } - - return false; - }; - - // START - Town.doChores(false, { fullChores: true }); - myPrint("starting diablo"); - - Pather.checkWP(sdk.areas.RiverofFlame, true) ? Pather.useWaypoint(sdk.areas.RiverofFlame) : Pather.getWP(sdk.areas.RiverofFlame); - Precast.doPrecast(true); - - let attempts = 0; - - while ([7790, 5544].distance > 15 && attempts < 5) { - myPrint("Moving to Chaos Sanctuary Entrance :: Attempt: " + attempts); - Pather.moveTo(7790, 5544); - attempts++; - } - - if (me.coldRes < 75 || me.poisonRes < 75) { - Town.doChores(null, { thawing: me.coldRes < 75, antidote: me.poisonRes < 75 }); - Town.move("portalspot"); - Pather.usePortal(sdk.areas.ChaosSanctuary, me.name); - Misc.poll(() => { - if (me.inArea(sdk.areas.ChaosSanctuary)) { - console.log("Returned to chaos"); - return true; - } - return false; - }, 500, 100); - } - - Common.Diablo.initLayout(); - - const oldCP = Object.assign({}, Config.ClearPath); - const oldBP = Config.BossPriority; - - try { - !me.diablo && me.barbarian && (Config.BossPriority = true); - - switch (true) { - case (Check.brokeAf(false)): - break; - case (me.diffCompleted && Pather.canTeleport()): - let cLvl = me.charlvl; - if ((me.normal && cLvl > 28) || (me.nightmare && cLvl > 65)) { - // try running fast diablo - may neeed work - Config.Diablo.Fast = true; - Config.ClearPath.Range = 15; - Config.ClearPath.Spectype = 0xF; // skip normal mobs - console.debug("CP Settings: ", Config.ClearPath); - } - } - - Common.Diablo.vizierSeal(); - Common.Diablo.seisSeal(); - Common.Diablo.infectorSeal(); - } catch (e) { - console.error(e); - } finally { - oldCP.Range !== Config.ClearPath.Range && (Object.assign(Config.ClearPath, oldCP)); - oldBP !== Config.BossPriority && (Config.BossPriority = oldBP); - } - - try { - if (!Check.currentBuild().caster || (Skill.getRange(Config.AttackSkill[1]) < 13)) { - Messaging.sendToScript(SoloEvents.filePath, "addDiaEvent"); - } - - (me.sorceress || me.necromancer || me.assassin) ? Pather.moveNear(7792, 5292, 37) : Pather.moveTo(7788, 5292, 3, 30); - - diabloPrep(); - let theD = Game.getMonster(sdk.monsters.Diablo); - - if (!theD) { - console.log("ÿc8Kolbot-SoloPlayÿc0: Diablo not found. Checking seal bosses."); - try { - Common.Diablo.vizierSeal(); - Common.Diablo.seisSeal(); - Common.Diablo.infectorSeal(); - } catch (e) { - // - } - - (me.sorceress || me.necromancer || me.assassin) ? Pather.moveNear(7792, 5292, 37) : Pather.moveTo(7788, 5292, 3, 30); - diabloPrep(); - } - - !Attack.pwnDia() && Attack.killTarget(sdk.monsters.Diablo); - Pickit.pickItems(); - } catch (e) { - // - } finally { - Messaging.sendToScript(SoloEvents.filePath, "removeDiaEvent"); - } - - if (me.classic) return true; - - try { - Pather.changeAct(); - } catch (err) { - Town.npcInteract("tyrael"); - me.cancel(); - delay(500); - Pather.useUnit(sdk.unittype.Object, 566, 109); - } - - return true; + include("core/Common/Diablo.js"); + // Start Diablo Quest + const diabloPrep = function () { + let tick = getTickCount(); + let decoyDuration = (me.amazon ? Skill.getDuration(sdk.skills.Decoy) : 0); + + while (getTickCount() - tick < 17500) { + me.getMobCount(20) > 1 && Attack.clear(20); + if (getTickCount() - tick >= 8000) { + switch (me.classid) { + case sdk.player.class.Amazon: + if (me.getSkill(sdk.skills.Decoy, sdk.skills.subindex.SoftPoints)) { + let decoy = Game.getMonster(sdk.summons.Dopplezon); + + if (!decoy || (getTickCount() - tick >= decoyDuration)) { + Skill.cast(sdk.skills.Decoy, sdk.skills.hand.Right, 7793, 5293); + } + } + + break; + case sdk.player.class.Sorceress: + if ([sdk.skills.Meteor, sdk.skills.Blizzard, sdk.skills.FrozenOrb, sdk.skills.FireWall].includes(Config.AttackSkill[1])) { + Skill.cast(Config.AttackSkill[1], sdk.skills.hand.Right, 7793 + rand(-1, 1), 5293); + } + + delay(500); + + break; + case sdk.player.class.Paladin: + Skill.setSkill(Config.AttackSkill[2]); + Config.AttackSkill[1] === sdk.skills.BlessedHammer && Skill.cast(Config.AttackSkill[1], sdk.skills.hand.Left); + + break; + case sdk.player.class.Druid: + if ([sdk.skills.Tornado, sdk.skills.Fissure, sdk.skills.Volcano].includes(Config.AttackSkill[3])) { + Skill.cast(Config.AttackSkill[1], sdk.skills.hand.Right, 7793 + rand(-1, 1), 5293); + + break; + } + + delay(500); + + break; + case sdk.player.class.Assassin: + if (Config.UseTraps) { + let trapCheck = ClassAttack.checkTraps({ x: 7793, y: 5293 }); + trapCheck && ClassAttack.placeTraps({ x: 7793, y: 5293, classid: sdk.monsters.Diablo }, trapCheck); + } + + Config.AttackSkill[1] === sdk.skills.ShockWeb && Skill.cast(Config.AttackSkill[1], sdk.skills.hand.Right, 7793, 5293); + + delay(500); + + break; + default: + delay(500); + } + } else { + delay(500); + } + + if (Game.getMonster(sdk.monsters.Diablo)) { + return true; + } + } + + return false; + }; + + // START + Town.doChores(false, { fullChores: true }); + myPrint("starting diablo"); + + Pather.checkWP(sdk.areas.RiverofFlame, true) ? Pather.useWaypoint(sdk.areas.RiverofFlame) : Pather.getWP(sdk.areas.RiverofFlame); + Precast.doPrecast(true); + + let attempts = 0; + + while ([7790, 5544].distance > 15 && attempts < 5) { + myPrint("Moving to Chaos Sanctuary Entrance :: Attempt: " + attempts); + Pather.moveTo(7790, 5544); + attempts++; + } + + if (me.coldRes < 75 || me.poisonRes < 75) { + Town.doChores(null, { thawing: me.coldRes < 75, antidote: me.poisonRes < 75 }); + Town.move("portalspot"); + Pather.usePortal(sdk.areas.ChaosSanctuary, me.name); + Misc.poll(() => { + if (me.inArea(sdk.areas.ChaosSanctuary)) { + console.log("Returned to chaos"); + return true; + } + return false; + }, 500, 100); + } + + Common.Diablo.initLayout(); + + const oldCP = Object.assign({}, Config.ClearPath); + const oldBP = Config.BossPriority; + + try { + !me.diablo && me.barbarian && (Config.BossPriority = true); + + switch (true) { + case (Check.brokeAf(false)): + break; + case (me.diffCompleted && Pather.canTeleport()): + let cLvl = me.charlvl; + if ((me.normal && cLvl > 28) || (me.nightmare && cLvl > 65)) { + // try running fast diablo - may neeed work + Config.Diablo.Fast = true; + Config.ClearPath.Range = 15; + Config.ClearPath.Spectype = 0xF; // skip normal mobs + console.debug("CP Settings: ", Config.ClearPath); + } + } + + Common.Diablo.vizierSeal(); + Common.Diablo.seisSeal(); + Common.Diablo.infectorSeal(); + } catch (e) { + console.error(e); + } finally { + oldCP.Range !== Config.ClearPath.Range && (Object.assign(Config.ClearPath, oldCP)); + oldBP !== Config.BossPriority && (Config.BossPriority = oldBP); + } + + try { + if (!Check.currentBuild().caster || (Skill.getRange(Config.AttackSkill[1]) < 13)) { + Messaging.sendToScript(SoloEvents.filePath, "addDiaEvent"); + } + + (me.sorceress || me.necromancer || me.assassin) ? Pather.moveNear(7792, 5292, 37) : Pather.moveTo(7788, 5292, 3, 30); + + diabloPrep(); + let theD = Game.getMonster(sdk.monsters.Diablo); + + if (!theD) { + console.log("ÿc8Kolbot-SoloPlayÿc0: Diablo not found. Checking seal bosses."); + try { + Common.Diablo.vizierSeal(); + Common.Diablo.seisSeal(); + Common.Diablo.infectorSeal(); + } catch (e) { + // + } + + (me.sorceress || me.necromancer || me.assassin) ? Pather.moveNear(7792, 5292, 37) : Pather.moveTo(7788, 5292, 3, 30); + diabloPrep(); + } + + !Attack.pwnDia() && Attack.killTarget(sdk.monsters.Diablo); + Pickit.pickItems(); + } catch (e) { + // + } finally { + Messaging.sendToScript(SoloEvents.filePath, "removeDiaEvent"); + } + + if (me.classic) return true; + + try { + Pather.changeAct(); + } catch (err) { + Town.npcInteract("tyrael"); + me.cancel(); + delay(500); + Pather.useUnit(sdk.unittype.Object, 566, 109); + } + + return true; } diff --git a/libs/SoloPlay/Scripts/duriel.js b/libs/SoloPlay/Scripts/duriel.js index 81697afd..6f56ddaf 100644 --- a/libs/SoloPlay/Scripts/duriel.js +++ b/libs/SoloPlay/Scripts/duriel.js @@ -6,73 +6,73 @@ */ function duriel () { - // pre-tasks - Quest.preReqs(); - Quest.cubeItems(sdk.quest.item.HoradricStaff, sdk.quest.item.ShaftoftheHoradricStaff, sdk.quest.item.ViperAmulet); + // pre-tasks + Quest.preReqs(); + Quest.cubeItems(sdk.quest.item.HoradricStaff, sdk.quest.item.ShaftoftheHoradricStaff, sdk.quest.item.ViperAmulet); - // Start - Town.doChores(false, { fullChores: true }); - myPrint("starting duriel"); - Pather.checkWP(sdk.areas.CanyonofMagic, true) ? Pather.useWaypoint(sdk.areas.CanyonofMagic) : Pather.getWP(sdk.areas.CanyonofMagic); - Precast.doPrecast(true); - Pather.moveToExit(getRoom().correcttomb, true); - Pather.moveToPreset(me.area, sdk.unittype.Object, sdk.objects.HoradricStaffHolder); - Attack.securePosition(me.x, me.y, 30, 3000, true, me.hell); - Quest.placeStaff(); + // Start + Town.doChores(false, { fullChores: true }); + myPrint("starting duriel"); + Pather.checkWP(sdk.areas.CanyonofMagic, true) ? Pather.useWaypoint(sdk.areas.CanyonofMagic) : Pather.getWP(sdk.areas.CanyonofMagic); + Precast.doPrecast(true); + Pather.moveToExit(getRoom().correcttomb, true); + Pather.moveToPreset(me.area, sdk.unittype.Object, sdk.objects.HoradricStaffHolder); + Attack.securePosition(me.x, me.y, 30, 3000, true, me.hell); + Quest.placeStaff(); - // quest-prep - let preArea = me.area; - Town.doChores(null, { thawing: me.coldRes < 75 }); + // quest-prep + let preArea = me.area; + Town.doChores(null, { thawing: me.coldRes < 75 }); - let oldMercWatch = Config.MercWatch; - Config.MercWatch = false; + let oldMercWatch = Config.MercWatch; + Config.MercWatch = false; - if (!Pather.usePortal(preArea, me.name)) { - if (!Pather.journeyTo(preArea)) throw new Error("Failed to move back to duriels tomb"); - } + if (!Pather.usePortal(preArea, me.name)) { + if (!Pather.journeyTo(preArea)) throw new Error("Failed to move back to duriels tomb"); + } - // move to and kill dury - let unit = Misc.poll(() => Game.getObject(sdk.objects.PortaltoDurielsLair)); + // move to and kill dury + let unit = Misc.poll(() => Game.getObject(sdk.objects.PortaltoDurielsLair)); - if (me.sorceress && unit && Skill.useTK(unit)) { - for (let i = 0; i < 3; i++) { - !me.inArea(sdk.areas.DurielsLair) && Packet.telekinesis(unit); - if (me.inArea(sdk.areas.DurielsLair)) { - break; - } - } + if (me.sorceress && unit && Skill.useTK(unit)) { + for (let i = 0; i < 3; i++) { + !me.inArea(sdk.areas.DurielsLair) && Packet.telekinesis(unit); + if (me.inArea(sdk.areas.DurielsLair)) { + break; + } + } - if (!me.inArea(sdk.areas.DurielsLair) && !Pather.useUnit(sdk.unittype.Object, sdk.objects.PortaltoDurielsLair, sdk.areas.DurielsLair)) { - Attack.clear(10); - Pather.useUnit(sdk.unittype.Object, sdk.objects.PortaltoDurielsLair, sdk.areas.DurielsLair); - } - } else { - Pather.useUnit(sdk.unittype.Object, sdk.objects.PortaltoDurielsLair, sdk.areas.DurielsLair); - } + if (!me.inArea(sdk.areas.DurielsLair) && !Pather.useUnit(sdk.unittype.Object, sdk.objects.PortaltoDurielsLair, sdk.areas.DurielsLair)) { + Attack.clear(10); + Pather.useUnit(sdk.unittype.Object, sdk.objects.PortaltoDurielsLair, sdk.areas.DurielsLair); + } + } else { + Pather.useUnit(sdk.unittype.Object, sdk.objects.PortaltoDurielsLair, sdk.areas.DurielsLair); + } - me.sorceress && !me.normal ? Attack.pwnDury() : Attack.killTarget("Duriel"); - Pickit.pickItems(); + me.sorceress && !me.normal ? Attack.pwnDury() : Attack.killTarget("Duriel"); + Pickit.pickItems(); - !me.duriel && !Misc.checkQuest(sdk.quest.id.TheSevenTombs, 3) && Quest.tyraelTomb(); + !me.duriel && !Misc.checkQuest(sdk.quest.id.TheSevenTombs, 3) && Quest.tyraelTomb(); - if (Misc.checkQuest(sdk.quest.id.TheSevenTombs, 3)) { - for (let i = 0; i < 3; i++) { - if (!me.duriel && !Misc.checkQuest(sdk.quest.id.TheSevenTombs, 4)) { - Town.move("palace"); - Town.npcInteract("jerhyn"); - } + if (Misc.checkQuest(sdk.quest.id.TheSevenTombs, 3)) { + for (let i = 0; i < 3; i++) { + if (!me.duriel && !Misc.checkQuest(sdk.quest.id.TheSevenTombs, 4)) { + Town.move("palace"); + Town.npcInteract("jerhyn"); + } - if (Misc.checkQuest(sdk.quest.id.TheSevenTombs, 4)) { - Pather.moveToExit(sdk.areas.HaremLvl1, true); - break; - } else { - delay(250 + me.ping); - } - } - } + if (Misc.checkQuest(sdk.quest.id.TheSevenTombs, 4)) { + Pather.moveToExit(sdk.areas.HaremLvl1, true); + break; + } else { + delay(250 + me.ping); + } + } + } - Pather.changeAct(); - Config.MercWatch = oldMercWatch; + Pather.changeAct(); + Config.MercWatch = oldMercWatch; - return true; + return true; } diff --git a/libs/SoloPlay/Scripts/eye.js b/libs/SoloPlay/Scripts/eye.js index 978ee4e7..efc5614e 100644 --- a/libs/SoloPlay/Scripts/eye.js +++ b/libs/SoloPlay/Scripts/eye.js @@ -6,27 +6,27 @@ */ function eye () { - Town.doChores(false, { fullChores: true }); - myPrint("starting eye"); + Town.doChores(false, { fullChores: true }); + myPrint("starting eye"); - Pather.checkWP(sdk.areas.SpiderForest, true) ? Pather.useWaypoint(sdk.areas.SpiderForest) : Pather.getWP(sdk.areas.SpiderForest); - Precast.doPrecast(true); + Pather.checkWP(sdk.areas.SpiderForest, true) ? Pather.useWaypoint(sdk.areas.SpiderForest) : Pather.getWP(sdk.areas.SpiderForest); + Precast.doPrecast(true); - if (!Pather.moveToExit([sdk.areas.SpiderForest, sdk.areas.SpiderCavern], true)) { - if (!me.inArea(sdk.areas.SpiderCavern)) { - if (!Pather.journeyTo(sdk.areas.SpiderCavern)) { - console.log("ÿc8Kolbot-SoloPlayÿc0: Failed to get the eye"); - return false; - } - } - } + if (!Pather.moveToExit([sdk.areas.SpiderForest, sdk.areas.SpiderCavern], true)) { + if (!me.inArea(sdk.areas.SpiderCavern)) { + if (!Pather.journeyTo(sdk.areas.SpiderCavern)) { + console.log("ÿc8Kolbot-SoloPlayÿc0: Failed to get the eye"); + return false; + } + } + } - Town.doChores(null, { antidote: me.poisonRes < 75 }); - Pather.usePortal(sdk.areas.SpiderCavern, me.name); - Pather.moveToPreset(me.area, sdk.unittype.Object, sdk.quest.chest.KhalimsEyeChest); - Attack.clear(0x7); - Quest.collectItem(sdk.items.quest.KhalimsEye, sdk.quest.chest.KhalimsEyeChest); - Quest.stashItem(sdk.items.quest.KhalimsEye); + Town.doChores(null, { antidote: me.poisonRes < 75 }); + Pather.usePortal(sdk.areas.SpiderCavern, me.name); + Pather.moveToPreset(me.area, sdk.unittype.Object, sdk.quest.chest.KhalimsEyeChest); + Attack.clear(0x7); + Quest.collectItem(sdk.items.quest.KhalimsEye, sdk.quest.chest.KhalimsEyeChest); + Quest.stashItem(sdk.items.quest.KhalimsEye); - return me.getItem(sdk.items.quest.KhalimsEye); + return me.getItem(sdk.items.quest.KhalimsEye); } diff --git a/libs/SoloPlay/Scripts/getkeys.js b/libs/SoloPlay/Scripts/getkeys.js index 9dff255f..4d0fe8df 100644 --- a/libs/SoloPlay/Scripts/getkeys.js +++ b/libs/SoloPlay/Scripts/getkeys.js @@ -6,31 +6,31 @@ */ function getkeys() { - Town.doChores(); + Town.doChores(); - if (!me.findItems(sdk.items.quest.KeyofTerror) || me.findItems(sdk.items.quest.KeyofTerror).length < 3) { - try { - Loader.runScript("countess"); - } catch (countessError) { - console.log("ÿc8Kolbot-SoloPlayÿc0: Countess failed"); - } - } + if (!me.findItems(sdk.items.quest.KeyofTerror) || me.findItems(sdk.items.quest.KeyofTerror).length < 3) { + try { + Loader.runScript("countess"); + } catch (countessError) { + console.log("ÿc8Kolbot-SoloPlayÿc0: Countess failed"); + } + } - if (!me.findItems(sdk.items.quest.KeyofHate) || me.findItems(sdk.items.quest.KeyofHate).length < 3) { - try { - Loader.runScript("summoner"); - } catch (summonerError) { - console.log("ÿc8Kolbot-SoloPlayÿc0: Summoner failed"); - } - } + if (!me.findItems(sdk.items.quest.KeyofHate) || me.findItems(sdk.items.quest.KeyofHate).length < 3) { + try { + Loader.runScript("summoner"); + } catch (summonerError) { + console.log("ÿc8Kolbot-SoloPlayÿc0: Summoner failed"); + } + } - if (!me.findItems(sdk.items.quest.KeyofDestruction) || me.findItems(sdk.items.quest.KeyofDestruction).length < 3) { - try { - Loader.runScript("nith"); - } catch (nihlathakError) { - console.log("ÿc8Kolbot-SoloPlayÿc0: Nihlathak failed"); - } - } + if (!me.findItems(sdk.items.quest.KeyofDestruction) || me.findItems(sdk.items.quest.KeyofDestruction).length < 3) { + try { + Loader.runScript("nith"); + } catch (nihlathakError) { + console.log("ÿc8Kolbot-SoloPlayÿc0: Nihlathak failed"); + } + } - return true; + return true; } diff --git a/libs/SoloPlay/Scripts/heart.js b/libs/SoloPlay/Scripts/heart.js index ca0d0dd0..8e08e76d 100644 --- a/libs/SoloPlay/Scripts/heart.js +++ b/libs/SoloPlay/Scripts/heart.js @@ -6,23 +6,23 @@ */ function heart () { - Town.doChores(false, { fullChores: true }); - myPrint("starting heart"); + Town.doChores(false, { fullChores: true }); + myPrint("starting heart"); - Pather.checkWP(sdk.areas.KurastBazaar, true) ? Pather.useWaypoint(sdk.areas.KurastBazaar) : Pather.getWP(sdk.areas.KurastBazaar); - Precast.doPrecast(true); + Pather.checkWP(sdk.areas.KurastBazaar, true) ? Pather.useWaypoint(sdk.areas.KurastBazaar) : Pather.getWP(sdk.areas.KurastBazaar); + Precast.doPrecast(true); - if (!Pather.journeyTo(sdk.areas.A3SewersLvl2) - || !Pather.moveToPresetObject(me.area, sdk.quest.chest.KhalimsHeartChest)) { - if (!me.getItem(sdk.items.quest.KhalimsHeart)) { - console.log("ÿc8Kolbot-SoloPlayÿc0: Failed to get the heart"); - return false; - } - } + if (!Pather.journeyTo(sdk.areas.A3SewersLvl2) + || !Pather.moveToPresetObject(me.area, sdk.quest.chest.KhalimsHeartChest)) { + if (!me.getItem(sdk.items.quest.KhalimsHeart)) { + console.log("ÿc8Kolbot-SoloPlayÿc0: Failed to get the heart"); + return false; + } + } - Attack.clear(0x7); // clear level - Quest.collectItem(sdk.items.quest.KhalimsHeart, sdk.quest.chest.KhalimsHeartChest); - Quest.stashItem(sdk.items.quest.KhalimsHeart); + Attack.clear(0x7); // clear level + Quest.collectItem(sdk.items.quest.KhalimsHeart, sdk.quest.chest.KhalimsHeartChest); + Quest.stashItem(sdk.items.quest.KhalimsHeart); - return me.getItem(sdk.items.quest.KhalimsHeart); + return me.getItem(sdk.items.quest.KhalimsHeart); } diff --git a/libs/SoloPlay/Scripts/hellforge.js b/libs/SoloPlay/Scripts/hellforge.js index 934dc196..a5b12085 100644 --- a/libs/SoloPlay/Scripts/hellforge.js +++ b/libs/SoloPlay/Scripts/hellforge.js @@ -6,82 +6,82 @@ */ function hellforge () { - if (Misc.checkQuest(sdk.quest.id.HellsForge, sdk.quest.states.ReqComplete)) { - Town.goToTown(4) && Town.npcInteract("cain"); - if (Misc.poll(() => Misc.checkQuest(sdk.quest.id.HellsForge, sdk.quest.states.Completed), 2000, 500)) return true; - } - myPrint("starting hellforge"); - Town.doChores(false, { thawing: me.coldRes < 75, antidote: me.poisonRes < 75, fullChores: true }); - - Pather.checkWP(sdk.areas.RiverofFlame, true) ? Pather.useWaypoint(sdk.areas.RiverofFlame) : Pather.getWP(sdk.areas.RiverofFlame); - Precast.doPrecast(true); + if (Misc.checkQuest(sdk.quest.id.HellsForge, sdk.quest.states.ReqComplete)) { + Town.goToTown(4) && Town.npcInteract("cain"); + if (Misc.poll(() => Misc.checkQuest(sdk.quest.id.HellsForge, sdk.quest.states.Completed), 2000, 500)) return true; + } + myPrint("starting hellforge"); + Town.doChores(false, { thawing: me.coldRes < 75, antidote: me.poisonRes < 75, fullChores: true }); + + Pather.checkWP(sdk.areas.RiverofFlame, true) ? Pather.useWaypoint(sdk.areas.RiverofFlame) : Pather.getWP(sdk.areas.RiverofFlame); + Precast.doPrecast(true); - /** - * @todo - * - Calculate safe spots to cast from so we can kill heph from a distance - * - Possibly try luring the rest of the monsters away from the forge? - * - Generate path and use callback to stop after we detect heph in range instead of moving all the way to the forge - */ + /** + * @todo + * - Calculate safe spots to cast from so we can kill heph from a distance + * - Possibly try luring the rest of the monsters away from the forge? + * - Generate path and use callback to stop after we detect heph in range instead of moving all the way to the forge + */ - if (!Pather.moveToPresetObject(me.area, sdk.quest.chest.HellForge, { callback: () => { - let heph = Game.getMonster(getLocaleString(sdk.locale.monsters.HephastoTheArmorer)); - return (heph && heph.distance < 30); - } })) { - console.warn("ÿc8Kolbot-SoloPlayÿc0: Failed to move to Hephasto"); - } + if (!Pather.moveToPresetObject(me.area, sdk.quest.chest.HellForge, { callback: () => { + let heph = Game.getMonster(getLocaleString(sdk.locale.monsters.HephastoTheArmorer)); + return (heph && heph.distance < 30); + } })) { + console.warn("ÿc8Kolbot-SoloPlayÿc0: Failed to move to Hephasto"); + } - try { - Attack.clear(20, 0, getLocaleString(sdk.locale.monsters.HephastoTheArmorer)); - } catch (err) { - console.warn("ÿc8Kolbot-SoloPlayÿc0: Failed to kill Hephasto"); - } + try { + Attack.clear(20, 0, getLocaleString(sdk.locale.monsters.HephastoTheArmorer)); + } catch (err) { + console.warn("ÿc8Kolbot-SoloPlayÿc0: Failed to kill Hephasto"); + } - Pickit.pickItems(); - let forge = Game.getObject(sdk.quest.chest.HellForge); - !!forge && Attack.clearPos(forge.x, forge.y, 25) && Attack.securePosition(forge.x, forge.y, 25, 3000); + Pickit.pickItems(); + let forge = Game.getObject(sdk.quest.chest.HellForge); + !!forge && Attack.clearPos(forge.x, forge.y, 25) && Attack.securePosition(forge.x, forge.y, 25, 3000); - if (!me.getItem(sdk.items.quest.HellForgeHammer)) { - // we don't have the hammer, is Hephasto dead? - let heph = getUnits(sdk.unittype.Monster).filter((unit) => unit.classid === sdk.monsters.Hephasto).first(); - !!heph && heph.attackable && Attack.kill(heph); - // hammer on ground? - let ham = getUnits(sdk.unittype.Item).filter((unit) => unit.classid === sdk.items.quest.HellForgeHammer).first(); - !!ham && ham.onGroundOrDropping && Pather.moveToUnit(ham) && Pickit.pickItem(ham); - // do we have the hammer now? - if (!me.getItem(sdk.items.quest.HellForgeHammer)) { - console.warn("Failed to collect Hellforge hammer"); - - return true; - } - } + if (!me.getItem(sdk.items.quest.HellForgeHammer)) { + // we don't have the hammer, is Hephasto dead? + let heph = getUnits(sdk.unittype.Monster).filter((unit) => unit.classid === sdk.monsters.Hephasto).first(); + !!heph && heph.attackable && Attack.kill(heph); + // hammer on ground? + let ham = getUnits(sdk.unittype.Item).filter((unit) => unit.classid === sdk.items.quest.HellForgeHammer).first(); + !!ham && ham.onGroundOrDropping && Pather.moveToUnit(ham) && Pickit.pickItem(ham); + // do we have the hammer now? + if (!me.getItem(sdk.items.quest.HellForgeHammer)) { + console.warn("Failed to collect Hellforge hammer"); + + return true; + } + } - Town.doChores(); - Town.npcInteract("cain"); + Town.doChores(); + Town.npcInteract("cain"); - let oldItem = me.getItemsEx().filter(function (item) { - return item.isEquipped && item.bodylocation === 4 && !item.isOnSwap; - }).first(); + let oldItem = me.getItemsEx().filter(function (item) { + return item.isEquipped && item.bodylocation === 4 && !item.isOnSwap; + }).first(); - if (!Quest.equipItem(sdk.items.quest.HellForgeHammer, 4)) { - console.warn("Failed to equip HellForge Hammer"); + if (!Quest.equipItem(sdk.items.quest.HellForgeHammer, 4)) { + console.warn("Failed to equip HellForge Hammer"); - return true; - } - - Pather.usePortal(sdk.areas.RiverofFlame, me.name); + return true; + } + + Pather.usePortal(sdk.areas.RiverofFlame, me.name); - if (!Pather.moveToPreset(me.area, sdk.unittype.Object, sdk.quest.chest.HellForge)) { - console.warn("ÿc8Kolbot-SoloPlayÿc0: Failed to move to forge"); + if (!Pather.moveToPreset(me.area, sdk.unittype.Object, sdk.quest.chest.HellForge)) { + console.warn("ÿc8Kolbot-SoloPlayÿc0: Failed to move to forge"); - return true; - } + return true; + } - Misc.openChest(sdk.quest.chest.HellForge); - Quest.smashSomething(sdk.quest.chest.HellForge) && delay(4500 + me.ping); - !!oldItem && oldItem.isInInventory && oldItem.equip(4); - Pickit.pickItems(); - Item.autoEquip(); - Town.npcInteract("cain"); + Misc.openChest(sdk.quest.chest.HellForge); + Quest.smashSomething(sdk.quest.chest.HellForge) && delay(4500 + me.ping); + !!oldItem && oldItem.isInInventory && oldItem.equip(4); + Pickit.pickItems(); + Item.autoEquip(); + Town.npcInteract("cain"); - return true; + return true; } diff --git a/libs/SoloPlay/Scripts/hephasto.js b/libs/SoloPlay/Scripts/hephasto.js index f01e2865..966ec48d 100644 --- a/libs/SoloPlay/Scripts/hephasto.js +++ b/libs/SoloPlay/Scripts/hephasto.js @@ -6,23 +6,23 @@ */ function hephasto() { - myPrint("starting hephasto"); - Town.doChores(null, { thawing: me.coldRes < 75, antidote: me.poisonRes < 75 }); + myPrint("starting hephasto"); + Town.doChores(null, { thawing: me.coldRes < 75, antidote: me.poisonRes < 75 }); - Pather.checkWP(sdk.areas.RiverofFlame, true) ? Pather.useWaypoint(sdk.areas.RiverofFlame) : Pather.getWP(sdk.areas.RiverofFlame); - Precast.doPrecast(true); + Pather.checkWP(sdk.areas.RiverofFlame, true) ? Pather.useWaypoint(sdk.areas.RiverofFlame) : Pather.getWP(sdk.areas.RiverofFlame); + Precast.doPrecast(true); - if (!Pather.moveToPreset(me.area, sdk.unittype.Object, sdk.quest.chest.HellForge)) { - console.warn("ÿc8Kolbot-SoloPlayÿc0: Failed to move to Hephasto"); - } + if (!Pather.moveToPreset(me.area, sdk.unittype.Object, sdk.quest.chest.HellForge)) { + console.warn("ÿc8Kolbot-SoloPlayÿc0: Failed to move to Hephasto"); + } - try { - Attack.killTarget(getLocaleString(sdk.locale.monsters.HephastoTheArmorer)); - } catch (err) { - console.log("ÿc8Kolbot-SoloPlayÿc0: Failed to kill Hephasto"); - } + try { + Attack.killTarget(getLocaleString(sdk.locale.monsters.HephastoTheArmorer)); + } catch (err) { + console.log("ÿc8Kolbot-SoloPlayÿc0: Failed to kill Hephasto"); + } - Pickit.pickItems(); + Pickit.pickItems(); - return true; + return true; } diff --git a/libs/SoloPlay/Scripts/izual.js b/libs/SoloPlay/Scripts/izual.js index 914a87ae..5f074b8f 100644 --- a/libs/SoloPlay/Scripts/izual.js +++ b/libs/SoloPlay/Scripts/izual.js @@ -6,20 +6,20 @@ */ function izual () { - myPrint("starting izual"); + myPrint("starting izual"); - Town.doChores(false, { thawing: true, antidote: true, stamina: true, fullChores: true }); + Town.doChores(false, { thawing: true, antidote: true, stamina: true, fullChores: true }); - Pather.checkWP(sdk.areas.CityoftheDamned, true) ? Pather.useWaypoint(sdk.areas.CityoftheDamned) : Pather.getWP(sdk.areas.CityoftheDamned); - Precast.doPrecast(true); - Pather.moveToPreset(sdk.areas.PlainsofDespair, sdk.unittype.Monster, sdk.monsters.Izual); - Attack.killTarget("Izual"); + Pather.checkWP(sdk.areas.CityoftheDamned, true) ? Pather.useWaypoint(sdk.areas.CityoftheDamned) : Pather.getWP(sdk.areas.CityoftheDamned); + Precast.doPrecast(true); + Pather.moveToPreset(sdk.areas.PlainsofDespair, sdk.unittype.Monster, sdk.monsters.Izual); + Attack.killTarget("Izual"); - if (!Misc.checkQuest(sdk.quest.id.TheFallenAngel, sdk.quest.states.Completed)) { - Town.goToTown(); - Town.npcInteract("tyrael"); - me.getStat(sdk.stats.NewSkills) > 0 && AutoSkill.init(Config.AutoSkill.Build, Config.AutoSkill.Save); - } + if (!Misc.checkQuest(sdk.quest.id.TheFallenAngel, sdk.quest.states.Completed)) { + Town.goToTown(); + Town.npcInteract("tyrael"); + me.getStat(sdk.stats.NewSkills) > 0 && AutoSkill.init(Config.AutoSkill.Build, Config.AutoSkill.Save); + } - return true; + return true; } diff --git a/libs/SoloPlay/Scripts/jail.js b/libs/SoloPlay/Scripts/jail.js index e9842856..7c28f7c7 100644 --- a/libs/SoloPlay/Scripts/jail.js +++ b/libs/SoloPlay/Scripts/jail.js @@ -6,33 +6,33 @@ */ function jail () { - Town.doChores(false, { fullChores: true }); - myPrint("starting jail"); - const levels = [sdk.areas.JailLvl1, sdk.areas.JailLvl2, sdk.areas.JailLvl3]; + Town.doChores(false, { fullChores: true }); + myPrint("starting jail"); + const levels = [sdk.areas.JailLvl1, sdk.areas.JailLvl2, sdk.areas.JailLvl3]; - Pather.checkWP(sdk.areas.JailLvl1, true) ? Pather.useWaypoint(sdk.areas.JailLvl1) : Pather.getWP(sdk.areas.JailLvl1); + Pather.checkWP(sdk.areas.JailLvl1, true) ? Pather.useWaypoint(sdk.areas.JailLvl1) : Pather.getWP(sdk.areas.JailLvl1); - for (let i = 1; i < levels.length; i++) { - myPrint("clearing jail level " + i); + for (let i = 1; i < levels.length; i++) { + myPrint("clearing jail level " + i); - Precast.doPrecast(true); - Attack.clearLevelEx({ quitWhen: function () { - if (!me.hell) return false; // don't quit - let dangerMob = Game.getMonster(); + Precast.doPrecast(true); + Attack.clearLevelEx({ quitWhen: function () { + if (!me.hell) return false; // don't quit + let dangerMob = Game.getMonster(); - if (dangerMob) { - do { - if (dangerMob.attackable && [sdk.monsters.Tainted, sdk.monsters.Tainted2].includes(dangerMob.classid)) { - myPrint("Tainted mob found. Moving to next level"); - return true; - } - } while (dangerMob.getNext()); - } + if (dangerMob) { + do { + if (dangerMob.attackable && [sdk.monsters.Tainted, sdk.monsters.Tainted2].includes(dangerMob.classid)) { + myPrint("Tainted mob found. Moving to next level"); + return true; + } + } while (dangerMob.getNext()); + } - return false; - } }); - Pather.clearToExit(me.area, levels[i], true); - } + return false; + } }); + Pather.clearToExit(me.area, levels[i], true); + } - return true; + return true; } diff --git a/libs/SoloPlay/Scripts/lamessen.js b/libs/SoloPlay/Scripts/lamessen.js index bdaec097..cd618f14 100644 --- a/libs/SoloPlay/Scripts/lamessen.js +++ b/libs/SoloPlay/Scripts/lamessen.js @@ -6,20 +6,20 @@ */ function lamessen () { - Town.doChores(false, { fullChores: true }); - myPrint("starting lamessen"); + Town.doChores(false, { fullChores: true }); + myPrint("starting lamessen"); - Pather.checkWP(sdk.areas.KurastBazaar, true) ? Pather.useWaypoint(sdk.areas.KurastBazaar) : Pather.getWP(sdk.areas.KurastBazaar); - Precast.doPrecast(true); + Pather.checkWP(sdk.areas.KurastBazaar, true) ? Pather.useWaypoint(sdk.areas.KurastBazaar) : Pather.getWP(sdk.areas.KurastBazaar); + Precast.doPrecast(true); - if (!Pather.moveToExit(sdk.areas.RuinedTemple, true) || !Pather.moveToPresetObject(me.area, sdk.quest.chest.LamEsensTomeHolder)) { - throw new Error("Failed to move to LamEssen Tome"); - } + if (!Pather.moveToExit(sdk.areas.RuinedTemple, true) || !Pather.moveToPresetObject(me.area, sdk.quest.chest.LamEsensTomeHolder)) { + throw new Error("Failed to move to LamEssen Tome"); + } - if (!Misc.checkQuest(sdk.quest.id.LamEsensTome, sdk.quest.states.Completed)) { - Quest.collectItem(sdk.items.quest.LamEsensTome, sdk.quest.chest.LamEsensTomeHolder); - } - Quest.unfinishedQuests(); + if (!Misc.checkQuest(sdk.quest.id.LamEsensTome, sdk.quest.states.Completed)) { + Quest.collectItem(sdk.items.quest.LamEsensTome, sdk.quest.chest.LamEsensTomeHolder); + } + Quest.unfinishedQuests(); - return true; + return true; } diff --git a/libs/SoloPlay/Scripts/lowerkurast.js b/libs/SoloPlay/Scripts/lowerkurast.js index 1d81f224..e01386a3 100644 --- a/libs/SoloPlay/Scripts/lowerkurast.js +++ b/libs/SoloPlay/Scripts/lowerkurast.js @@ -6,12 +6,12 @@ */ function lowerkurast () { - Town.doChores(false, { fullChores: true }); - myPrint("starting lower kurast"); + Town.doChores(false, { fullChores: true }); + myPrint("starting lower kurast"); - Pather.checkWP(sdk.areas.LowerKurast, true) ? Pather.useWaypoint(sdk.areas.LowerKurast) : Pather.getWP(sdk.areas.LowerKurast); - Precast.doPrecast(true); - Misc.openChestsInArea(sdk.areas.LowerKurast); + Pather.checkWP(sdk.areas.LowerKurast, true) ? Pather.useWaypoint(sdk.areas.LowerKurast) : Pather.getWP(sdk.areas.LowerKurast); + Precast.doPrecast(true); + Misc.openChestsInArea(sdk.areas.LowerKurast); - return true; + return true; } diff --git a/libs/SoloPlay/Scripts/maggotlair.js b/libs/SoloPlay/Scripts/maggotlair.js index 1bb367cd..bfe75480 100644 --- a/libs/SoloPlay/Scripts/maggotlair.js +++ b/libs/SoloPlay/Scripts/maggotlair.js @@ -6,53 +6,53 @@ */ function maggotlair () { - const clearBeetles = function () { - let room = getRoom(); - if (!room) return false; + const clearBeetles = function () { + let room = getRoom(); + if (!room) return false; - let rooms = []; + let rooms = []; - do { - rooms.push([room.x * 5 + room.xsize / 2, room.y * 5 + room.ysize / 2]); - } while (room.getNext()); + do { + rooms.push([room.x * 5 + room.xsize / 2, room.y * 5 + room.ysize / 2]); + } while (room.getNext()); - while (rooms.length > 0) { - rooms.sort(Sort.points); - room = rooms.shift(); + while (rooms.length > 0) { + rooms.sort(Sort.points); + room = rooms.shift(); - let result = Pather.getNearestWalkable(room[0], room[1], 15, 2); + let result = Pather.getNearestWalkable(room[0], room[1], 15, 2); - if (result) { - Pather.moveTo(result[0], result[1], 3); + if (result) { + Pather.moveTo(result[0], result[1], 3); - let monList = []; - let monster = Game.getMonster(); + let monList = []; + let monster = Game.getMonster(); - if (monster) { - do { - if (monster.isBeetle && monster.distance <= 30 && monster.attackable) { - monList.push(copyUnit(monster)); - } - } while (monster.getNext()); - } + if (monster) { + do { + if (monster.isBeetle && monster.distance <= 30 && monster.attackable) { + monList.push(copyUnit(monster)); + } + } while (monster.getNext()); + } - if (!Attack.clearList(monList)) return false; - } - } + if (!Attack.clearList(monList)) return false; + } + } - return true; - }; + return true; + }; - // START - Town.doChores(false, { fullChores: true }); - myPrint("starting maggot lair beetle bursting"); + // START + Town.doChores(false, { fullChores: true }); + myPrint("starting maggot lair beetle bursting"); - Pather.checkWP(sdk.areas.FarOasis, true) ? Pather.useWaypoint(sdk.areas.FarOasis) : Pather.getWP(sdk.areas.FarOasis); - Precast.doPrecast(true); - [sdk.areas.MaggotLairLvl1, sdk.areas.MaggotLairLvl2, sdk.areas.MaggotLairLvl3].forEach((mLair, i) => { - Pather.moveToExit(mLair, true) && clearBeetles(); - console.log("Cleared mLair " + (i + 1)); - }); + Pather.checkWP(sdk.areas.FarOasis, true) ? Pather.useWaypoint(sdk.areas.FarOasis) : Pather.getWP(sdk.areas.FarOasis); + Precast.doPrecast(true); + [sdk.areas.MaggotLairLvl1, sdk.areas.MaggotLairLvl2, sdk.areas.MaggotLairLvl3].forEach((mLair, i) => { + Pather.moveToExit(mLair, true) && clearBeetles(); + console.log("Cleared mLair " + (i + 1)); + }); - return true; + return true; } diff --git a/libs/SoloPlay/Scripts/mausoleum.js b/libs/SoloPlay/Scripts/mausoleum.js index 65513f23..4ef5c8cd 100644 --- a/libs/SoloPlay/Scripts/mausoleum.js +++ b/libs/SoloPlay/Scripts/mausoleum.js @@ -6,32 +6,32 @@ */ function mausoleum () { - Town.doChores(); - myPrint("ÿc8Kolbot-SoloPlayÿc0: starting mausoleum"); + Town.doChores(); + myPrint("ÿc8Kolbot-SoloPlayÿc0: starting mausoleum"); - me.gold > 1000 && Town.buyPots(12, "stamina", true); + me.gold > 1000 && Town.buyPots(12, "stamina", true); - if (!Pather.checkWP(sdk.areas.ColdPlains)) { - Pather.moveToExit(sdk.areas.BloodMoor, true); + if (!Pather.checkWP(sdk.areas.ColdPlains)) { + Pather.moveToExit(sdk.areas.BloodMoor, true); - try { - Pather.moveToPreset(sdk.areas.BloodMoor, sdk.unittype.Object, sdk.objects.SuperChest) && Misc.openChests(5); - } catch (e) { - console.warn(e.message ? e.message : e); - } - Pather.getWP(sdk.areas.ColdPlains); + try { + Pather.moveToPreset(sdk.areas.BloodMoor, sdk.unittype.Object, sdk.objects.SuperChest) && Misc.openChests(5); + } catch (e) { + console.warn(e.message ? e.message : e); + } + Pather.getWP(sdk.areas.ColdPlains); - // check if we need to do chores - Storage.Inventory.UsedSpacePercent() > 50 && Pather.useWaypoint(sdk.areas.RogueEncampment) && Town.doChores(); - } + // check if we need to do chores + Storage.Inventory.UsedSpacePercent() > 50 && Pather.useWaypoint(sdk.areas.RogueEncampment) && Town.doChores(); + } - Pather.useWaypoint(sdk.areas.ColdPlains); - Precast.doPrecast(true); - Pather.moveToExit([sdk.areas.BurialGrounds, sdk.areas.Mausoleum], true); - // need to figure out better clearLevel method, for now just clear to superchest + Pather.useWaypoint(sdk.areas.ColdPlains); + Precast.doPrecast(true); + Pather.moveToExit([sdk.areas.BurialGrounds, sdk.areas.Mausoleum], true); + // need to figure out better clearLevel method, for now just clear to superchest - me.inArea(sdk.areas.Mausoleum) && Pather.moveToPreset(sdk.areas.Mausoleum, sdk.unittype.Object, sdk.objects.SmallSparklyChest); - Misc.openChests(5); - - return true; + me.inArea(sdk.areas.Mausoleum) && Pather.moveToPreset(sdk.areas.Mausoleum, sdk.unittype.Object, sdk.objects.SmallSparklyChest); + Misc.openChests(5); + + return true; } diff --git a/libs/SoloPlay/Scripts/mephisto.js b/libs/SoloPlay/Scripts/mephisto.js index 5ecb6b39..f763baa9 100644 --- a/libs/SoloPlay/Scripts/mephisto.js +++ b/libs/SoloPlay/Scripts/mephisto.js @@ -6,75 +6,75 @@ */ function mephisto () { - const killCouncil = function () { - let coords = [17600, 8125, 17600, 8015, 17643, 8068]; + const killCouncil = function () { + let coords = [17600, 8125, 17600, 8015, 17643, 8068]; - for (let i = 0; i < coords.length; i += 2) { - [coords[i], coords[i + 1]].distance > 60 && Pather.moveNear(coords[i], coords[i + 1], 60); - if ([coords[i], coords[i + 1]].mobCount({ range: 30 }) === 0) continue; - Pather.moveTo(coords[i], coords[i + 1]); - Attack.clearList(Attack.getMob([sdk.monsters.Council1, sdk.monsters.Council2, sdk.monsters.Council3], 0, 40)); - } + for (let i = 0; i < coords.length; i += 2) { + [coords[i], coords[i + 1]].distance > 60 && Pather.moveNear(coords[i], coords[i + 1], 60); + if ([coords[i], coords[i + 1]].mobCount({ range: 30 }) === 0) continue; + Pather.moveTo(coords[i], coords[i + 1]); + Attack.clearList(Attack.getMob([sdk.monsters.Council1, sdk.monsters.Council2, sdk.monsters.Council3], 0, 40)); + } - return true; - }; + return true; + }; - Town.doChores(false, { fullChores: true }); - myPrint("starting mephisto"); + Town.doChores(false, { fullChores: true }); + myPrint("starting mephisto"); - Pather.checkWP(sdk.areas.DuranceofHateLvl2, true) ? Pather.useWaypoint(sdk.areas.DuranceofHateLvl2) : Pather.getWP(sdk.areas.DuranceofHateLvl2); - Precast.doPrecast(true); - const oldCPRange = Config.ClearPath.Range; - const canTele = Pather.canTeleport(); - try { - canTele && (Config.ClearPath.Range = 0); - canTele - ? Pather.moveToExit(sdk.areas.DuranceofHateLvl3, true, false) - : Pather.clearToExit(sdk.areas.DuranceofHateLvl2, sdk.areas.DuranceofHateLvl3, true); - } finally { - oldCPRange !== Config.ClearPath.Range && (Config.ClearPath.Range = oldCPRange); - } - - if (!me.inArea(sdk.areas.DuranceofHateLvl3)) { - console.log("ÿc8Kolbot-SoloPlayÿc0: Failed to move to mephisto"); - return false; - } + Pather.checkWP(sdk.areas.DuranceofHateLvl2, true) ? Pather.useWaypoint(sdk.areas.DuranceofHateLvl2) : Pather.getWP(sdk.areas.DuranceofHateLvl2); + Precast.doPrecast(true); + const oldCPRange = Config.ClearPath.Range; + const canTele = Pather.canTeleport(); + try { + canTele && (Config.ClearPath.Range = 0); + canTele + ? Pather.moveToExit(sdk.areas.DuranceofHateLvl3, true, false) + : Pather.clearToExit(sdk.areas.DuranceofHateLvl2, sdk.areas.DuranceofHateLvl3, true); + } finally { + oldCPRange !== Config.ClearPath.Range && (Config.ClearPath.Range = oldCPRange); + } + + if (!me.inArea(sdk.areas.DuranceofHateLvl3)) { + console.log("ÿc8Kolbot-SoloPlayÿc0: Failed to move to mephisto"); + return false; + } - // Town stuff - if (me.coldRes < 75 || me.poisonRes < 75) { - Town.doChores(null, { thawing: me.coldRes < 75, antidote: me.poisonRes < 75 }); - // Re-enter portal - Pather.usePortal(sdk.areas.DuranceofHateLvl3, me.name); - Precast.doPrecast(true); - } + // Town stuff + if (me.coldRes < 75 || me.poisonRes < 75) { + Town.doChores(null, { thawing: me.coldRes < 75, antidote: me.poisonRes < 75 }); + // Re-enter portal + Pather.usePortal(sdk.areas.DuranceofHateLvl3, me.name); + Precast.doPrecast(true); + } - const oldPickRange = Config.PickRange; - const oldUseMerc = Config.MercWatch; + const oldPickRange = Config.PickRange; + const oldUseMerc = Config.MercWatch; - if (me.mephisto) { - // activate bridge - Pather.moveTo(17587, 8069); - delay(400); - } + if (me.mephisto) { + // activate bridge + Pather.moveTo(17587, 8069); + delay(400); + } - Pather.moveTo(17563, 8072); - Attack.killTarget("Mephisto"); + Pather.moveTo(17563, 8072); + Attack.killTarget("Mephisto"); - // Reset to normal value - Config.MercWatch !== oldUseMerc && (Config.MercWatch = oldUseMerc); - Config.PickRange !== oldPickRange && (Config.PickRange = oldPickRange); - - Pickit.pickItems(); - me.mephisto && !me.hell && killCouncil(); + // Reset to normal value + Config.MercWatch !== oldUseMerc && (Config.MercWatch = oldUseMerc); + Config.PickRange !== oldPickRange && (Config.PickRange = oldPickRange); + + Pickit.pickItems(); + me.mephisto && !me.hell && killCouncil(); - Pather.moveTo(17581, 8070); - delay(250 + me.ping * 2); - Pather.useUnit(sdk.unittype.Object, sdk.objects.RedPortalToAct4, sdk.areas.PandemoniumFortress); - Misc.poll(() => me.inArea(sdk.areas.PandemoniumFortress), 1000, 30); + Pather.moveTo(17581, 8070); + delay(250 + me.ping * 2); + Pather.useUnit(sdk.unittype.Object, sdk.objects.RedPortalToAct4, sdk.areas.PandemoniumFortress); + Misc.poll(() => me.inArea(sdk.areas.PandemoniumFortress), 1000, 30); - while (!me.gameReady) { - delay(40); - } + while (!me.gameReady) { + delay(40); + } - return me.inArea(sdk.areas.PandemoniumFortress); + return me.inArea(sdk.areas.PandemoniumFortress); } diff --git a/libs/SoloPlay/Scripts/nith.js b/libs/SoloPlay/Scripts/nith.js index 97aebe75..e07dfe22 100644 --- a/libs/SoloPlay/Scripts/nith.js +++ b/libs/SoloPlay/Scripts/nith.js @@ -7,29 +7,29 @@ */ function nith() { - Town.doChores(); - myPrint("starting nith"); + Town.doChores(); + myPrint("starting nith"); - Pather.checkWP(sdk.areas.HallsofPain, true) ? Pather.useWaypoint(sdk.areas.HallsofPain) : Pather.getWP(sdk.areas.HallsofPain); - Precast.doPrecast(false); + Pather.checkWP(sdk.areas.HallsofPain, true) ? Pather.useWaypoint(sdk.areas.HallsofPain) : Pather.getWP(sdk.areas.HallsofPain); + Precast.doPrecast(false); - if (!Pather.moveToExit(sdk.areas.HallsofVaught, true)) { - console.log("ÿc8Kolbot-SoloPlayÿc0: Failed to go to Nihlathak"); - - return true; - } + if (!Pather.moveToExit(sdk.areas.HallsofVaught, true)) { + console.log("ÿc8Kolbot-SoloPlayÿc0: Failed to go to Nihlathak"); + + return true; + } - Pather.moveToPreset(me.area, sdk.unittype.Object, sdk.objects.NihlathaksPlatform); + Pather.moveToPreset(me.area, sdk.unittype.Object, sdk.objects.NihlathaksPlatform); - // Stop script in hardcore mode if vipers are found - if (me.hardcore && Game.getMonster(sdk.monsters.TombViper2)) { - console.log("Tomb Vipers found."); + // Stop script in hardcore mode if vipers are found + if (me.hardcore && Game.getMonster(sdk.monsters.TombViper2)) { + console.log("Tomb Vipers found."); - return true; - } + return true; + } - Attack.killTarget(sdk.monsters.Nihlathak); - Pickit.pickItems(); + Attack.killTarget(sdk.monsters.Nihlathak); + Pickit.pickItems(); - return true; + return true; } diff --git a/libs/SoloPlay/Scripts/orgtorch.js b/libs/SoloPlay/Scripts/orgtorch.js index 3b718a37..59f0ccf7 100644 --- a/libs/SoloPlay/Scripts/orgtorch.js +++ b/libs/SoloPlay/Scripts/orgtorch.js @@ -6,361 +6,361 @@ */ function orgtorch() { - this.doneAreas = []; - - // Identify & mule - this.checkTorch = function () { - if (me.inArea(sdk.areas.UberTristram)) { - Pather.moveTo(25105, 5140); - Pather.usePortal(sdk.areas.Harrogath); - } - - Town.doChores(); - - let torch = me.findItem(604, 0, null, 7); - - if (!torch || torch === undefined) { - return false; - } - - if (!torch.identified) { - Town.identify(); - } - - for (let i = 0; i < 7; i++) { - if (torch.getStat(sdk.stats.AddClassSkills, i)) { - SoloEvents.sendToList({ profile: me.profile, ladder: me.ladder, torchType: i }); - - return true; - } - } - - return false; - }; - - // Check whether the killer is alone in the game - this.aloneInGame = function () { - let party = getParty(); - - if (party) { - do { - if (party.name !== me.name) { - return false; - } - } while (party.getNext()); - } - - return true; - }; - - // Try to lure a monster - wait until it's close enough - this.lure = function (bossId) { - let tick, - unit = Game.getMonster(bossId); - - if (unit) { - tick = getTickCount(); - - while (getTickCount() - tick < 2000) { - if (unit.distance <= 10) { - return true; - } - - delay(50); - } - } - - return false; - }; - - // Check if we have complete sets of organs - this.completeSetCheck = function () { - let horns = me.findItems(sdk.items.quest.DiablosHorn), - brains = me.findItems(sdk.items.quest.MephistosBrain), - eyes = me.findItems(sdk.items.quest.BaalsEye); - - if (!horns || !brains || !eyes) { - return false; - } - - // We just need one set to make a torch - return horns.length && brains.length && eyes.length; - }; - - // Get fade in River of Flames - this.getFade = function () { - if (!me.getState(sdk.states.Fade) - && (me.checkItem({ name: sdk.locale.items.Treachery, equipped: true }).have - || me.checkItem({ name: sdk.locale.items.LastWish, equipped: true }).have - || me.checkItem({ name: sdk.locale.items.SpiritWard, equipped: true }).have)) { - if (!me.getState(sdk.states.Fade)) { - myPrint(sdk.colors.Orange + "OrgTorch :: " + sdk.colors.White + "Getting Fade"); - Pather.useWaypoint(sdk.states.RiverofFlame); - Precast.doPrecast(true); - Pather.moveTo(7811, 5872); - - me.paladin && me.checkSkill(sdk.skills.Salvation, sdk.skills.subindex.SoftPoints) && Skill.setSkill(sdk.skills.Salvation, sdk.skills.hand.Right); - - while (!me.getState(sdk.states.Fade)) { - delay(100); - } - - myPrint(sdk.colors.Orange + "OrgTorch :: " + sdk.colors.Green + "Fade Achieved"); - } - } - - return true; - }; - - // Open a red portal. Mode 0 = mini ubers, mode 1 = Tristram - this.openPortal = function (mode) { - let portal, - item1 = mode === 0 - ? me.findItem(sdk.items.quest.KeyofTerror, sdk.items.mode.inStorage) - : me.findItem(sdk.items.quest.DiablosHorn, sdk.items.mode.inStorage), - item2 = mode === 0 - ? me.findItem(sdk.items.quest.KeyofHate, sdk.items.mode.inStorage) - : me.findItem(sdk.items.quest.BaalsEye, sdk.items.mode.inStorage), - item3 = mode === 0 - ? me.findItem(sdk.items.quest.KeyofDestruction, sdk.items.mode.inStorage) - : me.findItem(sdk.items.quest.MephistosBrain, sdk.items.mode.inStorage); - - Town.goToTown(5); - Town.doChores(); - - if (Town.openStash() && Cubing.emptyCube()) { - if (!Storage.Cube.MoveTo(item1) - || !Storage.Cube.MoveTo(item2) - || !Storage.Cube.MoveTo(item3) - || !Cubing.openCube()) { - return false; - } - - transmute(); - delay(1000); - - portal = Game.getObject("portal"); - - if (portal) { - do { - switch (mode) { - case 0: - if ([sdk.areas.MatronsDen, sdk.areas.ForgottenSands, sdk.areas.FurnaceofPain].indexOf(portal.objtype) > -1 && this.doneAreas.indexOf(portal.objtype) === -1) { - this.doneAreas.push(portal.objtype); - - return copyUnit(portal); - } - - break; - case 1: - if (portal.objtype === sdk.areas.UberTristram) { - return copyUnit(portal); - } - - break; - } - } while (portal.getNext()); - } - } - - return false; - }; - - // Do mini ubers or Tristram based on area we're already in - this.pandemoniumRun = function () { - let i, findLoc; - - switch (me.area) { - case sdk.areas.MatronsDen: - Precast.doPrecast(true); - Pather.moveToPreset(sdk.areas.MatronsDen, sdk.unittype.Object, 397, 2, 2); - Attack.killTarget(707); - Pickit.pickItems(); - Town.goToTown(); - - break; - case sdk.areas.ForgottenSands: - Precast.doPrecast(true); - - findLoc = [20196, 8694, 20308, 8588, 20187, 8639, 20100, 8550, 20103, 8688, 20144, 8709, 20263, 8811, 20247, 8665]; - - for (i = 0; i < findLoc.length; i += 2) { - Pather.moveTo(findLoc[i], findLoc[i + 1]); - delay(500); - - if (Game.getMonster(708)) { - break; - } - } - - Attack.killTarget(708); - Pickit.pickItems(); - Town.goToTown(); - - break; - case sdk.areas.FurnaceofPain: - Precast.doPrecast(true); - Pather.moveToPreset(sdk.areas.FurnaceofPain, sdk.unittype.Object, 397, 2, 2); - Attack.killTarget(706); - Pickit.pickItems(); - Town.goToTown(); - - break; - case sdk.areas.UberTristram: - Pather.moveTo(25068, 5078); - Precast.doPrecast(true); - - findLoc = [25040, 5101, 25040, 5166, 25122, 5170]; - - for (i = 0; i < findLoc.length; i += 2) { - Pather.moveTo(findLoc[i], findLoc[i + 1]); - } - - if (me.paladin && me.checkSkill(sdk.skills.Salvation, sdk.skills.subindex.SoftPoints)) { - Skill.setSkill(sdk.skills.Salvation, sdk.skills.hand.Right); - } - - this.lure(704); - Pather.moveTo(25129, 5198); - - if (me.paladin && me.checkSkill(sdk.skills.Salvation, sdk.skills.subindex.SoftPoints)) { - Skill.setSkill(sdk.skills.Salvation, sdk.skills.hand.Right); - } - - this.lure(704); - - if (!Game.getMonster(704)) { - Pather.moveTo(25122, 5170); - } - - Attack.killTarget(704); - - Pather.moveTo(25162, 5141); - delay(3250); - - if (!Game.getMonster(709)) { - Pather.moveTo(25122, 5170); - } - - Attack.killTarget(709); - - if (!Game.getMonster(705)) { - Pather.moveTo(25122, 5170); - } - - Attack.killTarget(705); - Pickit.pickItems(); - this.checkTorch(); - - break; - } - }; - - this.juvCheck = function () { - let i, - needJuvs = 0, - col = Storage.Belt.checkColumns(Storage.BeltSize()); - - for (i = 0; i < 4; i += 1) { - if (Config.BeltColumn[i] === "rv") { - needJuvs += col[i]; - } - } - - console.log("Need " + needJuvs + " juvs."); - - return needJuvs; - }; - - // Start - let i, portal, tkeys, hkeys, dkeys, brains, eyes, horns; - - // Do town chores and quit if MakeTorch is true and we have a torch. - this.checkTorch(); + this.doneAreas = []; + + // Identify & mule + this.checkTorch = function () { + if (me.inArea(sdk.areas.UberTristram)) { + Pather.moveTo(25105, 5140); + Pather.usePortal(sdk.areas.Harrogath); + } + + Town.doChores(); + + let torch = me.findItem(604, 0, null, 7); + + if (!torch || torch === undefined) { + return false; + } + + if (!torch.identified) { + Town.identify(); + } + + for (let i = 0; i < 7; i++) { + if (torch.getStat(sdk.stats.AddClassSkills, i)) { + SoloEvents.sendToList({ profile: me.profile, ladder: me.ladder, torchType: i }); + + return true; + } + } + + return false; + }; + + // Check whether the killer is alone in the game + this.aloneInGame = function () { + let party = getParty(); + + if (party) { + do { + if (party.name !== me.name) { + return false; + } + } while (party.getNext()); + } + + return true; + }; + + // Try to lure a monster - wait until it's close enough + this.lure = function (bossId) { + let tick, + unit = Game.getMonster(bossId); + + if (unit) { + tick = getTickCount(); + + while (getTickCount() - tick < 2000) { + if (unit.distance <= 10) { + return true; + } + + delay(50); + } + } + + return false; + }; + + // Check if we have complete sets of organs + this.completeSetCheck = function () { + let horns = me.findItems(sdk.items.quest.DiablosHorn), + brains = me.findItems(sdk.items.quest.MephistosBrain), + eyes = me.findItems(sdk.items.quest.BaalsEye); + + if (!horns || !brains || !eyes) { + return false; + } + + // We just need one set to make a torch + return horns.length && brains.length && eyes.length; + }; + + // Get fade in River of Flames + this.getFade = function () { + if (!me.getState(sdk.states.Fade) + && (me.checkItem({ name: sdk.locale.items.Treachery, equipped: true }).have + || me.checkItem({ name: sdk.locale.items.LastWish, equipped: true }).have + || me.checkItem({ name: sdk.locale.items.SpiritWard, equipped: true }).have)) { + if (!me.getState(sdk.states.Fade)) { + myPrint(sdk.colors.Orange + "OrgTorch :: " + sdk.colors.White + "Getting Fade"); + Pather.useWaypoint(sdk.states.RiverofFlame); + Precast.doPrecast(true); + Pather.moveTo(7811, 5872); + + me.paladin && me.checkSkill(sdk.skills.Salvation, sdk.skills.subindex.SoftPoints) && Skill.setSkill(sdk.skills.Salvation, sdk.skills.hand.Right); + + while (!me.getState(sdk.states.Fade)) { + delay(100); + } + + myPrint(sdk.colors.Orange + "OrgTorch :: " + sdk.colors.Green + "Fade Achieved"); + } + } + + return true; + }; + + // Open a red portal. Mode 0 = mini ubers, mode 1 = Tristram + this.openPortal = function (mode) { + let portal, + item1 = mode === 0 + ? me.findItem(sdk.items.quest.KeyofTerror, sdk.items.mode.inStorage) + : me.findItem(sdk.items.quest.DiablosHorn, sdk.items.mode.inStorage), + item2 = mode === 0 + ? me.findItem(sdk.items.quest.KeyofHate, sdk.items.mode.inStorage) + : me.findItem(sdk.items.quest.BaalsEye, sdk.items.mode.inStorage), + item3 = mode === 0 + ? me.findItem(sdk.items.quest.KeyofDestruction, sdk.items.mode.inStorage) + : me.findItem(sdk.items.quest.MephistosBrain, sdk.items.mode.inStorage); + + Town.goToTown(5); + Town.doChores(); + + if (Town.openStash() && Cubing.emptyCube()) { + if (!Storage.Cube.MoveTo(item1) + || !Storage.Cube.MoveTo(item2) + || !Storage.Cube.MoveTo(item3) + || !Cubing.openCube()) { + return false; + } + + transmute(); + delay(1000); + + portal = Game.getObject("portal"); + + if (portal) { + do { + switch (mode) { + case 0: + if ([sdk.areas.MatronsDen, sdk.areas.ForgottenSands, sdk.areas.FurnaceofPain].indexOf(portal.objtype) > -1 && this.doneAreas.indexOf(portal.objtype) === -1) { + this.doneAreas.push(portal.objtype); + + return copyUnit(portal); + } + + break; + case 1: + if (portal.objtype === sdk.areas.UberTristram) { + return copyUnit(portal); + } + + break; + } + } while (portal.getNext()); + } + } + + return false; + }; + + // Do mini ubers or Tristram based on area we're already in + this.pandemoniumRun = function () { + let i, findLoc; + + switch (me.area) { + case sdk.areas.MatronsDen: + Precast.doPrecast(true); + Pather.moveToPreset(sdk.areas.MatronsDen, sdk.unittype.Object, 397, 2, 2); + Attack.killTarget(707); + Pickit.pickItems(); + Town.goToTown(); + + break; + case sdk.areas.ForgottenSands: + Precast.doPrecast(true); + + findLoc = [20196, 8694, 20308, 8588, 20187, 8639, 20100, 8550, 20103, 8688, 20144, 8709, 20263, 8811, 20247, 8665]; + + for (i = 0; i < findLoc.length; i += 2) { + Pather.moveTo(findLoc[i], findLoc[i + 1]); + delay(500); + + if (Game.getMonster(708)) { + break; + } + } + + Attack.killTarget(708); + Pickit.pickItems(); + Town.goToTown(); + + break; + case sdk.areas.FurnaceofPain: + Precast.doPrecast(true); + Pather.moveToPreset(sdk.areas.FurnaceofPain, sdk.unittype.Object, 397, 2, 2); + Attack.killTarget(706); + Pickit.pickItems(); + Town.goToTown(); + + break; + case sdk.areas.UberTristram: + Pather.moveTo(25068, 5078); + Precast.doPrecast(true); + + findLoc = [25040, 5101, 25040, 5166, 25122, 5170]; + + for (i = 0; i < findLoc.length; i += 2) { + Pather.moveTo(findLoc[i], findLoc[i + 1]); + } + + if (me.paladin && me.checkSkill(sdk.skills.Salvation, sdk.skills.subindex.SoftPoints)) { + Skill.setSkill(sdk.skills.Salvation, sdk.skills.hand.Right); + } + + this.lure(704); + Pather.moveTo(25129, 5198); + + if (me.paladin && me.checkSkill(sdk.skills.Salvation, sdk.skills.subindex.SoftPoints)) { + Skill.setSkill(sdk.skills.Salvation, sdk.skills.hand.Right); + } + + this.lure(704); + + if (!Game.getMonster(704)) { + Pather.moveTo(25122, 5170); + } + + Attack.killTarget(704); + + Pather.moveTo(25162, 5141); + delay(3250); + + if (!Game.getMonster(709)) { + Pather.moveTo(25122, 5170); + } + + Attack.killTarget(709); + + if (!Game.getMonster(705)) { + Pather.moveTo(25122, 5170); + } + + Attack.killTarget(705); + Pickit.pickItems(); + this.checkTorch(); + + break; + } + }; + + this.juvCheck = function () { + let i, + needJuvs = 0, + col = Storage.Belt.checkColumns(Storage.BeltSize()); + + for (i = 0; i < 4; i += 1) { + if (Config.BeltColumn[i] === "rv") { + needJuvs += col[i]; + } + } + + console.log("Need " + needJuvs + " juvs."); + + return needJuvs; + }; + + // Start + let i, portal, tkeys, hkeys, dkeys, brains, eyes, horns; + + // Do town chores and quit if MakeTorch is true and we have a torch. + this.checkTorch(); - // Count keys and organs - tkeys = me.findItems(sdk.items.quest.KeyofTerror, sdk.items.mode.inStorage).length || 0; - hkeys = me.findItems(sdk.items.quest.KeyofHate, sdk.items.mode.inStorage).length || 0; - dkeys = me.findItems(sdk.items.quest.KeyofDestruction, sdk.items.mode.inStorage).length || 0; - brains = me.findItems(sdk.items.quest.MephistosBrain, sdk.items.mode.inStorage).length || 0; - eyes = me.findItems(sdk.items.quest.BaalsEye, sdk.items.mode.inStorage).length || 0; - horns = me.findItems(sdk.items.quest.DiablosHorn, sdk.items.mode.inStorage).length || 0; - - // End the script if we don't have enough keys nor organs - if ((tkeys < 3 || hkeys < 3 || dkeys < 3) && (brains < 1 || eyes < 1 || horns < 1)) { - console.log("ÿc8Kolbot-SoloPlayÿc0: Not enough keys or organs."); - - return true; - } + // Count keys and organs + tkeys = me.findItems(sdk.items.quest.KeyofTerror, sdk.items.mode.inStorage).length || 0; + hkeys = me.findItems(sdk.items.quest.KeyofHate, sdk.items.mode.inStorage).length || 0; + dkeys = me.findItems(sdk.items.quest.KeyofDestruction, sdk.items.mode.inStorage).length || 0; + brains = me.findItems(sdk.items.quest.MephistosBrain, sdk.items.mode.inStorage).length || 0; + eyes = me.findItems(sdk.items.quest.BaalsEye, sdk.items.mode.inStorage).length || 0; + horns = me.findItems(sdk.items.quest.DiablosHorn, sdk.items.mode.inStorage).length || 0; + + // End the script if we don't have enough keys nor organs + if ((tkeys < 3 || hkeys < 3 || dkeys < 3) && (brains < 1 || eyes < 1 || horns < 1)) { + console.log("ÿc8Kolbot-SoloPlayÿc0: Not enough keys or organs."); + + return true; + } - Config.UseMerc = false; - - // We have enough keys, do mini ubers - if (tkeys >= 3 && hkeys >= 3 && dkeys >= 3) { - this.getFade(); - console.log("ÿc8Kolbot-SoloPlayÿc0: Making organs."); - D2Bot.printToConsole("ÿc8Kolbot-SoloPlayÿc0 :: OrgTorch: Making organs.", sdk.colors.D2Bot.Orange); + Config.UseMerc = false; + + // We have enough keys, do mini ubers + if (tkeys >= 3 && hkeys >= 3 && dkeys >= 3) { + this.getFade(); + console.log("ÿc8Kolbot-SoloPlayÿc0: Making organs."); + D2Bot.printToConsole("ÿc8Kolbot-SoloPlayÿc0 :: OrgTorch: Making organs.", sdk.colors.D2Bot.Orange); - for (i = 0; i < 3; i += 1) { - // Abort if we have a complete set of organs - // check after at least one portal is made - if (i > 0 && this.completeSetCheck()) { - break; - } + for (i = 0; i < 3; i += 1) { + // Abort if we have a complete set of organs + // check after at least one portal is made + if (i > 0 && this.completeSetCheck()) { + break; + } - portal = this.openPortal(0); + portal = this.openPortal(0); - if (portal) { - switch (portal.objtype) { - case sdk.areas.MatronsDen: - Town.buyPots(10, "Thawing", true, true); - Town.buyPots(10, "Antidote", true, true); - Town.buyPots(10, "Antidote", true, true); // Double stack to ensure it lasts + if (portal) { + switch (portal.objtype) { + case sdk.areas.MatronsDen: + Town.buyPots(10, "Thawing", true, true); + Town.buyPots(10, "Antidote", true, true); + Town.buyPots(10, "Antidote", true, true); // Double stack to ensure it lasts - break; - case sdk.areas.ForgottenSands: - Town.buyPots(10, "Thawing", true, true); + break; + case sdk.areas.ForgottenSands: + Town.buyPots(10, "Thawing", true, true); - break; - case sdk.areas.FurnaceofPain: - Town.buyPots(10, "Thawing", true, true); - Town.buyPots(10, "Antidote", true, true); + break; + case sdk.areas.FurnaceofPain: + Town.buyPots(10, "Thawing", true, true); + Town.buyPots(10, "Antidote", true, true); - break; - } + break; + } - Town.move("stash"); - Pather.usePortal(null, null, portal); - } + Town.move("stash"); + Pather.usePortal(null, null, portal); + } - this.pandemoniumRun(); - } - } + this.pandemoniumRun(); + } + } - // Don't make torches if not configured to OR if the char already has one - if (this.checkTorch()) { - return true; - } + // Don't make torches if not configured to OR if the char already has one + if (this.checkTorch()) { + return true; + } - // Count organs - brains = me.findItems(sdk.items.quest.MephistosBrain, sdk.items.mode.inStorage).length || 0; - eyes = me.findItems(sdk.items.quest.BaalsEye, sdk.items.mode.inStorage).length || 0; - horns = me.findItems(sdk.items.quest.DiablosHorn, sdk.items.mode.inStorage).length || 0; - - // We have enough organs, do Tristram - if (brains && eyes && horns) { - this.getFade(); - console.log("ÿc8Kolbot-SoloPlayÿc0: Making torch"); - D2Bot.printToConsole("ÿc8Kolbot-SoloPlayÿc0 :: OrgTorch: Making torch.", sdk.colors.D2Bot.Orange); - - portal = this.openPortal(1); - - if (portal) { - Pather.usePortal(null, null, portal); - } - - this.pandemoniumRun(); - } - - return true; + // Count organs + brains = me.findItems(sdk.items.quest.MephistosBrain, sdk.items.mode.inStorage).length || 0; + eyes = me.findItems(sdk.items.quest.BaalsEye, sdk.items.mode.inStorage).length || 0; + horns = me.findItems(sdk.items.quest.DiablosHorn, sdk.items.mode.inStorage).length || 0; + + // We have enough organs, do Tristram + if (brains && eyes && horns) { + this.getFade(); + console.log("ÿc8Kolbot-SoloPlayÿc0: Making torch"); + D2Bot.printToConsole("ÿc8Kolbot-SoloPlayÿc0 :: OrgTorch: Making torch.", sdk.colors.D2Bot.Orange); + + portal = this.openPortal(1); + + if (portal) { + Pather.usePortal(null, null, portal); + } + + this.pandemoniumRun(); + } + + return true; } diff --git a/libs/SoloPlay/Scripts/pindle.js b/libs/SoloPlay/Scripts/pindle.js index 8d6298f3..6d666dc2 100644 --- a/libs/SoloPlay/Scripts/pindle.js +++ b/libs/SoloPlay/Scripts/pindle.js @@ -6,17 +6,17 @@ */ function pindle () { - Town.doChores(false, { fullChores: true }); - Town.goToTown(5); - myPrint("starting pindle"); + Town.doChores(false, { fullChores: true }); + Town.goToTown(5); + myPrint("starting pindle"); - !Pather.getPortal(sdk.areas.NihlathaksTemple) && Town.npcInteract("anya"); - if (!Pather.usePortal(sdk.areas.NihlathaksTemple)) return true; + !Pather.getPortal(sdk.areas.NihlathaksTemple) && Town.npcInteract("anya"); + if (!Pather.usePortal(sdk.areas.NihlathaksTemple)) return true; - Precast.doPrecast(true); - Pather.moveTo(10058, 13234); - Attack.killTarget(getLocaleString(sdk.locale.monsters.Pindleskin)); - Pickit.pickItems(); + Precast.doPrecast(true); + Pather.moveTo(10058, 13234); + Attack.killTarget(getLocaleString(sdk.locale.monsters.Pindleskin)); + Pickit.pickItems(); - return true; + return true; } diff --git a/libs/SoloPlay/Scripts/pits.js b/libs/SoloPlay/Scripts/pits.js index 584259e6..983d0798 100644 --- a/libs/SoloPlay/Scripts/pits.js +++ b/libs/SoloPlay/Scripts/pits.js @@ -6,18 +6,18 @@ */ function pits () { - Town.doChores(false, { fullChores: true }); - myPrint("starting pits"); + Town.doChores(false, { fullChores: true }); + myPrint("starting pits"); - Pather.checkWP(sdk.areas.BlackMarsh, true) ? Pather.useWaypoint(sdk.areas.BlackMarsh) : Pather.getWP(sdk.areas.BlackMarsh); - Precast.doPrecast(true); + Pather.checkWP(sdk.areas.BlackMarsh, true) ? Pather.useWaypoint(sdk.areas.BlackMarsh) : Pather.getWP(sdk.areas.BlackMarsh); + Precast.doPrecast(true); - if (!Pather.moveToExit([sdk.areas.TamoeHighland, sdk.areas.PitLvl1], true)) throw new Error("Failed to move to Pit level 1"); - Attack.clearLevel(); + if (!Pather.moveToExit([sdk.areas.TamoeHighland, sdk.areas.PitLvl1], true)) throw new Error("Failed to move to Pit level 1"); + Attack.clearLevel(); - if (!Pather.moveToExit(sdk.areas.PitLvl2, true)) throw new Error("Failed to move to Pit level 2"); - Attack.clearLevel(); - Misc.openChestsInArea(sdk.areas.PitLvl2); + if (!Pather.moveToExit(sdk.areas.PitLvl2, true)) throw new Error("Failed to move to Pit level 2"); + Attack.clearLevel(); + Misc.openChestsInArea(sdk.areas.PitLvl2); - return true; + return true; } diff --git a/libs/SoloPlay/Scripts/radament.js b/libs/SoloPlay/Scripts/radament.js index d7e483c8..8709ba17 100644 --- a/libs/SoloPlay/Scripts/radament.js +++ b/libs/SoloPlay/Scripts/radament.js @@ -6,35 +6,35 @@ */ function radament () { - Town.doChores(false, { fullChores: true }); - myPrint("starting radament"); + Town.doChores(false, { fullChores: true }); + myPrint("starting radament"); - if (!Pather.checkWP(sdk.areas.A2SewersLvl2, true)) { - Town.goToTown(2); - Pather.moveToExit(sdk.areas.A2SewersLvl1, true); - Pather.getWP(sdk.areas.A2SewersLvl2); - } else { - Pather.useWaypoint(sdk.areas.A2SewersLvl2); - } + if (!Pather.checkWP(sdk.areas.A2SewersLvl2, true)) { + Town.goToTown(2); + Pather.moveToExit(sdk.areas.A2SewersLvl1, true); + Pather.getWP(sdk.areas.A2SewersLvl2); + } else { + Pather.useWaypoint(sdk.areas.A2SewersLvl2); + } - Precast.doPrecast(true); - Pather.clearToExit(sdk.areas.A2SewersLvl2, sdk.areas.A2SewersLvl3, true); - Pather.moveToPreset(me.area, sdk.unittype.Object, sdk.quest.chest.HoradricScrollChest); - Attack.killTarget("Radament"); - let book = Game.getItem(sdk.quest.item.BookofSkill); - !!book ? Pickit.pickItem(book) : Attack.killTarget("Radament"); - Pickit.pickItems(); + Precast.doPrecast(true); + Pather.clearToExit(sdk.areas.A2SewersLvl2, sdk.areas.A2SewersLvl3, true); + Pather.moveToPreset(me.area, sdk.unittype.Object, sdk.quest.chest.HoradricScrollChest); + Attack.killTarget("Radament"); + let book = Game.getItem(sdk.quest.item.BookofSkill); + !!book ? Pickit.pickItem(book) : Attack.killTarget("Radament"); + Pickit.pickItems(); - if (Misc.checkQuest(sdk.quest.id.RadamentsLair, sdk.quest.states.ReqComplete)) { - Town.npcInteract("atma"); - me.cancelUIFlags(); + if (Misc.checkQuest(sdk.quest.id.RadamentsLair, sdk.quest.states.ReqComplete)) { + Town.npcInteract("atma"); + me.cancelUIFlags(); - let book = me.getItem(sdk.items.quest.BookofSkill); - if (book) { - book.isInStash && Town.openStash() && delay(300); - book.use() && delay(500) && AutoSkill.init(Config.AutoSkill.Build, Config.AutoSkill.Save); - } - } + let book = me.getItem(sdk.items.quest.BookofSkill); + if (book) { + book.isInStash && Town.openStash() && delay(300); + book.use() && delay(500) && AutoSkill.init(Config.AutoSkill.Build, Config.AutoSkill.Save); + } + } - return true; + return true; } diff --git a/libs/SoloPlay/Scripts/river.js b/libs/SoloPlay/Scripts/river.js index f3f528e6..95d4c260 100644 --- a/libs/SoloPlay/Scripts/river.js +++ b/libs/SoloPlay/Scripts/river.js @@ -6,25 +6,25 @@ */ function river() { - myPrint("starting river"); + myPrint("starting river"); - Town.doChores(null, { thawing: me.coldRes < 75, antidote: me.poisonRes < 75 }); + Town.doChores(null, { thawing: me.coldRes < 75, antidote: me.poisonRes < 75 }); - Pather.checkWP(sdk.areas.CityoftheDamned, true) ? Pather.useWaypoint(sdk.areas.CityoftheDamned) : Pather.getWP(sdk.areas.CityoftheDamned); - Precast.doPrecast(true); - Pather.clearToExit(sdk.areas.CityoftheDamned, sdk.areas.RiverofFlame, true); + Pather.checkWP(sdk.areas.CityoftheDamned, true) ? Pather.useWaypoint(sdk.areas.CityoftheDamned) : Pather.getWP(sdk.areas.CityoftheDamned); + Precast.doPrecast(true); + Pather.clearToExit(sdk.areas.CityoftheDamned, sdk.areas.RiverofFlame, true); - if (!Pather.moveToPreset(me.area, sdk.unittype.Object, sdk.quest.chest.HellForge)) { - console.log("ÿc8Kolbot-SoloPlayÿc0: Failed to move to Hephasto"); - } + if (!Pather.moveToPreset(me.area, sdk.unittype.Object, sdk.quest.chest.HellForge)) { + console.log("ÿc8Kolbot-SoloPlayÿc0: Failed to move to Hephasto"); + } - try { - Attack.clear(20, 0, sdk.monsters.Hephasto); - } catch (err) { - console.log("ÿc8Kolbot-SoloPlayÿc0: Failed to kill Hephasto"); - } + try { + Attack.clear(20, 0, sdk.monsters.Hephasto); + } catch (err) { + console.log("ÿc8Kolbot-SoloPlayÿc0: Failed to kill Hephasto"); + } - Pather.getWP(sdk.areas.RiverofFlame, true); + Pather.getWP(sdk.areas.RiverofFlame, true); - return true; + return true; } diff --git a/libs/SoloPlay/Scripts/savebarby.js b/libs/SoloPlay/Scripts/savebarby.js index 142bc274..2ed447ab 100644 --- a/libs/SoloPlay/Scripts/savebarby.js +++ b/libs/SoloPlay/Scripts/savebarby.js @@ -7,41 +7,41 @@ */ function savebarby () { - let coords = []; + let coords = []; - Town.doChores(false, { fullChores: true }); - myPrint("starting barbies"); + Town.doChores(false, { fullChores: true }); + myPrint("starting barbies"); - Pather.checkWP(sdk.areas.FrigidHighlands, true) ? Pather.useWaypoint(sdk.areas.FrigidHighlands) : Pather.getWP(sdk.areas.FrigidHighlands); - Precast.doPrecast(true); - let barbies = (Game.getPresetObjects(me.area, sdk.quest.chest.BarbCage) || []); + Pather.checkWP(sdk.areas.FrigidHighlands, true) ? Pather.useWaypoint(sdk.areas.FrigidHighlands) : Pather.getWP(sdk.areas.FrigidHighlands); + Precast.doPrecast(true); + let barbies = (Game.getPresetObjects(me.area, sdk.quest.chest.BarbCage) || []); - if (!barbies) return false; + if (!barbies) return false; - for (let cage = 0 ; cage < barbies.length ; cage += 1) { - coords.push({ - x: barbies[cage].roomx * 5 + barbies[cage].x - 3, //Dark-f: x-3 - y: barbies[cage].roomy * 5 + barbies[cage].y - }); - } + for (let cage = 0 ; cage < barbies.length ; cage += 1) { + coords.push({ + x: barbies[cage].roomx * 5 + barbies[cage].x - 3, //Dark-f: x-3 + y: barbies[cage].roomy * 5 + barbies[cage].y + }); + } - for (let k = 0; k < coords.length; k += 1) { - me.overhead("let my barby go! " + (k + 1) + "/" + barbies.length); - Pather.moveToUnit(coords[k], 2, 0); - let door = Game.getMonster(sdk.monsters.PrisonDoor); + for (let k = 0; k < coords.length; k += 1) { + me.overhead("let my barby go! " + (k + 1) + "/" + barbies.length); + Pather.moveToUnit(coords[k], 2, 0); + let door = Game.getMonster(sdk.monsters.PrisonDoor); - if (door) { - Pather.moveToUnit(door, 1, 0); + if (door) { + Pather.moveToUnit(door, 1, 0); - for (let i = 0; i < 20 && door.hp; i += 1) { - Skill.cast(Config.AttackSkill[1], Skill.getHand(Config.AttackSkill[1]), door); - } - } + for (let i = 0; i < 20 && door.hp; i += 1) { + Skill.cast(Config.AttackSkill[1], Skill.getHand(Config.AttackSkill[1]), door); + } + } - delay(1500 + me.ping); - } + delay(1500 + me.ping); + } - Town.npcInteract("qual_kehk"); + Town.npcInteract("qual_kehk"); - return true; + return true; } diff --git a/libs/SoloPlay/Scripts/shenk.js b/libs/SoloPlay/Scripts/shenk.js index 24eb05e3..6ddc93b2 100644 --- a/libs/SoloPlay/Scripts/shenk.js +++ b/libs/SoloPlay/Scripts/shenk.js @@ -6,17 +6,17 @@ */ function shenk () { - Town.doChores(false, { fullChores: true }); - myPrint("starting shenk"); + Town.doChores(false, { fullChores: true }); + myPrint("starting shenk"); - Pather.checkWP(sdk.areas.FrigidHighlands, true) ? Pather.useWaypoint(sdk.areas.FrigidHighlands) : Pather.getWP(sdk.areas.FrigidHighlands); - Precast.doPrecast(true); - Pather.moveTo(3745, 5084); - Attack.killTarget(getLocaleString(sdk.locale.monsters.EldritchtheRectifier)); + Pather.checkWP(sdk.areas.FrigidHighlands, true) ? Pather.useWaypoint(sdk.areas.FrigidHighlands) : Pather.getWP(sdk.areas.FrigidHighlands); + Precast.doPrecast(true); + Pather.moveTo(3745, 5084); + Attack.killTarget(getLocaleString(sdk.locale.monsters.EldritchtheRectifier)); - Pather.moveToExit(sdk.areas.BloodyFoothills, true); - Pather.moveTo(3883, 5113); - Attack.killTarget(getLocaleString(sdk.locale.monsters.ShenktheOverseer)); + Pather.moveToExit(sdk.areas.BloodyFoothills, true); + Pather.moveTo(3883, 5113); + Attack.killTarget(getLocaleString(sdk.locale.monsters.ShenktheOverseer)); - return true; + return true; } diff --git a/libs/SoloPlay/Scripts/smith.js b/libs/SoloPlay/Scripts/smith.js index 057c14b8..1daa1623 100644 --- a/libs/SoloPlay/Scripts/smith.js +++ b/libs/SoloPlay/Scripts/smith.js @@ -6,34 +6,34 @@ */ function smith () { - Town.doChores(false, { fullChores: true }); - myPrint("starting smith"); + Town.doChores(false, { fullChores: true }); + myPrint("starting smith"); - Pather.checkWP(sdk.areas.OuterCloister, true) ? Pather.useWaypoint(sdk.areas.OuterCloister) : Pather.getWP(sdk.areas.OuterCloister); - Precast.doPrecast(true); - Pather.moveToExit(sdk.areas.Barracks); + Pather.checkWP(sdk.areas.OuterCloister, true) ? Pather.useWaypoint(sdk.areas.OuterCloister) : Pather.getWP(sdk.areas.OuterCloister); + Precast.doPrecast(true); + Pather.moveToExit(sdk.areas.Barracks); - if (!Pather.moveToPreset(sdk.areas.Barracks, sdk.unittype.Object, sdk.quest.chest.MalusHolder)) { - console.log("ÿc8Kolbot-SoloPlayÿc0: Failed to move to the Smith"); - return false; - } + if (!Pather.moveToPreset(sdk.areas.Barracks, sdk.unittype.Object, sdk.quest.chest.MalusHolder)) { + console.log("ÿc8Kolbot-SoloPlayÿc0: Failed to move to the Smith"); + return false; + } - try { - Attack.killTarget(sdk.monsters.TheSmith); - } catch (err) { - console.log("ÿc8Kolbot-SoloPlayÿc0: Failed to kill Smith"); - } + try { + Attack.killTarget(sdk.monsters.TheSmith); + } catch (err) { + console.log("ÿc8Kolbot-SoloPlayÿc0: Failed to kill Smith"); + } - Quest.collectItem(sdk.items.quest.HoradricMalus, sdk.quest.chest.MalusHolder); - Pickit.pickItems(); - Town.goToTown(); - Town.npcInteract("charsi"); + Quest.collectItem(sdk.items.quest.HoradricMalus, sdk.quest.chest.MalusHolder); + Pickit.pickItems(); + Town.goToTown(); + Town.npcInteract("charsi"); - if (!getWaypoint(Pather.wpAreas.indexOf(sdk.areas.JailLvl1))) { - Pather.usePortal(null, me.name); - Pather.getWP(sdk.areas.JailLvl1); - Pather.useWaypoint(sdk.areas.RogueEncampment); - } + if (!getWaypoint(Pather.wpAreas.indexOf(sdk.areas.JailLvl1))) { + Pather.usePortal(null, me.name); + Pather.getWP(sdk.areas.JailLvl1); + Pather.useWaypoint(sdk.areas.RogueEncampment); + } - return true; + return true; } diff --git a/libs/SoloPlay/Scripts/staff.js b/libs/SoloPlay/Scripts/staff.js index 305a0907..427df518 100644 --- a/libs/SoloPlay/Scripts/staff.js +++ b/libs/SoloPlay/Scripts/staff.js @@ -6,23 +6,23 @@ */ function staff () { - Town.doChores(false, { fullChores: true }); - myPrint("starting staff"); + Town.doChores(false, { fullChores: true }); + myPrint("starting staff"); - Pather.checkWP(sdk.areas.FarOasis, true) ? Pather.useWaypoint(sdk.areas.FarOasis) : Pather.getWP(sdk.areas.FarOasis); - Precast.doPrecast(true); - Pather.clearToExit(sdk.areas.FarOasis, sdk.areas.MaggotLairLvl1, true); - Pather.clearToExit(sdk.areas.MaggotLairLvl1, sdk.areas.MaggotLairLvl2, true); - Pather.clearToExit(sdk.areas.MaggotLairLvl2, sdk.areas.MaggotLairLvl3, true); + Pather.checkWP(sdk.areas.FarOasis, true) ? Pather.useWaypoint(sdk.areas.FarOasis) : Pather.getWP(sdk.areas.FarOasis); + Precast.doPrecast(true); + Pather.clearToExit(sdk.areas.FarOasis, sdk.areas.MaggotLairLvl1, true); + Pather.clearToExit(sdk.areas.MaggotLairLvl1, sdk.areas.MaggotLairLvl2, true); + Pather.clearToExit(sdk.areas.MaggotLairLvl2, sdk.areas.MaggotLairLvl3, true); - if (!Pather.moveToPreset(me.area, sdk.unittype.Object, sdk.quest.chest.ShaftoftheHoradricStaffChest)) { - console.log("ÿc8Kolbot-SoloPlayÿc0: Failed to move to staff"); - return me.getItem(sdk.items.quest.ShaftoftheHoradricStaff); - } + if (!Pather.moveToPreset(me.area, sdk.unittype.Object, sdk.quest.chest.ShaftoftheHoradricStaffChest)) { + console.log("ÿc8Kolbot-SoloPlayÿc0: Failed to move to staff"); + return me.getItem(sdk.items.quest.ShaftoftheHoradricStaff); + } - Quest.collectItem(sdk.items.quest.ShaftoftheHoradricStaff, sdk.quest.chest.ShaftoftheHoradricStaffChest); - Quest.stashItem(sdk.items.quest.ShaftoftheHoradricStaff); + Quest.collectItem(sdk.items.quest.ShaftoftheHoradricStaff, sdk.quest.chest.ShaftoftheHoradricStaffChest); + Quest.stashItem(sdk.items.quest.ShaftoftheHoradricStaff); - return me.getItem(sdk.items.quest.ShaftoftheHoradricStaff); + return me.getItem(sdk.items.quest.ShaftoftheHoradricStaff); } diff --git a/libs/SoloPlay/Scripts/summoner.js b/libs/SoloPlay/Scripts/summoner.js index 7d89388d..99da5772 100644 --- a/libs/SoloPlay/Scripts/summoner.js +++ b/libs/SoloPlay/Scripts/summoner.js @@ -6,117 +6,117 @@ */ function summoner () { - // @isid0re - const teleportPads = function () { - if (!me.inArea(sdk.areas.ArcaneSanctuary) || Pather.useTeleport()) return true; - - let tppPath; - let [wpX, wpY] = [25449, 5449]; - let ntppPath = [[53, 2], [103, -3], [113, -68], [173, -58], [243, -73], [293, -58], [353, -68], [372, -62], [342, -17]]; - let stppPath = [[-56, 2], [-128, -7], [-98, 78], [-176, 62], [-243, 58], [-296, 62], [-372, 62], [-366, 12]]; - let etppPath = [[28, 52], [-12, 92], [53, 112], [72, 118], [88, 172], [54, 227], [43, 247], [88, 292], [82, 378], [-16, 332], [2, 353]]; - let wtppPath = [[-26, -63], [2, -121], [3, -133], [62, -117], [34, -183], [54, -228], [43, -243], [34, -303], [72, -351], [64, -368], [23, -338]]; - let stand = Game.getPresetObject(me.area, sdk.objects.Journal); - let [tppPathX, tppPathY] = [(stand.roomx * 5 + stand.x), (stand.roomy * 5 + stand.y)]; - console.debug(tppPathX, tppPathY); - let tppID = [192, 304, 305, 306]; - - switch (tppPathX) { - case 25011: - tppPath = ntppPath; - break; - case 25866: - tppPath = stppPath; - break; - case 25431: - switch (tppPathY) { - case 5011: - tppPath = etppPath; - break; - case 5861: - tppPath = wtppPath; - break; - } - - break; - } - - if (getPath(me.area, me.x, me.y, tppPathX, tppPathY, 0, 10).length === 0) { - me.overhead("Using telepad layout"); - - for (let i = 0; i < tppPath.length; i += 1) { - for (let h = 0; h < 5; h += 1) { - Pather.moveTo(wpX - tppPath[i][0], wpY - tppPath[i][1]); - - for (let activate = 0; activate < tppID.length; activate += 1) { - let telepad = Game.getObject(tppID[activate]); - - if (telepad) { - do { - if (Math.abs((telepad.x - (wpX - tppPath[i][0]) + (telepad.y - (wpY - tppPath[i][1])))) <= 0) { - delay(100 + me.ping); - telepad.interact(); - } - } while (telepad.getNext()); - } - } - } - } - } - - return true; - }; - - // START - Town.doChores(false, { fullChores: true }); - myPrint("starting summoner"); - - if (!Misc.checkQuest(sdk.quest.id.TheArcaneSanctuary, 3/* talked to Jerhyn */)) { - Town.npcInteract("jerhyn"); - } - - Pather.checkWP(sdk.areas.ArcaneSanctuary, true) ? Pather.useWaypoint(sdk.areas.ArcaneSanctuary) : Pather.getWP(sdk.areas.ArcaneSanctuary); - Precast.doPrecast(true); - teleportPads(); - - try { - Pather.moveNearPreset(sdk.areas.ArcaneSanctuary, sdk.unittype.Object, sdk.objects.Journal, 10); - } catch (err) { - console.log("ÿc8Kolbot-SoloPlayÿc0: Failed to reach Summoner. Retry"); - - if (!Pather.moveToPreset(sdk.areas.ArcaneSanctuary, sdk.unittype.Object, sdk.objects.Journal, -3, -3)) { - throw new Error("Failed to reach summoner"); - } - } - - try { - Attack.killTarget(sdk.monsters.TheSummoner); - } catch (e) { - console.log("ÿc8Kolbot-SoloPlayÿc0: Failed to kill summoner"); - - return false; - } - - let journal = Game.getObject(sdk.objects.Journal); - - if (journal) { - while (!Pather.getPortal(sdk.areas.CanyonofMagic)) { - Misc.openChest(journal); - delay(1000 + me.ping); - me.cancel(); - } - } - - Pather.usePortal(sdk.areas.CanyonofMagic); - - if (!Pather.checkWP(sdk.areas.CanyonofMagic)) { - Pather.getWP(sdk.areas.CanyonofMagic); - Pather.useWaypoint(sdk.areas.LutGholein); - } else { - Pather.useWaypoint(sdk.areas.LutGholein); - } - - !me.summoner && Town.npcInteract("drognan"); - - return true; + // @isid0re + const teleportPads = function () { + if (!me.inArea(sdk.areas.ArcaneSanctuary) || Pather.useTeleport()) return true; + + let tppPath; + let [wpX, wpY] = [25449, 5449]; + let ntppPath = [[53, 2], [103, -3], [113, -68], [173, -58], [243, -73], [293, -58], [353, -68], [372, -62], [342, -17]]; + let stppPath = [[-56, 2], [-128, -7], [-98, 78], [-176, 62], [-243, 58], [-296, 62], [-372, 62], [-366, 12]]; + let etppPath = [[28, 52], [-12, 92], [53, 112], [72, 118], [88, 172], [54, 227], [43, 247], [88, 292], [82, 378], [-16, 332], [2, 353]]; + let wtppPath = [[-26, -63], [2, -121], [3, -133], [62, -117], [34, -183], [54, -228], [43, -243], [34, -303], [72, -351], [64, -368], [23, -338]]; + let stand = Game.getPresetObject(me.area, sdk.objects.Journal); + let [tppPathX, tppPathY] = [(stand.roomx * 5 + stand.x), (stand.roomy * 5 + stand.y)]; + console.debug(tppPathX, tppPathY); + let tppID = [192, 304, 305, 306]; + + switch (tppPathX) { + case 25011: + tppPath = ntppPath; + break; + case 25866: + tppPath = stppPath; + break; + case 25431: + switch (tppPathY) { + case 5011: + tppPath = etppPath; + break; + case 5861: + tppPath = wtppPath; + break; + } + + break; + } + + if (getPath(me.area, me.x, me.y, tppPathX, tppPathY, 0, 10).length === 0) { + me.overhead("Using telepad layout"); + + for (let i = 0; i < tppPath.length; i += 1) { + for (let h = 0; h < 5; h += 1) { + Pather.moveTo(wpX - tppPath[i][0], wpY - tppPath[i][1]); + + for (let activate = 0; activate < tppID.length; activate += 1) { + let telepad = Game.getObject(tppID[activate]); + + if (telepad) { + do { + if (Math.abs((telepad.x - (wpX - tppPath[i][0]) + (telepad.y - (wpY - tppPath[i][1])))) <= 0) { + delay(100 + me.ping); + telepad.interact(); + } + } while (telepad.getNext()); + } + } + } + } + } + + return true; + }; + + // START + Town.doChores(false, { fullChores: true }); + myPrint("starting summoner"); + + if (!Misc.checkQuest(sdk.quest.id.TheArcaneSanctuary, 3/* talked to Jerhyn */)) { + Town.npcInteract("jerhyn"); + } + + Pather.checkWP(sdk.areas.ArcaneSanctuary, true) ? Pather.useWaypoint(sdk.areas.ArcaneSanctuary) : Pather.getWP(sdk.areas.ArcaneSanctuary); + Precast.doPrecast(true); + teleportPads(); + + try { + Pather.moveNearPreset(sdk.areas.ArcaneSanctuary, sdk.unittype.Object, sdk.objects.Journal, 10); + } catch (err) { + console.log("ÿc8Kolbot-SoloPlayÿc0: Failed to reach Summoner. Retry"); + + if (!Pather.moveToPreset(sdk.areas.ArcaneSanctuary, sdk.unittype.Object, sdk.objects.Journal, -3, -3)) { + throw new Error("Failed to reach summoner"); + } + } + + try { + Attack.killTarget(sdk.monsters.TheSummoner); + } catch (e) { + console.log("ÿc8Kolbot-SoloPlayÿc0: Failed to kill summoner"); + + return false; + } + + let journal = Game.getObject(sdk.objects.Journal); + + if (journal) { + while (!Pather.getPortal(sdk.areas.CanyonofMagic)) { + Misc.openChest(journal); + delay(1000 + me.ping); + me.cancel(); + } + } + + Pather.usePortal(sdk.areas.CanyonofMagic); + + if (!Pather.checkWP(sdk.areas.CanyonofMagic)) { + Pather.getWP(sdk.areas.CanyonofMagic); + Pather.useWaypoint(sdk.areas.LutGholein); + } else { + Pather.useWaypoint(sdk.areas.LutGholein); + } + + !me.summoner && Town.npcInteract("drognan"); + + return true; } diff --git a/libs/SoloPlay/Scripts/templeruns.js b/libs/SoloPlay/Scripts/templeruns.js index 1b09efb5..ab186fbd 100644 --- a/libs/SoloPlay/Scripts/templeruns.js +++ b/libs/SoloPlay/Scripts/templeruns.js @@ -7,45 +7,45 @@ */ function templeruns () { - myPrint("starting temple runs"); - // todo - calculate effort required to clear temple and decide whether to run it or move to next - const temples = [ - [sdk.areas.FlayerJungle, sdk.areas.LowerKurast], [sdk.areas.KurastBazaar, sdk.areas.RuinedTemple], - [sdk.areas.KurastBazaar, sdk.areas.DisusedFane], [sdk.areas.UpperKurast, sdk.areas.ForgottenReliquary], - [sdk.areas.UpperKurast, sdk.areas.ForgottenTemple], [sdk.areas.UpperKurast, sdk.areas.KurastCauseway, sdk.areas.RuinedFane], [sdk.areas.UpperKurast, sdk.areas.KurastCauseway, sdk.areas.DisusedReliquary] - ]; - Town.doChores(false, { fullChores: true }); + myPrint("starting temple runs"); + // todo - calculate effort required to clear temple and decide whether to run it or move to next + const temples = [ + [sdk.areas.FlayerJungle, sdk.areas.LowerKurast], [sdk.areas.KurastBazaar, sdk.areas.RuinedTemple], + [sdk.areas.KurastBazaar, sdk.areas.DisusedFane], [sdk.areas.UpperKurast, sdk.areas.ForgottenReliquary], + [sdk.areas.UpperKurast, sdk.areas.ForgottenTemple], [sdk.areas.UpperKurast, sdk.areas.KurastCauseway, sdk.areas.RuinedFane], [sdk.areas.UpperKurast, sdk.areas.KurastCauseway, sdk.areas.DisusedReliquary] + ]; + Town.doChores(false, { fullChores: true }); - for (let run = 0; run < temples.length; run++) { - Pather.checkWP(temples[run][0], true) ? Pather.useWaypoint(temples[run][0]) : Pather.getWP(temples[run][0]); - Precast.doPrecast(true); + for (let run = 0; run < temples.length; run++) { + Pather.checkWP(temples[run][0], true) ? Pather.useWaypoint(temples[run][0]) : Pather.getWP(temples[run][0]); + Precast.doPrecast(true); - if (Pather.moveToExit(temples[run], true, true)) { - if (me.inArea(sdk.areas.LowerKurast)) { - Misc.openChestsInArea(sdk.areas.LowerKurast); - } else if (me.inArea(sdk.areas.RuinedTemple) && !me.lamessen) { - me.overhead("lamessen"); - Pather.moveToPreset(sdk.areas.RuinedTemple, sdk.unittype.Object, sdk.quest.chest.LamEsensTomeHolder); - Quest.collectItem(sdk.quest.item.LamEsensTome, sdk.quest.chest.LamEsensTomeHolder); - Quest.unfinishedQuests(); - } else { - Attack.clearLevel(0xF); - } - } + if (Pather.moveToExit(temples[run], true, true)) { + if (me.inArea(sdk.areas.LowerKurast)) { + Misc.openChestsInArea(sdk.areas.LowerKurast); + } else if (me.inArea(sdk.areas.RuinedTemple) && !me.lamessen) { + me.overhead("lamessen"); + Pather.moveToPreset(sdk.areas.RuinedTemple, sdk.unittype.Object, sdk.quest.chest.LamEsensTomeHolder); + Quest.collectItem(sdk.quest.item.LamEsensTome, sdk.quest.chest.LamEsensTomeHolder); + Quest.unfinishedQuests(); + } else { + Attack.clearLevel(0xF); + } + } - Town.goToTown(); - } + Town.goToTown(); + } - if (!me.inTown) { - Town.goToTown(); + if (!me.inTown) { + Town.goToTown(); - if (!me.mephisto) { - if (!Pather.checkWP(sdk.areas.Travincal)) { - Pather.getWP(sdk.areas.Travincal); - Town.goToTown(); - } - } - } + if (!me.mephisto) { + if (!Pather.checkWP(sdk.areas.Travincal)) { + Pather.getWP(sdk.areas.Travincal); + Town.goToTown(); + } + } + } - return true; + return true; } diff --git a/libs/SoloPlay/Scripts/tombs.js b/libs/SoloPlay/Scripts/tombs.js index ac6d50bc..d6baf725 100644 --- a/libs/SoloPlay/Scripts/tombs.js +++ b/libs/SoloPlay/Scripts/tombs.js @@ -15,39 +15,39 @@ */ function tombs () { - myPrint("starting tombs"); + myPrint("starting tombs"); - const tombID = [ - sdk.areas.TalRashasTomb4, sdk.areas.TalRashasTomb3, sdk.areas.TalRashasTomb2, sdk.areas.TalRashasTomb1, - sdk.areas.TalRashasTomb5, sdk.areas.TalRashasTomb6, sdk.areas.TalRashasTomb7 - ]; - Town.doChores(false, { fullChores: true }); + const tombID = [ + sdk.areas.TalRashasTomb4, sdk.areas.TalRashasTomb3, sdk.areas.TalRashasTomb2, sdk.areas.TalRashasTomb1, + sdk.areas.TalRashasTomb5, sdk.areas.TalRashasTomb6, sdk.areas.TalRashasTomb7 + ]; + Town.doChores(false, { fullChores: true }); - for (let number = 0; number < tombID.length; number++) { - if (SoloIndex.index.duriel.skipIf()) return true; - Pather.checkWP(sdk.areas.CanyonofMagic, true) ? Pather.useWaypoint(sdk.areas.CanyonofMagic) : Pather.getWP(sdk.areas.CanyonofMagic); - Precast.doPrecast(true); + for (let number = 0; number < tombID.length; number++) { + if (SoloIndex.index.duriel.skipIf()) return true; + Pather.checkWP(sdk.areas.CanyonofMagic, true) ? Pather.useWaypoint(sdk.areas.CanyonofMagic) : Pather.getWP(sdk.areas.CanyonofMagic); + Precast.doPrecast(true); - if (Pather.moveToExit(tombID[number], true, true)) { - me.overhead("Tomb #" + (number + 1)); - const duryTomb = getRoom().correcttomb === me.area; + if (Pather.moveToExit(tombID[number], true, true)) { + me.overhead("Tomb #" + (number + 1)); + const duryTomb = getRoom().correcttomb === me.area; - let obj = Game.getPresetObject(me.area, (!duryTomb ? sdk.objects.SmallSparklyChest : sdk.objects.HoradricStaffHolder)); - !!obj && Pather.moveToUnit(obj); + let obj = Game.getPresetObject(me.area, (!duryTomb ? sdk.objects.SmallSparklyChest : sdk.objects.HoradricStaffHolder)); + !!obj && Pather.moveToUnit(obj); - Attack.clear(50); - Pickit.pickItems(); + Attack.clear(50); + Pickit.pickItems(); - if (me.duriel && Game.getObject(sdk.objects.PortaltoDurielsLair)) { - Pather.useUnit(sdk.unittype.Object, sdk.objects.PortaltoDurielsLair, sdk.areas.DurielsLair); - me.sorceress && !me.normal ? Attack.pwnDury() : Attack.killTarget("Duriel"); - Pickit.pickItems(); - } - } + if (me.duriel && Game.getObject(sdk.objects.PortaltoDurielsLair)) { + Pather.useUnit(sdk.unittype.Object, sdk.objects.PortaltoDurielsLair, sdk.areas.DurielsLair); + me.sorceress && !me.normal ? Attack.pwnDury() : Attack.killTarget("Duriel"); + Pickit.pickItems(); + } + } - Town.goToTown(); - Town.heal(); - } + Town.goToTown(); + Town.heal(); + } - return true; + return true; } diff --git a/libs/SoloPlay/Scripts/travincal.js b/libs/SoloPlay/Scripts/travincal.js index d09b0aaa..64be8054 100644 --- a/libs/SoloPlay/Scripts/travincal.js +++ b/libs/SoloPlay/Scripts/travincal.js @@ -6,97 +6,97 @@ */ function travincal () { - Quest.preReqs(); - Town.doChores(false, { fullChores: true }); - myPrint("starting travincal"); - - Pather.checkWP(sdk.areas.Travincal, true) ? Pather.useWaypoint(sdk.areas.Travincal) : Pather.getWP(sdk.areas.Travincal); - Precast.doPrecast(true); - - const council = { - x: me.x + 76, - y: me.y - 67 - }; - - Pather.moveToUnit(council); - Attack.killTarget("Ismail Vilehand"); - Pickit.pickItems(); - - // go to orb - Pather.moveToPreset(sdk.areas.Travincal, sdk.unittype.Object, sdk.objects.CompellingOrb); - - let orb = Game.getObject(sdk.objects.CompellingOrb); - !!orb && Attack.clearPos(orb.x, orb.y, 15); - - // khalim's will quest not complete - if (!me.travincal) { - // cleared council didn't pick flail and hasn't already made flail - if (!me.getItem(sdk.items.quest.KhalimsFlail) && !me.getItem(sdk.items.quest.KhalimsWill)) { - let flail = Game.getItem(sdk.items.quest.KhalimsFlail); - - Pather.moveToUnit(flail); - Pickit.pickItems(); - Pather.moveToPreset(sdk.areas.Travincal, sdk.unittype.Object, sdk.objects.CompellingOrb); - } - - // cube flail to will - if (!me.getItem(sdk.items.quest.KhalimsWill) && me.getItem(sdk.items.quest.KhalimsFlail)) { - Quest.cubeItems(sdk.items.quest.KhalimsWill, - sdk.quest.item.KhalimsEye, sdk.quest.item.KhalimsHeart, sdk.quest.item.KhalimsBrain, sdk.quest.item.KhalimsFlail); - delay(250 + me.ping); - } - - // From SoloLeveling Commit eb818af - if (!me.inTown && me.getItem(sdk.items.quest.KhalimsWill)) { - Town.goToTown(); - } - - Quest.equipItem(sdk.items.quest.KhalimsWill, sdk.body.RightArm); - delay(250 + me.ping); - - // return to Trav - if (!Pather.usePortal(sdk.areas.Travincal, me.name)) { - Pather.moveToPreset(sdk.areas.Travincal, sdk.unittype.Object, sdk.objects.CompellingOrb); - } - - Quest.smashSomething(sdk.objects.CompellingOrb); - Item.autoEquip(); // equip previous weapon - Town.doChores(false, { fullChores: true }); - - // return to Trav - if (!Pather.usePortal(sdk.areas.Travincal, me.name)) { - console.log("ÿc8Kolbot-SoloPlayÿc0: Failed to go back to Travincal and take entrance"); - Pather.useWaypoint(sdk.areas.Travincal); - Pather.moveToPreset(sdk.areas.Travincal, sdk.unittype.Object, sdk.objects.CompellingOrb); - } - - // Wait until exit pops open - Misc.poll(() => Game.getObject(sdk.objects.DuranceEntryStairs).mode === sdk.objects.mode.Active, 10000); - // Move close to the exit - let exit_1 = Game.getObject(sdk.objects.DuranceEntryStairs); - // Since d2 sucks, move around the thingy - Pather.moveToUnit(exit_1, 7, 7); - // keep on clicking the exit until we are not @ travincal anymore - Misc.poll(function () { - if (me.inArea(sdk.areas.Travincal)) { - Pather.moveToUnit(exit_1); - Misc.click(2, 0, exit_1); - } - return me.inArea(sdk.areas.DuranceofHateLvl1); - }, 10000, 40); - - if (!me.inArea(sdk.areas.DuranceofHateLvl1)) { - Pather.moveToExit([sdk.areas.DuranceofHateLvl1, sdk.areas.DuranceofHateLvl2]); - } else { - Pather.journeyTo(sdk.areas.DuranceofHateLvl2); - } - Pather.getWP(sdk.areas.DuranceofHateLvl2); - Pather.useWaypoint(sdk.areas.KurastDocktown); - } - - if (!Pather.checkWP(sdk.areas.DuranceofHateLvl2)) { - Pather.getWP(sdk.areas.DuranceofHateLvl2); - } - - return true; + Quest.preReqs(); + Town.doChores(false, { fullChores: true }); + myPrint("starting travincal"); + + Pather.checkWP(sdk.areas.Travincal, true) ? Pather.useWaypoint(sdk.areas.Travincal) : Pather.getWP(sdk.areas.Travincal); + Precast.doPrecast(true); + + const council = { + x: me.x + 76, + y: me.y - 67 + }; + + Pather.moveToUnit(council); + Attack.killTarget("Ismail Vilehand"); + Pickit.pickItems(); + + // go to orb + Pather.moveToPreset(sdk.areas.Travincal, sdk.unittype.Object, sdk.objects.CompellingOrb); + + let orb = Game.getObject(sdk.objects.CompellingOrb); + !!orb && Attack.clearPos(orb.x, orb.y, 15); + + // khalim's will quest not complete + if (!me.travincal) { + // cleared council didn't pick flail and hasn't already made flail + if (!me.getItem(sdk.items.quest.KhalimsFlail) && !me.getItem(sdk.items.quest.KhalimsWill)) { + let flail = Game.getItem(sdk.items.quest.KhalimsFlail); + + Pather.moveToUnit(flail); + Pickit.pickItems(); + Pather.moveToPreset(sdk.areas.Travincal, sdk.unittype.Object, sdk.objects.CompellingOrb); + } + + // cube flail to will + if (!me.getItem(sdk.items.quest.KhalimsWill) && me.getItem(sdk.items.quest.KhalimsFlail)) { + Quest.cubeItems(sdk.items.quest.KhalimsWill, + sdk.quest.item.KhalimsEye, sdk.quest.item.KhalimsHeart, sdk.quest.item.KhalimsBrain, sdk.quest.item.KhalimsFlail); + delay(250 + me.ping); + } + + // From SoloLeveling Commit eb818af + if (!me.inTown && me.getItem(sdk.items.quest.KhalimsWill)) { + Town.goToTown(); + } + + Quest.equipItem(sdk.items.quest.KhalimsWill, sdk.body.RightArm); + delay(250 + me.ping); + + // return to Trav + if (!Pather.usePortal(sdk.areas.Travincal, me.name)) { + Pather.moveToPreset(sdk.areas.Travincal, sdk.unittype.Object, sdk.objects.CompellingOrb); + } + + Quest.smashSomething(sdk.objects.CompellingOrb); + Item.autoEquip(); // equip previous weapon + Town.doChores(false, { fullChores: true }); + + // return to Trav + if (!Pather.usePortal(sdk.areas.Travincal, me.name)) { + console.log("ÿc8Kolbot-SoloPlayÿc0: Failed to go back to Travincal and take entrance"); + Pather.useWaypoint(sdk.areas.Travincal); + Pather.moveToPreset(sdk.areas.Travincal, sdk.unittype.Object, sdk.objects.CompellingOrb); + } + + // Wait until exit pops open + Misc.poll(() => Game.getObject(sdk.objects.DuranceEntryStairs).mode === sdk.objects.mode.Active, 10000); + // Move close to the exit + let exit_1 = Game.getObject(sdk.objects.DuranceEntryStairs); + // Since d2 sucks, move around the thingy + Pather.moveToUnit(exit_1, 7, 7); + // keep on clicking the exit until we are not @ travincal anymore + Misc.poll(function () { + if (me.inArea(sdk.areas.Travincal)) { + Pather.moveToUnit(exit_1); + Misc.click(2, 0, exit_1); + } + return me.inArea(sdk.areas.DuranceofHateLvl1); + }, 10000, 40); + + if (!me.inArea(sdk.areas.DuranceofHateLvl1)) { + Pather.moveToExit([sdk.areas.DuranceofHateLvl1, sdk.areas.DuranceofHateLvl2]); + } else { + Pather.journeyTo(sdk.areas.DuranceofHateLvl2); + } + Pather.getWP(sdk.areas.DuranceofHateLvl2); + Pather.useWaypoint(sdk.areas.KurastDocktown); + } + + if (!Pather.checkWP(sdk.areas.DuranceofHateLvl2)) { + Pather.getWP(sdk.areas.DuranceofHateLvl2); + } + + return true; } diff --git a/libs/SoloPlay/Scripts/treehead.js b/libs/SoloPlay/Scripts/treehead.js index b655f67c..415bc2ce 100644 --- a/libs/SoloPlay/Scripts/treehead.js +++ b/libs/SoloPlay/Scripts/treehead.js @@ -6,21 +6,21 @@ */ function treehead() { - Town.doChores(); - Pather.useWaypoint(sdk.areas.DarkWood); - Precast.doPrecast(true); + Town.doChores(); + Pather.useWaypoint(sdk.areas.DarkWood); + Precast.doPrecast(true); - try { - Pather.moveToPreset(me.area, sdk.unittype.Object, sdk.quest.chest.InifussTree, 5, 5); - } catch (e) { - Attack.clear(5); - // Try again - if (Pather.moveToPreset(me.area, sdk.unittype.Object, sdk.quest.chest.InifussTree, 5, 5)) { - return false; - } - } + try { + Pather.moveToPreset(me.area, sdk.unittype.Object, sdk.quest.chest.InifussTree, 5, 5); + } catch (e) { + Attack.clear(5); + // Try again + if (Pather.moveToPreset(me.area, sdk.unittype.Object, sdk.quest.chest.InifussTree, 5, 5)) { + return false; + } + } - Attack.killTarget(getLocaleString(sdk.locale.monsters.TreeheadWoodFist)); + Attack.killTarget(getLocaleString(sdk.locale.monsters.TreeheadWoodFist)); - return true; + return true; } diff --git a/libs/SoloPlay/Scripts/tristram.js b/libs/SoloPlay/Scripts/tristram.js index ff5f0c90..e37e895b 100644 --- a/libs/SoloPlay/Scripts/tristram.js +++ b/libs/SoloPlay/Scripts/tristram.js @@ -7,143 +7,143 @@ */ function tristram () { - let spots = [ - [25176, 5128], [25175, 5145], [25171, 5159], [25166, 5178], - [25173, 5192], [25153, 5198], [25136, 5189], [25127, 5167], - [25120, 5148], [25101, 5136], [25119, 5106], [25121, 5080], - [25119, 5061], [4933, 4363] - ]; - - Town.doChores(false, { fullChores: true }); - myPrint("starting tristram"); - - // Tristram portal hasn't been opened - if (!Misc.checkQuest(sdk.quest.id.TheSearchForCain, 4)) { - const getScroll = () => { - if (me.getItem(sdk.quest.item.ScrollofInifuss) || me.getItem(sdk.quest.item.KeytotheCairnStones)) return true; - Precast.doPrecast(true); - if (!Pather.moveToPreset(sdk.areas.DarkWood, sdk.unittype.Object, sdk.quest.chest.InifussTree, 5, 5)) { - console.log("ÿc8Kolbot-SoloPlayÿc0: Failed to move to Tree of Inifuss"); - return false; - } - - Quest.collectItem(sdk.quest.item.ScrollofInifuss, sdk.quest.chest.InifussTree); - Pickit.pickItems(); - return (me.getItem(sdk.quest.item.ScrollofInifuss || me.getItem(sdk.quest.item.KeytotheCairnStones))); - }; - // missing scroll and key - if (!me.getItem(sdk.items.quest.ScrollofInifuss) && !me.getItem(sdk.items.quest.KeytotheCairnStones)) { - if (!Pather.checkWP(sdk.areas.BlackMarsh, true)) { - Pather.useWaypoint(sdk.areas.DarkWood); - getScroll(); - Pather.getWP(sdk.areas.BlackMarsh); - } else { - Pather.useWaypoint(sdk.areas.DarkWood); - getScroll(); - } - } - - if (me.getItem(sdk.items.quest.ScrollofInifuss)) { - !me.inTown && Town.goToTown(); - Town.npcInteract("akara"); - } - } - - Pather.checkWP(sdk.areas.StonyField, true) - ? Pather.useWaypoint(sdk.areas.StonyField) - : Pather.getWP(sdk.areas.StonyField); - Precast.doPrecast(true); - Pather.moveToPresetMonster(sdk.areas.StonyField, sdk.monsters.preset.Rakanishu, { callback: () => { - let rak = Game.getMonster(getLocaleString(sdk.locale.monsters.Rakanishu)); - return rak && (rak.dead || rak.distance < 20); - }, offX: 10, offY: 10 }); - Attack.killTarget(getLocaleString(sdk.locale.monsters.Rakanishu)); - Pather.moveToPreset(sdk.areas.StonyField, sdk.unittype.Object, sdk.quest.chest.StoneAlpha, null, null, true); - - if (!Misc.checkQuest(sdk.quest.id.TheSearchForCain, 4) && me.getItem(sdk.items.quest.KeytotheCairnStones)) { - try { - /** - * @todo I know there is a way to read the correct stone order from the packet response, need to figure that out - */ - include("core/Common/Cain.js"); - - const stoneIds = [ - sdk.quest.chest.StoneAlpha, sdk.quest.chest.StoneBeta, sdk.quest.chest.StoneGamma, - sdk.quest.chest.StoneDelta, sdk.quest.chest.StoneLambda - ]; - const getStones = () => getUnits(sdk.unittype.Object).filter(s => stoneIds.includes(s.classid) && !s.mode); - let stones = getStones(); - let sTick = getTickCount(); - let retry = true; - - while (stones.some((stone) => !stone.mode)) { - for (let i = 0; i < stones.length; i++) { - let stone = stones[i]; - - if (Common.Cain.activateStone(stone)) { - stones.splice(i, 1); - i--; - } - - if (getTickCount() - sTick < Time.minutes(2)) { - if (retry) { - stones = getStones(); - sTick = getTickCount(); - } else { - return false; - } - } - Attack.securePosition(me.x, me.y, 10, 0); - delay(10); - } - } - - let tick = getTickCount(); - // wait up to two minutes - while (getTickCount() - tick < 60 * 1000 * 2) { - if (Pather.usePortal(sdk.areas.Tristram)) { - break; - } - Attack.securePosition(me.x, me.y, 10, 1000); - } - } catch (err) { - console.error(err); - Pather.usePortal(sdk.areas.Tristram); - } - } else { - Pather.usePortal(sdk.areas.Tristram); - } - - if (me.inArea(sdk.areas.Tristram)) { - if (!me.tristram) { - let clearCoords = [ - { "x": 25166, "y": 5108, "radius": 10 }, - { "x": 25164, "y": 5115, "radius": 10 }, - { "x": 25163, "y": 5121, "radius": 10 }, - { "x": 25158, "y": 5126, "radius": 10 }, - { "x": 25151, "y": 5125, "radius": 10 }, - { "x": 25145, "y": 5129, "radius": 10 }, - { "x": 25142, "y": 5135, "radius": 10 } - ]; - Attack.clearCoordList(clearCoords); - - let gibbet = Game.getObject(sdk.quest.chest.CainsJail); - - if (gibbet && !gibbet.mode) { - Pather.moveTo(gibbet.x, gibbet.y); - Misc.openChest(gibbet); - } - - Town.npcInteract("akara"); - Pather.usePortal(sdk.areas.Tristram, me.name); - } - - Attack.clearLocations(spots); - } - - if (me.cain) { - delete Common.Cain; - } - - return true; + let spots = [ + [25176, 5128], [25175, 5145], [25171, 5159], [25166, 5178], + [25173, 5192], [25153, 5198], [25136, 5189], [25127, 5167], + [25120, 5148], [25101, 5136], [25119, 5106], [25121, 5080], + [25119, 5061], [4933, 4363] + ]; + + Town.doChores(false, { fullChores: true }); + myPrint("starting tristram"); + + // Tristram portal hasn't been opened + if (!Misc.checkQuest(sdk.quest.id.TheSearchForCain, 4)) { + const getScroll = () => { + if (me.getItem(sdk.quest.item.ScrollofInifuss) || me.getItem(sdk.quest.item.KeytotheCairnStones)) return true; + Precast.doPrecast(true); + if (!Pather.moveToPreset(sdk.areas.DarkWood, sdk.unittype.Object, sdk.quest.chest.InifussTree, 5, 5)) { + console.log("ÿc8Kolbot-SoloPlayÿc0: Failed to move to Tree of Inifuss"); + return false; + } + + Quest.collectItem(sdk.quest.item.ScrollofInifuss, sdk.quest.chest.InifussTree); + Pickit.pickItems(); + return (me.getItem(sdk.quest.item.ScrollofInifuss || me.getItem(sdk.quest.item.KeytotheCairnStones))); + }; + // missing scroll and key + if (!me.getItem(sdk.items.quest.ScrollofInifuss) && !me.getItem(sdk.items.quest.KeytotheCairnStones)) { + if (!Pather.checkWP(sdk.areas.BlackMarsh, true)) { + Pather.useWaypoint(sdk.areas.DarkWood); + getScroll(); + Pather.getWP(sdk.areas.BlackMarsh); + } else { + Pather.useWaypoint(sdk.areas.DarkWood); + getScroll(); + } + } + + if (me.getItem(sdk.items.quest.ScrollofInifuss)) { + !me.inTown && Town.goToTown(); + Town.npcInteract("akara"); + } + } + + Pather.checkWP(sdk.areas.StonyField, true) + ? Pather.useWaypoint(sdk.areas.StonyField) + : Pather.getWP(sdk.areas.StonyField); + Precast.doPrecast(true); + Pather.moveToPresetMonster(sdk.areas.StonyField, sdk.monsters.preset.Rakanishu, { callback: () => { + let rak = Game.getMonster(getLocaleString(sdk.locale.monsters.Rakanishu)); + return rak && (rak.dead || rak.distance < 20); + }, offX: 10, offY: 10 }); + Attack.killTarget(getLocaleString(sdk.locale.monsters.Rakanishu)); + Pather.moveToPreset(sdk.areas.StonyField, sdk.unittype.Object, sdk.quest.chest.StoneAlpha, null, null, true); + + if (!Misc.checkQuest(sdk.quest.id.TheSearchForCain, 4) && me.getItem(sdk.items.quest.KeytotheCairnStones)) { + try { + /** + * @todo I know there is a way to read the correct stone order from the packet response, need to figure that out + */ + include("core/Common/Cain.js"); + + const stoneIds = [ + sdk.quest.chest.StoneAlpha, sdk.quest.chest.StoneBeta, sdk.quest.chest.StoneGamma, + sdk.quest.chest.StoneDelta, sdk.quest.chest.StoneLambda + ]; + const getStones = () => getUnits(sdk.unittype.Object).filter(s => stoneIds.includes(s.classid) && !s.mode); + let stones = getStones(); + let sTick = getTickCount(); + let retry = true; + + while (stones.some((stone) => !stone.mode)) { + for (let i = 0; i < stones.length; i++) { + let stone = stones[i]; + + if (Common.Cain.activateStone(stone)) { + stones.splice(i, 1); + i--; + } + + if (getTickCount() - sTick < Time.minutes(2)) { + if (retry) { + stones = getStones(); + sTick = getTickCount(); + } else { + return false; + } + } + Attack.securePosition(me.x, me.y, 10, 0); + delay(10); + } + } + + let tick = getTickCount(); + // wait up to two minutes + while (getTickCount() - tick < 60 * 1000 * 2) { + if (Pather.usePortal(sdk.areas.Tristram)) { + break; + } + Attack.securePosition(me.x, me.y, 10, 1000); + } + } catch (err) { + console.error(err); + Pather.usePortal(sdk.areas.Tristram); + } + } else { + Pather.usePortal(sdk.areas.Tristram); + } + + if (me.inArea(sdk.areas.Tristram)) { + if (!me.tristram) { + let clearCoords = [ + { "x": 25166, "y": 5108, "radius": 10 }, + { "x": 25164, "y": 5115, "radius": 10 }, + { "x": 25163, "y": 5121, "radius": 10 }, + { "x": 25158, "y": 5126, "radius": 10 }, + { "x": 25151, "y": 5125, "radius": 10 }, + { "x": 25145, "y": 5129, "radius": 10 }, + { "x": 25142, "y": 5135, "radius": 10 } + ]; + Attack.clearCoordList(clearCoords); + + let gibbet = Game.getObject(sdk.quest.chest.CainsJail); + + if (gibbet && !gibbet.mode) { + Pather.moveTo(gibbet.x, gibbet.y); + Misc.openChest(gibbet); + } + + Town.npcInteract("akara"); + Pather.usePortal(sdk.areas.Tristram, me.name); + } + + Attack.clearLocations(spots); + } + + if (me.cain) { + delete Common.Cain; + } + + return true; } diff --git a/libs/SoloPlay/Scripts/worldstone.js b/libs/SoloPlay/Scripts/worldstone.js index 9e31ba23..03a651de 100644 --- a/libs/SoloPlay/Scripts/worldstone.js +++ b/libs/SoloPlay/Scripts/worldstone.js @@ -6,37 +6,37 @@ */ function worldstone() { - myPrint("starting worldstone"); + myPrint("starting worldstone"); - Town.doChores(null, { thawing: me.coldRes < 75, antidote: me.poisonRes < 75 }); + Town.doChores(null, { thawing: me.coldRes < 75, antidote: me.poisonRes < 75 }); - Pather.useWaypoint(sdk.areas.WorldstoneLvl2); - Precast.doPrecast(true); - /** - * Calc distances so we know whether to tp to town or not after clearing WSK1 - * - WP -> WSK3 - * - WSK1 -> WSK3 - * @todo Take into account walking vs tele and adjust distance check accordingly - */ - - /** @type {Exit[]} */ - let exits = getArea().exits; - let WS1 = exits.find(t => t.target === sdk.areas.WorldstoneLvl1); - let WS3 = exits.find(t => t.target === sdk.areas.WorldstoneLvl3); - let wpToWS3 = WS3.distance; - let ws1ToWS3 = getDistance(WS1, WS3); + Pather.useWaypoint(sdk.areas.WorldstoneLvl2); + Precast.doPrecast(true); + /** + * Calc distances so we know whether to tp to town or not after clearing WSK1 + * - WP -> WSK3 + * - WSK1 -> WSK3 + * @todo Take into account walking vs tele and adjust distance check accordingly + */ + + /** @type {Exit[]} */ + let exits = getArea().exits; + let WS1 = exits.find(t => t.target === sdk.areas.WorldstoneLvl1); + let WS3 = exits.find(t => t.target === sdk.areas.WorldstoneLvl3); + let wpToWS3 = WS3.distance; + let ws1ToWS3 = getDistance(WS1, WS3); - Attack.clearLevel(Config.ClearType); - Pather.moveToExit(sdk.areas.WorldstoneLvl1, true) && Attack.clearLevel(Config.ClearType); - if (wpToWS3 < ws1ToWS3 + Pather.getDistanceToExit(me.area, sdk.areas.WorldstoneLvl2)) { - console.log("Going to town to start from WSK2 waypoint."); - Town.goToTown(); - Pather.useWaypoint(sdk.areas.WorldstoneLvl2); - } else { - Pather.moveToExit(sdk.areas.WorldstoneLvl2, true); - } - - Pather.moveToExit(sdk.areas.WorldstoneLvl3, true) && Attack.clearLevel(Config.ClearType); + Attack.clearLevel(Config.ClearType); + Pather.moveToExit(sdk.areas.WorldstoneLvl1, true) && Attack.clearLevel(Config.ClearType); + if (wpToWS3 < ws1ToWS3 + Pather.getDistanceToExit(me.area, sdk.areas.WorldstoneLvl2)) { + console.log("Going to town to start from WSK2 waypoint."); + Town.goToTown(); + Pather.useWaypoint(sdk.areas.WorldstoneLvl2); + } else { + Pather.moveToExit(sdk.areas.WorldstoneLvl2, true); + } + + Pather.moveToExit(sdk.areas.WorldstoneLvl3, true) && Attack.clearLevel(Config.ClearType); - return true; + return true; } diff --git a/libs/SoloPlay/SoloPlay.js b/libs/SoloPlay/SoloPlay.js index b353e36b..cc53bc08 100644 --- a/libs/SoloPlay/SoloPlay.js +++ b/libs/SoloPlay/SoloPlay.js @@ -34,330 +34,330 @@ const LocalChat = require("../modules/LocalChat", null, false); */ function main () { - D2Bot.init(); // Get D2Bot# handle - D2Bot.ingame(); - - (function (global, original) { - global.load = function (...args) { - original.apply(this, args); - delay(500); - }; - })([].filter.constructor("return this")(), load); - - /** - * Fixes d2bs bug where this returns the "function" - */ - (function (original) { - me.move = function (...args) { - original.apply(this, args); - return true; - }; - })(me.move); - - // wait until game is ready - while (!me.gameReady) { - delay(50); - } - - clearAllEvents(); // remove any event listeners from game crash - - // load heartbeat if it isn't already running - !getScript("threads/heartbeat.js") && load("threads/heartbeat.js"); - - SetUp.include(); - SetUp.init(); - - let sojCounter = 0; - let sojPause = false; - let startTime = getTickCount(); - - /** - * Handle script/thread communications - * @param {string} msg - * @returns {void} - */ - const scriptEvent = function (msg) { - if (!msg || typeof msg !== "string") return; - let obj; - - if (msg.includes("--")) { - let sub = msg.match(/\w+?--/gm).first(); - - switch (sub) { - case "config--": - console.debug("update config"); - Config = JSON.parse(msg.split("config--")[1]); - - return; - case "skill--": - console.debug("update skillData"); - obj = JSON.parse(msg.split("skill--")[1]); - Misc.updateRecursively(CharData.skillData, obj); - - return; - case "data--": - console.debug("update me.data"); - obj = JSON.parse(msg.split("data--")[1]); - Misc.updateRecursively(me.data, obj); - - return; - } - } - - switch (msg) { - case "testing": - case "finishDen": - case "dodge": - case "skip": - case "killdclone": - me.emit("soloEvent", msg); - - break; - case "addDiaEvent": - console.log("Added dia lightning listener"); - addEventListener("gamepacket", SoloEvents.diaEvent); - - break; - case "removeDiaEvent": - console.log("Removed dia lightning listener"); - removeEventListener("gamepacket", SoloEvents.diaEvent); - - break; - case "addBaalEvent": - console.log("Added baal wave listener"); - addEventListener("gamepacket", SoloEvents.baalEvent); - - break; - case "removeBaalEvent": - console.log("Removed baal wave listener"); - removeEventListener("gamepacket", SoloEvents.baalEvent); - - break; - case "nextScript": - // testing - works so maybe can handle other events as well? - me.emit("nextScript"); - - break; - case "soj": - sojPause = true; - sojCounter = 0; - - break; - case "test": - { - console.debug(sdk.colors.Green + "//-----------DataDump Start-----------//", - "\nÿc8ThreadData ::\n", getScript(true), - "\nÿc8MainData ::\n", me.data, - "\nÿc8BuffData ::\n", CharData.pots, - "\nÿc8SkillData ::\n", CharData.skillData, - "\nÿc8GlobalVariabls ::\n", Object.keys(global), - "\n" + sdk.colors.Red + "//-----------DataDump End-----------//"); - } - break; - } - }; - - const copyDataEvent = function (mode, msg) { - // "Mule Profile" option from D2Bot# - if (mode === 0 && msg === "mule") { - if (AutoMule.getInfo() && AutoMule.getInfo().hasOwnProperty("muleInfo")) { - if (AutoMule.getMuleItems().length > 0) { - D2Bot.printToConsole("Mule triggered"); - scriptBroadcast("mule"); - scriptBroadcast("quit"); - } else { - D2Bot.printToConsole("No items to mule."); - } - } else { - D2Bot.printToConsole("Profile not enabled for muling."); - } - } else if (mode === 70) { - Messaging.sendToScript("D2BotSoloPlay.dbj", "event"); - delay(100 + me.ping); - scriptBroadcast("quit"); - } else if ([55, 60, 65].includes(mode)) { - // torch/anni sharing event - does this even still work? Haven't tested in awhile - me.emit("processProfileEvent", mode, msg); - } - }; - - // Initialize libs - load config variables, build pickit list, attacks, containers and cubing and runeword recipes - Config.init(true); - Pickit.init(true); - Attack.init(); - Storage.Init(); - CraftingSystem.buildLists(); - Runewords.init(); - Cubing.init(); - LocalChat.init(); - - // Load event listeners - addEventListener("scriptmsg", scriptEvent); - addEventListener("copydata", copyDataEvent); - - // AutoMule/TorchSystem/Gambling/Crafting handler - if (AutoMule.inGameCheck() - || TorchSystem.inGameCheck() - || Gambling.inGameCheck() - || CraftingSystem.inGameCheck() - || SoloEvents.inGameCheck()) { - return true; - } - - me.maxgametime = Time.seconds(Config.MaxGameTime); - const stats = DataFile.getStats(); - - // Check for experience decrease -> log death. Skip report if life chicken is disabled. - if (stats.name === me.name && me.getStat(sdk.stats.Experience) < stats.experience && Config.LifeChicken > 0) { - D2Bot.printToConsole("You died in last game. | Area :: " + stats.lastArea + " | Script :: " + stats.lastScript, sdk.colors.D2Bot.Red); - D2Bot.printToConsole("Experience decreased by " + (stats.experience - me.getStat(sdk.stats.Experience)), sdk.colors.D2Bot.Red); - DataFile.updateStats("deaths"); - D2Bot.updateDeaths(); - } - - DataFile.updateStats(["experience", "name"]); - - // Load threads - load("libs/SoloPlay/Threads/ToolsThread.js"); - - require("./Workers/EventEmitter"); - require("./Workers/EventHandler"); - require("./Workers/TownChicken"); - SoloEvents.filePath = "libs/SoloPlay/SoloPlay.js"; // hacky for now, don't want to mess up others running so we just broadcast to ourselves - - // Load guard if we want to see the stack as it runs - if (Developer.debugging.showStack.enabled) { - // check in case we reloaded and guard was still running - let guard = getScript("libs/SoloPlay/Modules/Guard.js"); - !!guard && guard.running && guard.stop(); - Developer.debugging.showStack.profiles.some(prof => String.isEqual(prof, me.profile) || String.isEqual(prof, "all")) && require("../SoloPlay/Modules/Guard"); - delay(1000); - } - - if (Config.PublicMode) { - Config.PublicMode === true ? require("libs/modules/SimpleParty") : load("threads/Party.js"); - } - - // One time maintenance - check cursor, get corpse, clear leftover items, pick items in case anything important was dropped - Cubing.cursorCheck(); - Town.getCorpse(); - Town.clearBelt(); - Pather.init(); // initialize wp data - - let { x, y } = me; - Config.ClearInvOnStart && Town.clearInventory(); - [x, y].distance > 3 && Pather.moveTo(x, y); - Pickit.pickItems(); - me.hpPercent <= 10 && Town.heal() && me.cancelUIFlags(); - - me.automap = Config.AutoMap; - - // Next game = drop keys - TorchSystem.keyCheck() && scriptBroadcast("torch"); - - // Auto skill and stat - if (Config.AutoSkill.Enabled && include("core/Auto/AutoSkill.js")) { - AutoSkill.init(Config.AutoSkill.Build, Config.AutoSkill.Save); - } - - if (Config.AutoStat.Enabled && include("core/Auto/AutoStat.js")) { - AutoStat.init(Config.AutoStat.Build, Config.AutoStat.Save, Config.AutoStat.BlockChance, Config.AutoStat.UseBulk); - } - - // offline - ensure we didn't just reload the thread and are still in the same game - if (!me.realm && getTickCount() - me.gamestarttime < Time.minutes(1)) { - D2Bot.updateRuns(); - } - - // Start Running Script - includeIfNotIncluded("SoloPlay/Utils/Init.js"); - - // log threads - track memory use - if (Config.DebugMode.Memory) { - console.log("//~~~~~~~Current Threads~~~~~~~//"); - getThreads() - .sort((a, b) => b.memory - a.memory) - .forEach(t => console.log(t)); - console.log("//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~//"); - } - - // Start Developer mode - this stops the script from progressing past this point and allows running specific scripts/functions through chat commands - if (Developer.developerMode.enabled) { - if (Developer.developerMode.profiles.some(prof => String.isEqual(prof, me.profile))) { - Developer.debugging.pathing && (me.automap = true); - Loader.runScript("developermode"); - } - } - - if (Check.brokeCheck()) return true; - if (Check.usePreviousSocketQuest()) return true; // Currently only supports going back to nightmare to socket a lidless if one is equipped. - myPrint("starting run"); - Loader.run(); - // we have scripts to retry so lets run them - if (SoloIndex.retryList.length) { - SoloIndex.scripts = SoloIndex.retryList.slice(0); - Loader.run(); - } - - if (Config.MinGameTime && getTickCount() - startTime < Time.seconds(Config.MinGameTime)) { - try { - Town.goToTown(); - - while (getTickCount() - startTime < Time.seconds(Config.MinGameTime)) { - me.overhead("Stalling for " + Math.round(((startTime + Time.seconds(Config.MinGameTime)) - getTickCount()) / 1000) + " Seconds"); - delay(1000); - } - } catch (e1) { - console.error(e1); - } - } - - DataFile.updateStats("gold"); - - if (sojPause) { - try { - Town.doChores(); - me.maxgametime = 0; - - while (sojCounter < Config.SoJWaitTime) { - me.overhead("Waiting for SoJ sales... " + (Config.SoJWaitTime - sojCounter) + " min"); - delay(6e4); - - sojCounter += 1; - } - } catch (e2) { - console.error(e2); - } - } - - if (Config.LastMessage) { - switch (typeof Config.LastMessage) { - case "string": - say(Config.LastMessage.replace("$nextgame", DataFile.getStats().nextGame, "i")); - - break; - case "object": - for (let i = 0; i < Config.LastMessage.length; i += 1) { - say(Config.LastMessage[i].replace("$nextgame", DataFile.getStats().nextGame, "i")); - } - - break; - } - } - - AutoMule.muleCheck() && scriptBroadcast("mule"); - CraftingSystem.checkFullSets() && scriptBroadcast("crafting"); - TorchSystem.keyCheck() && scriptBroadcast("torch"); - - // Anni handler. Mule Anni if it's in unlocked space and profile is set to mule torch/anni. - let anni = me.findItem(sdk.items.SmallCharm, sdk.items.mode.inStorage, -1, sdk.items.quality.Unique); - - if (anni && !Storage.Inventory.IsLocked(anni, Config.Inventory) - && AutoMule.getInfo() && AutoMule.getInfo().hasOwnProperty("torchMuleInfo")) { - scriptBroadcast("muleAnni"); - } - - scriptBroadcast("quit"); - - return true; + D2Bot.init(); // Get D2Bot# handle + D2Bot.ingame(); + + (function (global, original) { + global.load = function (...args) { + original.apply(this, args); + delay(500); + }; + })([].filter.constructor("return this")(), load); + + /** + * Fixes d2bs bug where this returns the "function" + */ + (function (original) { + me.move = function (...args) { + original.apply(this, args); + return true; + }; + })(me.move); + + // wait until game is ready + while (!me.gameReady) { + delay(50); + } + + clearAllEvents(); // remove any event listeners from game crash + + // load heartbeat if it isn't already running + !getScript("threads/heartbeat.js") && load("threads/heartbeat.js"); + + SetUp.include(); + SetUp.init(); + + let sojCounter = 0; + let sojPause = false; + let startTime = getTickCount(); + + /** + * Handle script/thread communications + * @param {string} msg + * @returns {void} + */ + const scriptEvent = function (msg) { + if (!msg || typeof msg !== "string") return; + let obj; + + if (msg.includes("--")) { + let sub = msg.match(/\w+?--/gm).first(); + + switch (sub) { + case "config--": + console.debug("update config"); + Config = JSON.parse(msg.split("config--")[1]); + + return; + case "skill--": + console.debug("update skillData"); + obj = JSON.parse(msg.split("skill--")[1]); + Misc.updateRecursively(CharData.skillData, obj); + + return; + case "data--": + console.debug("update me.data"); + obj = JSON.parse(msg.split("data--")[1]); + Misc.updateRecursively(me.data, obj); + + return; + } + } + + switch (msg) { + case "testing": + case "finishDen": + case "dodge": + case "skip": + case "killdclone": + me.emit("soloEvent", msg); + + break; + case "addDiaEvent": + console.log("Added dia lightning listener"); + addEventListener("gamepacket", SoloEvents.diaEvent); + + break; + case "removeDiaEvent": + console.log("Removed dia lightning listener"); + removeEventListener("gamepacket", SoloEvents.diaEvent); + + break; + case "addBaalEvent": + console.log("Added baal wave listener"); + addEventListener("gamepacket", SoloEvents.baalEvent); + + break; + case "removeBaalEvent": + console.log("Removed baal wave listener"); + removeEventListener("gamepacket", SoloEvents.baalEvent); + + break; + case "nextScript": + // testing - works so maybe can handle other events as well? + me.emit("nextScript"); + + break; + case "soj": + sojPause = true; + sojCounter = 0; + + break; + case "test": + { + console.debug(sdk.colors.Green + "//-----------DataDump Start-----------//", + "\nÿc8ThreadData ::\n", getScript(true), + "\nÿc8MainData ::\n", me.data, + "\nÿc8BuffData ::\n", CharData.pots, + "\nÿc8SkillData ::\n", CharData.skillData, + "\nÿc8GlobalVariabls ::\n", Object.keys(global), + "\n" + sdk.colors.Red + "//-----------DataDump End-----------//"); + } + break; + } + }; + + const copyDataEvent = function (mode, msg) { + // "Mule Profile" option from D2Bot# + if (mode === 0 && msg === "mule") { + if (AutoMule.getInfo() && AutoMule.getInfo().hasOwnProperty("muleInfo")) { + if (AutoMule.getMuleItems().length > 0) { + D2Bot.printToConsole("Mule triggered"); + scriptBroadcast("mule"); + scriptBroadcast("quit"); + } else { + D2Bot.printToConsole("No items to mule."); + } + } else { + D2Bot.printToConsole("Profile not enabled for muling."); + } + } else if (mode === 70) { + Messaging.sendToScript("D2BotSoloPlay.dbj", "event"); + delay(100 + me.ping); + scriptBroadcast("quit"); + } else if ([55, 60, 65].includes(mode)) { + // torch/anni sharing event - does this even still work? Haven't tested in awhile + me.emit("processProfileEvent", mode, msg); + } + }; + + // Initialize libs - load config variables, build pickit list, attacks, containers and cubing and runeword recipes + Config.init(true); + Pickit.init(true); + Attack.init(); + Storage.Init(); + CraftingSystem.buildLists(); + Runewords.init(); + Cubing.init(); + LocalChat.init(); + + // Load event listeners + addEventListener("scriptmsg", scriptEvent); + addEventListener("copydata", copyDataEvent); + + // AutoMule/TorchSystem/Gambling/Crafting handler + if (AutoMule.inGameCheck() + || TorchSystem.inGameCheck() + || Gambling.inGameCheck() + || CraftingSystem.inGameCheck() + || SoloEvents.inGameCheck()) { + return true; + } + + me.maxgametime = Time.seconds(Config.MaxGameTime); + const stats = DataFile.getStats(); + + // Check for experience decrease -> log death. Skip report if life chicken is disabled. + if (stats.name === me.name && me.getStat(sdk.stats.Experience) < stats.experience && Config.LifeChicken > 0) { + D2Bot.printToConsole("You died in last game. | Area :: " + stats.lastArea + " | Script :: " + stats.lastScript, sdk.colors.D2Bot.Red); + D2Bot.printToConsole("Experience decreased by " + (stats.experience - me.getStat(sdk.stats.Experience)), sdk.colors.D2Bot.Red); + DataFile.updateStats("deaths"); + D2Bot.updateDeaths(); + } + + DataFile.updateStats(["experience", "name"]); + + // Load threads + load("libs/SoloPlay/Threads/ToolsThread.js"); + + require("./Workers/EventEmitter"); + require("./Workers/EventHandler"); + require("./Workers/TownChicken"); + SoloEvents.filePath = "libs/SoloPlay/SoloPlay.js"; // hacky for now, don't want to mess up others running so we just broadcast to ourselves + + // Load guard if we want to see the stack as it runs + if (Developer.debugging.showStack.enabled) { + // check in case we reloaded and guard was still running + let guard = getScript("libs/SoloPlay/Modules/Guard.js"); + !!guard && guard.running && guard.stop(); + Developer.debugging.showStack.profiles.some(prof => String.isEqual(prof, me.profile) || String.isEqual(prof, "all")) && require("../SoloPlay/Modules/Guard"); + delay(1000); + } + + if (Config.PublicMode) { + Config.PublicMode === true ? require("libs/modules/SimpleParty") : load("threads/Party.js"); + } + + // One time maintenance - check cursor, get corpse, clear leftover items, pick items in case anything important was dropped + Cubing.cursorCheck(); + Town.getCorpse(); + Town.clearBelt(); + Pather.init(); // initialize wp data + + let { x, y } = me; + Config.ClearInvOnStart && Town.clearInventory(); + [x, y].distance > 3 && Pather.moveTo(x, y); + Pickit.pickItems(); + me.hpPercent <= 10 && Town.heal() && me.cancelUIFlags(); + + me.automap = Config.AutoMap; + + // Next game = drop keys + TorchSystem.keyCheck() && scriptBroadcast("torch"); + + // Auto skill and stat + if (Config.AutoSkill.Enabled && include("core/Auto/AutoSkill.js")) { + AutoSkill.init(Config.AutoSkill.Build, Config.AutoSkill.Save); + } + + if (Config.AutoStat.Enabled && include("core/Auto/AutoStat.js")) { + AutoStat.init(Config.AutoStat.Build, Config.AutoStat.Save, Config.AutoStat.BlockChance, Config.AutoStat.UseBulk); + } + + // offline - ensure we didn't just reload the thread and are still in the same game + if (!me.realm && getTickCount() - me.gamestarttime < Time.minutes(1)) { + D2Bot.updateRuns(); + } + + // Start Running Script + includeIfNotIncluded("SoloPlay/Utils/Init.js"); + + // log threads - track memory use + if (Config.DebugMode.Memory) { + console.log("//~~~~~~~Current Threads~~~~~~~//"); + getThreads() + .sort((a, b) => b.memory - a.memory) + .forEach(t => console.log(t)); + console.log("//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~//"); + } + + // Start Developer mode - this stops the script from progressing past this point and allows running specific scripts/functions through chat commands + if (Developer.developerMode.enabled) { + if (Developer.developerMode.profiles.some(prof => String.isEqual(prof, me.profile))) { + Developer.debugging.pathing && (me.automap = true); + Loader.runScript("developermode"); + } + } + + if (Check.brokeCheck()) return true; + if (Check.usePreviousSocketQuest()) return true; // Currently only supports going back to nightmare to socket a lidless if one is equipped. + myPrint("starting run"); + Loader.run(); + // we have scripts to retry so lets run them + if (SoloIndex.retryList.length) { + SoloIndex.scripts = SoloIndex.retryList.slice(0); + Loader.run(); + } + + if (Config.MinGameTime && getTickCount() - startTime < Time.seconds(Config.MinGameTime)) { + try { + Town.goToTown(); + + while (getTickCount() - startTime < Time.seconds(Config.MinGameTime)) { + me.overhead("Stalling for " + Math.round(((startTime + Time.seconds(Config.MinGameTime)) - getTickCount()) / 1000) + " Seconds"); + delay(1000); + } + } catch (e1) { + console.error(e1); + } + } + + DataFile.updateStats("gold"); + + if (sojPause) { + try { + Town.doChores(); + me.maxgametime = 0; + + while (sojCounter < Config.SoJWaitTime) { + me.overhead("Waiting for SoJ sales... " + (Config.SoJWaitTime - sojCounter) + " min"); + delay(6e4); + + sojCounter += 1; + } + } catch (e2) { + console.error(e2); + } + } + + if (Config.LastMessage) { + switch (typeof Config.LastMessage) { + case "string": + say(Config.LastMessage.replace("$nextgame", DataFile.getStats().nextGame, "i")); + + break; + case "object": + for (let i = 0; i < Config.LastMessage.length; i += 1) { + say(Config.LastMessage[i].replace("$nextgame", DataFile.getStats().nextGame, "i")); + } + + break; + } + } + + AutoMule.muleCheck() && scriptBroadcast("mule"); + CraftingSystem.checkFullSets() && scriptBroadcast("crafting"); + TorchSystem.keyCheck() && scriptBroadcast("torch"); + + // Anni handler. Mule Anni if it's in unlocked space and profile is set to mule torch/anni. + let anni = me.findItem(sdk.items.SmallCharm, sdk.items.mode.inStorage, -1, sdk.items.quality.Unique); + + if (anni && !Storage.Inventory.IsLocked(anni, Config.Inventory) + && AutoMule.getInfo() && AutoMule.getInfo().hasOwnProperty("torchMuleInfo")) { + scriptBroadcast("muleAnni"); + } + + scriptBroadcast("quit"); + + return true; } diff --git a/libs/SoloPlay/Threads/Reload.js b/libs/SoloPlay/Threads/Reload.js index bf08c11c..473285b5 100644 --- a/libs/SoloPlay/Threads/Reload.js +++ b/libs/SoloPlay/Threads/Reload.js @@ -8,28 +8,28 @@ js_strict(true); include("critical.js"); function main () { - let tick = getTickCount(); - let scripts = [ - "default.dbj", "libs/SoloPlay/SoloPlay.js", "libs/SoloPlay/Threads/Toolsthread.js", - "libs/SoloPlay/Modules/Guard.js", "libs/SoloPlay/Modules/TownGuard.js" - ]; + let tick = getTickCount(); + let scripts = [ + "default.dbj", "libs/SoloPlay/SoloPlay.js", "libs/SoloPlay/Threads/Toolsthread.js", + "libs/SoloPlay/Modules/Guard.js", "libs/SoloPlay/Modules/TownGuard.js" + ]; - while (scripts.length) { - let script = getScript(scripts[0]); + while (scripts.length) { + let script = getScript(scripts[0]); - if (script && script.running) { - script.stop(); + if (script && script.running) { + script.stop(); - while (script.running) { - delay(100); - } - } - scripts.shift(); - } + while (script.running) { + delay(100); + } + } + scripts.shift(); + } - while (getTickCount() - tick < 5000) { - delay(100); - } - console.log("All threads shutdown ~ Reloading bot"); - load("default.dbj"); + while (getTickCount() - tick < 5000) { + delay(100); + } + console.log("All threads shutdown ~ Reloading bot"); + load("default.dbj"); } diff --git a/libs/SoloPlay/Threads/ToolsThread.js b/libs/SoloPlay/Threads/ToolsThread.js index c3c5f2d5..c7cd513d 100644 --- a/libs/SoloPlay/Threads/ToolsThread.js +++ b/libs/SoloPlay/Threads/ToolsThread.js @@ -28,747 +28,747 @@ include("SoloPlay/Functions/Globals.js"); */ function main () { - let ironGolem, tick, quitListDelayTime; - let canQuit = true; - let timerLastDrink = []; - let [quitFlag, restart] = [false, false]; - let debugInfo = { area: 0, currScript: "no entry" }; - - new Overrides.Override(Attack, Attack.getNearestMonster, function (orignal) { - let monster = orignal({ skipBlocked: false, skipImmune: false }); - return (monster ? " to " + monster.name : ""); - }).apply(); - - console.log("ÿc8Kolbot-SoloPlayÿc0: Start Custom ToolsThread script"); - D2Bot.init(); - SetUp.include(); - Config.init(false); - Pickit.init(false); - Attack.init(); - Storage.Init(); - CraftingSystem.buildLists(); - Runewords.init(); - Cubing.init(); - - Developer.overlay && include("SoloPlay/Tools/Overlay.js"); - - for (let i = 0; i < 5; i += 1) { - timerLastDrink[i] = 0; - } - - // Reset core chicken - me.chickenhp = -1; - me.chickenmp = -1; - - // General functions - this.togglePause = function () { - ["libs/SoloPlay/SoloPlay.js", "threads/party.js"].forEach((script) => { - let thread = getScript(script); - if (thread) { - if (thread.running) { - script === "libs/SoloPlay/SoloPlay.js" && console.log("ÿc8ToolsThread :: ÿc1Pausing " + script); - thread.pause(); - } else { - script === "libs/SoloPlay/SoloPlay.js" && console.log("ÿc8ToolsThread :: ÿc2Resuming threads"); - thread.resume(); - } - } - }); - - return true; - }; - - this.stopDefault = function () { - ["libs/SoloPlay/SoloPlay.js", "libs/SoloPlay/Modules/Guard.js", "threads/party.js"] - .forEach(script => { - let thread = getScript(script); - if (thread && thread.running) { - thread.stop(); - } - }); - return true; - }; - - this.exit = function (chickenExit = false) { - chickenExit && D2Bot.updateChickens(); - Config.LogExperience && Experience.log(); - Developer.logPerformance && Tracker.update(); - console.log("ÿc8Run duration ÿc2" + Time.format(getTickCount() - me.gamestarttime)); - this.stopDefault(); - quit(); - }; - - this.restart = function () { - Config.LogExperience && Experience.log(); - Developer.logPerformance && Tracker.update(); - this.stopDefault(); - D2Bot.restart(); - }; - - this.getPotion = function (pottype = -1, type = -1) { - if (pottype === undefined) return false; - - let items = me.getItemsEx() - .filter(item => item.itemType === pottype && (type > Common.Toolsthread.pots.Rejuv ? item.isInBelt : true)); - if (items.length === 0) return false; - let invoFirst = [Common.Toolsthread.pots.Health, Common.Toolsthread.pots.Mana].includes(type); - - if (invoFirst) { - // sort by location (invo first, then classid) - items.sort(function (a, b) { - let [aLoc, bLoc] = [a.location, b.location]; - if (bLoc < aLoc) return -1; - if (bLoc > aLoc) return 1; - return b.classid - a.classid; - }); - } else { - // Get highest id = highest potion first - items.sort(function (a, b) { - return b.classid - a.classid; - }); - } - - for (let k = 0; k < items.length; k += 1) { - if (type < Common.Toolsthread.pots.MercHealth && items[k].isInInventory && items[k].itemType === pottype) { - console.log("ÿc2Drinking " + items[k].name + " from inventory."); - return items[k]; - } - - if (items[k].mode === sdk.items.mode.inBelt && items[k].itemType === pottype) { - console.log("ÿc2" + (type > 2 ? "Giving Merc " : "Drinking ") + items[k].name + " from belt."); - return items[k]; - } - } - - return false; - }; - - this.drinkPotion = function (type) { - if (type === undefined) return false; - let tNow = getTickCount(); - - switch (type) { - case Common.Toolsthread.pots.Health: - case Common.Toolsthread.pots.Mana: - if ((timerLastDrink[type] && (tNow - timerLastDrink[type] < 1000)) || me.getState(type === 0 ? 100 : 106)) { - return false; - } - - break; - case Common.Toolsthread.pots.Rejuv: - // small delay for juvs just to prevent using more at once - if (timerLastDrink[type] && (tNow - timerLastDrink[type] < 300)) { - return false; - } - - break; - case Common.Toolsthread.pots.MercRejuv: - // larger delay for juvs just to prevent using more at once, considering merc update rate - if (timerLastDrink[type] && (tNow - timerLastDrink[type] < 2000)) { - return false; - } - - break; - default: - if (timerLastDrink[type] && (tNow - timerLastDrink[type] < 8000)) { - return false; - } - - break; - } - - // mode 18 - can't drink while leaping/whirling etc. - if (me.dead || me.mode === sdk.player.mode.SkillActionSequence) return false; - - let pottype = (() => { - switch (type) { - case Common.Toolsthread.pots.Health: - case Common.Toolsthread.pots.MercHealth: - return sdk.items.type.HealingPotion; - case Common.Toolsthread.pots.Mana: - return sdk.items.type.ManaPotion; - default: - return sdk.items.type.RejuvPotion; - } - })(); - - let potion = this.getPotion(pottype, type); - - if (!!potion) { - // mode 18 - can't drink while leaping/whirling etc. - if (me.dead || me.mode === sdk.player.mode.SkillActionSequence) return false; - - try { - type < Common.Toolsthread.pots.MercHealth ? potion.interact() : Packet.useBeltItemForMerc(potion); - } catch (e) { - console.error(e); - } - - timerLastDrink[type] = getTickCount(); - delay(25); - - return true; - } - - return false; - }; - - /** - * Handles thawing/antidote/stamina potions - * @param {number} type - * @returns {boolean} - */ - this.drinkSpecialPotion = function (type) { - if (type === undefined) return false; - if (!CharData.pots.has(type)) return false; - // give at least a second delay between pots - if (CharData.pots.get(type).tick < 1000) return false; - - // mode 18 - can't drink while leaping/whirling etc. - if (me.dead || me.mode === sdk.player.mode.SkillActionSequence) { - return false; - } - - let pot = me.getItemsEx(-1, sdk.items.mode.inStorage) - .filter((p) => p.isInInventory && p.classid === type).first(); - - if (pot) { - try { - pot.interact(); - - if (!CharData.pots.get(type).active() || CharData.pots.get(type).timeLeft() <= 0) { - CharData.pots.get(type).tick = getTickCount(); - CharData.pots.get(type).duration = 3e4; - } else { - CharData.pots.get(type).duration += 3e4 - (getTickCount() - CharData.pots.get(type).tick); - } - - console.debug(CharData.pots); - } catch (e) { - console.warn(e); - } - - return true; - } - - return false; - }; - - // ~~~~~~~~~~~~~~~ // - // Event functions // - // ~~~~~~~~~~~~~~~ // - - /** - * Handle keyUp events - * @param {number} key - */ - const keyEvent = function (key) { - switch (key) { - case sdk.keys.PauseBreak: // pause default.dbj - this.togglePause(); - - break; - case sdk.keys.Numpad0: // stop profile without logging character - Developer.logPerformance && Tracker.update(); - console.log("ÿc8Kolbot-SoloPlay: ÿc1Stopping profile"); - delay(rand(2e3, 5e3)); - D2Bot.stop(me.profile, true); - - break; - case sdk.keys.End: // stop profile and log character - Developer.logEquipped ? MuleLogger.logEquippedItems() : MuleLogger.logChar(); - Developer.logPerformance && Tracker.update(); - - delay(rand(Config.QuitListDelay[0] * 1e3, Config.QuitListDelay[1] * 1e3)); - D2Bot.printToConsole(me.profile + " - end run " + me.gamename); - D2Bot.stop(me.profile, true); - - break; - case sdk.keys.Delete: // quit current game - this.exit(); - - break; - case sdk.keys.Insert: // reveal level - me.overhead("Revealing " + getAreaName(me.area)); - revealLevel(true); - - break; - case sdk.keys.NumpadPlus: // log stats - showConsole(); - - console.log("ÿc8My stats :: " + Common.Toolsthread.getStatsString(me)); - let merc = me.getMerc(); - !!merc && console.log("ÿc8Merc stats :: " + Common.Toolsthread.getStatsString(merc)); - console.log("//------ÿc8SoloWants.needListÿc0-----//"); - console.log(SoloWants.needList); - - break; - case sdk.keys.Numpad5: // force automule check - if (AutoMule.getInfo() && AutoMule.getInfo().hasOwnProperty("muleInfo")) { - if (AutoMule.getMuleItems().length > 0) { - console.log("ÿc2Mule triggered"); - scriptBroadcast("mule"); - this.exit(); - - } else { - me.overhead("No items to mule."); - } - } else { - me.overhead("Profile not enabled for muling."); - } - - break; - case sdk.keys.Numpad6: // log character to char viewer - Developer.logEquipped ? MuleLogger.logEquippedItems() : MuleLogger.logChar(); - me.overhead("Logged char: " + me.name); - - break; - case sdk.keys.NumpadDash: - { - let itemToCheck = Game.getSelectedUnit(); - if (!!itemToCheck) { - D2Bot.printToConsole("getTier: " + NTIP.GetTier(itemToCheck)); - D2Bot.printToConsole("tierscore: " + tierscore(itemToCheck)); - D2Bot.printToConsole("getSecondaryTier: " + NTIP.GetSecondaryTier(itemToCheck)); - D2Bot.printToConsole("secondarytierscore: " + secondaryscore(itemToCheck)); - D2Bot.printToConsole("charmTier: " + NTIP.GetCharmTier(itemToCheck)); - D2Bot.printToConsole("charmscore: " + charmscore(itemToCheck)); - D2Bot.printToConsole("getMercTier: " + NTIP.GetMercTier(itemToCheck)); - D2Bot.printToConsole("mercscore: " + mercscore(itemToCheck)); - console.log(itemToCheck.fname + " info printed to console"); - } - } - - break; - case sdk.keys.NumpadDecimal: // dump item info - { - let [itemString, charmString, generalString] = ["", "", ""]; - let itemToCheck = Game.getSelectedUnit(); - if (!!itemToCheck) { - let special = ""; - if (itemToCheck.itemType === sdk.items.type.Ring) { - special = (" | ÿc4TierLHS: ÿc0" + tierscore(itemToCheck, 1, sdk.body.RingRight) + " | ÿc4TierRHS: ÿc0" + tierscore(itemToCheck, 1, sdk.body.RingLeft)); - } - itemString = "ÿc4MaxQuantity: ÿc0" + NTIP.getMaxQuantity(itemToCheck) + " | ÿc4ItemsOwned: ÿc0" + Item.getQuantityOwned(itemToCheck) + " | ÿc4Tier: ÿc0" + NTIP.GetTier(itemToCheck) + (special ? special : "") - + " | ÿc4SecondaryTier: ÿc0" + NTIP.GetSecondaryTier(itemToCheck) + " | ÿc4MercTier: ÿc0" + NTIP.GetMercTier(itemToCheck) + "\n" - + "ÿc4AutoEquipKeepCheck: ÿc0" + Item.autoEquipCheck(itemToCheck, true) + " | ÿc4AutoEquipCheckSecondary: ÿc0" + Item.autoEquipCheckSecondary(itemToCheck) - + " | ÿc4AutoEquipKeepCheckMerc: ÿc0" + Item.autoEquipCheckMerc(itemToCheck, true) + "\nÿc4Cubing Item: ÿc0" + Cubing.keepItem(itemToCheck) - + " | ÿc4Runeword Item: ÿc0" + Runewords.keepItem(itemToCheck) + " | ÿc4Crafting Item: ÿc0" + CraftingSystem.keepItem(itemToCheck) + " | ÿc4SoloWants Item: ÿc0" + SoloWants.keepItem(itemToCheck) - + "\nÿc4ItemType: ÿc0" + itemToCheck.itemType + "| ÿc4Classid: ÿc0" + itemToCheck.classid + "| ÿc4Quality: ÿc0" + itemToCheck.quality; - charmString = "ÿc4InvoQuantity: ÿc0" + NTIP.getInvoQuantity(itemToCheck) + " | ÿc4hasStats: ÿc0" + NTIP.hasStats(itemToCheck) + " | ÿc4FinalCharm: ÿc0" + CharmEquip.isFinalCharm(itemToCheck) + "\n" - + "ÿc4CharmType: ÿc0" + CharmEquip.getCharmType(itemToCheck) + " | ÿc4AutoEquipCharmCheck: ÿc0" + CharmEquip.check(itemToCheck) + " | ÿc4CharmTier: ÿc0" + NTIP.GetCharmTier(itemToCheck); - generalString = "ÿc4ItemName: ÿc0" + itemToCheck.fname.split("\n").reverse().join(" ").replace(/ÿc[0-9!"+<;.*]/, "") - + "\nÿc4Pickit: ÿc0" + Pickit.checkItem(itemToCheck).result + " | ÿc4NTIP.CheckItem: ÿc0" + NTIP.CheckItem(itemToCheck, false, true).result + " | ÿc4NTIP.CheckItem No Tier: ÿc0" + NTIP.CheckItem(itemToCheck, NTIP_CheckListNoTier, true).result; - } - - console.log("ÿc8Kolbot-SoloPlay: ÿc2Item Info Start"); - console.log(itemString); - console.log("ÿc8Kolbot-SoloPlay: ÿc2Charm Info Start"); - console.log(charmString); - console.log("ÿc8Kolbot-SoloPlay: ÿc2General Info Start"); - console.log(generalString); - console.log("ÿc8Kolbot-SoloPlay: ÿc1****************Info End****************"); - } - - break; - case sdk.keys.Numpad9: // get nearest preset unit id - console.log(Common.Toolsthread.getNearestPreset()); - - break; - case sdk.keys.NumpadStar: // precast - Precast.doPrecast(true); - - break; - case sdk.keys.NumpadSlash: // re-load default - console.log("ÿc8ToolsThread :: " + sdk.colors.Red + "Stopping threads and waiting 5 seconds to restart"); - this.stopDefault() && delay(1e3); - load("libs/SoloPlay/Threads/Reload.js"); - - break; - } - }; - - /** - * Handle game events - * @param {number} mode - * @param {number} [param1] - * @param {number} [param2] - * @param {string} [name1] - * @param {string} [name2] - */ - const gameEvent = function (mode, param1, param2, name1, name2) { - switch (mode) { - case 0x00: // "%Name1(%Name2) dropped due to time out." - case 0x01: // "%Name1(%Name2) dropped due to errors." - case 0x03: // "%Name1(%Name2) left our world. Diablo's minions weaken." - Config.DebugMode.Stack && mode === 0 && D2Bot.printToConsole(name1 + " timed out, check their logs"); - - if (Config.QuitList.includes(name1) || Config.QuitList.some(str => String.isEqual(str, "all"))) { - console.log(name1 + (mode === 0 ? " timed out" : " left")); - - if (typeof quitListDelayTime === "undefined" && Config.QuitListDelay.length > 0) { - let [min, max] = Config.QuitListDelay.sort((a, b) => a - b).map(s => Time.seconds(s)); - - quitListDelayTime = getTickCount() + rand(min, max); - } else { - quitListDelayTime = getTickCount(); - } - - quitFlag = true; - } - - Config.AntiHostile && scriptBroadcast("remove " + name1); - - break; - case 0x06: - // "%Name1 was Slain by %Name2" - if (Config.AntiHostile && param2 === 0x00 && name2 === me.name) { - scriptBroadcast("mugshot " + name1); - } - - break; - case 0x07: - // "%Player has declared hostility towards you." - if (Config.AntiHostile && param2 === 0x03) { - scriptBroadcast("findHostiles"); - } - - break; - case 0x11: // "%Param1 Stones of Jordan Sold to Merchants" - if (Config.DCloneQuit === 2) { - D2Bot.printToConsole("SoJ sold in game. Leaving."); - quitFlag = true; - - break; - } - - // Only do this in expansion - if (Config.SoJWaitTime && !me.classic) { - !!me.gameserverip && D2Bot.printToConsole(param1 + " Stones of Jordan Sold to Merchants on IP " + me.gameserverip.split(".")[3], sdk.colors.D2Bot.DarkGold); - Messaging.sendToScript("libs/SoloPlay/SoloPlay.js", "soj"); - } - - break; - case 0x12: // "Diablo Walks the Earth" - if (Config.DCloneQuit > 0) { - D2Bot.printToConsole("Diablo walked in game. Leaving."); - quitFlag = true; - - break; - } - - // Only do this in expansion - if (Config.StopOnDClone && !me.classic && me.hell) { - D2Bot.printToConsole("Diablo Walks the Earth", sdk.colors.D2Bot.DarkGold); - SoloEvents.cloneWalked = true; - this.togglePause(); - Town.goToTown(); - showConsole(); - myPrint("ÿc4Diablo Walks the Earth"); - me.maxgametime += (30 * 1000 * 60); // Add 30 minutes to current maxgametime - Config.KillDclone && Messaging.sendToScript(SoloEvents.filePath, "killdclone"); - } - - break; - } - }; - - /** - * Handle script/thread communications - * @param {string} msg - * @returns {void} - */ - const scriptEvent = function (msg) { - if (!msg || typeof msg !== "string") return; - - let obj; - - if (msg.includes("--")) { - let sub = msg.match(/\w+?--/gm).first(); - - switch (sub) { - case "config--": - console.debug("update config"); - Config = JSON.parse(msg.split("config--")[1]); - - return; - case "skill--": - console.debug("update skillData"); - obj = JSON.parse(msg.split("skill--")[1]); - Misc.updateRecursively(CharData.skillData, obj); - - return; - case "data--": - console.debug("update me.data"); - obj = JSON.parse(msg.split("data--")[1]); - Misc.updateRecursively(me.data, obj); - - return; - } - } - - switch (msg) { - case "deleteAndRemake": - Developer.testingMode.enabled && (quitFlag = true); - - break; - case "toggleQuitlist": - canQuit = !canQuit; - - break; - case "quit": - quitFlag = true; - - break; - case "restart": - restart = true; - - break; - case "test": - { - console.debug(sdk.colors.Green + "//-----------DataDump Start-----------//", - "\nÿc8ThreadData ::\n", getScript(true), - "\nÿc8MainData ::\n", me.data, - "\nÿc8BuffData ::\n", CharData.pots, - "\nÿc8SkillData ::\n", CharData.skillData, - "\nÿc8GlobalVariabls ::\n", Object.keys(global), - "\n" + sdk.colors.Red + "//-----------DataDump End-----------//"); - } - break; - // ignore common scriptBroadcast messages that aren't relevent to this thread - case "mule": - case "muleTorch": - case "muleAnni": - case "torch": - case "crafting": - case "getMuleMode": - case "pingquit": - case "townCheck": - break; - default: - try { - obj = JSON.parse(msg); - } catch (e) { - return; - } - - if (obj) { - obj.hasOwnProperty("currScript") && (debugInfo.currScript = obj.currScript); - obj.hasOwnProperty("lastAction") && (debugInfo.lastAction = obj.lastAction); - // D2Bot.store(JSON.stringify(debugInfo)); - DataFile.updateStats("debugInfo", JSON.stringify(debugInfo)); - } - - break; - } - }; - - // Cache variables to prevent a bug where d2bs loses the reference to Config object - Config = copyObj(Config); - tick = getTickCount(); - - // getUnit test - getUnit(-1) === null && console.warn("getUnit bug detected"); - - addEventListener("keyup", keyEvent); - addEventListener("gameevent", gameEvent); - addEventListener("scriptmsg", scriptEvent); - - Config.QuitListMode > 0 && Common.Toolsthread.initQuitList(); - !Array.isArray(Config.QuitList) && (Config.QuitList = [Config.QuitList]); - - let myAct = me.act; - - if (Developer.overlay && !Developer.logPerformance) { - console.warn("Without logPerformance set, the overlay will only show partial values"); - } - - const Worker = require("../../modules/Worker"); - const diffShort = ["Norm", "Night", "Hell"][me.diff]; - - // Start worker - handles overlay and d2bot# profile display - Worker.runInBackground.display = (new function () { - let _timeout = 0; - let gameTracker; - - function timer () { - const currInGame = (getTickCount() - me.gamestarttime); - let timeStr = " (Time: " + Time.format(currInGame) + ") "; - - if (Developer.displayClockInConsole && Developer.logPerformance) { - try { - gameTracker === undefined && (gameTracker = Tracker.readObj(Tracker.GTPath)); - let [tTime, tInGame, tDays] = [ - (gameTracker.Total + currInGame), - (gameTracker.InGame + currInGame), - (gameTracker.Total + currInGame) - ]; - let [totalTime, totalInGame, totalDays] = [ - Time.format(tTime), - Time.format(tInGame), - Tracker.totalDays(tDays) - ]; - timeStr += ("(Days: " + totalDays + ") (Total: " + totalTime + ") (IG: " + totalInGame + ") (OOG: " + Time.format(gameTracker.OOG) + ")"); - } catch (e) { - console.log(e); - } - } - return timeStr; - } - - this.run = () => { - if (getTickCount() - _timeout < 500) return true; - _timeout = getTickCount(); - - if (me.gameReady) { - // handle d2bot# profile display - let statusString = ""; - - try { - statusString = [ - (me.name + " | "), - ("Lvl: " + me.charlvl), - (" (" + Experience.progress() + "%) "), - ("(Diff: " + diffShort + ") "), - ("(A: " + getAreaName(me.area) + ") "), - ("(G: " + me.gold + ") "), - ("(F: " + me.FR + "/C: " + me.CR + "/L: " + me.LR + "/P: " + me.PR + ")"), - ].join(""); - - D2Bot.updateStatus(statusString + timer()); - } catch (e) { - console.error(e); - } - - // handle overlay - if (Developer.overlay) { - try { - if (me.ingame && me.gameReady && me.area) { - Overlay.update(quitFlag); - - if (me.act !== myAct) { - Overlay.flush(); - myAct = me.act; - Overlay.update(quitFlag); - } - } - } catch (e) { - console.error(e); - console.log("Overlay disabled"); - D2Bot.printToConsole("Overlay disabled", sdk.colors.D2Bot.Red); - Developer.overlay = false; - } - } - } - - return true; - }; - }).run; - - // Start ToolsThread - while (true) { - try { - if (me.gameReady && !me.inTown) { - // todo - build potion list only once per iteration - Config.UseHP > 0 && me.hpPercent < Config.UseHP && this.drinkPotion(Common.Toolsthread.pots.Health); - Config.UseRejuvHP > 0 && me.hpPercent < Config.UseRejuvHP && this.drinkPotion(Common.Toolsthread.pots.Rejuv); - - if (Config.LifeChicken > 0 && me.hpPercent <= Config.LifeChicken && !me.inTown) { - if (!Developer.hideChickens) { - D2Bot.printToConsole("Life Chicken (" + me.hp + "/" + me.hpmax + ")" + Attack.getNearestMonster() + " in " + getAreaName(me.area) + ". Ping: " + me.ping, sdk.colors.D2Bot.Red); - } - this.exit(true); - - return true; - } - - Config.UseMP > 0 && me.mpPercent < Config.UseMP && this.drinkPotion(Common.Toolsthread.pots.Mana); - Config.UseRejuvMP > 0 && me.mpPercent < Config.UseRejuvMP && this.drinkPotion(Common.Toolsthread.pots.Rejuv); - - (me.staminaPercent <= 20 || me.walking) && this.drinkSpecialPotion(sdk.items.StaminaPotion); - me.getState(sdk.states.Poison) && this.drinkSpecialPotion(sdk.items.AntidotePotion); - [sdk.states.Frozen, sdk.states.FrozenSolid].some(state => me.getState(state)) && this.drinkSpecialPotion(sdk.items.ThawingPotion); - - if (Config.ManaChicken > 0 && me.mpPercent <= Config.ManaChicken && !me.inTown) { - !Developer.hideChickens && D2Bot.printToConsole("Mana Chicken: (" + me.mp + "/" + me.mpmax + ") in " + getAreaName(me.area), sdk.colors.D2Bot.Red); - this.exit(true); - - return true; - } - - if (Config.IronGolemChicken > 0 && me.necromancer) { - if (!ironGolem || copyUnit(ironGolem).x === undefined) { - ironGolem = Common.Toolsthread.getIronGolem(); - } - - if (ironGolem) { - // ironGolem.hpmax is bugged with BO - if (ironGolem.hp <= Math.floor(128 * Config.IronGolemChicken / 100)) { - !Developer.hideChickens && D2Bot.printToConsole("Irom Golem Chicken in " + getAreaName(me.area), sdk.colors.D2Bot.Red); - this.exit(true); - - return true; - } - } - } - - if (Config.UseMerc) { - let merc = me.getMerc(); - if (!!merc) { - let mercHP = getMercHP(); - - if (mercHP > 0 && merc.mode !== sdk.monsters.mode.Dead) { - if (mercHP < Config.MercChicken) { - !Developer.hideChickens && D2Bot.printToConsole("Merc Chicken in " + getAreaName(me.area), sdk.colors.D2Bot.Red); - this.exit(true); - - return true; - } - - mercHP < Config.UseMercHP && this.drinkPotion(Common.Toolsthread.pots.MercHealth); - mercHP < Config.UseMercRejuv && this.drinkPotion(Common.Toolsthread.pots.MercRejuv); - } - } - } - - if (Config.ViperCheck && getTickCount() - tick >= 250) { - if (Common.Toolsthread.checkVipers()) { - D2Bot.printToConsole("Revived Tomb Vipers found. Leaving game.", sdk.colors.D2Bot.Red); - quitFlag = true; - } - - tick = getTickCount(); - } - - Common.Toolsthread.checkPing(true) && (quitFlag = true); - } - } catch (e) { - Misc.errorReport(e, "ToolsThread"); - - quitFlag = true; - } - - if (me.maxgametime - (getTickCount() - me.gamestarttime) < 10e3) { - console.log("Max game time reached"); - quitFlag = true; - } - - if (quitFlag && canQuit && (typeof quitListDelayTime === "undefined" || getTickCount() >= quitListDelayTime)) { - Common.Toolsthread.checkPing(false); // In case of quitlist triggering first - this.exit(); - - break; - } - - !!restart && this.restart(); - - if (debugInfo.area !== getAreaName(me.area)) { - debugInfo.area = getAreaName(me.area); - DataFile.updateStats("debugInfo", JSON.stringify(debugInfo)); - } - - delay(20); - } - - return true; + let ironGolem, tick, quitListDelayTime; + let canQuit = true; + let timerLastDrink = []; + let [quitFlag, restart] = [false, false]; + let debugInfo = { area: 0, currScript: "no entry" }; + + new Overrides.Override(Attack, Attack.getNearestMonster, function (orignal) { + let monster = orignal({ skipBlocked: false, skipImmune: false }); + return (monster ? " to " + monster.name : ""); + }).apply(); + + console.log("ÿc8Kolbot-SoloPlayÿc0: Start Custom ToolsThread script"); + D2Bot.init(); + SetUp.include(); + Config.init(false); + Pickit.init(false); + Attack.init(); + Storage.Init(); + CraftingSystem.buildLists(); + Runewords.init(); + Cubing.init(); + + Developer.overlay && include("SoloPlay/Tools/Overlay.js"); + + for (let i = 0; i < 5; i += 1) { + timerLastDrink[i] = 0; + } + + // Reset core chicken + me.chickenhp = -1; + me.chickenmp = -1; + + // General functions + this.togglePause = function () { + ["libs/SoloPlay/SoloPlay.js", "threads/party.js"].forEach((script) => { + let thread = getScript(script); + if (thread) { + if (thread.running) { + script === "libs/SoloPlay/SoloPlay.js" && console.log("ÿc8ToolsThread :: ÿc1Pausing " + script); + thread.pause(); + } else { + script === "libs/SoloPlay/SoloPlay.js" && console.log("ÿc8ToolsThread :: ÿc2Resuming threads"); + thread.resume(); + } + } + }); + + return true; + }; + + this.stopDefault = function () { + ["libs/SoloPlay/SoloPlay.js", "libs/SoloPlay/Modules/Guard.js", "threads/party.js"] + .forEach(script => { + let thread = getScript(script); + if (thread && thread.running) { + thread.stop(); + } + }); + return true; + }; + + this.exit = function (chickenExit = false) { + chickenExit && D2Bot.updateChickens(); + Config.LogExperience && Experience.log(); + Developer.logPerformance && Tracker.update(); + console.log("ÿc8Run duration ÿc2" + Time.format(getTickCount() - me.gamestarttime)); + this.stopDefault(); + quit(); + }; + + this.restart = function () { + Config.LogExperience && Experience.log(); + Developer.logPerformance && Tracker.update(); + this.stopDefault(); + D2Bot.restart(); + }; + + this.getPotion = function (pottype = -1, type = -1) { + if (pottype === undefined) return false; + + let items = me.getItemsEx() + .filter(item => item.itemType === pottype && (type > Common.Toolsthread.pots.Rejuv ? item.isInBelt : true)); + if (items.length === 0) return false; + let invoFirst = [Common.Toolsthread.pots.Health, Common.Toolsthread.pots.Mana].includes(type); + + if (invoFirst) { + // sort by location (invo first, then classid) + items.sort(function (a, b) { + let [aLoc, bLoc] = [a.location, b.location]; + if (bLoc < aLoc) return -1; + if (bLoc > aLoc) return 1; + return b.classid - a.classid; + }); + } else { + // Get highest id = highest potion first + items.sort(function (a, b) { + return b.classid - a.classid; + }); + } + + for (let k = 0; k < items.length; k += 1) { + if (type < Common.Toolsthread.pots.MercHealth && items[k].isInInventory && items[k].itemType === pottype) { + console.log("ÿc2Drinking " + items[k].name + " from inventory."); + return items[k]; + } + + if (items[k].mode === sdk.items.mode.inBelt && items[k].itemType === pottype) { + console.log("ÿc2" + (type > 2 ? "Giving Merc " : "Drinking ") + items[k].name + " from belt."); + return items[k]; + } + } + + return false; + }; + + this.drinkPotion = function (type) { + if (type === undefined) return false; + let tNow = getTickCount(); + + switch (type) { + case Common.Toolsthread.pots.Health: + case Common.Toolsthread.pots.Mana: + if ((timerLastDrink[type] && (tNow - timerLastDrink[type] < 1000)) || me.getState(type === 0 ? 100 : 106)) { + return false; + } + + break; + case Common.Toolsthread.pots.Rejuv: + // small delay for juvs just to prevent using more at once + if (timerLastDrink[type] && (tNow - timerLastDrink[type] < 300)) { + return false; + } + + break; + case Common.Toolsthread.pots.MercRejuv: + // larger delay for juvs just to prevent using more at once, considering merc update rate + if (timerLastDrink[type] && (tNow - timerLastDrink[type] < 2000)) { + return false; + } + + break; + default: + if (timerLastDrink[type] && (tNow - timerLastDrink[type] < 8000)) { + return false; + } + + break; + } + + // mode 18 - can't drink while leaping/whirling etc. + if (me.dead || me.mode === sdk.player.mode.SkillActionSequence) return false; + + let pottype = (() => { + switch (type) { + case Common.Toolsthread.pots.Health: + case Common.Toolsthread.pots.MercHealth: + return sdk.items.type.HealingPotion; + case Common.Toolsthread.pots.Mana: + return sdk.items.type.ManaPotion; + default: + return sdk.items.type.RejuvPotion; + } + })(); + + let potion = this.getPotion(pottype, type); + + if (!!potion) { + // mode 18 - can't drink while leaping/whirling etc. + if (me.dead || me.mode === sdk.player.mode.SkillActionSequence) return false; + + try { + type < Common.Toolsthread.pots.MercHealth ? potion.interact() : Packet.useBeltItemForMerc(potion); + } catch (e) { + console.error(e); + } + + timerLastDrink[type] = getTickCount(); + delay(25); + + return true; + } + + return false; + }; + + /** + * Handles thawing/antidote/stamina potions + * @param {number} type + * @returns {boolean} + */ + this.drinkSpecialPotion = function (type) { + if (type === undefined) return false; + if (!CharData.pots.has(type)) return false; + // give at least a second delay between pots + if (CharData.pots.get(type).tick < 1000) return false; + + // mode 18 - can't drink while leaping/whirling etc. + if (me.dead || me.mode === sdk.player.mode.SkillActionSequence) { + return false; + } + + let pot = me.getItemsEx(-1, sdk.items.mode.inStorage) + .filter((p) => p.isInInventory && p.classid === type).first(); + + if (pot) { + try { + pot.interact(); + + if (!CharData.pots.get(type).active() || CharData.pots.get(type).timeLeft() <= 0) { + CharData.pots.get(type).tick = getTickCount(); + CharData.pots.get(type).duration = 3e4; + } else { + CharData.pots.get(type).duration += 3e4 - (getTickCount() - CharData.pots.get(type).tick); + } + + console.debug(CharData.pots); + } catch (e) { + console.warn(e); + } + + return true; + } + + return false; + }; + + // ~~~~~~~~~~~~~~~ // + // Event functions // + // ~~~~~~~~~~~~~~~ // + + /** + * Handle keyUp events + * @param {number} key + */ + const keyEvent = function (key) { + switch (key) { + case sdk.keys.PauseBreak: // pause default.dbj + this.togglePause(); + + break; + case sdk.keys.Numpad0: // stop profile without logging character + Developer.logPerformance && Tracker.update(); + console.log("ÿc8Kolbot-SoloPlay: ÿc1Stopping profile"); + delay(rand(2e3, 5e3)); + D2Bot.stop(me.profile, true); + + break; + case sdk.keys.End: // stop profile and log character + Developer.logEquipped ? MuleLogger.logEquippedItems() : MuleLogger.logChar(); + Developer.logPerformance && Tracker.update(); + + delay(rand(Config.QuitListDelay[0] * 1e3, Config.QuitListDelay[1] * 1e3)); + D2Bot.printToConsole(me.profile + " - end run " + me.gamename); + D2Bot.stop(me.profile, true); + + break; + case sdk.keys.Delete: // quit current game + this.exit(); + + break; + case sdk.keys.Insert: // reveal level + me.overhead("Revealing " + getAreaName(me.area)); + revealLevel(true); + + break; + case sdk.keys.NumpadPlus: // log stats + showConsole(); + + console.log("ÿc8My stats :: " + Common.Toolsthread.getStatsString(me)); + let merc = me.getMerc(); + !!merc && console.log("ÿc8Merc stats :: " + Common.Toolsthread.getStatsString(merc)); + console.log("//------ÿc8SoloWants.needListÿc0-----//"); + console.log(SoloWants.needList); + + break; + case sdk.keys.Numpad5: // force automule check + if (AutoMule.getInfo() && AutoMule.getInfo().hasOwnProperty("muleInfo")) { + if (AutoMule.getMuleItems().length > 0) { + console.log("ÿc2Mule triggered"); + scriptBroadcast("mule"); + this.exit(); + + } else { + me.overhead("No items to mule."); + } + } else { + me.overhead("Profile not enabled for muling."); + } + + break; + case sdk.keys.Numpad6: // log character to char viewer + Developer.logEquipped ? MuleLogger.logEquippedItems() : MuleLogger.logChar(); + me.overhead("Logged char: " + me.name); + + break; + case sdk.keys.NumpadDash: + { + let itemToCheck = Game.getSelectedUnit(); + if (!!itemToCheck) { + D2Bot.printToConsole("getTier: " + NTIP.GetTier(itemToCheck)); + D2Bot.printToConsole("tierscore: " + tierscore(itemToCheck)); + D2Bot.printToConsole("getSecondaryTier: " + NTIP.GetSecondaryTier(itemToCheck)); + D2Bot.printToConsole("secondarytierscore: " + secondaryscore(itemToCheck)); + D2Bot.printToConsole("charmTier: " + NTIP.GetCharmTier(itemToCheck)); + D2Bot.printToConsole("charmscore: " + charmscore(itemToCheck)); + D2Bot.printToConsole("getMercTier: " + NTIP.GetMercTier(itemToCheck)); + D2Bot.printToConsole("mercscore: " + mercscore(itemToCheck)); + console.log(itemToCheck.fname + " info printed to console"); + } + } + + break; + case sdk.keys.NumpadDecimal: // dump item info + { + let [itemString, charmString, generalString] = ["", "", ""]; + let itemToCheck = Game.getSelectedUnit(); + if (!!itemToCheck) { + let special = ""; + if (itemToCheck.itemType === sdk.items.type.Ring) { + special = (" | ÿc4TierLHS: ÿc0" + tierscore(itemToCheck, 1, sdk.body.RingRight) + " | ÿc4TierRHS: ÿc0" + tierscore(itemToCheck, 1, sdk.body.RingLeft)); + } + itemString = "ÿc4MaxQuantity: ÿc0" + NTIP.getMaxQuantity(itemToCheck) + " | ÿc4ItemsOwned: ÿc0" + Item.getQuantityOwned(itemToCheck) + " | ÿc4Tier: ÿc0" + NTIP.GetTier(itemToCheck) + (special ? special : "") + + " | ÿc4SecondaryTier: ÿc0" + NTIP.GetSecondaryTier(itemToCheck) + " | ÿc4MercTier: ÿc0" + NTIP.GetMercTier(itemToCheck) + "\n" + + "ÿc4AutoEquipKeepCheck: ÿc0" + Item.autoEquipCheck(itemToCheck, true) + " | ÿc4AutoEquipCheckSecondary: ÿc0" + Item.autoEquipCheckSecondary(itemToCheck) + + " | ÿc4AutoEquipKeepCheckMerc: ÿc0" + Item.autoEquipCheckMerc(itemToCheck, true) + "\nÿc4Cubing Item: ÿc0" + Cubing.keepItem(itemToCheck) + + " | ÿc4Runeword Item: ÿc0" + Runewords.keepItem(itemToCheck) + " | ÿc4Crafting Item: ÿc0" + CraftingSystem.keepItem(itemToCheck) + " | ÿc4SoloWants Item: ÿc0" + SoloWants.keepItem(itemToCheck) + + "\nÿc4ItemType: ÿc0" + itemToCheck.itemType + "| ÿc4Classid: ÿc0" + itemToCheck.classid + "| ÿc4Quality: ÿc0" + itemToCheck.quality; + charmString = "ÿc4InvoQuantity: ÿc0" + NTIP.getInvoQuantity(itemToCheck) + " | ÿc4hasStats: ÿc0" + NTIP.hasStats(itemToCheck) + " | ÿc4FinalCharm: ÿc0" + CharmEquip.isFinalCharm(itemToCheck) + "\n" + + "ÿc4CharmType: ÿc0" + CharmEquip.getCharmType(itemToCheck) + " | ÿc4AutoEquipCharmCheck: ÿc0" + CharmEquip.check(itemToCheck) + " | ÿc4CharmTier: ÿc0" + NTIP.GetCharmTier(itemToCheck); + generalString = "ÿc4ItemName: ÿc0" + itemToCheck.fname.split("\n").reverse().join(" ").replace(/ÿc[0-9!"+<;.*]/, "") + + "\nÿc4Pickit: ÿc0" + Pickit.checkItem(itemToCheck).result + " | ÿc4NTIP.CheckItem: ÿc0" + NTIP.CheckItem(itemToCheck, false, true).result + " | ÿc4NTIP.CheckItem No Tier: ÿc0" + NTIP.CheckItem(itemToCheck, NTIP_CheckListNoTier, true).result; + } + + console.log("ÿc8Kolbot-SoloPlay: ÿc2Item Info Start"); + console.log(itemString); + console.log("ÿc8Kolbot-SoloPlay: ÿc2Charm Info Start"); + console.log(charmString); + console.log("ÿc8Kolbot-SoloPlay: ÿc2General Info Start"); + console.log(generalString); + console.log("ÿc8Kolbot-SoloPlay: ÿc1****************Info End****************"); + } + + break; + case sdk.keys.Numpad9: // get nearest preset unit id + console.log(Common.Toolsthread.getNearestPreset()); + + break; + case sdk.keys.NumpadStar: // precast + Precast.doPrecast(true); + + break; + case sdk.keys.NumpadSlash: // re-load default + console.log("ÿc8ToolsThread :: " + sdk.colors.Red + "Stopping threads and waiting 5 seconds to restart"); + this.stopDefault() && delay(1e3); + load("libs/SoloPlay/Threads/Reload.js"); + + break; + } + }; + + /** + * Handle game events + * @param {number} mode + * @param {number} [param1] + * @param {number} [param2] + * @param {string} [name1] + * @param {string} [name2] + */ + const gameEvent = function (mode, param1, param2, name1, name2) { + switch (mode) { + case 0x00: // "%Name1(%Name2) dropped due to time out." + case 0x01: // "%Name1(%Name2) dropped due to errors." + case 0x03: // "%Name1(%Name2) left our world. Diablo's minions weaken." + Config.DebugMode.Stack && mode === 0 && D2Bot.printToConsole(name1 + " timed out, check their logs"); + + if (Config.QuitList.includes(name1) || Config.QuitList.some(str => String.isEqual(str, "all"))) { + console.log(name1 + (mode === 0 ? " timed out" : " left")); + + if (typeof quitListDelayTime === "undefined" && Config.QuitListDelay.length > 0) { + let [min, max] = Config.QuitListDelay.sort((a, b) => a - b).map(s => Time.seconds(s)); + + quitListDelayTime = getTickCount() + rand(min, max); + } else { + quitListDelayTime = getTickCount(); + } + + quitFlag = true; + } + + Config.AntiHostile && scriptBroadcast("remove " + name1); + + break; + case 0x06: + // "%Name1 was Slain by %Name2" + if (Config.AntiHostile && param2 === 0x00 && name2 === me.name) { + scriptBroadcast("mugshot " + name1); + } + + break; + case 0x07: + // "%Player has declared hostility towards you." + if (Config.AntiHostile && param2 === 0x03) { + scriptBroadcast("findHostiles"); + } + + break; + case 0x11: // "%Param1 Stones of Jordan Sold to Merchants" + if (Config.DCloneQuit === 2) { + D2Bot.printToConsole("SoJ sold in game. Leaving."); + quitFlag = true; + + break; + } + + // Only do this in expansion + if (Config.SoJWaitTime && !me.classic) { + !!me.gameserverip && D2Bot.printToConsole(param1 + " Stones of Jordan Sold to Merchants on IP " + me.gameserverip.split(".")[3], sdk.colors.D2Bot.DarkGold); + Messaging.sendToScript("libs/SoloPlay/SoloPlay.js", "soj"); + } + + break; + case 0x12: // "Diablo Walks the Earth" + if (Config.DCloneQuit > 0) { + D2Bot.printToConsole("Diablo walked in game. Leaving."); + quitFlag = true; + + break; + } + + // Only do this in expansion + if (Config.StopOnDClone && !me.classic && me.hell) { + D2Bot.printToConsole("Diablo Walks the Earth", sdk.colors.D2Bot.DarkGold); + SoloEvents.cloneWalked = true; + this.togglePause(); + Town.goToTown(); + showConsole(); + myPrint("ÿc4Diablo Walks the Earth"); + me.maxgametime += (30 * 1000 * 60); // Add 30 minutes to current maxgametime + Config.KillDclone && Messaging.sendToScript(SoloEvents.filePath, "killdclone"); + } + + break; + } + }; + + /** + * Handle script/thread communications + * @param {string} msg + * @returns {void} + */ + const scriptEvent = function (msg) { + if (!msg || typeof msg !== "string") return; + + let obj; + + if (msg.includes("--")) { + let sub = msg.match(/\w+?--/gm).first(); + + switch (sub) { + case "config--": + console.debug("update config"); + Config = JSON.parse(msg.split("config--")[1]); + + return; + case "skill--": + console.debug("update skillData"); + obj = JSON.parse(msg.split("skill--")[1]); + Misc.updateRecursively(CharData.skillData, obj); + + return; + case "data--": + console.debug("update me.data"); + obj = JSON.parse(msg.split("data--")[1]); + Misc.updateRecursively(me.data, obj); + + return; + } + } + + switch (msg) { + case "deleteAndRemake": + Developer.testingMode.enabled && (quitFlag = true); + + break; + case "toggleQuitlist": + canQuit = !canQuit; + + break; + case "quit": + quitFlag = true; + + break; + case "restart": + restart = true; + + break; + case "test": + { + console.debug(sdk.colors.Green + "//-----------DataDump Start-----------//", + "\nÿc8ThreadData ::\n", getScript(true), + "\nÿc8MainData ::\n", me.data, + "\nÿc8BuffData ::\n", CharData.pots, + "\nÿc8SkillData ::\n", CharData.skillData, + "\nÿc8GlobalVariabls ::\n", Object.keys(global), + "\n" + sdk.colors.Red + "//-----------DataDump End-----------//"); + } + break; + // ignore common scriptBroadcast messages that aren't relevent to this thread + case "mule": + case "muleTorch": + case "muleAnni": + case "torch": + case "crafting": + case "getMuleMode": + case "pingquit": + case "townCheck": + break; + default: + try { + obj = JSON.parse(msg); + } catch (e) { + return; + } + + if (obj) { + obj.hasOwnProperty("currScript") && (debugInfo.currScript = obj.currScript); + obj.hasOwnProperty("lastAction") && (debugInfo.lastAction = obj.lastAction); + // D2Bot.store(JSON.stringify(debugInfo)); + DataFile.updateStats("debugInfo", JSON.stringify(debugInfo)); + } + + break; + } + }; + + // Cache variables to prevent a bug where d2bs loses the reference to Config object + Config = copyObj(Config); + tick = getTickCount(); + + // getUnit test + getUnit(-1) === null && console.warn("getUnit bug detected"); + + addEventListener("keyup", keyEvent); + addEventListener("gameevent", gameEvent); + addEventListener("scriptmsg", scriptEvent); + + Config.QuitListMode > 0 && Common.Toolsthread.initQuitList(); + !Array.isArray(Config.QuitList) && (Config.QuitList = [Config.QuitList]); + + let myAct = me.act; + + if (Developer.overlay && !Developer.logPerformance) { + console.warn("Without logPerformance set, the overlay will only show partial values"); + } + + const Worker = require("../../modules/Worker"); + const diffShort = ["Norm", "Night", "Hell"][me.diff]; + + // Start worker - handles overlay and d2bot# profile display + Worker.runInBackground.display = (new function () { + let _timeout = 0; + let gameTracker; + + function timer () { + const currInGame = (getTickCount() - me.gamestarttime); + let timeStr = " (Time: " + Time.format(currInGame) + ") "; + + if (Developer.displayClockInConsole && Developer.logPerformance) { + try { + gameTracker === undefined && (gameTracker = Tracker.readObj(Tracker.GTPath)); + let [tTime, tInGame, tDays] = [ + (gameTracker.Total + currInGame), + (gameTracker.InGame + currInGame), + (gameTracker.Total + currInGame) + ]; + let [totalTime, totalInGame, totalDays] = [ + Time.format(tTime), + Time.format(tInGame), + Tracker.totalDays(tDays) + ]; + timeStr += ("(Days: " + totalDays + ") (Total: " + totalTime + ") (IG: " + totalInGame + ") (OOG: " + Time.format(gameTracker.OOG) + ")"); + } catch (e) { + console.log(e); + } + } + return timeStr; + } + + this.run = () => { + if (getTickCount() - _timeout < 500) return true; + _timeout = getTickCount(); + + if (me.gameReady) { + // handle d2bot# profile display + let statusString = ""; + + try { + statusString = [ + (me.name + " | "), + ("Lvl: " + me.charlvl), + (" (" + Experience.progress() + "%) "), + ("(Diff: " + diffShort + ") "), + ("(A: " + getAreaName(me.area) + ") "), + ("(G: " + me.gold + ") "), + ("(F: " + me.FR + "/C: " + me.CR + "/L: " + me.LR + "/P: " + me.PR + ")"), + ].join(""); + + D2Bot.updateStatus(statusString + timer()); + } catch (e) { + console.error(e); + } + + // handle overlay + if (Developer.overlay) { + try { + if (me.ingame && me.gameReady && me.area) { + Overlay.update(quitFlag); + + if (me.act !== myAct) { + Overlay.flush(); + myAct = me.act; + Overlay.update(quitFlag); + } + } + } catch (e) { + console.error(e); + console.log("Overlay disabled"); + D2Bot.printToConsole("Overlay disabled", sdk.colors.D2Bot.Red); + Developer.overlay = false; + } + } + } + + return true; + }; + }).run; + + // Start ToolsThread + while (true) { + try { + if (me.gameReady && !me.inTown) { + // todo - build potion list only once per iteration + Config.UseHP > 0 && me.hpPercent < Config.UseHP && this.drinkPotion(Common.Toolsthread.pots.Health); + Config.UseRejuvHP > 0 && me.hpPercent < Config.UseRejuvHP && this.drinkPotion(Common.Toolsthread.pots.Rejuv); + + if (Config.LifeChicken > 0 && me.hpPercent <= Config.LifeChicken && !me.inTown) { + if (!Developer.hideChickens) { + D2Bot.printToConsole("Life Chicken (" + me.hp + "/" + me.hpmax + ")" + Attack.getNearestMonster() + " in " + getAreaName(me.area) + ". Ping: " + me.ping, sdk.colors.D2Bot.Red); + } + this.exit(true); + + return true; + } + + Config.UseMP > 0 && me.mpPercent < Config.UseMP && this.drinkPotion(Common.Toolsthread.pots.Mana); + Config.UseRejuvMP > 0 && me.mpPercent < Config.UseRejuvMP && this.drinkPotion(Common.Toolsthread.pots.Rejuv); + + (me.staminaPercent <= 20 || me.walking) && this.drinkSpecialPotion(sdk.items.StaminaPotion); + me.getState(sdk.states.Poison) && this.drinkSpecialPotion(sdk.items.AntidotePotion); + [sdk.states.Frozen, sdk.states.FrozenSolid].some(state => me.getState(state)) && this.drinkSpecialPotion(sdk.items.ThawingPotion); + + if (Config.ManaChicken > 0 && me.mpPercent <= Config.ManaChicken && !me.inTown) { + !Developer.hideChickens && D2Bot.printToConsole("Mana Chicken: (" + me.mp + "/" + me.mpmax + ") in " + getAreaName(me.area), sdk.colors.D2Bot.Red); + this.exit(true); + + return true; + } + + if (Config.IronGolemChicken > 0 && me.necromancer) { + if (!ironGolem || copyUnit(ironGolem).x === undefined) { + ironGolem = Common.Toolsthread.getIronGolem(); + } + + if (ironGolem) { + // ironGolem.hpmax is bugged with BO + if (ironGolem.hp <= Math.floor(128 * Config.IronGolemChicken / 100)) { + !Developer.hideChickens && D2Bot.printToConsole("Irom Golem Chicken in " + getAreaName(me.area), sdk.colors.D2Bot.Red); + this.exit(true); + + return true; + } + } + } + + if (Config.UseMerc) { + let merc = me.getMerc(); + if (!!merc) { + let mercHP = getMercHP(); + + if (mercHP > 0 && merc.mode !== sdk.monsters.mode.Dead) { + if (mercHP < Config.MercChicken) { + !Developer.hideChickens && D2Bot.printToConsole("Merc Chicken in " + getAreaName(me.area), sdk.colors.D2Bot.Red); + this.exit(true); + + return true; + } + + mercHP < Config.UseMercHP && this.drinkPotion(Common.Toolsthread.pots.MercHealth); + mercHP < Config.UseMercRejuv && this.drinkPotion(Common.Toolsthread.pots.MercRejuv); + } + } + } + + if (Config.ViperCheck && getTickCount() - tick >= 250) { + if (Common.Toolsthread.checkVipers()) { + D2Bot.printToConsole("Revived Tomb Vipers found. Leaving game.", sdk.colors.D2Bot.Red); + quitFlag = true; + } + + tick = getTickCount(); + } + + Common.Toolsthread.checkPing(true) && (quitFlag = true); + } + } catch (e) { + Misc.errorReport(e, "ToolsThread"); + + quitFlag = true; + } + + if (me.maxgametime - (getTickCount() - me.gamestarttime) < 10e3) { + console.log("Max game time reached"); + quitFlag = true; + } + + if (quitFlag && canQuit && (typeof quitListDelayTime === "undefined" || getTickCount() >= quitListDelayTime)) { + Common.Toolsthread.checkPing(false); // In case of quitlist triggering first + this.exit(); + + break; + } + + !!restart && this.restart(); + + if (debugInfo.area !== getAreaName(me.area)) { + debugInfo.area = getAreaName(me.area); + DataFile.updateStats("debugInfo", JSON.stringify(debugInfo)); + } + + delay(20); + } + + return true; } diff --git a/libs/SoloPlay/Tools/CharData.js b/libs/SoloPlay/Tools/CharData.js index adcb6ee5..e310975c 100644 --- a/libs/SoloPlay/Tools/CharData.js +++ b/libs/SoloPlay/Tools/CharData.js @@ -8,319 +8,319 @@ includeIfNotIncluded("SoloPlay/Tools/Tracker.js"); const CharData = (function () { - const _create = function () { - let obj = Object.assign({}, this._default); - let string = JSON.stringify(obj, null, 2); - - if (!FileTools.exists("libs/SoloPlay/Data/" + me.profile)) { - let folder = dopen("libs/SoloPlay/Data"); - folder && folder.create(me.profile); - } - - FileAction.write(this.filePath, string); - - return obj; - }; - - const _getObj = function () { - if (!FileTools.exists(this.filePath)) return this.create(); - - let obj; - let string = FileAction.read(this.filePath); - - try { - obj = JSON.parse(string); - } catch (e) { - // If we failed, file might be corrupted, so create a new one - obj = this.create(); - } - - return obj ? obj : this._default; - }; - - const _getStats = function () { - let obj = this.getObj(); - return clone(obj); - }; - - const _updateData = function (arg, property, value) { - let obj = this.getObj(); - typeof arg !== "string" && (arg = arg.toString()); - typeof arg === "string" && (arg = arg.toLowerCase()); - - if (typeof property === "object") { - obj = Object.assign(obj, property); - return FileAction.write(this.filePath, JSON.stringify(obj, null, 2)); - } - - if (obj.hasOwnProperty(property)) { - obj[property] = value; - return FileAction.write(this.filePath, JSON.stringify(obj, null, 2)); - } else if (obj.hasOwnProperty(arg) && obj[arg].hasOwnProperty(property)) { - obj[arg][property] = value; - return FileAction.write(this.filePath, JSON.stringify(obj, null, 2)); - } - - return false; - }; - - return { - filePath: "libs/SoloPlay/Data/" + me.profile + "/" + me.profile + "-CharData.json", - threads: [ - "libs/SoloPlay/SoloPlay.js", "libs/SoloPlay/Threads/TownChicken.js", - "libs/SoloPlay/Threads/ToolsThread.js", "libs/SoloPlay/Threads/EventThread.js" - ], - _default: (function () { - let diffObj = { respecUsed: false, imbueUsed: false, socketUsed: false }; - return { - initialized: false, - normal: Object.assign({}, diffObj), - nightmare: Object.assign({}, diffObj), - hell: Object.assign({}, diffObj), - task: "", - startTime: 0, - charName: "", - classid: -1, - level: 1, - strength: 0, - dexterity: 0, - currentBuild: "Start", - finalBuild: "", - highestDifficulty: "Normal", - setDifficulty: "Normal", - charms: {}, - charmGids: [], - merc: { - act: 1, - classid: sdk.mercs.Rogue, - difficulty: sdk.difficulty.Normal, - strength: 0, - dexterity: 0, - skill: 0, - skillName: "", - gear: [], - } - }; - })(), - - login: (function () { - return { - filePath: "libs/SoloPlay/Data/" + me.profile + "/" + me.profile + "-LoginData.json", - _default: { account: "", pass: "", currentChar: "", tag: "", charCount: 0, existing: false }, - - create: function () { - return _create.call(this); - }, - getObj: function () { - return _getObj.call(this); - }, - - getStats: function () { - return _getStats.call(this); - }, - - updateData: function (arg, property, value) { - return _updateData.call(this, arg, property, value); - }, - }; - })(), - - charms: (function () { - /** - * @constructor - * @param {number} classid - */ - function Charm (classid) { - this.classid = classid; - } - - Charm.prototype.count = function () { - let [curr, max] = [0, 0]; - Object.keys(me.data.charms).forEach(cKey => { - if (me.data.charms[cKey].classid === this.classid) { - curr += me.data.charms[cKey].have.length; - max += me.data.charms[cKey].max; - } - }); - - return { - curr: curr, - max: max - }; - }; - /** @type {Map true); - this.tick = 0; - this.duration = 0; - } - - BuffPot.prototype.active = function () { - return me.getState(this.state); - }; - - BuffPot.prototype.timeLeft = function () { - return this.duration > 0 ? this.duration - (getTickCount() - this.tick) : 0; - }; - - BuffPot.prototype.need = function () { - return (this.check() && (!this.active() || this.timeLeft() < Time.minutes(5))); - }; - - /** @type {Map} */ - const _buffPots = new Map(); - _buffPots.set(sdk.items.StaminaPotion, new BuffPot(sdk.states.StaminaPot, - () => Skill.canUse(sdk.skills.Vigor) || Pather.canTeleport()) - ); - _buffPots.set(sdk.items.ThawingPotion, new BuffPot(sdk.states.Thawing, () => me.coldRes < 75)); - _buffPots.set(sdk.items.AntidotePotion, new BuffPot(sdk.states.Antidote, () => me.poisonRes < 75)); - - // hacky for now - just to handle the old way of accessing buff pots - _buffPots.set("stamina", _buffPots.get(sdk.items.StaminaPotion)); - _buffPots.set("thawing", _buffPots.get(sdk.items.ThawingPotion)); - _buffPots.set("antidote", _buffPots.get(sdk.items.AntidotePotion)); - - return _buffPots; - }()), - - skillData: { - skills: [], - currentChargedSkills: [], - chargedSkills: [], - chargedSkillsOnSwitch: [], - /** - * @todo fix this, it's ugly - */ - bow: { - initialized: false, - onSwitch: false, - bowGid: 0, - bowType: 0, - arrows: 0, - quiverType: 0, - setBowInfo: function (bow, init = false) { - if (bow === undefined) return; - this.bowGid = bow.gid; - this.bowType = bow.itemType; - this.bowOnSwitch = bow.isOnSwap; - SetUp.bowQuiver(); - init && (this.initialized = true); - !init && CharData.skillData.update(); - }, - setArrowInfo: function (quiver) { - if (quiver === undefined) return; - this.arrows = Math.floor((quiver.getStat(sdk.stats.Quantity) * 100) / getBaseStat("items", quiver.classid, "maxstack")); - this.quiverType = quiver.itemType; - }, - resetBowData: function () { - this.bowOnSwitch = false; - [this.bowGid, this.bowType, this.arrows, this.quiverType] = [0, 0, 0, 0]; - NTIP.resetRuntimeList(); - CharData.skillData.update(); - }, - }, - - init: function (skillIds, mainSkills, switchSkills) { - this.currentChargedSkills = skillIds.slice(0); - this.chargedSkills = mainSkills.slice(0); - this.chargedSkillsOnSwitch = switchSkills.slice(0); - this.skills = me.getSkill(4).map((skill) => skill[0]); - }, - - update: function () { - let obj = JSON.stringify(copyObj(this)); - let myThread = getScript(true).name; - CharData.threads.forEach(function (script) { - let curr = getScript(script); - if (curr && myThread !== curr.name) { - curr.send("skill--" + obj); - } - }); - }, - - haveChargedSkill: function (skillid = []) { - // convert to array if not one - !Array.isArray(skillid) && (skillid = [skillid]); - return this.currentChargedSkills.some(s => skillid.includes(s)); - }, - - haveChargedSkillOnSwitch: function (skillid = 0) { - return this.chargedSkillsOnSwitch.some(chargeSkill => chargeSkill.skill === skillid); - } - }, - - // updates config obj across all threads - excluding our current - updateConfig: function () { - let obj = JSON.stringify(copyObj(Config)); - let myThread = getScript(true).name; - CharData.threads.forEach(function (script) { - let curr = getScript(script); - if (curr && myThread !== curr.name) { - curr.send("config--" + obj); - } - }); - }, - - /** - * @returns {MyData} - */ - create: function () { - return _create.call(this); - }, - - /** - * @returns {MyData} - */ - getObj: function () { - return _getObj.call(this); - }, - - /** - * @returns {MyData} - */ - getStats: function () { - return _getStats.call(this); - }, - - updateData: function (arg, property, value) { - while (me.ingame && !me.gameReady) { - delay(100); - } - - console.trace(); - - return _updateData.call(this, arg, property, value); - }, - - delete: function (deleteMain = false) { - if (deleteMain && FileTools.exists("data/" + me.profile + ".json")) { - FileTools.remove("data/" + me.profile + ".json"); - } - - FileTools.exists(this.filePath) && FileTools.remove(this.filePath); - FileTools.exists(Tracker.GTPath) && FileTools.remove(Tracker.GTPath); - - return !(FileTools.exists(this.filePath) && FileTools.exists("libs/SoloPlay/Data/" + me.profile + ".GameTime" + ".json")); - }, - }; + const _create = function () { + let obj = Object.assign({}, this._default); + let string = JSON.stringify(obj, null, 2); + + if (!FileTools.exists("libs/SoloPlay/Data/" + me.profile)) { + let folder = dopen("libs/SoloPlay/Data"); + folder && folder.create(me.profile); + } + + FileAction.write(this.filePath, string); + + return obj; + }; + + const _getObj = function () { + if (!FileTools.exists(this.filePath)) return this.create(); + + let obj; + let string = FileAction.read(this.filePath); + + try { + obj = JSON.parse(string); + } catch (e) { + // If we failed, file might be corrupted, so create a new one + obj = this.create(); + } + + return obj ? obj : this._default; + }; + + const _getStats = function () { + let obj = this.getObj(); + return clone(obj); + }; + + const _updateData = function (arg, property, value) { + let obj = this.getObj(); + typeof arg !== "string" && (arg = arg.toString()); + typeof arg === "string" && (arg = arg.toLowerCase()); + + if (typeof property === "object") { + obj = Object.assign(obj, property); + return FileAction.write(this.filePath, JSON.stringify(obj, null, 2)); + } + + if (obj.hasOwnProperty(property)) { + obj[property] = value; + return FileAction.write(this.filePath, JSON.stringify(obj, null, 2)); + } else if (obj.hasOwnProperty(arg) && obj[arg].hasOwnProperty(property)) { + obj[arg][property] = value; + return FileAction.write(this.filePath, JSON.stringify(obj, null, 2)); + } + + return false; + }; + + return { + filePath: "libs/SoloPlay/Data/" + me.profile + "/" + me.profile + "-CharData.json", + threads: [ + "libs/SoloPlay/SoloPlay.js", "libs/SoloPlay/Threads/TownChicken.js", + "libs/SoloPlay/Threads/ToolsThread.js", "libs/SoloPlay/Threads/EventThread.js" + ], + _default: (function () { + let diffObj = { respecUsed: false, imbueUsed: false, socketUsed: false }; + return { + initialized: false, + normal: Object.assign({}, diffObj), + nightmare: Object.assign({}, diffObj), + hell: Object.assign({}, diffObj), + task: "", + startTime: 0, + charName: "", + classid: -1, + level: 1, + strength: 0, + dexterity: 0, + currentBuild: "Start", + finalBuild: "", + highestDifficulty: "Normal", + setDifficulty: "Normal", + charms: {}, + charmGids: [], + merc: { + act: 1, + classid: sdk.mercs.Rogue, + difficulty: sdk.difficulty.Normal, + strength: 0, + dexterity: 0, + skill: 0, + skillName: "", + gear: [], + } + }; + })(), + + login: (function () { + return { + filePath: "libs/SoloPlay/Data/" + me.profile + "/" + me.profile + "-LoginData.json", + _default: { account: "", pass: "", currentChar: "", tag: "", charCount: 0, existing: false }, + + create: function () { + return _create.call(this); + }, + getObj: function () { + return _getObj.call(this); + }, + + getStats: function () { + return _getStats.call(this); + }, + + updateData: function (arg, property, value) { + return _updateData.call(this, arg, property, value); + }, + }; + })(), + + charms: (function () { + /** + * @constructor + * @param {number} classid + */ + function Charm (classid) { + this.classid = classid; + } + + Charm.prototype.count = function () { + let [curr, max] = [0, 0]; + Object.keys(me.data.charms).forEach(cKey => { + if (me.data.charms[cKey].classid === this.classid) { + curr += me.data.charms[cKey].have.length; + max += me.data.charms[cKey].max; + } + }); + + return { + curr: curr, + max: max + }; + }; + /** @type {Map true); + this.tick = 0; + this.duration = 0; + } + + BuffPot.prototype.active = function () { + return me.getState(this.state); + }; + + BuffPot.prototype.timeLeft = function () { + return this.duration > 0 ? this.duration - (getTickCount() - this.tick) : 0; + }; + + BuffPot.prototype.need = function () { + return (this.check() && (!this.active() || this.timeLeft() < Time.minutes(5))); + }; + + /** @type {Map} */ + const _buffPots = new Map(); + _buffPots.set(sdk.items.StaminaPotion, new BuffPot(sdk.states.StaminaPot, + () => Skill.canUse(sdk.skills.Vigor) || Pather.canTeleport()) + ); + _buffPots.set(sdk.items.ThawingPotion, new BuffPot(sdk.states.Thawing, () => me.coldRes < 75)); + _buffPots.set(sdk.items.AntidotePotion, new BuffPot(sdk.states.Antidote, () => me.poisonRes < 75)); + + // hacky for now - just to handle the old way of accessing buff pots + _buffPots.set("stamina", _buffPots.get(sdk.items.StaminaPotion)); + _buffPots.set("thawing", _buffPots.get(sdk.items.ThawingPotion)); + _buffPots.set("antidote", _buffPots.get(sdk.items.AntidotePotion)); + + return _buffPots; + }()), + + skillData: { + skills: [], + currentChargedSkills: [], + chargedSkills: [], + chargedSkillsOnSwitch: [], + /** + * @todo fix this, it's ugly + */ + bow: { + initialized: false, + onSwitch: false, + bowGid: 0, + bowType: 0, + arrows: 0, + quiverType: 0, + setBowInfo: function (bow, init = false) { + if (bow === undefined) return; + this.bowGid = bow.gid; + this.bowType = bow.itemType; + this.bowOnSwitch = bow.isOnSwap; + SetUp.bowQuiver(); + init && (this.initialized = true); + !init && CharData.skillData.update(); + }, + setArrowInfo: function (quiver) { + if (quiver === undefined) return; + this.arrows = Math.floor((quiver.getStat(sdk.stats.Quantity) * 100) / getBaseStat("items", quiver.classid, "maxstack")); + this.quiverType = quiver.itemType; + }, + resetBowData: function () { + this.bowOnSwitch = false; + [this.bowGid, this.bowType, this.arrows, this.quiverType] = [0, 0, 0, 0]; + NTIP.resetRuntimeList(); + CharData.skillData.update(); + }, + }, + + init: function (skillIds, mainSkills, switchSkills) { + this.currentChargedSkills = skillIds.slice(0); + this.chargedSkills = mainSkills.slice(0); + this.chargedSkillsOnSwitch = switchSkills.slice(0); + this.skills = me.getSkill(4).map((skill) => skill[0]); + }, + + update: function () { + let obj = JSON.stringify(copyObj(this)); + let myThread = getScript(true).name; + CharData.threads.forEach(function (script) { + let curr = getScript(script); + if (curr && myThread !== curr.name) { + curr.send("skill--" + obj); + } + }); + }, + + haveChargedSkill: function (skillid = []) { + // convert to array if not one + !Array.isArray(skillid) && (skillid = [skillid]); + return this.currentChargedSkills.some(s => skillid.includes(s)); + }, + + haveChargedSkillOnSwitch: function (skillid = 0) { + return this.chargedSkillsOnSwitch.some(chargeSkill => chargeSkill.skill === skillid); + } + }, + + // updates config obj across all threads - excluding our current + updateConfig: function () { + let obj = JSON.stringify(copyObj(Config)); + let myThread = getScript(true).name; + CharData.threads.forEach(function (script) { + let curr = getScript(script); + if (curr && myThread !== curr.name) { + curr.send("config--" + obj); + } + }); + }, + + /** + * @returns {MyData} + */ + create: function () { + return _create.call(this); + }, + + /** + * @returns {MyData} + */ + getObj: function () { + return _getObj.call(this); + }, + + /** + * @returns {MyData} + */ + getStats: function () { + return _getStats.call(this); + }, + + updateData: function (arg, property, value) { + while (me.ingame && !me.gameReady) { + delay(100); + } + + console.trace(); + + return _updateData.call(this, arg, property, value); + }, + + delete: function (deleteMain = false) { + if (deleteMain && FileTools.exists("data/" + me.profile + ".json")) { + FileTools.remove("data/" + me.profile + ".json"); + } + + FileTools.exists(this.filePath) && FileTools.remove(this.filePath); + FileTools.exists(Tracker.GTPath) && FileTools.remove(Tracker.GTPath); + + return !(FileTools.exists(this.filePath) && FileTools.exists("libs/SoloPlay/Data/" + me.profile + ".GameTime" + ".json")); + }, + }; })(); diff --git a/libs/SoloPlay/Tools/Developer.js b/libs/SoloPlay/Tools/Developer.js index 6dddfc0a..16180e93 100644 --- a/libs/SoloPlay/Tools/Developer.js +++ b/libs/SoloPlay/Tools/Developer.js @@ -11,85 +11,85 @@ * - add name choices in similar manner, would have to experiment with max lengths allowed as a prefix */ const Developer = { - // @desc - set to true if using the PlugY mod - allows use of larger stash - plugyMode: false, - // @desc - log game/bot statistics to .csv files located at SoloPlay/Data/ - logPerformance: true, - // @desc - show in game overlay (see bottom of README.md for example) - overlay: true, - // @desc - show Total, InGame, and OOG (out of game) time in the D2bot# status window - displayClockInConsole: false, - // @desc - log currently equipped items to D2Bot# charviewer tab - logEquipped: false, - // @desc - disable printing chicken info in D2Bot console - hideChickens: true, - // @desc - enable ladder runewords in single player mode ONLY WORKS IF RUNEWORDS.TXT IS INSTALLED AND D2BS PROFILE IS CONFIGURED - // or patch.json has been updated (see Single Player Additions in README.md) - addLadderRW: !me.profile.toLowerCase().contains("nl"), - // @desc - hide casting animations for better stability (reduce d2bs crashes) - forcePacketCasting: { - enabled: true, - // @desc - allow specific profiles to show casting animations without disabling it for every profile running (helpful when debugging) - excludeProfiles: [""], - }, - // @desc - set to true in use with tag Bumper, Socketmule, or Imbuemule to make next character after reaching goal until account is full - fillAccount: { - bumpers: false, - socketMules: false, - imbueMule: false, - }, - // @desc - set level for imbueMule to stop at - imbueStopLevel: 30, - // @desc - stop a profile once it reaches a certain level - stopAtLevel: { - enabled: false, - profiles: [ - // ["scl-example-001", 60], - // ["hcl-example-001", 40] - ], - }, - // @desc - allows a profile to loaded without starting any of the scripts. enables chat commands for testing. See Scripts/developermode.js for more info. - developerMode: { - enabled: false, - // Enter in the profiles that you wish to start in developermode, i.e "scl-sorc" - profiles: [""], - }, - testingMode: { - enabled: false, - // Enter in the profiles that you wish to start in testing mode, i.e "scl-sorc" - profiles: [""], - }, - // @desc [experimental don't use] - set email during account creation - setEmail: { - enabled: false, - //email: "", - //domain: "", - profiles: [], - realms: ["asia"] - }, - // @desc - enable/disable logging debug info to the console - debugging: { - smallCharm: false, - largeCharm: false, - grandCharm: false, - baseCheck: false, - junkCheck: false, - autoEquip: false, - crafting: false, - pathing: false, - skills: false, - showStack: { - enabled: false, - // @desc - Enter in the profiles that you wish to see the stack walk for, this loads up guard.js and displays on the overlay - profiles: [""], - }, - }, + // @desc - set to true if using the PlugY mod - allows use of larger stash + plugyMode: false, + // @desc - log game/bot statistics to .csv files located at SoloPlay/Data/ + logPerformance: true, + // @desc - show in game overlay (see bottom of README.md for example) + overlay: true, + // @desc - show Total, InGame, and OOG (out of game) time in the D2bot# status window + displayClockInConsole: false, + // @desc - log currently equipped items to D2Bot# charviewer tab + logEquipped: false, + // @desc - disable printing chicken info in D2Bot console + hideChickens: true, + // @desc - enable ladder runewords in single player mode ONLY WORKS IF RUNEWORDS.TXT IS INSTALLED AND D2BS PROFILE IS CONFIGURED + // or patch.json has been updated (see Single Player Additions in README.md) + addLadderRW: !me.profile.toLowerCase().contains("nl"), + // @desc - hide casting animations for better stability (reduce d2bs crashes) + forcePacketCasting: { + enabled: true, + // @desc - allow specific profiles to show casting animations without disabling it for every profile running (helpful when debugging) + excludeProfiles: [""], + }, + // @desc - set to true in use with tag Bumper, Socketmule, or Imbuemule to make next character after reaching goal until account is full + fillAccount: { + bumpers: false, + socketMules: false, + imbueMule: false, + }, + // @desc - set level for imbueMule to stop at + imbueStopLevel: 30, + // @desc - stop a profile once it reaches a certain level + stopAtLevel: { + enabled: false, + profiles: [ + // ["scl-example-001", 60], + // ["hcl-example-001", 40] + ], + }, + // @desc - allows a profile to loaded without starting any of the scripts. enables chat commands for testing. See Scripts/developermode.js for more info. + developerMode: { + enabled: false, + // Enter in the profiles that you wish to start in developermode, i.e "scl-sorc" + profiles: [""], + }, + testingMode: { + enabled: false, + // Enter in the profiles that you wish to start in testing mode, i.e "scl-sorc" + profiles: [""], + }, + // @desc [experimental don't use] - set email during account creation + setEmail: { + enabled: false, + //email: "", + //domain: "", + profiles: [], + realms: ["asia"] + }, + // @desc - enable/disable logging debug info to the console + debugging: { + smallCharm: false, + largeCharm: false, + grandCharm: false, + baseCheck: false, + junkCheck: false, + autoEquip: false, + crafting: false, + pathing: false, + skills: false, + showStack: { + enabled: false, + // @desc - Enter in the profiles that you wish to see the stack walk for, this loads up guard.js and displays on the overlay + profiles: [""], + }, + }, }; // Set after Developer has been initialized - always load guard in developer mode if (Developer.developerMode.enabled && Developer.developerMode.profiles.some(profile => profile.toLowerCase() === me.profile.toLowerCase())) { - Developer.debugging.pathing = true; - //Developer.debugging.skills = true; - Developer.debugging.showStack.enabled = true; - Developer.debugging.showStack.profiles.push(me.profile.toLowerCase()); + Developer.debugging.pathing = true; + //Developer.debugging.skills = true; + Developer.debugging.showStack.enabled = true; + Developer.debugging.showStack.profiles.push(me.profile.toLowerCase()); } diff --git a/libs/SoloPlay/Tools/NameGen.js b/libs/SoloPlay/Tools/NameGen.js index 0f1e03e2..81f433f9 100644 --- a/libs/SoloPlay/Tools/NameGen.js +++ b/libs/SoloPlay/Tools/NameGen.js @@ -6,247 +6,247 @@ */ (function (module) { - const adjectives = [ - "Ancient", "Angry", "Artful", "Able", "Abundant", "Accepting", "Acclaimed", "Active", "Addictive", "Adept", "Adequate", "Admired", "Adorable", - "Adored", "Agile", "Amazing", "Amiable", "Amicable", "Amusing", "Anxious", "Anxious", "Apathetic", "Aquatic", "Arrogant", "Artistic", - "Attentive", "Awesome", "Azure", "Barren", "Bitter", "Black", "Blue", "Blasted", "Bold", "Bonding", "Boorish", "Bountiful", "Braggart", - "Brave", "Bright", "Brilliant", "Broken", "Burning", "Busy", "Buzzing", "Callous", "Captious", "Caring", "Cautious", "Celestial", - "Changing", "Charming", "Chaste", "Cheating", "Cheerful", "Churlish", "Civil", "Clean", "Clever", "Coastal", "Cold", "Colossal", - "Composed", "Concerned", "Concrete", "Complex", "Cheap", "Compact", "Confident", "Congenial", "Cordial", "Courteous", "Covetous", "Crazy", - "Crazed", "Creative", "Crimson", "Critical", "Crossing", "Crucial", "Crude", "Crushing", "Culpable", "Curious", "Current", "Curt", "Cynical", - "Dancing", "Dark", "Decent", "Decorous", "Defensive", "Deft", "Dejected", "Delirious", "Demanding", "Demeaning", "Demise", "Depressed", - "Devious", "Devoted", "Diligent", "Discreet", "Diving", "Dishonest", "Docile", "Downcast", "Doubting", "Drunken", "Dry", "Dull", "Dutiful", - "Dynamic", "Eager", "Earnest", "Earthy", "East", "Efficient", "Elegant", "Elitist", "Emerald", "Endemic", "Energetic", "Enigmatic", "Esteemed", - "Estimable", "Ethical", "Euphoric", "Evergreen", "Exclusive", "Expectant", "Explosive", "Exquisite", "Exuberant", "Endless", "Fair", "Faithful", - "False", "Famous", "Fancy", "Fat", "Fatal", "Festive", "Feral", "Ferocious", "Fertile", "Fervent", "Funky", "Fibrous", "Fierce", "Firm", - "Flawless", "Flexible", "Flowing", "Focused", "Forgiving", "Forlorn", "Frail", "Fierce", "Flustered", "Flying", "Foolish", "Friendly", - "Generous", "Genial", "Genteel", "Gentle", "Genuine", "Gifted", "Gigantic", "Glib", "Gloomy", "Golden", "Good", "Gorgeous", "Graceful", - "Gracious", "Grand", "Grateful", "Gravity", "Green", "Grouchy", "Guilty", "Guilty", "Gusty", "Grim", "Green", "Greedy", "Handsome", "Handy", - "Hard", "Happy", "Haunting", "Healing", "Headless", "Heavenly", "Heroic", "Hidden", "High", "Honest", "Honorable", "Hopeful", "Hostile", - "Humane", "Humble", "Humorous", "Hungry", "Hygienic", "Idolize", "Ignoble", "Ignorant", "Impartial", "Impolite", "Improper", "Imprudent", - "Impudent", "Indecent", "Infinite", "Ingenuous", "Innocent", "Insolent", "Insulting", "Intense", "Introvert", "Intuitive", "Inventive", - "Irascible", "Intrepid", "Jade", "Janky", "Jaundiced", "Jealous", "Jealous", "Jocular", "Jolly", "Jovial", "Juicy", "Joyful", "Jubilant", - "Just", "Juvenile", "Kingly", "Keen", "Kind", "Kindred", "Kooky", "Liberal", "Listening", "Loathsome", "Loving", "LOYAL", "Limp", "Lord", - "Loud", "Light", "Little", "Lanky", "Lazy", "Long", "Lucky", "Last", "Leaping", "Lone", "Lonely", "Lost", "Magical", "Majestic", "Malicious", - "Mammoth", "Marine", "Masterful", "Meddling", "Migratory", "Minuscule", "Miserable", "Misty", "Modest", "Moral", "Mediocre", "Mellow", "Mute", - "Miserable", "Naive", "Nascent", "Native", "Natural", "Natures", "Needy", "Nefarious", "Negative", "Neglected", "Negligent", "Nice", "Noble", - "Northern", "Notorious", "Obedient", "Observant", "Open", "Orderly", "Original", "Outspoken", "Organic", "Ornate", "Ordinary", "Orange", - "Parasitic", "Partial", "Patient", "Personal", "Petulant", "Pleasant", "Poise", "Polite", "Pollutant", "Popular", "Pouncing", "Powerful", - "Prideful", "Primal", "Prime", "Pristine", "Prompt", "Proper", "Punctual", "Pure", "Purple", "Putrid", "Practical", "Precious", "Puzzled", - "Quaint", "Quick", "Quiet", "Quirky", "Radiant", "Raging", "Rancorous", "Regular", "Red", "Rancid", "Rough", "Rational", "Reckless", "Refined", - "Regal", "Renewable", "Repugnant", "Resilient", "Resolute", "Reverent", "Rotting", "Ruby", "Rude", "Ruthless", "Sad", "Safe", "Savage", - "Scorching", "Scornful", "Secret", "Selfish", "Sensible", "Sensitive", "Sharing", "Silver", "Simple", "Sober", "Solar", "Solemn", "Solitary", - "Southern", "Sour", "Spatial", "Special", "Splendid", "Staunch", "Singing", "Stern", "Stunning", "Subtle", "Sullen", "Superb", "Superior", - "Surly", "Sweet", "Strong", "Smart", "Short", "Skinny", "Stupid", "Salty", "Soft", "Smooth", "Sharp", "Sneaky", "Stinky", "Tactful", "Tainted", - "Temperate", "Temperate", "Tenacious", "Terrible", "Terrific", "Testy", "Tolerant", "Towering", "Toxic", "Tropical", "True", "Truthful", "Tasty", - "Tricky", "Ultimate", "Ultimate", "Uncivil", "Uncouth", "Unethical", "Unfair", "Unique", "United", "Unfit", "Unrefined", "Unsavory", "Unworthy", - "Uplifting", "Upright", "Uprooted", "Valiant", "Veracious", "Versatile", "Vicious", "Vigilant", "Vigilant", "Vigorous", "Vile", "Virtuous", - "Visible", "Vivacious", "Vocal", "Volatile", "Violent", "Violet", "Void", "Weak", "West", "White", "Willful", "Wet", "Warm", "Wary", "Watchful", - "Weeping", "Wicked", "Wild", "Willing", "Winning", "Winsome", "Wise", "Wistful", "Witty", "Woeful", "Wonderful", "Worldwide", "Wretched", - "Worthy", "Yellow", "Yearning", "Yielding", "Yielding", "Yourself", "Youthful", "Zany", "Zealot", "Zealous", "Zealous", "Zero", - ]; + const adjectives = [ + "Ancient", "Angry", "Artful", "Able", "Abundant", "Accepting", "Acclaimed", "Active", "Addictive", "Adept", "Adequate", "Admired", "Adorable", + "Adored", "Agile", "Amazing", "Amiable", "Amicable", "Amusing", "Anxious", "Anxious", "Apathetic", "Aquatic", "Arrogant", "Artistic", + "Attentive", "Awesome", "Azure", "Barren", "Bitter", "Black", "Blue", "Blasted", "Bold", "Bonding", "Boorish", "Bountiful", "Braggart", + "Brave", "Bright", "Brilliant", "Broken", "Burning", "Busy", "Buzzing", "Callous", "Captious", "Caring", "Cautious", "Celestial", + "Changing", "Charming", "Chaste", "Cheating", "Cheerful", "Churlish", "Civil", "Clean", "Clever", "Coastal", "Cold", "Colossal", + "Composed", "Concerned", "Concrete", "Complex", "Cheap", "Compact", "Confident", "Congenial", "Cordial", "Courteous", "Covetous", "Crazy", + "Crazed", "Creative", "Crimson", "Critical", "Crossing", "Crucial", "Crude", "Crushing", "Culpable", "Curious", "Current", "Curt", "Cynical", + "Dancing", "Dark", "Decent", "Decorous", "Defensive", "Deft", "Dejected", "Delirious", "Demanding", "Demeaning", "Demise", "Depressed", + "Devious", "Devoted", "Diligent", "Discreet", "Diving", "Dishonest", "Docile", "Downcast", "Doubting", "Drunken", "Dry", "Dull", "Dutiful", + "Dynamic", "Eager", "Earnest", "Earthy", "East", "Efficient", "Elegant", "Elitist", "Emerald", "Endemic", "Energetic", "Enigmatic", "Esteemed", + "Estimable", "Ethical", "Euphoric", "Evergreen", "Exclusive", "Expectant", "Explosive", "Exquisite", "Exuberant", "Endless", "Fair", "Faithful", + "False", "Famous", "Fancy", "Fat", "Fatal", "Festive", "Feral", "Ferocious", "Fertile", "Fervent", "Funky", "Fibrous", "Fierce", "Firm", + "Flawless", "Flexible", "Flowing", "Focused", "Forgiving", "Forlorn", "Frail", "Fierce", "Flustered", "Flying", "Foolish", "Friendly", + "Generous", "Genial", "Genteel", "Gentle", "Genuine", "Gifted", "Gigantic", "Glib", "Gloomy", "Golden", "Good", "Gorgeous", "Graceful", + "Gracious", "Grand", "Grateful", "Gravity", "Green", "Grouchy", "Guilty", "Guilty", "Gusty", "Grim", "Green", "Greedy", "Handsome", "Handy", + "Hard", "Happy", "Haunting", "Healing", "Headless", "Heavenly", "Heroic", "Hidden", "High", "Honest", "Honorable", "Hopeful", "Hostile", + "Humane", "Humble", "Humorous", "Hungry", "Hygienic", "Idolize", "Ignoble", "Ignorant", "Impartial", "Impolite", "Improper", "Imprudent", + "Impudent", "Indecent", "Infinite", "Ingenuous", "Innocent", "Insolent", "Insulting", "Intense", "Introvert", "Intuitive", "Inventive", + "Irascible", "Intrepid", "Jade", "Janky", "Jaundiced", "Jealous", "Jealous", "Jocular", "Jolly", "Jovial", "Juicy", "Joyful", "Jubilant", + "Just", "Juvenile", "Kingly", "Keen", "Kind", "Kindred", "Kooky", "Liberal", "Listening", "Loathsome", "Loving", "LOYAL", "Limp", "Lord", + "Loud", "Light", "Little", "Lanky", "Lazy", "Long", "Lucky", "Last", "Leaping", "Lone", "Lonely", "Lost", "Magical", "Majestic", "Malicious", + "Mammoth", "Marine", "Masterful", "Meddling", "Migratory", "Minuscule", "Miserable", "Misty", "Modest", "Moral", "Mediocre", "Mellow", "Mute", + "Miserable", "Naive", "Nascent", "Native", "Natural", "Natures", "Needy", "Nefarious", "Negative", "Neglected", "Negligent", "Nice", "Noble", + "Northern", "Notorious", "Obedient", "Observant", "Open", "Orderly", "Original", "Outspoken", "Organic", "Ornate", "Ordinary", "Orange", + "Parasitic", "Partial", "Patient", "Personal", "Petulant", "Pleasant", "Poise", "Polite", "Pollutant", "Popular", "Pouncing", "Powerful", + "Prideful", "Primal", "Prime", "Pristine", "Prompt", "Proper", "Punctual", "Pure", "Purple", "Putrid", "Practical", "Precious", "Puzzled", + "Quaint", "Quick", "Quiet", "Quirky", "Radiant", "Raging", "Rancorous", "Regular", "Red", "Rancid", "Rough", "Rational", "Reckless", "Refined", + "Regal", "Renewable", "Repugnant", "Resilient", "Resolute", "Reverent", "Rotting", "Ruby", "Rude", "Ruthless", "Sad", "Safe", "Savage", + "Scorching", "Scornful", "Secret", "Selfish", "Sensible", "Sensitive", "Sharing", "Silver", "Simple", "Sober", "Solar", "Solemn", "Solitary", + "Southern", "Sour", "Spatial", "Special", "Splendid", "Staunch", "Singing", "Stern", "Stunning", "Subtle", "Sullen", "Superb", "Superior", + "Surly", "Sweet", "Strong", "Smart", "Short", "Skinny", "Stupid", "Salty", "Soft", "Smooth", "Sharp", "Sneaky", "Stinky", "Tactful", "Tainted", + "Temperate", "Temperate", "Tenacious", "Terrible", "Terrific", "Testy", "Tolerant", "Towering", "Toxic", "Tropical", "True", "Truthful", "Tasty", + "Tricky", "Ultimate", "Ultimate", "Uncivil", "Uncouth", "Unethical", "Unfair", "Unique", "United", "Unfit", "Unrefined", "Unsavory", "Unworthy", + "Uplifting", "Upright", "Uprooted", "Valiant", "Veracious", "Versatile", "Vicious", "Vigilant", "Vigilant", "Vigorous", "Vile", "Virtuous", + "Visible", "Vivacious", "Vocal", "Volatile", "Violent", "Violet", "Void", "Weak", "West", "White", "Willful", "Wet", "Warm", "Wary", "Watchful", + "Weeping", "Wicked", "Wild", "Willing", "Winning", "Winsome", "Wise", "Wistful", "Witty", "Woeful", "Wonderful", "Worldwide", "Wretched", + "Worthy", "Yellow", "Yearning", "Yielding", "Yielding", "Yourself", "Youthful", "Zany", "Zealot", "Zealous", "Zealous", "Zero", + ]; - const nouns = [ - "glue", "riot", "boom", "veil", "poet", "hype", "cafe", "gene", "fame", "sin", "zon", "barb", "core", "dust", "bite", "maid", "scar", - "wing", "horn", "crew", "lake", "duke", "mask", "dawn", "seed", "tank", "flag", "jazz", "tart", "brew", "meow", "boot", "shoe", "sage", "drum", - "babe", "cash", "luck", "lime", "eyes", "boat", "milk", "tuna", "cube", "oreo", "worm", "rage", "itch", "four", "bomb", "pear", "ship", "oven", - "fear", "hate", "leaf", "hero", "wife", "bean", "hope", "girl", "baby", "meme", "wish", "one", "nine", "work", "cake", "lady", "fire", "pain", - "rain", "fool", "soul", "tree", "five", "fish", "love", "life", "elk", "dad", "hog", "elf", "mop", "rod", "bat", "bug", "bot", "pus", "ufo", - "zen", "ark", "rag", "egg", "bed", "car", "boy", "man", "cricket", "aura", "moon", "hippo", "vortex", "palm", "panther", "meteor", "deer", - "vein", "plan", "atom", "hole", "weed", "boss", "army", "meat", "lock", "song", "rat", "rose", "blossom", "twin", "comet", "fist", "crow", - "star", "starlight", "axe", "fury", "mouse", "blow", "swan", "bee", "asp", "viper", "feather", "bird", "bolt", "sun", "mind", "beaver", "frog", - "mist", "day", "night", "falcon", "blood", "poison", "lily", "inferno", "kiss", "lotus", "giant", "monarch", "lord", "autumn", "spring", - "summer", "winter", "paragon", "vulture", "condor", "coil", "chain", "spell", "dove", "peach", "petal", "droplet", "eruption", "heaven", "fog", - "boa", "needle", "shield", "rock", "turtle", "ghost", "death", "cobra", "bane", "princess", "king", "fingers", "toes", "hand", "foot", "ear", - "eye", "skull", "cat", "dog", "pig", "piggy", "cow", "snake", "horse", "rabbit", "goat", "wolf", "sheep", "duck", "eagle", "crab", "baboon", "basilisk", - "fox", "badger", "beetle", "butterfly", "shark", "clownfish", "crane", "cicada", "dingo", "elephant", "jackal", "jaguar", "lion", "mandrill", - "lungfish", "heart", "spleen", "liver", "guts", "brains", "bones", "chocolate", "candy", "surprise", "cheese", "furball", "salami", "beef", - "supreme", "taco", "burger", "hotdog", "carrot", "onion", "fungus", "brick", "rock", "banana", "killer", "demon", "angel", "saint", "bamboo", - "panda", "broom", "hammer", "snow", "cur", "toad", "raven", "claw", "pine", "rice", "sushi", "bread", "toast", "cereal", "smoke", "fart", - "beer", "bear", "faucet", "pipe", "iron", "dork", "genius", "hunter", "farmer", "wiz", "witch", "churro", "donut", "shrimp", "sand", "pagoda", - "eel", "ant", "pants", "jeans", "socks", "sword", "fork", "pizza", "trap", "pork", "wort", "sack", "hawk", "rite", "tire", "dirt", "plum", - "ATM", "CD", "SUV", "TV", "abacus", "abbey", "abdomen", "ability", "absence", "abuse", "academy", "accent", "access", "accord", "account", "acetate", "acid", "acorn", - "acre", "acrylic", "act", "action", "actor", "actress", "ad", "adapter", "address", "admin", "admire", "adobe", "adult", "advance", "advent", "adverb", "advice", "adviser", - "affair", "affect", "afoul", "age", "agency", "agenda", "agent", "aglet", "agony", "aid", "aide", "aim", "air", "airbag", "airbus", "airfare", "airline", "airmail", "airman", - "airport", "airship", "alarm", "alb", "album", "alcohol", "alcove", "alder", "ale", "alert", "alfalfa", "algebra", "alias", "alibi", "alien", "alley", "alloy", "almanac", "almond", - "alpaca", "alpha", "altar", "alto", "amazon", "amber", "amenity", "amnesty", "amount", "anagram", "analog", "analogy", "analyst", "anarchy", "anatomy", "anchovy", "android", "angel", - "anger", "angina", "angle", "angora", "anguish", "animal", "anime", "anise", "ankle", "anklet", "annual", "anorak", "answer", "ant", "antigen", "antique", "antler", "antling", "anxiety", - "anybody", "anyone", "ape", "apology", "app", "apparel", "appeal", "apple", "apricot", "apron", "apse", "aquifer", "arcade", "arch", "archer", "area", "arena", "ark", "arm", "armoire", "armor", - "armour", "armpit", "armrest", "army", "array", "arrest", "arrival", "arrow", "art", "artery", "arthur", "article", "artist", "ascend", "ascent", "ascot", "ash", "ashram", "ashtray", "aside", - "aspect", "asphalt", "aspic", "assault", "asset", "assist", "atelier", "athlete", "atom", "atrium", "attack", "attempt", "attic", "auction", "audit", "aunt", "author", "auto", - "autumn", "avenue", "average", "avocado", "award", "awe", "axis", "azimuth", "babe", "baboon", "baby", "back", "back-up", "backup", "bacon", "badge", "badger", "bag", "bagel", "baggage", "baggie", - "baggy", "bagpipe", "bail", "bait", "bake", "baker", "bakery", "balance", "balcony", "ball", "ballet", "balloon", "ballot", "bamboo", "ban", "banana", "band", "bandana", "bangle", "banjo", "bank", - "banker", "banking", "banner", "banyan", "baobab", "bar", "barber", "bargain", "barge", "barium", "bark", "barley", "barn", "barrage", "barrel", "barrier", "base", "basics", "basil", "basin", "basis", - "basket", "bass", "bassoon", "bat", "bath", "bather", "bathtub", "batter", "battery", "batting", "battle", "bay", "bayou", "beach", "bead", "beak", "beam", "bean", "beanie", "bear", "beard", "beast", - "beastie", "beat", "beating", "beauty", "beaver", "beck", "bed", "bedrock", "bedroom", "bee", "beech", "beef", "beer", "beet", "beetle", "beggar", "begonia", "behalf", "behest", "behold", "being", - "belfry", "belief", "bell", "bellows", "belly", "belt", "bench", "bend", "benefit", "beret", "berry", "bet", "beyond", "bias", "bicycle", "bid", "bidder", "bidding", "bidet", "bijou", "bike", "bikini", - "bill", "billing", "billion", "bin", "biology", "biopsy", "biplane", "birch", "bird", "birth", "biscuit", "bit", "bite", "bitten", "bitter", "black", "bladder", "blade", "blame", "blank", "blanket", - "blast", "blazer", "blend", "blight", "blind", "blinker", "blister", "block", "blocker", "blog", "blogger", "blood", "bloom", "bloomer", "blossom", "blouse", "blow", "blowgun", "blue", "blush", "boar", - "board", "boat", "bob", "bobcat", "body", "bog", "bolero", "bolt", "bomb", "bomber", "bombing", "bond", "bonding", "bone", "bonfire", "bongo", "bonnet", "bonsai", "bonus", "book", "bookend", "booking", - "booklet", "boolean", "boom", "boon", "boost", "booster", "boot", "bootee", "bootie", "booty", "border", "bore", "bosom", "boss", "botany", "bother", "bottle", "bottom", "boudoir", "bough", "boulder", - "bouquet", "bout", "bow", "bower", "bowl", "bowler", "bowling", "bowtie", "box", "boxer", "boy", "boycott", "boyhood", "bra", "brace", "bracket", "brain", "brake", "bran", "branch", "brand", "brandy", - "brass", "bread", "break", "breast", "breath", "breeze", "brewer", "bribery", "brick", "bride", "bridge", "brief", "briefly", "briefs", "brink", "brisket", "broad", "broiler", "broker", "bronco", - "bronze", "brooch", "brood", "brook", "broom", "brother", "brow", "brown", "brownie", "browser", "brunch", "brush", "bubble", "buck", "bucket", "buckle", "bud", "buddy", "budget", "buffalo", "buffer", - "buffet", "bug", "buggy", "bugle", "builder", "bulb", "bulk", "bull", "bullet", "bump", "bumper", "bun", "bunch", "burden", "bureau", "burglar", "burial", "burn", "burning", "burrito", "burro", "burrow", - "burst", "bus", "bush", "bust", "bustle", "butane", "butcher", "butler", "butter", "button", "buy", "buyer", "buying", "buzz", "buzzard", "c-clamp", "cabana", "cabbage", "cabin", "cabinet", "cable", - "caboose", "cacao", "cactus", "caddy", "cadet", "cafe", "caftan", "cage", "cake", "calf", "caliber", "calibre", "calico", "call", "calm", "calorie", "camel", "cameo", "camera", "camp", "camper", "campus", - "can", "canal", "cancer", "candle", "candy", "cane", "cannon", "canoe", "canon", "canopy", "canteen", "canvas", "cap", "cape", "caper", "capital", "capon", "captain", "caption", "captor", "car", "carabao", - "caramel", "caravan", "carbon", "card", "care", "career", "cargo", "caribou", "carload", "carol", "carp", "carpet", "carport", "carrier", "carrot", "carry", "cart", "cartel", "carter", "cartoon", "carving", - "cascade", "case", "cash", "cashew", "cashier", "casino", "casket", "cassava", "cassock", "cast", "castle", "cat", "catch", "catcher", "cation", "catsup", "cattle", "causal", "cause", "caution", "cave", - "caviar", "cayenne", "ceiling", "celery", "cell", "cellar", "cello", "celsius", "cement", "census", "cent", "center", "centre", "century", "ceramic", "cereal", "chafe", "chain", "chair", "chaise", "chalet", - "chalice", "chalk", "chamber", "chance", "change", "channel", "chaos", "chap", "chapel", "chapter", "chard", "charge", "charger", "charity", "charm", "charset", "chart", "charter", "chasm", "chassis", - "chateau", "chatter", "check", "cheddar", "cheek", "cheer", "cheese", "cheetah", "chef", "chem", "cheque", "cherry", "chess", "chest", "chick", "chicken", "chicory", "chief", "child", "chili", "chill", - "chime", "chin", "chino", "chip", "chive", "chives", "choice", "choir", "choker", "chop", "chops", "chord", "chorus", "chow", "chowder", "chrome", "chub", "chuck", "chug", "church", "churn", "chutney", - "cicada", "cinder", "cinema", "circle", "circuit", "cirrus", "citizen", "citron", "citrus", "city", "claim", "clam", "clamp", "clan", "clank", "clarity", "clasp", "class", "classic", "clause", "clave", - "clavier", "claw", "clay", "cleaner", "cleat", "clef", "cleft", "cleric", "clerk", "click", "client", "cliff", "climate", "climb", "clinic", "clip", "clipper", "cloak", "clock", "clogs", "clone", "close", - "closet", "closing", "closure", "cloth", "clothes", "cloud", "clove", "clover", "cloves", "club", "clue", "cluster", "clutch", "coach", "coal", "coast", "coaster", "coat", "cob", "cobbler", "cobweb", "cock", - "cockpit", "cocoa", "coconut", "cod", "code", "codling", "codon", "coffee", "coffin", "cohort", "coil", "coin", "coke", "cold", "collar", "collard", "college", "colon", "colony", "color", "colt", "column", - "comb", "combat", "combine", "comedy", "comfort", "comic", "comics", "comma", "command", "comment", "common", "company", "compass", "complex", "compost", "con", "concept", "concern", "concert", "condor", - "conduct", "cone", "conga", "congo", "conifer", "consent", "consist", "console", "consul", "contact", "contact", "lens", "content", "contest", "context", "contour", "control", "convert", "cook", "cookie", - "cooking", "cop", "cop-out", "cope", "copper", "copy", "copying", "coral", "cord", "core", "cork", "corn", "corner", "cornet", "corps", "corral", "corsage", "cosset", "cost", "costume", "cot", "cottage", - "cotton", "couch", "cougar", "cough", "council", "counsel", "count", "counter", "country", "county", "couple", "coupon", "courage", "course", "court", "cousin", "cover", "cow", "cowbell", "cowboy", "coyote", - "crab", "crack", "cracker", "cradle", "craft", "crane", "cranky", "crap", "crash", "crate", "cravat", "craw", "crawdad", "crayon", "crazy", "cream", "creator", "creche", "credit", "creek", "creme", "brulee", "crepe", - "crest", "crew", "crewman", "crewmen", "cria", "crib", "cricket", "crime", "crisis", "crisp", "critic", "crocus", "crook", "crop", "cross", "crotch", "croup", "crow", "crowd", "crown", "crude", "cruelty", "cruise", - "crumb", "crunch", "crush", "crust", "cry", "crystal", "cub", "cube", "cuckoo", "cue", "cuisine", "culture", "culvert", "cup", "cupcake", "cupola", "curd", "cure", "curio", "curl", "curler", "currant", "current", - "curry", "curse", "cursor", "curtain", "curve", "cushion", "custard", "custody", "custom", "cut", "cuticle", "cutlet", "cutover", "cutting", "cycle", "cyclone", "cygnet", "cymbal", "cynic", "cyst", "dad", "daddy", - "dagger", "dahlia", "daikon", "daily", "dairy", "daisy", "dam", "damage", "dame", "damn", "dance", "dancer", "dancing", "danger", "dare", "dark", "darn", "dart", "dash", "data", "date", "dawn", "day", "daybed", "dead", - "deal", "dealer", "dealing", "dearest", "death", "debate", "debris", "debt", "debtor", "decade", "decency", "decimal", "deck", "decline", "decoder", "deduce", "deed", "deep", "deer", "default", "defeat", "defense", - "deficit", "degree", "delay", "delight", "demand", "demon", "demur", "den", "denim", "density", "dentist", "deposit", "depot", "depth", "deputy", "derby", "derrick", "descent", "desert", "design", "desire", "desk", - "desktop", "dessert", "destiny", "detail", "detour", "device", "devil", "dew", "dhow", "diadem", "diagram", "dial", "dialect", "diam", "diamond", "diaper", "diarist", "diary", "dibble", "dick", "dickey", "diction", - "die", "diesel", "diet", "diffuse", "dig", "digger", "digging", "digit", "dignity", "dill", "dime", "dimple", "diner", "dinghy", "dining", "dinner", "dioxide", "dip", "diploma", "dirndl", "dirt", "disco", "disdain", - "disease", "disgust", "dish", "disk", "display", "dispute", "divan", "diver", "divide", "divider", "divine", "diving", "divorce", "doc", "dock", "doctor", "doe", "dog", "doggie", "dogsled", "dogwood", "doing", "doll", - "dollar", "dollop", "dolman", "dolor", "dolphin", "domain", "dome", "donkey", "donor", "donut", "door", "doorway", "dory", "dose", "dot", "double", "doubt", "doubter", "dough", "down", "dozen", "draft", "drag", "dragon", - "drain", "drake", "drama", "drapes", "draw", "drawer", "drawing", "dream", "dreamer", "dredger", "dress", "dresser", "drill", "drink", "drive", "driver", "driving", "drizzle", "drop", "drug", "drum", "drummer", "drunk", - "dryer", "duck", "dud", "dude", "due", "duel", "dueling", "duffel", "dugout", "dump", "dump", "truck", "dune", "dune", "buggy", "dungeon", "durian", "dusk", "dust", "dust", "storm", "duster", "duty", "dwarf", "dwell", - "dynamo", "dynasty", "e-book", "e-mail", "eagle", "eaglet", "ear", "eardrum", "earplug", "earring", "earth", "ease", "easel", "east", "eating", "eaves", "echidna", "eclipse", "ecology", "economy", "eddy", "edge", - "edger", "edible", "editing", "edition", "editor", "eel", "effect", "effort", "egg", "egghead", "eggnog", "ego", "ejector", "elbow", "element", "elf", "elicit", "elite", "elixir", "elk", "ellipse", "elm", "elver", - "email", "emanate", "embassy", "embryo", "emerald", "emery", "emitter", "emotion", "empire", "employ", "emu", "enclave", "end", "endive", "enemy", "energy", "engine", "enigma", "enquiry", "entity", "entree", "entry", - "envy", "enzyme", "epee", "ephyra", "epic", "episode", "epoch", "eponym", "epoxy", "equal", "equinox", "equity", "era", "eraser", "erosion", "error", "escape", "escort", "essay", "essence", "estate", "estuary", "ethics", - "ethyl", "eve", "evening", "event", "evil", "ex-wife", "exam", "example", "excerpt", "excess", "excuse", "exhaust", "exhibit", "exile", "exit", "expense", "expert", "export", "expose", "extent", "extreme", "eye", "eyeball", - "eyebrow", "eyelash", "eyelid", "eyelids", "eyrie", "fabric", "face", "facet", "fact", "factor", "factory", "faculty", "fail", "failure", "fairy", "faith", "fall", "fallacy", "fame", "family", "fan", "fang", "fanny", "fantasy", - "farm", "farmer", "farming", "farrow", "fascia", "fashion", "fat", "fate", "father", "fatigue", "faucet", "fault", "fav", "fava", "favor", "fawn", "fax", "fear", "feast", "feather", "feature", "fedora", "fee", "feed", "feeding", - "feel", "feeling", "fellow", "felony", "female", "fen", "fence", "fencing", "fender", "feng", "fennel", "ferret", "ferry", "fetus", "few", "fiber", "fibre", "ficlet", "fiction", "fiddle", "field", "fiery", "fiesta", "fifth", "fig", - "fight", "fighter", "figure", "file", "filing", "fill", "fillet", "filly", "film", "filter", "filth", "final", "finance", "finding", "fine", "finer", "finger", "finish", "fir", "fire", "fireman", "firm", "first", "fish", "fishery", - "fishing", "fishnet", "fisting", "fit", "fitness", "fix", "fixture", "flag", "flair", "flame", "flan", "flanker", "flare", "flash", "flat", "flavor", "flax", "fleck", "fleece", "flesh", "flick", "flicker", "flight", "flint", "flock", - "flood", "floor", "floozie", "flour", "flow", "flower", "flu", "fluke", "flume", "flung", "flute", "fly", "flytrap", "foal", "foam", "fob", "focus", "fog", "fold", "folder", "folk", "fondue", "font", "food", "fool", "foot", "footage", - "forage", "forager", "foray", "force", "ford", "forearm", "forest", "forever", "forgery", "fork", "form", "formal", "format", "former", "formula", "fort", "forte", "fortune", "forum", "founder", "fourths", "fowl", "fox", "frame", - "fraud", "freak", "freckle", "freedom", "freezer", "freight", "frenzy", "freon", "fresco", "fridge", "friend", "fries", "frigate", "fright", "fringe", "fritter", "frock", "frog", "front", "frost", "frown", "fruit", "fry", "fvck", - "fuel", "fugato", "full", "fun", "fund", "funding", "funeral", "fur", "furnace", "furry", "futon", "future", "gadget", "gaffe", "gaffer", "gain", "gaiters", "gale", "gallery", "galley", "gallon", "game", "gaming", "gander", "gang", - "gap", "garage", "garb", "garbage", "garden", "garlic", "garment", "garter", "gas", "gasket", "gasp", "gate", "gateway", "gather", "gator", "gauge", "gavel", "gazebo", "gazelle", "gear", "geek", "gel", "gelatin", "gelding", "gem", - "gemsbok", "gender", "gene", "general", "genie", "genius", "genre", "geology", "gerbil", "gesture", "geyser", "gherkin", "ghost", "giant", "gift", "gig", "giggle", "ginger", "ginseng", "giraffe", "girdle", "girl", "git", "glacier", - "glance", "gland", "glass", "glasses", "glee", "glen", "glider", "gliding", "glimpse", "globe", "gloom", "glory", "glove", "glow", "glucose", "glue", "glut", "gnat", "gnu", "go-kart", "goal", "goat", "gobbler", "god", "goddess", "goggles", - "going", "gold", "golf", "gondola", "gong", "good", "goodbye", "goodie", "goose", "gopher", "gorilla", "gosling", "gossip", "gown", "grace", "grade", "graft", "grain", "gram", "grammar", "gran", "grand", "grandma", "grandpa", "granny", - "granola", "grant", "grape", "graph", "graphic", "grasp", "grass", "gravel", "gravity", "gravy", "gray", "grease", "greed", "green", "greens", "grenade", "grey", "grid", "grief", "grill", "grin", "grip", "gripper", "grit", "grocery", - "ground", "group", "grouper", "grouse", "grove", "growth", "grub", "guard", "guava", "guess", "guest", "guide", "guilder", "guilt", "guilty", "guinea", "guitar", "gum", "gumshoe", "gun", "gutter", "guy", "gym", "gymnast", "gyro", "habit", - "habitat", "hacksaw", "hail", "hair", "haircut", "hake", "half", "halibut", "hall", "hallway", "halt", "ham", "hammer", "hammock", "hamster", "hand", "handful", "handgun", "handle", "handsaw", "hanger", "harald", "harbor", "harbour", - "hardhat", "hare", "harm", "harmony", "harp", "harvest", "hash", "hashtag", "hassock", "haste", "hat", "hatbox", "hatchet", "hate", "hatred", "haunt", "haven", "havoc", "hawk", "hay", "haze", "hazel", "head", "health", "hearing", "hearsay", - "heart", "hearth", "heat", "heater", "heating", "heaven", "heavy", "hectare", "hedge", "heel", "heifer", "height", "heir", "helium", "hell", "hellcat", "hello", "helmet", "helo", "help", "hemp", "hen", "herb", "herbs", "hermit", "hero", - "heroine", "heron", "herring", "hexagon", "heyday", "hiccups", "hide", "high", "highway", "hike", "hiking", "hill", "hint", "hip", "hire", "hiring", "history", "hit", "hive", "hobbit", "hobby", "hockey", "hoe", "hog", "hold", "holder", - "hole", "holiday", "home", "homonym", "honesty", "honey", "honor", "honoree", "hood", "hoof", "hook", "hop", "hope", "hops", "horde", "horizon", "hormone", "horn", "hornet", "horror", "horse", "horst", "hose", "hosiery", "hospice", - "host", "hostel", "hostess", "hotdog", "hotel", "hound", "hour", "house", "housing", "hovel", "howard", "hub", "hubcap", "hubris", "hug", "hugger", "hull", "human", "hummus", "humor", "humour", "hundred", "hunger", "hunt", "hunter", - "hunting", "hurdle", "hurdler", "hurry", "hurt", "husband", "hut", "hutch", "hydrant", "hyena", "hype", "ice", "iceberg", "icicle", "icing", "icon", "icy", "id", "idea", "ideal", "idiom", "idiot", "igloo", "ikebana", "illegal", "illness", - "image", "impact", "impala", "import", "impress", "impulse", "in-joke", "in-laws", "inbox", "incense", "inch", "income", "index", "infancy", "infant", "infix", "influx", "info", "ingrate", "initial", "injury", "ink", "inlay", "inn", "input", - "inquiry", "insect", "insert", "inside", "insight", "instant", "integer", "intent", "invader", "inverse", "invite", "invoice", "iris", "iron", "irony", "island", "issue", "item", "ivory", "jack", "jackal", "jacket", "jade", "jaguar", "jail", - "jam", "jar", "jasmine", "jaw", "jazz", "jeans", "jeep", "jelly", "jerk", "jet", "jewel", "jewelry", "jicama", "jiffy", "job", "jockey", "joey", "jogging", "joint", "joke", "jot", "journal", "journey", "joy", "judge", "judo", "jug", "juice", - "jumbo", "jump", "jumper", "jungle", "junior", "junk", "junker", "junket", "jury", "justice", "jute", "kale", "karate", "kayak", "kazoo", "kebab", "keep", "keeper", "kendo", "kennel", "ketch", "ketchup", "kettle", "key", "kick", "kid", "kidney", - "kill", "killer", "killing", "kilt", "kimono", "kinase", "kind", "king", "kingdom", "kiosk", "kiss", "kit", "kitchen", "kite", "kitsch", "kitten", "kitty", "kiwi", "knee", "knife", "knight", "knock", "knot", "knuckle", "koala", "kumquat", "lab", - "label", "labor", "laborer", "labour", "lace", "lack", "lad", "ladder", "ladle", "lady", "ladybug", "lag", "lake", "lamb", "lambkin", "lament", "lamp", "lanai", "land", "landing", "lane", "lantern", "lap", "lapdog", "laptop", "larch", "lard", - "larder", "lark", "larva", "lasagna", "lashes", "last", "latency", "latex", "lathe", "latte", "latter", "laugh", "laundry", "lava", "law", "lawn", "lawsuit", "lawyer", "lay", "layer", "layout", "lead", "leader", "leading", "leaf", "league", - "leaker", "leap", "leash", "leather", "leave", "leaver", "lecture", "leek", "leeway", "left", "leg", "legacy", "legal", "legend", "legging", "legume", "leisure", "lemon", "lemur", "lender", "lending", "length", "lens", "lentil", "leopard", - "leprosy", "lesbian", "lesson", "letter", "lettuce", "level", "lever", "leveret", "liar", "liberty", "libido", "library", "licence", "license", "lid", "lie", "lieu", "life", "lift", "ligand", "light", "ligula", "lilac", "lily", "limb", "lime", - "limit", "limo", "line", "linen", "liner", "lining", "link", "linkage", "linseed", "lion", "lip", "lipid", "liquid", "liquor", "list", "listing", "litmus", "litter", "liver", "living", "lizard", "llama", "load", "loading", "loaf", "loafer", "loan", - "lobby", "lobster", "local", "lock", "locker", "locket", "locust", "lode", "loft", "log", "loggia", "logic", "login", "logo", "look", "lookout", "loop", "loquat", "lord", "loss", "lot", "lotion", "lottery", "lounge", "louse", "lout", "love", "lover", - "lox", "loyalty", "luck", "luggage", "lumber", "lunch", "lung", "lunge", "lust", "lute", "luxury", "lychee", "lycra", "lye", "lynx", "lyocell", "lyre", "lyrics", "lysine", "mRNA", "macaw", "machine", "macrame", "macro", "madam", "maestro", "maggot", - "magic", "magnet", "maid", "maiden", "mail", "mailbox", "mailer", "mailing", "mailman", "main", "maize", "major", "maker", "makeup", "making", "male", "malice", "mall", "mallard", "mallet", "mama", "mambo", "mammoth", "man", "manacle", "manager", - "manatee", "mandate", "mangle", "mango", "manhunt", "maniac", "mankind", "manner", "manor", "mansard", "mansion", "mantel", "mantle", "mantua", "many", "map", "maple", "mapping", "maracas", "marble", "march", "mare", "margin", "marimba", "marines", - "mark", "marker", "market", "markup", "marsh", "marten", "marxism", "mascara", "mask", "masonry", "mass", "massage", "mast", "master", "mastoid", "mat", "match", "mate", "math", "matrix", "matter", "mattock", "max", "maximum", "maybe", "mayor", - "meadow", "meal", "mean", "meander", "meaning", "means", "measles", "measure", "meat", "mecca", "med", "medal", "media", "median", "medium", "meet", "meeting", "melody", "melon", "member", "meme", "memo", "memory", "men", "menorah", "mention", "mentor", - "menu", "mercury", "merit", "mess", "message", "messy", "metal", "meteor", "meter", "methane", "method", "metric", "metro", "midden", "middle", "midline", "midwife", "might", "migrant", "mile", "mileage", "milk", "mill", "millet", "million", "mime", "mimosa", - "min", "mind", "mine", "mineral", "mini", "minibus", "minimum", "mining", "minion", "mink", "minnow", "minor", "mint", "minute", "miracle", "mirror", "misfit", "miss", "missile", "mission", "mist", "mistake", "mister", "miter", "mitten", "mix", "mixer", - "mixture", "moai", "moat", "mob", "mobile", "mobster", "mocha", "mochi", "mode", "model", "modem", "molar", "molding", "mole", "mom", "moment", "money", "monger", "monitor", "monk", "monkey", "monocle", "monsoon", "monster", "month", "mood", "moody", "moon", - "moose", "mop", "morale", "morbid", "morning", "moron", "morsel", "mortal", "mortise", "mosque", "most", "motel", "moth", "mother", "motion", "motive", "motor", "mound", "mouse", "mouser", "mousse", "mouth", "mouton", "mover", "movie", "mower", "mud", "muffin", - "mug", "mukluk", "mule", "murder", "muscat", "muscle", "museum", "music", "muskrat", "mussel", "mustard", "mutt", "mutton", "mystery", "myth", "nail", "name", "naming", "napkin", "nasal", "nation", "native", "nature", "neck", "necktie", "nectar", "need", - "needle", "neglect", "neon", "neonate", "nephew", "nerve", "nest", "net", "netball", "netbook", "netsuke", "network", "neuron", "news", "nexus", "nibble", "nicety", "niche", "nick", "nickel", "niece", "night", "ninja", "nit", "nobody", "nod", "node", "noir", - "noise", "noodle", "noodles", "noon", "norm", "normal", "north", "nose", "note", "notepad", "nothing", "notice", "notion", "nougat", "noun", "novel", "nudge", "nuke", "number", "numeric", "nun", "nurse", "nursery", "nursing", "nurture", "nut", "nutmeg", - "nylon", "nymph", "oak", "oar", "oasis", "oat", "oatmeal", "oats", "obesity", "obi", "object", "oboe", "ocean", "ocelot", "octagon", "octave", "octavo", "octet", "octopus", "odyssey", "oeuvre", "offence", "offense", "offer", "office", "officer", "offset", - "oil", "okra", "oldie", "oleo", "olive", "omega", "omelet", "onion", "online", "onset", "opening", "opera", "opinion", "opium", "opossum", "optimal", "option", "orange", "orator", "orchard", "orchid", "order", "ore", "oregano", "organ", "orient", "origin", - "osmosis", "osprey", "ostrich", "other", "otter", "ottoman", "ounce", "outback", "outcome", "outfit", "outlaw", "outlay", "outlet", "outline", "outlook", "output", "outrage", "outrun", "outset", "outside", "oval", "ovary", "oven", "owl", "owner", "ox", - "oxford", "oxygen", "oyster", "ozone", "pace", "pack", "package", "packet", "pad", "paddle", "paddock", "pagan", "page", "pagoda", "pail", "pain", "paint", "painter", "pair", "pajamas", "palace", "palate", "palm", "pan", "pancake", "panda", "panel", "panic", - "pannier", "panpipe", "pansy", "panther", "panties", "pantry", "pants", "panty", "papa", "papaya", "paper", "parable", "parade", "parcel", "pard", "pardon", "parent", "park", "parka", "parking", "parole", "parrot", "parser", "parsley", "parsnip", "part", - "partner", "party", "pass", "passage", "passing", "passion", "passive", "past", "pasta", "paste", "pastor", "pastry", "pasture", "pat", "patch", "pate", "patent", "path", "pathway", "patient", "patina", "patio", "patriot", "patrol", "patron", "pattern", - "patty", "pause", "paw", "pay", "payee", "payment", "payoff", "pea", "peace", "peach", "peacoat", "peacock", "peak", "peanut", "pear", "pearl", "peasant", "pecan", "pecker", "pedal", "peek", "peen", "peer", "pelican", "pelt", "pen", "penalty", "pence", - "pencil", "pendant", "penguin", "penis", "pennant", "penny", "pension", "peony", "people", "pepper", "percent", "perch", "perfume", "period", "permit", "perp", "person", "pest", "pet", "petal", "pew", "phase", "phone", "photo", "phrase", "physics", - "pianist", "piano", "piccolo", "pick", "pickax", "pickaxe", "picket", "pickle", "pickup", "picnic", "picture", "pie", "piece", "pier", "piety", "pig", "pigeon", "piglet", "pigpen", "pigsty", "pike", "pilaf", "pile", "pilgrim", "pill", "pillar", "pillbox", - "pillow", "pilot", "pimp", "pimple", "pin", "pine", "ping", "pink", "pinkie", "pinot", "pint", "pinto", "pinworm", "pioneer", "pipe", "piracy", "pirate", "piss", "pistol", "pit", "pita", "pitch", "pitcher", "pith", "pizza", "place", "placebo", "placode", - "plain", "plan", "plane", "planet", "plant", "planter", "planula", "plaster", "plastic", "plate", "platter", "play", "player", "plea", "pleat", "pledge", "plenty", "plier", "pliers", "plight", "plot", "plough", "plover", "plow", "plowman", "plug", "plugin", - "plum", "plumber", "plume", "plunger", "plywood", "pocket", "pod", "podcast", "poem", "poet", "poetry", "point", "poison", "poker", "pole", "polenta", "police", "policy", "polish", "poll", "polo", "polyp", "pomelo", "pompom", "poncho", "pond", "pony", "pool", - "poor", "pop", "popcorn", "poppy", "porch", "pork", "port", "porter", "portion", "post", "postage", "postbox", "poster", "postfix", "pot", "potato", "pottery", "potty", "pouch", "poultry", "pound", "poverty", "powder", "power", "prairie", "praise", "pray", - "prayer", "preface", "prefix", "prelude", "premier", "premise", "premium", "present", "press", "presume", "pretzel", "prey", "price", "pricing", "pride", "priest", "primary", "primate", "prince", "print", "printer", "prior", "prison", "privacy", "private", - "prize", "probe", "problem", "process", "proctor", "produce", "product", "profile", "profit", "program", "project", "promise", "prompt", "pronoun", "proof", "propane", "prophet", "prose", "protein", "protest", "prow", "prune", "pruner", "pub", "public", - "pudding", "puddle", "puffin", "pug", "puggle", "pulley", "pulse", "puma", "pump", "pumpkin", "pun", "punch", "pup", "pupa", "pupil", "puppet", "puppy", "puritan", "purity", "purple", "purpose", "purr", "purse", "pursuit", "push", "pusher", "put", "puzzle", - "pyramid", "quail", "quality", "quart", "quarter", "quartet", "quartz", "queen", "query", "quest", "quiche", "quiet", "quill", "quilt", "quince", "quinoa", "quit", "quiver", "quota", "quote", "rabbi", "rabbit", "raccoon", "race", "racer", "racing", "racism", - "racist", "rack", "radar", "radio", "radish", "raffle", "raft", "rag", "rage", "raid", "rail", "railing", "railway", "raiment", "rain", "rainbow", "rainy", "raise", "raisin", "rake", "rally", "ram", "rambler", "ramen", "ramie", "ranch", "rancher", "range", - "ranger", "rank", "rap", "rape", "rat", "rate", "rating", "ratio", "rations", "raven", "ravioli", "rawhide", "ray", "rayon", "razor", "reach", "read", "reader", "reading", "real", "reality", "realm", "reamer", "rear", "reason", "rebel", "reboot", "recall", - "receipt", "recess", "recipe", "record", "recruit", "red", "redhead", "reef", "reform", "refuge", "refund", "refusal", "refuse", "regard", "regime", "region", "regret", "reject", "relay", "release", "relief", "relish", "remains", "remark", "remnant", - "remote", "removal", "rent", "repair", "repeat", "replica", "reply", "report", "request", "resale", "rescue", "reserve", "reset", "residue", "resist", "resolve", "resort", "respect", "respite", "rest", "result", "resume", "retina", "retreat", "return", - "reunion", "reveal", "revenge", "revenue", "reverse", "review", "revival", "reward", "rhubarb", "rhyme", "rhythm", "rib", "ribbon", "rice", "riddle", "ride", "rider", "ridge", "riding", "rifle", "right", "rim", "ring", "riot", "rip", "ripple", "rise", "riser", - "risk", "rite", "ritual", "river", "rivulet", "road", "roadway", "roar", "roast", "robe", "robin", "robot", "rock", "rocker", "rocket", "rod", "role", "roll", "roller", "romaine", "romance", "roof", "room", "rooster", "root", "rope", "rose", "roster", "rostrum", - "round", "route", "router", "routine", "row", "rowboat", "rowing", "rubber", "rubbish", "rubric", "ruby", "ruckus", "ruffle", "rug", "rugby", "ruin", "rule", "ruler", "ruling", "rum", "rumor", "run", "runaway", "runner", "running", "runway", "rush", "rust", - "rye", "sabre", "sac", "sack", "saddle", "sadness", "safari", "safe", "safety", "saffron", "sage", "sail", "sailing", "sailor", "saint", "sake", "salad", "salami", "salary", "sale", "salmon", "salon", "saloon", "salsa", "salt", "salute", "samovar", "sampan", - "sample", "samurai", "sand", "sandal", "sandbar", "sanity", "sardine", "sari", "sarong", "sash", "satin", "satire", "sauce", "saucer", "sausage", "savage", "saving", "savings", "savior", "saviour", "savory", "saw", "scale", "scalp", "scam", "scanner", "scarf", - "scene", "scenery", "scent", "schema", "scheme", "scholar", "school", "science", "scooter", "scope", "score", "scorn", "scotch", "scout", "scow", "scrap", "scraper", "scratch", "screen", "screw", "scrim", "scrip", "script", "sea", "seabass", "seafood", - "seagull", "seal", "search", "seaside", "season", "seat", "seaweed", "second", "secrecy", "secret", "section", "sector", "seed", "seeder", "seeker", "seep", "segment", "seizure", "self", "seller", "selling", "seminar", "senate", "senator", "sender", "senior", - "sense", "sensor", "sepal", "sequel", "serial", "series", "sermon", "serum", "serval", "servant", "server", "service", "sesame", "session", "set", "setback", "setting", "settler", "sewer", "sex", "shack", "shackle", "shade", "shadow", "shaker", "shallot", - "shame", "shampoo", "shanty", "shape", "share", "shark", "shaw", "shawl", "shear", "sheath", "shed", "sheep", "sheet", "shelf", "shell", "shelter", "sherbet", "sherry", "shield", "shift", "shin", "shine", "shingle", "ship", "shipper", "shirt", "shjt", "shoat", - "shock", "shoe", "shoes", "shofar", "shoot", "shop", "shopper", "shore", "short", "shorts", "shot", "shout", "shovel", "show", "shower", "shred", "shrimp", "shrine", "sibling", "sick", "side", "sidecar", "siding", "siege", "sigh", "sight", "sign", "signal", - "signet", "signify", "signup", "silence", "silica", "silicon", "silk", "sill", "silly", "silo", "silver", "simple", "sin", "singer", "singing", "sink", "sip", "sir", "sister", "sitar", "site", "size", "skate", "skating", "skean", "ski", "skiing", "skill", - "skin", "skirt", "skull", "skunk", "sky", "skyline", "skywalk", "slang", "slash", "slate", "slave", "slavery", "slaw", "sled", "sledge", "sleep", "sleet", "sleuth", "slice", "slide", "slider", "slime", "slip", "slipper", "slope", "slot", "sloth", "slump", - "smell", "smile", "smith", "smock", "smog", "smoke", "smoking", "smolt", "snack", "snail", "snake", "snap", "snarl", "sneaker", "sneeze", "sniffle", "snob", "snorer", "snow", "snowman", "snuck", "snug", "snuggle", "soap", "soccer", "society", "sock", "socks", - "soda", "sofa", "soil", "soldier", "sole", "someone", "son", "sonar", "sonata", "song", "sonnet", "soot", "soprano", "sorbet", "sorghum", "sorrel", "sorrow", "sort", "soul", "sound", "soup", "source", "south", "sow", "soy", "soybean", "space", "spacing", - "spade", "span", "spandex", "spank", "spark", "sparrow", "spasm", "spat", "spatula", "spawn", "speaker", "spear", "spec", "special", "species", "speech", "speed", "spell", "spelt", "sphere", "sphynx", "spice", "spider", "spike", "spill", "spinach", "spine", - "spiral", "spirit", "spit", "spite", "spleen", "split", "sponge", "sponsor", "spool", "spoon", "spork", "sport", "spot", "spouse", "sprag", "sprat", "spray", "spread", "spree", "spring", "sprout", "spruce", "spud", "spume", "spur", "spy", "square", "squash", - "squid", "stab", "stable", "stack", "stadium", "staff", "stag", "stage", "stain", "stair", "stake", "stalk", "stall", "stamen", "stamina", "stamp", "stance", "stand", "star", "start", "starter", "state", "statin", "station", "statue", "status", "statute", - "stay", "steak", "stealth", "steam", "steel", "steeple", "stem", "stench", "stencil", "step", "stepson", "stereo", "stew", "steward", "stick", "sticker", "still", "sting", "stinger", "stitch", "stock", "stole", "stomach", "stone", "stool", "stop", "storage", - "store", "storey", "storm", "story", "stot", "stove", "strait", "strand", "strap", "straw", "stream", "street", "stress", "stretch", "strife", "strike", "string", "strip", "stripe", "strobe", "stroke", "strudel", "stucco", "stud", "student", "studio", "study", - "stuff", "stump", "sty", "style", "styling", "stylus", "sub", "subject", "subset", "subsidy", "suburb", "subway", "success", "suck", "sucker", "suede", "suet", "sugar", "suicide", "suit", "suite", "sulfur", "sultan", "sum", "summary", "summer", "summit", "sun", - "sunbeam", "sundae", "sunday", "sundial", "sunlamp", "sunrise", "sunroom", "sunset", "supper", "supply", "support", "supreme", "surface", "surge", "surgeon", "surgery", "surname", "surplus", "survey", "sushi", "suspect", "swallow", "swamp", "swan", "swath", - "sweat", "sweater", "sweets", "swell", "swim", "swine", "swing", "switch", "swivel", "sword", "symbol", "symptom", "synergy", "synod", "synonym", "syrup", "system", "t-shirt", "tab", "tabby", "table", "tablet", "tackle", "taco", "tactics", "tactile", - "tadpole", "tag", "tail", "tailbud", "tailor", "tale", "talent", "talk", "talking", "tamale", "tambour", "tan", "tandem", "tank", "tanker", "tankful", "tap", "tape", "tapioca", "target", "taro", "tart", "task", "tassel", "taste", "tatami", "tattler", "tattoo", - "tavern", "tax", "taxi", "taxicab", "tea", "teacher", "team", "teapot", "tear", "tech", "teen", "teepee", "tell", "teller", "temp", "temper", "temple", "tempo", "tenant", "tender", "tenet", "tennis", "tenor", "tension", "tensor", "tent", "tenth", "tepee", - "term", "termite", "terrace", "terror", "test", "testing", "text", "textual", "texture", "thanks", "thaw", "theater", "theft", "theism", "theme", "theory", "therapy", "thesis", "thief", "thigh", "thing", "thirst", "thistle", "thong", "thongs", "thorn", "thought", - "thread", "threat", "thrift", "thrill", "throat", "throne", "thrush", "thrust", "thug", "thumb", "thump", "thunder", "thyme", "tiara", "tic", "tick", "ticket", "tide", "tie", "tiger", "tights", "tile", "till", "tilt", "timbale", "timber", "time", "timeout", - "timer", "timing", "timpani", "tin", "tinkle", "tintype", "tip", "tire", "tissue", "title", "toad", "toast", "toaster", "tobacco", "today", "toe", "toenail", "toffee", "tofu", "tog", "toga", "toilet", "toll", "tom-tom", "tomato", "tomb", "ton", "tone", "tongue", - "tonic", "tonight", "tool", "toot", "tooth", "top", "top-hat", "topic", "topsail", "toque", "tornado", "torso", "torte", "tosser", "total", "tote", "touch", "tour", "tourism", "tourist", "towel", "tower", "town", "toy", "trace", "track", "tract", "tractor", - "trade", "trader", "trading", "traffic", "tragedy", "trail", "trailer", "train", "trainer", "trait", "tram", "tramp", "trance", "transit", "transom", "trap", "trash", "travel", "tray", "treat", "treaty", "tree", "trek", "trellis", "tremor", "trench", "trend", - "triad", "trial", "tribe", "trick", "trigger", "trim", "trinket", "trip", "tripod", "tritone", "triumph", "trolley", "troop", "trooper", "trophy", "trouble", "trout", "trove", "trowel", "truck", "trumpet", "trunk", "trust", "trustee", "truth", "try", - "tsunami", "tub", "tuba", "tube", "tuber", "tug", "tugboat", "tuition", "tulip", "tumbler", "tummy", "tuna", "tune", "tune-up", "tunic", "tunnel", "turban", "turf", "turkey", "turn", "turning", "turnip", "turret", "turtle", "tusk", "tussle", "tutu", - "tuxedo", "tweet", "twig", "twine", "twins", "twist", "twister", "twitter", "type", "typhoon", "ukulele", "uncle", "unibody", "uniform", "union", "unique", "unit", "unity", "update", "upgrade", "uplift", "upper", "upward", "urge", "urgency", "urn", "usage", - "use", "user", "usher", "usual", "utensil", "utility", "vaccine", "vacuum", "vagrant", "valance", "valley", "value", "vampire", "van", "vanadyl", "vane", "vanilla", "vanity", "variant", "variety", "vase", "vault", "veal", "vector", "vehicle", "veil", "vein", - "veldt", "vellum", "velvet", "vendor", "veneer", "venison", "venom", "venti", "venture", "venue", "veranda", "verb", "verdict", "verse", "version", "vertigo", "verve", "vessel", "vest", "vet", "veteran", "veto", "vibe", "vice", "victim", "victory", "video", - "view", "viewer", "villa", "village", "vine", "vinegar", "vintage", "vintner", "vinyl", "viola", "violet", "violin", "virtue", "virus", "visa", "viscose", "vise", "vision", "visit", "visitor", "visor", "vista", "visual", "vitamin", "vitro", "vivo", "vixen", - "vodka", "vogue", "voice", "void", "vol", "volcano", "volume", "vomit", "vote", "voter", "voting", "voyage", "vulture", "wad", "wafer", "waffle", "wage", "wagon", "waist", "wait", "waiter", "waiting", "waiver", "wake", "walk", "walker", "walking", "walkway", - "wall", "wallaby", "wallet", "walnut", "walrus", "wampum", "wannabe", "want", "war", "warden", "warfare", "warlock", "warlord", "warm-up", "warming", "warmth", "warning", "warrant", "warren", "warrior", "wasabi", "wash", "washer", "washtub", "wasp", "waste", - "wasting", "watch", "watcher", "water", "wave", "wax", "way", "wealth", "weapon", "wear", "weasel", "weather", "web", "webinar", "webmail", "webpage", "website", "wedding", "wedge", "weed", "weeder", "week", "weekend", "weight", "weird", "welcome", "welfare", - "well", "west", "western", "wet-bar", "wetland", "wetsuit", "whack", "whale", "wharf", "wheat", "wheel", "whelp", "whey", "whip", "whisker", "whiskey", "whisper", "whistle", "white", "whole", "whorl", "wick", "widget", "widow", "width", "wife", "wifi", "wild", - "will", "willow", "win", "wind", "windage", "window", "wine", "winery", "wing", "wingman", "wingtip", "wink", "winner", "winter", "wire", "wiretap", "wiring", "wisdom", "wiseguy", "wish", "wit", "witch", "witness", "wok", "wolf", "woman", "wombat", "wonder", - "wont", "wood", "wool", "woolens", "word", "wording", "work", "worker", "working", "workout", "world", "worm", "worry", "worship", "worth", "wound", "wrap", "wrapper", "wreck", "wrecker", "wren", "wrench", "wrinkle", "wrist", "writer", "writing", "wrong", - "yacht", "yahoo", "yak", "yam", "yang", "yard", "yarn", "yawl", "year", "yeast", "yellow", "yew", "yin", "yoga", "yogurt", "yoke", "yolk", "young", "youth", "yoyo", "yurt", "zampone", "zebra", "zen", "zephyr", "zero", "zinc", "zipper", "zither", "zombie", "zone", - "zoo", "zoology", - ]; + const nouns = [ + "glue", "riot", "boom", "veil", "poet", "hype", "cafe", "gene", "fame", "sin", "zon", "barb", "core", "dust", "bite", "maid", "scar", + "wing", "horn", "crew", "lake", "duke", "mask", "dawn", "seed", "tank", "flag", "jazz", "tart", "brew", "meow", "boot", "shoe", "sage", "drum", + "babe", "cash", "luck", "lime", "eyes", "boat", "milk", "tuna", "cube", "oreo", "worm", "rage", "itch", "four", "bomb", "pear", "ship", "oven", + "fear", "hate", "leaf", "hero", "wife", "bean", "hope", "girl", "baby", "meme", "wish", "one", "nine", "work", "cake", "lady", "fire", "pain", + "rain", "fool", "soul", "tree", "five", "fish", "love", "life", "elk", "dad", "hog", "elf", "mop", "rod", "bat", "bug", "bot", "pus", "ufo", + "zen", "ark", "rag", "egg", "bed", "car", "boy", "man", "cricket", "aura", "moon", "hippo", "vortex", "palm", "panther", "meteor", "deer", + "vein", "plan", "atom", "hole", "weed", "boss", "army", "meat", "lock", "song", "rat", "rose", "blossom", "twin", "comet", "fist", "crow", + "star", "starlight", "axe", "fury", "mouse", "blow", "swan", "bee", "asp", "viper", "feather", "bird", "bolt", "sun", "mind", "beaver", "frog", + "mist", "day", "night", "falcon", "blood", "poison", "lily", "inferno", "kiss", "lotus", "giant", "monarch", "lord", "autumn", "spring", + "summer", "winter", "paragon", "vulture", "condor", "coil", "chain", "spell", "dove", "peach", "petal", "droplet", "eruption", "heaven", "fog", + "boa", "needle", "shield", "rock", "turtle", "ghost", "death", "cobra", "bane", "princess", "king", "fingers", "toes", "hand", "foot", "ear", + "eye", "skull", "cat", "dog", "pig", "piggy", "cow", "snake", "horse", "rabbit", "goat", "wolf", "sheep", "duck", "eagle", "crab", "baboon", "basilisk", + "fox", "badger", "beetle", "butterfly", "shark", "clownfish", "crane", "cicada", "dingo", "elephant", "jackal", "jaguar", "lion", "mandrill", + "lungfish", "heart", "spleen", "liver", "guts", "brains", "bones", "chocolate", "candy", "surprise", "cheese", "furball", "salami", "beef", + "supreme", "taco", "burger", "hotdog", "carrot", "onion", "fungus", "brick", "rock", "banana", "killer", "demon", "angel", "saint", "bamboo", + "panda", "broom", "hammer", "snow", "cur", "toad", "raven", "claw", "pine", "rice", "sushi", "bread", "toast", "cereal", "smoke", "fart", + "beer", "bear", "faucet", "pipe", "iron", "dork", "genius", "hunter", "farmer", "wiz", "witch", "churro", "donut", "shrimp", "sand", "pagoda", + "eel", "ant", "pants", "jeans", "socks", "sword", "fork", "pizza", "trap", "pork", "wort", "sack", "hawk", "rite", "tire", "dirt", "plum", + "ATM", "CD", "SUV", "TV", "abacus", "abbey", "abdomen", "ability", "absence", "abuse", "academy", "accent", "access", "accord", "account", "acetate", "acid", "acorn", + "acre", "acrylic", "act", "action", "actor", "actress", "ad", "adapter", "address", "admin", "admire", "adobe", "adult", "advance", "advent", "adverb", "advice", "adviser", + "affair", "affect", "afoul", "age", "agency", "agenda", "agent", "aglet", "agony", "aid", "aide", "aim", "air", "airbag", "airbus", "airfare", "airline", "airmail", "airman", + "airport", "airship", "alarm", "alb", "album", "alcohol", "alcove", "alder", "ale", "alert", "alfalfa", "algebra", "alias", "alibi", "alien", "alley", "alloy", "almanac", "almond", + "alpaca", "alpha", "altar", "alto", "amazon", "amber", "amenity", "amnesty", "amount", "anagram", "analog", "analogy", "analyst", "anarchy", "anatomy", "anchovy", "android", "angel", + "anger", "angina", "angle", "angora", "anguish", "animal", "anime", "anise", "ankle", "anklet", "annual", "anorak", "answer", "ant", "antigen", "antique", "antler", "antling", "anxiety", + "anybody", "anyone", "ape", "apology", "app", "apparel", "appeal", "apple", "apricot", "apron", "apse", "aquifer", "arcade", "arch", "archer", "area", "arena", "ark", "arm", "armoire", "armor", + "armour", "armpit", "armrest", "army", "array", "arrest", "arrival", "arrow", "art", "artery", "arthur", "article", "artist", "ascend", "ascent", "ascot", "ash", "ashram", "ashtray", "aside", + "aspect", "asphalt", "aspic", "assault", "asset", "assist", "atelier", "athlete", "atom", "atrium", "attack", "attempt", "attic", "auction", "audit", "aunt", "author", "auto", + "autumn", "avenue", "average", "avocado", "award", "awe", "axis", "azimuth", "babe", "baboon", "baby", "back", "back-up", "backup", "bacon", "badge", "badger", "bag", "bagel", "baggage", "baggie", + "baggy", "bagpipe", "bail", "bait", "bake", "baker", "bakery", "balance", "balcony", "ball", "ballet", "balloon", "ballot", "bamboo", "ban", "banana", "band", "bandana", "bangle", "banjo", "bank", + "banker", "banking", "banner", "banyan", "baobab", "bar", "barber", "bargain", "barge", "barium", "bark", "barley", "barn", "barrage", "barrel", "barrier", "base", "basics", "basil", "basin", "basis", + "basket", "bass", "bassoon", "bat", "bath", "bather", "bathtub", "batter", "battery", "batting", "battle", "bay", "bayou", "beach", "bead", "beak", "beam", "bean", "beanie", "bear", "beard", "beast", + "beastie", "beat", "beating", "beauty", "beaver", "beck", "bed", "bedrock", "bedroom", "bee", "beech", "beef", "beer", "beet", "beetle", "beggar", "begonia", "behalf", "behest", "behold", "being", + "belfry", "belief", "bell", "bellows", "belly", "belt", "bench", "bend", "benefit", "beret", "berry", "bet", "beyond", "bias", "bicycle", "bid", "bidder", "bidding", "bidet", "bijou", "bike", "bikini", + "bill", "billing", "billion", "bin", "biology", "biopsy", "biplane", "birch", "bird", "birth", "biscuit", "bit", "bite", "bitten", "bitter", "black", "bladder", "blade", "blame", "blank", "blanket", + "blast", "blazer", "blend", "blight", "blind", "blinker", "blister", "block", "blocker", "blog", "blogger", "blood", "bloom", "bloomer", "blossom", "blouse", "blow", "blowgun", "blue", "blush", "boar", + "board", "boat", "bob", "bobcat", "body", "bog", "bolero", "bolt", "bomb", "bomber", "bombing", "bond", "bonding", "bone", "bonfire", "bongo", "bonnet", "bonsai", "bonus", "book", "bookend", "booking", + "booklet", "boolean", "boom", "boon", "boost", "booster", "boot", "bootee", "bootie", "booty", "border", "bore", "bosom", "boss", "botany", "bother", "bottle", "bottom", "boudoir", "bough", "boulder", + "bouquet", "bout", "bow", "bower", "bowl", "bowler", "bowling", "bowtie", "box", "boxer", "boy", "boycott", "boyhood", "bra", "brace", "bracket", "brain", "brake", "bran", "branch", "brand", "brandy", + "brass", "bread", "break", "breast", "breath", "breeze", "brewer", "bribery", "brick", "bride", "bridge", "brief", "briefly", "briefs", "brink", "brisket", "broad", "broiler", "broker", "bronco", + "bronze", "brooch", "brood", "brook", "broom", "brother", "brow", "brown", "brownie", "browser", "brunch", "brush", "bubble", "buck", "bucket", "buckle", "bud", "buddy", "budget", "buffalo", "buffer", + "buffet", "bug", "buggy", "bugle", "builder", "bulb", "bulk", "bull", "bullet", "bump", "bumper", "bun", "bunch", "burden", "bureau", "burglar", "burial", "burn", "burning", "burrito", "burro", "burrow", + "burst", "bus", "bush", "bust", "bustle", "butane", "butcher", "butler", "butter", "button", "buy", "buyer", "buying", "buzz", "buzzard", "c-clamp", "cabana", "cabbage", "cabin", "cabinet", "cable", + "caboose", "cacao", "cactus", "caddy", "cadet", "cafe", "caftan", "cage", "cake", "calf", "caliber", "calibre", "calico", "call", "calm", "calorie", "camel", "cameo", "camera", "camp", "camper", "campus", + "can", "canal", "cancer", "candle", "candy", "cane", "cannon", "canoe", "canon", "canopy", "canteen", "canvas", "cap", "cape", "caper", "capital", "capon", "captain", "caption", "captor", "car", "carabao", + "caramel", "caravan", "carbon", "card", "care", "career", "cargo", "caribou", "carload", "carol", "carp", "carpet", "carport", "carrier", "carrot", "carry", "cart", "cartel", "carter", "cartoon", "carving", + "cascade", "case", "cash", "cashew", "cashier", "casino", "casket", "cassava", "cassock", "cast", "castle", "cat", "catch", "catcher", "cation", "catsup", "cattle", "causal", "cause", "caution", "cave", + "caviar", "cayenne", "ceiling", "celery", "cell", "cellar", "cello", "celsius", "cement", "census", "cent", "center", "centre", "century", "ceramic", "cereal", "chafe", "chain", "chair", "chaise", "chalet", + "chalice", "chalk", "chamber", "chance", "change", "channel", "chaos", "chap", "chapel", "chapter", "chard", "charge", "charger", "charity", "charm", "charset", "chart", "charter", "chasm", "chassis", + "chateau", "chatter", "check", "cheddar", "cheek", "cheer", "cheese", "cheetah", "chef", "chem", "cheque", "cherry", "chess", "chest", "chick", "chicken", "chicory", "chief", "child", "chili", "chill", + "chime", "chin", "chino", "chip", "chive", "chives", "choice", "choir", "choker", "chop", "chops", "chord", "chorus", "chow", "chowder", "chrome", "chub", "chuck", "chug", "church", "churn", "chutney", + "cicada", "cinder", "cinema", "circle", "circuit", "cirrus", "citizen", "citron", "citrus", "city", "claim", "clam", "clamp", "clan", "clank", "clarity", "clasp", "class", "classic", "clause", "clave", + "clavier", "claw", "clay", "cleaner", "cleat", "clef", "cleft", "cleric", "clerk", "click", "client", "cliff", "climate", "climb", "clinic", "clip", "clipper", "cloak", "clock", "clogs", "clone", "close", + "closet", "closing", "closure", "cloth", "clothes", "cloud", "clove", "clover", "cloves", "club", "clue", "cluster", "clutch", "coach", "coal", "coast", "coaster", "coat", "cob", "cobbler", "cobweb", "cock", + "cockpit", "cocoa", "coconut", "cod", "code", "codling", "codon", "coffee", "coffin", "cohort", "coil", "coin", "coke", "cold", "collar", "collard", "college", "colon", "colony", "color", "colt", "column", + "comb", "combat", "combine", "comedy", "comfort", "comic", "comics", "comma", "command", "comment", "common", "company", "compass", "complex", "compost", "con", "concept", "concern", "concert", "condor", + "conduct", "cone", "conga", "congo", "conifer", "consent", "consist", "console", "consul", "contact", "contact", "lens", "content", "contest", "context", "contour", "control", "convert", "cook", "cookie", + "cooking", "cop", "cop-out", "cope", "copper", "copy", "copying", "coral", "cord", "core", "cork", "corn", "corner", "cornet", "corps", "corral", "corsage", "cosset", "cost", "costume", "cot", "cottage", + "cotton", "couch", "cougar", "cough", "council", "counsel", "count", "counter", "country", "county", "couple", "coupon", "courage", "course", "court", "cousin", "cover", "cow", "cowbell", "cowboy", "coyote", + "crab", "crack", "cracker", "cradle", "craft", "crane", "cranky", "crap", "crash", "crate", "cravat", "craw", "crawdad", "crayon", "crazy", "cream", "creator", "creche", "credit", "creek", "creme", "brulee", "crepe", + "crest", "crew", "crewman", "crewmen", "cria", "crib", "cricket", "crime", "crisis", "crisp", "critic", "crocus", "crook", "crop", "cross", "crotch", "croup", "crow", "crowd", "crown", "crude", "cruelty", "cruise", + "crumb", "crunch", "crush", "crust", "cry", "crystal", "cub", "cube", "cuckoo", "cue", "cuisine", "culture", "culvert", "cup", "cupcake", "cupola", "curd", "cure", "curio", "curl", "curler", "currant", "current", + "curry", "curse", "cursor", "curtain", "curve", "cushion", "custard", "custody", "custom", "cut", "cuticle", "cutlet", "cutover", "cutting", "cycle", "cyclone", "cygnet", "cymbal", "cynic", "cyst", "dad", "daddy", + "dagger", "dahlia", "daikon", "daily", "dairy", "daisy", "dam", "damage", "dame", "damn", "dance", "dancer", "dancing", "danger", "dare", "dark", "darn", "dart", "dash", "data", "date", "dawn", "day", "daybed", "dead", + "deal", "dealer", "dealing", "dearest", "death", "debate", "debris", "debt", "debtor", "decade", "decency", "decimal", "deck", "decline", "decoder", "deduce", "deed", "deep", "deer", "default", "defeat", "defense", + "deficit", "degree", "delay", "delight", "demand", "demon", "demur", "den", "denim", "density", "dentist", "deposit", "depot", "depth", "deputy", "derby", "derrick", "descent", "desert", "design", "desire", "desk", + "desktop", "dessert", "destiny", "detail", "detour", "device", "devil", "dew", "dhow", "diadem", "diagram", "dial", "dialect", "diam", "diamond", "diaper", "diarist", "diary", "dibble", "dick", "dickey", "diction", + "die", "diesel", "diet", "diffuse", "dig", "digger", "digging", "digit", "dignity", "dill", "dime", "dimple", "diner", "dinghy", "dining", "dinner", "dioxide", "dip", "diploma", "dirndl", "dirt", "disco", "disdain", + "disease", "disgust", "dish", "disk", "display", "dispute", "divan", "diver", "divide", "divider", "divine", "diving", "divorce", "doc", "dock", "doctor", "doe", "dog", "doggie", "dogsled", "dogwood", "doing", "doll", + "dollar", "dollop", "dolman", "dolor", "dolphin", "domain", "dome", "donkey", "donor", "donut", "door", "doorway", "dory", "dose", "dot", "double", "doubt", "doubter", "dough", "down", "dozen", "draft", "drag", "dragon", + "drain", "drake", "drama", "drapes", "draw", "drawer", "drawing", "dream", "dreamer", "dredger", "dress", "dresser", "drill", "drink", "drive", "driver", "driving", "drizzle", "drop", "drug", "drum", "drummer", "drunk", + "dryer", "duck", "dud", "dude", "due", "duel", "dueling", "duffel", "dugout", "dump", "dump", "truck", "dune", "dune", "buggy", "dungeon", "durian", "dusk", "dust", "dust", "storm", "duster", "duty", "dwarf", "dwell", + "dynamo", "dynasty", "e-book", "e-mail", "eagle", "eaglet", "ear", "eardrum", "earplug", "earring", "earth", "ease", "easel", "east", "eating", "eaves", "echidna", "eclipse", "ecology", "economy", "eddy", "edge", + "edger", "edible", "editing", "edition", "editor", "eel", "effect", "effort", "egg", "egghead", "eggnog", "ego", "ejector", "elbow", "element", "elf", "elicit", "elite", "elixir", "elk", "ellipse", "elm", "elver", + "email", "emanate", "embassy", "embryo", "emerald", "emery", "emitter", "emotion", "empire", "employ", "emu", "enclave", "end", "endive", "enemy", "energy", "engine", "enigma", "enquiry", "entity", "entree", "entry", + "envy", "enzyme", "epee", "ephyra", "epic", "episode", "epoch", "eponym", "epoxy", "equal", "equinox", "equity", "era", "eraser", "erosion", "error", "escape", "escort", "essay", "essence", "estate", "estuary", "ethics", + "ethyl", "eve", "evening", "event", "evil", "ex-wife", "exam", "example", "excerpt", "excess", "excuse", "exhaust", "exhibit", "exile", "exit", "expense", "expert", "export", "expose", "extent", "extreme", "eye", "eyeball", + "eyebrow", "eyelash", "eyelid", "eyelids", "eyrie", "fabric", "face", "facet", "fact", "factor", "factory", "faculty", "fail", "failure", "fairy", "faith", "fall", "fallacy", "fame", "family", "fan", "fang", "fanny", "fantasy", + "farm", "farmer", "farming", "farrow", "fascia", "fashion", "fat", "fate", "father", "fatigue", "faucet", "fault", "fav", "fava", "favor", "fawn", "fax", "fear", "feast", "feather", "feature", "fedora", "fee", "feed", "feeding", + "feel", "feeling", "fellow", "felony", "female", "fen", "fence", "fencing", "fender", "feng", "fennel", "ferret", "ferry", "fetus", "few", "fiber", "fibre", "ficlet", "fiction", "fiddle", "field", "fiery", "fiesta", "fifth", "fig", + "fight", "fighter", "figure", "file", "filing", "fill", "fillet", "filly", "film", "filter", "filth", "final", "finance", "finding", "fine", "finer", "finger", "finish", "fir", "fire", "fireman", "firm", "first", "fish", "fishery", + "fishing", "fishnet", "fisting", "fit", "fitness", "fix", "fixture", "flag", "flair", "flame", "flan", "flanker", "flare", "flash", "flat", "flavor", "flax", "fleck", "fleece", "flesh", "flick", "flicker", "flight", "flint", "flock", + "flood", "floor", "floozie", "flour", "flow", "flower", "flu", "fluke", "flume", "flung", "flute", "fly", "flytrap", "foal", "foam", "fob", "focus", "fog", "fold", "folder", "folk", "fondue", "font", "food", "fool", "foot", "footage", + "forage", "forager", "foray", "force", "ford", "forearm", "forest", "forever", "forgery", "fork", "form", "formal", "format", "former", "formula", "fort", "forte", "fortune", "forum", "founder", "fourths", "fowl", "fox", "frame", + "fraud", "freak", "freckle", "freedom", "freezer", "freight", "frenzy", "freon", "fresco", "fridge", "friend", "fries", "frigate", "fright", "fringe", "fritter", "frock", "frog", "front", "frost", "frown", "fruit", "fry", "fvck", + "fuel", "fugato", "full", "fun", "fund", "funding", "funeral", "fur", "furnace", "furry", "futon", "future", "gadget", "gaffe", "gaffer", "gain", "gaiters", "gale", "gallery", "galley", "gallon", "game", "gaming", "gander", "gang", + "gap", "garage", "garb", "garbage", "garden", "garlic", "garment", "garter", "gas", "gasket", "gasp", "gate", "gateway", "gather", "gator", "gauge", "gavel", "gazebo", "gazelle", "gear", "geek", "gel", "gelatin", "gelding", "gem", + "gemsbok", "gender", "gene", "general", "genie", "genius", "genre", "geology", "gerbil", "gesture", "geyser", "gherkin", "ghost", "giant", "gift", "gig", "giggle", "ginger", "ginseng", "giraffe", "girdle", "girl", "git", "glacier", + "glance", "gland", "glass", "glasses", "glee", "glen", "glider", "gliding", "glimpse", "globe", "gloom", "glory", "glove", "glow", "glucose", "glue", "glut", "gnat", "gnu", "go-kart", "goal", "goat", "gobbler", "god", "goddess", "goggles", + "going", "gold", "golf", "gondola", "gong", "good", "goodbye", "goodie", "goose", "gopher", "gorilla", "gosling", "gossip", "gown", "grace", "grade", "graft", "grain", "gram", "grammar", "gran", "grand", "grandma", "grandpa", "granny", + "granola", "grant", "grape", "graph", "graphic", "grasp", "grass", "gravel", "gravity", "gravy", "gray", "grease", "greed", "green", "greens", "grenade", "grey", "grid", "grief", "grill", "grin", "grip", "gripper", "grit", "grocery", + "ground", "group", "grouper", "grouse", "grove", "growth", "grub", "guard", "guava", "guess", "guest", "guide", "guilder", "guilt", "guilty", "guinea", "guitar", "gum", "gumshoe", "gun", "gutter", "guy", "gym", "gymnast", "gyro", "habit", + "habitat", "hacksaw", "hail", "hair", "haircut", "hake", "half", "halibut", "hall", "hallway", "halt", "ham", "hammer", "hammock", "hamster", "hand", "handful", "handgun", "handle", "handsaw", "hanger", "harald", "harbor", "harbour", + "hardhat", "hare", "harm", "harmony", "harp", "harvest", "hash", "hashtag", "hassock", "haste", "hat", "hatbox", "hatchet", "hate", "hatred", "haunt", "haven", "havoc", "hawk", "hay", "haze", "hazel", "head", "health", "hearing", "hearsay", + "heart", "hearth", "heat", "heater", "heating", "heaven", "heavy", "hectare", "hedge", "heel", "heifer", "height", "heir", "helium", "hell", "hellcat", "hello", "helmet", "helo", "help", "hemp", "hen", "herb", "herbs", "hermit", "hero", + "heroine", "heron", "herring", "hexagon", "heyday", "hiccups", "hide", "high", "highway", "hike", "hiking", "hill", "hint", "hip", "hire", "hiring", "history", "hit", "hive", "hobbit", "hobby", "hockey", "hoe", "hog", "hold", "holder", + "hole", "holiday", "home", "homonym", "honesty", "honey", "honor", "honoree", "hood", "hoof", "hook", "hop", "hope", "hops", "horde", "horizon", "hormone", "horn", "hornet", "horror", "horse", "horst", "hose", "hosiery", "hospice", + "host", "hostel", "hostess", "hotdog", "hotel", "hound", "hour", "house", "housing", "hovel", "howard", "hub", "hubcap", "hubris", "hug", "hugger", "hull", "human", "hummus", "humor", "humour", "hundred", "hunger", "hunt", "hunter", + "hunting", "hurdle", "hurdler", "hurry", "hurt", "husband", "hut", "hutch", "hydrant", "hyena", "hype", "ice", "iceberg", "icicle", "icing", "icon", "icy", "id", "idea", "ideal", "idiom", "idiot", "igloo", "ikebana", "illegal", "illness", + "image", "impact", "impala", "import", "impress", "impulse", "in-joke", "in-laws", "inbox", "incense", "inch", "income", "index", "infancy", "infant", "infix", "influx", "info", "ingrate", "initial", "injury", "ink", "inlay", "inn", "input", + "inquiry", "insect", "insert", "inside", "insight", "instant", "integer", "intent", "invader", "inverse", "invite", "invoice", "iris", "iron", "irony", "island", "issue", "item", "ivory", "jack", "jackal", "jacket", "jade", "jaguar", "jail", + "jam", "jar", "jasmine", "jaw", "jazz", "jeans", "jeep", "jelly", "jerk", "jet", "jewel", "jewelry", "jicama", "jiffy", "job", "jockey", "joey", "jogging", "joint", "joke", "jot", "journal", "journey", "joy", "judge", "judo", "jug", "juice", + "jumbo", "jump", "jumper", "jungle", "junior", "junk", "junker", "junket", "jury", "justice", "jute", "kale", "karate", "kayak", "kazoo", "kebab", "keep", "keeper", "kendo", "kennel", "ketch", "ketchup", "kettle", "key", "kick", "kid", "kidney", + "kill", "killer", "killing", "kilt", "kimono", "kinase", "kind", "king", "kingdom", "kiosk", "kiss", "kit", "kitchen", "kite", "kitsch", "kitten", "kitty", "kiwi", "knee", "knife", "knight", "knock", "knot", "knuckle", "koala", "kumquat", "lab", + "label", "labor", "laborer", "labour", "lace", "lack", "lad", "ladder", "ladle", "lady", "ladybug", "lag", "lake", "lamb", "lambkin", "lament", "lamp", "lanai", "land", "landing", "lane", "lantern", "lap", "lapdog", "laptop", "larch", "lard", + "larder", "lark", "larva", "lasagna", "lashes", "last", "latency", "latex", "lathe", "latte", "latter", "laugh", "laundry", "lava", "law", "lawn", "lawsuit", "lawyer", "lay", "layer", "layout", "lead", "leader", "leading", "leaf", "league", + "leaker", "leap", "leash", "leather", "leave", "leaver", "lecture", "leek", "leeway", "left", "leg", "legacy", "legal", "legend", "legging", "legume", "leisure", "lemon", "lemur", "lender", "lending", "length", "lens", "lentil", "leopard", + "leprosy", "lesbian", "lesson", "letter", "lettuce", "level", "lever", "leveret", "liar", "liberty", "libido", "library", "licence", "license", "lid", "lie", "lieu", "life", "lift", "ligand", "light", "ligula", "lilac", "lily", "limb", "lime", + "limit", "limo", "line", "linen", "liner", "lining", "link", "linkage", "linseed", "lion", "lip", "lipid", "liquid", "liquor", "list", "listing", "litmus", "litter", "liver", "living", "lizard", "llama", "load", "loading", "loaf", "loafer", "loan", + "lobby", "lobster", "local", "lock", "locker", "locket", "locust", "lode", "loft", "log", "loggia", "logic", "login", "logo", "look", "lookout", "loop", "loquat", "lord", "loss", "lot", "lotion", "lottery", "lounge", "louse", "lout", "love", "lover", + "lox", "loyalty", "luck", "luggage", "lumber", "lunch", "lung", "lunge", "lust", "lute", "luxury", "lychee", "lycra", "lye", "lynx", "lyocell", "lyre", "lyrics", "lysine", "mRNA", "macaw", "machine", "macrame", "macro", "madam", "maestro", "maggot", + "magic", "magnet", "maid", "maiden", "mail", "mailbox", "mailer", "mailing", "mailman", "main", "maize", "major", "maker", "makeup", "making", "male", "malice", "mall", "mallard", "mallet", "mama", "mambo", "mammoth", "man", "manacle", "manager", + "manatee", "mandate", "mangle", "mango", "manhunt", "maniac", "mankind", "manner", "manor", "mansard", "mansion", "mantel", "mantle", "mantua", "many", "map", "maple", "mapping", "maracas", "marble", "march", "mare", "margin", "marimba", "marines", + "mark", "marker", "market", "markup", "marsh", "marten", "marxism", "mascara", "mask", "masonry", "mass", "massage", "mast", "master", "mastoid", "mat", "match", "mate", "math", "matrix", "matter", "mattock", "max", "maximum", "maybe", "mayor", + "meadow", "meal", "mean", "meander", "meaning", "means", "measles", "measure", "meat", "mecca", "med", "medal", "media", "median", "medium", "meet", "meeting", "melody", "melon", "member", "meme", "memo", "memory", "men", "menorah", "mention", "mentor", + "menu", "mercury", "merit", "mess", "message", "messy", "metal", "meteor", "meter", "methane", "method", "metric", "metro", "midden", "middle", "midline", "midwife", "might", "migrant", "mile", "mileage", "milk", "mill", "millet", "million", "mime", "mimosa", + "min", "mind", "mine", "mineral", "mini", "minibus", "minimum", "mining", "minion", "mink", "minnow", "minor", "mint", "minute", "miracle", "mirror", "misfit", "miss", "missile", "mission", "mist", "mistake", "mister", "miter", "mitten", "mix", "mixer", + "mixture", "moai", "moat", "mob", "mobile", "mobster", "mocha", "mochi", "mode", "model", "modem", "molar", "molding", "mole", "mom", "moment", "money", "monger", "monitor", "monk", "monkey", "monocle", "monsoon", "monster", "month", "mood", "moody", "moon", + "moose", "mop", "morale", "morbid", "morning", "moron", "morsel", "mortal", "mortise", "mosque", "most", "motel", "moth", "mother", "motion", "motive", "motor", "mound", "mouse", "mouser", "mousse", "mouth", "mouton", "mover", "movie", "mower", "mud", "muffin", + "mug", "mukluk", "mule", "murder", "muscat", "muscle", "museum", "music", "muskrat", "mussel", "mustard", "mutt", "mutton", "mystery", "myth", "nail", "name", "naming", "napkin", "nasal", "nation", "native", "nature", "neck", "necktie", "nectar", "need", + "needle", "neglect", "neon", "neonate", "nephew", "nerve", "nest", "net", "netball", "netbook", "netsuke", "network", "neuron", "news", "nexus", "nibble", "nicety", "niche", "nick", "nickel", "niece", "night", "ninja", "nit", "nobody", "nod", "node", "noir", + "noise", "noodle", "noodles", "noon", "norm", "normal", "north", "nose", "note", "notepad", "nothing", "notice", "notion", "nougat", "noun", "novel", "nudge", "nuke", "number", "numeric", "nun", "nurse", "nursery", "nursing", "nurture", "nut", "nutmeg", + "nylon", "nymph", "oak", "oar", "oasis", "oat", "oatmeal", "oats", "obesity", "obi", "object", "oboe", "ocean", "ocelot", "octagon", "octave", "octavo", "octet", "octopus", "odyssey", "oeuvre", "offence", "offense", "offer", "office", "officer", "offset", + "oil", "okra", "oldie", "oleo", "olive", "omega", "omelet", "onion", "online", "onset", "opening", "opera", "opinion", "opium", "opossum", "optimal", "option", "orange", "orator", "orchard", "orchid", "order", "ore", "oregano", "organ", "orient", "origin", + "osmosis", "osprey", "ostrich", "other", "otter", "ottoman", "ounce", "outback", "outcome", "outfit", "outlaw", "outlay", "outlet", "outline", "outlook", "output", "outrage", "outrun", "outset", "outside", "oval", "ovary", "oven", "owl", "owner", "ox", + "oxford", "oxygen", "oyster", "ozone", "pace", "pack", "package", "packet", "pad", "paddle", "paddock", "pagan", "page", "pagoda", "pail", "pain", "paint", "painter", "pair", "pajamas", "palace", "palate", "palm", "pan", "pancake", "panda", "panel", "panic", + "pannier", "panpipe", "pansy", "panther", "panties", "pantry", "pants", "panty", "papa", "papaya", "paper", "parable", "parade", "parcel", "pard", "pardon", "parent", "park", "parka", "parking", "parole", "parrot", "parser", "parsley", "parsnip", "part", + "partner", "party", "pass", "passage", "passing", "passion", "passive", "past", "pasta", "paste", "pastor", "pastry", "pasture", "pat", "patch", "pate", "patent", "path", "pathway", "patient", "patina", "patio", "patriot", "patrol", "patron", "pattern", + "patty", "pause", "paw", "pay", "payee", "payment", "payoff", "pea", "peace", "peach", "peacoat", "peacock", "peak", "peanut", "pear", "pearl", "peasant", "pecan", "pecker", "pedal", "peek", "peen", "peer", "pelican", "pelt", "pen", "penalty", "pence", + "pencil", "pendant", "penguin", "penis", "pennant", "penny", "pension", "peony", "people", "pepper", "percent", "perch", "perfume", "period", "permit", "perp", "person", "pest", "pet", "petal", "pew", "phase", "phone", "photo", "phrase", "physics", + "pianist", "piano", "piccolo", "pick", "pickax", "pickaxe", "picket", "pickle", "pickup", "picnic", "picture", "pie", "piece", "pier", "piety", "pig", "pigeon", "piglet", "pigpen", "pigsty", "pike", "pilaf", "pile", "pilgrim", "pill", "pillar", "pillbox", + "pillow", "pilot", "pimp", "pimple", "pin", "pine", "ping", "pink", "pinkie", "pinot", "pint", "pinto", "pinworm", "pioneer", "pipe", "piracy", "pirate", "piss", "pistol", "pit", "pita", "pitch", "pitcher", "pith", "pizza", "place", "placebo", "placode", + "plain", "plan", "plane", "planet", "plant", "planter", "planula", "plaster", "plastic", "plate", "platter", "play", "player", "plea", "pleat", "pledge", "plenty", "plier", "pliers", "plight", "plot", "plough", "plover", "plow", "plowman", "plug", "plugin", + "plum", "plumber", "plume", "plunger", "plywood", "pocket", "pod", "podcast", "poem", "poet", "poetry", "point", "poison", "poker", "pole", "polenta", "police", "policy", "polish", "poll", "polo", "polyp", "pomelo", "pompom", "poncho", "pond", "pony", "pool", + "poor", "pop", "popcorn", "poppy", "porch", "pork", "port", "porter", "portion", "post", "postage", "postbox", "poster", "postfix", "pot", "potato", "pottery", "potty", "pouch", "poultry", "pound", "poverty", "powder", "power", "prairie", "praise", "pray", + "prayer", "preface", "prefix", "prelude", "premier", "premise", "premium", "present", "press", "presume", "pretzel", "prey", "price", "pricing", "pride", "priest", "primary", "primate", "prince", "print", "printer", "prior", "prison", "privacy", "private", + "prize", "probe", "problem", "process", "proctor", "produce", "product", "profile", "profit", "program", "project", "promise", "prompt", "pronoun", "proof", "propane", "prophet", "prose", "protein", "protest", "prow", "prune", "pruner", "pub", "public", + "pudding", "puddle", "puffin", "pug", "puggle", "pulley", "pulse", "puma", "pump", "pumpkin", "pun", "punch", "pup", "pupa", "pupil", "puppet", "puppy", "puritan", "purity", "purple", "purpose", "purr", "purse", "pursuit", "push", "pusher", "put", "puzzle", + "pyramid", "quail", "quality", "quart", "quarter", "quartet", "quartz", "queen", "query", "quest", "quiche", "quiet", "quill", "quilt", "quince", "quinoa", "quit", "quiver", "quota", "quote", "rabbi", "rabbit", "raccoon", "race", "racer", "racing", "racism", + "racist", "rack", "radar", "radio", "radish", "raffle", "raft", "rag", "rage", "raid", "rail", "railing", "railway", "raiment", "rain", "rainbow", "rainy", "raise", "raisin", "rake", "rally", "ram", "rambler", "ramen", "ramie", "ranch", "rancher", "range", + "ranger", "rank", "rap", "rape", "rat", "rate", "rating", "ratio", "rations", "raven", "ravioli", "rawhide", "ray", "rayon", "razor", "reach", "read", "reader", "reading", "real", "reality", "realm", "reamer", "rear", "reason", "rebel", "reboot", "recall", + "receipt", "recess", "recipe", "record", "recruit", "red", "redhead", "reef", "reform", "refuge", "refund", "refusal", "refuse", "regard", "regime", "region", "regret", "reject", "relay", "release", "relief", "relish", "remains", "remark", "remnant", + "remote", "removal", "rent", "repair", "repeat", "replica", "reply", "report", "request", "resale", "rescue", "reserve", "reset", "residue", "resist", "resolve", "resort", "respect", "respite", "rest", "result", "resume", "retina", "retreat", "return", + "reunion", "reveal", "revenge", "revenue", "reverse", "review", "revival", "reward", "rhubarb", "rhyme", "rhythm", "rib", "ribbon", "rice", "riddle", "ride", "rider", "ridge", "riding", "rifle", "right", "rim", "ring", "riot", "rip", "ripple", "rise", "riser", + "risk", "rite", "ritual", "river", "rivulet", "road", "roadway", "roar", "roast", "robe", "robin", "robot", "rock", "rocker", "rocket", "rod", "role", "roll", "roller", "romaine", "romance", "roof", "room", "rooster", "root", "rope", "rose", "roster", "rostrum", + "round", "route", "router", "routine", "row", "rowboat", "rowing", "rubber", "rubbish", "rubric", "ruby", "ruckus", "ruffle", "rug", "rugby", "ruin", "rule", "ruler", "ruling", "rum", "rumor", "run", "runaway", "runner", "running", "runway", "rush", "rust", + "rye", "sabre", "sac", "sack", "saddle", "sadness", "safari", "safe", "safety", "saffron", "sage", "sail", "sailing", "sailor", "saint", "sake", "salad", "salami", "salary", "sale", "salmon", "salon", "saloon", "salsa", "salt", "salute", "samovar", "sampan", + "sample", "samurai", "sand", "sandal", "sandbar", "sanity", "sardine", "sari", "sarong", "sash", "satin", "satire", "sauce", "saucer", "sausage", "savage", "saving", "savings", "savior", "saviour", "savory", "saw", "scale", "scalp", "scam", "scanner", "scarf", + "scene", "scenery", "scent", "schema", "scheme", "scholar", "school", "science", "scooter", "scope", "score", "scorn", "scotch", "scout", "scow", "scrap", "scraper", "scratch", "screen", "screw", "scrim", "scrip", "script", "sea", "seabass", "seafood", + "seagull", "seal", "search", "seaside", "season", "seat", "seaweed", "second", "secrecy", "secret", "section", "sector", "seed", "seeder", "seeker", "seep", "segment", "seizure", "self", "seller", "selling", "seminar", "senate", "senator", "sender", "senior", + "sense", "sensor", "sepal", "sequel", "serial", "series", "sermon", "serum", "serval", "servant", "server", "service", "sesame", "session", "set", "setback", "setting", "settler", "sewer", "sex", "shack", "shackle", "shade", "shadow", "shaker", "shallot", + "shame", "shampoo", "shanty", "shape", "share", "shark", "shaw", "shawl", "shear", "sheath", "shed", "sheep", "sheet", "shelf", "shell", "shelter", "sherbet", "sherry", "shield", "shift", "shin", "shine", "shingle", "ship", "shipper", "shirt", "shjt", "shoat", + "shock", "shoe", "shoes", "shofar", "shoot", "shop", "shopper", "shore", "short", "shorts", "shot", "shout", "shovel", "show", "shower", "shred", "shrimp", "shrine", "sibling", "sick", "side", "sidecar", "siding", "siege", "sigh", "sight", "sign", "signal", + "signet", "signify", "signup", "silence", "silica", "silicon", "silk", "sill", "silly", "silo", "silver", "simple", "sin", "singer", "singing", "sink", "sip", "sir", "sister", "sitar", "site", "size", "skate", "skating", "skean", "ski", "skiing", "skill", + "skin", "skirt", "skull", "skunk", "sky", "skyline", "skywalk", "slang", "slash", "slate", "slave", "slavery", "slaw", "sled", "sledge", "sleep", "sleet", "sleuth", "slice", "slide", "slider", "slime", "slip", "slipper", "slope", "slot", "sloth", "slump", + "smell", "smile", "smith", "smock", "smog", "smoke", "smoking", "smolt", "snack", "snail", "snake", "snap", "snarl", "sneaker", "sneeze", "sniffle", "snob", "snorer", "snow", "snowman", "snuck", "snug", "snuggle", "soap", "soccer", "society", "sock", "socks", + "soda", "sofa", "soil", "soldier", "sole", "someone", "son", "sonar", "sonata", "song", "sonnet", "soot", "soprano", "sorbet", "sorghum", "sorrel", "sorrow", "sort", "soul", "sound", "soup", "source", "south", "sow", "soy", "soybean", "space", "spacing", + "spade", "span", "spandex", "spank", "spark", "sparrow", "spasm", "spat", "spatula", "spawn", "speaker", "spear", "spec", "special", "species", "speech", "speed", "spell", "spelt", "sphere", "sphynx", "spice", "spider", "spike", "spill", "spinach", "spine", + "spiral", "spirit", "spit", "spite", "spleen", "split", "sponge", "sponsor", "spool", "spoon", "spork", "sport", "spot", "spouse", "sprag", "sprat", "spray", "spread", "spree", "spring", "sprout", "spruce", "spud", "spume", "spur", "spy", "square", "squash", + "squid", "stab", "stable", "stack", "stadium", "staff", "stag", "stage", "stain", "stair", "stake", "stalk", "stall", "stamen", "stamina", "stamp", "stance", "stand", "star", "start", "starter", "state", "statin", "station", "statue", "status", "statute", + "stay", "steak", "stealth", "steam", "steel", "steeple", "stem", "stench", "stencil", "step", "stepson", "stereo", "stew", "steward", "stick", "sticker", "still", "sting", "stinger", "stitch", "stock", "stole", "stomach", "stone", "stool", "stop", "storage", + "store", "storey", "storm", "story", "stot", "stove", "strait", "strand", "strap", "straw", "stream", "street", "stress", "stretch", "strife", "strike", "string", "strip", "stripe", "strobe", "stroke", "strudel", "stucco", "stud", "student", "studio", "study", + "stuff", "stump", "sty", "style", "styling", "stylus", "sub", "subject", "subset", "subsidy", "suburb", "subway", "success", "suck", "sucker", "suede", "suet", "sugar", "suicide", "suit", "suite", "sulfur", "sultan", "sum", "summary", "summer", "summit", "sun", + "sunbeam", "sundae", "sunday", "sundial", "sunlamp", "sunrise", "sunroom", "sunset", "supper", "supply", "support", "supreme", "surface", "surge", "surgeon", "surgery", "surname", "surplus", "survey", "sushi", "suspect", "swallow", "swamp", "swan", "swath", + "sweat", "sweater", "sweets", "swell", "swim", "swine", "swing", "switch", "swivel", "sword", "symbol", "symptom", "synergy", "synod", "synonym", "syrup", "system", "t-shirt", "tab", "tabby", "table", "tablet", "tackle", "taco", "tactics", "tactile", + "tadpole", "tag", "tail", "tailbud", "tailor", "tale", "talent", "talk", "talking", "tamale", "tambour", "tan", "tandem", "tank", "tanker", "tankful", "tap", "tape", "tapioca", "target", "taro", "tart", "task", "tassel", "taste", "tatami", "tattler", "tattoo", + "tavern", "tax", "taxi", "taxicab", "tea", "teacher", "team", "teapot", "tear", "tech", "teen", "teepee", "tell", "teller", "temp", "temper", "temple", "tempo", "tenant", "tender", "tenet", "tennis", "tenor", "tension", "tensor", "tent", "tenth", "tepee", + "term", "termite", "terrace", "terror", "test", "testing", "text", "textual", "texture", "thanks", "thaw", "theater", "theft", "theism", "theme", "theory", "therapy", "thesis", "thief", "thigh", "thing", "thirst", "thistle", "thong", "thongs", "thorn", "thought", + "thread", "threat", "thrift", "thrill", "throat", "throne", "thrush", "thrust", "thug", "thumb", "thump", "thunder", "thyme", "tiara", "tic", "tick", "ticket", "tide", "tie", "tiger", "tights", "tile", "till", "tilt", "timbale", "timber", "time", "timeout", + "timer", "timing", "timpani", "tin", "tinkle", "tintype", "tip", "tire", "tissue", "title", "toad", "toast", "toaster", "tobacco", "today", "toe", "toenail", "toffee", "tofu", "tog", "toga", "toilet", "toll", "tom-tom", "tomato", "tomb", "ton", "tone", "tongue", + "tonic", "tonight", "tool", "toot", "tooth", "top", "top-hat", "topic", "topsail", "toque", "tornado", "torso", "torte", "tosser", "total", "tote", "touch", "tour", "tourism", "tourist", "towel", "tower", "town", "toy", "trace", "track", "tract", "tractor", + "trade", "trader", "trading", "traffic", "tragedy", "trail", "trailer", "train", "trainer", "trait", "tram", "tramp", "trance", "transit", "transom", "trap", "trash", "travel", "tray", "treat", "treaty", "tree", "trek", "trellis", "tremor", "trench", "trend", + "triad", "trial", "tribe", "trick", "trigger", "trim", "trinket", "trip", "tripod", "tritone", "triumph", "trolley", "troop", "trooper", "trophy", "trouble", "trout", "trove", "trowel", "truck", "trumpet", "trunk", "trust", "trustee", "truth", "try", + "tsunami", "tub", "tuba", "tube", "tuber", "tug", "tugboat", "tuition", "tulip", "tumbler", "tummy", "tuna", "tune", "tune-up", "tunic", "tunnel", "turban", "turf", "turkey", "turn", "turning", "turnip", "turret", "turtle", "tusk", "tussle", "tutu", + "tuxedo", "tweet", "twig", "twine", "twins", "twist", "twister", "twitter", "type", "typhoon", "ukulele", "uncle", "unibody", "uniform", "union", "unique", "unit", "unity", "update", "upgrade", "uplift", "upper", "upward", "urge", "urgency", "urn", "usage", + "use", "user", "usher", "usual", "utensil", "utility", "vaccine", "vacuum", "vagrant", "valance", "valley", "value", "vampire", "van", "vanadyl", "vane", "vanilla", "vanity", "variant", "variety", "vase", "vault", "veal", "vector", "vehicle", "veil", "vein", + "veldt", "vellum", "velvet", "vendor", "veneer", "venison", "venom", "venti", "venture", "venue", "veranda", "verb", "verdict", "verse", "version", "vertigo", "verve", "vessel", "vest", "vet", "veteran", "veto", "vibe", "vice", "victim", "victory", "video", + "view", "viewer", "villa", "village", "vine", "vinegar", "vintage", "vintner", "vinyl", "viola", "violet", "violin", "virtue", "virus", "visa", "viscose", "vise", "vision", "visit", "visitor", "visor", "vista", "visual", "vitamin", "vitro", "vivo", "vixen", + "vodka", "vogue", "voice", "void", "vol", "volcano", "volume", "vomit", "vote", "voter", "voting", "voyage", "vulture", "wad", "wafer", "waffle", "wage", "wagon", "waist", "wait", "waiter", "waiting", "waiver", "wake", "walk", "walker", "walking", "walkway", + "wall", "wallaby", "wallet", "walnut", "walrus", "wampum", "wannabe", "want", "war", "warden", "warfare", "warlock", "warlord", "warm-up", "warming", "warmth", "warning", "warrant", "warren", "warrior", "wasabi", "wash", "washer", "washtub", "wasp", "waste", + "wasting", "watch", "watcher", "water", "wave", "wax", "way", "wealth", "weapon", "wear", "weasel", "weather", "web", "webinar", "webmail", "webpage", "website", "wedding", "wedge", "weed", "weeder", "week", "weekend", "weight", "weird", "welcome", "welfare", + "well", "west", "western", "wet-bar", "wetland", "wetsuit", "whack", "whale", "wharf", "wheat", "wheel", "whelp", "whey", "whip", "whisker", "whiskey", "whisper", "whistle", "white", "whole", "whorl", "wick", "widget", "widow", "width", "wife", "wifi", "wild", + "will", "willow", "win", "wind", "windage", "window", "wine", "winery", "wing", "wingman", "wingtip", "wink", "winner", "winter", "wire", "wiretap", "wiring", "wisdom", "wiseguy", "wish", "wit", "witch", "witness", "wok", "wolf", "woman", "wombat", "wonder", + "wont", "wood", "wool", "woolens", "word", "wording", "work", "worker", "working", "workout", "world", "worm", "worry", "worship", "worth", "wound", "wrap", "wrapper", "wreck", "wrecker", "wren", "wrench", "wrinkle", "wrist", "writer", "writing", "wrong", + "yacht", "yahoo", "yak", "yam", "yang", "yard", "yarn", "yawl", "year", "yeast", "yellow", "yew", "yin", "yoga", "yogurt", "yoke", "yolk", "young", "youth", "yoyo", "yurt", "zampone", "zebra", "zen", "zephyr", "zero", "zinc", "zipper", "zither", "zombie", "zone", + "zoo", "zoology", + ]; - const NameGen = function () { - //let random1 = Math.floor(Math.random() * (adjectives.length + 1)); - let adjective = adjectives[rand(0, adjectives.length - 1)]; - let list2Limit = 16 - adjective.length; - let list2 = nouns.filter(function (element) { - return element.length < list2Limit; - }); + const NameGen = function () { + //let random1 = Math.floor(Math.random() * (adjectives.length + 1)); + let adjective = adjectives[rand(0, adjectives.length - 1)]; + let list2Limit = 16 - adjective.length; + let list2 = nouns.filter(function (element) { + return element.length < list2Limit; + }); - let noun = list2[rand(0, list2.length - 1)]; - let namechosen = adjective + noun; + let noun = list2[rand(0, list2.length - 1)]; + let namechosen = adjective + noun; - return namechosen.toLowerCase(); - }; + return namechosen.toLowerCase(); + }; - module.exports = NameGen; + module.exports = NameGen; })(module); diff --git a/libs/SoloPlay/Tools/OOGOverrides.js b/libs/SoloPlay/Tools/OOGOverrides.js index 56ae329d..8e6d1d5b 100644 --- a/libs/SoloPlay/Tools/OOGOverrides.js +++ b/libs/SoloPlay/Tools/OOGOverrides.js @@ -11,1244 +11,1244 @@ includeIfNotIncluded("OOG.js"); (function (global, original) { - global.login = function (...args) { - console.trace(); - return original.apply(this, args); - }; + global.login = function (...args) { + console.trace(); + return original.apply(this, args); + }; })([].filter.constructor("return this")(), login); const locations = {}; (function() { - let joinInfo; - - Starter.Config.StopOnDeadHardcore = false; - const Controls = require("../../modules/Control"); - const Overrides = require("../../modules/Override"); - const SoloEvents = (() => { - let { outOfGameCheck, check, gameInfo } = require("../Functions/SoloEvents"); - return { - check: check, - gameInfo: gameInfo, - outOfGameCheck: outOfGameCheck, - }; - })(); - - new Overrides.Override(Starter, Starter.receiveCopyData, function (orignal, mode, msg) { - switch (mode) { - case 1: // Join Info - console.log("Got Join Info"); - joinInfo = JSON.parse(msg); - - SoloEvents.gameInfo.gameName = joinInfo.gameName.toLowerCase(); - SoloEvents.gameInfo.gamePass = joinInfo.gamePass.toLowerCase(); - - break; - case 1638: - try { - /** @type {Map} */ - const classMap = new Map(); - classMap.set("ZON", "amazon"); - classMap.set("SOR", "sorceress"); - classMap.set("NEC", "necromancer"); - classMap.set("PAL", "paladin"); - classMap.set("BAR", "barbarian"); - classMap.set("DRU", "druid"); - classMap.set("SIN", "assassin"); - let [modePrefix, charClass] = me.profile.toUpperCase().split("-"); - - if (!modePrefix || !charClass || !(charClass = classMap.get(charClass.substring(0, 3)))) { - D2Bot.printToConsole( - "*** Invalid profile name ***\n" - + "Profile :: " + me.profile + " | Prefix: " + modePrefix + " | CharClass: " + charClass + "\n" - + "@see https://github.com/blizzhackers/kolbot-SoloPlay#possible-profile-names \n" - + "**********************************************************************************", - sdk.colors.D2Bot.Red - ); - CharData.delete(true); - throw new Error("Invalid profile name :: " + me.profile); - } - - let obj = JSON.parse(msg); - let infoTag = obj.Tag.trim().capitalize(true) || ""; - if (!infoTag) { - D2Bot.printToConsole( - "*** Invalid profile InfoTag ***\n" - + "Tag :: " + obj.Tag + "\n" - + "@see https://github.com/blizzhackers/kolbot-SoloPlay#available-characters-and-builds \n" - + "**********************************************************************************", - sdk.colors.D2Bot.Red - ); - throw new Error("Invalid profile InfoTag :: " + obj.Tag); - } - - Starter.profileInfo.profile = me.profile; - Starter.profileInfo.account = obj.Account; - if (Starter.profileInfo.account.length < 2 || Starter.profileInfo.account.length > 15) { - // console.warn("Invalid account name length"); - Starter.profileInfo.account = ""; - } - Starter.profileInfo.password = ""; - Starter.profileInfo.charName = obj.Character; - Starter.profileInfo.difficulty = obj.Difficulty; - obj.Realm = obj.Realm.toLowerCase(); - Starter.profileInfo.realm = ["east", "west"].includes(obj.Realm) ? "us" + obj.Realm : obj.Realm; - Starter.profileInfo.mode = Profile().type; - Starter.profileInfo.tag = infoTag; - - /** - * @example SCL-ZON123 - */ - Starter.profileInfo.hardcore = modePrefix.includes("HC"); // SC softcore = false - Starter.profileInfo.expansion = modePrefix.indexOf("CC") === -1; // not CC so not classic - true - Starter.profileInfo.ladder = modePrefix.indexOf("NL") === -1; // not NL so its ladder - true - Starter.profileInfo.charClass = charClass; - - if (["druid", "assassin"].includes(charClass) && !Starter.profileInfo.expansion) { - D2Bot.printToConsole( - "*** Invalid character class for mode ***\n" - + "CharClass :: " + charClass + "\n" - + "Expansion characters cannot be made in classic\n" - + "**********************************************************************************", - sdk.colors.D2Bot.Red - ); - throw new Error("Expansion characters cannot be made in classic"); - } - - { - let soloStats = CharData.getStats(); - let update = false; - // new profile - if (!soloStats.finalBuild) { - soloStats.finalBuild = Starter.profileInfo.tag; - CharData.updateData("me", soloStats); - } else if (soloStats.finalBuild !== Starter.profileInfo.tag) { - soloStats.finalBuild = Starter.profileInfo.tag; - update = true; - } - - if (soloStats.currentBuild !== soloStats.finalBuild - && !["Start", "Stepping", "Leveling"].includes(soloStats.currentBuild)) { - soloStats.currentBuild = "Leveling"; - } - - if (update) { - soloStats.charms = {}; - CharData.updateData("me", soloStats); - } - } - } catch (e) { - console.error(e); - D2Bot.stop(); - } - - break; - default: - orignal(mode, msg); - } - }).apply(); - - new Overrides.Override(Starter, Starter.scriptMsgEvent, function (orignal, msg) { - if (typeof msg !== "string") return; - if (msg === "event") { - SoloEvents.check = true; - } else if (msg === "diffChange") { - Starter.checkDifficulty(); - } else if (msg === "test") { - console.debug( - sdk.colors.Green - + "//-----------DataDump Start-----------//", - "\nÿc8ThreadData ::\n", getScript(true), - "\nÿc8GlobalVariabls ::\n", Object.keys(global), - "\n" + sdk.colors.Red - + "//-----------DataDump End-----------//" - ); - } else if (msg === "deleteAndRemake") { - Starter.deadCheck = true; - } else { - orignal(msg); - } - }).apply(); - - ControlAction.scrollDown = function () { - me.blockMouse = true; - for (let i = 0; i < 4; i++) { - sendKey(sdk.keys.code.DownArrow); - } - me.blockMouse = false; - }; - - ControlAction.makeCharacter = function (info) { - const NameGen = require("./NameGen"); - !info.charClass && (info.charClass = "barbarian"); - !info.charName && (info.charName = NameGen()); - me.blockMouse = true; - - let clickCoords = []; - let soloStats = CharData.getStats(); - let timeout = getTickCount() + Time.minutes(5); - - /** @type {Map 1) { - CharData.delete(false); - CharData.updateData("me", "finalBuild", Starter.profileInfo.tag); - Developer.logPerformance && Tracker.initialize(); - } - - D2Bot.updateStatus("Making Character: " + info.charName); - - // cycle until in lobby - while (getLocation() !== sdk.game.locations.Lobby && !me.ingame) { - switch (getLocation()) { - case sdk.game.locations.CharSelect: - case sdk.game.locations.CharSelectConnecting: - case sdk.game.locations.CharSelectNoChars: - let control = Controls.CharSelectCreate.control; - - // Create Character greyed out - if (control && control.disabled === sdk.game.controls.Disabled) { - me.blockMouse = false; - - return false; - } - - Controls.CharSelectCreate.click(); - - break; - case sdk.game.locations.LobbyPleaseWait: - D2Bot.restart(); // single player error on finding character - - break; - case sdk.game.locations.CharacterCreate: - clickCoords = coords.get(info.charClass.toLowerCase()) || coords.get("paladin"); - getControl().click(clickCoords[0], clickCoords[1]); - delay(500); - - break; - case sdk.game.locations.NewCharSelected: - // hardcore char warning - if (Controls.CharCreateHCWarningOk.control) { - Controls.CharCreateHCWarningOk.click(); - } else { - Controls.CharCreateCharName.setText(info.charName); - - if (!info.expansion) { - // @credit isid0re - if (["druid", "assassin"].includes(info.charClass)) { - D2Bot.printToConsole("Error in profile name. Expansion characters cannot be made in classic", sdk.colors.D2Bot.Red); - D2Bot.stop(); - - return false; - } - - Controls.CharCreateExpansion.click(); - } - - !info.ladder && Controls.CharCreateLadder.click(); - info.hardcore && Controls.CharCreateHardcore.click(); - Controls.BottomRightOk.click(); - } - - break; - case sdk.game.locations.OkCenteredErrorPopUp: - // char name exists (text box 4, 268, 320, 264, 120) - ControlAction.timeoutDelay("Character Name exists: " + info.charName + ". Making new Name.", 5e3); - Starter.profileInfo.charName = info.charName = NameGen(); - Controls.OkCentered.click(); - D2Bot.updateStatus("Making Character: " + info.charName); - - break; - default: - break; - } - - // Singleplayer loop break fix. - if (me.ingame) { - break; - } - - if (getTickCount() > timeout) { - D2Bot.printToConsole("Failed to create character: " + info.charName + " Location: " + getLocation(), sdk.colors.D2Bot.Red); - return false; - } - - delay(500); - } - - me.blockMouse = false; - D2Bot.setProfile(null, null, info.charName, "Normal"); - let gamename = Starter.profileInfo.charName.substring(0, 7) + "-" + Starter.randomString(3, false) + "-"; - DataFile.updateStats("gameName", gamename); - - return true; - }; - - ControlAction.findCharacter = function (info) { - let count = 0; - let singlePlayer = ![sdk.game.gametype.OpenBattlenet, sdk.game.gametype.BattleNet].includes(Profile().type); - // offline doesn't have a character limit cap - let cap = singlePlayer ? 999 : 24; - let tick = getTickCount(); - let firstCheck; - - while (getLocation() !== sdk.game.locations.CharSelect) { - if (getTickCount() - tick >= 5000) { - break; - } - - delay(25); - } - - // Wrong char select screen fix - if ([sdk.game.locations.CharSelect, sdk.game.locations.CharSelectNoChars].includes(getLocation())) { - hideConsole(); // seems to fix odd crash with single-player characters if the console is open to type in - let spCheck = Profile().type === sdk.game.profiletype.Battlenet; - let realmControl = !!Controls.CharSelectCurrentRealm.control; - if ((spCheck && !realmControl) || ((!spCheck && realmControl))) { - Controls.BottomLeftExit.click(); - return false; // what about a recursive call to loginCharacter? - } - } - - if (getLocation() === sdk.game.locations.CharSelectConnecting) { - if (!Starter.charSelectConnecting()) { - D2Bot.printToConsole("Stuck at connecting screen"); - D2Bot.restart(); - } - } - - // start from beginning of the char list - sendKey(sdk.keys.code.Home); - - while (getLocation() === sdk.game.locations.CharSelect && count < cap) { - let control = Controls.CharSelectCharInfo0.control; - - if (control) { - firstCheck = control.getText(); - do { - let text = control.getText(); - - if (text instanceof Array && typeof text[1] === "string") { - count++; - - if (String.isEqual(text[1], info.charName)) { - return control; - } - } - } while (count < cap && control.getNext()); - } - - // check for additional characters up to 24 (online) or 999 offline (no character limit cap) - if (count > 0 && count % 8 === 0) { - if (Controls.CharSelectChar6.click()) { - this.scrollDown(); - let check = Controls.CharSelectCharInfo0.control; - - if (firstCheck && check) { - let nameCheck = check.getText(); - - if (String.isEqual(firstCheck[1], nameCheck[1])) { - return false; - } - } - } - } else { - // no further check necessary - break; - } - } - - return false; - }; - - ControlAction.loginCharacter = function (info, startFromTop = true) { - me.blockMouse = true; - - // start from beginning of the char list - startFromTop && sendKey(sdk.keys.code.Home); - - MainLoop: - // cycle until in lobby or in game - while (getLocation() !== sdk.game.locations.Lobby) { - switch (getLocation()) { - case sdk.game.locations.SplashScreen: - case sdk.game.locations.MainMenu: - case sdk.game.locations.Login: - if (getLocation() === sdk.game.locations.MainMenu - && Profile().type === sdk.game.profiletype.SinglePlayer - && Controls.SinglePlayer.click()) { - Starter.checkDifficulty(); - break; - } else if (Starter.BNET) { - Starter.LocationEvents.login(); - } - - break; - case sdk.game.locations.CharSelect: - let control = ControlAction.findCharacter(info); - - if (control) { - control.click(); - Controls.BottomRightOk.click(); - me.blockMouse = false; - - if (getLocation() === sdk.game.locations.SelectDifficultySP) { - try { - Starter.LocationEvents.selectDifficultySP(); - Starter.locationTimeout(Time.seconds(3), sdk.game.locations.SelectDifficultySP); - } catch (err) { - break MainLoop; - } - - if (me.ingame) { - return true; - } - } - - return true; - } else if (getLocation() !== sdk.game.locations.CharSelect) { - break; - } - - break MainLoop; - case sdk.game.locations.CharSelectNoChars: - Controls.BottomLeftExit.click(); // why exit rather than returning false? - - break; - case sdk.game.locations.Disconnected: - case sdk.game.locations.OkCenteredErrorPopUp: - break MainLoop; - default: - break; - } - - delay(100); - } - - me.blockMouse = false; - - return false; - }; - - // need open bnet check - ControlAction.makeAccount = function (info) { - me.blockMouse = true; - - let tick; - let realms = { "uswest": 0, "useast": 1, "asia": 2, "europe": 3 }; - D2Bot.updateStatus("Making Account: " + info.account); - - // cycle until in empty char screen - while (getLocation() !== sdk.game.locations.CharSelectNoChars) { - switch (getLocation()) { - case sdk.game.locations.MainMenu: - ControlAction.clickRealm(realms[info.realm]); - Controls.BattleNet.click(); - - break; - case sdk.game.locations.Login: - Controls.CreateNewAccount.click(); - - break; - case sdk.game.locations.LoginError: - case sdk.game.locations.LoginUnableToConnect: - return false; - case sdk.game.locations.SplashScreen: - Controls.SplashScreen.click(); - - break; - case sdk.game.locations.MainMenuConnecting: - tick = getTickCount(); - - while (getLocation() === sdk.game.locations.MainMenuConnecting) { - if (getTickCount() - tick > 10000) { - Controls.LoginCancelWait.click(); - } - - delay(500); - } - - break; - case sdk.game.locations.CharacterCreate: - Controls.BottomLeftExit.click(); - - break; - case sdk.game.locations.OkCenteredErrorPopUp: - info.account = ""; - info.password = ""; - D2Bot.setProfile(info.account, info.password); - D2Bot.restart(true); - - break; - case sdk.game.locations.TermsOfUse: - Controls.TermsOfUseAgree.click(); - - break; - case sdk.game.locations.CreateNewAccount: - Controls.EnterAccountName.setText(info.account); - Controls.EnterAccountPassword.setText(info.password); - Controls.ConfirmPassword.setText(info.password); - Controls.BottomRightOk.click(); - - break; - case sdk.game.locations.PleaseRead: - Controls.PleaseReadOk.click(); - - break; - case sdk.game.locations.RegisterEmail: - if (Developer.setEmail.enabled - && (!Developer.setEmail.profiles.length || Developer.setEmail.profiles.includes(me.profile)) - && (!Developer.setEmail.realms.length || Developer.setEmail.realms.includes(Profile().gateway.toLowerCase()))) { - ControlAction.setEmail(); - } else { - Controls.EmailDontRegisterContinue.control ? Controls.EmailDontRegisterContinue.click() : Controls.EmailDontRegister.click(); - } - - break; - default: - break; - } - - delay(100); - } - - me.blockMouse = false; - - return true; - }; - - ControlAction.deleteAndRemakeChar = function (info) { - /** @type {Control} */ - let control = ControlAction.findCharacter(info); - if (!control) return false; - let cInfo = control.getText(); - console.debug(cInfo); - - me.blockMouse = true; - - control.click(); - Controls.CharSelectDelete.click(); - delay(500); - Controls.PopupYes.click(); - - me.blockMouse = false; - - // Delete old files - leaving csv file's for now as I don't think they interfere with the overlay - CharData.delete(true); - DataFile.create(); - DataFile.updateStats("handle", Starter.handle); - CharData.updateData("me", "finalBuild", Starter.profileInfo.tag); - Developer.logPerformance && Tracker.initialize(); - D2Bot.printToConsole("Deleted: " + info.charName + ". Now remaking...", sdk.colors.D2Bot.Gold); - Starter.deadCheck = false; - - return ControlAction.makeCharacter(Starter.profileInfo); - }; - - ControlAction.saveInfo = function (info) { - // Data-file already exists - if (FileTools.exists("logs/Kolbot-SoloPlay/" + info.realm + "/" + info.charClass + "-" + info.charClass + "-" + info.charName + ".json")) { - return; - } - - let folder; - - if (!FileTools.exists("logs/Kolbot-SoloPlay")) { - folder = dopen("logs"); - folder.create("Kolbot-SoloPlay"); - } - - if (!FileTools.exists("logs/Kolbot-SoloPlay/" + info.realm)) { - folder = dopen("logs/Kolbot-SoloPlay"); - folder.create(info.realm); - } - - if (!FileTools.exists("logs/Kolbot-SoloPlay/" + info.realm + "/" + info.charClass + "-" + info.charName + ".json")) { - FileTools.writeText("logs/Kolbot-SoloPlay/" + info.realm + "/" + info.charClass + "-" + info.charName + ".json", JSON.stringify(info)); - } - }; - - ControlAction.loginAccount = function (info) { - me.blockMouse = true; - - let locTick; - let tick = getTickCount(); - let realms = { "uswest": 0, "useast": 1, "asia": 2, "europe": 3 }; - - MainLoop: - while (true) { - switch (getLocation()) { - case sdk.game.locations.PreSplash: - break; - case sdk.game.locations.MainMenu: - info.realm && ControlAction.clickRealm(realms[info.realm]); - Controls.BattleNet.click(); - - break; - case sdk.game.locations.Login: - Controls.EnterAccountName.setText(info.account); - Controls.EnterAccountPassword.setText(info.password); - Controls.Login.click(); - - break; - case sdk.game.locations.LoginUnableToConnect: - case sdk.game.locations.RealmDown: - // Unable to connect, let the caller handle it. - me.blockMouse = false; - - return false; - case sdk.game.locations.CharSelect: - break MainLoop; - case sdk.game.locations.SplashScreen: - Controls.SplashScreen.click(); - - break; - case sdk.game.locations.CharSelectPleaseWait: - case sdk.game.locations.MainMenuConnecting: - case sdk.game.locations.CharSelectConnecting: - break; - case sdk.game.locations.CharSelectNoChars: - // make sure we're not on connecting screen - locTick = getTickCount(); - - while (getTickCount() - locTick < 3000 && getLocation() === sdk.game.locations.CharSelectNoChars) { - delay(25); - } - - if (getLocation() === sdk.game.locations.CharSelectConnecting) { - break; - } - - break MainLoop; // break if we're sure we're on empty char screen - case sdk.game.locations.Lobby: - case sdk.game.locations.LobbyChat: - // somehow we are in the lobby? - Control.LobbyQuit.click(); - - break; - default: - console.log(getLocation()); - - me.blockMouse = false; - - return false; - } - - if (getTickCount() - tick >= 20000) { - return false; - } - - delay(100); - } - - delay(1000); - - me.blockMouse = false; - - return getLocation() === sdk.game.locations.CharSelect || getLocation() === sdk.game.locations.CharSelectNoChars; - }; - - Starter.randomNumberString = function (len) { - len === undefined && (len = rand(2, 5)); - - let rval = ""; - let vals = "0123456789"; - - for (let i = 0; i < len; i += 1) { - rval += vals[rand(0, vals.length - 1)]; - } - - return rval; - }; - - Starter.charSelectConnecting = function () { - if (getLocation() === sdk.game.locations.CharSelectConnecting) { - // bugged? lets see if we can unbug it - // Click create char button on infinite "connecting" screen - Controls.CharSelectCreate.click() && delay(1000); - Controls.BottomLeftExit.click() && delay(1000); - - return (getLocation() !== sdk.game.locations.CharSelectConnecting); - } else { - return true; - } - }; - - Starter.BNET = ([sdk.game.profiletype.Battlenet, sdk.game.profiletype.OpenBattlenet].includes(Profile().type)); - Starter.LocationEvents.oogCheck = function () { - return (AutoMule.outOfGameCheck() || TorchSystem.outOfGameCheck() || Gambling.outOfGameCheck() || CraftingSystem.outOfGameCheck() || SoloEvents.outOfGameCheck()); - }; - - Starter.checkDifficulty = function () { - let setDiff = CharData.getStats().setDifficulty; - if (setDiff) { - console.debug(setDiff); - Starter.gameInfo.difficulty = setDiff; - } - }; - - Starter.LocationEvents.login = function () { - Starter.inGame && (Starter.inGame = false); - let pType = Profile().type; - - if (getLocation() === sdk.game.locations.MainMenu && Starter.firstRun - && pType === sdk.game.profiletype.SinglePlayer - && Controls.SinglePlayer.click()) { - return; - } - - // Wrong char select screen fix - if ([sdk.game.locations.CharSelect, sdk.game.locations.CharSelectNoChars].includes(getLocation())) { - hideConsole(); // seems to fix odd crash with single-player characters if the console is open to type in - let spCheck = pType === sdk.game.profiletype.Battlenet; - let realmControl = !!Controls.CharSelectCurrentRealm.control; - if ((spCheck && !realmControl) || ((!spCheck && realmControl))) { - Controls.BottomLeftExit.click(); - - return; - } - } - - // Multiple realm botting fix in case of R/D or disconnect - Starter.firstLogin && getLocation() === sdk.game.locations.Login && Controls.BottomLeftExit.click(); - - D2Bot.updateStatus("Logging In"); - - try { - // make battlenet accounts/characters - if (Starter.BNET) { - ControlAction.timeoutDelay("Login Delay", Starter.Config.DelayBeforeLogin * 1e3); - D2Bot.updateStatus("Logging in"); - // existing account - if (Starter.profileInfo.account !== "") { - try { - // ControlAction.loginAccount(Starter.profileInfo); - login(me.profile); - } catch (error) { - if (DataFile.getStats().AcctPswd) { - Starter.profileInfo.account = DataFile.getStats().AcctName; - Starter.profileInfo.password = DataFile.getStats().AcctPswd; - - for (let i = 0; i < 5; i++) { - if (ControlAction.loginAccount(Starter.profileInfo)) { - break; - } - - if (getLocation() === sdk.game.locations.CharSelectConnecting) { - if (Starter.charSelectConnecting()) { - break; - } - } - - ControlAction.timeoutDelay("Unable to Connect", Starter.Config.UnableToConnectDelay * 6e4); - Starter.profileInfo.account = DataFile.getStats().AcctName; - Starter.profileInfo.password = DataFile.getStats().AcctPswd; - } - } - } - } else { - // new account - if (Starter.profileInfo.account === "") { - if (Starter.Config.GlobalAccount || Starter.Config.GlobalAccountPassword) { - Starter.profileInfo.account = Starter.Config.GlobalAccount.length > 0 ? Starter.Config.GlobalAccount + Starter.randomNumberString(Starter.Config.AccountSuffixLength) : Starter.randomString(12, true); - Starter.profileInfo.password = Starter.Config.GlobalAccountPassword.length > 0 ? Starter.Config.GlobalAccountPassword : Starter.randomString(12, true); - - try { - if (Starter.profileInfo.account.length > 15) throw new Error("Account name exceeds MAXIMUM length (15). Please enter a shorter name or reduce the AccountSuffixLength under StarterConfig"); - if (Starter.profileInfo.password.length > 15) throw new Error("Password name exceeds MAXIMUM length (15). Please enter a shorter name under StarterConfig"); - } catch (e) { - D2Bot.printToConsole("Kolbot-SoloPlay: " + e.message, sdk.colors.D2Bot.Gold); - D2Bot.setProfile("", "", null, "Normal"); - D2Bot.stop(); - } - - console.log("Kolbot-SoloPlay :: Generated account information. " + (Starter.Config.GlobalAccount.length > 0 ? "Pre-defined " : "Random ") + "account used"); - console.log("Kolbot-SoloPlay :: Generated password information. " + (Starter.Config.GlobalAccountPassword.length > 0 ? "Pre-defined " : "Random ") + "password used"); - ControlAction.timeoutDelay("Generating Account Information", Starter.Config.DelayBeforeLogin * 1e3); - } else { - Starter.profileInfo.account = Starter.randomString(12, true); - Starter.profileInfo.password = Starter.randomString(12, true); - console.log("Generating Random Account Information"); - ControlAction.timeoutDelay("Generating Random Account Information", Starter.Config.DelayBeforeLogin * 1e3); - } - - if (ControlAction.makeAccount(Starter.profileInfo)) { - D2Bot.setProfile(Starter.profileInfo.account, Starter.profileInfo.password, null, "Normal"); - DataFile.updateStats("AcctName", Starter.profileInfo.account); - DataFile.updateStats("AcctPswd", Starter.profileInfo.password); - - return; - } else { - Starter.profileInfo.account = ""; - Starter.profileInfo.password = ""; - D2Bot.setProfile(Starter.profileInfo.account, Starter.profileInfo.password, null, "Normal"); - D2Bot.restart(true); - } - } - } - } else { - // SP/TCP characters - try { - if (getLocation() === sdk.game.locations.MainMenu) { - pType === sdk.game.profiletype.SinglePlayer - ? Controls.SinglePlayer.click() - : ControlAction.loginOtherMultiplayer(); - } - Starter.checkDifficulty(); - Starter.LocationEvents.charSelect(getLocation()); - } catch (err) { - console.error(err); - // Try to find the character and if that fails, make character - if (!ControlAction.findCharacter(Starter.profileInfo)) { - // Pop-up that happens when choosing a dead HC char - if (getLocation() === sdk.game.locations.OkCenteredErrorPopUp) { - Controls.OkCentered.click(); // Exit from that pop-up - D2Bot.printToConsole("Character died", sdk.colors.D2Bot.Red); - ControlAction.deleteAndRemakeChar(Starter.profileInfo); - } else { - // If make character fails, check how many characters are on that account - if (!ControlAction.makeCharacter(Starter.profileInfo)) { - // Account is full - if (ControlAction.getCharacters().length >= 18) { - D2Bot.printToConsole("Kolbot-SoloPlay: Account is full", sdk.colors.D2Bot.Orange); - D2Bot.stop(); - } - } - } - } - } - } - } catch (e) { - console.log(e + " " + getLocation()); - } - }; - - Starter.accountExists = false; - - Starter.LocationEvents.loginError = function () { - let string = ""; - let text = Controls.LoginErrorText.getText(); - - if (text) { - for (let i = 0; i < text.length; i++) { - string += text[i]; - - if (i !== text.length - 1) { - string += " "; - } - } - - switch (string) { - case getLocaleString(sdk.locale.text.UsernameIncludedIllegalChars): - case getLocaleString(sdk.locale.text.UsernameIncludedDisallowedwords): - case getLocaleString(sdk.locale.text.UsernameMustBeAtLeast): - case getLocaleString(sdk.locale.text.PasswordMustBeAtLeast): - case getLocaleString(sdk.locale.text.AccountMustBeAtLeast): - case getLocaleString(sdk.locale.text.PasswordCantBeMoreThan): - case getLocaleString(sdk.locale.text.AccountCantBeMoreThan): - case getLocaleString(sdk.locale.text.InvalidPassword): - D2Bot.printToConsole(string); - D2Bot.stop(); - - break; - case getLocaleString(5208): // Invalid account - D2Bot.updateStatus("Invalid Account Name"); - D2Bot.printToConsole("Invalid Account Name :: " + Starter.profileInfo.account); - Starter.profileInfo.account = ""; - Starter.profileInfo.password = ""; - D2Bot.setProfile(Starter.profileInfo.account, Starter.profileInfo.password); - D2Bot.restart(true); - - break; - case getLocaleString(5249): // Unable to create account - case getLocaleString(5239): // An account name already exists - if (!Starter.accountExists) { - Starter.accountExists = true; - Control.LoginErrorOk.click(); - delay(100); - Control.BottomLeftExit.click(); - Starter.LocationEvents.login(); - return; - } - D2Bot.updateStatus("Account name already exists :: " + Starter.profileInfo.account); - D2Bot.printToConsole("Account name already exists :: " + Starter.profileInfo.account); - Starter.profileInfo.account = ""; - Starter.profileInfo.password = ""; - D2Bot.setProfile(Starter.profileInfo.account, Starter.profileInfo.password); - - break; - case getLocaleString(sdk.locale.text.CdKeyInUseBy): - string += (" " + Controls.LoginCdKeyInUseBy.getText()); - D2Bot.printToConsole(Starter.gameInfo.mpq + " " + string, sdk.colors.D2Bot.Gold); - D2Bot.CDKeyInUse(); - - if (Starter.gameInfo.switchKeys) { - cdkeyError = true; - } else { - Controls.UnableToConnectOk.click(); - ControlAction.timeoutDelay("LoD key in use", Starter.Config.CDKeyInUseDelay * 6e4); - - return; - } - - break; - case getLocaleString(5202): // cd key intended for another product - case getLocaleString(10915): // lod key intended for another product - D2Bot.updateStatus("Invalid CDKey"); - D2Bot.printToConsole("Invalid CDKey: " + Starter.gameInfo.mpq, sdk.colors.D2Bot.Gold); - D2Bot.CDKeyDisabled(); - - if (Starter.gameInfo.switchKeys) { - ControlAction.timeoutDelay("Key switch delay", Starter.Config.SwitchKeyDelay * 1000); - D2Bot.restart(true); - } else { - D2Bot.stop(); - } - - break; - case getLocaleString(5199): - D2Bot.updateStatus("Disabled CDKey"); - D2Bot.printToConsole("Disabled CDKey: " + Starter.gameInfo.mpq, sdk.colors.D2Bot.Gold); - D2Bot.CDKeyDisabled(); - - if (Starter.gameInfo.switchKeys) { - ControlAction.timeoutDelay("Key switch delay", Starter.Config.SwitchKeyDelay * 1000); - D2Bot.restart(true); - } else { - D2Bot.stop(); - } - - break; - case getLocaleString(10913): - D2Bot.updateStatus("Disabled LoD CDKey"); - D2Bot.printToConsole("Disabled LoD CDKey: " + Starter.gameInfo.mpq, sdk.colors.D2Bot.Gold); - D2Bot.CDKeyDisabled(); - - if (Starter.gameInfo.switchKeys) { - ControlAction.timeoutDelay("Key switch delay", Starter.Config.SwitchKeyDelay * 1000); - D2Bot.restart(true); - } else { - D2Bot.stop(); - } - - break; - case getLocaleString(5347): - D2Bot.updateStatus("Disconnected from battle.net."); - D2Bot.printToConsole("Disconnected from battle.net."); - Controls.OkCentered.click(); - Controls.LoginErrorOk.click(); - - return; - default: - D2Bot.updateStatus("Login Error"); - D2Bot.printToConsole("Login Error - " + string); - - if (Starter.gameInfo.switchKeys) { - ControlAction.timeoutDelay("Key switch delay", Starter.Config.SwitchKeyDelay * 1000); - D2Bot.restart(true); - } else { - D2Bot.stop(); - } - - break; - } - } - - Controls.LoginErrorOk.click(); - delay(1000); - Controls.BottomLeftExit.click(); - }; - - Starter.LocationEvents.charSelect = function (loc) { - let string = ""; - let text = Controls.CharSelectError.getText(); - - if (text) { - for (let i = 0; i < text.length; i++) { - string += text[i]; - - if (i !== text.length - 1) { - string += " "; - } - } - - // CDKey disabled from realm play - if (string === getLocaleString(sdk.locale.text.CdKeyDisabledFromRealm)) { - D2Bot.updateStatus("Realm Disabled CDKey"); - D2Bot.printToConsole("Realm Disabled CDKey: " + Starter.gameInfo.mpq, sdk.colors.D2Bot.Gold); - D2Bot.CDKeyDisabled(); - - if (Starter.gameInfo.switchKeys) { - ControlAction.timeoutDelay("Key switch delay", Starter.Config.SwitchKeyDelay * 1000); - D2Bot.restart(true); - } else { - D2Bot.stop(); - } - } - } - - if (Starter.deadCheck && ControlAction.deleteAndRemakeChar(Starter.profileInfo)) { - Starter.deadCheck = false; - - return; - } - - if (!Controls.CharSelectCreate.control) { - // We aren't in the right place - return; - } - - if (Object.keys(Starter.profileInfo).length) { - if (!ControlAction.findCharacter(Starter.profileInfo)) { - let currLoc = getLocation(); - if (Starter.profileInfo.charName === DataFile.getObj().name - && currLoc !== sdk.game.locations.CharSelectNoChars - && ControlAction.getCharacters().length === 0) { - ControlAction.timeoutDelay("[R/D] Character not found ", 18e4); - D2Bot.printToConsole("Avoid Creating New Character - Restart"); - D2Bot.restart(); - } else { - if (!ControlAction.makeCharacter(Starter.profileInfo)) { - if (ControlAction.getCharacters().length >= 18) { - D2Bot.printToConsole("Kolbot-SoloPlay: Account is full", sdk.colors.D2Bot.Red); - D2Bot.stop(); - } - } - } - } else { - ControlAction.loginCharacter(Starter.profileInfo, false); - } - } - - if (!Starter.locationTimeout(Starter.Config.ConnectingTimeout * 1e3, loc)) { - Controls.BottomLeftExit.click(); - Starter.gameInfo.rdBlocker && D2Bot.restart(); - } - }; - - Starter.LocationEvents.lobbyChat = function () { - D2Bot.updateStatus("Lobby Chat"); - Starter.lastGameStatus === "pending" && (Starter.gameCount += 1); - - if (Starter.inGame || Starter.gameInfo.error) { - !Starter.gameStart && (Starter.gameStart = DataFile.getStats().ingameTick); - - if (getTickCount() - Starter.gameStart < Starter.Config.MinGameTime * 1e3) { - ControlAction.timeoutDelay("Min game time wait", Starter.Config.MinGameTime * 1e3 + Starter.gameStart - getTickCount()); - } - } - - if (Starter.inGame) { - if (oogCheck()) return; - - console.log("updating runs"); - D2Bot.updateRuns(); - - Starter.gameCount += 1; - Starter.lastGameStatus = "ready"; - Starter.inGame = false; - - if (Starter.Config.ResetCount && Starter.gameCount > Starter.Config.ResetCount) { - Starter.gameCount = 1; - DataFile.updateStats("runs", Starter.gameCount); - } - - Starter.chanInfo.afterMsg = Starter.Config.AfterGameMessage; - - if (Starter.chanInfo.afterMsg) { - !Array.isArray(Starter.chanInfo.afterMsg) && (Starter.chanInfo.afterMsg = [Starter.chanInfo.afterMsg]); - - for (let i = 0; i < Starter.chanInfo.afterMsg.length; i++) { - Starter.sayMsg(Starter.chanInfo.afterMsg[i]); - delay(500); - } - } - } - - if (!Starter.chatActionsDone) { - Starter.chatActionsDone = true; - - Starter.chanInfo.joinChannel = Starter.Config.JoinChannel; - Starter.chanInfo.firstMsg = Starter.Config.FirstJoinMessage; - - if (Starter.chanInfo.joinChannel) { - !Array.isArray(Starter.chanInfo.joinChannel) && (Starter.chanInfo.joinChannel = [Starter.chanInfo.joinChannel]); - !Array.isArray(Starter.chanInfo.firstMsg) && (Starter.chanInfo.firstMsg = [Starter.chanInfo.firstMsg]); - - for (let i = 0; i < Starter.chanInfo.joinChannel.length; i++) { - ControlAction.timeoutDelay("Chat delay", Starter.Config.ChatActionsDelay * 1e3); - - if (ControlAction.joinChannel(Starter.chanInfo.joinChannel[i])) { - Starter.useChat = true; - } else { - console.log("ÿc1Unable to join channel, disabling chat messages."); - - Starter.useChat = false; - } - - if (Starter.chanInfo.firstMsg[i] !== "") { - Starter.sayMsg(Starter.chanInfo.firstMsg[i]); - delay(500); - } - } - } - } - - // Announce game - Starter.chanInfo.announce = Starter.Config.AnnounceGames; - - if (Starter.chanInfo.announce) { - Starter.sayMsg("Next game is " + Starter.gameInfo.gameName + Starter.gameCount + (Starter.gameInfo.gamePass === "" ? "" : "//" + Starter.gameInfo.gamePass)); - } - - Starter.LocationEvents.openCreateGameWindow(); - }; - - const oogCheck = () => ( - AutoMule.outOfGameCheck() || TorchSystem.outOfGameCheck() - || Gambling.outOfGameCheck() || CraftingSystem.outOfGameCheck() || SoloEvents.outOfGameCheck() - ); - - locations[sdk.game.locations.PreSplash] = () => ControlAction.click(); - locations[sdk.game.locations.GatewaySelect] = () => Controls.GatewayCancel.click(); - locations[sdk.game.locations.SplashScreen] = () => Starter.LocationEvents.login(); - locations[sdk.game.locations.MainMenu] = () => Starter.LocationEvents.login(); - locations[sdk.game.locations.Login] = () => Starter.LocationEvents.login(); - locations[sdk.game.locations.OtherMultiplayer] = () => Starter.LocationEvents.otherMultiplayerSelect(); - locations[sdk.game.locations.TcpIp] = () => Profile().type === sdk.game.profiletype.TcpIpHost ? Controls.TcpIpHost.click() : Controls.TcpIpCancel.click(); - locations[sdk.game.locations.TcpIpEnterIp] = () => Controls.TcpIpCancel.click(); - locations[sdk.game.locations.LoginError] = () => Starter.LocationEvents.loginError(); - locations[sdk.game.locations.LoginUnableToConnect] = () => Starter.LocationEvents.unableToConnect(); - locations[sdk.game.locations.TcpIpUnableToConnect] = () => Starter.LocationEvents.unableToConnect(); - locations[sdk.game.locations.CdKeyInUse] = () => Starter.LocationEvents.loginError(); - locations[sdk.game.locations.InvalidCdKey] = () => Starter.LocationEvents.loginError(); - locations[sdk.game.locations.RealmDown] = () => Starter.LocationEvents.realmDown(); - locations[sdk.game.locations.Disconnected] = () => { - ControlAction.timeoutDelay("Disconnected", 3000); - Controls.OkCentered.click(); - }; - locations[sdk.game.locations.RegisterEmail] = () => Controls.EmailDontRegisterContinue.control ? Controls.EmailDontRegisterContinue.click() : Controls.EmailDontRegister.click(); - locations[sdk.game.locations.MainMenuConnecting] = (loc) => !Starter.locationTimeout(Starter.Config.ConnectingTimeout * 1e3, loc) && Controls.LoginCancelWait.click(); - locations[sdk.game.locations.CharSelectPleaseWait] = (loc) => !Starter.locationTimeout(Starter.Config.PleaseWaitTimeout * 1e3, loc) && Controls.OkCentered.click(); - locations[sdk.game.locations.CharSelect] = (loc) => Starter.LocationEvents.charSelect(loc); - locations[sdk.game.locations.CharSelectConnecting] = (loc) => Starter.LocationEvents.charSelect(loc); - locations[sdk.game.locations.CharSelectNoChars] = (loc) => Starter.LocationEvents.charSelect(loc); - locations[sdk.game.locations.SelectDifficultySP] = () => Starter.LocationEvents.selectDifficultySP(); - locations[sdk.game.locations.CharacterCreate] = (loc) => !Starter.locationTimeout(5e3, loc) && Controls.BottomLeftExit.click(); - locations[sdk.game.locations.ServerDown] = () => { - ControlAction.timeoutDelay("Server Down", Time.minutes(5)); - Controls.OkCentered.click(); - }; - locations[sdk.game.locations.LobbyPleaseWait] = (loc) => !Starter.locationTimeout(Starter.Config.PleaseWaitTimeout * 1e3, loc) && Controls.OkCentered.click(); - locations[sdk.game.locations.Lobby] = () => { - D2Bot.updateStatus("Lobby"); - ControlAction.saveInfo(Starter.profileInfo); - - me.blockKeys = false; - - !Starter.firstLogin && (Starter.firstLogin = true); - Starter.lastGameStatus === "pending" && (Starter.gameCount += 1); - - if (Starter.Config.PingQuitDelay && Starter.pingQuit) { - ControlAction.timeoutDelay("Ping Delay", Starter.Config.PingQuitDelay * 1e3); - Starter.pingQuit = false; - } - - if (Starter.Config.JoinChannel !== "" && Controls.LobbyEnterChat.click()) return; - - if (Starter.inGame || Starter.gameInfo.error) { - !Starter.gameStart && (Starter.gameStart = DataFile.getStats().ingameTick); - - if (getTickCount() - Starter.gameStart < Starter.Config.MinGameTime * 1e3 && !joinInfo) { - ControlAction.timeoutDelay("Min game time wait", Starter.Config.MinGameTime * 1e3 + Starter.gameStart - getTickCount()); - } - } - - if (Starter.inGame) { - if (oogCheck()) return; - - D2Bot.updateRuns(); - - Starter.gameCount += 1; - Starter.lastGameStatus = "ready"; - Starter.inGame = false; - - if (Starter.Config.ResetCount && Starter.gameCount > Starter.Config.ResetCount) { - Starter.gameCount = 1; - DataFile.updateStats("runs", Starter.gameCount); - } - } - - Starter.LocationEvents.openCreateGameWindow(); - }; - locations[sdk.game.locations.LobbyChat] = () => Starter.LocationEvents.lobbyChat(); - locations[sdk.game.locations.CreateGame] = (loc) => { - ControlAction.timeoutDelay("Create Game Delay", Starter.Config.DelayBeforeLogin * 1e3); - D2Bot.updateStatus("Creating Game"); - - if (typeof Starter.Config.CharacterDifference === "number") { - Controls.CharacterDifference.disabled === sdk.game.controls.Disabled && Controls.CharacterDifferenceButton.click(); - Controls.CharacterDifference.setText(Starter.Config.CharacterDifference.toString()); - } else if (!Starter.Config.CharacterDifference && Controls.CharacterDifference.disabled === 5) { - Controls.CharacterDifferenceButton.click(); - } - - typeof Starter.Config.MaxPlayerCount === "number" && Controls.MaxPlayerCount.setText(Starter.Config.MaxPlayerCount.toString()); - - D2Bot.requestGameInfo(); - delay(500); - - // todo - really don't need use profiles set difficulty for online. Only single player so re-write difficulty stuff - Starter.checkDifficulty(); - - Starter.gameInfo.gameName = DataFile.getStats().gameName; - Starter.gameInfo.gamePass = Starter.randomString(5, true); - - switch (true) { - case Starter.gameInfo.gameName === "": - case Starter.gameInfo.gameName === "Name": - Starter.gameInfo.gameName = Starter.profileInfo.charName.substring(0, 7) + "-" + Starter.randomString(3, false) + "-"; - - break; - } - - // FTJ handler - if (Starter.lastGameStatus === "pending") { - Starter.isUp = "no"; - - D2Bot.printToConsole("Failed to create game"); - ControlAction.timeoutDelay("FTJ delay", Starter.Config.FTJDelay * 1e3); - D2Bot.updateRuns(); - } - - ControlAction.createGame((Starter.gameInfo.gameName + Starter.gameCount), Starter.gameInfo.gamePass, Starter.gameInfo.difficulty, Starter.Config.CreateGameDelay * 1000); - Starter.lastGameStatus = "pending"; - Starter.setNextGame(Starter.gameInfo); - Starter.locationTimeout(10000, loc); - }; - locations[sdk.game.locations.GameNameExists] = () => { - Controls.CreateGameWindow.click(); - Starter.gameCount += 1; - Starter.lastGameStatus = "ready"; - }; - locations[sdk.game.locations.WaitingInLine] = () => Starter.LocationEvents.waitingInLine(); - locations[sdk.game.locations.JoinGame] = () => Starter.LocationEvents.openCreateGameWindow(); - locations[sdk.game.locations.Ladder] = () => Starter.LocationEvents.openCreateGameWindow(); - locations[sdk.game.locations.ChannelList] = () => Starter.LocationEvents.openCreateGameWindow(); - locations[sdk.game.locations.LobbyLostConnection] = () => { - ControlAction.timeoutDelay("LostConnection", 3000); - Controls.OkCentered.click(); - }; - locations[sdk.game.locations.GameDoesNotExist] = () => Starter.LocationEvents.gameDoesNotExist(); - locations[sdk.game.locations.GameIsFull] = () => Starter.LocationEvents.openCreateGameWindow(); + let joinInfo; + + Starter.Config.StopOnDeadHardcore = false; + const Controls = require("../../modules/Control"); + const Overrides = require("../../modules/Override"); + const SoloEvents = (() => { + let { outOfGameCheck, check, gameInfo } = require("../Functions/SoloEvents"); + return { + check: check, + gameInfo: gameInfo, + outOfGameCheck: outOfGameCheck, + }; + })(); + + new Overrides.Override(Starter, Starter.receiveCopyData, function (orignal, mode, msg) { + switch (mode) { + case 1: // Join Info + console.log("Got Join Info"); + joinInfo = JSON.parse(msg); + + SoloEvents.gameInfo.gameName = joinInfo.gameName.toLowerCase(); + SoloEvents.gameInfo.gamePass = joinInfo.gamePass.toLowerCase(); + + break; + case 1638: + try { + /** @type {Map} */ + const classMap = new Map(); + classMap.set("ZON", "amazon"); + classMap.set("SOR", "sorceress"); + classMap.set("NEC", "necromancer"); + classMap.set("PAL", "paladin"); + classMap.set("BAR", "barbarian"); + classMap.set("DRU", "druid"); + classMap.set("SIN", "assassin"); + let [modePrefix, charClass] = me.profile.toUpperCase().split("-"); + + if (!modePrefix || !charClass || !(charClass = classMap.get(charClass.substring(0, 3)))) { + D2Bot.printToConsole( + "*** Invalid profile name ***\n" + + "Profile :: " + me.profile + " | Prefix: " + modePrefix + " | CharClass: " + charClass + "\n" + + "@see https://github.com/blizzhackers/kolbot-SoloPlay#possible-profile-names \n" + + "**********************************************************************************", + sdk.colors.D2Bot.Red + ); + CharData.delete(true); + throw new Error("Invalid profile name :: " + me.profile); + } + + let obj = JSON.parse(msg); + let infoTag = obj.Tag.trim().capitalize(true) || ""; + if (!infoTag) { + D2Bot.printToConsole( + "*** Invalid profile InfoTag ***\n" + + "Tag :: " + obj.Tag + "\n" + + "@see https://github.com/blizzhackers/kolbot-SoloPlay#available-characters-and-builds \n" + + "**********************************************************************************", + sdk.colors.D2Bot.Red + ); + throw new Error("Invalid profile InfoTag :: " + obj.Tag); + } + + Starter.profileInfo.profile = me.profile; + Starter.profileInfo.account = obj.Account; + if (Starter.profileInfo.account.length < 2 || Starter.profileInfo.account.length > 15) { + // console.warn("Invalid account name length"); + Starter.profileInfo.account = ""; + } + Starter.profileInfo.password = ""; + Starter.profileInfo.charName = obj.Character; + Starter.profileInfo.difficulty = obj.Difficulty; + obj.Realm = obj.Realm.toLowerCase(); + Starter.profileInfo.realm = ["east", "west"].includes(obj.Realm) ? "us" + obj.Realm : obj.Realm; + Starter.profileInfo.mode = Profile().type; + Starter.profileInfo.tag = infoTag; + + /** + * @example SCL-ZON123 + */ + Starter.profileInfo.hardcore = modePrefix.includes("HC"); // SC softcore = false + Starter.profileInfo.expansion = modePrefix.indexOf("CC") === -1; // not CC so not classic - true + Starter.profileInfo.ladder = modePrefix.indexOf("NL") === -1; // not NL so its ladder - true + Starter.profileInfo.charClass = charClass; + + if (["druid", "assassin"].includes(charClass) && !Starter.profileInfo.expansion) { + D2Bot.printToConsole( + "*** Invalid character class for mode ***\n" + + "CharClass :: " + charClass + "\n" + + "Expansion characters cannot be made in classic\n" + + "**********************************************************************************", + sdk.colors.D2Bot.Red + ); + throw new Error("Expansion characters cannot be made in classic"); + } + + { + let soloStats = CharData.getStats(); + let update = false; + // new profile + if (!soloStats.finalBuild) { + soloStats.finalBuild = Starter.profileInfo.tag; + CharData.updateData("me", soloStats); + } else if (soloStats.finalBuild !== Starter.profileInfo.tag) { + soloStats.finalBuild = Starter.profileInfo.tag; + update = true; + } + + if (soloStats.currentBuild !== soloStats.finalBuild + && !["Start", "Stepping", "Leveling"].includes(soloStats.currentBuild)) { + soloStats.currentBuild = "Leveling"; + } + + if (update) { + soloStats.charms = {}; + CharData.updateData("me", soloStats); + } + } + } catch (e) { + console.error(e); + D2Bot.stop(); + } + + break; + default: + orignal(mode, msg); + } + }).apply(); + + new Overrides.Override(Starter, Starter.scriptMsgEvent, function (orignal, msg) { + if (typeof msg !== "string") return; + if (msg === "event") { + SoloEvents.check = true; + } else if (msg === "diffChange") { + Starter.checkDifficulty(); + } else if (msg === "test") { + console.debug( + sdk.colors.Green + + "//-----------DataDump Start-----------//", + "\nÿc8ThreadData ::\n", getScript(true), + "\nÿc8GlobalVariabls ::\n", Object.keys(global), + "\n" + sdk.colors.Red + + "//-----------DataDump End-----------//" + ); + } else if (msg === "deleteAndRemake") { + Starter.deadCheck = true; + } else { + orignal(msg); + } + }).apply(); + + ControlAction.scrollDown = function () { + me.blockMouse = true; + for (let i = 0; i < 4; i++) { + sendKey(sdk.keys.code.DownArrow); + } + me.blockMouse = false; + }; + + ControlAction.makeCharacter = function (info) { + const NameGen = require("./NameGen"); + !info.charClass && (info.charClass = "barbarian"); + !info.charName && (info.charName = NameGen()); + me.blockMouse = true; + + let clickCoords = []; + let soloStats = CharData.getStats(); + let timeout = getTickCount() + Time.minutes(5); + + /** @type {Map 1) { + CharData.delete(false); + CharData.updateData("me", "finalBuild", Starter.profileInfo.tag); + Developer.logPerformance && Tracker.initialize(); + } + + D2Bot.updateStatus("Making Character: " + info.charName); + + // cycle until in lobby + while (getLocation() !== sdk.game.locations.Lobby && !me.ingame) { + switch (getLocation()) { + case sdk.game.locations.CharSelect: + case sdk.game.locations.CharSelectConnecting: + case sdk.game.locations.CharSelectNoChars: + let control = Controls.CharSelectCreate.control; + + // Create Character greyed out + if (control && control.disabled === sdk.game.controls.Disabled) { + me.blockMouse = false; + + return false; + } + + Controls.CharSelectCreate.click(); + + break; + case sdk.game.locations.LobbyPleaseWait: + D2Bot.restart(); // single player error on finding character + + break; + case sdk.game.locations.CharacterCreate: + clickCoords = coords.get(info.charClass.toLowerCase()) || coords.get("paladin"); + getControl().click(clickCoords[0], clickCoords[1]); + delay(500); + + break; + case sdk.game.locations.NewCharSelected: + // hardcore char warning + if (Controls.CharCreateHCWarningOk.control) { + Controls.CharCreateHCWarningOk.click(); + } else { + Controls.CharCreateCharName.setText(info.charName); + + if (!info.expansion) { + // @credit isid0re + if (["druid", "assassin"].includes(info.charClass)) { + D2Bot.printToConsole("Error in profile name. Expansion characters cannot be made in classic", sdk.colors.D2Bot.Red); + D2Bot.stop(); + + return false; + } + + Controls.CharCreateExpansion.click(); + } + + !info.ladder && Controls.CharCreateLadder.click(); + info.hardcore && Controls.CharCreateHardcore.click(); + Controls.BottomRightOk.click(); + } + + break; + case sdk.game.locations.OkCenteredErrorPopUp: + // char name exists (text box 4, 268, 320, 264, 120) + ControlAction.timeoutDelay("Character Name exists: " + info.charName + ". Making new Name.", 5e3); + Starter.profileInfo.charName = info.charName = NameGen(); + Controls.OkCentered.click(); + D2Bot.updateStatus("Making Character: " + info.charName); + + break; + default: + break; + } + + // Singleplayer loop break fix. + if (me.ingame) { + break; + } + + if (getTickCount() > timeout) { + D2Bot.printToConsole("Failed to create character: " + info.charName + " Location: " + getLocation(), sdk.colors.D2Bot.Red); + return false; + } + + delay(500); + } + + me.blockMouse = false; + D2Bot.setProfile(null, null, info.charName, "Normal"); + let gamename = Starter.profileInfo.charName.substring(0, 7) + "-" + Starter.randomString(3, false) + "-"; + DataFile.updateStats("gameName", gamename); + + return true; + }; + + ControlAction.findCharacter = function (info) { + let count = 0; + let singlePlayer = ![sdk.game.gametype.OpenBattlenet, sdk.game.gametype.BattleNet].includes(Profile().type); + // offline doesn't have a character limit cap + let cap = singlePlayer ? 999 : 24; + let tick = getTickCount(); + let firstCheck; + + while (getLocation() !== sdk.game.locations.CharSelect) { + if (getTickCount() - tick >= 5000) { + break; + } + + delay(25); + } + + // Wrong char select screen fix + if ([sdk.game.locations.CharSelect, sdk.game.locations.CharSelectNoChars].includes(getLocation())) { + hideConsole(); // seems to fix odd crash with single-player characters if the console is open to type in + let spCheck = Profile().type === sdk.game.profiletype.Battlenet; + let realmControl = !!Controls.CharSelectCurrentRealm.control; + if ((spCheck && !realmControl) || ((!spCheck && realmControl))) { + Controls.BottomLeftExit.click(); + return false; // what about a recursive call to loginCharacter? + } + } + + if (getLocation() === sdk.game.locations.CharSelectConnecting) { + if (!Starter.charSelectConnecting()) { + D2Bot.printToConsole("Stuck at connecting screen"); + D2Bot.restart(); + } + } + + // start from beginning of the char list + sendKey(sdk.keys.code.Home); + + while (getLocation() === sdk.game.locations.CharSelect && count < cap) { + let control = Controls.CharSelectCharInfo0.control; + + if (control) { + firstCheck = control.getText(); + do { + let text = control.getText(); + + if (text instanceof Array && typeof text[1] === "string") { + count++; + + if (String.isEqual(text[1], info.charName)) { + return control; + } + } + } while (count < cap && control.getNext()); + } + + // check for additional characters up to 24 (online) or 999 offline (no character limit cap) + if (count > 0 && count % 8 === 0) { + if (Controls.CharSelectChar6.click()) { + this.scrollDown(); + let check = Controls.CharSelectCharInfo0.control; + + if (firstCheck && check) { + let nameCheck = check.getText(); + + if (String.isEqual(firstCheck[1], nameCheck[1])) { + return false; + } + } + } + } else { + // no further check necessary + break; + } + } + + return false; + }; + + ControlAction.loginCharacter = function (info, startFromTop = true) { + me.blockMouse = true; + + // start from beginning of the char list + startFromTop && sendKey(sdk.keys.code.Home); + + MainLoop: + // cycle until in lobby or in game + while (getLocation() !== sdk.game.locations.Lobby) { + switch (getLocation()) { + case sdk.game.locations.SplashScreen: + case sdk.game.locations.MainMenu: + case sdk.game.locations.Login: + if (getLocation() === sdk.game.locations.MainMenu + && Profile().type === sdk.game.profiletype.SinglePlayer + && Controls.SinglePlayer.click()) { + Starter.checkDifficulty(); + break; + } else if (Starter.BNET) { + Starter.LocationEvents.login(); + } + + break; + case sdk.game.locations.CharSelect: + let control = ControlAction.findCharacter(info); + + if (control) { + control.click(); + Controls.BottomRightOk.click(); + me.blockMouse = false; + + if (getLocation() === sdk.game.locations.SelectDifficultySP) { + try { + Starter.LocationEvents.selectDifficultySP(); + Starter.locationTimeout(Time.seconds(3), sdk.game.locations.SelectDifficultySP); + } catch (err) { + break MainLoop; + } + + if (me.ingame) { + return true; + } + } + + return true; + } else if (getLocation() !== sdk.game.locations.CharSelect) { + break; + } + + break MainLoop; + case sdk.game.locations.CharSelectNoChars: + Controls.BottomLeftExit.click(); // why exit rather than returning false? + + break; + case sdk.game.locations.Disconnected: + case sdk.game.locations.OkCenteredErrorPopUp: + break MainLoop; + default: + break; + } + + delay(100); + } + + me.blockMouse = false; + + return false; + }; + + // need open bnet check + ControlAction.makeAccount = function (info) { + me.blockMouse = true; + + let tick; + let realms = { "uswest": 0, "useast": 1, "asia": 2, "europe": 3 }; + D2Bot.updateStatus("Making Account: " + info.account); + + // cycle until in empty char screen + while (getLocation() !== sdk.game.locations.CharSelectNoChars) { + switch (getLocation()) { + case sdk.game.locations.MainMenu: + ControlAction.clickRealm(realms[info.realm]); + Controls.BattleNet.click(); + + break; + case sdk.game.locations.Login: + Controls.CreateNewAccount.click(); + + break; + case sdk.game.locations.LoginError: + case sdk.game.locations.LoginUnableToConnect: + return false; + case sdk.game.locations.SplashScreen: + Controls.SplashScreen.click(); + + break; + case sdk.game.locations.MainMenuConnecting: + tick = getTickCount(); + + while (getLocation() === sdk.game.locations.MainMenuConnecting) { + if (getTickCount() - tick > 10000) { + Controls.LoginCancelWait.click(); + } + + delay(500); + } + + break; + case sdk.game.locations.CharacterCreate: + Controls.BottomLeftExit.click(); + + break; + case sdk.game.locations.OkCenteredErrorPopUp: + info.account = ""; + info.password = ""; + D2Bot.setProfile(info.account, info.password); + D2Bot.restart(true); + + break; + case sdk.game.locations.TermsOfUse: + Controls.TermsOfUseAgree.click(); + + break; + case sdk.game.locations.CreateNewAccount: + Controls.EnterAccountName.setText(info.account); + Controls.EnterAccountPassword.setText(info.password); + Controls.ConfirmPassword.setText(info.password); + Controls.BottomRightOk.click(); + + break; + case sdk.game.locations.PleaseRead: + Controls.PleaseReadOk.click(); + + break; + case sdk.game.locations.RegisterEmail: + if (Developer.setEmail.enabled + && (!Developer.setEmail.profiles.length || Developer.setEmail.profiles.includes(me.profile)) + && (!Developer.setEmail.realms.length || Developer.setEmail.realms.includes(Profile().gateway.toLowerCase()))) { + ControlAction.setEmail(); + } else { + Controls.EmailDontRegisterContinue.control ? Controls.EmailDontRegisterContinue.click() : Controls.EmailDontRegister.click(); + } + + break; + default: + break; + } + + delay(100); + } + + me.blockMouse = false; + + return true; + }; + + ControlAction.deleteAndRemakeChar = function (info) { + /** @type {Control} */ + let control = ControlAction.findCharacter(info); + if (!control) return false; + let cInfo = control.getText(); + console.debug(cInfo); + + me.blockMouse = true; + + control.click(); + Controls.CharSelectDelete.click(); + delay(500); + Controls.PopupYes.click(); + + me.blockMouse = false; + + // Delete old files - leaving csv file's for now as I don't think they interfere with the overlay + CharData.delete(true); + DataFile.create(); + DataFile.updateStats("handle", Starter.handle); + CharData.updateData("me", "finalBuild", Starter.profileInfo.tag); + Developer.logPerformance && Tracker.initialize(); + D2Bot.printToConsole("Deleted: " + info.charName + ". Now remaking...", sdk.colors.D2Bot.Gold); + Starter.deadCheck = false; + + return ControlAction.makeCharacter(Starter.profileInfo); + }; + + ControlAction.saveInfo = function (info) { + // Data-file already exists + if (FileTools.exists("logs/Kolbot-SoloPlay/" + info.realm + "/" + info.charClass + "-" + info.charClass + "-" + info.charName + ".json")) { + return; + } + + let folder; + + if (!FileTools.exists("logs/Kolbot-SoloPlay")) { + folder = dopen("logs"); + folder.create("Kolbot-SoloPlay"); + } + + if (!FileTools.exists("logs/Kolbot-SoloPlay/" + info.realm)) { + folder = dopen("logs/Kolbot-SoloPlay"); + folder.create(info.realm); + } + + if (!FileTools.exists("logs/Kolbot-SoloPlay/" + info.realm + "/" + info.charClass + "-" + info.charName + ".json")) { + FileTools.writeText("logs/Kolbot-SoloPlay/" + info.realm + "/" + info.charClass + "-" + info.charName + ".json", JSON.stringify(info)); + } + }; + + ControlAction.loginAccount = function (info) { + me.blockMouse = true; + + let locTick; + let tick = getTickCount(); + let realms = { "uswest": 0, "useast": 1, "asia": 2, "europe": 3 }; + + MainLoop: + while (true) { + switch (getLocation()) { + case sdk.game.locations.PreSplash: + break; + case sdk.game.locations.MainMenu: + info.realm && ControlAction.clickRealm(realms[info.realm]); + Controls.BattleNet.click(); + + break; + case sdk.game.locations.Login: + Controls.EnterAccountName.setText(info.account); + Controls.EnterAccountPassword.setText(info.password); + Controls.Login.click(); + + break; + case sdk.game.locations.LoginUnableToConnect: + case sdk.game.locations.RealmDown: + // Unable to connect, let the caller handle it. + me.blockMouse = false; + + return false; + case sdk.game.locations.CharSelect: + break MainLoop; + case sdk.game.locations.SplashScreen: + Controls.SplashScreen.click(); + + break; + case sdk.game.locations.CharSelectPleaseWait: + case sdk.game.locations.MainMenuConnecting: + case sdk.game.locations.CharSelectConnecting: + break; + case sdk.game.locations.CharSelectNoChars: + // make sure we're not on connecting screen + locTick = getTickCount(); + + while (getTickCount() - locTick < 3000 && getLocation() === sdk.game.locations.CharSelectNoChars) { + delay(25); + } + + if (getLocation() === sdk.game.locations.CharSelectConnecting) { + break; + } + + break MainLoop; // break if we're sure we're on empty char screen + case sdk.game.locations.Lobby: + case sdk.game.locations.LobbyChat: + // somehow we are in the lobby? + Control.LobbyQuit.click(); + + break; + default: + console.log(getLocation()); + + me.blockMouse = false; + + return false; + } + + if (getTickCount() - tick >= 20000) { + return false; + } + + delay(100); + } + + delay(1000); + + me.blockMouse = false; + + return getLocation() === sdk.game.locations.CharSelect || getLocation() === sdk.game.locations.CharSelectNoChars; + }; + + Starter.randomNumberString = function (len) { + len === undefined && (len = rand(2, 5)); + + let rval = ""; + let vals = "0123456789"; + + for (let i = 0; i < len; i += 1) { + rval += vals[rand(0, vals.length - 1)]; + } + + return rval; + }; + + Starter.charSelectConnecting = function () { + if (getLocation() === sdk.game.locations.CharSelectConnecting) { + // bugged? lets see if we can unbug it + // Click create char button on infinite "connecting" screen + Controls.CharSelectCreate.click() && delay(1000); + Controls.BottomLeftExit.click() && delay(1000); + + return (getLocation() !== sdk.game.locations.CharSelectConnecting); + } else { + return true; + } + }; + + Starter.BNET = ([sdk.game.profiletype.Battlenet, sdk.game.profiletype.OpenBattlenet].includes(Profile().type)); + Starter.LocationEvents.oogCheck = function () { + return (AutoMule.outOfGameCheck() || TorchSystem.outOfGameCheck() || Gambling.outOfGameCheck() || CraftingSystem.outOfGameCheck() || SoloEvents.outOfGameCheck()); + }; + + Starter.checkDifficulty = function () { + let setDiff = CharData.getStats().setDifficulty; + if (setDiff) { + console.debug(setDiff); + Starter.gameInfo.difficulty = setDiff; + } + }; + + Starter.LocationEvents.login = function () { + Starter.inGame && (Starter.inGame = false); + let pType = Profile().type; + + if (getLocation() === sdk.game.locations.MainMenu && Starter.firstRun + && pType === sdk.game.profiletype.SinglePlayer + && Controls.SinglePlayer.click()) { + return; + } + + // Wrong char select screen fix + if ([sdk.game.locations.CharSelect, sdk.game.locations.CharSelectNoChars].includes(getLocation())) { + hideConsole(); // seems to fix odd crash with single-player characters if the console is open to type in + let spCheck = pType === sdk.game.profiletype.Battlenet; + let realmControl = !!Controls.CharSelectCurrentRealm.control; + if ((spCheck && !realmControl) || ((!spCheck && realmControl))) { + Controls.BottomLeftExit.click(); + + return; + } + } + + // Multiple realm botting fix in case of R/D or disconnect + Starter.firstLogin && getLocation() === sdk.game.locations.Login && Controls.BottomLeftExit.click(); + + D2Bot.updateStatus("Logging In"); + + try { + // make battlenet accounts/characters + if (Starter.BNET) { + ControlAction.timeoutDelay("Login Delay", Starter.Config.DelayBeforeLogin * 1e3); + D2Bot.updateStatus("Logging in"); + // existing account + if (Starter.profileInfo.account !== "") { + try { + // ControlAction.loginAccount(Starter.profileInfo); + login(me.profile); + } catch (error) { + if (DataFile.getStats().AcctPswd) { + Starter.profileInfo.account = DataFile.getStats().AcctName; + Starter.profileInfo.password = DataFile.getStats().AcctPswd; + + for (let i = 0; i < 5; i++) { + if (ControlAction.loginAccount(Starter.profileInfo)) { + break; + } + + if (getLocation() === sdk.game.locations.CharSelectConnecting) { + if (Starter.charSelectConnecting()) { + break; + } + } + + ControlAction.timeoutDelay("Unable to Connect", Starter.Config.UnableToConnectDelay * 6e4); + Starter.profileInfo.account = DataFile.getStats().AcctName; + Starter.profileInfo.password = DataFile.getStats().AcctPswd; + } + } + } + } else { + // new account + if (Starter.profileInfo.account === "") { + if (Starter.Config.GlobalAccount || Starter.Config.GlobalAccountPassword) { + Starter.profileInfo.account = Starter.Config.GlobalAccount.length > 0 ? Starter.Config.GlobalAccount + Starter.randomNumberString(Starter.Config.AccountSuffixLength) : Starter.randomString(12, true); + Starter.profileInfo.password = Starter.Config.GlobalAccountPassword.length > 0 ? Starter.Config.GlobalAccountPassword : Starter.randomString(12, true); + + try { + if (Starter.profileInfo.account.length > 15) throw new Error("Account name exceeds MAXIMUM length (15). Please enter a shorter name or reduce the AccountSuffixLength under StarterConfig"); + if (Starter.profileInfo.password.length > 15) throw new Error("Password name exceeds MAXIMUM length (15). Please enter a shorter name under StarterConfig"); + } catch (e) { + D2Bot.printToConsole("Kolbot-SoloPlay: " + e.message, sdk.colors.D2Bot.Gold); + D2Bot.setProfile("", "", null, "Normal"); + D2Bot.stop(); + } + + console.log("Kolbot-SoloPlay :: Generated account information. " + (Starter.Config.GlobalAccount.length > 0 ? "Pre-defined " : "Random ") + "account used"); + console.log("Kolbot-SoloPlay :: Generated password information. " + (Starter.Config.GlobalAccountPassword.length > 0 ? "Pre-defined " : "Random ") + "password used"); + ControlAction.timeoutDelay("Generating Account Information", Starter.Config.DelayBeforeLogin * 1e3); + } else { + Starter.profileInfo.account = Starter.randomString(12, true); + Starter.profileInfo.password = Starter.randomString(12, true); + console.log("Generating Random Account Information"); + ControlAction.timeoutDelay("Generating Random Account Information", Starter.Config.DelayBeforeLogin * 1e3); + } + + if (ControlAction.makeAccount(Starter.profileInfo)) { + D2Bot.setProfile(Starter.profileInfo.account, Starter.profileInfo.password, null, "Normal"); + DataFile.updateStats("AcctName", Starter.profileInfo.account); + DataFile.updateStats("AcctPswd", Starter.profileInfo.password); + + return; + } else { + Starter.profileInfo.account = ""; + Starter.profileInfo.password = ""; + D2Bot.setProfile(Starter.profileInfo.account, Starter.profileInfo.password, null, "Normal"); + D2Bot.restart(true); + } + } + } + } else { + // SP/TCP characters + try { + if (getLocation() === sdk.game.locations.MainMenu) { + pType === sdk.game.profiletype.SinglePlayer + ? Controls.SinglePlayer.click() + : ControlAction.loginOtherMultiplayer(); + } + Starter.checkDifficulty(); + Starter.LocationEvents.charSelect(getLocation()); + } catch (err) { + console.error(err); + // Try to find the character and if that fails, make character + if (!ControlAction.findCharacter(Starter.profileInfo)) { + // Pop-up that happens when choosing a dead HC char + if (getLocation() === sdk.game.locations.OkCenteredErrorPopUp) { + Controls.OkCentered.click(); // Exit from that pop-up + D2Bot.printToConsole("Character died", sdk.colors.D2Bot.Red); + ControlAction.deleteAndRemakeChar(Starter.profileInfo); + } else { + // If make character fails, check how many characters are on that account + if (!ControlAction.makeCharacter(Starter.profileInfo)) { + // Account is full + if (ControlAction.getCharacters().length >= 18) { + D2Bot.printToConsole("Kolbot-SoloPlay: Account is full", sdk.colors.D2Bot.Orange); + D2Bot.stop(); + } + } + } + } + } + } + } catch (e) { + console.log(e + " " + getLocation()); + } + }; + + Starter.accountExists = false; + + Starter.LocationEvents.loginError = function () { + let string = ""; + let text = Controls.LoginErrorText.getText(); + + if (text) { + for (let i = 0; i < text.length; i++) { + string += text[i]; + + if (i !== text.length - 1) { + string += " "; + } + } + + switch (string) { + case getLocaleString(sdk.locale.text.UsernameIncludedIllegalChars): + case getLocaleString(sdk.locale.text.UsernameIncludedDisallowedwords): + case getLocaleString(sdk.locale.text.UsernameMustBeAtLeast): + case getLocaleString(sdk.locale.text.PasswordMustBeAtLeast): + case getLocaleString(sdk.locale.text.AccountMustBeAtLeast): + case getLocaleString(sdk.locale.text.PasswordCantBeMoreThan): + case getLocaleString(sdk.locale.text.AccountCantBeMoreThan): + case getLocaleString(sdk.locale.text.InvalidPassword): + D2Bot.printToConsole(string); + D2Bot.stop(); + + break; + case getLocaleString(5208): // Invalid account + D2Bot.updateStatus("Invalid Account Name"); + D2Bot.printToConsole("Invalid Account Name :: " + Starter.profileInfo.account); + Starter.profileInfo.account = ""; + Starter.profileInfo.password = ""; + D2Bot.setProfile(Starter.profileInfo.account, Starter.profileInfo.password); + D2Bot.restart(true); + + break; + case getLocaleString(5249): // Unable to create account + case getLocaleString(5239): // An account name already exists + if (!Starter.accountExists) { + Starter.accountExists = true; + Control.LoginErrorOk.click(); + delay(100); + Control.BottomLeftExit.click(); + Starter.LocationEvents.login(); + return; + } + D2Bot.updateStatus("Account name already exists :: " + Starter.profileInfo.account); + D2Bot.printToConsole("Account name already exists :: " + Starter.profileInfo.account); + Starter.profileInfo.account = ""; + Starter.profileInfo.password = ""; + D2Bot.setProfile(Starter.profileInfo.account, Starter.profileInfo.password); + + break; + case getLocaleString(sdk.locale.text.CdKeyInUseBy): + string += (" " + Controls.LoginCdKeyInUseBy.getText()); + D2Bot.printToConsole(Starter.gameInfo.mpq + " " + string, sdk.colors.D2Bot.Gold); + D2Bot.CDKeyInUse(); + + if (Starter.gameInfo.switchKeys) { + cdkeyError = true; + } else { + Controls.UnableToConnectOk.click(); + ControlAction.timeoutDelay("LoD key in use", Starter.Config.CDKeyInUseDelay * 6e4); + + return; + } + + break; + case getLocaleString(5202): // cd key intended for another product + case getLocaleString(10915): // lod key intended for another product + D2Bot.updateStatus("Invalid CDKey"); + D2Bot.printToConsole("Invalid CDKey: " + Starter.gameInfo.mpq, sdk.colors.D2Bot.Gold); + D2Bot.CDKeyDisabled(); + + if (Starter.gameInfo.switchKeys) { + ControlAction.timeoutDelay("Key switch delay", Starter.Config.SwitchKeyDelay * 1000); + D2Bot.restart(true); + } else { + D2Bot.stop(); + } + + break; + case getLocaleString(5199): + D2Bot.updateStatus("Disabled CDKey"); + D2Bot.printToConsole("Disabled CDKey: " + Starter.gameInfo.mpq, sdk.colors.D2Bot.Gold); + D2Bot.CDKeyDisabled(); + + if (Starter.gameInfo.switchKeys) { + ControlAction.timeoutDelay("Key switch delay", Starter.Config.SwitchKeyDelay * 1000); + D2Bot.restart(true); + } else { + D2Bot.stop(); + } + + break; + case getLocaleString(10913): + D2Bot.updateStatus("Disabled LoD CDKey"); + D2Bot.printToConsole("Disabled LoD CDKey: " + Starter.gameInfo.mpq, sdk.colors.D2Bot.Gold); + D2Bot.CDKeyDisabled(); + + if (Starter.gameInfo.switchKeys) { + ControlAction.timeoutDelay("Key switch delay", Starter.Config.SwitchKeyDelay * 1000); + D2Bot.restart(true); + } else { + D2Bot.stop(); + } + + break; + case getLocaleString(5347): + D2Bot.updateStatus("Disconnected from battle.net."); + D2Bot.printToConsole("Disconnected from battle.net."); + Controls.OkCentered.click(); + Controls.LoginErrorOk.click(); + + return; + default: + D2Bot.updateStatus("Login Error"); + D2Bot.printToConsole("Login Error - " + string); + + if (Starter.gameInfo.switchKeys) { + ControlAction.timeoutDelay("Key switch delay", Starter.Config.SwitchKeyDelay * 1000); + D2Bot.restart(true); + } else { + D2Bot.stop(); + } + + break; + } + } + + Controls.LoginErrorOk.click(); + delay(1000); + Controls.BottomLeftExit.click(); + }; + + Starter.LocationEvents.charSelect = function (loc) { + let string = ""; + let text = Controls.CharSelectError.getText(); + + if (text) { + for (let i = 0; i < text.length; i++) { + string += text[i]; + + if (i !== text.length - 1) { + string += " "; + } + } + + // CDKey disabled from realm play + if (string === getLocaleString(sdk.locale.text.CdKeyDisabledFromRealm)) { + D2Bot.updateStatus("Realm Disabled CDKey"); + D2Bot.printToConsole("Realm Disabled CDKey: " + Starter.gameInfo.mpq, sdk.colors.D2Bot.Gold); + D2Bot.CDKeyDisabled(); + + if (Starter.gameInfo.switchKeys) { + ControlAction.timeoutDelay("Key switch delay", Starter.Config.SwitchKeyDelay * 1000); + D2Bot.restart(true); + } else { + D2Bot.stop(); + } + } + } + + if (Starter.deadCheck && ControlAction.deleteAndRemakeChar(Starter.profileInfo)) { + Starter.deadCheck = false; + + return; + } + + if (!Controls.CharSelectCreate.control) { + // We aren't in the right place + return; + } + + if (Object.keys(Starter.profileInfo).length) { + if (!ControlAction.findCharacter(Starter.profileInfo)) { + let currLoc = getLocation(); + if (Starter.profileInfo.charName === DataFile.getObj().name + && currLoc !== sdk.game.locations.CharSelectNoChars + && ControlAction.getCharacters().length === 0) { + ControlAction.timeoutDelay("[R/D] Character not found ", 18e4); + D2Bot.printToConsole("Avoid Creating New Character - Restart"); + D2Bot.restart(); + } else { + if (!ControlAction.makeCharacter(Starter.profileInfo)) { + if (ControlAction.getCharacters().length >= 18) { + D2Bot.printToConsole("Kolbot-SoloPlay: Account is full", sdk.colors.D2Bot.Red); + D2Bot.stop(); + } + } + } + } else { + ControlAction.loginCharacter(Starter.profileInfo, false); + } + } + + if (!Starter.locationTimeout(Starter.Config.ConnectingTimeout * 1e3, loc)) { + Controls.BottomLeftExit.click(); + Starter.gameInfo.rdBlocker && D2Bot.restart(); + } + }; + + Starter.LocationEvents.lobbyChat = function () { + D2Bot.updateStatus("Lobby Chat"); + Starter.lastGameStatus === "pending" && (Starter.gameCount += 1); + + if (Starter.inGame || Starter.gameInfo.error) { + !Starter.gameStart && (Starter.gameStart = DataFile.getStats().ingameTick); + + if (getTickCount() - Starter.gameStart < Starter.Config.MinGameTime * 1e3) { + ControlAction.timeoutDelay("Min game time wait", Starter.Config.MinGameTime * 1e3 + Starter.gameStart - getTickCount()); + } + } + + if (Starter.inGame) { + if (oogCheck()) return; + + console.log("updating runs"); + D2Bot.updateRuns(); + + Starter.gameCount += 1; + Starter.lastGameStatus = "ready"; + Starter.inGame = false; + + if (Starter.Config.ResetCount && Starter.gameCount > Starter.Config.ResetCount) { + Starter.gameCount = 1; + DataFile.updateStats("runs", Starter.gameCount); + } + + Starter.chanInfo.afterMsg = Starter.Config.AfterGameMessage; + + if (Starter.chanInfo.afterMsg) { + !Array.isArray(Starter.chanInfo.afterMsg) && (Starter.chanInfo.afterMsg = [Starter.chanInfo.afterMsg]); + + for (let i = 0; i < Starter.chanInfo.afterMsg.length; i++) { + Starter.sayMsg(Starter.chanInfo.afterMsg[i]); + delay(500); + } + } + } + + if (!Starter.chatActionsDone) { + Starter.chatActionsDone = true; + + Starter.chanInfo.joinChannel = Starter.Config.JoinChannel; + Starter.chanInfo.firstMsg = Starter.Config.FirstJoinMessage; + + if (Starter.chanInfo.joinChannel) { + !Array.isArray(Starter.chanInfo.joinChannel) && (Starter.chanInfo.joinChannel = [Starter.chanInfo.joinChannel]); + !Array.isArray(Starter.chanInfo.firstMsg) && (Starter.chanInfo.firstMsg = [Starter.chanInfo.firstMsg]); + + for (let i = 0; i < Starter.chanInfo.joinChannel.length; i++) { + ControlAction.timeoutDelay("Chat delay", Starter.Config.ChatActionsDelay * 1e3); + + if (ControlAction.joinChannel(Starter.chanInfo.joinChannel[i])) { + Starter.useChat = true; + } else { + console.log("ÿc1Unable to join channel, disabling chat messages."); + + Starter.useChat = false; + } + + if (Starter.chanInfo.firstMsg[i] !== "") { + Starter.sayMsg(Starter.chanInfo.firstMsg[i]); + delay(500); + } + } + } + } + + // Announce game + Starter.chanInfo.announce = Starter.Config.AnnounceGames; + + if (Starter.chanInfo.announce) { + Starter.sayMsg("Next game is " + Starter.gameInfo.gameName + Starter.gameCount + (Starter.gameInfo.gamePass === "" ? "" : "//" + Starter.gameInfo.gamePass)); + } + + Starter.LocationEvents.openCreateGameWindow(); + }; + + const oogCheck = () => ( + AutoMule.outOfGameCheck() || TorchSystem.outOfGameCheck() + || Gambling.outOfGameCheck() || CraftingSystem.outOfGameCheck() || SoloEvents.outOfGameCheck() + ); + + locations[sdk.game.locations.PreSplash] = () => ControlAction.click(); + locations[sdk.game.locations.GatewaySelect] = () => Controls.GatewayCancel.click(); + locations[sdk.game.locations.SplashScreen] = () => Starter.LocationEvents.login(); + locations[sdk.game.locations.MainMenu] = () => Starter.LocationEvents.login(); + locations[sdk.game.locations.Login] = () => Starter.LocationEvents.login(); + locations[sdk.game.locations.OtherMultiplayer] = () => Starter.LocationEvents.otherMultiplayerSelect(); + locations[sdk.game.locations.TcpIp] = () => Profile().type === sdk.game.profiletype.TcpIpHost ? Controls.TcpIpHost.click() : Controls.TcpIpCancel.click(); + locations[sdk.game.locations.TcpIpEnterIp] = () => Controls.TcpIpCancel.click(); + locations[sdk.game.locations.LoginError] = () => Starter.LocationEvents.loginError(); + locations[sdk.game.locations.LoginUnableToConnect] = () => Starter.LocationEvents.unableToConnect(); + locations[sdk.game.locations.TcpIpUnableToConnect] = () => Starter.LocationEvents.unableToConnect(); + locations[sdk.game.locations.CdKeyInUse] = () => Starter.LocationEvents.loginError(); + locations[sdk.game.locations.InvalidCdKey] = () => Starter.LocationEvents.loginError(); + locations[sdk.game.locations.RealmDown] = () => Starter.LocationEvents.realmDown(); + locations[sdk.game.locations.Disconnected] = () => { + ControlAction.timeoutDelay("Disconnected", 3000); + Controls.OkCentered.click(); + }; + locations[sdk.game.locations.RegisterEmail] = () => Controls.EmailDontRegisterContinue.control ? Controls.EmailDontRegisterContinue.click() : Controls.EmailDontRegister.click(); + locations[sdk.game.locations.MainMenuConnecting] = (loc) => !Starter.locationTimeout(Starter.Config.ConnectingTimeout * 1e3, loc) && Controls.LoginCancelWait.click(); + locations[sdk.game.locations.CharSelectPleaseWait] = (loc) => !Starter.locationTimeout(Starter.Config.PleaseWaitTimeout * 1e3, loc) && Controls.OkCentered.click(); + locations[sdk.game.locations.CharSelect] = (loc) => Starter.LocationEvents.charSelect(loc); + locations[sdk.game.locations.CharSelectConnecting] = (loc) => Starter.LocationEvents.charSelect(loc); + locations[sdk.game.locations.CharSelectNoChars] = (loc) => Starter.LocationEvents.charSelect(loc); + locations[sdk.game.locations.SelectDifficultySP] = () => Starter.LocationEvents.selectDifficultySP(); + locations[sdk.game.locations.CharacterCreate] = (loc) => !Starter.locationTimeout(5e3, loc) && Controls.BottomLeftExit.click(); + locations[sdk.game.locations.ServerDown] = () => { + ControlAction.timeoutDelay("Server Down", Time.minutes(5)); + Controls.OkCentered.click(); + }; + locations[sdk.game.locations.LobbyPleaseWait] = (loc) => !Starter.locationTimeout(Starter.Config.PleaseWaitTimeout * 1e3, loc) && Controls.OkCentered.click(); + locations[sdk.game.locations.Lobby] = () => { + D2Bot.updateStatus("Lobby"); + ControlAction.saveInfo(Starter.profileInfo); + + me.blockKeys = false; + + !Starter.firstLogin && (Starter.firstLogin = true); + Starter.lastGameStatus === "pending" && (Starter.gameCount += 1); + + if (Starter.Config.PingQuitDelay && Starter.pingQuit) { + ControlAction.timeoutDelay("Ping Delay", Starter.Config.PingQuitDelay * 1e3); + Starter.pingQuit = false; + } + + if (Starter.Config.JoinChannel !== "" && Controls.LobbyEnterChat.click()) return; + + if (Starter.inGame || Starter.gameInfo.error) { + !Starter.gameStart && (Starter.gameStart = DataFile.getStats().ingameTick); + + if (getTickCount() - Starter.gameStart < Starter.Config.MinGameTime * 1e3 && !joinInfo) { + ControlAction.timeoutDelay("Min game time wait", Starter.Config.MinGameTime * 1e3 + Starter.gameStart - getTickCount()); + } + } + + if (Starter.inGame) { + if (oogCheck()) return; + + D2Bot.updateRuns(); + + Starter.gameCount += 1; + Starter.lastGameStatus = "ready"; + Starter.inGame = false; + + if (Starter.Config.ResetCount && Starter.gameCount > Starter.Config.ResetCount) { + Starter.gameCount = 1; + DataFile.updateStats("runs", Starter.gameCount); + } + } + + Starter.LocationEvents.openCreateGameWindow(); + }; + locations[sdk.game.locations.LobbyChat] = () => Starter.LocationEvents.lobbyChat(); + locations[sdk.game.locations.CreateGame] = (loc) => { + ControlAction.timeoutDelay("Create Game Delay", Starter.Config.DelayBeforeLogin * 1e3); + D2Bot.updateStatus("Creating Game"); + + if (typeof Starter.Config.CharacterDifference === "number") { + Controls.CharacterDifference.disabled === sdk.game.controls.Disabled && Controls.CharacterDifferenceButton.click(); + Controls.CharacterDifference.setText(Starter.Config.CharacterDifference.toString()); + } else if (!Starter.Config.CharacterDifference && Controls.CharacterDifference.disabled === 5) { + Controls.CharacterDifferenceButton.click(); + } + + typeof Starter.Config.MaxPlayerCount === "number" && Controls.MaxPlayerCount.setText(Starter.Config.MaxPlayerCount.toString()); + + D2Bot.requestGameInfo(); + delay(500); + + // todo - really don't need use profiles set difficulty for online. Only single player so re-write difficulty stuff + Starter.checkDifficulty(); + + Starter.gameInfo.gameName = DataFile.getStats().gameName; + Starter.gameInfo.gamePass = Starter.randomString(5, true); + + switch (true) { + case Starter.gameInfo.gameName === "": + case Starter.gameInfo.gameName === "Name": + Starter.gameInfo.gameName = Starter.profileInfo.charName.substring(0, 7) + "-" + Starter.randomString(3, false) + "-"; + + break; + } + + // FTJ handler + if (Starter.lastGameStatus === "pending") { + Starter.isUp = "no"; + + D2Bot.printToConsole("Failed to create game"); + ControlAction.timeoutDelay("FTJ delay", Starter.Config.FTJDelay * 1e3); + D2Bot.updateRuns(); + } + + ControlAction.createGame((Starter.gameInfo.gameName + Starter.gameCount), Starter.gameInfo.gamePass, Starter.gameInfo.difficulty, Starter.Config.CreateGameDelay * 1000); + Starter.lastGameStatus = "pending"; + Starter.setNextGame(Starter.gameInfo); + Starter.locationTimeout(10000, loc); + }; + locations[sdk.game.locations.GameNameExists] = () => { + Controls.CreateGameWindow.click(); + Starter.gameCount += 1; + Starter.lastGameStatus = "ready"; + }; + locations[sdk.game.locations.WaitingInLine] = () => Starter.LocationEvents.waitingInLine(); + locations[sdk.game.locations.JoinGame] = () => Starter.LocationEvents.openCreateGameWindow(); + locations[sdk.game.locations.Ladder] = () => Starter.LocationEvents.openCreateGameWindow(); + locations[sdk.game.locations.ChannelList] = () => Starter.LocationEvents.openCreateGameWindow(); + locations[sdk.game.locations.LobbyLostConnection] = () => { + ControlAction.timeoutDelay("LostConnection", 3000); + Controls.OkCentered.click(); + }; + locations[sdk.game.locations.GameDoesNotExist] = () => Starter.LocationEvents.gameDoesNotExist(); + locations[sdk.game.locations.GameIsFull] = () => Starter.LocationEvents.openCreateGameWindow(); })(); diff --git a/libs/SoloPlay/Tools/Overlay.js b/libs/SoloPlay/Tools/Overlay.js index 6d4e7943..9ff13ee5 100644 --- a/libs/SoloPlay/Tools/Overlay.js +++ b/libs/SoloPlay/Tools/Overlay.js @@ -14,451 +14,451 @@ includeIfNotIncluded("SoloPlay/Functions/PrototypeOverrides.js"); * @todo Clean this up, probably needs to be entirely rewritten */ const Overlay = { - resfix: { x: -10, y: me.screensize ? 0 : -120 }, - quest: { x: 8, y: 368 }, - qYMod: { 1: 368, 2: 384, 3: 384, 4: 414, 5: 384 }, - dashboard: { x: 120, y: 470 }, - timer: { x: 0, y: 595 }, - build: SetUp.currentBuild, - script: "", - realm: (me.realm ? me.realm : "SinglePlayer"), - difficulty: sdk.difficulty.nameOf(me.diff), - level: () => me.data.level, - - text: (function () { - const _gameTracker = Tracker.readObj(Tracker.GTPath); - let [_tick, _charlvl] = [0, 0]; - - const _format = function (ms = 0) { - const hours = Math.floor(ms / 3600000); - const minutes = Math.floor((ms % 3600000) / 60000); - const seconds = Math.floor((ms % 60000) / 1000); - - /** @param {number} num */ - const pad = (num) => (num < 10 ? '0' + num : num); - - return pad(hours) + ':' + pad(minutes) + ':' + pad(seconds); - }; - - const _timer = function () { - return (new Date(getTickCount() - me.gamestarttime).toISOString().slice(11, -5)); - }; - - return { - /** @type {Array<{ name: string, hook: Hook }>} */ - hooks: [], - enabled: true, - - clock: function () { - if (!Developer.logPerformance) return ""; - _gameTracker === undefined && (Object.assign(_gameTracker, Tracker.readObj(Tracker.GTPath))); - _tick = getTickCount(); - let currInGame = getTickCount() - me.gamestarttime; - let totalTime = _format(_gameTracker.Total + currInGame); - let totalInGame = _format(_gameTracker.InGame + currInGame); - - return ("Total: ÿc0" + totalTime + "ÿc4 InGame: ÿc0" + totalInGame + "ÿc4 OOG: ÿc0" + _format(_gameTracker.OOG)); - }, - - check: function () { - if (!this.enabled) { - this.flush(); - - return; - } - - // Double check in case still got here before being ready - if (!me.gameReady && !me.ingame && !me.area) return; - - !this.getHook("dashboard") && this.add("dashboard"); - !this.getHook("credits") && this.add("credits"); - - if (!this.getHook("InGameTimer")) { - this.add("InGameTimer"); - } else { - if (getTickCount() - _tick >= 1000) { - this.getHook("InGameTimer").hook.text = "ÿc0" + _timer(); - } - } - - if (Developer.logPerformance) { - if (!this.getHook("times")) { - this.add("times"); - } else { - if (getTickCount() - _tick >= 1000) { - this.getHook("times").hook.text = this.clock(); - } - } - } - - if (!this.getHook("level")) { - this.add("level"); - } else if (_charlvl !== Overlay.level()) { - _charlvl = Overlay.level(); - this.getHook("level").hook.text = "Name: ÿc0" + me.name + "ÿc4 Diff: ÿc0" + Overlay.difficulty + "ÿc4 Level: ÿc0" + _charlvl; - } - }, - - add: function (name) { - switch (name) { - case "dashboard": - this.hooks.push({ - name: "dashboard", - hook: new Box(Overlay.dashboard.x + Overlay.resfix.x, Overlay.dashboard.y + Overlay.resfix.y, 370, 80, 0x0, 4, 0) - }); - - this.hooks.push({ - name: "dashboardframe", - hook: new Frame(Overlay.dashboard.x + Overlay.resfix.x, Overlay.dashboard.y + Overlay.resfix.y, 370, 80, 0) - }); - - this.getHook("dashboard").hook.zorder = 0; - - break; - case "credits": - this.hooks.push({ - name: "credits", - hook: new Text("Kolbot-SoloPlay by: ÿc0 theBGuy" + "ÿc4 Realm: ÿc0" + Overlay.realm, Overlay.dashboard.x, Overlay.dashboard.y + Overlay.resfix.y + 15, 4, 13, 0) - }); - - break; - case "level": - _charlvl = Overlay.level(); - this.hooks.push({ - name: "level", - hook: new Text("Name: ÿc0" + me.name + "ÿc4 Diff: ÿc0" + Overlay.difficulty + "ÿc4 Level: ÿc0" + _charlvl, Overlay.dashboard.x, Overlay.dashboard.y + Overlay.resfix.y + 30, 4, 13, 0) - }); - - break; - case "times": - this.hooks.push({ - name: "times", - hook: new Text(this.clock(), Overlay.dashboard.x, Overlay.dashboard.y + Overlay.resfix.y + 75, 4, 13, 0) - }); - - break; - case "InGameTimer": - this.hooks.push({ - name: "timerBoard", - hook: new Box(Overlay.timer.x, Overlay.timer.y - 15 + Overlay.resfix.y, 68, 18, 0, 4, 0) - }); - - this.hooks.push({ - name: "timerFrame", - hook: new Frame(Overlay.timer.x, Overlay.timer.y - 15 + Overlay.resfix.y, 68, 18, 0) - }); - - this.hooks.push({ - name: "InGameTimer", - hook: new Text("ÿc0" + _timer(), Overlay.timer.x + 7, Overlay.timer.y + Overlay.resfix.y, 0, 13, 0) - }); - - break; - } - }, - - getHook: function (name) { - for (let i = 0; i < this.hooks.length; i++) { - if (this.hooks[i].name === name) { - return this.hooks[i]; - } - } - - return false; - }, - - flush: function () { - while (this.hooks.length) { - this.hooks.shift().hook.remove(); - } - return true; - } - }; - })(), - - quests: (function () { - const QuestData = require("../../core/GameData/QuestData"); - - /** - * @constructor - * @param {string} name - * @param {number} id - * @param {boolean} [preReq] - */ - function QuestHook (name, id, preReq = false) { - this.name = name; - this.id = id; - this.preReq = preReq; - } - - /** - * @this {QuestHook} - * @returns {string} - */ - QuestHook.prototype.status = function () { - return QuestData.get(this.id).complete(this.preReq) ? "ÿc2Complete" : "ÿc1Incomplete"; - }; - - /** - * @this {QuestHook} - * @returns {{ name: string, hook: Hook }} - */ - QuestHook.prototype.hook = function () { - return { - name: this.name, - hook: new Text(this.name + ": " + this.status(), Overlay.quest.x, Overlay.quest.y + Overlay.resfix.y + (15 * _qHooks.length), 4, _font, 0) - }; - }; - - const _quests = new Map([ - // Act 1 - ["Den", new QuestHook("Den", sdk.quest.id.DenofEvil)], - ["Blood Raven", new QuestHook("Blood Raven", sdk.quest.id.SistersBurialGrounds)], - ["Tristram", new QuestHook("Tristram", sdk.quest.id.TheSearchForCain)], - ["Countess", new QuestHook("Countess", sdk.quest.id.ForgottenTower)], - ["Smith", new QuestHook("Smith", sdk.quest.id.ToolsoftheTrade, true)], - ["Andariel", new QuestHook("Andariel", sdk.quest.id.SistersToTheSlaughter)], - // Act 2 - ["Cube", new QuestHook("Cube", sdk.quest.id.TheHoradricStaff)], - ["Radament", new QuestHook("Radament", sdk.quest.id.RadamentsLair)], - ["Horadric Staff", new QuestHook("Horadric Staff", sdk.quest.id.TheHoradricStaff)], - ["Amulet", new QuestHook("Amulet", sdk.quest.id.TheTaintedSun)], - ["Summoner", new QuestHook("Summoner", sdk.quest.id.TheSummoner)], - ["Duriel", new QuestHook("Duriel", sdk.quest.id.TheSevenTombs)], - // Act 3 - ["Golden Bird", new QuestHook("Golden Bird", sdk.quest.id.TheGoldenBird)], - ["Khalim's Will", new QuestHook("Khalim's Will", sdk.quest.id.KhalimsWill)], - ["Lam Esen", new QuestHook("Lam Esen", sdk.quest.id.LamEsensTome)], - ["Travincal", new QuestHook("Travincal", sdk.quest.id.TheBlackenedTemple)], - ["Mephisto", new QuestHook("Mephisto", sdk.quest.id.TheGuardian)], - // Act 4 - ["Izual", new QuestHook("Izual", sdk.quest.id.TheFallenAngel)], - ["Hell Forge", new QuestHook("Hell Forge", sdk.quest.id.HellsForge, true)], - ["Diablo", new QuestHook("Diablo", sdk.quest.id.TerrorsEnd)], - // Act 5 - ["Shenk", new QuestHook("Shenk", sdk.quest.id.SiegeOnHarrogath, true)], - ["Barbies", new QuestHook("Barbies", sdk.quest.id.RescueonMountArreat)], - ["Anya", new QuestHook("Anya", sdk.quest.id.PrisonofIce)], - ["Ancients", new QuestHook("Ancients", sdk.quest.id.RiteofPassage)], - ["Baal", new QuestHook("Baal", sdk.quest.id.EyeofDestruction)] - ]); - - const _acts = new Map([ - [1, ["Den", "Blood Raven", "Tristram", "Countess", "Smith", "Andariel"]], - [2, [/* "Cube", */"Radament", "Horadric Staff", "Amulet", "Summoner", "Duriel"]], - [3, ["Golden Bird", "Khalim's Will", "Lam Esen", "Travincal", "Mephisto"]], - [4, ["Izual", "Hell Forge", "Diablo"]], - [5, ["Shenk", "Barbies", "Anya", "Ancients", "Baal"]] - ]); - - const _font = 12; - const _qHooks = []; - - return { - enabled: true, - hooks: [], - - getRes: function () { - // Double check in case still got here before being ready - if (!me.gameReady || !me.ingame || !me.area) return ""; - return ("FR: ÿc1" + me.FR + "ÿc4 CR: ÿc3" + me.CR + "ÿc4 LR: ÿc9" + me.LR + "ÿc4 PR: ÿc2" + me.PR + "ÿc4 CurrentBuild: ÿc0" + Overlay.build); - }, - - getStats: function () { - // Double check in case still got here before being ready - if (!me.gameReady || !me.ingame || !me.area) return ""; - - let textLine = ("MF: ÿc8" + me.getStat(sdk.stats.MagicBonus) + "ÿc4 FHR: ÿc8" + (me.FHR) + "ÿc4 FBR: ÿc8" + (me.FBR) + "ÿc4 FCR: ÿc8" + (me.FCR) - + "ÿc4 IAS: ÿc8" + (me.IAS)); - - return textLine; - }, - - /** - * @param {string} name - * @returns {void} - */ - addQuest: function (name) { - const quest = _quests.get(name); - if (!quest) return; - - _qHooks.push(quest.hook()); - }, - - /** - * @param {string} name - * @returns {void} - */ - updateQuest: function (name) { - const quest = _quests.get(name); - if (!quest) return; - - const hook = this.getHook(name); - if (!hook) { - this.addQuest(name); - } else { - hook.hook.text = quest.name + ": " + quest.status(); - } - }, - - check: function () { - if (!this.enabled || !me.gameReady || !me.ingame || !me.area || me.dead) { - this.flush(); - - return; - } - - !this.getHook("resistances", this.hooks) ? this.add("resistances") : this.getHook("resistances", this.hooks).hook.text = this.getRes(); - !this.getHook("stats", this.hooks) ? this.add("stats") : this.getHook("stats", this.hooks).hook.text = this.getStats(); - !this.getHook("questheader") && this.add("questheader"); - - _acts.get(me.act).forEach((quest) => this.updateQuest(quest)); - - !this.getHook("questbox") && this.add("questbox"); - }, - - /** - * @param {string} name - */ - add: function (name) { - switch (name) { - case "resistances": - this.hooks.push({ - name: "resistances", - hook: new Text(this.getRes(), Overlay.dashboard.x, Overlay.dashboard.y + Overlay.resfix.y + 45, 4, 13, 0) - }); - - break; - case "stats": - this.hooks.push({ - name: "stats", - hook: new Text(this.getStats(), Overlay.dashboard.x, Overlay.dashboard.y + Overlay.resfix.y + 60, 4, 13, 0) - }); - - break; - case "questbox": - _qHooks.push({ - name: "questbox", - hook: new Box(Overlay.quest.x - 8, Overlay.quest.y + Overlay.resfix.y - 17, 145, 10 + [0, 105, 90, 90, 60, 90][me.act], 0x0, 4, 0) - }); - - _qHooks.push({ - name: "questframe", - hook: new Frame(Overlay.quest.x - 8, Overlay.quest.y + Overlay.resfix.y - 17, 145, 10 + [0, 105, 90, 90, 60, 90][me.act], 0) - }); - - this.getHook("questbox").hook.zorder = 0; - - break; - case "questheader": - Overlay.quest.y = Overlay.qYMod[me.act]; - - _qHooks.push({ - name: "questheader", - hook: new Text("Quests in Act: ÿc0" + me.act, Overlay.quest.x, Overlay.quest.y + Overlay.resfix.y, 4, 0, 0) - }); - - break; - } - }, - - getHook: function (name, hooks) { - while (!me.gameReady || !me.ingame || !me.area) { - delay(500); - } - - hooks === undefined && (hooks = _qHooks); - - for (let i = 0; i < hooks.length; i += 1) { - if (hooks[i].name === name) return hooks[i]; - } - - return false; - }, - - flush: function () { - while (this.hooks.length) { - this.hooks.shift().hook.remove(); - } - - while (_qHooks.length) { - _qHooks.shift().hook.remove(); - } - return true; - } - }; - })(), - - timeOut: 0, - - update: function (msg = false) { - function status () { - let hide = [ - sdk.uiflags.Inventory, sdk.uiflags.StatsWindow, sdk.uiflags.QuickSkill, sdk.uiflags.SkillWindow, - sdk.uiflags.ChatBox, sdk.uiflags.EscMenu, sdk.uiflags.KeytotheCairnStonesScreen, sdk.uiflags.Shop, - sdk.uiflags.SubmitItem, sdk.uiflags.Quest, sdk.uiflags.Party, sdk.uiflags.Msgs, sdk.uiflags.Stash, - sdk.uiflags.Cube, sdk.uiflags.Help, sdk.uiflags.MercScreen - ]; - - if (!me.gameReady || !me.ingame || !me.area || me.dead) { - Overlay.disable(true); - } else { - while (!me.gameReady) { - delay(100); - } - - for (let flag = 0; flag < hide.length; flag++) { - if (getUIFlag(hide[flag])) { - Overlay.text.flush(); - Overlay.quests.flush(); - - while (getUIFlag(hide[flag])) { - delay(100); - } - - Misc.poll(() => me.gameReady); - flag = 0; - } else { - Overlay.text.enabled = true; - } - } - } - - Overlay.text.check(); - if (Overlay.quests.enabled) { - Overlay.quests.check(); - } else { - if (Overlay.timeOut > 0 && getTickCount() > Overlay.timeOut) { - Overlay.quests.enabled = true; - Overlay.timeOut = 0; - } - Overlay.quests.flush(); - } - } - - return msg ? true : (me.gameReady && me.ingame && !me.dead) ? status() : false; - }, - - disable: function (all = false) { - me.overhead("Disable"); - - if (all) { - me.overhead("Disable All"); - Overlay.text.flush() && Overlay.quests.flush(); - [Overlay.text.enabled, Overlay.quests.enabled] = [false, false]; - this.timeOut = getTickCount() + Time.seconds(15); - } else { - Overlay.quests.flush(); - Overlay.quests.enabled = false; - console.log(Overlay.quests.enabled); - } - - delay(100); - - return true; - }, - - flush: function () { - return Overlay.quests.flush(); - }, + resfix: { x: -10, y: me.screensize ? 0 : -120 }, + quest: { x: 8, y: 368 }, + qYMod: { 1: 368, 2: 384, 3: 384, 4: 414, 5: 384 }, + dashboard: { x: 120, y: 470 }, + timer: { x: 0, y: 595 }, + build: SetUp.currentBuild, + script: "", + realm: (me.realm ? me.realm : "SinglePlayer"), + difficulty: sdk.difficulty.nameOf(me.diff), + level: () => me.data.level, + + text: (function () { + const _gameTracker = Tracker.readObj(Tracker.GTPath); + let [_tick, _charlvl] = [0, 0]; + + const _format = function (ms = 0) { + const hours = Math.floor(ms / 3600000); + const minutes = Math.floor((ms % 3600000) / 60000); + const seconds = Math.floor((ms % 60000) / 1000); + + /** @param {number} num */ + const pad = (num) => (num < 10 ? '0' + num : num); + + return pad(hours) + ':' + pad(minutes) + ':' + pad(seconds); + }; + + const _timer = function () { + return (new Date(getTickCount() - me.gamestarttime).toISOString().slice(11, -5)); + }; + + return { + /** @type {Array<{ name: string, hook: Hook }>} */ + hooks: [], + enabled: true, + + clock: function () { + if (!Developer.logPerformance) return ""; + _gameTracker === undefined && (Object.assign(_gameTracker, Tracker.readObj(Tracker.GTPath))); + _tick = getTickCount(); + let currInGame = getTickCount() - me.gamestarttime; + let totalTime = _format(_gameTracker.Total + currInGame); + let totalInGame = _format(_gameTracker.InGame + currInGame); + + return ("Total: ÿc0" + totalTime + "ÿc4 InGame: ÿc0" + totalInGame + "ÿc4 OOG: ÿc0" + _format(_gameTracker.OOG)); + }, + + check: function () { + if (!this.enabled) { + this.flush(); + + return; + } + + // Double check in case still got here before being ready + if (!me.gameReady && !me.ingame && !me.area) return; + + !this.getHook("dashboard") && this.add("dashboard"); + !this.getHook("credits") && this.add("credits"); + + if (!this.getHook("InGameTimer")) { + this.add("InGameTimer"); + } else { + if (getTickCount() - _tick >= 1000) { + this.getHook("InGameTimer").hook.text = "ÿc0" + _timer(); + } + } + + if (Developer.logPerformance) { + if (!this.getHook("times")) { + this.add("times"); + } else { + if (getTickCount() - _tick >= 1000) { + this.getHook("times").hook.text = this.clock(); + } + } + } + + if (!this.getHook("level")) { + this.add("level"); + } else if (_charlvl !== Overlay.level()) { + _charlvl = Overlay.level(); + this.getHook("level").hook.text = "Name: ÿc0" + me.name + "ÿc4 Diff: ÿc0" + Overlay.difficulty + "ÿc4 Level: ÿc0" + _charlvl; + } + }, + + add: function (name) { + switch (name) { + case "dashboard": + this.hooks.push({ + name: "dashboard", + hook: new Box(Overlay.dashboard.x + Overlay.resfix.x, Overlay.dashboard.y + Overlay.resfix.y, 370, 80, 0x0, 4, 0) + }); + + this.hooks.push({ + name: "dashboardframe", + hook: new Frame(Overlay.dashboard.x + Overlay.resfix.x, Overlay.dashboard.y + Overlay.resfix.y, 370, 80, 0) + }); + + this.getHook("dashboard").hook.zorder = 0; + + break; + case "credits": + this.hooks.push({ + name: "credits", + hook: new Text("Kolbot-SoloPlay by: ÿc0 theBGuy" + "ÿc4 Realm: ÿc0" + Overlay.realm, Overlay.dashboard.x, Overlay.dashboard.y + Overlay.resfix.y + 15, 4, 13, 0) + }); + + break; + case "level": + _charlvl = Overlay.level(); + this.hooks.push({ + name: "level", + hook: new Text("Name: ÿc0" + me.name + "ÿc4 Diff: ÿc0" + Overlay.difficulty + "ÿc4 Level: ÿc0" + _charlvl, Overlay.dashboard.x, Overlay.dashboard.y + Overlay.resfix.y + 30, 4, 13, 0) + }); + + break; + case "times": + this.hooks.push({ + name: "times", + hook: new Text(this.clock(), Overlay.dashboard.x, Overlay.dashboard.y + Overlay.resfix.y + 75, 4, 13, 0) + }); + + break; + case "InGameTimer": + this.hooks.push({ + name: "timerBoard", + hook: new Box(Overlay.timer.x, Overlay.timer.y - 15 + Overlay.resfix.y, 68, 18, 0, 4, 0) + }); + + this.hooks.push({ + name: "timerFrame", + hook: new Frame(Overlay.timer.x, Overlay.timer.y - 15 + Overlay.resfix.y, 68, 18, 0) + }); + + this.hooks.push({ + name: "InGameTimer", + hook: new Text("ÿc0" + _timer(), Overlay.timer.x + 7, Overlay.timer.y + Overlay.resfix.y, 0, 13, 0) + }); + + break; + } + }, + + getHook: function (name) { + for (let i = 0; i < this.hooks.length; i++) { + if (this.hooks[i].name === name) { + return this.hooks[i]; + } + } + + return false; + }, + + flush: function () { + while (this.hooks.length) { + this.hooks.shift().hook.remove(); + } + return true; + } + }; + })(), + + quests: (function () { + const QuestData = require("../../core/GameData/QuestData"); + + /** + * @constructor + * @param {string} name + * @param {number} id + * @param {boolean} [preReq] + */ + function QuestHook (name, id, preReq = false) { + this.name = name; + this.id = id; + this.preReq = preReq; + } + + /** + * @this {QuestHook} + * @returns {string} + */ + QuestHook.prototype.status = function () { + return QuestData.get(this.id).complete(this.preReq) ? "ÿc2Complete" : "ÿc1Incomplete"; + }; + + /** + * @this {QuestHook} + * @returns {{ name: string, hook: Hook }} + */ + QuestHook.prototype.hook = function () { + return { + name: this.name, + hook: new Text(this.name + ": " + this.status(), Overlay.quest.x, Overlay.quest.y + Overlay.resfix.y + (15 * _qHooks.length), 4, _font, 0) + }; + }; + + const _quests = new Map([ + // Act 1 + ["Den", new QuestHook("Den", sdk.quest.id.DenofEvil)], + ["Blood Raven", new QuestHook("Blood Raven", sdk.quest.id.SistersBurialGrounds)], + ["Tristram", new QuestHook("Tristram", sdk.quest.id.TheSearchForCain)], + ["Countess", new QuestHook("Countess", sdk.quest.id.ForgottenTower)], + ["Smith", new QuestHook("Smith", sdk.quest.id.ToolsoftheTrade, true)], + ["Andariel", new QuestHook("Andariel", sdk.quest.id.SistersToTheSlaughter)], + // Act 2 + ["Cube", new QuestHook("Cube", sdk.quest.id.TheHoradricStaff)], + ["Radament", new QuestHook("Radament", sdk.quest.id.RadamentsLair)], + ["Horadric Staff", new QuestHook("Horadric Staff", sdk.quest.id.TheHoradricStaff)], + ["Amulet", new QuestHook("Amulet", sdk.quest.id.TheTaintedSun)], + ["Summoner", new QuestHook("Summoner", sdk.quest.id.TheSummoner)], + ["Duriel", new QuestHook("Duriel", sdk.quest.id.TheSevenTombs)], + // Act 3 + ["Golden Bird", new QuestHook("Golden Bird", sdk.quest.id.TheGoldenBird)], + ["Khalim's Will", new QuestHook("Khalim's Will", sdk.quest.id.KhalimsWill)], + ["Lam Esen", new QuestHook("Lam Esen", sdk.quest.id.LamEsensTome)], + ["Travincal", new QuestHook("Travincal", sdk.quest.id.TheBlackenedTemple)], + ["Mephisto", new QuestHook("Mephisto", sdk.quest.id.TheGuardian)], + // Act 4 + ["Izual", new QuestHook("Izual", sdk.quest.id.TheFallenAngel)], + ["Hell Forge", new QuestHook("Hell Forge", sdk.quest.id.HellsForge, true)], + ["Diablo", new QuestHook("Diablo", sdk.quest.id.TerrorsEnd)], + // Act 5 + ["Shenk", new QuestHook("Shenk", sdk.quest.id.SiegeOnHarrogath, true)], + ["Barbies", new QuestHook("Barbies", sdk.quest.id.RescueonMountArreat)], + ["Anya", new QuestHook("Anya", sdk.quest.id.PrisonofIce)], + ["Ancients", new QuestHook("Ancients", sdk.quest.id.RiteofPassage)], + ["Baal", new QuestHook("Baal", sdk.quest.id.EyeofDestruction)] + ]); + + const _acts = new Map([ + [1, ["Den", "Blood Raven", "Tristram", "Countess", "Smith", "Andariel"]], + [2, [/* "Cube", */"Radament", "Horadric Staff", "Amulet", "Summoner", "Duriel"]], + [3, ["Golden Bird", "Khalim's Will", "Lam Esen", "Travincal", "Mephisto"]], + [4, ["Izual", "Hell Forge", "Diablo"]], + [5, ["Shenk", "Barbies", "Anya", "Ancients", "Baal"]] + ]); + + const _font = 12; + const _qHooks = []; + + return { + enabled: true, + hooks: [], + + getRes: function () { + // Double check in case still got here before being ready + if (!me.gameReady || !me.ingame || !me.area) return ""; + return ("FR: ÿc1" + me.FR + "ÿc4 CR: ÿc3" + me.CR + "ÿc4 LR: ÿc9" + me.LR + "ÿc4 PR: ÿc2" + me.PR + "ÿc4 CurrentBuild: ÿc0" + Overlay.build); + }, + + getStats: function () { + // Double check in case still got here before being ready + if (!me.gameReady || !me.ingame || !me.area) return ""; + + let textLine = ("MF: ÿc8" + me.getStat(sdk.stats.MagicBonus) + "ÿc4 FHR: ÿc8" + (me.FHR) + "ÿc4 FBR: ÿc8" + (me.FBR) + "ÿc4 FCR: ÿc8" + (me.FCR) + + "ÿc4 IAS: ÿc8" + (me.IAS)); + + return textLine; + }, + + /** + * @param {string} name + * @returns {void} + */ + addQuest: function (name) { + const quest = _quests.get(name); + if (!quest) return; + + _qHooks.push(quest.hook()); + }, + + /** + * @param {string} name + * @returns {void} + */ + updateQuest: function (name) { + const quest = _quests.get(name); + if (!quest) return; + + const hook = this.getHook(name); + if (!hook) { + this.addQuest(name); + } else { + hook.hook.text = quest.name + ": " + quest.status(); + } + }, + + check: function () { + if (!this.enabled || !me.gameReady || !me.ingame || !me.area || me.dead) { + this.flush(); + + return; + } + + !this.getHook("resistances", this.hooks) ? this.add("resistances") : this.getHook("resistances", this.hooks).hook.text = this.getRes(); + !this.getHook("stats", this.hooks) ? this.add("stats") : this.getHook("stats", this.hooks).hook.text = this.getStats(); + !this.getHook("questheader") && this.add("questheader"); + + _acts.get(me.act).forEach((quest) => this.updateQuest(quest)); + + !this.getHook("questbox") && this.add("questbox"); + }, + + /** + * @param {string} name + */ + add: function (name) { + switch (name) { + case "resistances": + this.hooks.push({ + name: "resistances", + hook: new Text(this.getRes(), Overlay.dashboard.x, Overlay.dashboard.y + Overlay.resfix.y + 45, 4, 13, 0) + }); + + break; + case "stats": + this.hooks.push({ + name: "stats", + hook: new Text(this.getStats(), Overlay.dashboard.x, Overlay.dashboard.y + Overlay.resfix.y + 60, 4, 13, 0) + }); + + break; + case "questbox": + _qHooks.push({ + name: "questbox", + hook: new Box(Overlay.quest.x - 8, Overlay.quest.y + Overlay.resfix.y - 17, 145, 10 + [0, 105, 90, 90, 60, 90][me.act], 0x0, 4, 0) + }); + + _qHooks.push({ + name: "questframe", + hook: new Frame(Overlay.quest.x - 8, Overlay.quest.y + Overlay.resfix.y - 17, 145, 10 + [0, 105, 90, 90, 60, 90][me.act], 0) + }); + + this.getHook("questbox").hook.zorder = 0; + + break; + case "questheader": + Overlay.quest.y = Overlay.qYMod[me.act]; + + _qHooks.push({ + name: "questheader", + hook: new Text("Quests in Act: ÿc0" + me.act, Overlay.quest.x, Overlay.quest.y + Overlay.resfix.y, 4, 0, 0) + }); + + break; + } + }, + + getHook: function (name, hooks) { + while (!me.gameReady || !me.ingame || !me.area) { + delay(500); + } + + hooks === undefined && (hooks = _qHooks); + + for (let i = 0; i < hooks.length; i += 1) { + if (hooks[i].name === name) return hooks[i]; + } + + return false; + }, + + flush: function () { + while (this.hooks.length) { + this.hooks.shift().hook.remove(); + } + + while (_qHooks.length) { + _qHooks.shift().hook.remove(); + } + return true; + } + }; + })(), + + timeOut: 0, + + update: function (msg = false) { + function status () { + let hide = [ + sdk.uiflags.Inventory, sdk.uiflags.StatsWindow, sdk.uiflags.QuickSkill, sdk.uiflags.SkillWindow, + sdk.uiflags.ChatBox, sdk.uiflags.EscMenu, sdk.uiflags.KeytotheCairnStonesScreen, sdk.uiflags.Shop, + sdk.uiflags.SubmitItem, sdk.uiflags.Quest, sdk.uiflags.Party, sdk.uiflags.Msgs, sdk.uiflags.Stash, + sdk.uiflags.Cube, sdk.uiflags.Help, sdk.uiflags.MercScreen + ]; + + if (!me.gameReady || !me.ingame || !me.area || me.dead) { + Overlay.disable(true); + } else { + while (!me.gameReady) { + delay(100); + } + + for (let flag = 0; flag < hide.length; flag++) { + if (getUIFlag(hide[flag])) { + Overlay.text.flush(); + Overlay.quests.flush(); + + while (getUIFlag(hide[flag])) { + delay(100); + } + + Misc.poll(() => me.gameReady); + flag = 0; + } else { + Overlay.text.enabled = true; + } + } + } + + Overlay.text.check(); + if (Overlay.quests.enabled) { + Overlay.quests.check(); + } else { + if (Overlay.timeOut > 0 && getTickCount() > Overlay.timeOut) { + Overlay.quests.enabled = true; + Overlay.timeOut = 0; + } + Overlay.quests.flush(); + } + } + + return msg ? true : (me.gameReady && me.ingame && !me.dead) ? status() : false; + }, + + disable: function (all = false) { + me.overhead("Disable"); + + if (all) { + me.overhead("Disable All"); + Overlay.text.flush() && Overlay.quests.flush(); + [Overlay.text.enabled, Overlay.quests.enabled] = [false, false]; + this.timeOut = getTickCount() + Time.seconds(15); + } else { + Overlay.quests.flush(); + Overlay.quests.enabled = false; + console.log(Overlay.quests.enabled); + } + + delay(100); + + return true; + }, + + flush: function () { + return Overlay.quests.flush(); + }, }; diff --git a/libs/SoloPlay/Tools/SoloIndex.js b/libs/SoloPlay/Tools/SoloIndex.js index 71b413f0..a85cd9ef 100644 --- a/libs/SoloPlay/Tools/SoloIndex.js +++ b/libs/SoloPlay/Tools/SoloIndex.js @@ -12,714 +12,714 @@ */ const SoloIndex = { - doneList: [], - retryList: [], - goldScripts: ["bishibosh", "tristram", "treehead", "countess", "lowerkurast"], + doneList: [], + retryList: [], + goldScripts: ["bishibosh", "tristram", "treehead", "countess", "lowerkurast"], - // this controls the order - scripts: [ - // Act 1 - "corpsefire", "mausoleum", "den", "bishibosh", "bloodraven", "tristram", "treehead", - "countess", "smith", "pits", "jail", "boneash", "andariel", "a1chests", "cows", - // Act 2 - "cube", "radament", "amulet", "summoner", "maggotlair", "tombs", "ancienttunnels", "staff", "duriel", - // Act 3 - "lamessen", "templeruns", "lowerkurast", "eye", "heart", "brain", "travincal", "mephisto", - // Act 4 - "izual", "hellforge", "river", "hephasto", "diablo", - // Act 5 - "shenk", "savebarby", "anya", "pindle", "ancients", "baal", "a5chests", - ], + // this controls the order + scripts: [ + // Act 1 + "corpsefire", "mausoleum", "den", "bishibosh", "bloodraven", "tristram", "treehead", + "countess", "smith", "pits", "jail", "boneash", "andariel", "a1chests", "cows", + // Act 2 + "cube", "radament", "amulet", "summoner", "maggotlair", "tombs", "ancienttunnels", "staff", "duriel", + // Act 3 + "lamessen", "templeruns", "lowerkurast", "eye", "heart", "brain", "travincal", "mephisto", + // Act 4 + "izual", "hellforge", "river", "hephasto", "diablo", + // Act 5 + "shenk", "savebarby", "anya", "pindle", "ancients", "baal", "a5chests", + ], - index: { - "corpsefire": { - preReq: function () { - return (me.den && me.hell); - }, - skipIf: function () { - return (me.druid && me.paladin); - }, - shouldRun: function () { - if (!this.preReq() || this.skipIf()) return false; - return (!me.andariel || Check.brokeAf()); - } - }, - "mausoleum": { - preReq: function () { - return false; - }, - skipIf: function () { - return false; - }, - shouldRun: function () { - if (!this.preReq() || this.skipIf()) return false; - return true; - } - }, - "den": { - skipIf: function () { - return me.den; - }, - shouldRun: function () { - if (this.skipIf()) return false; - return true; - } - }, - "bishibosh": { - preReq: function () { - // return me.normal && me.charlvl < 6; - return (me.charlvl > 10); // figure out if this would be good to run - }, - skipIf: function () { - return me.sorceress; - }, - shouldRun: function () { - if (!this.preReq() || this.skipIf()) return false; - return Check.brokeAf(); - } - }, - "bloodraven": { - skipIf: function () { - if (me.hell) { - // too many light immunes - although come back to this cause maybe just kill raven - return (["Lightning", "Trapsin", "Javazon"].includes(SetUp.currentBuild) || (me.amazon && SetUp.currentBuild !== SetUp.finalBuild)); - } - return false; - }, - shouldRun: function () { - switch (me.diff) { - case sdk.difficulty.Normal: - return !me.bloodraven || (!me.summoner && Check.brokeAf()) || (!me.tristram && me.barbarian); - case sdk.difficulty.Nightmare: - return !me.bloodraven; - case sdk.difficulty.Hell: - return !this.skipIf(); - } - return false; - } - }, - "tristram": { - skipIf: function () { - switch (me.classid) { - case sdk.player.class.Paladin: - return me.accessToAct(3) || Attack.auradin || me.checkItem({ name: sdk.locale.items.Enigma }).have; - case sdk.player.class.Barbarian: - return me.accessToAct(3) || me.checkItem({ name: sdk.locale.items.Lawbringer }).have; - default: - if (me.hell && me.charlvl > 72 && me.accessToAct(2)) return true; - return false; - } - }, - shouldRun: function () { - switch (true) { - case (me.normal && (!me.tristram || me.charlvl < (me.classic || !me.barbarian ? 12 : 6) || Check.brokeAf())): - case (me.nightmare && ((!me.tristram && me.charlvl < 43) || Check.brokeAf())): - case (me.hell && ((!me.tristram && me.diffCompleted) || !this.skipIf())): - return true; - } - return false; - } - }, - "treehead": { - preReq: function () { - return (me.hell && !me.accessToAct(3)); - }, - skipIf: function () { - return !me.paladin || Attack.auradin || me.checkItem({ name: sdk.locale.items.Enigma }).have; - }, - shouldRun: function () { - if (!this.preReq() || this.skipIf()) return false; - return true; - } - }, - "countess": { - skipIf: function () { - return (me.hell && (me.classic || (me.sorceress && !me.onFinalBuild && !me.diffCompleted))); - }, - shouldRun: function () { - if (this.skipIf()) return false; - let needRunes = Check.runes(); - switch (true) { - case (me.normal && (needRunes || Check.brokeAf())): // todo - better determination for low gold - case (me.barbarian && me.hell && me.checkItem({ name: sdk.locale.items.Lawbringer }).have): - case (!me.normal && (Pather.canTeleport() || me.charlvl < 60)): - return true; - } - return false; - } - }, - "smith": { - preReq: function () { - return me.charlvl > 8; - }, - skipIf: function () { - // todo - test leveling/experience potential - return (!!Misc.checkQuest(sdk.quest.id.ToolsoftheTrade, sdk.quest.states.ReqComplete) || me.smith); - }, - shouldRun: function () { - if (!this.preReq() || this.skipIf()) return false; - return true; - } - }, - "pits": { - preReq: function () { - return me.hell; - }, - skipIf: function () { - switch (me.classid) { - case sdk.player.class.Amazon: - return SetUp.currentBuild !== SetUp.finalBuild || me.charlvl < 85; - case sdk.player.class.Sorceress: - return me.charlvl < 85; - case sdk.player.class.Paladin: - case sdk.player.class.Druid: - return !Check.currentBuild().caster; - } - return false; - }, - shouldRun: function () { - if (!this.preReq() || this.skipIf()) return false; - return true; - } - }, - "jail": { - preReq: function () { - return (me.hell && me.amazon && !me.mephisto); - }, - skipIf: function () { - return (SetUp.currentBuild === SetUp.finalBuild); - }, - shouldRun: function () { - if (!this.preReq() || this.skipIf()) return false; - return true; - } - }, - "boneash": { - preReq: function () { - return true; - }, - skipIf: function () { - return (me.charlvl < 10); - }, - shouldRun: function () { - if (!this.preReq() || this.skipIf()) return false; - switch (true) { - case (me.charlvl < 12): - case (Check.brokeAf()): - case (me.classic && me.hell && !me.diablo): - return true; - } - return false; - } - }, - "andariel": { - skipIf: function () { - if (me.charlvl < 12) return true; - if (!me.andariel) return false; - if (me.hell && me.amazon && SetUp.currentBuild !== SetUp.finalBuild) return true; - return false; - }, - shouldRun: function () { - if (this.skipIf()) return false; - switch (true) { - case (!me.andariel): - case (me.normal && Check.brokeAf()): - case (me.classic && me.hell): - case (!me.normal && (Pather.canTeleport() || me.charlvl < 60)): - return true; - } - return false; - } - }, - "a1chests": { - preReq: function () { - return (!me.classic && !me.normal); - }, - skipIf: function () { - if (me.barbarian && (!me.hell || me.accessToAct(3) - || (me.equipped.get(sdk.body.LeftArm).tier > 1270 - || me.checkItem({ name: sdk.locale.items.Lawbringer }).have))) { - return true; - } - - return (me.charlvl < 70 || !Pather.canTeleport()); - }, - shouldRun: function () { - if (!this.preReq() || this.skipIf()) return false; - return true; - } - }, - "cows": { - preReq: function () { - return !me.cows && me.diffCompleted; - }, - skipIf: function () { - if (me.normal && !Check.brokeAf()) return true; - switch (me.classid) { - case sdk.player.class.Barbarian: - return ["Whirlwind", "Immortalwhirl", "Singer"].indexOf(SetUp.currentBuild) === -1; - case sdk.player.class.Druid: - return me.nightmare && me.charlvl > 65; - case sdk.player.class.Sorceress: - return me.nightmare && me.expansion && me.charlvl > 62; - } - return false; - }, - shouldRun: function () { - if (!this.preReq() || this.skipIf()) return false; - return true; - } - }, - "cube": { - preReq: function () { - return me.accessToAct(2); - }, - skipIf: function () { - return me.cube; - }, - shouldRun: function () { - if (!this.preReq() || this.skipIf()) return false; - return true; - } - }, - "radament": { - preReq: function () { - return me.accessToAct(2); - }, - shouldRun: function () { - if (!this.preReq()) return false; - switch (true) { - case (!me.radament): - case (me.normal && Check.brokeAf()): - case (me.hell && me.amazon && SetUp.currentBuild !== SetUp.finalBuild): - case (me.hell && me.sorceress && me.classic && !me.diablo): - return true; - } - return false; - } - }, - "staff": { - preReq: function () { - return me.accessToAct(2); - }, - skipIf: function () { - return (me.horadricstaff || me.shaft || me.completestaff); - }, - shouldRun: function () { - if (!this.preReq() || this.skipIf()) return false; - return true; - } - }, - "amulet": { - preReq: function () { - return me.accessToAct(2); - }, - skipIf: function () { - return (me.horadricstaff || me.amulet || me.completestaff); - }, - shouldRun: function () { - if (!this.preReq() || this.skipIf()) return false; - return true; - } - }, - "ancienttunnels": { - preReq: function () { - return (me.hell && me.accessToAct(2)); - }, - skipIf: function () { - switch (me.classid) { - case sdk.player.class.Amazon: - return SetUp.currentBuild !== SetUp.finalBuild; - default: - return Attack.getSkillElement(Config.AttackSkill[3]) === "magic"; - } - }, - shouldRun: function () { - if (!this.preReq() || this.skipIf()) return false; - return true; - } - }, - "beetleburst": { - preReq: function () { - return (me.accessToAct(2)); - }, - skipIf: function () { - return (me.charlvl < 12 || me.charlvl > 20); - }, - shouldRun: function () { - if (!this.preReq() || this.skipIf()) return false; - return true; - } - }, - "summoner": { - preReq: function () { - return me.accessToAct(2) && me.getQuest(sdk.quest.id.TheTaintedSun, sdk.quest.states.Completed) === 1; - }, - skipIf: function () { - return me.summoner; - }, - shouldRun: function () { - // does summoner have leveling potential? - if (!this.preReq() || this.skipIf()) return false; - return true; - } - }, - "maggotlair": { - preReq: function () { - return me.accessToAct(2) && Pather.canTeleport(); - }, - skipIf: function () { - return (!me.normal || me.charlvl > 21); - }, - shouldRun: function () { - if (!this.preReq() || this.skipIf()) return false; - return true; - } - }, - "tombs": { - preReq: function () { - return me.accessToAct(2) && me.summoner; - }, - skipIf: function () { - return (!me.normal || me.charlvl > 22); - }, - shouldRun: function () { - if (!this.preReq() || this.skipIf()) return false; - return true; - } - }, - "duriel": { - preReq: function () { - return me.accessToAct(2) && (me.horadricstaff || me.completestaff || (me.amulet && me.shaft)); - }, - skipIf: function () { - return me.duriel; - }, - shouldRun: function () { - // does duriel have leveling potential? - if (!this.preReq() || this.skipIf()) return false; - return true; - } - }, - "eye": { - preReq: function () { - return me.accessToAct(3); - }, - skipIf: function () { - return (me.eye || me.khalimswill || me.travincal); - }, - shouldRun: function () { - if (!this.preReq() || this.skipIf()) return false; - return true; - } - }, - "templeruns": { - preReq: function () { - return me.accessToAct(3); - }, - skipIf: function () { - return ((me.paladin && Check.currentBuild().caster) || (me.hell && me.sorceress && me.charlvl < 90)); - }, - shouldRun: function () { - if (!this.preReq() || this.skipIf()) return false; - switch (true) { - case (me.normal && ((me.charlvl > 18 && me.charlvl < 25) || (me.charlvl >= 25 && !me.diffCompleted && Check.brokeAf()))): - case (me.nightmare && me.charlvl < 50): - case (me.hell && !me.classic && me.charlvl > 80): - return true; - } - return false; - } - }, - "lamessen": { - preReq: function () { - return me.accessToAct(3); - }, - skipIf: function () { - return (me.lamessen); - }, - shouldRun: function () { - if (!this.preReq() || this.skipIf()) return false; - return true; - } - }, - "lowerkurast": { - preReq: function () { - return me.accessToAct(3); - }, - skipIf: function () { - return (!me.barbarian); - }, - shouldRun: function () { - if (!this.preReq() || this.skipIf()) return false; - return (me.nightmare && me.charlvl >= 50 && !me.checkItem({ name: sdk.locale.items.VoiceofReason }).have); - } - }, - "heart": { - preReq: function () { - return me.accessToAct(3); - }, - skipIf: function () { - return (me.heart || me.khalimswill || me.travincal); - }, - shouldRun: function () { - if (!this.preReq() || this.skipIf()) return false; - return true; - } - }, - "brain": { - preReq: function () { - return me.accessToAct(3); - }, - skipIf: function () { - return (me.brain || me.khalimswill || me.travincal); - }, - shouldRun: function () { - if (!this.preReq() || this.skipIf()) return false; - return true; - } - }, - "travincal": { - preReq: function () { - return me.accessToAct(3); - }, - skipIf: function () { - return false; - }, - shouldRun: function () { - if (!this.preReq() || this.skipIf()) return false; - switch (true) { - case !me.travincal: - case (me.charlvl < 25 || (me.charlvl >= 25 && me.normal && !me.diffCompleted && Check.brokeAf())): - case (me.nightmare && !me.diablo && me.barbarian && !me.checkItem({ name: sdk.locale.items.Lawbringer }).have): - case (me.hell && me.paladin && me.charlvl > 85 && (!Attack.auradin || !me.checkItem({ name: sdk.locale.items.Enigma }).have)): - return true; - } - return false; - } - }, - "mephisto": { - preReq: function () { - return (me.accessToAct(3) && me.travincal); - }, - skipIf: function () { - return false; - }, - shouldRun: function () { - if (!this.preReq() || this.skipIf()) return false; - const canTele = Pather.canTeleport(); - switch (true) { - case !me.mephisto: - case (me.normal && (Check.brokeAf() || ((canTele && !me.diablo) || !me.izual))): - case (me.nightmare && (canTele || me.charlvl <= 65)): - case (me.hell && (canTele || !me.hardcore)): - return true; - } - return false; - } - }, - "izual": { - preReq: function () { - return me.accessToAct(4); - }, - skipIf: function () { - return false; - }, - shouldRun: function () { - if (!this.preReq() || this.skipIf()) return false; - switch (true) { - case !me.izual: - case (me.normal && !me.diablo): - return true; - } - return false; - } - }, - "river": { - preReq: function () { - const cLvl = me.charlvl; - return (me.accessToAct(4) && ((me.normal && cLvl >= 24) || (me.nightmare && cLvl >= 40) || (me.hell && cLvl >= 80))); - }, - skipIf: function () { - return (me.diablo || me.normal); - }, - shouldRun: function () { - if (!this.preReq() || this.skipIf()) return false; - switch (true) { - case (me.barbarian && !me.checkItem({ name: sdk.locale.items.Lawbringer }).have): - case (me.sorceress && me.classic): - return true; - } - return false; - } - }, - "hephasto": { - preReq: function () { - const cLvl = me.charlvl; - return (me.accessToAct(4) && ((me.normal && cLvl >= 24) || (me.nightmare && cLvl >= 40) || (me.hell && cLvl >= 80))); - }, - skipIf: function () { - return (!me.barbarian || me.normal || me.diablo); - }, - shouldRun: function () { - if (!this.preReq() || this.skipIf()) return false; - switch (true) { - case (me.charlvl <= 70 && !me.checkItem({ name: sdk.locale.items.Lawbringer }).have): - return true; - } - return false; - } - }, - "hellforge": { - preReq: function () { - const cLvl = me.charlvl; - return (me.accessToAct(4) && (me.classic || me.anya) && ((me.normal && cLvl >= 24) || (me.nightmare && cLvl >= 40) || (me.hell && cLvl >= 80))); - }, - skipIf: function () { - return (me.hellforge || me.getQuest(sdk.quest.id.HellsForge, sdk.quest.states.ReqComplete)); - }, - shouldRun: function () { - if (!this.preReq() || this.skipIf()) return false; - return true; - } - }, - "diablo": { - preReq: function () { - const cLvl = me.charlvl; - return (me.accessToAct(4) && ((me.normal && cLvl >= 24) || (me.nightmare && cLvl >= 40) || (me.hell && cLvl >= 80))); - }, - skipIf: function () { - return false; - }, - shouldRun: function () { - if (!this.preReq() || this.skipIf()) return false; - switch (true) { - case !me.diablo: - case (me.normal && me.classic): - case (me.normal && me.expansion && (me.charlvl < 30 || !me.diffCompleted)): - case (me.nightmare && (Pather.canTeleport() || me.charlvl <= 65)): - case (me.hell): - return true; - } - return false; - } - }, - "shenk": { - preReq: function () { - return (me.expansion && me.accessToAct(5)); - }, - skipIf: function () { - return false; - }, - shouldRun: function () { - if (!this.preReq() || this.skipIf()) return false; - switch (true) { - case !me.shenk: - case (!me.druid || me.charlvl <= 70): - return true; - } - return false; - } - }, - "savebarby": { - preReq: function () { - return (me.expansion && me.accessToAct(5)); - }, - skipIf: function () { - return me.savebarby; - }, - shouldRun: function () { - if (!this.preReq() || this.skipIf()) return false; - switch (true) { - case Runewords.checkRune(sdk.items.runes.Tal, sdk.items.runes.Ral, sdk.items.runes.Ort): - return true; - } - return false; - } - }, - "anya": { - preReq: function () { - return (me.expansion && me.accessToAct(5)); - }, - skipIf: function () { - return me.anya; - }, - shouldRun: function () { - if (!this.preReq() || this.skipIf()) return false; - return true; - } - }, - "pindle": { - preReq: function () { - return (me.expansion && me.accessToAct(5) && me.anya); - }, - skipIf: function () { - return false; - }, - shouldRun: function () { - if (!this.preReq() || this.skipIf()) return false; - return true; - } - }, - "ancients": { - preReq: function () { - return (me.expansion && me.accessToAct(5)); - }, - skipIf: function () { - return me.ancients; - }, - shouldRun: function () { - if (!this.preReq() || this.skipIf()) return false; - return true; - } - }, - "baal": { - preReq: function () { - return (me.expansion && me.accessToAct(5)); - }, - skipIf: function () { - return !me.ancients; - }, - shouldRun: function () { - if (!this.preReq() || this.skipIf()) return false; - return true; - } - }, - "a5chests": { - preReq: function () { - return (me.expansion && me.accessToAct(5) && me.baal); - }, - skipIf: function () { - return me.normal; - }, - shouldRun: function () { - if (!this.preReq() || this.skipIf()) return false; - return true; - } - }, - "getkeys": { - preReq: function () { - return (me.expansion && me.accessToAct(5) && me.hell); - }, - skipIf: function () { - return (["Zealer", "Smiter", "Uberconc"].indexOf(SetUp.currentBuild) === -1); - }, - shouldRun: function () { - if (!this.preReq() || this.skipIf()) return false; - return true; - } - }, - "orgtorch": { - preReq: function () { - return (me.expansion && me.accessToAct(5) && me.hell); - }, - skipIf: function () { - return (["Zealer", "Smiter", "Uberconc"].indexOf(SetUp.currentBuild) === -1); - }, - shouldRun: function () { - if (!this.preReq() || this.skipIf()) return false; - return true; - } - }, - } + index: { + "corpsefire": { + preReq: function () { + return (me.den && me.hell); + }, + skipIf: function () { + return (me.druid && me.paladin); + }, + shouldRun: function () { + if (!this.preReq() || this.skipIf()) return false; + return (!me.andariel || Check.brokeAf()); + } + }, + "mausoleum": { + preReq: function () { + return false; + }, + skipIf: function () { + return false; + }, + shouldRun: function () { + if (!this.preReq() || this.skipIf()) return false; + return true; + } + }, + "den": { + skipIf: function () { + return me.den; + }, + shouldRun: function () { + if (this.skipIf()) return false; + return true; + } + }, + "bishibosh": { + preReq: function () { + // return me.normal && me.charlvl < 6; + return (me.charlvl > 10); // figure out if this would be good to run + }, + skipIf: function () { + return me.sorceress; + }, + shouldRun: function () { + if (!this.preReq() || this.skipIf()) return false; + return Check.brokeAf(); + } + }, + "bloodraven": { + skipIf: function () { + if (me.hell) { + // too many light immunes - although come back to this cause maybe just kill raven + return (["Lightning", "Trapsin", "Javazon"].includes(SetUp.currentBuild) || (me.amazon && SetUp.currentBuild !== SetUp.finalBuild)); + } + return false; + }, + shouldRun: function () { + switch (me.diff) { + case sdk.difficulty.Normal: + return !me.bloodraven || (!me.summoner && Check.brokeAf()) || (!me.tristram && me.barbarian); + case sdk.difficulty.Nightmare: + return !me.bloodraven; + case sdk.difficulty.Hell: + return !this.skipIf(); + } + return false; + } + }, + "tristram": { + skipIf: function () { + switch (me.classid) { + case sdk.player.class.Paladin: + return me.accessToAct(3) || Attack.auradin || me.checkItem({ name: sdk.locale.items.Enigma }).have; + case sdk.player.class.Barbarian: + return me.accessToAct(3) || me.checkItem({ name: sdk.locale.items.Lawbringer }).have; + default: + if (me.hell && me.charlvl > 72 && me.accessToAct(2)) return true; + return false; + } + }, + shouldRun: function () { + switch (true) { + case (me.normal && (!me.tristram || me.charlvl < (me.classic || !me.barbarian ? 12 : 6) || Check.brokeAf())): + case (me.nightmare && ((!me.tristram && me.charlvl < 43) || Check.brokeAf())): + case (me.hell && ((!me.tristram && me.diffCompleted) || !this.skipIf())): + return true; + } + return false; + } + }, + "treehead": { + preReq: function () { + return (me.hell && !me.accessToAct(3)); + }, + skipIf: function () { + return !me.paladin || Attack.auradin || me.checkItem({ name: sdk.locale.items.Enigma }).have; + }, + shouldRun: function () { + if (!this.preReq() || this.skipIf()) return false; + return true; + } + }, + "countess": { + skipIf: function () { + return (me.hell && (me.classic || (me.sorceress && !me.onFinalBuild && !me.diffCompleted))); + }, + shouldRun: function () { + if (this.skipIf()) return false; + let needRunes = Check.runes(); + switch (true) { + case (me.normal && (needRunes || Check.brokeAf())): // todo - better determination for low gold + case (me.barbarian && me.hell && me.checkItem({ name: sdk.locale.items.Lawbringer }).have): + case (!me.normal && (Pather.canTeleport() || me.charlvl < 60)): + return true; + } + return false; + } + }, + "smith": { + preReq: function () { + return me.charlvl > 8; + }, + skipIf: function () { + // todo - test leveling/experience potential + return (!!Misc.checkQuest(sdk.quest.id.ToolsoftheTrade, sdk.quest.states.ReqComplete) || me.smith); + }, + shouldRun: function () { + if (!this.preReq() || this.skipIf()) return false; + return true; + } + }, + "pits": { + preReq: function () { + return me.hell; + }, + skipIf: function () { + switch (me.classid) { + case sdk.player.class.Amazon: + return SetUp.currentBuild !== SetUp.finalBuild || me.charlvl < 85; + case sdk.player.class.Sorceress: + return me.charlvl < 85; + case sdk.player.class.Paladin: + case sdk.player.class.Druid: + return !Check.currentBuild().caster; + } + return false; + }, + shouldRun: function () { + if (!this.preReq() || this.skipIf()) return false; + return true; + } + }, + "jail": { + preReq: function () { + return (me.hell && me.amazon && !me.mephisto); + }, + skipIf: function () { + return (SetUp.currentBuild === SetUp.finalBuild); + }, + shouldRun: function () { + if (!this.preReq() || this.skipIf()) return false; + return true; + } + }, + "boneash": { + preReq: function () { + return true; + }, + skipIf: function () { + return (me.charlvl < 10); + }, + shouldRun: function () { + if (!this.preReq() || this.skipIf()) return false; + switch (true) { + case (me.charlvl < 12): + case (Check.brokeAf()): + case (me.classic && me.hell && !me.diablo): + return true; + } + return false; + } + }, + "andariel": { + skipIf: function () { + if (me.charlvl < 12) return true; + if (!me.andariel) return false; + if (me.hell && me.amazon && SetUp.currentBuild !== SetUp.finalBuild) return true; + return false; + }, + shouldRun: function () { + if (this.skipIf()) return false; + switch (true) { + case (!me.andariel): + case (me.normal && Check.brokeAf()): + case (me.classic && me.hell): + case (!me.normal && (Pather.canTeleport() || me.charlvl < 60)): + return true; + } + return false; + } + }, + "a1chests": { + preReq: function () { + return (!me.classic && !me.normal); + }, + skipIf: function () { + if (me.barbarian && (!me.hell || me.accessToAct(3) + || (me.equipped.get(sdk.body.LeftArm).tier > 1270 + || me.checkItem({ name: sdk.locale.items.Lawbringer }).have))) { + return true; + } + + return (me.charlvl < 70 || !Pather.canTeleport()); + }, + shouldRun: function () { + if (!this.preReq() || this.skipIf()) return false; + return true; + } + }, + "cows": { + preReq: function () { + return !me.cows && me.diffCompleted; + }, + skipIf: function () { + if (me.normal && !Check.brokeAf()) return true; + switch (me.classid) { + case sdk.player.class.Barbarian: + return ["Whirlwind", "Immortalwhirl", "Singer"].indexOf(SetUp.currentBuild) === -1; + case sdk.player.class.Druid: + return me.nightmare && me.charlvl > 65; + case sdk.player.class.Sorceress: + return me.nightmare && me.expansion && me.charlvl > 62; + } + return false; + }, + shouldRun: function () { + if (!this.preReq() || this.skipIf()) return false; + return true; + } + }, + "cube": { + preReq: function () { + return me.accessToAct(2); + }, + skipIf: function () { + return me.cube; + }, + shouldRun: function () { + if (!this.preReq() || this.skipIf()) return false; + return true; + } + }, + "radament": { + preReq: function () { + return me.accessToAct(2); + }, + shouldRun: function () { + if (!this.preReq()) return false; + switch (true) { + case (!me.radament): + case (me.normal && Check.brokeAf()): + case (me.hell && me.amazon && SetUp.currentBuild !== SetUp.finalBuild): + case (me.hell && me.sorceress && me.classic && !me.diablo): + return true; + } + return false; + } + }, + "staff": { + preReq: function () { + return me.accessToAct(2); + }, + skipIf: function () { + return (me.horadricstaff || me.shaft || me.completestaff); + }, + shouldRun: function () { + if (!this.preReq() || this.skipIf()) return false; + return true; + } + }, + "amulet": { + preReq: function () { + return me.accessToAct(2); + }, + skipIf: function () { + return (me.horadricstaff || me.amulet || me.completestaff); + }, + shouldRun: function () { + if (!this.preReq() || this.skipIf()) return false; + return true; + } + }, + "ancienttunnels": { + preReq: function () { + return (me.hell && me.accessToAct(2)); + }, + skipIf: function () { + switch (me.classid) { + case sdk.player.class.Amazon: + return SetUp.currentBuild !== SetUp.finalBuild; + default: + return Attack.getSkillElement(Config.AttackSkill[3]) === "magic"; + } + }, + shouldRun: function () { + if (!this.preReq() || this.skipIf()) return false; + return true; + } + }, + "beetleburst": { + preReq: function () { + return (me.accessToAct(2)); + }, + skipIf: function () { + return (me.charlvl < 12 || me.charlvl > 20); + }, + shouldRun: function () { + if (!this.preReq() || this.skipIf()) return false; + return true; + } + }, + "summoner": { + preReq: function () { + return me.accessToAct(2) && me.getQuest(sdk.quest.id.TheTaintedSun, sdk.quest.states.Completed) === 1; + }, + skipIf: function () { + return me.summoner; + }, + shouldRun: function () { + // does summoner have leveling potential? + if (!this.preReq() || this.skipIf()) return false; + return true; + } + }, + "maggotlair": { + preReq: function () { + return me.accessToAct(2) && Pather.canTeleport(); + }, + skipIf: function () { + return (!me.normal || me.charlvl > 21); + }, + shouldRun: function () { + if (!this.preReq() || this.skipIf()) return false; + return true; + } + }, + "tombs": { + preReq: function () { + return me.accessToAct(2) && me.summoner; + }, + skipIf: function () { + return (!me.normal || me.charlvl > 22); + }, + shouldRun: function () { + if (!this.preReq() || this.skipIf()) return false; + return true; + } + }, + "duriel": { + preReq: function () { + return me.accessToAct(2) && (me.horadricstaff || me.completestaff || (me.amulet && me.shaft)); + }, + skipIf: function () { + return me.duriel; + }, + shouldRun: function () { + // does duriel have leveling potential? + if (!this.preReq() || this.skipIf()) return false; + return true; + } + }, + "eye": { + preReq: function () { + return me.accessToAct(3); + }, + skipIf: function () { + return (me.eye || me.khalimswill || me.travincal); + }, + shouldRun: function () { + if (!this.preReq() || this.skipIf()) return false; + return true; + } + }, + "templeruns": { + preReq: function () { + return me.accessToAct(3); + }, + skipIf: function () { + return ((me.paladin && Check.currentBuild().caster) || (me.hell && me.sorceress && me.charlvl < 90)); + }, + shouldRun: function () { + if (!this.preReq() || this.skipIf()) return false; + switch (true) { + case (me.normal && ((me.charlvl > 18 && me.charlvl < 25) || (me.charlvl >= 25 && !me.diffCompleted && Check.brokeAf()))): + case (me.nightmare && me.charlvl < 50): + case (me.hell && !me.classic && me.charlvl > 80): + return true; + } + return false; + } + }, + "lamessen": { + preReq: function () { + return me.accessToAct(3); + }, + skipIf: function () { + return (me.lamessen); + }, + shouldRun: function () { + if (!this.preReq() || this.skipIf()) return false; + return true; + } + }, + "lowerkurast": { + preReq: function () { + return me.accessToAct(3); + }, + skipIf: function () { + return (!me.barbarian); + }, + shouldRun: function () { + if (!this.preReq() || this.skipIf()) return false; + return (me.nightmare && me.charlvl >= 50 && !me.checkItem({ name: sdk.locale.items.VoiceofReason }).have); + } + }, + "heart": { + preReq: function () { + return me.accessToAct(3); + }, + skipIf: function () { + return (me.heart || me.khalimswill || me.travincal); + }, + shouldRun: function () { + if (!this.preReq() || this.skipIf()) return false; + return true; + } + }, + "brain": { + preReq: function () { + return me.accessToAct(3); + }, + skipIf: function () { + return (me.brain || me.khalimswill || me.travincal); + }, + shouldRun: function () { + if (!this.preReq() || this.skipIf()) return false; + return true; + } + }, + "travincal": { + preReq: function () { + return me.accessToAct(3); + }, + skipIf: function () { + return false; + }, + shouldRun: function () { + if (!this.preReq() || this.skipIf()) return false; + switch (true) { + case !me.travincal: + case (me.charlvl < 25 || (me.charlvl >= 25 && me.normal && !me.diffCompleted && Check.brokeAf())): + case (me.nightmare && !me.diablo && me.barbarian && !me.checkItem({ name: sdk.locale.items.Lawbringer }).have): + case (me.hell && me.paladin && me.charlvl > 85 && (!Attack.auradin || !me.checkItem({ name: sdk.locale.items.Enigma }).have)): + return true; + } + return false; + } + }, + "mephisto": { + preReq: function () { + return (me.accessToAct(3) && me.travincal); + }, + skipIf: function () { + return false; + }, + shouldRun: function () { + if (!this.preReq() || this.skipIf()) return false; + const canTele = Pather.canTeleport(); + switch (true) { + case !me.mephisto: + case (me.normal && (Check.brokeAf() || ((canTele && !me.diablo) || !me.izual))): + case (me.nightmare && (canTele || me.charlvl <= 65)): + case (me.hell && (canTele || !me.hardcore)): + return true; + } + return false; + } + }, + "izual": { + preReq: function () { + return me.accessToAct(4); + }, + skipIf: function () { + return false; + }, + shouldRun: function () { + if (!this.preReq() || this.skipIf()) return false; + switch (true) { + case !me.izual: + case (me.normal && !me.diablo): + return true; + } + return false; + } + }, + "river": { + preReq: function () { + const cLvl = me.charlvl; + return (me.accessToAct(4) && ((me.normal && cLvl >= 24) || (me.nightmare && cLvl >= 40) || (me.hell && cLvl >= 80))); + }, + skipIf: function () { + return (me.diablo || me.normal); + }, + shouldRun: function () { + if (!this.preReq() || this.skipIf()) return false; + switch (true) { + case (me.barbarian && !me.checkItem({ name: sdk.locale.items.Lawbringer }).have): + case (me.sorceress && me.classic): + return true; + } + return false; + } + }, + "hephasto": { + preReq: function () { + const cLvl = me.charlvl; + return (me.accessToAct(4) && ((me.normal && cLvl >= 24) || (me.nightmare && cLvl >= 40) || (me.hell && cLvl >= 80))); + }, + skipIf: function () { + return (!me.barbarian || me.normal || me.diablo); + }, + shouldRun: function () { + if (!this.preReq() || this.skipIf()) return false; + switch (true) { + case (me.charlvl <= 70 && !me.checkItem({ name: sdk.locale.items.Lawbringer }).have): + return true; + } + return false; + } + }, + "hellforge": { + preReq: function () { + const cLvl = me.charlvl; + return (me.accessToAct(4) && (me.classic || me.anya) && ((me.normal && cLvl >= 24) || (me.nightmare && cLvl >= 40) || (me.hell && cLvl >= 80))); + }, + skipIf: function () { + return (me.hellforge || me.getQuest(sdk.quest.id.HellsForge, sdk.quest.states.ReqComplete)); + }, + shouldRun: function () { + if (!this.preReq() || this.skipIf()) return false; + return true; + } + }, + "diablo": { + preReq: function () { + const cLvl = me.charlvl; + return (me.accessToAct(4) && ((me.normal && cLvl >= 24) || (me.nightmare && cLvl >= 40) || (me.hell && cLvl >= 80))); + }, + skipIf: function () { + return false; + }, + shouldRun: function () { + if (!this.preReq() || this.skipIf()) return false; + switch (true) { + case !me.diablo: + case (me.normal && me.classic): + case (me.normal && me.expansion && (me.charlvl < 30 || !me.diffCompleted)): + case (me.nightmare && (Pather.canTeleport() || me.charlvl <= 65)): + case (me.hell): + return true; + } + return false; + } + }, + "shenk": { + preReq: function () { + return (me.expansion && me.accessToAct(5)); + }, + skipIf: function () { + return false; + }, + shouldRun: function () { + if (!this.preReq() || this.skipIf()) return false; + switch (true) { + case !me.shenk: + case (!me.druid || me.charlvl <= 70): + return true; + } + return false; + } + }, + "savebarby": { + preReq: function () { + return (me.expansion && me.accessToAct(5)); + }, + skipIf: function () { + return me.savebarby; + }, + shouldRun: function () { + if (!this.preReq() || this.skipIf()) return false; + switch (true) { + case Runewords.checkRune(sdk.items.runes.Tal, sdk.items.runes.Ral, sdk.items.runes.Ort): + return true; + } + return false; + } + }, + "anya": { + preReq: function () { + return (me.expansion && me.accessToAct(5)); + }, + skipIf: function () { + return me.anya; + }, + shouldRun: function () { + if (!this.preReq() || this.skipIf()) return false; + return true; + } + }, + "pindle": { + preReq: function () { + return (me.expansion && me.accessToAct(5) && me.anya); + }, + skipIf: function () { + return false; + }, + shouldRun: function () { + if (!this.preReq() || this.skipIf()) return false; + return true; + } + }, + "ancients": { + preReq: function () { + return (me.expansion && me.accessToAct(5)); + }, + skipIf: function () { + return me.ancients; + }, + shouldRun: function () { + if (!this.preReq() || this.skipIf()) return false; + return true; + } + }, + "baal": { + preReq: function () { + return (me.expansion && me.accessToAct(5)); + }, + skipIf: function () { + return !me.ancients; + }, + shouldRun: function () { + if (!this.preReq() || this.skipIf()) return false; + return true; + } + }, + "a5chests": { + preReq: function () { + return (me.expansion && me.accessToAct(5) && me.baal); + }, + skipIf: function () { + return me.normal; + }, + shouldRun: function () { + if (!this.preReq() || this.skipIf()) return false; + return true; + } + }, + "getkeys": { + preReq: function () { + return (me.expansion && me.accessToAct(5) && me.hell); + }, + skipIf: function () { + return (["Zealer", "Smiter", "Uberconc"].indexOf(SetUp.currentBuild) === -1); + }, + shouldRun: function () { + if (!this.preReq() || this.skipIf()) return false; + return true; + } + }, + "orgtorch": { + preReq: function () { + return (me.expansion && me.accessToAct(5) && me.hell); + }, + skipIf: function () { + return (["Zealer", "Smiter", "Uberconc"].indexOf(SetUp.currentBuild) === -1); + }, + shouldRun: function () { + if (!this.preReq() || this.skipIf()) return false; + return true; + } + }, + } }; diff --git a/libs/SoloPlay/Tools/Tracker.js b/libs/SoloPlay/Tools/Tracker.js index 4fd3e120..d41ae78e 100644 --- a/libs/SoloPlay/Tools/Tracker.js +++ b/libs/SoloPlay/Tools/Tracker.js @@ -10,208 +10,208 @@ includeIfNotIncluded("SoloPlay/Tools/Developer.js"); includeIfNotIncluded("SoloPlay/Functions/PrototypeOverrides.js"); const Tracker = { - GTPath: "libs/SoloPlay/Data/" + me.profile + "/" + me.profile + "-GameTime.json", - LPPath: "libs/SoloPlay/Data/" + me.profile + "/" + me.profile + "-LevelingPerformance.csv", - SPPath: "libs/SoloPlay/Data/" + me.profile + "/" + me.profile + "-ScriptPerformance.csv", - // Leveling Performance - LPHeader: "Total Time,InGame Time,Split Time,Area,Character Level,Gained EXP,Gained EXP/Minute,Difficulty,Gold,Fire Resist,Cold Resist,Light Resist,Poison Resist,Current Build" + "\n", - // Script Performance - SPHeader: "Total Time,InGame Time,Sequence Time,Sequence,Character Level,Gained EXP,Gained EXP/Minute,EXP Gain %,Difficulty,Gold,Fire Resist,Cold Resist,Light Resist,Poison Resist,Current Build" + "\n", - tick: 0, - default: { - "Total": 0, - "InGame": 0, - "OOG": 0, - "LastLevel": 0, - "LastSave": getTickCount() - }, - - initialize: function () { - const GameTracker = Object.assign({}, this.default); - - // Create Files - if (!FileTools.exists("libs/SoloPlay/Data/" + me.profile)) { - let folder = dopen("libs/SoloPlay/Data"); - folder.create(me.profile); - } - - !FileTools.exists(this.GTPath) && Tracker.writeObj(GameTracker, this.GTPath); - !FileTools.exists(this.LPPath) && FileAction.write(this.LPPath, this.LPHeader); - !FileTools.exists(this.SPPath) && FileAction.write(this.SPPath, this.SPHeader); - - return true; - }, - - getObj: function (path) { - let obj, OBJstring = FileAction.read(path); - - try { - obj = JSON.parse(OBJstring); - } catch (e) { - // If we failed, file might be corrupted, so create a new one - Misc.errorReport(e, "Tracker"); - FileTools.remove(path); - Tracker.initialize(); - OBJstring = FileAction.read(path); - obj = JSON.parse(OBJstring); - } - - if (obj) { - return obj; - } - - console.error("ÿc8Kolbot-SoloPlayÿc0: Failed to read Obj. (Tracker.getObj)"); - - return false; - }, - - readObj: function (jsonPath) { - let obj = this.getObj(jsonPath); - return clone(obj); - }, - - writeObj: function (obj, path) { - let string; - try { - string = JSON.stringify(obj, null, 2); - // try to parse the string to ensure it converted correctly - JSON.parse(string); - // JSON.parse throws an error if it fails so if we are here now we are good - FileAction.write(path, string); - } catch (e) { - console.warn("Malformed JSON object"); - console.error(e); - return false; - } - - return true; - }, - - resetGameTime: function () { - Tracker.writeObj(Object.assign({}, this.default), this.GTPath); - }, - - reset: function () { - this.resetGameTime(); - // for now just re-init the header so it's easier to look at the file and see where we restarted - // might later save the files to a sub folder and re-init a new one - FileTools.exists(this.LPPath) && FileAction.append(this.LPPath, this.LPHeader); - FileTools.exists(this.SPPath) && FileAction.append(this.SPPath, this.SPHeader); - }, - - checkValidity: function () { - const GameTracker = Tracker.readObj(this.GTPath); - let found = false; - GameTracker && Object.keys(GameTracker).forEach(function (key) { - if (GameTracker[key] < 0) { - console.debug("Negative value found"); - GameTracker[key] = 0; - found = true; - } - }); - found && Tracker.writeObj(GameTracker, this.GTPath); - }, - - totalDays: function (milliseconds) { - let days = Math.floor(milliseconds / 86.4e6).toFixed(0); - return days.toString().padStart(1, "0"); - }, - - script: function (starttime, subscript, startexp) { - const GameTracker = Tracker.readObj(Tracker.GTPath); - - // GameTracker - // this seems to happen when my pc restarts so set last save equal to current tick count and then continue - GameTracker.LastSave > getTickCount() && (GameTracker.LastSave = getTickCount()); - - const newTick = me.gamestarttime >= GameTracker.LastSave ? me.gamestarttime : GameTracker.LastSave; - GameTracker.InGame += Time.elapsed(newTick); - GameTracker.Total += Time.elapsed(newTick); - GameTracker.LastSave = getTickCount(); - Tracker.writeObj(GameTracker, Tracker.GTPath); - - // csv file - const scriptTime = Time.elapsed(starttime); - const currLevel = me.charlvl; - const diffString = sdk.difficulty.nameOf(me.diff); - const gainAMT = me.getStat(sdk.stats.Experience) - startexp; - const gainTime = gainAMT / (scriptTime / 60000); - const gainPercent = currLevel === 99 ? 0 : (gainAMT * 100 / Experience.nextExp[currLevel]).toFixed(6); - const currentBuild = SetUp.currentBuild; - const [GOLD, FR, CR, LR, PR] = [me.gold, me.realFR, me.realCR, me.realLR, me.realPR]; - const string = ( - Time.format(GameTracker.Total) + "," + Time.format(GameTracker.InGame) + "," + Time.format(scriptTime) - + "," + subscript + "," + currLevel + "," + gainAMT + "," + gainTime + "," + gainPercent + "," + diffString - + "," + GOLD + "," + FR + "," + CR + "," + LR + "," + PR + "," + currentBuild + "\n" - ); - - FileAction.append(Tracker.SPPath, string); - Tracker.tick = GameTracker.LastSave; - - return true; - }, - - leveling: function () { - const GameTracker = Tracker.readObj(this.GTPath); - - // GameTracker - // this seems to happen when my pc restarts so set last save equal to current tick count and then continue - GameTracker.LastSave > getTickCount() && (GameTracker.LastSave = getTickCount()); - - const newSave = getTickCount(); - const newTick = me.gamestarttime > GameTracker.LastSave ? me.gamestarttime : GameTracker.LastSave; - const splitTime = Time.elapsed(GameTracker.LastLevel); - GameTracker.InGame += Time.elapsed(newTick); - GameTracker.Total += Time.elapsed(newTick); - GameTracker.LastLevel = newSave; - GameTracker.LastSave = newSave; - Tracker.writeObj(GameTracker, Tracker.GTPath); - - // csv file - const diffString = sdk.difficulty.nameOf(me.diff); - const areaName = getAreaName(me.area); - const currentBuild = SetUp.currentBuild; - const gainAMT = me.getStat(sdk.stats.Experience) - Experience.totalExp[me.charlvl - 1]; - const gainTime = gainAMT / (splitTime / 60000); - const [GOLD, FR, CR, LR, PR] = [me.gold, me.realFR, me.realCR, me.realLR, me.realPR]; - const string = ( - Time.format(GameTracker.Total) + "," + Time.format(GameTracker.InGame) + "," + Time.format(splitTime) + "," - + areaName + "," + me.charlvl + "," + gainAMT + "," + gainTime + "," + diffString + "," - + GOLD + "," + FR + "," + CR + "," + LR + "," + PR + "," + currentBuild + "\n" - ); - - FileAction.append(Tracker.LPPath, string); - Tracker.tick = GameTracker.LastSave; - - return true; - }, - - update: function (oogTick = 0) { - let heartBeat = getScript("threads/heartbeat.js"); - if (!heartBeat) { - console.debug("Couldn't find heartbeat"); - return false; - } - if (!me.ingame) { - console.debug("Not in game"); - return false; - } - - const GameTracker = Tracker.readObj(this.GTPath); - - // this seems to happen when my pc restarts so set last save equal to current tick count and then continue - GameTracker.LastSave > getTickCount() && (GameTracker.LastSave = getTickCount()); - - // make sure we aren't attempting to use a corrupted file (only way we get negative values) - const newTick = me.gamestarttime > GameTracker.LastSave ? me.gamestarttime : GameTracker.LastSave; - - GameTracker.OOG += oogTick; - GameTracker.InGame += Time.elapsed(newTick); - GameTracker.Total += (Time.elapsed(newTick) + oogTick); - GameTracker.LastSave = getTickCount(); - Tracker.writeObj(GameTracker, Tracker.GTPath); - Tracker.tick = GameTracker.LastSave; - - return true; - } + GTPath: "libs/SoloPlay/Data/" + me.profile + "/" + me.profile + "-GameTime.json", + LPPath: "libs/SoloPlay/Data/" + me.profile + "/" + me.profile + "-LevelingPerformance.csv", + SPPath: "libs/SoloPlay/Data/" + me.profile + "/" + me.profile + "-ScriptPerformance.csv", + // Leveling Performance + LPHeader: "Total Time,InGame Time,Split Time,Area,Character Level,Gained EXP,Gained EXP/Minute,Difficulty,Gold,Fire Resist,Cold Resist,Light Resist,Poison Resist,Current Build" + "\n", + // Script Performance + SPHeader: "Total Time,InGame Time,Sequence Time,Sequence,Character Level,Gained EXP,Gained EXP/Minute,EXP Gain %,Difficulty,Gold,Fire Resist,Cold Resist,Light Resist,Poison Resist,Current Build" + "\n", + tick: 0, + default: { + "Total": 0, + "InGame": 0, + "OOG": 0, + "LastLevel": 0, + "LastSave": getTickCount() + }, + + initialize: function () { + const GameTracker = Object.assign({}, this.default); + + // Create Files + if (!FileTools.exists("libs/SoloPlay/Data/" + me.profile)) { + let folder = dopen("libs/SoloPlay/Data"); + folder.create(me.profile); + } + + !FileTools.exists(this.GTPath) && Tracker.writeObj(GameTracker, this.GTPath); + !FileTools.exists(this.LPPath) && FileAction.write(this.LPPath, this.LPHeader); + !FileTools.exists(this.SPPath) && FileAction.write(this.SPPath, this.SPHeader); + + return true; + }, + + getObj: function (path) { + let obj, OBJstring = FileAction.read(path); + + try { + obj = JSON.parse(OBJstring); + } catch (e) { + // If we failed, file might be corrupted, so create a new one + Misc.errorReport(e, "Tracker"); + FileTools.remove(path); + Tracker.initialize(); + OBJstring = FileAction.read(path); + obj = JSON.parse(OBJstring); + } + + if (obj) { + return obj; + } + + console.error("ÿc8Kolbot-SoloPlayÿc0: Failed to read Obj. (Tracker.getObj)"); + + return false; + }, + + readObj: function (jsonPath) { + let obj = this.getObj(jsonPath); + return clone(obj); + }, + + writeObj: function (obj, path) { + let string; + try { + string = JSON.stringify(obj, null, 2); + // try to parse the string to ensure it converted correctly + JSON.parse(string); + // JSON.parse throws an error if it fails so if we are here now we are good + FileAction.write(path, string); + } catch (e) { + console.warn("Malformed JSON object"); + console.error(e); + return false; + } + + return true; + }, + + resetGameTime: function () { + Tracker.writeObj(Object.assign({}, this.default), this.GTPath); + }, + + reset: function () { + this.resetGameTime(); + // for now just re-init the header so it's easier to look at the file and see where we restarted + // might later save the files to a sub folder and re-init a new one + FileTools.exists(this.LPPath) && FileAction.append(this.LPPath, this.LPHeader); + FileTools.exists(this.SPPath) && FileAction.append(this.SPPath, this.SPHeader); + }, + + checkValidity: function () { + const GameTracker = Tracker.readObj(this.GTPath); + let found = false; + GameTracker && Object.keys(GameTracker).forEach(function (key) { + if (GameTracker[key] < 0) { + console.debug("Negative value found"); + GameTracker[key] = 0; + found = true; + } + }); + found && Tracker.writeObj(GameTracker, this.GTPath); + }, + + totalDays: function (milliseconds) { + let days = Math.floor(milliseconds / 86.4e6).toFixed(0); + return days.toString().padStart(1, "0"); + }, + + script: function (starttime, subscript, startexp) { + const GameTracker = Tracker.readObj(Tracker.GTPath); + + // GameTracker + // this seems to happen when my pc restarts so set last save equal to current tick count and then continue + GameTracker.LastSave > getTickCount() && (GameTracker.LastSave = getTickCount()); + + const newTick = me.gamestarttime >= GameTracker.LastSave ? me.gamestarttime : GameTracker.LastSave; + GameTracker.InGame += Time.elapsed(newTick); + GameTracker.Total += Time.elapsed(newTick); + GameTracker.LastSave = getTickCount(); + Tracker.writeObj(GameTracker, Tracker.GTPath); + + // csv file + const scriptTime = Time.elapsed(starttime); + const currLevel = me.charlvl; + const diffString = sdk.difficulty.nameOf(me.diff); + const gainAMT = me.getStat(sdk.stats.Experience) - startexp; + const gainTime = gainAMT / (scriptTime / 60000); + const gainPercent = currLevel === 99 ? 0 : (gainAMT * 100 / Experience.nextExp[currLevel]).toFixed(6); + const currentBuild = SetUp.currentBuild; + const [GOLD, FR, CR, LR, PR] = [me.gold, me.realFR, me.realCR, me.realLR, me.realPR]; + const string = ( + Time.format(GameTracker.Total) + "," + Time.format(GameTracker.InGame) + "," + Time.format(scriptTime) + + "," + subscript + "," + currLevel + "," + gainAMT + "," + gainTime + "," + gainPercent + "," + diffString + + "," + GOLD + "," + FR + "," + CR + "," + LR + "," + PR + "," + currentBuild + "\n" + ); + + FileAction.append(Tracker.SPPath, string); + Tracker.tick = GameTracker.LastSave; + + return true; + }, + + leveling: function () { + const GameTracker = Tracker.readObj(this.GTPath); + + // GameTracker + // this seems to happen when my pc restarts so set last save equal to current tick count and then continue + GameTracker.LastSave > getTickCount() && (GameTracker.LastSave = getTickCount()); + + const newSave = getTickCount(); + const newTick = me.gamestarttime > GameTracker.LastSave ? me.gamestarttime : GameTracker.LastSave; + const splitTime = Time.elapsed(GameTracker.LastLevel); + GameTracker.InGame += Time.elapsed(newTick); + GameTracker.Total += Time.elapsed(newTick); + GameTracker.LastLevel = newSave; + GameTracker.LastSave = newSave; + Tracker.writeObj(GameTracker, Tracker.GTPath); + + // csv file + const diffString = sdk.difficulty.nameOf(me.diff); + const areaName = getAreaName(me.area); + const currentBuild = SetUp.currentBuild; + const gainAMT = me.getStat(sdk.stats.Experience) - Experience.totalExp[me.charlvl - 1]; + const gainTime = gainAMT / (splitTime / 60000); + const [GOLD, FR, CR, LR, PR] = [me.gold, me.realFR, me.realCR, me.realLR, me.realPR]; + const string = ( + Time.format(GameTracker.Total) + "," + Time.format(GameTracker.InGame) + "," + Time.format(splitTime) + "," + + areaName + "," + me.charlvl + "," + gainAMT + "," + gainTime + "," + diffString + "," + + GOLD + "," + FR + "," + CR + "," + LR + "," + PR + "," + currentBuild + "\n" + ); + + FileAction.append(Tracker.LPPath, string); + Tracker.tick = GameTracker.LastSave; + + return true; + }, + + update: function (oogTick = 0) { + let heartBeat = getScript("threads/heartbeat.js"); + if (!heartBeat) { + console.debug("Couldn't find heartbeat"); + return false; + } + if (!me.ingame) { + console.debug("Not in game"); + return false; + } + + const GameTracker = Tracker.readObj(this.GTPath); + + // this seems to happen when my pc restarts so set last save equal to current tick count and then continue + GameTracker.LastSave > getTickCount() && (GameTracker.LastSave = getTickCount()); + + // make sure we aren't attempting to use a corrupted file (only way we get negative values) + const newTick = me.gamestarttime > GameTracker.LastSave ? me.gamestarttime : GameTracker.LastSave; + + GameTracker.OOG += oogTick; + GameTracker.InGame += Time.elapsed(newTick); + GameTracker.Total += (Time.elapsed(newTick) + oogTick); + GameTracker.LastSave = getTickCount(); + Tracker.writeObj(GameTracker, Tracker.GTPath); + Tracker.tick = GameTracker.LastSave; + + return true; + } }; diff --git a/libs/SoloPlay/Utils/General.js b/libs/SoloPlay/Utils/General.js index 6cee61f4..f4839f3a 100644 --- a/libs/SoloPlay/Utils/General.js +++ b/libs/SoloPlay/Utils/General.js @@ -1,122 +1,122 @@ (function (module) { - // these builds are not possible to do on classic - const impossibleClassicBuilds = ["Bumper", "Socketmule", "Witchyzon", "Auradin", "Torchadin", "Immortalwhirl", "Sancdreamer", "Faithbowzon", "Wfzon"]; - // these builds are not possible to do without ladder runewords - const impossibleNonLadderBuilds = ["Auradin", "Sancdreamer", "Faithbowzon"]; + // these builds are not possible to do on classic + const impossibleClassicBuilds = ["Bumper", "Socketmule", "Witchyzon", "Auradin", "Torchadin", "Immortalwhirl", "Sancdreamer", "Faithbowzon", "Wfzon"]; + // these builds are not possible to do without ladder runewords + const impossibleNonLadderBuilds = ["Auradin", "Sancdreamer", "Faithbowzon"]; - // SoloPlay general gameplay items - const nipItems = { - General: [ - "[name] == tomeoftownportal", - "[name] == tomeofidentify", - "[name] == gold # [gold] >= me.charlvl * 3 * me.diff", - "[name] == gold && [distance] < 5 # [gold] >= 1", - "(me.charlvl < 20 || me.gold < 500) && [name] == minorhealingpotion", - "(me.charlvl < 25 || me.gold < 2000) && [name] == lighthealingpotion", - "(me.charlvl < 29 || me.gold < 5000) && [name] == healingpotion", - "[name] == greaterhealingpotion", - "[name] == superhealingpotion", - "(me.charlvl < 20 || me.gold < 1000) && [name] == minormanapotion", - "[name] == lightmanapotion", - "[name] == manapotion", - "[name] == greatermanapotion", - "[name] == supermanapotion", - "[name] == rejuvenationpotion", - "[name] == fullrejuvenationpotion", - "[name] == scrolloftownportal # # [maxquantity] == 20", - "[name] == scrollofidentify # # [maxquantity] == 20", - "[name] == key # # [maxquantity] == 12", - ], + // SoloPlay general gameplay items + const nipItems = { + General: [ + "[name] == tomeoftownportal", + "[name] == tomeofidentify", + "[name] == gold # [gold] >= me.charlvl * 3 * me.diff", + "[name] == gold && [distance] < 5 # [gold] >= 1", + "(me.charlvl < 20 || me.gold < 500) && [name] == minorhealingpotion", + "(me.charlvl < 25 || me.gold < 2000) && [name] == lighthealingpotion", + "(me.charlvl < 29 || me.gold < 5000) && [name] == healingpotion", + "[name] == greaterhealingpotion", + "[name] == superhealingpotion", + "(me.charlvl < 20 || me.gold < 1000) && [name] == minormanapotion", + "[name] == lightmanapotion", + "[name] == manapotion", + "[name] == greatermanapotion", + "[name] == supermanapotion", + "[name] == rejuvenationpotion", + "[name] == fullrejuvenationpotion", + "[name] == scrolloftownportal # # [maxquantity] == 20", + "[name] == scrollofidentify # # [maxquantity] == 20", + "[name] == key # # [maxquantity] == 12", + ], - Quest: [ - "[name] == mephisto'ssoulstone", - "[name] == hellforgehammer", - "[name] == scrollofinifuss", - "[name] == keytothecairnstones", - "[name] == bookofskill", - "[name] == horadriccube", - "[name] == shaftofthehoradricstaff", - "[name] == topofthehoradricstaff", - "[name] == horadricstaff", - "[name] == ajadefigurine", - "[name] == thegoldenbird", - "[name] == potionoflife", - "[name] == lamesen'stome", - "[name] == khalim'seye", - "[name] == khalim'sheart", - "[name] == khalim'sbrain", - "[name] == khalim'sflail", - "[name] == khalim'swill", - "[name] == scrollofresistance", - ], - }; - - const addSocketableObj = (classid, socketWith = [], temp = [], useSocketQuest = false, condition = () => {}) => ({ - classid: classid, - socketWith: socketWith, - temp: temp, - useSocketQuest: useSocketQuest, - condition: condition - }); - const basicSocketables = { - caster: [], - all: [], - }; - // insight base - basicSocketables.all.push(addSocketableObj(sdk.items.Bill, [], [], true, (item) => - me.nightmare && item.ilvl >= 26 && item.isBaseType && item.ethereal - )); - // insight base - basicSocketables.all.push(addSocketableObj(sdk.items.ColossusVoulge, [], [], true, (item) => - me.nightmare && item.ilvl >= 26 && item.isBaseType && item.ethereal - )); - // Crown of Ages - basicSocketables.caster.push(addSocketableObj(sdk.items.Corona, [sdk.items.runes.Ber, sdk.items.runes.Um], [sdk.items.gems.Perfect.Ruby], - false, (item) => item.unique - )); - // Moser's - basicSocketables.caster.push(addSocketableObj(sdk.items.RoundShield, [sdk.items.runes.Um], [sdk.items.gems.Perfect.Diamond], - false, (item) => item.unique && !item.ethereal - )); - // Spirit Forge - basicSocketables.caster.push(addSocketableObj(sdk.items.LinkedMail, [sdk.items.runes.Shael], [sdk.items.gems.Perfect.Ruby], - false, (item) => item.unique && !item.ethereal - )); - // Dijjin Slayer - basicSocketables.caster.push(addSocketableObj(sdk.items.Ataghan, [sdk.items.runes.Amn], [sdk.items.gems.Perfect.Skull], - false, (item) => !Check.currentBuild().caster && item.unique && !item.ethereal - )); - // Bone Hew - for merc - basicSocketables.caster.push(addSocketableObj(sdk.items.OgreAxe, [sdk.items.runes.Hel, sdk.items.runes.Amn], [sdk.items.gems.Perfect.Skull], - false, (item) => item.unique - )); - // spirit base - basicSocketables.caster.push(addSocketableObj(sdk.items.BroadSword, [], [], true, (item) => - me.normal && !Check.haveBase("sword", 4) && !me.checkItem({ name: sdk.locale.items.Spirit, itemtype: sdk.items.type.Sword }).have - && item.ilvl >= 26 && item.isBaseType && !item.ethereal - )); - // spirit base - basicSocketables.caster.push(addSocketableObj(sdk.items.CrystalSword, [], [], true, (item) => - me.normal && !Check.haveBase("sword", 4) && !me.checkItem({ name: sdk.locale.items.Spirit, itemtype: sdk.items.type.Sword }).have - && item.ilvl >= 26 && item.ilvl <= 40 && item.isBaseType && !item.ethereal - )); - // Lidless - basicSocketables.caster.push(addSocketableObj(sdk.items.GrimShield, [sdk.items.runes.Um], [sdk.items.gems.Perfect.Diamond], !me.hell, (item) => - item.unique && (item.isInStorage || (item.isEquipped && !item.isOnSwap)) && !item.ethereal - )); + Quest: [ + "[name] == mephisto'ssoulstone", + "[name] == hellforgehammer", + "[name] == scrollofinifuss", + "[name] == keytothecairnstones", + "[name] == bookofskill", + "[name] == horadriccube", + "[name] == shaftofthehoradricstaff", + "[name] == topofthehoradricstaff", + "[name] == horadricstaff", + "[name] == ajadefigurine", + "[name] == thegoldenbird", + "[name] == potionoflife", + "[name] == lamesen'stome", + "[name] == khalim'seye", + "[name] == khalim'sheart", + "[name] == khalim'sbrain", + "[name] == khalim'sflail", + "[name] == khalim'swill", + "[name] == scrollofresistance", + ], + }; + + const addSocketableObj = (classid, socketWith = [], temp = [], useSocketQuest = false, condition = () => {}) => ({ + classid: classid, + socketWith: socketWith, + temp: temp, + useSocketQuest: useSocketQuest, + condition: condition + }); + const basicSocketables = { + caster: [], + all: [], + }; + // insight base + basicSocketables.all.push(addSocketableObj(sdk.items.Bill, [], [], true, (item) => + me.nightmare && item.ilvl >= 26 && item.isBaseType && item.ethereal + )); + // insight base + basicSocketables.all.push(addSocketableObj(sdk.items.ColossusVoulge, [], [], true, (item) => + me.nightmare && item.ilvl >= 26 && item.isBaseType && item.ethereal + )); + // Crown of Ages + basicSocketables.caster.push(addSocketableObj(sdk.items.Corona, [sdk.items.runes.Ber, sdk.items.runes.Um], [sdk.items.gems.Perfect.Ruby], + false, (item) => item.unique + )); + // Moser's + basicSocketables.caster.push(addSocketableObj(sdk.items.RoundShield, [sdk.items.runes.Um], [sdk.items.gems.Perfect.Diamond], + false, (item) => item.unique && !item.ethereal + )); + // Spirit Forge + basicSocketables.caster.push(addSocketableObj(sdk.items.LinkedMail, [sdk.items.runes.Shael], [sdk.items.gems.Perfect.Ruby], + false, (item) => item.unique && !item.ethereal + )); + // Dijjin Slayer + basicSocketables.caster.push(addSocketableObj(sdk.items.Ataghan, [sdk.items.runes.Amn], [sdk.items.gems.Perfect.Skull], + false, (item) => !Check.currentBuild().caster && item.unique && !item.ethereal + )); + // Bone Hew - for merc + basicSocketables.caster.push(addSocketableObj(sdk.items.OgreAxe, [sdk.items.runes.Hel, sdk.items.runes.Amn], [sdk.items.gems.Perfect.Skull], + false, (item) => item.unique + )); + // spirit base + basicSocketables.caster.push(addSocketableObj(sdk.items.BroadSword, [], [], true, (item) => + me.normal && !Check.haveBase("sword", 4) && !me.checkItem({ name: sdk.locale.items.Spirit, itemtype: sdk.items.type.Sword }).have + && item.ilvl >= 26 && item.isBaseType && !item.ethereal + )); + // spirit base + basicSocketables.caster.push(addSocketableObj(sdk.items.CrystalSword, [], [], true, (item) => + me.normal && !Check.haveBase("sword", 4) && !me.checkItem({ name: sdk.locale.items.Spirit, itemtype: sdk.items.type.Sword }).have + && item.ilvl >= 26 && item.ilvl <= 40 && item.isBaseType && !item.ethereal + )); + // Lidless + basicSocketables.caster.push(addSocketableObj(sdk.items.GrimShield, [sdk.items.runes.Um], [sdk.items.gems.Perfect.Diamond], !me.hell, (item) => + item.unique && (item.isInStorage || (item.isEquipped && !item.isOnSwap)) && !item.ethereal + )); - const buildAutoBuildTempObj = (update = () => {}) => ({ - SkillPoints: [-1], - StatPoints: [-1, -1, -1, -1, -1], - Update: update - }); + const buildAutoBuildTempObj = (update = () => {}) => ({ + SkillPoints: [-1], + StatPoints: [-1, -1, -1, -1, -1], + Update: update + }); - module.exports = { - impossibleClassicBuilds: impossibleClassicBuilds, - impossibleNonLadderBuilds: impossibleNonLadderBuilds, - nipItems: nipItems, - basicSocketables: basicSocketables, - addSocketableObj: addSocketableObj, - buildAutoBuildTempObj: buildAutoBuildTempObj, - }; + module.exports = { + impossibleClassicBuilds: impossibleClassicBuilds, + impossibleNonLadderBuilds: impossibleNonLadderBuilds, + nipItems: nipItems, + basicSocketables: basicSocketables, + addSocketableObj: addSocketableObj, + buildAutoBuildTempObj: buildAutoBuildTempObj, + }; })(module); diff --git a/libs/SoloPlay/Utils/Init.js b/libs/SoloPlay/Utils/Init.js index 5346fec0..2a541c95 100644 --- a/libs/SoloPlay/Utils/Init.js +++ b/libs/SoloPlay/Utils/Init.js @@ -6,59 +6,59 @@ */ (function() { - // Only load this in global scope - if (getScript(true).name.toLowerCase() === "libs\\soloplay\\soloplay.js") { - myPrint("start setup"); - const { nipItems, impossibleClassicBuilds, impossibleNonLadderBuilds } = require("../Utils/General"); - NTIP.buildList(nipItems.Quest, nipItems.General); + // Only load this in global scope + if (getScript(true).name.toLowerCase() === "libs\\soloplay\\soloplay.js") { + myPrint("start setup"); + const { nipItems, impossibleClassicBuilds, impossibleNonLadderBuilds } = require("../Utils/General"); + NTIP.buildList(nipItems.Quest, nipItems.General); - try { - if (impossibleClassicBuilds.includes(SetUp.finalBuild) && me.classic) { - throw new Error("Kolbot-SoloPlay: " + SetUp.finalBuild + " cannot be used in classic. Change the info tag or remake as an expansion character...Shutting down"); - } + try { + if (impossibleClassicBuilds.includes(SetUp.finalBuild) && me.classic) { + throw new Error("Kolbot-SoloPlay: " + SetUp.finalBuild + " cannot be used in classic. Change the info tag or remake as an expansion character...Shutting down"); + } - if (impossibleNonLadderBuilds.includes(SetUp.finalBuild) && !Developer.addLadderRW) { - throw new Error("Kolbot-SoloPlay: " + SetUp.finalBuild + " cannot be used in non-ladder as they require ladder runewords. Change the info tag or remake as an ladder character...Shutting down"); - } - } catch (e) { - D2Bot.printToConsole(e, sdk.colors.D2Bot.Red); - FileTools.remove("data/" + me.profile + ".json"); - FileTools.remove("libs/SoloPlay/Data/" + me.profile + ".GameTime" + ".json"); - D2Bot.stop(); - } + if (impossibleNonLadderBuilds.includes(SetUp.finalBuild) && !Developer.addLadderRW) { + throw new Error("Kolbot-SoloPlay: " + SetUp.finalBuild + " cannot be used in non-ladder as they require ladder runewords. Change the info tag or remake as an ladder character...Shutting down"); + } + } catch (e) { + D2Bot.printToConsole(e, sdk.colors.D2Bot.Red); + FileTools.remove("data/" + me.profile + ".json"); + FileTools.remove("libs/SoloPlay/Data/" + me.profile + ".GameTime" + ".json"); + D2Bot.stop(); + } - if (me.charlvl === 1) { - let buckler = me.getItem(sdk.items.Buckler); - !!buckler && buckler.isEquipped && buckler.drop(); - } + if (me.charlvl === 1) { + let buckler = me.getItem(sdk.items.Buckler); + !!buckler && buckler.isEquipped && buckler.drop(); + } - Town.heal() && me.cancelUIFlags(); - Check.checkSpecialCase(); + Town.heal() && me.cancelUIFlags(); + Check.checkSpecialCase(); - // check if any of our currently equipped items are no longer usable - can happen after respec - me.getItemsEx() - .filter(item => item.isEquipped) - .forEach(item => { - if (me.getStat(sdk.stats.Strength) < item.strreq - || me.getStat(sdk.stats.Dexterity) < item.dexreq - || item.ethereal && item.isBroken) { - myPrint("No longer able to use: " + item.fname); - Item.removeItem(null, item); - } else if (sdk.quest.items.includes(item.classid)) { - myPrint("Removing Quest Item: " + item.fname); - Item.removeItem(null, item); - } - }); - - me.getItemsEx() - .filter(item => item.isInInventory && sdk.quest.items.includes(item.classid)) - .forEach(item => { - Quest.stashItem(item); - }); - - me.cancelUIFlags(); - // initialize final charms if we have any - CharmEquip.init(); - } - return true; + // check if any of our currently equipped items are no longer usable - can happen after respec + me.getItemsEx() + .filter(item => item.isEquipped) + .forEach(item => { + if (me.getStat(sdk.stats.Strength) < item.strreq + || me.getStat(sdk.stats.Dexterity) < item.dexreq + || item.ethereal && item.isBroken) { + myPrint("No longer able to use: " + item.fname); + Item.removeItem(null, item); + } else if (sdk.quest.items.includes(item.classid)) { + myPrint("Removing Quest Item: " + item.fname); + Item.removeItem(null, item); + } + }); + + me.getItemsEx() + .filter(item => item.isInInventory && sdk.quest.items.includes(item.classid)) + .forEach(item => { + Quest.stashItem(item); + }); + + me.cancelUIFlags(); + // initialize final charms if we have any + CharmEquip.init(); + } + return true; })(); diff --git a/libs/SoloPlay/Workers/EventEmitter.js b/libs/SoloPlay/Workers/EventEmitter.js index e9a40b01..395b6570 100644 --- a/libs/SoloPlay/Workers/EventEmitter.js +++ b/libs/SoloPlay/Workers/EventEmitter.js @@ -7,70 +7,70 @@ */ (function (factory) { - if (typeof module === "object" && typeof module.exports === "object") { - let v = factory(require, exports); - if (v !== undefined) module.exports = v; - } else if (typeof define === "function" && define.amd) { - define(["require", "exports", "../../modules/Worker", "../Modules/Events"], factory); - } + if (typeof module === "object" && typeof module.exports === "object") { + let v = factory(require, exports); + if (v !== undefined) module.exports = v; + } else if (typeof define === "function" && define.amd) { + define(["require", "exports", "../../modules/Worker", "../Modules/Events"], factory); + } })(function (require, exports) { - "use strict"; - Object.defineProperty(exports, "__esModule", { value: true }); + "use strict"; + Object.defineProperty(exports, "__esModule", { value: true }); - const Worker = require("../../modules/Worker"); - require("../Modules/Events"); - const old = { - level: me.charlvl, - }; - const gainedLevels = () => me.charlvl - old.level; + const Worker = require("../../modules/Worker"); + require("../Modules/Events"); + const old = { + level: me.charlvl, + }; + const gainedLevels = () => me.charlvl - old.level; - let levelTimeout = getTickCount(); + let levelTimeout = getTickCount(); - const _AutoBuild = new function () { - this.enabled = true; + const _AutoBuild = new function () { + this.enabled = true; - this.run = function () { - if (!this.enabled) return; + this.run = function () { + if (!this.enabled) return; - try { - let levels = gainedLevels(); + try { + let levels = gainedLevels(); - if (levels > 0 && (Config.AutoSkill.Enabled || Config.AutoStat.Enabled)) { - scriptBroadcast("toggleQuitlist"); - AutoBuild.print("Level up detected (", old.level, "-->", me.charlvl, ")"); - Config.AutoSkill.Enabled && AutoSkill.init(Config.AutoSkill.Build, Config.AutoSkill.Save); - Config.AutoStat.Enabled && AutoStat.init(Config.AutoStat.Build, Config.AutoStat.Save, Config.AutoStat.BlockChance, Config.AutoStat.UseBulk); - scriptBroadcast({ event: "level up" }); - AutoBuild.applyConfigUpdates(); // scriptBroadcast() won't trigger listener on this thread. + if (levels > 0 && (Config.AutoSkill.Enabled || Config.AutoStat.Enabled)) { + scriptBroadcast("toggleQuitlist"); + AutoBuild.print("Level up detected (", old.level, "-->", me.charlvl, ")"); + Config.AutoSkill.Enabled && AutoSkill.init(Config.AutoSkill.Build, Config.AutoSkill.Save); + Config.AutoStat.Enabled && AutoStat.init(Config.AutoStat.Build, Config.AutoStat.Save, Config.AutoStat.BlockChance, Config.AutoStat.UseBulk); + scriptBroadcast({ event: "level up" }); + AutoBuild.applyConfigUpdates(); // scriptBroadcast() won't trigger listener on this thread. - AutoBuild.print("Incrementing cached character level to", old.level + 1); - Tracker.leveling(); + AutoBuild.print("Incrementing cached character level to", old.level + 1); + Tracker.leveling(); - // prevLevel doesn't get set to me.charlvl because - // we may have gained multiple levels at once - old.level += 1; + // prevLevel doesn't get set to me.charlvl because + // we may have gained multiple levels at once + old.level += 1; - scriptBroadcast("toggleQuitlist"); - } - } catch (e) { - this.enabled = false; - console.error(e); - console.warn("Something broke! StackWalk :: ", e.stack); - } - }; - }; + scriptBroadcast("toggleQuitlist"); + } + } catch (e) { + this.enabled = false; + console.error(e); + console.warn("Something broke! StackWalk :: ", e.stack); + } + }; + }; - // Start - Worker.runInBackground.EventWatcher = function () { - // AutoBuild - if (getTickCount() - levelTimeout > 1000) { - levelTimeout = getTickCount(); - _AutoBuild.run(); - } + // Start + Worker.runInBackground.EventWatcher = function () { + // AutoBuild + if (getTickCount() - levelTimeout > 1000) { + levelTimeout = getTickCount(); + _AutoBuild.run(); + } - return true; - }; + return true; + }; - AutoBuild.print("Loaded AutoBuild"); - console.log("ÿc8Kolbot-SoloPlayÿc0: Start AutoBuild"); + AutoBuild.print("Loaded AutoBuild"); + console.log("ÿc8Kolbot-SoloPlayÿc0: Start AutoBuild"); }); diff --git a/libs/SoloPlay/Workers/EventHandler.js b/libs/SoloPlay/Workers/EventHandler.js index 52f6ab40..4c0d22b4 100644 --- a/libs/SoloPlay/Workers/EventHandler.js +++ b/libs/SoloPlay/Workers/EventHandler.js @@ -6,114 +6,114 @@ */ (function (module, require, Worker) { - // Only load this in global scope - if (getScript(true).name.toLowerCase() === "libs\\soloplay\\soloplay.js") { - - let tickDelay = 0; - let [actions, profiles] = [[], []]; - - me.on("soloEvent", function msgEvent (msg) { - switch (msg) { - case "testing": - console.debug(msg, actions); - - break; - case "finishDen": - case "dodge": - case "skip": - case "killdclone": - actions.push(msg); - console.debug(actions); - - break; - } - }); - - me.on("processProfileEvent", function copyDataProcessing (id, info) { - console.debug(id, info); - // Torch - if (id === 55) { - let { profile, ladder, torchType } = JSON.parse(info); - console.log("Mesage recived for torch...processing"); - - if (profile !== me.profile && (me.hell || (me.nightmare && me.baal)) && me.ladder === ladder) { - if (torchType === me.classid && !me.findItem(604, 0, null, 7)) { - console.log("Sent Response"); - SoloEvents.sendToProfile(profile, { profile: me.profile, level: me.charlvl, event: 604 }); - } - } - - return; - } - - // Annhilus - if (id === 60) { - let { profile, ladder } = JSON.parse(info); - console.log("Mesage recived for Annhilus...processing"); - - if (profile !== me.profile && (me.hell || (me.nightmare && me.baal)) && me.ladder === ladder) { - if (!me.findItem(603, 0, null, 7)) { - console.log("Sent Response"); - SoloEvents.sendToProfile(profile, { profile: me.profile, level: me.charlvl, event: 603 }); - } - } - - return; - } - - if (id === 65) { - let { profile, level, event } = JSON.parse(info); - - console.log("Sucess: profile that contacted me: " + profile + " level of char: " + level); - SoloEvents.profileResponded = true; - profiles.push({ profile: profile, level: level, event: event }); - tickDelay += 1000; - } - }); - - let waitTick = getTickCount(); - - // Start - Worker.runInBackground.EventWorker = function () { - if (getTickCount() - waitTick < 100 || SoloEvents.townChicken.running) return true; - waitTick = getTickCount(); - - try { - while (actions.length) { - let wasDisabled = SoloEvents.townChicken.disabled; - try { - SoloEvents[actions.shift()](); - } catch (e) { - console.warn(e); - } finally { - // if we disabled townchicken, re-enable it - if (!wasDisabled && SoloEvents.townChicken.disabled) { - SoloEvents.townChicken.disabled = false; - } - } - } - - if (profiles.length > 0) { - let tick = getTickCount(); - - while (getTickCount() - tick < tickDelay) { - delay(500); - } - - let lowestLevelProf = profiles.sort((a, b) => a.level - b.level).first(); - - SoloEvents.sendToProfile(lowestLevelProf.profile, lowestLevelProf.event, 70); - D2Bot.joinMe(lowestLevelProf.profile, me.gamename.toLowerCase(), "", me.gamepassword.toLowerCase(), true); - profiles = []; - } - } catch (e) { - console.error(e); - } - - return true; - }; - - // should there be a heartbeat for the workers? - console.log("ÿc8Kolbot-SoloPlayÿc0: Start EventHandler"); - } + // Only load this in global scope + if (getScript(true).name.toLowerCase() === "libs\\soloplay\\soloplay.js") { + + let tickDelay = 0; + let [actions, profiles] = [[], []]; + + me.on("soloEvent", function msgEvent (msg) { + switch (msg) { + case "testing": + console.debug(msg, actions); + + break; + case "finishDen": + case "dodge": + case "skip": + case "killdclone": + actions.push(msg); + console.debug(actions); + + break; + } + }); + + me.on("processProfileEvent", function copyDataProcessing (id, info) { + console.debug(id, info); + // Torch + if (id === 55) { + let { profile, ladder, torchType } = JSON.parse(info); + console.log("Mesage recived for torch...processing"); + + if (profile !== me.profile && (me.hell || (me.nightmare && me.baal)) && me.ladder === ladder) { + if (torchType === me.classid && !me.findItem(604, 0, null, 7)) { + console.log("Sent Response"); + SoloEvents.sendToProfile(profile, { profile: me.profile, level: me.charlvl, event: 604 }); + } + } + + return; + } + + // Annhilus + if (id === 60) { + let { profile, ladder } = JSON.parse(info); + console.log("Mesage recived for Annhilus...processing"); + + if (profile !== me.profile && (me.hell || (me.nightmare && me.baal)) && me.ladder === ladder) { + if (!me.findItem(603, 0, null, 7)) { + console.log("Sent Response"); + SoloEvents.sendToProfile(profile, { profile: me.profile, level: me.charlvl, event: 603 }); + } + } + + return; + } + + if (id === 65) { + let { profile, level, event } = JSON.parse(info); + + console.log("Sucess: profile that contacted me: " + profile + " level of char: " + level); + SoloEvents.profileResponded = true; + profiles.push({ profile: profile, level: level, event: event }); + tickDelay += 1000; + } + }); + + let waitTick = getTickCount(); + + // Start + Worker.runInBackground.EventWorker = function () { + if (getTickCount() - waitTick < 100 || SoloEvents.townChicken.running) return true; + waitTick = getTickCount(); + + try { + while (actions.length) { + let wasDisabled = SoloEvents.townChicken.disabled; + try { + SoloEvents[actions.shift()](); + } catch (e) { + console.warn(e); + } finally { + // if we disabled townchicken, re-enable it + if (!wasDisabled && SoloEvents.townChicken.disabled) { + SoloEvents.townChicken.disabled = false; + } + } + } + + if (profiles.length > 0) { + let tick = getTickCount(); + + while (getTickCount() - tick < tickDelay) { + delay(500); + } + + let lowestLevelProf = profiles.sort((a, b) => a.level - b.level).first(); + + SoloEvents.sendToProfile(lowestLevelProf.profile, lowestLevelProf.event, 70); + D2Bot.joinMe(lowestLevelProf.profile, me.gamename.toLowerCase(), "", me.gamepassword.toLowerCase(), true); + profiles = []; + } + } catch (e) { + console.error(e); + } + + return true; + }; + + // should there be a heartbeat for the workers? + console.log("ÿc8Kolbot-SoloPlayÿc0: Start EventHandler"); + } })(module, require, typeof Worker === "object" && Worker || require("../../modules/Worker")); diff --git a/libs/SoloPlay/Workers/TownChicken.js b/libs/SoloPlay/Workers/TownChicken.js index 3eae4540..159c56f9 100644 --- a/libs/SoloPlay/Workers/TownChicken.js +++ b/libs/SoloPlay/Workers/TownChicken.js @@ -11,397 +11,397 @@ * - How many chickens is too many for a script? How to end a script it that amount is reached. */ (function (module, require, Worker) { - // Only load this in global scope - if (getScript(true).name.toLowerCase() === "libs\\soloplay\\soloplay.js") { - const getNearestMonster = () => { - let gid = null; - let monster = Game.getMonster(); - let range = 30; - - if (monster) { - do { - if (monster.attackable && !monster.getParent()) { - let distance = getDistance(me, monster); - - if (distance < range) { - [range, gid] = [distance, monster.gid]; - } - } - } while (monster.getNext()); - } - - gid && (Game.getMonster(-1, -1, gid)); - - if (monster) { - console.log("ÿc9TownChickenÿc0 :: Closest monster to me: " + monster.name + " | Monster classid: " + monster.classid); - return monster.classid; - } - - return -1; - }; - - const usePortal = function (targetArea, owner, unit, dummy) { - if (targetArea && me.inArea(targetArea)) return true; - - me.cancelUIFlags(); - - const townAreaCheck = (area = 0) => sdk.areas.Towns.includes(area); - const preArea = me.area; - const leavingTown = townAreaCheck(preArea); - - for (let i = 0; i < 13; i += 1) { - if (me.dead) return false; - if (targetArea ? me.inArea(targetArea) : me.area !== preArea) return true; - - (i > 0 && owner && me.inTown) && Town.move("portalspot"); - - let portal = unit ? copyUnit(unit) : Pather.getPortal(targetArea, owner); - - if (portal && portal.area === me.area) { - const useTk = me.inTown && Skill.useTK(portal) && i < 3; - if (useTk) { - portal.distance > 21 && (me.inTown && me.act === 5 ? Town.move("portalspot") : Pather.moveNearUnit(portal, 20)); - if (Skill.cast(sdk.skills.Telekinesis, sdk.skills.hand.Right, portal) - && Misc.poll(() => targetArea ? me.inArea(targetArea) : me.area !== preArea)) { - Pather.lastPortalTick = getTickCount(); - delay(100); - - return true; - } - } else { - portal.distance > 5 && (i < 3 ? Pather.moveNearUnit(portal, 4, false) : Pather.moveToUnit(portal)); - - if (getTickCount() - Pather.lastPortalTick > (leavingTown ? 2500 : 1000)) { - i < 2 ? Packet.entityInteract(portal) : Misc.click(0, 0, portal); - } else { - // only delay if we are in town and leaving town, don't delay if we are attempting to portal from out of town since this is the chicken thread - // and we are likely being attacked - leavingTown && delay(300); - - continue; - } - } - - let tick = getTickCount(); - - while (getTickCount() - tick < 500) { - if (me.area !== preArea) { - Pather.lastPortalTick = getTickCount(); - delay(100); - - return true; - } - - delay(10); - } - // try clicking dummy portal - !!dummy && portal.area === 1 && Misc.click(0, 0, portal); - - i > 1 && (i % 3) === 0 && Packet.flash(me.gid); - } else { - console.log("Didn't find portal, retry: " + i); - i > 3 && me.inTown && Town.move("portalspot", false); - if (i === 12) { - let p = Game.getObject("portal"); - console.debug(p); - if (!!p && Misc.click(0, 0, p) && Misc.poll(() => me.area !== preArea, 1000, 100)) { - Pather.lastPortalTick = getTickCount(); - delay(100); - - return true; - } - } - Packet.flash(me.gid); - } - - delay(250); - } - - return (targetArea ? me.inArea(targetArea) : me.area !== preArea); - }; - - const makePortal = function (use = false) { - if (me.inTown) return true; - - let oldGid = -1; - - for (let i = 0; i < 5; i += 1) { - if (me.dead) return false; - - let tpTool = me.getTpTool(); - if (!tpTool) return false; - - let oldPortal = Game.getObject(sdk.objects.BluePortal); - if (oldPortal) { - do { - if (oldPortal.getParent() === me.name) { - oldGid = oldPortal.gid; - break; - } - } while (oldPortal.getNext()); - - // old portal is close to use, we should try to use it - if (oldPortal.getParent() === me.name && oldPortal.distance < 4) { - if (use) { - if (usePortal(null, null, copyUnit(oldPortal))) return true; - break; // don't spam usePortal - } else { - return copyUnit(oldPortal); - } - } - } - - let pingDelay = me.getPingDelay(); - - if (tpTool.use() || Game.getObject("portal")) { - let tick = getTickCount(); - - while (getTickCount() - tick < Math.max(500 + i * 100, pingDelay * 2 + 100)) { - const portal = getUnits(sdk.unittype.Object, "portal") - .filter((p) => p.getParent() === me.name && p.gid !== oldGid).first(); - - if (portal) { - if (use) { - if (usePortal(null, null, copyUnit(portal))) return true; - break; // don't spam usePortal - } else { - return copyUnit(portal); - } - } else { - // check dummy - let dummy = getUnits(sdk.unittype.Object, "portal").filter(p => p.name === "Dummy").first(); - if (dummy) { - console.debug(dummy); - if (use) return usePortal(null, null, dummy, true); - return copyUnit(dummy); - } - } - - delay(10); - } - } else { - console.log("Failed to use tp tool"); - Packet.flash(me.gid, pingDelay); - delay(200 + pingDelay); - } - - delay(40); - } - - return false; - }; - - const goToTown = function (act = 0, wpmenu = false) { - if (!me.inTown) { - const townArea = sdk.areas.townOf(me.act); - try { - !makePortal(true) && console.warn("Town.goToTown: Failed to make TP"); - if (!me.inTown && !usePortal(townArea, me.name)) { - console.warn("Town.goToTown: Failed to take TP"); - if (!me.inTown && !usePortal(sdk.areas.townOf(me.area))) throw new Error("Town.goToTown: Failed to take TP"); - } - } catch (e) { - let tpTool = me.getTpTool(); - if (!tpTool && Misc.getPlayerCount() <= 1) { - Misc.errorReport(new Error("Town.goToTown: Failed to go to town and no tps available. Restart.")); - scriptBroadcast("quit"); - } else { - if (!Misc.poll(() => { - if (me.inTown) return true; - let p = Game.getObject("portal"); - console.debug(p); - !!p && Misc.click(0, 0, p) && delay(100); - Misc.poll(() => me.idle, 1000, 100); - console.debug("inTown? " + me.inTown); - return me.inTown; - }, 700, 100)) { - Misc.errorReport(new Error("Town.goToTown: Failed to go to town. Quiting.")); - scriptBroadcast("quit"); - } - } - } - } - - if (!act) return true; - if (act < 1 || act > 5) throw new Error("Town.goToTown: Invalid act"); - if (act > me.highestAct) return false; - - if (act !== me.act) { - try { - Pather.useWaypoint(sdk.areas.townOfAct(act), wpmenu); - } catch (WPError) { - throw new Error("Town.goToTown: Failed use WP"); - } - } - - return true; - }; - - const visitTown = function () { - console.log("ÿc8Start ÿc0:: ÿc8visitTown"); - - const preArea = me.area; - const preAct = sdk.areas.actOf(preArea); - - if (!me.inTown && !me.getTpTool()) { - console.warn("Can't chicken to town. Quit"); - scriptBroadcast("quit"); - return false; - } - - let tick = getTickCount(); - - // not an essential function -> handle thrown errors - me.cancelUIFlags(); - try { - goToTown(); - } catch (e) { - return false; - } - - const { x, y } = me; - - Town.doChores(); - - console.debug("Current act: " + me.act + " Prev Act: " + preAct); - me.act !== preAct && goToTown(preAct); - Town.move("portalspot"); - Pather.moveTo(x, y); - - while (getTickCount() - tick < 4500) { - delay(10); - } - - if (!usePortal(preArea, me.name)) { - try { - usePortal(null, me.name); - } catch (e) { - throw new Error("Town.visitTown: Failed to go back from town"); - } - } - - console.log("ÿc8End ÿc0:: ÿc8visitTown - currentArea: " + getAreaName(me.area)); - - return me.area === preArea; - }; - - let [townCheck] = [false, false]; - - me.on("townChicken", function townChickenEvent (msg) { - if (SoloEvents.townChicken.disabled) return; - if (typeof msg !== "string") return; - switch (msg) { - case "townCheck": - switch (me.area) { - case sdk.areas.ArreatSummit: - case sdk.areas.UberTristram: - console.warn("Don't tp from " + getAreaName(me.area)); - return; - default: - console.log("townCheck message recieved. First check passed."); - townCheck = true; - - return; - } - case "quit": - //quitFlag = true; - // Maybe stop townChicken thread? Would that keep us from the crash that happens when we try to leave game while townChickening - break; - default: - break; - } - }); - - Misc.townCheck = function () { - return false; - }; - - // const lastChickens = []; - const useHowl = Skill.canUse(sdk.skills.Howl); - const useTerror = Skill.canUse(sdk.skills.Terror); - - Config.DebugMode.Stack = true; - let waitTick = getTickCount(); - let potTick = getTickCount(); - - // Start - Worker.runInBackground.TownChicken = function () { - if (getTickCount() - waitTick < 100 || SoloEvents.townChicken.disabled) return true; - waitTick = getTickCount(); - if (me.inTown) return true; - - let shouldChicken = ( - (townCheck || me.hpPercent < Config.TownHP || me.mpPercent < Config.TownMP) - ); - - if (shouldChicken && !me.canTpToTown()) { - // we should probably quit? - return true; - } - - if (!shouldChicken) { - if (getTickCount() - potTick < 300) return true; - potTick = getTickCount(); - // do we need potions? - if (!Config.TownCheck) return true; - // can we chicken? - if (!me.canTpToTown()) return true; - if (me.needPotions() || (Config.OpenChests.Enabled && Town.needKeys())) { - shouldChicken = true; - } - } - - if (shouldChicken) { - let t4 = getTickCount(); - try { - // const recentChicks = lastChickens - // .slice(Math.max(lastChickens.length - 3), lastChickens.length - 1); - - // const stopCurrentScript = recentChicks.length >= 2 && recentChicks - // .every(([count]) => getTickCount() - count < Time.minutes(1)); - - // lastChickens.push([getTickCount(), me.area, me.x, me.y]); - - // if (stopCurrentScript) { - // myPrint("ÿc8TownChicken :: ÿc0Too many chickens on this script, move to next :: " + me.gold); - // goToTown(); - // // scriptBroadcast("nextScript"); - // me.emit("nextScript"); - // return true; - // } - - myPrint("ÿc8TownChicken :: ÿc0Going to town. Initial Gold :: " + me.gold); - [Attack.stopClear, SoloEvents.townChicken.running] = [true, true]; - - // determine if this is really worth it - if (useHowl || useTerror) { - if ([156, 211, 242, 243, 544, 571, 345].indexOf(getNearestMonster()) === -1) { - if (useHowl && Skill.getManaCost(130) < me.mp) { - Skill.cast(130, sdk.skills.hand.Right); - } - - if (useTerror && Skill.getManaCost(77) < me.mp) { - Skill.cast(77, sdk.skills.hand.Right, getNearestMonster()); - } - } - } - - visitTown(); - } catch (e) { - Misc.errorReport(e, "TownChicken.js"); - scriptBroadcast("quit"); - - return false; - } finally { - Packet.flash(me.gid, 100); - console.log("ÿc8TownChicken :: Took: " + Time.format(getTickCount() - t4) + " to visit town. Ending Gold :: " + me.gold); - [Attack.stopClear, SoloEvents.townChicken.running, townCheck] = [false, false, false]; - } - } - - return true; - }; - - console.log("ÿc8Kolbot-SoloPlayÿc0: Start TownChicken"); - } + // Only load this in global scope + if (getScript(true).name.toLowerCase() === "libs\\soloplay\\soloplay.js") { + const getNearestMonster = () => { + let gid = null; + let monster = Game.getMonster(); + let range = 30; + + if (monster) { + do { + if (monster.attackable && !monster.getParent()) { + let distance = getDistance(me, monster); + + if (distance < range) { + [range, gid] = [distance, monster.gid]; + } + } + } while (monster.getNext()); + } + + gid && (Game.getMonster(-1, -1, gid)); + + if (monster) { + console.log("ÿc9TownChickenÿc0 :: Closest monster to me: " + monster.name + " | Monster classid: " + monster.classid); + return monster.classid; + } + + return -1; + }; + + const usePortal = function (targetArea, owner, unit, dummy) { + if (targetArea && me.inArea(targetArea)) return true; + + me.cancelUIFlags(); + + const townAreaCheck = (area = 0) => sdk.areas.Towns.includes(area); + const preArea = me.area; + const leavingTown = townAreaCheck(preArea); + + for (let i = 0; i < 13; i += 1) { + if (me.dead) return false; + if (targetArea ? me.inArea(targetArea) : me.area !== preArea) return true; + + (i > 0 && owner && me.inTown) && Town.move("portalspot"); + + let portal = unit ? copyUnit(unit) : Pather.getPortal(targetArea, owner); + + if (portal && portal.area === me.area) { + const useTk = me.inTown && Skill.useTK(portal) && i < 3; + if (useTk) { + portal.distance > 21 && (me.inTown && me.act === 5 ? Town.move("portalspot") : Pather.moveNearUnit(portal, 20)); + if (Skill.cast(sdk.skills.Telekinesis, sdk.skills.hand.Right, portal) + && Misc.poll(() => targetArea ? me.inArea(targetArea) : me.area !== preArea)) { + Pather.lastPortalTick = getTickCount(); + delay(100); + + return true; + } + } else { + portal.distance > 5 && (i < 3 ? Pather.moveNearUnit(portal, 4, false) : Pather.moveToUnit(portal)); + + if (getTickCount() - Pather.lastPortalTick > (leavingTown ? 2500 : 1000)) { + i < 2 ? Packet.entityInteract(portal) : Misc.click(0, 0, portal); + } else { + // only delay if we are in town and leaving town, don't delay if we are attempting to portal from out of town since this is the chicken thread + // and we are likely being attacked + leavingTown && delay(300); + + continue; + } + } + + let tick = getTickCount(); + + while (getTickCount() - tick < 500) { + if (me.area !== preArea) { + Pather.lastPortalTick = getTickCount(); + delay(100); + + return true; + } + + delay(10); + } + // try clicking dummy portal + !!dummy && portal.area === 1 && Misc.click(0, 0, portal); + + i > 1 && (i % 3) === 0 && Packet.flash(me.gid); + } else { + console.log("Didn't find portal, retry: " + i); + i > 3 && me.inTown && Town.move("portalspot", false); + if (i === 12) { + let p = Game.getObject("portal"); + console.debug(p); + if (!!p && Misc.click(0, 0, p) && Misc.poll(() => me.area !== preArea, 1000, 100)) { + Pather.lastPortalTick = getTickCount(); + delay(100); + + return true; + } + } + Packet.flash(me.gid); + } + + delay(250); + } + + return (targetArea ? me.inArea(targetArea) : me.area !== preArea); + }; + + const makePortal = function (use = false) { + if (me.inTown) return true; + + let oldGid = -1; + + for (let i = 0; i < 5; i += 1) { + if (me.dead) return false; + + let tpTool = me.getTpTool(); + if (!tpTool) return false; + + let oldPortal = Game.getObject(sdk.objects.BluePortal); + if (oldPortal) { + do { + if (oldPortal.getParent() === me.name) { + oldGid = oldPortal.gid; + break; + } + } while (oldPortal.getNext()); + + // old portal is close to use, we should try to use it + if (oldPortal.getParent() === me.name && oldPortal.distance < 4) { + if (use) { + if (usePortal(null, null, copyUnit(oldPortal))) return true; + break; // don't spam usePortal + } else { + return copyUnit(oldPortal); + } + } + } + + let pingDelay = me.getPingDelay(); + + if (tpTool.use() || Game.getObject("portal")) { + let tick = getTickCount(); + + while (getTickCount() - tick < Math.max(500 + i * 100, pingDelay * 2 + 100)) { + const portal = getUnits(sdk.unittype.Object, "portal") + .filter((p) => p.getParent() === me.name && p.gid !== oldGid).first(); + + if (portal) { + if (use) { + if (usePortal(null, null, copyUnit(portal))) return true; + break; // don't spam usePortal + } else { + return copyUnit(portal); + } + } else { + // check dummy + let dummy = getUnits(sdk.unittype.Object, "portal").filter(p => p.name === "Dummy").first(); + if (dummy) { + console.debug(dummy); + if (use) return usePortal(null, null, dummy, true); + return copyUnit(dummy); + } + } + + delay(10); + } + } else { + console.log("Failed to use tp tool"); + Packet.flash(me.gid, pingDelay); + delay(200 + pingDelay); + } + + delay(40); + } + + return false; + }; + + const goToTown = function (act = 0, wpmenu = false) { + if (!me.inTown) { + const townArea = sdk.areas.townOf(me.act); + try { + !makePortal(true) && console.warn("Town.goToTown: Failed to make TP"); + if (!me.inTown && !usePortal(townArea, me.name)) { + console.warn("Town.goToTown: Failed to take TP"); + if (!me.inTown && !usePortal(sdk.areas.townOf(me.area))) throw new Error("Town.goToTown: Failed to take TP"); + } + } catch (e) { + let tpTool = me.getTpTool(); + if (!tpTool && Misc.getPlayerCount() <= 1) { + Misc.errorReport(new Error("Town.goToTown: Failed to go to town and no tps available. Restart.")); + scriptBroadcast("quit"); + } else { + if (!Misc.poll(() => { + if (me.inTown) return true; + let p = Game.getObject("portal"); + console.debug(p); + !!p && Misc.click(0, 0, p) && delay(100); + Misc.poll(() => me.idle, 1000, 100); + console.debug("inTown? " + me.inTown); + return me.inTown; + }, 700, 100)) { + Misc.errorReport(new Error("Town.goToTown: Failed to go to town. Quiting.")); + scriptBroadcast("quit"); + } + } + } + } + + if (!act) return true; + if (act < 1 || act > 5) throw new Error("Town.goToTown: Invalid act"); + if (act > me.highestAct) return false; + + if (act !== me.act) { + try { + Pather.useWaypoint(sdk.areas.townOfAct(act), wpmenu); + } catch (WPError) { + throw new Error("Town.goToTown: Failed use WP"); + } + } + + return true; + }; + + const visitTown = function () { + console.log("ÿc8Start ÿc0:: ÿc8visitTown"); + + const preArea = me.area; + const preAct = sdk.areas.actOf(preArea); + + if (!me.inTown && !me.getTpTool()) { + console.warn("Can't chicken to town. Quit"); + scriptBroadcast("quit"); + return false; + } + + let tick = getTickCount(); + + // not an essential function -> handle thrown errors + me.cancelUIFlags(); + try { + goToTown(); + } catch (e) { + return false; + } + + const { x, y } = me; + + Town.doChores(); + + console.debug("Current act: " + me.act + " Prev Act: " + preAct); + me.act !== preAct && goToTown(preAct); + Town.move("portalspot"); + Pather.moveTo(x, y); + + while (getTickCount() - tick < 4500) { + delay(10); + } + + if (!usePortal(preArea, me.name)) { + try { + usePortal(null, me.name); + } catch (e) { + throw new Error("Town.visitTown: Failed to go back from town"); + } + } + + console.log("ÿc8End ÿc0:: ÿc8visitTown - currentArea: " + getAreaName(me.area)); + + return me.area === preArea; + }; + + let [townCheck] = [false, false]; + + me.on("townChicken", function townChickenEvent (msg) { + if (SoloEvents.townChicken.disabled) return; + if (typeof msg !== "string") return; + switch (msg) { + case "townCheck": + switch (me.area) { + case sdk.areas.ArreatSummit: + case sdk.areas.UberTristram: + console.warn("Don't tp from " + getAreaName(me.area)); + return; + default: + console.log("townCheck message recieved. First check passed."); + townCheck = true; + + return; + } + case "quit": + //quitFlag = true; + // Maybe stop townChicken thread? Would that keep us from the crash that happens when we try to leave game while townChickening + break; + default: + break; + } + }); + + Misc.townCheck = function () { + return false; + }; + + // const lastChickens = []; + const useHowl = Skill.canUse(sdk.skills.Howl); + const useTerror = Skill.canUse(sdk.skills.Terror); + + Config.DebugMode.Stack = true; + let waitTick = getTickCount(); + let potTick = getTickCount(); + + // Start + Worker.runInBackground.TownChicken = function () { + if (getTickCount() - waitTick < 100 || SoloEvents.townChicken.disabled) return true; + waitTick = getTickCount(); + if (me.inTown) return true; + + let shouldChicken = ( + (townCheck || me.hpPercent < Config.TownHP || me.mpPercent < Config.TownMP) + ); + + if (shouldChicken && !me.canTpToTown()) { + // we should probably quit? + return true; + } + + if (!shouldChicken) { + if (getTickCount() - potTick < 300) return true; + potTick = getTickCount(); + // do we need potions? + if (!Config.TownCheck) return true; + // can we chicken? + if (!me.canTpToTown()) return true; + if (me.needPotions() || (Config.OpenChests.Enabled && Town.needKeys())) { + shouldChicken = true; + } + } + + if (shouldChicken) { + let t4 = getTickCount(); + try { + // const recentChicks = lastChickens + // .slice(Math.max(lastChickens.length - 3), lastChickens.length - 1); + + // const stopCurrentScript = recentChicks.length >= 2 && recentChicks + // .every(([count]) => getTickCount() - count < Time.minutes(1)); + + // lastChickens.push([getTickCount(), me.area, me.x, me.y]); + + // if (stopCurrentScript) { + // myPrint("ÿc8TownChicken :: ÿc0Too many chickens on this script, move to next :: " + me.gold); + // goToTown(); + // // scriptBroadcast("nextScript"); + // me.emit("nextScript"); + // return true; + // } + + myPrint("ÿc8TownChicken :: ÿc0Going to town. Initial Gold :: " + me.gold); + [Attack.stopClear, SoloEvents.townChicken.running] = [true, true]; + + // determine if this is really worth it + if (useHowl || useTerror) { + if ([156, 211, 242, 243, 544, 571, 345].indexOf(getNearestMonster()) === -1) { + if (useHowl && Skill.getManaCost(130) < me.mp) { + Skill.cast(130, sdk.skills.hand.Right); + } + + if (useTerror && Skill.getManaCost(77) < me.mp) { + Skill.cast(77, sdk.skills.hand.Right, getNearestMonster()); + } + } + } + + visitTown(); + } catch (e) { + Misc.errorReport(e, "TownChicken.js"); + scriptBroadcast("quit"); + + return false; + } finally { + Packet.flash(me.gid, 100); + console.log("ÿc8TownChicken :: Took: " + Time.format(getTickCount() - t4) + " to visit town. Ending Gold :: " + me.gold); + [Attack.stopClear, SoloEvents.townChicken.running, townCheck] = [false, false, false]; + } + } + + return true; + }; + + console.log("ÿc8Kolbot-SoloPlayÿc0: Start TownChicken"); + } })(module, require, typeof Worker === "object" && Worker || require("../../modules/Worker")); diff --git a/libs/SoloPlay/index.d.ts b/libs/SoloPlay/index.d.ts index 258bdea6..b0fb808f 100644 --- a/libs/SoloPlay/index.d.ts +++ b/libs/SoloPlay/index.d.ts @@ -1,364 +1,364 @@ // @ts-nocheck declare global { - interface Math { - percentDifference(value1: number, value2: number): number; - } - - interface ItemUnit { - readonly isCharm: boolean; - readonly isGem: boolean; - readonly isInsertable: boolean; - readonly isRuneword: boolean; - readonly isBroken: boolean; - readonly isBaseType: boolean; - readonly upgradedStrReq: boolean; - readonly upgradedDexReq: boolean; - readonly upgradedLvlReq: boolean; - readonly allRes: boolean; - readonly quantityPercent: number; - - getItemType(): string; - } - - interface Monster { - readonly isStunned: boolean; - readonly isUnderCoS: boolean; - readonly isUnderLowerRes: boolean; - } - - interface Unit { - getResPenalty(difficulty: number): number; - castChargedSkillEx(...args: any[]): boolean; - castSwitchChargedSkill(...args: any[]): boolean; - haveRunes(itemInfo: number[]): boolean; - } - - type MercObj = { - classid: number, - skill: number, - skillName: string, - act: number, - difficulty: number, - }; - - interface Build { - caster: boolean; - skillstab: number; - wantedskills: number[]; - usefulskills: number[]; - precastSkills: number[]; - wantedMerc: MercObj; - stats: Array<[string, number | "block" | "all"]>; - skills: Array<[number, number, boolean?]>; - charms: Record boolean; - }>; - AutoBuildTemplate: Record void }>; - respec: () => boolean; - active: () => boolean; - } - - interface MyData { - initialized: boolean; - normal: { - respecUsed: boolean; - imbueUsed: boolean; - socketUsed: boolean; - }; - nightmare: { - respecUsed: boolean; - imbueUsed: boolean; - socketUsed: boolean; - }; - hell: { - respecUsed: boolean; - imbueUsed: boolean; - socketUsed: boolean; - }; - task: string; - startTime: number; - charName: string; - classid: number; - level: number; - strength: number; - dexterity: number; - currentBuild: string; - finalBuild: string; - highestDifficulty: string; - setDifficulty: string; - charms: Record boolean; }>; - charmGids: number[]; - merc: { - act: number; - classid: number; - difficulty: number; - strength: number; - dexterity: number; - skill: number; - skillName: string; - gear: number[]; - }; - } - - interface EquippedItem extends ItemUnit { - location: number; - durability: number; - tier: number; - tierScore: number; - secondaryTier: number; - socketed: boolean; - twoHandedCheck: (strict?: boolean) => boolean; - } - - // type EquippedItem = { - // classid: number; - // name: string; - // fname: string; - // quality: number; - // prefixnum: number; - // suffixnum: number; - // itemType: number; - // strreq: number; - // dexreq: number; - // sockets: number; - // getStat: (stat: number, subid: number) => number; - // }; - type EquippedMap = Map; - - interface MeType { - readonly maxNearMonsters: number; - readonly dualWielding: boolean; - readonly realFR: number; - readonly realCR: number; - readonly realPR: number; - readonly realLR: number; - readonly FR: number; - readonly CR: number; - readonly LR: number; - readonly PR: number; - readonly onFinalBuild: boolean; - readonly trueStr: number; - readonly trueDex: number; - - finalBuild: Build; - currentBuild: Build; - data: MyData; - equipped: { - get: (bodylocation: number) => EquippedItem | undefined; - has: (bodylocation: number) => boolean; - set: (bodylocation: number, item: ItemUnit) => void; - init: () => void; - }; - - canTpToTown(): boolean; - getMercEx(): MercUnit | null; - getEquippedItem(bodyLoc: number): ItemUnit | null; - getSkillTabs(classid: number): number[]; - inDanger(checkLoc?: {x: number, y: number} | MeType, range?: number): boolean; - checkSkill(skillId: number, subId: number): boolean; - cleanUpInvoPotions(beltSize: number): boolean; - needPotions(): boolean; - getIdTool(): ItemUnit | null; - getTpTool(): ItemUnit | null; - getUnids(): ItemUnit[]; - fieldID(): boolean; - getWeaponQuantity(weaponLoc: number): number; - getItemsForRepair(repairPercent: number, chargedItems?: boolean): ItemUnit[]; - needRepair(): string[]; - needMerc(): boolean; - clearBelt(): boolean; - sortInventory(): boolean; - cleanUpScrolls(tome: ItemUnit, scrollId: number): number; - update(): void; - } - - interface Container { - /** - * A function that checks if the cube is located at { x: 0, y: 0 } in the stash and moves it there if not - * @param name - */ - CubeSpot(name: string): boolean; - - /** - * A function that sorts items with optional priority - * @param itemIdsLeft - * @param itemIdsRight - */ - SortItems(itemIdsLeft: number[], itemIdsRight: number[]): boolean; - - /** - * A function that moves an item to a location in a container - * @param item - * @param reverseX - * @param reverseY - * @param priorityClassIds - */ - MoveTo(item: ItemUnit, reverseX: boolean, reverseY: boolean, priorityClassIds: number[]): boolean - - /** - * @param item - * @param location - * @param force - */ - MakeSpot(item: ItemUnit, location: { x: number, y: number }, force: boolean): boolean; - - /** - * @param item - * @param mX - * @param mY - */ - MoveToSpot(item: ItemUnit, mX: number, mY: number): boolean; - } - - class Merc { - constructor(classid: number, skill: number, act: number, difficulty?: number); - classid: number; - skill: number; - skillName: string; - act: number; - difficulty: number; - } - - class MercData { - [sdk.skills.FireArrow]: Merc; - [sdk.skills.ColdArrow]: Merc; - [sdk.skills.Prayer]: Merc; - [sdk.skills.BlessedAim]: Merc; - [sdk.skills.Defiance]: Merc; - [sdk.skills.HolyFreeze]: Merc; - [sdk.skills.Might]: Merc; - [sdk.skills.Thorns]: Merc; - [sdk.skills.IceBlast]: Merc; - [sdk.skills.FireBall]: Merc; - [sdk.skills.Lightning]: Merc; - [sdk.skills.Bash]: Merc; - actMap: Map; - } - - namespace Mercenary { - let minCost: number; - - function getMercSkill(merc?: MercUnit): string | false; - function getMercDifficulty(merc?: MercUnit): number; - function getMercAct(merc?: MercUnit): number; - function getMercInfo(merc?: MercUnit): { classid: number, act: number, difficulty: number, type: string | false }; - function checkMercSkill(wanted: string, merc?: MercUnit): boolean; - function hireMerc(): boolean; - } - - namespace Misc { - let townEnabled: boolean; - let openChestsEnabled: boolean; - const presetShrineIds: number[]; - const presetChestIds: number[]; - - function openChestsInArea(area: number, chestIds: number[], sort?: Function): boolean; - function getExpShrine(shrineLocs: number[]): boolean; - function unsocketItem(item: ItemUnit): boolean; - function checkItemsForSocketing(): ItemUnit | boolean; - function checkItemsForImbueing(): ItemUnit | boolean; - function addSocketablesToItem(item: ItemUnit, runes: ItemUnit[]): boolean; - function getSocketables( - item: ItemUnit, - itemInfo?: { - classid: number, - socketWith: number[], - temp: number[], - useSocketQuest: boolean, - condition: Function - } - ): boolean; - function checkSocketables(): void; - } - - namespace Skill { - function switchCast(skillId: number, givenSettings: { hand?: number, x?: number, y?: number, switchBack?: boolean, oSkill?: boolean }): boolean; - } - - namespace CharData { - const filePath: string; - const threads: string[]; - - namespace login { - function create(): any; - function getObj(): any; - function getStats(): any; - function updateData(arg: string, property: object | string, value: any): boolean; - } - - // ignoring the sub objs for now - function updateConfig(): void; - function create(): MyData; - function getObj(): MyData; - function getStats(): MyData; - function updateData(arg: string, property: object | string, value: any): boolean; - /** @alias CharData.delete */ - function _delete(deleteMain: boolean): boolean; - } - - namespace SetUp { - let mercEnabled: boolean; - const currentBuild: string; - const finalBuild: string; - const stopAtLevel: number | false; - - function init(): void; - function include(): void; - function finalRespec(): number; - function getTemplate(): { buildType: string, template: string }; - function specPush(specType: string): number[]; - function makeNext(): void; - function belt(): void; - function buffers(): void; - function bowQuiver(): void; - function imbueItems(): string[]; - function config(): void; - } - - namespace Check { - let lowGold: boolean; - - function gold(): boolean; - function brokeAf(): boolean; - function broken(): 0 | 1 | 2; - function brokeCheck(): boolean; - function resistance(): { Status: boolean, FR: number, CR: number, LR: number, PR: number }; - function nextDifficulty(announce: boolean): string | false; - function runes(): boolean; - function haveItem(type: string | number, flag?: string | number, iName?: string): boolean; - function currentBuild(): Build; - function finalBuild(): Build; - } - - namespace SoloWants { - } - - namespace NPCAction { - function shopAt(npcName: string): boolean; - function buyPotions(): boolean; - function fillTome(classid: number, force?: boolean): boolean; - function cainID(force?: boolean): boolean; - function shopItems(force?: boolean): boolean; - function gamble(): boolean; - function repair(force?: boolean): boolean; - function reviveMerc(): boolean; - } - - namespace AutoEquip { - } - - type extraTasks = { - thawing?: boolean, - antidote?: boolean, - stamina?: boolean, - fullChores?: boolean, - }; - - namespace Town { - function doChores(repair?: boolean, givenTasks?: extraTasks): boolean; - } + interface Math { + percentDifference(value1: number, value2: number): number; + } + + interface ItemUnit { + readonly isCharm: boolean; + readonly isGem: boolean; + readonly isInsertable: boolean; + readonly isRuneword: boolean; + readonly isBroken: boolean; + readonly isBaseType: boolean; + readonly upgradedStrReq: boolean; + readonly upgradedDexReq: boolean; + readonly upgradedLvlReq: boolean; + readonly allRes: boolean; + readonly quantityPercent: number; + + getItemType(): string; + } + + interface Monster { + readonly isStunned: boolean; + readonly isUnderCoS: boolean; + readonly isUnderLowerRes: boolean; + } + + interface Unit { + getResPenalty(difficulty: number): number; + castChargedSkillEx(...args: any[]): boolean; + castSwitchChargedSkill(...args: any[]): boolean; + haveRunes(itemInfo: number[]): boolean; + } + + type MercObj = { + classid: number, + skill: number, + skillName: string, + act: number, + difficulty: number, + }; + + interface Build { + caster: boolean; + skillstab: number; + wantedskills: number[]; + usefulskills: number[]; + precastSkills: number[]; + wantedMerc: MercObj; + stats: Array<[string, number | "block" | "all"]>; + skills: Array<[number, number, boolean?]>; + charms: Record boolean; + }>; + AutoBuildTemplate: Record void }>; + respec: () => boolean; + active: () => boolean; + } + + interface MyData { + initialized: boolean; + normal: { + respecUsed: boolean; + imbueUsed: boolean; + socketUsed: boolean; + }; + nightmare: { + respecUsed: boolean; + imbueUsed: boolean; + socketUsed: boolean; + }; + hell: { + respecUsed: boolean; + imbueUsed: boolean; + socketUsed: boolean; + }; + task: string; + startTime: number; + charName: string; + classid: number; + level: number; + strength: number; + dexterity: number; + currentBuild: string; + finalBuild: string; + highestDifficulty: string; + setDifficulty: string; + charms: Record boolean; }>; + charmGids: number[]; + merc: { + act: number; + classid: number; + difficulty: number; + strength: number; + dexterity: number; + skill: number; + skillName: string; + gear: number[]; + }; + } + + interface EquippedItem extends ItemUnit { + location: number; + durability: number; + tier: number; + tierScore: number; + secondaryTier: number; + socketed: boolean; + twoHandedCheck: (strict?: boolean) => boolean; + } + + // type EquippedItem = { + // classid: number; + // name: string; + // fname: string; + // quality: number; + // prefixnum: number; + // suffixnum: number; + // itemType: number; + // strreq: number; + // dexreq: number; + // sockets: number; + // getStat: (stat: number, subid: number) => number; + // }; + type EquippedMap = Map; + + interface MeType { + readonly maxNearMonsters: number; + readonly dualWielding: boolean; + readonly realFR: number; + readonly realCR: number; + readonly realPR: number; + readonly realLR: number; + readonly FR: number; + readonly CR: number; + readonly LR: number; + readonly PR: number; + readonly onFinalBuild: boolean; + readonly trueStr: number; + readonly trueDex: number; + + finalBuild: Build; + currentBuild: Build; + data: MyData; + equipped: { + get: (bodylocation: number) => EquippedItem | undefined; + has: (bodylocation: number) => boolean; + set: (bodylocation: number, item: ItemUnit) => void; + init: () => void; + }; + + canTpToTown(): boolean; + getMercEx(): MercUnit | null; + getEquippedItem(bodyLoc: number): ItemUnit | null; + getSkillTabs(classid: number): number[]; + inDanger(checkLoc?: {x: number, y: number} | MeType, range?: number): boolean; + checkSkill(skillId: number, subId: number): boolean; + cleanUpInvoPotions(beltSize: number): boolean; + needPotions(): boolean; + getIdTool(): ItemUnit | null; + getTpTool(): ItemUnit | null; + getUnids(): ItemUnit[]; + fieldID(): boolean; + getWeaponQuantity(weaponLoc: number): number; + getItemsForRepair(repairPercent: number, chargedItems?: boolean): ItemUnit[]; + needRepair(): string[]; + needMerc(): boolean; + clearBelt(): boolean; + sortInventory(): boolean; + cleanUpScrolls(tome: ItemUnit, scrollId: number): number; + update(): void; + } + + interface Container { + /** + * A function that checks if the cube is located at { x: 0, y: 0 } in the stash and moves it there if not + * @param name + */ + CubeSpot(name: string): boolean; + + /** + * A function that sorts items with optional priority + * @param itemIdsLeft + * @param itemIdsRight + */ + SortItems(itemIdsLeft: number[], itemIdsRight: number[]): boolean; + + /** + * A function that moves an item to a location in a container + * @param item + * @param reverseX + * @param reverseY + * @param priorityClassIds + */ + MoveTo(item: ItemUnit, reverseX: boolean, reverseY: boolean, priorityClassIds: number[]): boolean + + /** + * @param item + * @param location + * @param force + */ + MakeSpot(item: ItemUnit, location: { x: number, y: number }, force: boolean): boolean; + + /** + * @param item + * @param mX + * @param mY + */ + MoveToSpot(item: ItemUnit, mX: number, mY: number): boolean; + } + + class Merc { + constructor(classid: number, skill: number, act: number, difficulty?: number); + classid: number; + skill: number; + skillName: string; + act: number; + difficulty: number; + } + + class MercData { + [sdk.skills.FireArrow]: Merc; + [sdk.skills.ColdArrow]: Merc; + [sdk.skills.Prayer]: Merc; + [sdk.skills.BlessedAim]: Merc; + [sdk.skills.Defiance]: Merc; + [sdk.skills.HolyFreeze]: Merc; + [sdk.skills.Might]: Merc; + [sdk.skills.Thorns]: Merc; + [sdk.skills.IceBlast]: Merc; + [sdk.skills.FireBall]: Merc; + [sdk.skills.Lightning]: Merc; + [sdk.skills.Bash]: Merc; + actMap: Map; + } + + namespace Mercenary { + let minCost: number; + + function getMercSkill(merc?: MercUnit): string | false; + function getMercDifficulty(merc?: MercUnit): number; + function getMercAct(merc?: MercUnit): number; + function getMercInfo(merc?: MercUnit): { classid: number, act: number, difficulty: number, type: string | false }; + function checkMercSkill(wanted: string, merc?: MercUnit): boolean; + function hireMerc(): boolean; + } + + namespace Misc { + let townEnabled: boolean; + let openChestsEnabled: boolean; + const presetShrineIds: number[]; + const presetChestIds: number[]; + + function openChestsInArea(area: number, chestIds: number[], sort?: Function): boolean; + function getExpShrine(shrineLocs: number[]): boolean; + function unsocketItem(item: ItemUnit): boolean; + function checkItemsForSocketing(): ItemUnit | boolean; + function checkItemsForImbueing(): ItemUnit | boolean; + function addSocketablesToItem(item: ItemUnit, runes: ItemUnit[]): boolean; + function getSocketables( + item: ItemUnit, + itemInfo?: { + classid: number, + socketWith: number[], + temp: number[], + useSocketQuest: boolean, + condition: Function + } + ): boolean; + function checkSocketables(): void; + } + + namespace Skill { + function switchCast(skillId: number, givenSettings: { hand?: number, x?: number, y?: number, switchBack?: boolean, oSkill?: boolean }): boolean; + } + + namespace CharData { + const filePath: string; + const threads: string[]; + + namespace login { + function create(): any; + function getObj(): any; + function getStats(): any; + function updateData(arg: string, property: object | string, value: any): boolean; + } + + // ignoring the sub objs for now + function updateConfig(): void; + function create(): MyData; + function getObj(): MyData; + function getStats(): MyData; + function updateData(arg: string, property: object | string, value: any): boolean; + /** @alias CharData.delete */ + function _delete(deleteMain: boolean): boolean; + } + + namespace SetUp { + let mercEnabled: boolean; + const currentBuild: string; + const finalBuild: string; + const stopAtLevel: number | false; + + function init(): void; + function include(): void; + function finalRespec(): number; + function getTemplate(): { buildType: string, template: string }; + function specPush(specType: string): number[]; + function makeNext(): void; + function belt(): void; + function buffers(): void; + function bowQuiver(): void; + function imbueItems(): string[]; + function config(): void; + } + + namespace Check { + let lowGold: boolean; + + function gold(): boolean; + function brokeAf(): boolean; + function broken(): 0 | 1 | 2; + function brokeCheck(): boolean; + function resistance(): { Status: boolean, FR: number, CR: number, LR: number, PR: number }; + function nextDifficulty(announce: boolean): string | false; + function runes(): boolean; + function haveItem(type: string | number, flag?: string | number, iName?: string): boolean; + function currentBuild(): Build; + function finalBuild(): Build; + } + + namespace SoloWants { + } + + namespace NPCAction { + function shopAt(npcName: string): boolean; + function buyPotions(): boolean; + function fillTome(classid: number, force?: boolean): boolean; + function cainID(force?: boolean): boolean; + function shopItems(force?: boolean): boolean; + function gamble(): boolean; + function repair(force?: boolean): boolean; + function reviveMerc(): boolean; + } + + namespace AutoEquip { + } + + type extraTasks = { + thawing?: boolean, + antidote?: boolean, + stamina?: boolean, + fullChores?: boolean, + }; + + namespace Town { + function doChores(repair?: boolean, givenTasks?: extraTasks): boolean; + } } export{}; From 5a14fb3c73782d9a4476bb32c2ea6b976739b39d Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Mon, 29 May 2023 13:16:53 -0400 Subject: [PATCH 124/263] Update Globals.js - don't add bows to secondary tier list after level 12 --- libs/SoloPlay/Functions/Globals.js | 33 ++++++++++++++++++++++-------- 1 file changed, 24 insertions(+), 9 deletions(-) diff --git a/libs/SoloPlay/Functions/Globals.js b/libs/SoloPlay/Functions/Globals.js index a274e158..00b6ca07 100644 --- a/libs/SoloPlay/Functions/Globals.js +++ b/libs/SoloPlay/Functions/Globals.js @@ -260,8 +260,11 @@ const SetUp = { sdk.items.TomeofIdentify, sdk.items.TomeofTownPortal, sdk.items.Key, // sort tomes and keys to the right // sort all inventory potions from the right sdk.items.RejuvenationPotion, sdk.items.FullRejuvenationPotion, - sdk.items.MinorHealingPotion, sdk.items.LightHealingPotion, sdk.items.HealingPotion, sdk.items.GreaterHealingPotion, sdk.items.SuperHealingPotion, - sdk.items.MinorManaPotion, sdk.items.LightManaPotion, sdk.items.ManaPotion, sdk.items.GreaterManaPotion, sdk.items.SuperManaPotion + sdk.items.MinorHealingPotion, sdk.items.LightHealingPotion, + sdk.items.HealingPotion, sdk.items.GreaterHealingPotion, + sdk.items.SuperHealingPotion, sdk.items.MinorManaPotion, + sdk.items.LightManaPotion, sdk.items.ManaPotion, + sdk.items.GreaterManaPotion, sdk.items.SuperManaPotion ], PrioritySorting: true, ItemsSortedFromLeftPriority: [/*605, 604, 603, 519, 518*/], // (NOTE: the earlier in the index, the further to the Left) @@ -289,8 +292,10 @@ const SetUp = { if (respec === me.charlvl && me.charlvl < 60) { showConsole(); - console.log("ÿc8Kolbot-SoloPlayÿc0: Bot has respecTwo items but is too low a level to respec."); - console.log("ÿc8Kolbot-SoloPlayÿc0: This only happens with user intervention. Remove the items you gave the bot until at least level 60"); + console.log( + "ÿc8Kolbot-SoloPlayÿc0: Bot has respecTwo items but is too low a level to respec." + "\n" + + "This only happens with user intervention. Remove the items you gave the bot until at least level 60" + ); respec = 100; } @@ -329,8 +334,12 @@ const SetUp = { // log info myPrint(this.finalBuild + " goal reached. On to the next."); - D2Bot.printToConsole("Kolbot-SoloPlay: " + this.finalBuild + " goal reached" + (printTotalTime ? " (" + (Time.format(gameObj.Total + Time.elapsed(gameObj.LastSave))) + "). " : ". ") + "Making next...", sdk.colors.D2Bot.Gold); - + D2Bot.printToConsole( + "Kolbot-SoloPlay: " + this.finalBuild + " goal reached" + + (printTotalTime ? " (" + (Time.format(gameObj.Total + Time.elapsed(gameObj.LastSave))) + "). " : ". ") + + "Making next...", + sdk.colors.D2Bot.Gold + ); D2Bot.setProfile(null, null, require("../Tools/NameGen")()); CharData.delete(true); delay(250); @@ -373,7 +382,11 @@ const SetUp = { for (let imbueItem of Config.imbueables) { try { if (imbueItem.condition()) { - temp.push("[name] == " + imbueItem.name + " && [quality] >= normal && [quality] <= superior && [flag] != ethereal # [Sockets] == 0 # [maxquantity] == 1"); + temp.push( + "[name] == " + imbueItem.name + + " && [quality] >= normal && [quality] <= superior && [flag] != ethereal" + + " # [Sockets] == 0 # [maxquantity] == 1" + ); } } catch (e) { console.log(e); @@ -426,8 +439,10 @@ const SetUp = { Config.Recipes.push([Recipe.Reroll.Magic, "Grand Charm"]); } // switch bow - only for zon/sorc/pal/necro classes right now - if (!me.barbarian && !me.assassin && !me.druid) { - NTIP.addLine("([type] == bow || [type] == crossbow) && [quality] >= normal # [itemchargedskill] >= 0 # [secondarytier] == tierscore(item)"); + if (me.charlvl < 12 && !me.barbarian && !me.assassin && !me.druid) { + NTIP.addLine( + "([type] == bow || [type] == crossbow) && [quality] >= normal # [itemchargedskill] >= 0 # [secondarytier] == tierscore(item)" + ); } const expansionExtras = [ // Special Charms From bac899a07e83e45a685ae6fd318305319dc5c16e Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Thu, 1 Jun 2023 19:45:42 -0400 Subject: [PATCH 125/263] Update for Town.tasks change and Skill.cast change - keep current with the core restructure branch --- libs/SoloPlay/Functions/Mercenary.js | 2 +- libs/SoloPlay/Functions/MiscOverrides.js | 15 ++++++++++++--- libs/SoloPlay/Functions/NPCAction.js | 12 ++++++------ libs/SoloPlay/Functions/TownOverrides.js | 9 +++++---- 4 files changed, 24 insertions(+), 14 deletions(-) diff --git a/libs/SoloPlay/Functions/Mercenary.js b/libs/SoloPlay/Functions/Mercenary.js index 3118c847..00c369d0 100644 --- a/libs/SoloPlay/Functions/Mercenary.js +++ b/libs/SoloPlay/Functions/Mercenary.js @@ -253,7 +253,7 @@ const Mercenary = { try { Town.goToTown(mercAct); myPrint("ÿc9Mercenaryÿc0 :: getting merc"); - Town.move(Town.tasks[me.act - 1].Merc); + Town.move(Town.tasks.get(me.act).Merc); me.sortInventory(); Item.removeItemsMerc(); // strip temp merc gear delay(500 + me.ping); diff --git a/libs/SoloPlay/Functions/MiscOverrides.js b/libs/SoloPlay/Functions/MiscOverrides.js index e73a7ef9..2ff15fb6 100644 --- a/libs/SoloPlay/Functions/MiscOverrides.js +++ b/libs/SoloPlay/Functions/MiscOverrides.js @@ -81,7 +81,7 @@ Misc.openChest = function (unit) { let useDodge = Pather.useTeleport() && Skill.useTK(unit); if (useTK) { unit.distance > 18 && Attack.getIntoPosition(unit, 18, sdk.collision.WallOrRanged, false, true); - if (!Skill.cast(sdk.skills.Telekinesis, sdk.skills.hand.Right, unit)) { + if (!Packet.telekinesis(unit)) { console.debug("Failed to tk: attempt: " + i); continue; } @@ -139,7 +139,16 @@ Misc.openChests = function (range = 15) { unitList.sort(Sort.units); let unit = unitList.shift(); - if (unit) { + if (unit && Pather.currentWalkingPath.length) { + /** + * @todo - check if the chest is in our path of if in the future we would be closer to it + * and if so assign a hook to be triggered at our point nearest to it so we can open it + * and save time + */ + if (unit.distance > 5 && PathDebug.coordsInPath(Pather.currentWalkingPath, unit.x, unit.y)) { + console.log("Skipping chest for now as it is in our path for later"); + continue; + } // check mob count at chest - think I need a new prototype for faster checking // allow specifying an amount and return true/false, rather than building the whole list then deciding what amount is too much // possibly also specify a danger modifier - 3 champions around a chest is much more dangerous than 3 fallens @@ -166,7 +175,7 @@ Misc.getWell = function (unit) { if (Skill.useTK(unit) && i < 2) { unit.distance > 21 && Pather.moveNearUnit(unit, 20); checkCollision(me, unit, sdk.collision.Ranged) && Attack.getIntoPosition(unit, 20, sdk.collision.Ranged); - Skill.cast(sdk.skills.Telekinesis, sdk.skills.hand.Right, unit); + Packet.telekinesis(unit); } else { if (unit.distance < 4 || Pather.moveToUnit(unit, 3, 0)) { Misc.click(0, 0, unit); diff --git a/libs/SoloPlay/Functions/NPCAction.js b/libs/SoloPlay/Functions/NPCAction.js index 86b1a92e..7d85a6c7 100644 --- a/libs/SoloPlay/Functions/NPCAction.js +++ b/libs/SoloPlay/Functions/NPCAction.js @@ -276,7 +276,7 @@ let npc = getInteractedNPC(); // Check if we're already in a shop. It would be pointless to go to Cain if so. - if (npc && npc.name.toLowerCase() === Town.tasks[me.act - 1].Shop) return false; + if (npc && npc.name.toLowerCase() === Town.tasks.get(me.act).Shop) return false; // Check if we may use Cain - minimum gold if (me.gold < Config.CainID.MinGold && !force) return false; @@ -512,22 +512,22 @@ let list = []; - if (Town.gambleIds.length === 0) { + if (Town.gambleIds.size === 0) { // change text to classid for (let i = 0; i < Config.GambleItems.length; i += 1) { if (isNaN(Config.GambleItems[i])) { if (NTIPAliasClassID.hasOwnProperty(Config.GambleItems[i].replace(/\s+/g, "").toLowerCase())) { - Town.gambleIds.push(NTIPAliasClassID[Config.GambleItems[i].replace(/\s+/g, "").toLowerCase()]); + Town.gambleIds.add(NTIPAliasClassID[Config.GambleItems[i].replace(/\s+/g, "").toLowerCase()]); } else { Misc.errorReport("ÿc1Invalid gamble entry:ÿc0 " + Config.GambleItems[i]); } } else { - Town.gambleIds.push(Config.GambleItems[i]); + Town.gambleIds.add(Config.GambleItems[i]); } } } - if (Town.gambleIds.length === 0) return true; + if (Town.gambleIds.size === 0) return true; // avoid Alkor me.act === 3 && Town.goToTown(me.accessToAct(4) ? 4 : 2); @@ -549,7 +549,7 @@ if (item) { do { - Town.gambleIds.includes(item.classid) && items.push(copyUnit(item)); + Town.gambleIds.has(item.classid) && items.push(copyUnit(item)); } while (item.getNext()); for (let i = 0; i < items.length; i += 1) { diff --git a/libs/SoloPlay/Functions/TownOverrides.js b/libs/SoloPlay/Functions/TownOverrides.js index c07ebbc4..a9cc7424 100644 --- a/libs/SoloPlay/Functions/TownOverrides.js +++ b/libs/SoloPlay/Functions/TownOverrides.js @@ -14,7 +14,7 @@ includeIfNotIncluded("core/Town.js"); -new Overrides.Override(Town, Town.drinkPots, function(orignal, type) { +new Overrides.Override(Town, Town.drinkPots, function (orignal, type) { const objDrank = orignal(type, false); const pots = {}; pots[sdk.items.StaminaPotion] = "stamina"; @@ -246,7 +246,7 @@ Town.identify = function () { let tome = me.getTome(sdk.items.TomeofIdentify); // if we have a tome might as well use it - this might prevent us from having to run from one npc to another - if (tome && tome.getStat(sdk.stats.Quantity) > 0 && Town.getDistance(Town.tasks[me.act - 1].Shop) > 5) { + if (tome && tome.getStat(sdk.stats.Quantity) > 0 && Town.getDistance(Town.tasks.get(me.act).Shop) > 5) { // not in the field but oh well no need to repeat the code if (me.fieldID() && !me.getUnids().length) { return true; @@ -636,7 +636,8 @@ Town.clearJunk = function () { myPrint("Junk items to sell: " + junkToSell.length); Town.initNPC("Shop", "clearInventory"); - if (getUIFlag(sdk.uiflags.Shop) || (Config.PacketShopping && getInteractedNPC() && getInteractedNPC().itemcount > 0)) { + if (getUIFlag(sdk.uiflags.Shop) + || (Config.PacketShopping && getInteractedNPC() && getInteractedNPC().itemcount > 0)) { for (let i = 0; i < junkToSell.length; i++) { console.log("ÿc9JunkCheckÿc0 :: Sell " + junkToSell[i].prettyPrint); Item.logger("Sold", junkToSell[i]); @@ -711,7 +712,7 @@ Town.doChores = function (repair = false, givenTasks = {}) { // Use cainId if we are low on gold or we are closer to him than the shopNPC if (me.getUnids().length) { if (me.gold < 5000 - || Town.getDistance("cain") < Town.getDistance(Town.tasks[me.act - 1].Heal)) { + || Town.getDistance("cain") < Town.getDistance(Town.tasks.get(me.act).Heal)) { NPCAction.cainID(true); } } From 931848dc5ba161a49fdf44a063a3d97196b879b9 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Sun, 4 Jun 2023 14:35:54 -0400 Subject: [PATCH 126/263] Update Overlay.js - little bit of refactoring, still more do to - added gold to the overlay - changed `Horadric Staff` to just `Staff`, so it didn't overflow the quest box --- libs/SoloPlay/Tools/Overlay.js | 151 +++++++++++++++++++++++---------- 1 file changed, 106 insertions(+), 45 deletions(-) diff --git a/libs/SoloPlay/Tools/Overlay.js b/libs/SoloPlay/Tools/Overlay.js index 9ff13ee5..5e4248cc 100644 --- a/libs/SoloPlay/Tools/Overlay.js +++ b/libs/SoloPlay/Tools/Overlay.js @@ -12,8 +12,10 @@ includeIfNotIncluded("SoloPlay/Functions/PrototypeOverrides.js"); /** * @todo Clean this up, probably needs to be entirely rewritten + * - show current script */ const Overlay = { + timeOut: 0, resfix: { x: -10, y: me.screensize ? 0 : -120 }, quest: { x: 8, y: 368 }, qYMod: { 1: 368, 2: 384, 3: 384, 4: 414, 5: 384 }, @@ -23,21 +25,25 @@ const Overlay = { script: "", realm: (me.realm ? me.realm : "SinglePlayer"), difficulty: sdk.difficulty.nameOf(me.diff), - level: () => me.data.level, + level: function () { + return me.data.level; + }, text: (function () { const _gameTracker = Tracker.readObj(Tracker.GTPath); let [_tick, _charlvl] = [0, 0]; - + const _format = function (ms = 0) { const hours = Math.floor(ms / 3600000); const minutes = Math.floor((ms % 3600000) / 60000); const seconds = Math.floor((ms % 60000) / 1000); /** @param {number} num */ - const pad = (num) => (num < 10 ? '0' + num : num); + const pad = function (num) { + return (num < 10 ? "0" + num : num); + }; - return pad(hours) + ':' + pad(minutes) + ':' + pad(seconds); + return pad(hours) + ":" + pad(minutes) + ":" + pad(seconds); }; const _timer = function () { @@ -210,6 +216,27 @@ const Overlay = { }; }; + function StatsHook (name, status, hook) { + this.name = name; + this.status = status; + /** + * @private + * @type {function(): Hook} + */ + this._hook = hook; + } + + /** + * @this {StatsHook} + * @returns {{ name: string, hook: Hook }} + */ + StatsHook.prototype.hook = function () { + return { + name: this.name, + hook: this._hook() + }; + }; + const _quests = new Map([ // Act 1 ["Den", new QuestHook("Den", sdk.quest.id.DenofEvil)], @@ -221,7 +248,7 @@ const Overlay = { // Act 2 ["Cube", new QuestHook("Cube", sdk.quest.id.TheHoradricStaff)], ["Radament", new QuestHook("Radament", sdk.quest.id.RadamentsLair)], - ["Horadric Staff", new QuestHook("Horadric Staff", sdk.quest.id.TheHoradricStaff)], + ["Staff", new QuestHook("Staff", sdk.quest.id.TheHoradricStaff)], ["Amulet", new QuestHook("Amulet", sdk.quest.id.TheTaintedSun)], ["Summoner", new QuestHook("Summoner", sdk.quest.id.TheSummoner)], ["Duriel", new QuestHook("Duriel", sdk.quest.id.TheSevenTombs)], @@ -245,34 +272,63 @@ const Overlay = { const _acts = new Map([ [1, ["Den", "Blood Raven", "Tristram", "Countess", "Smith", "Andariel"]], - [2, [/* "Cube", */"Radament", "Horadric Staff", "Amulet", "Summoner", "Duriel"]], + [2, [/* "Cube", */"Radament", "Staff", "Amulet", "Summoner", "Duriel"]], [3, ["Golden Bird", "Khalim's Will", "Lam Esen", "Travincal", "Mephisto"]], [4, ["Izual", "Hell Forge", "Diablo"]], [5, ["Shenk", "Barbies", "Anya", "Ancients", "Baal"]] ]); + const _res = function () { + return ( + "FR: ÿc1" + me.FR + + "ÿc4 CR: ÿc3" + me.CR + + "ÿc4 LR: ÿc9" + me.LR + + "ÿc4 PR: ÿc2" + me.PR + + "ÿc4 CurrentBuild: ÿc0" + Overlay.build + ); + }; + + const _stats = function () { + return ( + "MF: ÿc8" + me.getStat(sdk.stats.MagicBonus) + + "ÿc4 FHR: ÿc8" + (me.FHR) + + "ÿc4 FBR: ÿc8" + (me.FBR) + + "ÿc4 FCR: ÿc8" + (me.FCR) + + "ÿc4 IAS: ÿc8" + (me.IAS) + ); + }; + + const _statsMap = new Map([ + [ + "stats", + new StatsHook("stats", _stats, () => new Text(_stats(), Overlay.dashboard.x, Overlay.dashboard.y + Overlay.resfix.y + 60, 4, 13, 0)) + ], + [ + "res", + new StatsHook("res", _res, () => new Text(_res(), Overlay.dashboard.x, Overlay.dashboard.y + Overlay.resfix.y + 45, 4, 13, 0)) + ], + [ + "gold", + new StatsHook( + "gold", + function () { + return "ÿc6Goldÿc0: ÿc0" + me.gold; + }, + function () { + return new Text("ÿc6Goldÿc0: ÿc0" + me.gold, 275, 586, 4, 6, 0); + } + ) + ], + ]); + const _font = 12; + /** @type {Array<{ name: string, hook: Hook }>} */ const _qHooks = []; + /** @type {Array<{ name: string, hook: Hook }>} */ + const _hooks = []; return { enabled: true, - hooks: [], - - getRes: function () { - // Double check in case still got here before being ready - if (!me.gameReady || !me.ingame || !me.area) return ""; - return ("FR: ÿc1" + me.FR + "ÿc4 CR: ÿc3" + me.CR + "ÿc4 LR: ÿc9" + me.LR + "ÿc4 PR: ÿc2" + me.PR + "ÿc4 CurrentBuild: ÿc0" + Overlay.build); - }, - - getStats: function () { - // Double check in case still got here before being ready - if (!me.gameReady || !me.ingame || !me.area) return ""; - - let textLine = ("MF: ÿc8" + me.getStat(sdk.stats.MagicBonus) + "ÿc4 FHR: ÿc8" + (me.FHR) + "ÿc4 FBR: ÿc8" + (me.FBR) + "ÿc4 FCR: ÿc8" + (me.FCR) - + "ÿc4 IAS: ÿc8" + (me.IAS)); - - return textLine; - }, /** * @param {string} name @@ -301,6 +357,21 @@ const Overlay = { } }, + /** + * @param {string} name + * @returns {void} + */ + updateStats: function (name) { + const hook = this.getHook(name, _hooks); + if (!hook) { + const stat = _statsMap.get(name); + if (!stat) return; + _hooks.push(stat.hook()); + } else { + hook.hook.text = _statsMap.get(name).status(); + } + }, + check: function () { if (!this.enabled || !me.gameReady || !me.ingame || !me.area || me.dead) { this.flush(); @@ -308,8 +379,9 @@ const Overlay = { return; } - !this.getHook("resistances", this.hooks) ? this.add("resistances") : this.getHook("resistances", this.hooks).hook.text = this.getRes(); - !this.getHook("stats", this.hooks) ? this.add("stats") : this.getHook("stats", this.hooks).hook.text = this.getStats(); + this.updateStats("res"); + this.updateStats("stats"); + this.updateStats("gold"); !this.getHook("questheader") && this.add("questheader"); _acts.get(me.act).forEach((quest) => this.updateQuest(quest)); @@ -322,20 +394,6 @@ const Overlay = { */ add: function (name) { switch (name) { - case "resistances": - this.hooks.push({ - name: "resistances", - hook: new Text(this.getRes(), Overlay.dashboard.x, Overlay.dashboard.y + Overlay.resfix.y + 45, 4, 13, 0) - }); - - break; - case "stats": - this.hooks.push({ - name: "stats", - hook: new Text(this.getStats(), Overlay.dashboard.x, Overlay.dashboard.y + Overlay.resfix.y + 60, 4, 13, 0) - }); - - break; case "questbox": _qHooks.push({ name: "questbox", @@ -362,6 +420,11 @@ const Overlay = { } }, + /** + * @param {string} name + * @param {Array<{ name: string, hook: Hook }>} [hooks] + * @returns {{ name: string, hook: Hook } | false} + */ getHook: function (name, hooks) { while (!me.gameReady || !me.ingame || !me.area) { delay(500); @@ -377,8 +440,8 @@ const Overlay = { }, flush: function () { - while (this.hooks.length) { - this.hooks.shift().hook.remove(); + while (_hooks.length) { + _hooks.shift().hook.remove(); } while (_qHooks.length) { @@ -388,9 +451,7 @@ const Overlay = { } }; })(), - - timeOut: 0, - + update: function (msg = false) { function status () { let hide = [ @@ -446,7 +507,7 @@ const Overlay = { me.overhead("Disable All"); Overlay.text.flush() && Overlay.quests.flush(); [Overlay.text.enabled, Overlay.quests.enabled] = [false, false]; - this.timeOut = getTickCount() + Time.seconds(15); + Overlay.timeOut = getTickCount() + Time.seconds(15); } else { Overlay.quests.flush(); Overlay.quests.enabled = false; From a3783778a963dd4f1dd2c7b7e76c389fa757098c Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Sun, 4 Jun 2023 18:59:26 -0400 Subject: [PATCH 127/263] Update ToolsThread.js - redo toolsthread for better control over potting mechanics between player and merc. --- libs/SoloPlay/Threads/ToolsThread.js | 313 +++++++++++++++++---------- 1 file changed, 201 insertions(+), 112 deletions(-) diff --git a/libs/SoloPlay/Threads/ToolsThread.js b/libs/SoloPlay/Threads/ToolsThread.js index c7cd513d..4b80d43a 100644 --- a/libs/SoloPlay/Threads/ToolsThread.js +++ b/libs/SoloPlay/Threads/ToolsThread.js @@ -30,7 +30,6 @@ include("SoloPlay/Functions/Globals.js"); function main () { let ironGolem, tick, quitListDelayTime; let canQuit = true; - let timerLastDrink = []; let [quitFlag, restart] = [false, false]; let debugInfo = { area: 0, currScript: "no entry" }; @@ -52,35 +51,41 @@ function main () { Developer.overlay && include("SoloPlay/Tools/Overlay.js"); - for (let i = 0; i < 5; i += 1) { - timerLastDrink[i] = 0; - } - // Reset core chicken me.chickenhp = -1; me.chickenmp = -1; + const timerLastDrink = new Map([ + [sdk.items.type.HealingPotion, 0], + [sdk.items.type.ManaPotion, 0], + [sdk.items.type.RejuvPotion, 0], + ]); + const timerMercDrink = new Map([ + [sdk.items.type.HealingPotion, 0], + [sdk.items.type.RejuvPotion, 0], + ]); + // General functions - this.togglePause = function () { - ["libs/SoloPlay/SoloPlay.js", "threads/party.js"].forEach((script) => { - let thread = getScript(script); - if (thread) { - if (thread.running) { - script === "libs/SoloPlay/SoloPlay.js" && console.log("ÿc8ToolsThread :: ÿc1Pausing " + script); - thread.pause(); - } else { - script === "libs/SoloPlay/SoloPlay.js" && console.log("ÿc8ToolsThread :: ÿc2Resuming threads"); - thread.resume(); + const togglePause = function () { + ["libs/SoloPlay/SoloPlay.js", "threads/party.js"] + .forEach(function (script) { + let thread = getScript(script); + if (thread) { + if (thread.running) { + script === "libs/SoloPlay/SoloPlay.js" && console.log("ÿc8ToolsThread :: ÿc1Pausing " + script); + thread.pause(); + } else { + script === "libs/SoloPlay/SoloPlay.js" && console.log("ÿc8ToolsThread :: ÿc2Resuming threads"); + thread.resume(); + } } - } - }); - + }); return true; }; - this.stopDefault = function () { + const stopDefault = function () { ["libs/SoloPlay/SoloPlay.js", "libs/SoloPlay/Modules/Guard.js", "threads/party.js"] - .forEach(script => { + .forEach(function (script) { let thread = getScript(script); if (thread && thread.running) { thread.stop(); @@ -89,29 +94,34 @@ function main () { return true; }; - this.exit = function (chickenExit = false) { + const exit = function (chickenExit = false) { chickenExit && D2Bot.updateChickens(); Config.LogExperience && Experience.log(); Developer.logPerformance && Tracker.update(); console.log("ÿc8Run duration ÿc2" + Time.format(getTickCount() - me.gamestarttime)); - this.stopDefault(); + stopDefault(); quit(); }; - this.restart = function () { + const restartGame = function () { Config.LogExperience && Experience.log(); Developer.logPerformance && Tracker.update(); - this.stopDefault(); + stopDefault(); D2Bot.restart(); }; - this.getPotion = function (pottype = -1, type = -1) { - if (pottype === undefined) return false; + /** @param {number} type */ + const getPotion = function (type = -1) { + if (type === undefined) return false; let items = me.getItemsEx() - .filter(item => item.itemType === pottype && (type > Common.Toolsthread.pots.Rejuv ? item.isInBelt : true)); + .filter(function (item) { + return item.itemType === type; + }); if (items.length === 0) return false; - let invoFirst = [Common.Toolsthread.pots.Health, Common.Toolsthread.pots.Mana].includes(type); + const invoFirst = [ + sdk.items.type.HealingPotion, sdk.items.type.ManaPotion + ].includes(type); if (invoFirst) { // sort by location (invo first, then classid) @@ -128,83 +138,115 @@ function main () { }); } - for (let k = 0; k < items.length; k += 1) { - if (type < Common.Toolsthread.pots.MercHealth && items[k].isInInventory && items[k].itemType === pottype) { - console.log("ÿc2Drinking " + items[k].name + " from inventory."); - return items[k]; + for (let item of items) { + if (item.isInInventory && item.itemType === type) { + console.log("ÿc2Drinking " + item.name + " from inventory."); + return item; } - if (items[k].mode === sdk.items.mode.inBelt && items[k].itemType === pottype) { - console.log("ÿc2" + (type > 2 ? "Giving Merc " : "Drinking ") + items[k].name + " from belt."); - return items[k]; + if (item.mode === sdk.items.mode.inBelt && item.itemType === type) { + console.log("ÿc2Drinking " + item.name + " from belt."); + return item; } } return false; }; - this.drinkPotion = function (type) { + /** @param {number} type */ + const drinkPotion = function (type) { if (type === undefined) return false; let tNow = getTickCount(); switch (type) { - case Common.Toolsthread.pots.Health: - case Common.Toolsthread.pots.Mana: - if ((timerLastDrink[type] && (tNow - timerLastDrink[type] < 1000)) || me.getState(type === 0 ? 100 : 106)) { - return false; - } - + case sdk.items.type.HealingPotion: + if (tNow - timerLastDrink.get(type) < 1000 || me.getState(sdk.states.HealthPot)) return false; break; - case Common.Toolsthread.pots.Rejuv: - // small delay for juvs just to prevent using more at once - if (timerLastDrink[type] && (tNow - timerLastDrink[type] < 300)) { - return false; - } - + case sdk.items.type.ManaPotion: + if (tNow - timerLastDrink.get(type) < 1000 || me.getState(sdk.states.ManaPot)) return false; + break; + case sdk.items.type.RejuvPotion: + if (tNow - timerLastDrink.get(type) < 300) return false; break; - case Common.Toolsthread.pots.MercRejuv: - // larger delay for juvs just to prevent using more at once, considering merc update rate - if (timerLastDrink[type] && (tNow - timerLastDrink[type] < 2000)) { - return false; + } + + // mode 18 - can't drink while leaping/whirling etc. + if (me.dead || me.mode === sdk.player.mode.SkillActionSequence) return false; + + let potion = getPotion(type); + + if (potion) { + // mode 18 - can't drink while leaping/whirling etc. + if (me.dead || me.mode === sdk.player.mode.SkillActionSequence) return false; + + try { + potion.interact(); + } catch (e) { + console.error(e); } - break; - default: - if (timerLastDrink[type] && (tNow - timerLastDrink[type] < 8000)) { - return false; + timerLastDrink.set(type, getTickCount()); + delay(25); + + return true; + } + + return false; + }; + + /** @param {number} type */ + const getMercPotion = function (type = -1) { + if (type === undefined) return false; + + let items = me.getItemsEx() + .filter(function (item) { + return item.itemType === type && item.isInBelt; + }) + .sort(function (a, b) { + return b.classid - a.classid; + }); + if (items.length === 0) return false; + + for (let item of items) { + if (item.itemType === type) { + console.log("ÿc2 Giving Merc " + item.name + " from belt."); + return item; } + } + return false; + }; + + /** @param {number} type */ + const giveMercPotion = function (type) { + if (type === undefined) return false; + let tNow = getTickCount(); + + switch (type) { + case sdk.items.type.HealingPotion: + if (tNow - timerMercDrink.get(type) < 8000) return false; + break; + case sdk.items.type.RejuvPotion: + if (tNow - timerMercDrink.get(type) < 2000) return false; break; } // mode 18 - can't drink while leaping/whirling etc. if (me.dead || me.mode === sdk.player.mode.SkillActionSequence) return false; - let pottype = (() => { - switch (type) { - case Common.Toolsthread.pots.Health: - case Common.Toolsthread.pots.MercHealth: - return sdk.items.type.HealingPotion; - case Common.Toolsthread.pots.Mana: - return sdk.items.type.ManaPotion; - default: - return sdk.items.type.RejuvPotion; - } - })(); + let potion = getMercPotion(type); - let potion = this.getPotion(pottype, type); - - if (!!potion) { + if (potion) { // mode 18 - can't drink while leaping/whirling etc. if (me.dead || me.mode === sdk.player.mode.SkillActionSequence) return false; try { - type < Common.Toolsthread.pots.MercHealth ? potion.interact() : Packet.useBeltItemForMerc(potion); + Packet.useBeltItemForMerc(potion); } catch (e) { console.error(e); } - timerLastDrink[type] = getTickCount(); + timerMercDrink.set(type, getTickCount()); delay(25); return true; @@ -218,7 +260,7 @@ function main () { * @param {number} type * @returns {boolean} */ - this.drinkSpecialPotion = function (type) { + const drinkSpecialPotion = function (type) { if (type === undefined) return false; if (!CharData.pots.has(type)) return false; // give at least a second delay between pots @@ -230,7 +272,9 @@ function main () { } let pot = me.getItemsEx(-1, sdk.items.mode.inStorage) - .filter((p) => p.isInInventory && p.classid === type).first(); + .filter(function (p) { + return p.isInInventory && p.classid === type; + }).first(); if (pot) { try { @@ -265,7 +309,7 @@ function main () { const keyEvent = function (key) { switch (key) { case sdk.keys.PauseBreak: // pause default.dbj - this.togglePause(); + togglePause(); break; case sdk.keys.Numpad0: // stop profile without logging character @@ -285,7 +329,7 @@ function main () { break; case sdk.keys.Delete: // quit current game - this.exit(); + exit(); break; case sdk.keys.Insert: // reveal level @@ -308,8 +352,7 @@ function main () { if (AutoMule.getMuleItems().length > 0) { console.log("ÿc2Mule triggered"); scriptBroadcast("mule"); - this.exit(); - + exit(); } else { me.overhead("No items to mule."); } @@ -347,24 +390,63 @@ function main () { if (!!itemToCheck) { let special = ""; if (itemToCheck.itemType === sdk.items.type.Ring) { - special = (" | ÿc4TierLHS: ÿc0" + tierscore(itemToCheck, 1, sdk.body.RingRight) + " | ÿc4TierRHS: ÿc0" + tierscore(itemToCheck, 1, sdk.body.RingLeft)); + special = ( + " | ÿc4TierLHS: ÿc0" + tierscore(itemToCheck, 1, sdk.body.RingRight) + + " | ÿc4TierRHS: ÿc0" + tierscore(itemToCheck, 1, sdk.body.RingLeft) + ); } - itemString = "ÿc4MaxQuantity: ÿc0" + NTIP.getMaxQuantity(itemToCheck) + " | ÿc4ItemsOwned: ÿc0" + Item.getQuantityOwned(itemToCheck) + " | ÿc4Tier: ÿc0" + NTIP.GetTier(itemToCheck) + (special ? special : "") - + " | ÿc4SecondaryTier: ÿc0" + NTIP.GetSecondaryTier(itemToCheck) + " | ÿc4MercTier: ÿc0" + NTIP.GetMercTier(itemToCheck) + "\n" - + "ÿc4AutoEquipKeepCheck: ÿc0" + Item.autoEquipCheck(itemToCheck, true) + " | ÿc4AutoEquipCheckSecondary: ÿc0" + Item.autoEquipCheckSecondary(itemToCheck) - + " | ÿc4AutoEquipKeepCheckMerc: ÿc0" + Item.autoEquipCheckMerc(itemToCheck, true) + "\nÿc4Cubing Item: ÿc0" + Cubing.keepItem(itemToCheck) - + " | ÿc4Runeword Item: ÿc0" + Runewords.keepItem(itemToCheck) + " | ÿc4Crafting Item: ÿc0" + CraftingSystem.keepItem(itemToCheck) + " | ÿc4SoloWants Item: ÿc0" + SoloWants.keepItem(itemToCheck) - + "\nÿc4ItemType: ÿc0" + itemToCheck.itemType + "| ÿc4Classid: ÿc0" + itemToCheck.classid + "| ÿc4Quality: ÿc0" + itemToCheck.quality; - charmString = "ÿc4InvoQuantity: ÿc0" + NTIP.getInvoQuantity(itemToCheck) + " | ÿc4hasStats: ÿc0" + NTIP.hasStats(itemToCheck) + " | ÿc4FinalCharm: ÿc0" + CharmEquip.isFinalCharm(itemToCheck) + "\n" - + "ÿc4CharmType: ÿc0" + CharmEquip.getCharmType(itemToCheck) + " | ÿc4AutoEquipCharmCheck: ÿc0" + CharmEquip.check(itemToCheck) + " | ÿc4CharmTier: ÿc0" + NTIP.GetCharmTier(itemToCheck); - generalString = "ÿc4ItemName: ÿc0" + itemToCheck.fname.split("\n").reverse().join(" ").replace(/ÿc[0-9!"+<;.*]/, "") - + "\nÿc4Pickit: ÿc0" + Pickit.checkItem(itemToCheck).result + " | ÿc4NTIP.CheckItem: ÿc0" + NTIP.CheckItem(itemToCheck, false, true).result + " | ÿc4NTIP.CheckItem No Tier: ÿc0" + NTIP.CheckItem(itemToCheck, NTIP_CheckListNoTier, true).result; + itemString = ( + "ÿc4MaxQuantity: ÿc0" + NTIP.getMaxQuantity(itemToCheck) + + " | ÿc4ItemsOwned: ÿc0" + Item.getQuantityOwned(itemToCheck) + + " | ÿc4Tier: ÿc0" + NTIP.GetTier(itemToCheck) + (special ? special : "") + + " | ÿc4SecondaryTier: ÿc0" + NTIP.GetSecondaryTier(itemToCheck) + + " | ÿc4MercTier: ÿc0" + NTIP.GetMercTier(itemToCheck) + "\n" + + "ÿc4AutoEquipKeepCheck: ÿc0" + Item.autoEquipCheck(itemToCheck, true) + + " | ÿc4AutoEquipCheckSecondary: ÿc0" + Item.autoEquipCheckSecondary(itemToCheck) + + " | ÿc4AutoEquipKeepCheckMerc: ÿc0" + Item.autoEquipCheckMerc(itemToCheck, true) + + "\nÿc4Cubing Item: ÿc0" + Cubing.keepItem(itemToCheck) + + " | ÿc4Runeword Item: ÿc0" + Runewords.keepItem(itemToCheck) + + " | ÿc4Crafting Item: ÿc0" + CraftingSystem.keepItem(itemToCheck) + + " | ÿc4SoloWants Item: ÿc0" + SoloWants.keepItem(itemToCheck) + + "\nÿc4ItemType: ÿc0" + itemToCheck.itemType + + "| ÿc4Classid: ÿc0" + itemToCheck.classid + + "| ÿc4Quality: ÿc0" + itemToCheck.quality + + "| ÿc4Ilvl: ÿc0" + itemToCheck.ilvl + + "| ÿc4Gid: ÿc0" + itemToCheck.gid + ); + if (itemToCheck.isCharm) { + charmString = ( + "ÿc4InvoQuantity: ÿc0" + NTIP.getInvoQuantity(itemToCheck) + + " | ÿc4hasStats: ÿc0" + NTIP.hasStats(itemToCheck) + + " | ÿc4FinalCharm: ÿc0" + CharmEquip.isFinalCharm(itemToCheck) + "\n" + + "ÿc4CharmType: ÿc0" + CharmEquip.getCharmType(itemToCheck) + + " | ÿc4AutoEquipCharmCheck: ÿc0" + CharmEquip.check(itemToCheck) + + " | ÿc4CharmTier: ÿc0" + NTIP.GetCharmTier(itemToCheck) + ); + } + let pResult = Pickit.checkItem(itemToCheck); + let pString = "ÿc4Pickit: ÿc0" + pResult.result + " ÿc7Line: ÿc0" + pResult.line + "\n"; + let sResult = NTIP.CheckItem(itemToCheck, NTIP.SoloList, true); + let sString = "ÿc4SoloWants: ÿc0" + sResult.result + " ÿc7Line: ÿc0" + sResult.line + "\n"; + let nResult = NTIP.CheckItem(itemToCheck, NTIP.NoTier, true); + let nString = "ÿc4Solo-NoTier: ÿc0" + nResult.result + " ÿc7Line: ÿc0" + nResult.line + "\n"; + let cResult = NTIP.CheckItem(itemToCheck, NTIP.CheckList, true); + let cString = "ÿc4Nip CheckList: ÿc0" + cResult.result + " ÿc7Line: ÿc0" + cResult.line + "\n"; + generalString = ( + "ÿc4ItemName: ÿc0" + itemToCheck.fname.split("\n").reverse().join(" ").replace(/ÿc[0-9!"+<;.*]/, "") + + "\n" + pString + + "\n" + sString + + "\n" + nString + + "\n" + cString + ); } console.log("ÿc8Kolbot-SoloPlay: ÿc2Item Info Start"); console.log(itemString); - console.log("ÿc8Kolbot-SoloPlay: ÿc2Charm Info Start"); - console.log(charmString); + if (charmString) { + console.log("ÿc8Kolbot-SoloPlay: ÿc2Charm Info Start"); + console.log(charmString); + } console.log("ÿc8Kolbot-SoloPlay: ÿc2General Info Start"); console.log(generalString); console.log("ÿc8Kolbot-SoloPlay: ÿc1****************Info End****************"); @@ -381,7 +463,7 @@ function main () { break; case sdk.keys.NumpadSlash: // re-load default console.log("ÿc8ToolsThread :: " + sdk.colors.Red + "Stopping threads and waiting 5 seconds to restart"); - this.stopDefault() && delay(1e3); + stopDefault() && delay(1e3); load("libs/SoloPlay/Threads/Reload.js"); break; @@ -461,7 +543,7 @@ function main () { if (Config.StopOnDClone && !me.classic && me.hell) { D2Bot.printToConsole("Diablo Walks the Earth", sdk.colors.D2Bot.DarkGold); SoloEvents.cloneWalked = true; - this.togglePause(); + togglePause(); Town.goToTown(); showConsole(); myPrint("ÿc4Diablo Walks the Earth"); @@ -616,7 +698,7 @@ function main () { return timeStr; } - this.run = () => { + this.run = function () { if (getTickCount() - _timeout < 500) return true; _timeout = getTickCount(); @@ -670,28 +752,31 @@ function main () { try { if (me.gameReady && !me.inTown) { // todo - build potion list only once per iteration - Config.UseHP > 0 && me.hpPercent < Config.UseHP && this.drinkPotion(Common.Toolsthread.pots.Health); - Config.UseRejuvHP > 0 && me.hpPercent < Config.UseRejuvHP && this.drinkPotion(Common.Toolsthread.pots.Rejuv); + Config.UseHP > 0 && me.hpPercent < Config.UseHP && drinkPotion(sdk.items.type.HealingPotion); + Config.UseRejuvHP > 0 && me.hpPercent < Config.UseRejuvHP && drinkPotion(sdk.items.type.RejuvPotion); if (Config.LifeChicken > 0 && me.hpPercent <= Config.LifeChicken && !me.inTown) { if (!Developer.hideChickens) { D2Bot.printToConsole("Life Chicken (" + me.hp + "/" + me.hpmax + ")" + Attack.getNearestMonster() + " in " + getAreaName(me.area) + ". Ping: " + me.ping, sdk.colors.D2Bot.Red); } - this.exit(true); + exit(true); return true; } - Config.UseMP > 0 && me.mpPercent < Config.UseMP && this.drinkPotion(Common.Toolsthread.pots.Mana); - Config.UseRejuvMP > 0 && me.mpPercent < Config.UseRejuvMP && this.drinkPotion(Common.Toolsthread.pots.Rejuv); + Config.UseMP > 0 && me.mpPercent < Config.UseMP && drinkPotion(sdk.items.type.ManaPotion); + Config.UseRejuvMP > 0 && me.mpPercent < Config.UseRejuvMP && drinkPotion(sdk.items.type.RejuvPotion); - (me.staminaPercent <= 20 || me.walking) && this.drinkSpecialPotion(sdk.items.StaminaPotion); - me.getState(sdk.states.Poison) && this.drinkSpecialPotion(sdk.items.AntidotePotion); - [sdk.states.Frozen, sdk.states.FrozenSolid].some(state => me.getState(state)) && this.drinkSpecialPotion(sdk.items.ThawingPotion); + (me.staminaPercent <= 20 || me.walking) && drinkSpecialPotion(sdk.items.StaminaPotion); + me.getState(sdk.states.Poison) && drinkSpecialPotion(sdk.items.AntidotePotion); + [sdk.states.Frozen, sdk.states.FrozenSolid] + .some(state => me.getState(state)) && drinkSpecialPotion(sdk.items.ThawingPotion); if (Config.ManaChicken > 0 && me.mpPercent <= Config.ManaChicken && !me.inTown) { - !Developer.hideChickens && D2Bot.printToConsole("Mana Chicken: (" + me.mp + "/" + me.mpmax + ") in " + getAreaName(me.area), sdk.colors.D2Bot.Red); - this.exit(true); + if (!Developer.hideChickens) { + D2Bot.printToConsole("Mana Chicken: (" + me.mp + "/" + me.mpmax + ") in " + getAreaName(me.area), sdk.colors.D2Bot.Red); + } + exit(true); return true; } @@ -704,8 +789,10 @@ function main () { if (ironGolem) { // ironGolem.hpmax is bugged with BO if (ironGolem.hp <= Math.floor(128 * Config.IronGolemChicken / 100)) { - !Developer.hideChickens && D2Bot.printToConsole("Irom Golem Chicken in " + getAreaName(me.area), sdk.colors.D2Bot.Red); - this.exit(true); + if (!Developer.hideChickens) { + D2Bot.printToConsole("Irom Golem Chicken in " + getAreaName(me.area), sdk.colors.D2Bot.Red); + } + exit(true); return true; } @@ -719,14 +806,16 @@ function main () { if (mercHP > 0 && merc.mode !== sdk.monsters.mode.Dead) { if (mercHP < Config.MercChicken) { - !Developer.hideChickens && D2Bot.printToConsole("Merc Chicken in " + getAreaName(me.area), sdk.colors.D2Bot.Red); - this.exit(true); + if (!Developer.hideChickens) { + D2Bot.printToConsole("Merc Chicken in " + getAreaName(me.area), sdk.colors.D2Bot.Red); + } + exit(true); return true; } - mercHP < Config.UseMercHP && this.drinkPotion(Common.Toolsthread.pots.MercHealth); - mercHP < Config.UseMercRejuv && this.drinkPotion(Common.Toolsthread.pots.MercRejuv); + mercHP < Config.UseMercHP && giveMercPotion(sdk.items.type.HealingPotion); + mercHP < Config.UseMercRejuv && giveMercPotion(sdk.items.type.RejuvPotion); } } } @@ -755,12 +844,12 @@ function main () { if (quitFlag && canQuit && (typeof quitListDelayTime === "undefined" || getTickCount() >= quitListDelayTime)) { Common.Toolsthread.checkPing(false); // In case of quitlist triggering first - this.exit(); + exit(); break; } - !!restart && this.restart(); + !!restart && restartGame(); if (debugInfo.area !== getAreaName(me.area)) { debugInfo.area = getAreaName(me.area); From 26c5428d3997ebc84e22fbb39c697aadc3e8dcd2 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Sun, 4 Jun 2023 19:05:54 -0400 Subject: [PATCH 128/263] some refactoring - mostly changing my use of arrow functions to function expressions due to d2bs restrictions - formatting as well to keep the line length --- libs/SoloPlay/Functions/Globals.js | 55 +++++++++---- libs/SoloPlay/Functions/ItemUtilities.js | 91 +++++++++++++++------ libs/SoloPlay/Functions/Me.js | 70 ++++++++++------ libs/SoloPlay/Functions/Polyfills.js | 12 +-- libs/SoloPlay/Functions/PrecastOverrides.js | 7 +- libs/SoloPlay/Functions/SkillOverrides.js | 47 ++++++++--- libs/SoloPlay/Functions/SoloWants.js | 4 +- libs/SoloPlay/Functions/StorageOverrides.js | 4 +- libs/SoloPlay/SoloPlay.js | 16 +++- libs/SoloPlay/Utils/General.js | 77 ++++++++++------- libs/SoloPlay/Utils/Init.js | 18 +++- libs/SoloPlay/Workers/EventEmitter.js | 8 +- libs/SoloPlay/Workers/TownChicken.js | 4 +- 13 files changed, 279 insertions(+), 134 deletions(-) diff --git a/libs/SoloPlay/Functions/Globals.js b/libs/SoloPlay/Functions/Globals.js index 00b6ca07..8560954c 100644 --- a/libs/SoloPlay/Functions/Globals.js +++ b/libs/SoloPlay/Functions/Globals.js @@ -50,7 +50,7 @@ const SetUp = { init: function () { // ensure finalBuild is properly formatted - let checkBuildTemplate = () => { + let checkBuildTemplate = function () { let build = (["Bumper", "Socketmule", "Imbuemule"].includes(SetUp.finalBuild) ? ["Javazon", "Cold", "Bone", "Hammerdin", "Whirlwind", "Wind", "Trapsin"][me.classid] : SetUp.finalBuild) + "Build"; @@ -435,7 +435,8 @@ const SetUp = { Storage.Init(); } // sometimes it seems hard to find skillers, if we have the room lets try to cube some - if (Storage.Stash.UsedSpacePercent() < 60 && CharmEquip.grandCharm().keep.length < CharData.charms.get("grand").count().max) { + if (Storage.Stash.UsedSpacePercent() < 60 + && CharmEquip.grandCharm().keep.length < CharData.charms.get("grand").count().max) { Config.Recipes.push([Recipe.Reroll.Magic, "Grand Charm"]); } // switch bow - only for zon/sorc/pal/necro classes right now @@ -443,6 +444,7 @@ const SetUp = { NTIP.addLine( "([type] == bow || [type] == crossbow) && [quality] >= normal # [itemchargedskill] >= 0 # [secondarytier] == tierscore(item)" ); + this.bowQuiver(); } const expansionExtras = [ // Special Charms @@ -458,7 +460,6 @@ const SetUp = { "me.mercid === 338 && ([type] == polearm || [type] == spear) && ([quality] >= magic || [flag] == runeword) # [itemchargedskill] >= 0 # [Merctier] == mercscore(item)", ]; NTIP.buildList(expansionExtras); - this.bowQuiver(); } /* General configuration. */ @@ -510,15 +511,25 @@ const SetUp = { /* Shrine scan configuration. */ if (Check.currentBuild().caster) { Config.ScanShrines = [ - sdk.shrines.Refilling, sdk.shrines.Health, sdk.shrines.Mana, sdk.shrines.Gem, sdk.shrines.Monster, sdk.shrines.HealthExchange, - sdk.shrines.ManaExchange, sdk.shrines.Experience, sdk.shrines.Armor, sdk.shrines.ResistFire, sdk.shrines.ResistCold, - sdk.shrines.ResistLightning, sdk.shrines.ResistPoison, sdk.shrines.Skill, sdk.shrines.ManaRecharge, sdk.shrines.Stamina + sdk.shrines.Refilling, sdk.shrines.Health, + sdk.shrines.Mana, sdk.shrines.Gem, + sdk.shrines.Monster, sdk.shrines.HealthExchange, + sdk.shrines.ManaExchange, sdk.shrines.Experience, + sdk.shrines.Armor, sdk.shrines.ResistFire, + sdk.shrines.ResistCold, sdk.shrines.ResistLightning, + sdk.shrines.ResistPoison, sdk.shrines.Skill, + sdk.shrines.ManaRecharge, sdk.shrines.Stamina ]; } else { Config.ScanShrines = [ - sdk.shrines.Refilling, sdk.shrines.Health, sdk.shrines.Mana, sdk.shrines.Gem, sdk.shrines.Monster, sdk.shrines.HealthExchange, - sdk.shrines.ManaExchange, sdk.shrines.Experience, sdk.shrines.Combat, sdk.shrines.Skill, sdk.shrines.Armor, sdk.shrines.ResistFire, - sdk.shrines.ResistCold, sdk.shrines.ResistLightning, sdk.shrines.ResistPoison, sdk.shrines.ManaRecharge, sdk.shrines.Stamina + sdk.shrines.Refilling, sdk.shrines.Health, + sdk.shrines.Mana, sdk.shrines.Gem, + sdk.shrines.Monster, sdk.shrines.HealthExchange, + sdk.shrines.ManaExchange, sdk.shrines.Experience, + sdk.shrines.Combat, sdk.shrines.Skill, + sdk.shrines.Armor, sdk.shrines.ResistFire, + sdk.shrines.ResistCold, sdk.shrines.ResistLightning, + sdk.shrines.ResistPoison, sdk.shrines.ManaRecharge, sdk.shrines.Stamina ]; } @@ -677,7 +688,8 @@ const Check = { } // Broken - if ((me.equipped.get(sdk.body.RightArm).durability === 0 || me.equipped.get(sdk.body.LeftArm).durability === 0) && me.charlvl >= 15 && !me.normal && gold < 1000) { + if ((me.equipped.get(sdk.body.RightArm).durability === 0 || me.equipped.get(sdk.body.LeftArm).durability === 0) + && me.charlvl >= 15 && !me.normal && gold < 1000) { return 2; } @@ -730,7 +742,12 @@ const Check = { resistance: function () { let resPenalty = me.getResPenalty(me.diff + 1); - let [frRes, lrRes, crRes, prRes] = [(me.realFR - resPenalty), (me.realLR - resPenalty), (me.realCR - resPenalty), (me.realPR - resPenalty)]; + let [frRes, lrRes, crRes, prRes] = [ + (me.realFR - resPenalty), + (me.realLR - resPenalty), + (me.realCR - resPenalty), + (me.realPR - resPenalty) + ]; return { Status: ((frRes > 0) && (lrRes > 0) && (crRes > 0)), @@ -784,7 +801,8 @@ const Check = { switch (me.diff) { case sdk.difficulty.Normal: // Have runes or stealth and ancients pledge - if (me.haveRunes([sdk.items.runes.Tal, sdk.items.runes.Eth]) || me.checkItem({ name: sdk.locale.items.Stealth }).have) { + if (me.haveRunes([sdk.items.runes.Tal, sdk.items.runes.Eth]) + || me.checkItem({ name: sdk.locale.items.Stealth }).have) { needRunes = false; } @@ -900,7 +918,9 @@ const Check = { let socketableCHECK = isClassID ? Config.socketables.find(({ classid }) => type === classid) : false; let items = me.getItemsEx() .filter(function (item) { - return item.quality === quality && !item.questItem && !item.isRuneword && (isClassID ? item.classid === type : item.itemType === type) && getBaseStat("items", item.classid, "gemsockets") > 0; + return item.quality === quality && !item.questItem && !item.isRuneword + && (isClassID ? item.classid === type : item.itemType === type) + && getBaseStat("items", item.classid, "gemsockets") > 0; }); for (let i = 0; i < items.length; i++) { @@ -943,10 +963,13 @@ const Check = { } let items = me.getItemsEx() - .filter(item => item.isBaseType && item.isInStorage && (isClassID ? item.classid === type : item.itemType === type)); + .filter(function (item) { + return item.isBaseType && item.isInStorage && (isClassID ? item.classid === type : item.itemType === type); + }); for (let i = 0; i < items.length; i++) { - if (items[i].sockets === sockets && (isClassID ? items[i].classid === type : items[i].itemType === type)) { + if (items[i].sockets === sockets + && (isClassID ? items[i].classid === type : items[i].itemType === type)) { return true; } } @@ -961,7 +984,7 @@ const Check = { const shorthandDex = [sdk.stats.Dexterity, "d", "dex", "dexterity"]; const statToCheck = shorthandStr.includes(stat) ? "str" : shorthandDex.includes(stat) ? "dex" : ""; - buildInfo.stats.forEach(s => { + buildInfo.stats.forEach(function (s) { switch (true) { case (shorthandStr.includes(s[0]) && statToCheck === "str"): case (shorthandDex.includes(s[0]) && statToCheck === "dex"): diff --git a/libs/SoloPlay/Functions/ItemUtilities.js b/libs/SoloPlay/Functions/ItemUtilities.js index 78055ed7..7cf5dbe2 100644 --- a/libs/SoloPlay/Functions/ItemUtilities.js +++ b/libs/SoloPlay/Functions/ItemUtilities.js @@ -9,11 +9,11 @@ includeIfNotIncluded("core/Item.js"); (function () { /** * @param {ItemUnit} item - * @param {*} buildInfo + * @param {Build} buildInfo * @returns {number} * @todo clean this up (sigh) - 8/10/22 - update refactored alot, still think more can be done though */ - const baseSkillsScore = (item, buildInfo) => { + const baseSkillsScore = function (item, buildInfo) { buildInfo === undefined && (buildInfo = Check.currentBuild()); let generalScore = 0; let selectedWeights = [30, 20]; @@ -45,39 +45,67 @@ includeIfNotIncluded("core/Item.js"); let result = false, preSocketCheck = false; let bodyLoc = Item.getBodyLoc(base); - const checkNoSockets = (item) => [ - sdk.locale.items.AncientsPledge, sdk.locale.items.Exile, sdk.locale.items.Lore, sdk.locale.items.White, sdk.locale.items.Rhyme - ].includes(item.prefixnum) || (item.prefixnum === sdk.locale.items.Spirit && item.getItemType() === "Shield"); - const getRes = (item) => item.getStatEx(sdk.stats.FireResist) + item.getStatEx(sdk.stats.LightResist) + item.getStatEx(sdk.stats.ColdResist) + item.getStatEx(sdk.stats.PoisonResist); - const getDmg = (item) => item.getStatEx(sdk.stats.MinDamage) + item.getStatEx(sdk.stats.MaxDamage); - const getRealDmg = (item, maxED = 0, minOffset = 0, maxOffset = 0) => { + /** @param {ItemUnit} item */ + const checkNoSockets = function (item) { + return item.normal && [ + sdk.locale.items.AncientsPledge, + sdk.locale.items.Exile, + sdk.locale.items.Lore, + sdk.locale.items.White, + sdk.locale.items.Rhyme + ].includes(item.prefixnum) || (item.prefixnum === sdk.locale.items.Spirit && item.getItemType() === "Shield"); + }; + /** @param {ItemUnit} item */ + const getRes = function (item) { + return ( + item.getStatEx(sdk.stats.FireResist) + item.getStatEx(sdk.stats.LightResist) + + item.getStatEx(sdk.stats.ColdResist) + item.getStatEx(sdk.stats.PoisonResist) + ); + }; + /** @param {ItemUnit} item */ + const getDmg = function (item) { + return item.getStatEx(sdk.stats.MinDamage) + item.getStatEx(sdk.stats.MaxDamage); + }; + /** @param {ItemUnit} item */ + const getRealDmg = function (item, maxED = 0, minOffset = 0, maxOffset = 0) { let ED = item.getStatEx(sdk.stats.EnhancedDamage); ED > maxED && (ED = maxED); let itemsMinDmg = Math.ceil(((item.getStatEx(sdk.stats.MinDamage) - minOffset) / ((ED + 100) / 100))); let itemsMaxDmg = Math.ceil(((item.getStatEx(sdk.stats.MaxDamage) - maxOffset) / ((ED + 100) / 100))); return (itemsMinDmg + itemsMaxDmg); }; - const getDef = (item) => item.getStatEx(sdk.stats.Defense); - const getRealDef = (item, maxEDef) => { + /** @param {ItemUnit} item */ + const getDef = function (item) { + return item.getStatEx(sdk.stats.Defense); + }; + /** @param {ItemUnit} item */ + const getRealDef = function (item, maxEDef) { let ED = item.getStatEx(sdk.stats.ArmorPercent); ED > maxEDef && (ED = maxEDef); return (Math.ceil((item.getStatEx(sdk.stats.Defense) / ((ED + 100) / 100)))); }; - const resCheck = (baseResists, itemsResists) => { - verbose && console.log("ÿc9BetterThanWearingCheckÿc0 :: RW(" + name + ") BaseResists: " + baseResists + " EquippedItem: " + itemsResists); + const resCheck = function (baseResists, itemsResists) { + if (verbose) { + console.log("ÿc9BetterThanWearingCheckÿc0 :: RW(" + name + ") BaseResists: " + baseResists + " EquippedItem: " + itemsResists); + } return (baseResists > itemsResists); }; - const defCheck = (itemsDefense, baseDefense) => { - verbose && console.log("ÿc9BetterThanWearingCheckÿc0 :: RW(" + name + ") BaseDefense: " + baseDefense + " EquippedItem: " + itemsDefense); + const defCheck = function (itemsDefense, baseDefense) { + if (verbose) { + console.log("ÿc9BetterThanWearingCheckÿc0 :: RW(" + name + ") BaseDefense: " + baseDefense + " EquippedItem: " + itemsDefense); + } return (baseDefense > itemsDefense); }; - const dmgCheck = (itemsTotalDmg, baseDmg) => { - verbose && console.log("ÿc9BetterThanWearingCheckÿc0 :: RW(" + name + ") BaseDamage: " + baseDmg + " EquippedItem: " + itemsTotalDmg); + const dmgCheck = function (itemsTotalDmg, baseDmg) { + if (verbose) { + console.log("ÿc9BetterThanWearingCheckÿc0 :: RW(" + name + ") BaseDamage: " + baseDmg + " EquippedItem: " + itemsTotalDmg); + } return (baseDmg > itemsTotalDmg); }; // @todo - betterThanMercUsing check for now just keep merc items - if ([sdk.items.type.Polearm, sdk.items.type.Spear].includes(base.itemType) || ([sdk.items.type.Armor].includes(base.itemType) && base.ethereal)) { + if ([sdk.items.type.Polearm, sdk.items.type.Spear].includes(base.itemType) + || ([sdk.items.type.Armor].includes(base.itemType) && base.ethereal)) { let merc = me.getMercEx(); if (merc) { bodyLoc = Item.getBodyLocMerc(base); @@ -116,11 +144,14 @@ includeIfNotIncluded("core/Item.js"); // don't toss pb base crescent moon/HoJ/Grief if (base.classid === sdk.items.PhaseBlade && [3, 4, 5].includes(base.sockets)) return true; - let items = me.getItemsEx().filter(i => i.isEquipped && bodyLoc.includes(i.bodylocation)); + let items = me.getItemsEx() + .filter(function (i) { + return i.isEquipped && bodyLoc.includes(i.bodylocation); + }); for (let i = 0; i < bodyLoc.length; i++) { let equippedItem = items.find(item => item.bodylocation === bodyLoc[i]); - if (!equippedItem || !equippedItem.runeword || NTIP.GetTier(equippedItem) >= NTIP.MAX_TIER) { + if (!equippedItem || !equippedItem.runeword /* || NTIP.GetTier(equippedItem) >= NTIP.MAX_TIER */) { if (i === 0 && bodyLoc.length > 1) continue; return true; } @@ -247,7 +278,12 @@ includeIfNotIncluded("core/Item.js"); [equippedSkillsTier, baseSkillsTier] = [(baseSkillsScore(equippedItem) - 550), baseSkillsScore(base)]; if (equippedSkillsTier !== baseSkillsTier) { - verbose && console.log("ÿc9BetterThanWearingCheckÿc0 :: RW(White) EquippedSkillsTier: " + equippedSkillsTier + " BaseSkillsTier: " + baseSkillsTier); + if (verbose) { + console.debug( + "ÿc0 :: RW(White) EquippedSkillsTier: " + equippedSkillsTier + + " BaseSkillsTier: " + baseSkillsTier + ); + } if (baseSkillsTier > equippedSkillsTier) return true; } } @@ -329,17 +365,20 @@ includeIfNotIncluded("core/Item.js"); if (base.sockets === 1) return false; verbose === undefined && (verbose = Developer.debugging.baseCheck); + /** @param {ItemUnit} item */ const defenseScore = (item) => ({ def: item.getStatEx(sdk.stats.Defense), eDef: item.getStatEx(sdk.stats.ArmorPercent) }); + /** @param {ItemUnit} item */ const dmgScore = (item) => ({ dmg: Math.round((item.getStatEx(sdk.stats.MinDamagePercent) + item.getStatEx(sdk.stats.MaxDamagePercent)) / 2), twoHandDmg: Math.round((item.getStatEx(sdk.stats.SecondaryMinDamage) + item.getStatEx(sdk.stats.SecondaryMaxDamage)) / 2), eDmg: item.getStatEx(sdk.stats.EnhancedDamage) }); + /** @param {ItemUnit} item */ const generalScore = (item) => { const buildInfo = Check.currentBuild(); let generalScore = baseSkillsScore(item, buildInfo); @@ -379,7 +418,7 @@ includeIfNotIncluded("core/Item.js"); * @param {ItemUnit} a * @param {ItemUnit} b */ - const defenseSort = (a, b) => { + const defenseSort = function (a, b) { let [aDef, bDef] = [a.getStatEx(sdk.stats.Defense), b.getStatEx(sdk.stats.Defense)]; if (aDef !== bDef) return aDef - bDef; return a.getStatEx(sdk.stats.ArmorPercent) - b.getStatEx(sdk.stats.ArmorPercent); @@ -389,7 +428,7 @@ includeIfNotIncluded("core/Item.js"); * @param {ItemUnit} a * @param {ItemUnit} b */ - const generalScoreSort = (a, b) => { + const generalScoreSort = function (a, b) { let [aScore, bScore] = [generalScore(a), generalScore(b)]; if (aScore !== bScore) return aScore - bScore; let [aSoc, bSoc] = [a.sockets, b.sockets]; @@ -406,13 +445,13 @@ includeIfNotIncluded("core/Item.js"); * @param {ItemUnit} a * @param {ItemUnit} b */ - const twoHandDmgSort = (a, b) => { + const twoHandDmgSort = function (a, b) { let [aDmg, bDmg] = [dmgScore(a), dmgScore(b)]; if (aDmg.twoHandDmg !== bDmg.twoHandDmg) return aDmg.twoHandDmg - bDmg.twoHandDmg; return aDmg.eDmg - bDmg.eDmg; }; - const defenseScoreCheck = (base, itemToCheck) => { + const defenseScoreCheck = function (base, itemToCheck) { let [defScoreBase, defScoreItem] = [defenseScore(base), defenseScore(itemToCheck)]; verbose && console.log("ÿc9betterThanStashedÿc0 :: BaseScore: ", defScoreBase, " itemToCheckScore: ", defScoreItem); if (defScoreBase.def > defScoreItem.def || (defScoreBase.def === defScoreItem.def && (defScoreBase.eDef > defScoreItem.eDef || base.ilvl > itemToCheck.ilvl))) { @@ -421,7 +460,7 @@ includeIfNotIncluded("core/Item.js"); return false; }; - const damageScoreCheck = (base, itemToCheck) => { + const damageScoreCheck = function (base, itemToCheck) { let [gScoreBase, gScoreCheck] = [generalScore(base), generalScore(itemToCheck)]; verbose && console.log("ÿc9betterThanStashedÿc0 :: BaseScore: " + generalScore(base) + " itemToCheckScore: " + generalScore(itemToCheck)); switch (true) { @@ -433,7 +472,7 @@ includeIfNotIncluded("core/Item.js"); return false; }; - const generalScoreCheck = (base, itemToCheck) => { + const generalScoreCheck = function (base, itemToCheck) { let [gScoreBase, gScoreCheck] = [generalScore(base), generalScore(itemToCheck)]; verbose && console.log("ÿc9betterThanStashedÿc0 :: BaseScore: " + generalScore(base) + " itemToCheckScore: " + generalScore(itemToCheck)); if (gScoreBase > gScoreCheck) return true; diff --git a/libs/SoloPlay/Functions/Me.js b/libs/SoloPlay/Functions/Me.js index af6d1de7..8f156e0e 100644 --- a/libs/SoloPlay/Functions/Me.js +++ b/libs/SoloPlay/Functions/Me.js @@ -115,7 +115,7 @@ if (!me.hasOwnProperty("onFinalBuild")) { if (!me.hasOwnProperty("mercid")) { Object.defineProperty(me, "mercid", { get: function () { - return me.data.merc.classid || (() => { + return me.data.merc.classid || (function () { let merc = me.getMercEx(); if (!merc) return 0; me.data.merc.classid = merc.classid; @@ -128,7 +128,7 @@ if (!me.hasOwnProperty("mercid")) { if (!me.hasOwnProperty("trueStr")) { Object.defineProperty(me, "trueStr", { get: function () { - return me.data.strength || (() => { + return me.data.strength || (function () { let str = me.rawStrength; me.data.strength = str; return str; @@ -140,7 +140,7 @@ if (!me.hasOwnProperty("trueStr")) { if (!me.hasOwnProperty("trueDex")) { Object.defineProperty(me, "trueDex", { get: function () { - return me.data.dexterity || (() => { + return me.data.dexterity || (function () { let dex = me.rawDexterity; me.data.dexterity = dex; return dex; @@ -334,19 +334,20 @@ if (!me.hasOwnProperty("equipped")) { }; /** @type {Map} */ - const bodyMap = new Map(); - bodyMap.set(sdk.body.Head, new EquippedItem()); - bodyMap.set(sdk.body.Neck, new EquippedItem()); - bodyMap.set(sdk.body.Armor, new EquippedItem()); - bodyMap.set(sdk.body.RightArm, new EquippedItem()); - bodyMap.set(sdk.body.LeftArm, new EquippedItem()); - bodyMap.set(sdk.body.Gloves, new EquippedItem()); - bodyMap.set(sdk.body.RingRight, new EquippedItem()); - bodyMap.set(sdk.body.RingLeft, new EquippedItem()); - bodyMap.set(sdk.body.Belt, new EquippedItem()); - bodyMap.set(sdk.body.Feet, new EquippedItem()); - bodyMap.set(sdk.body.RightArmSecondary, new EquippedItem()); - bodyMap.set(sdk.body.LeftArmSecondary, new EquippedItem()); + const bodyMap = new Map([ + [sdk.body.Head, new EquippedItem()], + [sdk.body.Neck, new EquippedItem()], + [sdk.body.Armor, new EquippedItem()], + [sdk.body.RightArm, new EquippedItem()], + [sdk.body.LeftArm, new EquippedItem()], + [sdk.body.Gloves, new EquippedItem()], + [sdk.body.RingRight, new EquippedItem()], + [sdk.body.RingLeft, new EquippedItem()], + [sdk.body.Belt, new EquippedItem()], + [sdk.body.Feet, new EquippedItem()], + [sdk.body.RightArmSecondary, new EquippedItem()], + [sdk.body.LeftArmSecondary, new EquippedItem()], + ]); // const _dummy = new EquippedItem(); @@ -358,7 +359,9 @@ if (!me.hasOwnProperty("equipped")) { get: function (bodylocation) { // if (!bodyMap.has(bodylocation)) return _dummy; let item = bodyMap.get(bodylocation); - if (item._gid === -1 || (item._gid !== item.gid || (item._bodylocation !== item.location && me.weaponswitch !== sdk.player.slot.Secondary))) { + if (item._gid === -1 + || (item._gid !== item.gid + || (item._bodylocation !== item.location && me.weaponswitch !== sdk.player.slot.Secondary))) { // item has changed - find the new item let newItem = me.getItemsEx() .filter((el) => el.isEquipped && el.bodylocation === bodylocation) @@ -450,17 +453,28 @@ me.getSkillTabs = function (classid = me.classid) { // need check for ranged mobs so we can stick and move to avoid missiles me.inDanger = function (checkLoc, range) { let count = 0; - let _this = typeof checkLoc !== "undefined" && checkLoc.hasOwnProperty("x") ? checkLoc : me; + const _this = typeof checkLoc !== "undefined" && checkLoc.hasOwnProperty("x") + ? checkLoc + : me; range === undefined && (range = 10); - let nearUnits = getUnits(sdk.unittype.Monster).filter((mon) => mon && mon.attackable && getDistance(_this, mon) < 10); - nearUnits.forEach(u => u.isSpecial - ? [sdk.states.Fanaticism, sdk.states.Conviction].some(state => u.getState(state)) - ? (count += 3) - : (count += 2) - : (count += 1)); + let nearUnits = getUnits(sdk.unittype.Monster) + .filter(function (mon) { + return mon && mon.attackable && getDistance(_this, mon) < 10; + }); + nearUnits.forEach(function (u) { + return u.isSpecial + ? [sdk.states.Fanaticism, sdk.states.Conviction].some(state => u.getState(state)) + ? (count += 3) + : (count += 2) + : (count += 1); + }); if (count > me.maxNearMonsters) return true; let dangerClose = nearUnits - .find(mon => [sdk.enchant.ManaBurn, sdk.enchant.LightningEnchanted, sdk.enchant.FireEnchanted].some(chant => mon.getEnchant(chant))); + .find(function (mon) { + return [ + sdk.enchant.ManaBurn, sdk.enchant.LightningEnchanted, sdk.enchant.FireEnchanted + ].some(chant => mon.getEnchant(chant)); + }); return dangerClose; }; @@ -659,7 +673,8 @@ me.clearBelt = function () { }; me.getIdTool = function () { - let items = me.getItemsEx().filter((i) => i.isInInventory && [sdk.items.ScrollofIdentify, sdk.items.TomeofIdentify].includes(i.classid)); + let items = me.getItemsEx() + .filter((i) => i.isInInventory && [sdk.items.ScrollofIdentify, sdk.items.TomeofIdentify].includes(i.classid)); let scroll = items.find((i) => i.isInInventory && i.classid === sdk.items.ScrollofIdentify); if (scroll) return scroll; let tome = items.find((i) => i.isInInventory && i.classid === sdk.items.TomeofIdentify); @@ -669,7 +684,8 @@ me.getIdTool = function () { }; me.getTpTool = function () { - let items = me.getItemsEx(-1, sdk.items.mode.inStorage).filter((i) => i.isInInventory && [sdk.items.ScrollofTownPortal, sdk.items.TomeofTownPortal].includes(i.classid)); + let items = me.getItemsEx(-1, sdk.items.mode.inStorage) + .filter((i) => i.isInInventory && [sdk.items.ScrollofTownPortal, sdk.items.TomeofTownPortal].includes(i.classid)); if (!items.length) return null; let tome = items.find((i) => i.classid === sdk.items.TomeofTownPortal && i.getStat(sdk.stats.Quantity) > 0); if (tome) return tome; diff --git a/libs/SoloPlay/Functions/Polyfills.js b/libs/SoloPlay/Functions/Polyfills.js index 84838762..6d7d328c 100644 --- a/libs/SoloPlay/Functions/Polyfills.js +++ b/libs/SoloPlay/Functions/Polyfills.js @@ -22,16 +22,16 @@ if (!Array.prototype.at) { */ (function (global, _original) { - let __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { + let __createBinding = (this && this.__createBinding) || (Object.create ? (function (o, m, k, k2) { if (k2 === undefined) k2 = k; - Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); - }) : (function(o, m, k, k2) { + Object.defineProperty(o, k2, { enumerable: true, get: function () { return m[k]; } }); + }) : (function (o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); - let __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { + let __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function (o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); - }) : function(o, v) { + }) : function (o, v) { o.default = v; }); let __importStar = (this && this.__importStar) || function (mod) { @@ -49,7 +49,7 @@ if (!Array.prototype.at) { * @param args * @constructor */ - function Timer(cb, time, args) { + function Timer (cb, time, args) { let _this = this; if (time === void 0) { time = 0; } if (args === void 0) { args = []; } diff --git a/libs/SoloPlay/Functions/PrecastOverrides.js b/libs/SoloPlay/Functions/PrecastOverrides.js index a6df6f4b..8c35eadf 100644 --- a/libs/SoloPlay/Functions/PrecastOverrides.js +++ b/libs/SoloPlay/Functions/PrecastOverrides.js @@ -51,7 +51,9 @@ new Overrides.Override(Precast, Precast.doPrecast, function (orignal, force) { me.weaponswitch !== primary && me.switchWeapons(primary); } - if (CharData.skillData.haveChargedSkill(sdk.skills.Enchant) && !me.getState(sdk.states.Enchant) && me.gold > 500000) { + if (CharData.skillData.haveChargedSkill(sdk.skills.Enchant) + && !me.getState(sdk.states.Enchant) + && me.gold > 500000) { // Cast enchant Attack.castCharges(sdk.skills.Enchant, me); } @@ -77,7 +79,8 @@ Precast.summon = function (skillId, minionType) { let coord = CollMap.getRandCoordinate(me.x, -3, 3, me.y, -3, 3); // Get a random coordinate to summon using let unit = Attack.getNearestMonster({ skipImmune: false }); - if (unit && [sdk.summons.type.Golem, sdk.summons.type.Grizzly, sdk.summons.type.Shadow].includes(minionType) && unit.distance < 20 && !checkCollision(me, unit, sdk.collision.Ranged)) { + if (unit && [sdk.summons.type.Golem, sdk.summons.type.Grizzly, sdk.summons.type.Shadow].includes(minionType) + && unit.distance < 20 && !checkCollision(me, unit, sdk.collision.Ranged)) { try { if (Skill.cast(skillId, sdk.skills.hand.Right, unit)) { if (me.getMinionCount(minionType) === count) { diff --git a/libs/SoloPlay/Functions/SkillOverrides.js b/libs/SoloPlay/Functions/SkillOverrides.js index 50365c3d..67383604 100644 --- a/libs/SoloPlay/Functions/SkillOverrides.js +++ b/libs/SoloPlay/Functions/SkillOverrides.js @@ -8,14 +8,29 @@ includeIfNotIncluded("core/Skill.js"); includeIfNotIncluded("SoloPlay/Tools/Developer.js"); -Skill.forcePacket = (Developer.forcePacketCasting.enabled && !Developer.forcePacketCasting.excludeProfiles.includes(me.profile)); +Skill.forcePacket = ( + Developer.forcePacketCasting.enabled + && !Developer.forcePacketCasting.excludeProfiles.includes(me.profile) +); Skill.casterSkills = [ - sdk.skills.FireBolt, sdk.skills.ChargedBolt, sdk.skills.IceBolt, sdk.skills.FrostNova, sdk.skills.IceBlast, sdk.skills.FireBall, - sdk.skills.Nova, sdk.skills.Lightning, sdk.skills.ChainLightning, sdk.skills.Teleport, sdk.skills.GlacialSpike, sdk.skills.Meteor, - sdk.skills.Blizzard, sdk.skills.FrozenOrb, sdk.skills.BoneSpear, sdk.skills.Decrepify, sdk.skills.PoisonNova, sdk.skills.BoneSpirit, - sdk.skills.HolyBolt, sdk.skills.BlessedHammer, sdk.skills.FistoftheHeavens, sdk.skills.Howl, sdk.skills.Taunt, sdk.skills.BattleCry, - sdk.skills.WarCry, sdk.skills.Firestorm, sdk.skills.MoltenBoulder, sdk.skills.ArcticBlast, sdk.skills.Fissure, sdk.skills.Twister, - sdk.skills.Volcano, sdk.skills.Armageddon, sdk.skills.Hurricane, sdk.skills.FireBlast, sdk.skills.ShockField, sdk.skills.ChargedBoltSentry, + sdk.skills.FireBolt, sdk.skills.ChargedBolt, + sdk.skills.IceBolt, sdk.skills.FrostNova, + sdk.skills.IceBlast, sdk.skills.FireBall, + sdk.skills.Nova, sdk.skills.Lightning, + sdk.skills.ChainLightning, sdk.skills.Teleport, + sdk.skills.GlacialSpike, sdk.skills.Meteor, + sdk.skills.Blizzard, sdk.skills.FrozenOrb, + sdk.skills.BoneSpear, sdk.skills.Decrepify, + sdk.skills.PoisonNova, sdk.skills.BoneSpirit, + sdk.skills.HolyBolt, sdk.skills.BlessedHammer, + sdk.skills.FistoftheHeavens, sdk.skills.Howl, + sdk.skills.Taunt, sdk.skills.BattleCry, + sdk.skills.WarCry, sdk.skills.Firestorm, + sdk.skills.MoltenBoulder, sdk.skills.ArcticBlast, + sdk.skills.Fissure, sdk.skills.Twister, + sdk.skills.Volcano, sdk.skills.Armageddon, + sdk.skills.Hurricane, sdk.skills.FireBlast, + sdk.skills.ShockField, sdk.skills.ChargedBoltSentry, /* sdk.skills.WakeofFire, */ sdk.skills.LightningSentry, sdk.skills.DeathSentry ]; @@ -48,7 +63,8 @@ Skill.cast = function (skillId, hand, x, y, item) { if (!this.setSkill(skillId, hand, item)) return false; if (Config.PacketCasting > 1 || [sdk.skills.Teleport, sdk.skills.Telekinesis].includes(skillId) - || (this.forcePacket && this.casterSkills.includes(skillId) && (!!me.realm || [sdk.skills.Teeth, sdk.skills.Tornado].indexOf(skillId) === -1))) { + || (this.forcePacket && this.casterSkills.includes(skillId) + && (!!me.realm || [sdk.skills.Teeth, sdk.skills.Tornado].indexOf(skillId) === -1))) { switch (typeof x) { case "number": Packet.castSkill(hand, x, y); @@ -62,7 +78,7 @@ Skill.cast = function (skillId, hand, x, y, item) { break; } } else { - let [clickType, shift] = (() => { + let [clickType, shift] = (function () { switch (hand) { case sdk.skills.hand.Left: // Left hand + Shift return [sdk.clicktypes.click.map.LeftDown, sdk.clicktypes.shift.Shift]; @@ -158,7 +174,8 @@ Skill.switchCast = function (skillId, givenSettings = {}) { Skill.setSkill(Config.AttackSkill[2], sdk.skills.hand.Right); } - if ((this.forcePacket && this.casterSkills.includes(skillId) && (!!me.realm || [sdk.skills.Teeth, sdk.skills.Tornado].indexOf(skillId) === -1)) + if ((this.forcePacket && this.casterSkills.includes(skillId) + && (!!me.realm || [sdk.skills.Teeth, sdk.skills.Tornado].indexOf(skillId) === -1)) || Config.PacketCasting > 1 || skillId === sdk.skills.Teleport) { switch (typeof settings.x) { @@ -174,7 +191,7 @@ Skill.switchCast = function (skillId, givenSettings = {}) { // make sure we give enough time back so we don't fail our next cast delay(250); } else { - let [clickType, shift] = (() => { + let [clickType, shift] = (function () { switch (settings.hand) { case sdk.skills.hand.Left: // Left hand + Shift return [sdk.clicktypes.click.map.LeftDown, sdk.clicktypes.shift.Shift]; @@ -190,9 +207,13 @@ Skill.switchCast = function (skillId, givenSettings = {}) { MainLoop: for (let n = 0; n < 3; n += 1) { - typeof settings.x === "object" ? clickMap(clickType, shift, settings.x) : clickMap(clickType, shift, settings.x, settings.y); + typeof settings.x === "object" + ? clickMap(clickType, shift, settings.x) + : clickMap(clickType, shift, settings.x, settings.y); delay(20); - typeof settings.x === "object" ? clickMap(clickType + 2, shift, settings.x) : clickMap(clickType + 2, shift, settings.x, settings.y); + typeof settings.x === "object" + ? clickMap(clickType + 2, shift, settings.x) + : clickMap(clickType + 2, shift, settings.x, settings.y); for (let i = 0; i < 8; i++) { if (me.attacking) { diff --git a/libs/SoloPlay/Functions/SoloWants.js b/libs/SoloPlay/Functions/SoloWants.js index f1dc8a76..4400dc7c 100644 --- a/libs/SoloPlay/Functions/SoloWants.js +++ b/libs/SoloPlay/Functions/SoloWants.js @@ -101,7 +101,9 @@ const SoloWants = { // add the wanted items to the list for (let i = 0; i < numSockets - (hasWantedItems ? socketedWith.length : 0); i++) { // handle different wanted socketables - curr.socketWith.length === numSockets ? list.push(curr.socketWith[i]) : list.push(curr.socketWith[0]); + curr.socketWith.length === numSockets + ? list.push(curr.socketWith[i]) + : list.push(curr.socketWith[0]); } // currently no sockets but we might use our socket quest on it diff --git a/libs/SoloPlay/Functions/StorageOverrides.js b/libs/SoloPlay/Functions/StorageOverrides.js index 3e188290..47aa3c89 100644 --- a/libs/SoloPlay/Functions/StorageOverrides.js +++ b/libs/SoloPlay/Functions/StorageOverrides.js @@ -8,7 +8,7 @@ */ includeIfNotIncluded("SoloPlay/Tools/Developer.js"); -(function() { +(function () { /** * @constructor * @param {string} name - container name @@ -16,7 +16,7 @@ includeIfNotIncluded("SoloPlay/Tools/Developer.js"); * @param {number} height - container height * @param {number} location - container location */ - function Container(name, width, height, location) { + function Container (name, width, height, location) { this.name = name; this.width = width; this.height = height; diff --git a/libs/SoloPlay/SoloPlay.js b/libs/SoloPlay/SoloPlay.js index cc53bc08..d0db9761 100644 --- a/libs/SoloPlay/SoloPlay.js +++ b/libs/SoloPlay/SoloPlay.js @@ -209,8 +209,11 @@ function main () { // Check for experience decrease -> log death. Skip report if life chicken is disabled. if (stats.name === me.name && me.getStat(sdk.stats.Experience) < stats.experience && Config.LifeChicken > 0) { - D2Bot.printToConsole("You died in last game. | Area :: " + stats.lastArea + " | Script :: " + stats.lastScript, sdk.colors.D2Bot.Red); - D2Bot.printToConsole("Experience decreased by " + (stats.experience - me.getStat(sdk.stats.Experience)), sdk.colors.D2Bot.Red); + D2Bot.printToConsole( + "You died in last game. | Area :: " + stats.lastArea + " | Script :: " + stats.lastScript + "\n" + + "Experience decreased by " + (stats.experience - me.getStat(sdk.stats.Experience)), + sdk.colors.D2Bot.Red + ); DataFile.updateStats("deaths"); D2Bot.updateDeaths(); } @@ -230,7 +233,9 @@ function main () { // check in case we reloaded and guard was still running let guard = getScript("libs/SoloPlay/Modules/Guard.js"); !!guard && guard.running && guard.stop(); - Developer.debugging.showStack.profiles.some(prof => String.isEqual(prof, me.profile) || String.isEqual(prof, "all")) && require("../SoloPlay/Modules/Guard"); + Developer.debugging.showStack.profiles + .some(prof => String.isEqual(prof, me.profile) || String.isEqual(prof, "all")) + && require("../SoloPlay/Modules/Guard"); delay(1000); } @@ -304,7 +309,10 @@ function main () { Town.goToTown(); while (getTickCount() - startTime < Time.seconds(Config.MinGameTime)) { - me.overhead("Stalling for " + Math.round(((startTime + Time.seconds(Config.MinGameTime)) - getTickCount()) / 1000) + " Seconds"); + me.overhead( + "Stalling for " + + Math.round(((startTime + Time.seconds(Config.MinGameTime)) - getTickCount()) / 1000) + " Seconds" + ); delay(1000); } } catch (e1) { diff --git a/libs/SoloPlay/Utils/General.js b/libs/SoloPlay/Utils/General.js index f4839f3a..ab2ead4d 100644 --- a/libs/SoloPlay/Utils/General.js +++ b/libs/SoloPlay/Utils/General.js @@ -1,6 +1,10 @@ (function (module) { // these builds are not possible to do on classic - const impossibleClassicBuilds = ["Bumper", "Socketmule", "Witchyzon", "Auradin", "Torchadin", "Immortalwhirl", "Sancdreamer", "Faithbowzon", "Wfzon"]; + const impossibleClassicBuilds = [ + "Bumper", "Socketmule", "Witchyzon", + "Auradin", "Torchadin", "Immortalwhirl", + "Sancdreamer", "Faithbowzon", "Wfzon" + ]; // these builds are not possible to do without ladder runewords const impossibleNonLadderBuilds = ["Auradin", "Sancdreamer", "Faithbowzon"]; @@ -67,43 +71,56 @@ me.nightmare && item.ilvl >= 26 && item.isBaseType && item.ethereal )); // insight base - basicSocketables.all.push(addSocketableObj(sdk.items.ColossusVoulge, [], [], true, (item) => - me.nightmare && item.ilvl >= 26 && item.isBaseType && item.ethereal - )); + basicSocketables.all + .push(addSocketableObj(sdk.items.ColossusVoulge, [], [], true, (item) => + me.nightmare && item.ilvl >= 26 && item.isBaseType && item.ethereal + )); // Crown of Ages - basicSocketables.caster.push(addSocketableObj(sdk.items.Corona, [sdk.items.runes.Ber, sdk.items.runes.Um], [sdk.items.gems.Perfect.Ruby], - false, (item) => item.unique - )); + basicSocketables.caster + .push(addSocketableObj(sdk.items.Corona, [sdk.items.runes.Ber, sdk.items.runes.Um], [sdk.items.gems.Perfect.Ruby], + false, (item) => item.unique + )); // Moser's - basicSocketables.caster.push(addSocketableObj(sdk.items.RoundShield, [sdk.items.runes.Um], [sdk.items.gems.Perfect.Diamond], - false, (item) => item.unique && !item.ethereal - )); + basicSocketables.caster + .push(addSocketableObj(sdk.items.RoundShield, [sdk.items.runes.Um], [sdk.items.gems.Perfect.Diamond], + false, (item) => item.unique && !item.ethereal + )); // Spirit Forge - basicSocketables.caster.push(addSocketableObj(sdk.items.LinkedMail, [sdk.items.runes.Shael], [sdk.items.gems.Perfect.Ruby], - false, (item) => item.unique && !item.ethereal - )); + basicSocketables.caster + .push(addSocketableObj(sdk.items.LinkedMail, [sdk.items.runes.Shael], [sdk.items.gems.Perfect.Ruby], + false, (item) => item.unique && !item.ethereal + )); // Dijjin Slayer - basicSocketables.caster.push(addSocketableObj(sdk.items.Ataghan, [sdk.items.runes.Amn], [sdk.items.gems.Perfect.Skull], - false, (item) => !Check.currentBuild().caster && item.unique && !item.ethereal - )); + basicSocketables.caster + .push(addSocketableObj(sdk.items.Ataghan, [sdk.items.runes.Amn], [sdk.items.gems.Perfect.Skull], + false, (item) => !Check.currentBuild().caster && item.unique && !item.ethereal + )); // Bone Hew - for merc - basicSocketables.caster.push(addSocketableObj(sdk.items.OgreAxe, [sdk.items.runes.Hel, sdk.items.runes.Amn], [sdk.items.gems.Perfect.Skull], - false, (item) => item.unique - )); + basicSocketables.caster + .push(addSocketableObj(sdk.items.OgreAxe, + [sdk.items.runes.Hel, sdk.items.runes.Amn], [sdk.items.gems.Perfect.Skull], + false, (item) => item.unique + )); // spirit base - basicSocketables.caster.push(addSocketableObj(sdk.items.BroadSword, [], [], true, (item) => - me.normal && !Check.haveBase("sword", 4) && !me.checkItem({ name: sdk.locale.items.Spirit, itemtype: sdk.items.type.Sword }).have - && item.ilvl >= 26 && item.isBaseType && !item.ethereal - )); + basicSocketables.caster + .push(addSocketableObj(sdk.items.BroadSword, [], [], true, + (item) => me.normal && !Check.haveBase("sword", 4) + && !me.checkItem({ name: sdk.locale.items.Spirit, itemtype: sdk.items.type.Sword }).have + && item.ilvl >= 26 && item.isBaseType && !item.ethereal + )); // spirit base - basicSocketables.caster.push(addSocketableObj(sdk.items.CrystalSword, [], [], true, (item) => - me.normal && !Check.haveBase("sword", 4) && !me.checkItem({ name: sdk.locale.items.Spirit, itemtype: sdk.items.type.Sword }).have - && item.ilvl >= 26 && item.ilvl <= 40 && item.isBaseType && !item.ethereal - )); + basicSocketables.caster + .push(addSocketableObj(sdk.items.CrystalSword, [], [], true, + (item) => me.normal && !Check.haveBase("sword", 4) + && !me.checkItem({ name: sdk.locale.items.Spirit, itemtype: sdk.items.type.Sword }).have + && item.ilvl >= 26 && item.ilvl <= 40 && item.isBaseType && !item.ethereal + )); // Lidless - basicSocketables.caster.push(addSocketableObj(sdk.items.GrimShield, [sdk.items.runes.Um], [sdk.items.gems.Perfect.Diamond], !me.hell, (item) => - item.unique && (item.isInStorage || (item.isEquipped && !item.isOnSwap)) && !item.ethereal - )); + basicSocketables.caster + .push(addSocketableObj(sdk.items.GrimShield, + [sdk.items.runes.Um], [sdk.items.gems.Perfect.Diamond], !me.hell, + (item) => item.unique && (item.isInStorage || (item.isEquipped && !item.isOnSwap)) && !item.ethereal + )); const buildAutoBuildTempObj = (update = () => {}) => ({ SkillPoints: [-1], diff --git a/libs/SoloPlay/Utils/Init.js b/libs/SoloPlay/Utils/Init.js index 2a541c95..8132e5ee 100644 --- a/libs/SoloPlay/Utils/Init.js +++ b/libs/SoloPlay/Utils/Init.js @@ -5,7 +5,7 @@ * */ -(function() { +(function () { // Only load this in global scope if (getScript(true).name.toLowerCase() === "libs\\soloplay\\soloplay.js") { myPrint("start setup"); @@ -38,7 +38,7 @@ // check if any of our currently equipped items are no longer usable - can happen after respec me.getItemsEx() .filter(item => item.isEquipped) - .forEach(item => { + .forEach(function (item) { if (me.getStat(sdk.stats.Strength) < item.strreq || me.getStat(sdk.stats.Dexterity) < item.dexreq || item.ethereal && item.isBroken) { @@ -47,6 +47,20 @@ } else if (sdk.quest.items.includes(item.classid)) { myPrint("Removing Quest Item: " + item.fname); Item.removeItem(null, item); + } else if (me.charlvl >= 16 && item.isOnSwap + && [ + sdk.items.type.AmazonBow, sdk.items.type.Bow, + sdk.items.type.Crossbow, sdk.items.type.BowQuiver, sdk.items.type.CrossbowQuiver + ].includes(item.itemType)) { + myPrint("Removing old swap Item: " + item.fname); + try { + me.switchWeapons(sdk.player.slot.Secondary); + item.drop(); + } finally { + me.switchWeapons(sdk.player.slot.Main); + } + // Item.removeItem(null, item); + CharData.skillData.bow.resetBowData(); } }); diff --git a/libs/SoloPlay/Workers/EventEmitter.js b/libs/SoloPlay/Workers/EventEmitter.js index 395b6570..30ab652b 100644 --- a/libs/SoloPlay/Workers/EventEmitter.js +++ b/libs/SoloPlay/Workers/EventEmitter.js @@ -22,7 +22,9 @@ const old = { level: me.charlvl, }; - const gainedLevels = () => me.charlvl - old.level; + const gainedLevels = function () { + return me.charlvl - old.level; + }; let levelTimeout = getTickCount(); @@ -38,8 +40,8 @@ if (levels > 0 && (Config.AutoSkill.Enabled || Config.AutoStat.Enabled)) { scriptBroadcast("toggleQuitlist"); AutoBuild.print("Level up detected (", old.level, "-->", me.charlvl, ")"); - Config.AutoSkill.Enabled && AutoSkill.init(Config.AutoSkill.Build, Config.AutoSkill.Save); - Config.AutoStat.Enabled && AutoStat.init(Config.AutoStat.Build, Config.AutoStat.Save, Config.AutoStat.BlockChance, Config.AutoStat.UseBulk); + AutoSkill.init(Config.AutoSkill.Build, Config.AutoSkill.Save); + AutoStat.init(Config.AutoStat.Build, Config.AutoStat.Save, Config.AutoStat.BlockChance, Config.AutoStat.UseBulk); scriptBroadcast({ event: "level up" }); AutoBuild.applyConfigUpdates(); // scriptBroadcast() won't trigger listener on this thread. diff --git a/libs/SoloPlay/Workers/TownChicken.js b/libs/SoloPlay/Workers/TownChicken.js index 159c56f9..c2c48045 100644 --- a/libs/SoloPlay/Workers/TownChicken.js +++ b/libs/SoloPlay/Workers/TownChicken.js @@ -13,7 +13,7 @@ (function (module, require, Worker) { // Only load this in global scope if (getScript(true).name.toLowerCase() === "libs\\soloplay\\soloplay.js") { - const getNearestMonster = () => { + const getNearestMonster = function () { let gid = null; let monster = Game.getMonster(); let range = 30; @@ -61,7 +61,7 @@ const useTk = me.inTown && Skill.useTK(portal) && i < 3; if (useTk) { portal.distance > 21 && (me.inTown && me.act === 5 ? Town.move("portalspot") : Pather.moveNearUnit(portal, 20)); - if (Skill.cast(sdk.skills.Telekinesis, sdk.skills.hand.Right, portal) + if (Packet.telekinesis(portal) && Misc.poll(() => targetArea ? me.inArea(targetArea) : me.area !== preArea)) { Pather.lastPortalTick = getTickCount(); delay(100); From fcab2780b918037cf7ddba397005cb95101a552d Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Mon, 5 Jun 2023 14:27:07 -0400 Subject: [PATCH 129/263] Rebuild how NTIP handles different list - created a class `NTIPList` so it's easier to handle checking the various lists - NTIP now handles the solowanted items vs non-tier vs regular pickit items better - a lot of refactoring done --- libs/SoloPlay/Functions/CharmEquip.js | 196 +++++--- libs/SoloPlay/Functions/CubingOverrides.js | 85 +++- libs/SoloPlay/Functions/DynamicTiers.js | 12 +- libs/SoloPlay/Functions/Globals.js | 2 +- libs/SoloPlay/Functions/ItemOverrides.js | 69 ++- libs/SoloPlay/Functions/ItemPrototypes.js | 2 +- libs/SoloPlay/Functions/NPCAction.js | 186 +++---- libs/SoloPlay/Functions/NTIPOverrides.js | 468 +++++++++--------- libs/SoloPlay/Functions/PickitOverrides.js | 126 +++-- libs/SoloPlay/Functions/RunewordsOverrides.js | 47 +- libs/SoloPlay/Functions/TownOverrides.js | 177 ++++--- libs/SoloPlay/Utils/Init.js | 3 +- 12 files changed, 819 insertions(+), 554 deletions(-) diff --git a/libs/SoloPlay/Functions/CharmEquip.js b/libs/SoloPlay/Functions/CharmEquip.js index 0b0f55fe..86472a97 100644 --- a/libs/SoloPlay/Functions/CharmEquip.js +++ b/libs/SoloPlay/Functions/CharmEquip.js @@ -20,6 +20,14 @@ const CharmEquip = (function () { * gets read from the finalBuild file so instead of only keeping two it says we should keep 6. */ + /** + * @param {ItemUnit} a + * @param {ItemUnit} b + */ + const sortCharms = function (a, b) { + return NTIP.GetCharmTier(b) - NTIP.GetCharmTier(a); + }; + /** * Iterate over charm checklist, pickit result 0 and 4 get sold * Otherwise if its not in the stash already and not a final charm try and stash it. I don't remember why I checked if it wasn't a final charm @@ -29,14 +37,20 @@ const CharmEquip = (function () { const spliceCharmCheckList = function (checkList = [], verbose = false) { for (let i = 0; i < checkList.length; i++) { const currCharm = checkList[i]; - if (!currCharm || [Pickit.Result.UNWANTED, Pickit.Result.TRASH].includes(Pickit.checkItem(currCharm).result)) continue; + if (!currCharm) continue; + const pResult = NTIP.CheckItem(currCharm, NTIP.SoloList); + if (pResult === Pickit.Result.UNWANTED) { + continue; + } if (!currCharm.isInStash && !me.data.charmGids.includes(currCharm.gid)) { if (!Storage.Stash.MoveTo(currCharm)) { verbose && Item.logger("Dropped", currCharm); currCharm.drop(); } else { if (verbose) { - Cubing.checkItem(currCharm) ? Item.logItem("Stashed Cubing Ingredient", currCharm) : Item.logItem("Stashed", currCharm); + Cubing.checkItem(currCharm) + ? Item.logItem("Stashed Cubing Ingredient", currCharm) + : Item.logItem("Stashed", currCharm); } } } @@ -49,17 +63,21 @@ const CharmEquip = (function () { const spliceCharmKeepList = function (keep = [], sell = [], verbose = false) { if (!keep.length) return; const id = keep[0].classid; - const cInfo = (() => CharData.charms.get(id).count() || { max: 0 })(); + const cInfo = (function () { + return CharData.charms.get(id).count() || { max: 0 }; + })(); // sort through kept charms if (keep.length > cInfo.max) { - keep.sort((a, b) => NTIP.GetCharmTier(b) - NTIP.GetCharmTier(a)); + keep.sort(sortCharms); // everything after the cap (need a better method for this in the instances where the max cap is less then leveling wanted cap) for (let i = cInfo.max; i < keep.length; i++) { if (!!keep[i].classid && !CharmEquip.check(keep[i])) { sell.push(keep[i]); - verbose && console.log("ÿc8Kolbot-SoloPlayÿc0: CharmEquip Add " + keep[i].fname + " to checkList"); + if (verbose) { + console.log("ÿc8Kolbot-SoloPlayÿc0: CharmEquip Add " + keep[i].fname + " to checkList"); + } keep.splice(i, 1); i -= 1; } @@ -73,7 +91,7 @@ const CharmEquip = (function () { */ function CharmTypeEquip (classid) { this.classid = classid; - this.name = (() => { + this.name = (function () { switch (classid) { case sdk.items.SmallCharm: return "Small"; @@ -95,11 +113,14 @@ const CharmEquip = (function () { * @returns {{ keep: ItemUnit[], sell: ItemUnit[] }} */ CharmTypeEquip.prototype.autoEquip = function (charmList = []) { + const _classid = this.classid; let items = (charmList.length ? charmList : me.getItemsEx()) - .filter((charm) => charm.isInStorage && charm.classid === this.classid && charm.magic); + .filter(function (charm) { + return charm.isInStorage && charm.classid === _classid && charm.magic; + }); if (!items.length) { - this.debugging && console.debug("No charms found"); + this.debugging && console.debug("No charms found for " + this.name + "Charm"); return { keep: [], sell: [] }; } @@ -124,18 +145,25 @@ const CharmEquip = (function () { * @param {ItemUnit} item * @returns {boolean} */ - hasCharmTier: (item) => me.expansion && Config.AutoEquip && NTIP.GetCharmTier(item) > 0, + hasCharmTier: function (item) { + return me.expansion && Config.AutoEquip && NTIP.GetCharmTier(item) > 0; + }, /** * @param {ItemUnit} item * @returns {boolean} */ - isFinalCharm: (item) => me.data.charmGids.includes(item.gid), + isFinalCharm: function (item) { + return me.data.charmGids.includes(item.gid); + }, init: function () { // No charms in classic if (me.classic) return; - let myCharms = me.getItemsEx().filter(item => item.isInStorage && item.isCharm && item.magic); + let myCharms = me.getItemsEx() + .filter(function (item) { + return item.isInStorage && item.isCharm && item.magic; + }); let changed = false; const finalCharmKeys = Object.keys(me.data.charms); @@ -151,19 +179,18 @@ const CharmEquip = (function () { } }; - for (let i = 0; i < finalCharmKeys.length; i++) { - let cKey = finalCharmKeys[i]; - switch (me.data.charms[cKey].classid) { + for (let key of finalCharmKeys) { + switch (me.data.charms[key].classid) { case sdk.items.SmallCharm: - check(me.data.charms[cKey].have, myCharms); + check(me.data.charms[key].have, myCharms); break; case sdk.items.LargeCharm: - check(me.data.charms[cKey].have, myCharms); + check(me.data.charms[key].have, myCharms); break; case sdk.items.GrandCharm: - check(me.data.charms[cKey].have, myCharms); + check(me.data.charms[key].have, myCharms); break; } @@ -357,7 +384,7 @@ const CharmEquip = (function () { * @returns {{ skillerTypeA: ItemUnit[], skillerTypeB: ItemUnit[], skillerTypeC: ItemUnit[], resist: ItemUnit[], life: ItemUnit[], magicfind: ItemUnit[], damage: ItemUnit[], elemental: ItemUnit[], backup: ItemUnit[], keep: ItemUnit[], checkList: ItemUnit[] }}} */ sort: function (items = [], verbose = false) { - let charms = { + const charms = { skillerTypeA: [], skillerTypeB: [], skillerTypeC: [], @@ -376,18 +403,30 @@ const CharmEquip = (function () { return charms; } - const addToCheckList = (item) => charms.checkList.indexOf(item) === -1 && charms.checkList.push(item); - const addToBackUp = (item) => charms.backup.indexOf(item) === -1 && charms.backup.push(item); + /** @param {ItemUnit} item */ + const addToCheckList = function (item) { + return charms.checkList.indexOf(item) === -1 && charms.checkList.push(item); + }; + /** @param {ItemUnit} item */ + const addToBackUp = function (item) { + return charms.backup.indexOf(item) === -1 && charms.backup.push(item); + }; - const sortCharms = (arr = [], verbose = false, backUpCheck = true) => { + const iterateList = function (arr = [], verbose = false, backUpCheck = true) { let invoquantity = NTIP.getInvoQuantity(arr[0]); (invoquantity === undefined || invoquantity === -1) && (invoquantity = 2); let charmType = CharmEquip.getCharmType(arr[0]); verbose && console.log("Amount of " + charmType + " Charms: " + arr.length + " invoquantity: " + invoquantity); - arr.length > 1 && arr.sort((a, b) => NTIP.GetCharmTier(b) - NTIP.GetCharmTier(a)); + if (arr.length > 1) { + arr.sort(sortCharms); + } if (arr.length > invoquantity) { - verbose && arr.forEach((el, index) => console.log(charmType + "[" + index + "] = " + NTIP.GetCharmTier(el))); + if (verbose) { + arr.forEach(function (el, index) { + console.log(charmType + "[" + index + "] = " + NTIP.GetCharmTier(el)); + }); + } for (let i = invoquantity; i < arr.length; i++) { backUpCheck ? addToBackUp(arr[i]) : addToCheckList(arr[i]); @@ -399,7 +438,7 @@ const CharmEquip = (function () { }; verbose && console.log("Amount of items: " + items.length); - items.length > 1 && items.sort((a, b) => NTIP.GetCharmTier(b) - NTIP.GetCharmTier(a)); + items.length > 1 && items.sort(sortCharms); const finalCharmInfo = Check.finalBuild().finalCharms; const finalCharmKeys = Object.keys(finalCharmInfo); @@ -438,15 +477,14 @@ const CharmEquip = (function () { let next = false; - for (let i = 0; i < finalCharmKeys.length; i++) { - let cKey = finalCharmKeys[i]; + for (let key of finalCharmKeys) { try { - if (!!me.data.charms[cKey] && me.data.charms[cKey].have.indexOf(item.gid) === -1 - && me.data.charms[cKey].have.length < me.data.charms[cKey].max) { - if (finalCharmInfo[cKey].stats(item)) { + if (!!me.data.charms[key] && me.data.charms[key].have.indexOf(item.gid) === -1 + && me.data.charms[key].have.length < me.data.charms[key].max) { + if (finalCharmInfo[key].stats(item)) { console.debug(item.fname); me.data.charmGids.push(item.gid); - me.data.charms[cKey].have.push(item.gid); + me.data.charms[key].have.push(item.gid); charms.keep.push(item); found = true; next = true; @@ -497,25 +535,39 @@ const CharmEquip = (function () { me.update(); } - if (!charms.skillerTypeA.length && !charms.skillerTypeB.length && !charms.skillerTypeC.length - && !charms.damage.length && !charms.resist.length && !charms.elemental.length && !charms.life.length && !charms.backup.length) { + if (Object.values(charms).every(c => !c.length)) { verbose && console.log("No Charms"); return charms; } - charms.skillerTypeA.length > 0 && sortCharms(charms.skillerTypeA, verbose); - charms.skillerTypeB.length > 0 && sortCharms(charms.skillerTypeB, verbose); - charms.skillerTypeC.length > 0 && sortCharms(charms.skillerTypeC, verbose); - charms.resist.length > 0 && sortCharms(charms.resist, verbose); - charms.life.length > 0 && sortCharms(charms.life, verbose); - charms.magicfind.length > 0 && sortCharms(charms.magicfind, verbose); - charms.damage.length > 0 && sortCharms(charms.damage, verbose); - charms.elemental.length > 0 && sortCharms(charms.elemental, verbose); + charms.skillerTypeA.length > 0 && iterateList(charms.skillerTypeA, verbose); + charms.skillerTypeB.length > 0 && iterateList(charms.skillerTypeB, verbose); + charms.skillerTypeC.length > 0 && iterateList(charms.skillerTypeC, verbose); + charms.resist.length > 0 && iterateList(charms.resist, verbose); + charms.life.length > 0 && iterateList(charms.life, verbose); + charms.magicfind.length > 0 && iterateList(charms.magicfind, verbose); + charms.damage.length > 0 && iterateList(charms.damage, verbose); + charms.elemental.length > 0 && iterateList(charms.elemental, verbose); // If stats are unspecifed, this will filter charms and keep highest based on invoquantity. If no invoquantity defined it will keep two of that type - charms.backup.length > 0 && sortCharms(charms.backup, verbose, false); - charms.keep = charms.keep.concat(charms.skillerTypeA, charms.skillerTypeB, charms.skillerTypeC, charms.resist, charms.life, charms.magicfind, charms.damage, charms.elemental, charms.backup); - verbose && charms.checkList.forEach((el, index) => console.log("checkList[" + index + "] = " + NTIP.GetCharmTier(el) + " " + el.fname)); + charms.backup.length > 0 && iterateList(charms.backup, verbose, false); + charms.keep = charms.keep.concat( + charms.skillerTypeA, + charms.skillerTypeB, + charms.skillerTypeC, + charms.resist, + charms.life, + charms.magicfind, + charms.damage, + charms.elemental, + charms.backup + ); + if (verbose) { + charms.checkList + .forEach(function (el, index) { + console.log("checkList[" + index + "] = " + NTIP.GetCharmTier(el) + " " + el.fname); + }); + } return charms; }, @@ -535,7 +587,12 @@ const CharmEquip = (function () { let lowestCharm; let items = me.getItemsEx() - .filter(charm => charm.classid === item.classid && charm.isInStorage && charm.magic && NTIP.GetCharmTier(charm) > 0); + .filter(function (charm) { + return charm.classid === item.classid + && charm.isInStorage + && charm.magic + && NTIP.GetCharmTier(charm) > 0; + }); if (!items.length) return true; let quantityCap = NTIP.getInvoQuantity(item); @@ -551,7 +608,7 @@ const CharmEquip = (function () { if (cInfo.curr && (cInfo.curr / cInfo.max) * 100 >= 75) { // chop off past our cap newList = charms.keep - .sort((a, b) => NTIP.GetCharmTier(b) - NTIP.GetCharmTier(a)) + .sort(sortCharms) .slice(0, cInfo.max); // check if it made the cut if (!newList.find(i => i.gid === item.gid)) return false; @@ -566,7 +623,7 @@ const CharmEquip = (function () { if (cInfo.curr && (cInfo.curr / cInfo.max) * 100 >= 75) { // chop off past our cap newList = charms.keep - .sort((a, b) => NTIP.GetCharmTier(b) - NTIP.GetCharmTier(a)) + .sort(sortCharms) .slice(0, cInfo.max); // check if it made the cut if (!newList.find(i => i.gid === item.gid)) return false; @@ -581,7 +638,7 @@ const CharmEquip = (function () { if (cInfo.curr && (cInfo.curr / cInfo.max) * 100 >= 50) { // chop off past our cap newList = charms.keep - .sort((a, b) => NTIP.GetCharmTier(b) - NTIP.GetCharmTier(a)) + .sort(sortCharms) .slice(0, cInfo.max); // check if it made the cut if (!newList.find(i => i.gid === item.gid)) return false; @@ -630,7 +687,9 @@ const CharmEquip = (function () { if (tierParamItem === tierLowestItem) { // console.debug("Same tier value"); // super hacky - arbritrary comparsion of xpos if the tier value is the same - return (have < quantityCap) || (item.isInInventory && lowestCharm.isInInventory && item.x > lowestCharm.y) || (item.isInInventory && !lowestCharm.isInInventory); + return (have < quantityCap) + || (item.isInInventory && lowestCharm.isInInventory && item.x > lowestCharm.y) + || (item.isInInventory && !lowestCharm.isInInventory); } return (tierParamItem >= tierLowestItem); @@ -641,9 +700,15 @@ const CharmEquip = (function () { if (me.classic) return; console.log("ÿc8Kolbot-SoloPlayÿc0: Entering charm auto equip"); + const verbose = (Developer.debugging.smallCharm + || Developer.debugging.largeCharm + || Developer.debugging.grandCharm + ); let tick = getTickCount(); let charms = me.getItemsEx() - .filter(item => item.isInStorage && item.isCharm && item.magic); + .filter(function (item) { + return item.isInStorage && item.isCharm && item.magic; + }); // don't do anything if we don't have any charms if ((!charms.length) // don't do anything if we have the same charms as last time @@ -652,12 +717,14 @@ const CharmEquip = (function () { return; } CharmEquip.keptGids.clear(); - let totalKeep = [], totalSell = []; + /** @type {Array} */ + let totalKeep = []; + /** @type {Array} */ + let totalSell = []; let GCs = CharmEquip.grandCharm(charms); let LCs = CharmEquip.largeCharm(charms); let SCs = CharmEquip.smallCharm(charms); let specialCharms = charms.filter((charm) => charm.unique); - let verbose = !!(Developer.debugging.smallCharm || Developer.debugging.largeCharm || Developer.debugging.grandCharm); if (verbose) { console.log("Grand Charms Keep: " + GCs.keep.length + ", Sell: " + GCs.sell.length); @@ -675,7 +742,9 @@ const CharmEquip = (function () { } totalSell = totalSell .concat(SCs.sell, LCs.sell, GCs.sell) - .filter((charm) => NTIP.CheckItem(charm, NTIP_CheckListNoTier) === Pickit.Result.UNWANTED); + .filter(function (charm) { + return NTIP.CheckItem(charm, NTIP.CheckList) === Pickit.Result.UNWANTED; + }); totalKeep.length > 0 && console.log("ÿc8Kolbot-SoloPlayÿc0: Total Charms Kept: " + totalKeep.length); if (totalSell.length > 0) { @@ -692,23 +761,24 @@ const CharmEquip = (function () { Town.initNPC("Shop", "clearInventory"); - if (getUIFlag(sdk.uiflags.Shop) || (Config.PacketShopping && getInteractedNPC() && getInteractedNPC().itemcount > 0)) { - for (let i = 0; i < totalSell.length; i++) { - console.log("ÿc8Kolbot-SoloPlayÿc0: Sell old charm " + totalSell[i].name); - verbose && Item.logger("Sold", totalSell[i]); - verbose && Item.logItem("CharmEquip Sold", totalSell[i]); - totalSell[i].sell(); + if (getUIFlag(sdk.uiflags.Shop) + || (Config.PacketShopping && getInteractedNPC() && getInteractedNPC().itemcount > 0)) { + for (let item of totalSell) { + console.log("ÿc8Kolbot-SoloPlayÿc0: Sell old charm " + item.name); + verbose && Item.logger("Sold", item); + verbose && Item.logItem("CharmEquip Sold", item); + item.sell(); } } } if (totalKeep.length > 0) { - for (let i = 0; i < totalKeep.length; i++) { - CharmEquip.keptGids.add(totalKeep[i].gid); - if (totalKeep[i].isInStash && !Cubing.checkItem(totalKeep[i])) { + for (let item of totalKeep) { + CharmEquip.keptGids.add(item.gid); + if (item.isInStash && !Cubing.checkItem(item)) { !getUIFlag(sdk.uiflags.Stash) && Town.openStash() && delay(300 + me.ping); - if (Storage.Inventory.CanFit(totalKeep[i]) && Storage.Inventory.MoveTo(totalKeep[i])) { - verbose && Item.logItem("CharmEquip Equipped", totalKeep[i]); + if (Storage.Inventory.CanFit(item) && Storage.Inventory.MoveTo(item)) { + verbose && Item.logItem("CharmEquip Equipped", item); } } } diff --git a/libs/SoloPlay/Functions/CubingOverrides.js b/libs/SoloPlay/Functions/CubingOverrides.js index cea34798..1573c575 100644 --- a/libs/SoloPlay/Functions/CubingOverrides.js +++ b/libs/SoloPlay/Functions/CubingOverrides.js @@ -420,6 +420,7 @@ Cubing.buildRecipes = function () { } }; +/** @this Cubing */ Cubing.buildLists = function () { CraftingSystem.checkSubrecipes(); SoloWants.checkSubrecipes(); @@ -482,14 +483,38 @@ Cubing.buildLists = function () { // if the recipe is enabled (we have the main item), add flawless gem recipes (if needed) - if (this.subRecipes.indexOf(sdk.items.gems.Perfect.Amethyst) === -1 && (this.recipes[i].Ingredients[j] === sdk.items.gems.Perfect.Amethyst || (this.recipes[i].Ingredients[j] === "pgem" && this.gemList.indexOf(sdk.items.gems.Perfect.Amethyst) > -1))) { - this.recipes.push({ Ingredients: [sdk.items.gems.Flawless.Amethyst, sdk.items.gems.Flawless.Amethyst, sdk.items.gems.Flawless.Amethyst], Index: Recipe.Gem, AlwaysEnabled: true, MainRecipe: this.recipes[i].Index }); + if (this.subRecipes.indexOf(sdk.items.gems.Perfect.Amethyst) === -1 + && (this.recipes[i].Ingredients[j] === sdk.items.gems.Perfect.Amethyst + || (this.recipes[i].Ingredients[j] === "pgem" + && this.gemList.indexOf(sdk.items.gems.Perfect.Amethyst) > -1))) { + this.recipes.push({ + Ingredients: [ + sdk.items.gems.Flawless.Amethyst, + sdk.items.gems.Flawless.Amethyst, + sdk.items.gems.Flawless.Amethyst + ], + Index: Recipe.Gem, + AlwaysEnabled: true, + MainRecipe: this.recipes[i].Index + }); this.subRecipes.push(sdk.items.gems.Perfect.Amethyst); } // Make flawless amethyst - if (this.subRecipes.indexOf(sdk.items.gems.Flawless.Amethyst) === -1 && (this.recipes[i].Ingredients[j] === sdk.items.gems.Flawless.Amethyst || (this.recipes[i].Ingredients[j] === "fgem" && this.gemList.indexOf(sdk.items.gems.Flawless.Amethyst) > -1))) { - this.recipes.push({ Ingredients: [sdk.items.gems.Normal.Amethyst, sdk.items.gems.Normal.Amethyst, sdk.items.gems.Normal.Amethyst], Index: Recipe.Gem, AlwaysEnabled: true, MainRecipe: this.recipes[i].Index }); + if (this.subRecipes.indexOf(sdk.items.gems.Flawless.Amethyst) === -1 + && (this.recipes[i].Ingredients[j] === sdk.items.gems.Flawless.Amethyst + || (this.recipes[i].Ingredients[j] === "fgem" + && this.gemList.indexOf(sdk.items.gems.Flawless.Amethyst) > -1))) { + this.recipes.push({ + Ingredients: [ + sdk.items.gems.Normal.Amethyst, + sdk.items.gems.Normal.Amethyst, + sdk.items.gems.Normal.Amethyst + ], + Index: Recipe.Gem, + AlwaysEnabled: true, + MainRecipe: this.recipes[i].Index + }); this.subRecipes.push(sdk.items.gems.Flawless.Amethyst); } @@ -602,15 +627,23 @@ Cubing.checkItem = function (unit) { && unit.classid === this.validIngredients[i].classid && unit.quality === this.validIngredients[i].quality) { // item is better than the one we currently have, so add it to validIngredient array and remove old item if (unit.ilvl > this.validIngredients[i].ilvl && this.validItem(unit, this.validIngredients[i].recipe)) { - this.validIngredients.push({ classid: unit.classid, quality: unit.quality, ilvl: unit.ilvl, gid: unit.gid, recipe: this.validIngredients[i].recipe }); + this.validIngredients.push({ + classid: unit.classid, + quality: unit.quality, + ilvl: unit.ilvl, + gid: unit.gid, + recipe: this.validIngredients[i].recipe + }); this.validIngredients.splice(i, 1); return true; } } // its an item meant for socketing so lets be sure we have the best base - if (this.validIngredients[i].recipe.Index >= Recipe.Socket.Shield && this.validIngredients[i].recipe.Index <= Recipe.Socket.Helm) { + if (this.validIngredients[i].recipe.Index >= Recipe.Socket.Shield + && this.validIngredients[i].recipe.Index <= Recipe.Socket.Helm) { // not the same item but the same type of item - if (!unit.isEquipped && unit.gid !== this.validIngredients[i].gid && unit.itemType === this.validIngredients[i].type + if (!unit.isEquipped && unit.gid !== this.validIngredients[i].gid + && unit.itemType === this.validIngredients[i].type && unit.quality === this.validIngredients[i].quality) { // console.debug(this.validIngredients[i], "\n//~~~~//\n", unit, "\n//~~~~~/\n", Item.betterThanStashed(unit, true)); // item is better than the one we currently have, so add it to validIngredient array and remove old item @@ -634,8 +667,8 @@ Cubing.checkItem = function (unit) { return true; } - for (let i = 0; i < this.neededIngredients.length; i++) { - if (unit.classid === this.neededIngredients[i].classid && this.validItem(unit, this.neededIngredients[i].recipe)) { + for (let el of this.neededIngredients) { + if (unit.classid === el.classid && this.validItem(unit, el.recipe)) { return true; } } @@ -652,10 +685,13 @@ Cubing.validItem = function (unit, recipe) { // Don't use items in locked inventory space if (unit.isInInventory && Storage.Inventory.IsLocked(unit, Config.Inventory)) return false; // Don't use items that are wanted by other systems - if (Runewords.validGids.includes(unit.gid) || CraftingSystem.validGids.includes(unit.gid)) return false; + if (Runewords.validGids.includes(unit.gid) || CraftingSystem.validGids.includes(unit.gid)) { + return false; + } // Gems and runes - if ((unit.itemType >= sdk.items.type.Amethyst && unit.itemType <= sdk.items.type.Skull) || unit.itemType === sdk.items.type.Rune) { + if ((unit.itemType >= sdk.items.type.Amethyst + && unit.itemType <= sdk.items.type.Skull) || unit.itemType === sdk.items.type.Rune) { if (!recipe.Enabled && recipe.Ingredients[0] !== unit.classid && recipe.Ingredients[1] !== unit.classid) { return false; } @@ -669,7 +705,7 @@ Cubing.validItem = function (unit, recipe) { // START let valid = true; const ntipResult = NTIP.CheckItem(unit); - const ntipNoTierResult = NTIP.CheckItem(unit, NTIP_CheckListNoTier); + const ntipNoTierResult = NTIP.CheckItem(unit, NTIP.CheckList); if (recipe.Index >= Recipe.HitPower.Helm && recipe.Index <= Recipe.Safety.Weapon) { if (Math.floor(me.charlvl / 2) + Math.floor(unit.ilvl / 2) < recipe.Level) { @@ -689,7 +725,7 @@ Cubing.validItem = function (unit, recipe) { if (recipe.Enabled && ntipResult === Pickit.Result.UNWANTED) return true; // Main item, NOT matching a pickit entry } else if (unit.magic && Math.floor(me.charlvl / 2) + Math.floor(unit.ilvl / 2) >= recipe.Level - && NTIP.CheckItem(unit, NTIP_CheckListNoTier) === Pickit.Result.UNWANTED) { + && ntipNoTierResult === Pickit.Result.UNWANTED) { return true; } @@ -697,7 +733,9 @@ Cubing.validItem = function (unit, recipe) { } else if (recipe.Index >= Recipe.Unique.Weapon.ToExceptional && recipe.Index <= Recipe.Unique.Armor.ToElite) { // If item is equipped, ensure we can use the upgraded version if (unit.isEquipped) { - if (me.charlvl < unit.upgradedLvlReq || me.trueStr < unit.upgradedStrReq || me.trueDex < unit.upgradedDexReq) { + if (me.charlvl < unit.upgradedLvlReq + || me.trueStr < unit.upgradedStrReq + || me.trueDex < unit.upgradedDexReq) { return false; } } @@ -709,11 +747,12 @@ Cubing.validItem = function (unit, recipe) { if (valid) { // check to see if we are using this already and if so compare base stats to see if this one would be better // ignore things that get re-rolled like defense or min/max dmg just focus on base stats like enhanced defense/damage - let equipped = me.getItemsEx(-1, sdk.storage.Equipped).find(item => item.fname.toLowerCase().includes(recipe.Name.toLowerCase())); + let equipped = me.getItemsEx(-1, sdk.storage.Equipped) + .find(item => item.fname.toLowerCase().includes(recipe.Name.toLowerCase())); if (equipped) { switch (recipe.Name.toLowerCase()) { case "magefist": - // compare enhanced defense - keep "equal to" because base defense gets re-rolled so it might turn out better + // compare enhanced defense - keep "equal to" because base defense gets re-rolled so it might turn out better valid = (unit.getStat(sdk.stats.ArmorPercent) >= equipped.getStat(sdk.stats.ArmorPercent)); break; } @@ -735,7 +774,9 @@ Cubing.validItem = function (unit, recipe) { } else if (recipe.Index >= Recipe.Rare.Weapon.ToExceptional && recipe.Index <= Recipe.Rare.Armor.ToElite) { // If item is equipped, ensure we can use the upgraded version if (unit.isEquipped) { - if (me.charlvl < unit.upgradedLvlReq || me.trueStr < unit.upgradedStrReq || me.trueDex < unit.upgradedDexReq) { + if (me.charlvl < unit.upgradedLvlReq + || me.trueStr < unit.upgradedStrReq + || me.trueDex < unit.upgradedDexReq) { return false; } } @@ -757,7 +798,11 @@ Cubing.validItem = function (unit, recipe) { // Normal item matching pickit entry, no sockets if (unit.normal && unit.sockets === 0) { if (Pickit.Result.WANTED === ntipResult - && [sdk.items.type.Wand, sdk.items.type.VoodooHeads, sdk.items.type.AuricShields, sdk.items.type.PrimalHelm, sdk.items.type.Pelt].includes(unit.itemType)) { + && [ + sdk.items.type.Wand, sdk.items.type.VoodooHeads, + sdk.items.type.AuricShields, sdk.items.type.PrimalHelm, + sdk.items.type.Pelt + ].includes(unit.itemType)) { if (!Item.betterThanStashed(unit) || !Item.betterBaseThanWearing(unit)) return false; } switch (recipe.Ethereal) { @@ -782,7 +827,9 @@ Cubing.validItem = function (unit, recipe) { return false; } else if (recipe.Index === Recipe.Reroll.Charm) { - if (unit.isCharm && unit.magic && (ntipResult === Pickit.Result.UNWANTED || (!CharmEquip.check(unit) && ntipNoTierResult === Pickit.Result.UNWANTED))) { + if (unit.isCharm && unit.magic + && (ntipResult === Pickit.Result.UNWANTED + || (!CharmEquip.check(unit) && ntipNoTierResult === Pickit.Result.UNWANTED))) { switch (unit.itemType) { case sdk.items.type.SmallCharm: if (unit.ilvl >= recipe.Level[unit.code].ilvl) { diff --git a/libs/SoloPlay/Functions/DynamicTiers.js b/libs/SoloPlay/Functions/DynamicTiers.js index c7545f79..fd117ff6 100644 --- a/libs/SoloPlay/Functions/DynamicTiers.js +++ b/libs/SoloPlay/Functions/DynamicTiers.js @@ -355,7 +355,7 @@ // const eqItem = me.equipped.get(bodyloc); const eqItem = me.getEquippedItem(bodyloc); - const generalScore = () => { + const generalScore = function () { let generalRating = 0; // get item cbf stat from olditem equipped on body location @@ -391,7 +391,7 @@ ].reduce((acc, stat) => acc + item.getStatEx(stat) * _tierWeights.gen.get(stat), generalRating); }; - const resistScore = () => { + const resistScore = function () { let resistRating = 0; // get new item stats @@ -449,7 +449,7 @@ ].reduce((acc, stat) => acc + item.getStatEx(stat) * _tierWeights.res.get(stat), resistRating)); }; - const buildScore = () => { + const buildScore = function () { // dirty fix maybe? if (me.barbarian && SetUp.currentBuild !== "Immortalwhirl" && item.strictlyTwoHanded) { return 0; @@ -480,7 +480,7 @@ return 0; }; - const skillsScore = () => { + const skillsScore = function () { let skillsRating = [ [sdk.stats.AllSkills, -1], [sdk.stats.AddClassSkills, me.classid], [sdk.stats.AddSkillTab, buildInfo.tabSkills], ].reduce((acc, [stat, subId]) => acc + item.getStatEx(stat, subId) * _tierWeights.skill.get(stat), 0); @@ -504,7 +504,7 @@ return skillsRating; }; - const ctcScore = () => { + const ctcScore = function () { // chance to cast doesn't exist in classic if (me.classic) return 0; @@ -564,7 +564,7 @@ tier += ctcScore(); tier += chargeditemscore(item, -1, buildInfo); - if (tier > 1 && tier < NTIP.MAX_TIER && NTIP.CheckItem(item, NTIP.FinalGear.list) === Pickit.Result.WANTED) { + if (tier > 1 && tier < NTIP.MAX_TIER && NTIP.CheckItem(item, NTIP.FinalGear) === Pickit.Result.WANTED) { // console.debug(item.prettyPrint + "~~~" + tier); tier += NTIP.MAX_TIER; } diff --git a/libs/SoloPlay/Functions/Globals.js b/libs/SoloPlay/Functions/Globals.js index 8560954c..3862f86e 100644 --- a/libs/SoloPlay/Functions/Globals.js +++ b/libs/SoloPlay/Functions/Globals.js @@ -364,7 +364,7 @@ const SetUp = { }, bowQuiver: function () { - NTIP.resetRuntimeList(); + NTIP.Runtime.clear(); if (CharData.skillData.bow.onSwitch) { if ([sdk.items.type.Bow, sdk.items.type.AmazonBow].includes(CharData.skillData.bow.bowType)) { NTIP.addToRuntime("[type] == bowquiver # # [maxquantity] == 1"); diff --git a/libs/SoloPlay/Functions/ItemOverrides.js b/libs/SoloPlay/Functions/ItemOverrides.js index 2a2120c7..7e09e059 100644 --- a/libs/SoloPlay/Functions/ItemOverrides.js +++ b/libs/SoloPlay/Functions/ItemOverrides.js @@ -10,13 +10,20 @@ includeIfNotIncluded("core/Item.js"); includeIfNotIncluded("SoloPlay/Functions/ItemPrototypes.js"); Item.weaponTypes = [ - sdk.items.type.Scepter, sdk.items.type.Wand, sdk.items.type.Staff, sdk.items.type.Bow, sdk.items.type.Axe, sdk.items.type.Club, - sdk.items.type.Sword, sdk.items.type.Hammer, sdk.items.type.Knife, sdk.items.type.Spear, sdk.items.type.Polearm, sdk.items.type.Crossbow, - sdk.items.type.Mace, sdk.items.type.ThrowingKnife, sdk.items.type.ThrowingAxe, sdk.items.type.Javelin, sdk.items.type.Orb, sdk.items.type.AmazonBow, + sdk.items.type.Scepter, sdk.items.type.Wand, + sdk.items.type.Staff, sdk.items.type.Bow, + sdk.items.type.Axe, sdk.items.type.Club, + sdk.items.type.Sword, sdk.items.type.Hammer, + sdk.items.type.Knife, sdk.items.type.Spear, + sdk.items.type.Polearm, sdk.items.type.Crossbow, + sdk.items.type.Mace, sdk.items.type.ThrowingKnife, + sdk.items.type.ThrowingAxe, sdk.items.type.Javelin, + sdk.items.type.Orb, sdk.items.type.AmazonBow, sdk.items.type.AmazonSpear, sdk.items.type.AmazonJavelin, sdk.items.type.MissilePotion ]; Item.shieldTypes = [ - sdk.items.type.Shield, sdk.items.type.AuricShields, sdk.items.type.VoodooHeads, sdk.items.type.BowQuiver, sdk.items.type.CrossbowQuiver + sdk.items.type.Shield, sdk.items.type.AuricShields, + sdk.items.type.VoodooHeads, sdk.items.type.BowQuiver, sdk.items.type.CrossbowQuiver ]; Item.helmTypes = [ sdk.items.type.Helm, sdk.items.type.PrimalHelm, sdk.items.type.Circlet, sdk.items.type.Pelt @@ -30,14 +37,14 @@ Item.getQuantityOwned = function (item, skipSameItem = false) { if (!item) return 0; return me.getItemsEx() - .filter(check => - check.itemType === item.itemType - && (!skipSameItem || check.gid !== item.gid) - && check.classid === item.classid - && check.quality === item.quality - && check.sockets === item.sockets - && check.isInStorage - ).length; + .filter(function (check) { + return check.itemType === item.itemType + && (!skipSameItem || check.gid !== item.gid) + && check.classid === item.classid + && check.quality === item.quality + && check.sockets === item.sockets + && check.isInStorage; + }).length; }; /** @@ -141,7 +148,10 @@ Item.autoEquipCheck = function (item, basicCheck = false) { } if (!me.barbarian && loc === sdk.body.LeftArm && me.equipped.get(loc).tier === -1) { - if (me.equipped.get(sdk.body.RightArm).twoHanded && tier < me.equipped.get(sdk.body.RightArm).tier) return false; + if (me.equipped.get(sdk.body.RightArm).twoHanded + && tier < me.equipped.get(sdk.body.RightArm).tier) { + return false; + } } return true; @@ -150,7 +160,7 @@ Item.autoEquipCheck = function (item, basicCheck = false) { * @param {ItemUnit} item * @returns {boolean} */ - const checkForBetterItem = (item) => { + const checkForBetterItem = function (item) { let betterItem = me.getItemsEx() .filter(el => el.isInStorage && el.gid !== item.gid && el.identified && Item.getBodyLoc(el).includes(loc)) .sort((a, b) => NTIP.GetTier(b) - NTIP.GetTier(a)) @@ -205,7 +215,7 @@ Item.autoEquip = function (task = "") { me.switchWeapons(sdk.player.slot.Main); - const sortEq = (a, b) => { + const sortEq = function (a, b) { let [prioA, prioB] = [Item.canEquip(a), Item.canEquip(b)]; if (prioA && prioB) return NTIP.GetTier(b) - NTIP.GetTier(a); if (prioA) return -1; @@ -218,12 +228,12 @@ Item.autoEquip = function (task = "") { * @param {number} bodyLoc * @param {number} tier */ - const runEquip = (item, bodyLoc, tier) => { + const runEquip = function (item, bodyLoc, tier) { let gid = item.gid; let prettyName = item.prettyPrint; console.debug(prettyName + " tier: " + tier); - if (this.equip(item, bodyLoc)) { + if (Item.equip(item, bodyLoc)) { console.log( "ÿc9" + task + "ÿc0 :: \n" + "ÿc9 - Equippedÿc0: " + prettyName + "\n" @@ -294,6 +304,8 @@ Item.autoEquip = function (task = "") { if (!runEquip(item, loc, tier)) { continue; } + + break; } } else { if (tier > equippedItem.tier) { @@ -307,7 +319,9 @@ Item.autoEquip = function (task = "") { console.log("ÿc9" + task + "ÿc0 :: TwoHandedWep better than sum tier of currently equipped main + shield hand : " + item.fname + " Tier: " + tier); } - if (!me.barbarian && loc === sdk.body.LeftArm && equippedItem.tier === -1 && me.equipped.get(sdk.body.RightArm).twoHanded) { + if (!me.barbarian && loc === sdk.body.LeftArm + && equippedItem.tier === -1 + && me.equipped.get(sdk.body.RightArm).twoHanded) { if (tier < me.equipped.get(sdk.body.RightArm).tier) { continue; } @@ -385,7 +399,10 @@ Item.equip = function (item, bodyLoc) { break; default: checkScore = NTIP.GetTier(cursorItem); - if (checkScore > justEquipped.tier && !item.questItem && !justEquipped.isRuneword/*Wierd bug with runewords that it'll fail to get correct item desc so don't attempt rollback*/) { + if (checkScore > justEquipped.tier + && !item.questItem + /*Wierd bug with runewords that it'll fail to get correct item desc so don't attempt rollback*/ + && !justEquipped.isRuneword) { console.debug("ROLLING BACK TO OLD ITEM BECAUSE IT WAS BETTER"); console.debug("OldItem: " + checkScore + " Just Equipped Item: " + me.equipped.get(bodyLoc).tier); clickItemAndWait(sdk.clicktypes.click.item.Left, bodyLoc); @@ -425,7 +442,8 @@ Item.removeItem = function (bodyLoc = -1, item = undefined) { if (cursorItem) { // only keep wanted items - if ([Pickit.Result.WANTED, Pickit.Result.SOLOWANTS].includes(Pickit.checkItem(cursorItem).result) || AutoEquip.wanted(cursorItem)) { + if ([Pickit.Result.WANTED, Pickit.Result.SOLOWANTS].includes(Pickit.checkItem(cursorItem).result) + || AutoEquip.wanted(cursorItem)) { if (Storage.Inventory.CanFit(cursorItem)) { Storage.Inventory.MoveTo(cursorItem); } else if (Storage.Stash.CanFit(cursorItem)) { @@ -491,7 +509,9 @@ Item.secondaryEquip = function (item, bodyLoc) { if (item.bodylocation === bodyLoc - 7) { equipped = true; [sdk.items.Arrows, sdk.items.Bolts].includes(item.classid) && CharData.skillData.bow.setArrowInfo(item); - [sdk.items.type.Bow, sdk.items.type.AmazonBow, sdk.items.type.Crossbow].includes(item.itemType) && CharData.skillData.bow.setBowInfo(item); + if ([sdk.items.type.Bow, sdk.items.type.AmazonBow, sdk.items.type.Crossbow].includes(item.itemType)) { + CharData.skillData.bow.setBowInfo(item); + } if (getCursorType() === 3) { let cursorItem = Game.getCursorUnit(); @@ -507,8 +527,7 @@ Item.secondaryEquip = function (item, bodyLoc) { } } } finally { - // Switch back to primary - me.weaponswitch !== 0 && me.switchWeapons(0); + me.switchToPrimary(); } return equipped; @@ -555,7 +574,7 @@ Item.autoEquipSecondary = function (task = "") { if (!items) return false; - const sortEq = (a, b) => { + const sortEq = function (a, b) { if (Item.canEquip(a)) return -1; if (Item.canEquip(b)) return 1; return 0; @@ -876,7 +895,7 @@ Item.logItem = function (action, unit, keptLine, force) { const mercCheck = action.match("Merc"); const hasTier = AutoEquip.hasTier(unit); const charmCheck = (unit.isCharm && CharmEquip.check(unit)); - const nTResult = NTIP.CheckItem(unit, NTIP_CheckListNoTier) === 1; + const nTResult = NTIP.CheckItem(unit, NTIP.CheckList) === 1; if (!action.match("kept", "i") && !action.match("Shopped") && hasTier) { if (!mercCheck) { diff --git a/libs/SoloPlay/Functions/ItemPrototypes.js b/libs/SoloPlay/Functions/ItemPrototypes.js index 5b8bdc81..58e5de16 100644 --- a/libs/SoloPlay/Functions/ItemPrototypes.js +++ b/libs/SoloPlay/Functions/ItemPrototypes.js @@ -226,7 +226,7 @@ Unit.prototype.shouldKeep = function () { // or keep if item is worth selling || (this.getItemCost(sdk.items.cost.ToSell) / (this.sizex * this.sizey) >= (!me.inTown && me.charlvl < 12 ? 5 : me.normal ? 50 : me.nightmare ? 500 : 1000))) { if ((Storage.Inventory.CanFit(this) && Storage.Inventory.MoveTo(this))) { - !AutoEquip.wanted(this) && NTIP.CheckItem(this, NTIP_CheckListNoTier) === Pickit.Result.UNWANTED && Town.sell.push(this); + !AutoEquip.wanted(this) && NTIP.CheckItem(this, NTIP.CheckList) === Pickit.Result.UNWANTED && Town.sell.push(this); return true; } } diff --git a/libs/SoloPlay/Functions/NPCAction.js b/libs/SoloPlay/Functions/NPCAction.js index 7d85a6c7..6b854567 100644 --- a/libs/SoloPlay/Functions/NPCAction.js +++ b/libs/SoloPlay/Functions/NPCAction.js @@ -15,20 +15,22 @@ */ NPCAction.shopAt = function (npcName) { if (!me.inTown) return false; - - // check for invaid npcs to shop at - if ([NPC.Kashya, NPC.Warriv, NPC.Meshif, NPC.Atma, NPC.Greiz, NPC.Tyrael, NPC.Qual_Kehk, NPC.Cain].includes(npcName.toLowerCase())) { - console.warn(npcName + " is an invalid npc to shop at"); - - return false; - } - // keep track of where we start from const origAct = me.act; const npcAct = NPC.getAct(npcName); if (!npcAct.length) return false; try { + // check for invaid npcs to shop at + if ([ + NPC.Kashya, NPC.Warriv, + NPC.Meshif, NPC.Atma, + NPC.Greiz, NPC.Tyrael, + NPC.Qual_Kehk, NPC.Cain + ].includes(npcName.toLowerCase())) { + throw new Error(npcName + " is an invalid npc to shop at"); + } + if (!npcAct.includes(origAct)) { Town.goToTown(npcAct[0]); } @@ -82,7 +84,9 @@ (Config.HPBuffer > 0 || Config.MPBuffer > 0) && getNeededBuffer(); // Check if we need to buy potions based on Config.MinColumn - if (Config.BeltColumn.some((c, i) => ["hp", "mp"].includes(c) && col[i] > (beltSize - Math.min(Config.MinColumn[i], beltSize)))) { + if (Config.BeltColumn.some(function (c, i) { + return ["hp", "mp"].includes(c) && col[i] > (beltSize - Math.min(Config.MinColumn[i], beltSize)); + })) { needPots = true; } @@ -122,7 +126,7 @@ // special check, sometimes our rejuv slot is empty but we do still need buffer. Check if we can buy something to slot there if (specialCheck && Config.BeltColumn.some((c, i) => c === "rv" && col[i] >= beltSize)) { let pots = [sdk.items.ThawingPotion, sdk.items.AntidotePotion, sdk.items.StaminaPotion]; - Config.BeltColumn.forEach((c, i) => { + Config.BeltColumn.forEach(function (c, i) { if (c === "rv" && col[i] >= beltSize && pots.length) { let usePot = pots[0]; let pot = npc.getItem(usePot); @@ -163,7 +167,7 @@ // re-check !needBuffer && (Config.HPBuffer > 0 || Config.MPBuffer > 0) && getNeededBuffer(); - const buyHPBuffers = () => { + const buyHPBuffers = function () { if (needBuffer && buffer.hp < Config.HPBuffer) { for (let i = 0; i < Config.HPBuffer - buffer.hp; i += 1) { let pot = Town.getPotion(npc, "hp", wantedHpPot); @@ -172,7 +176,7 @@ } return true; }; - const buyMPBuffers = () => { + const buyMPBuffers = function () { if (needBuffer && buffer.mp < Config.MPBuffer) { for (let i = 0; i < Config.MPBuffer - buffer.mp; i += 1) { let pot = Town.getPotion(npc, "mp", wantedMpPot); @@ -182,7 +186,9 @@ return true; }; // priortize mana pots if caster - Check.currentBuild().caster ? buyMPBuffers() && buyHPBuffers() : buyHPBuffers() && buyMPBuffers(); + Check.currentBuild().caster + ? buyMPBuffers() && buyHPBuffers() + : buyHPBuffers() && buyMPBuffers(); // keep cold/pois res high with potions if (me.gold > 50000 && npc.getItem(sdk.items.ThawingPotion)) { @@ -199,7 +205,9 @@ * @returns {boolean} */ NPCAction.fillTome = function (classid, force = false) { - const scrollId = (classid === sdk.items.TomeofTownPortal ? sdk.items.ScrollofTownPortal : sdk.items.ScrollofIdentify); + const scrollId = (classid === sdk.items.TomeofTownPortal + ? sdk.items.ScrollofTownPortal + : sdk.items.ScrollofIdentify); const have = Town.checkScrolls(classid, force); let myTome = me.getTome(classid); let invoScrolls = 0; @@ -271,7 +279,10 @@ }; NPCAction.cainID = function (force = false) { - if ((!Config.CainID.Enable && !force) || !Misc.checkQuest(sdk.quest.id.TheSearchForCain, sdk.quest.states.Completed)) return false; + if ((!Config.CainID.Enable && !force) + || !Misc.checkQuest(sdk.quest.id.TheSearchForCain, sdk.quest.states.Completed)) { + return false; + } let npc = getInteractedNPC(); @@ -283,55 +294,54 @@ me.cancel(); let unids = me.getUnids(); + if (!unids.length) return true; - if (unids.length) { - // Check if we may use Cain - number of unid items - if (unids.length < Config.CainID.MinUnids && !force) return false; - - let cain = Town.initNPC("CainID", "cainID"); - if (!cain) return false; - - me.cancelUIFlags(); - - while (unids.length) { - const item = unids.shift(); - const { result, line } = Pickit.checkItem(item); - - switch (result) { - case Pickit.Result.TRASH: - Town.sell.push(item); - - break; - case Pickit.Result.WANTED: - case Pickit.Result.SOLOWANTS: - Item.logger("Kept", item); - Item.logItem("Kept", item, line); - - break; - case Pickit.Result.CUBING: - Item.logger("Kept", item, "Cubing-Town"); - Cubing.update(); - - break; - case Pickit.Result.RUNEWORD: - Item.logger("Kept", item, "Runewords-Town"); - Runewords.update(item.classid, item.gid); - - break; - case Pickit.Result.CRAFTING: - Item.logger("Kept", item, "CraftSys-Town"); - CraftingSystem.update(item); - - break; - case Pickit.Result.SOLOSYSTEM: - Item.logger("Kept", item, "SoloWants-Town"); - SoloWants.update(item); - - break; - case Pickit.Result.UNID: - default: - break; - } + // Check if we may use Cain - number of unid items + if (unids.length < Config.CainID.MinUnids && !force) return false; + + let cain = Town.initNPC("CainID", "cainID"); + if (!cain) return false; + + me.cancelUIFlags(); + + while (unids.length) { + const item = unids.shift(); + const { result, line } = Pickit.checkItem(item); + + switch (result) { + case Pickit.Result.TRASH: + Town.sell.push(item); + + break; + case Pickit.Result.WANTED: + case Pickit.Result.SOLOWANTS: + Item.logger("Kept", item); + Item.logItem("Kept", item, line); + + break; + case Pickit.Result.CUBING: + Item.logger("Kept", item, "Cubing-Town"); + Cubing.update(); + + break; + case Pickit.Result.RUNEWORD: + Item.logger("Kept", item, "Runewords-Town"); + Runewords.update(item.classid, item.gid); + + break; + case Pickit.Result.CRAFTING: + Item.logger("Kept", item, "CraftSys-Town"); + CraftingSystem.update(item); + + break; + case Pickit.Result.SOLOSYSTEM: + Item.logger("Kept", item, "SoloWants-Town"); + SoloWants.update(item); + + break; + case Pickit.Result.UNID: + default: + break; } } @@ -370,13 +380,16 @@ } } - if (getTickCount() - Town.lastShopped.tick < Time.seconds(3) && Town.lastShopped.who === npc.name) return false; + if (getTickCount() - Town.lastShopped.tick < Time.seconds(3) + && Town.lastShopped.who === npc.name) { + return false; + } let items = npc.getItemsEx() .filter((item) => !Town.ignoreType(item.itemType) && (itemTypes.length === 0 || itemTypes.includes(item.itemType)) && (NTIP.CheckItem(item) !== Pickit.Result.UNWANTED) && (startingGold - item.getItemCost(sdk.items.cost.ToBuy) > goldLimit)) - .sort((a, b) => { + .sort(function (a, b) { let priorityA = itemTypes.includes(a.itemType); let priorityB = itemTypes.includes(b.itemType); if (priorityA && priorityB) return NTIP.GetTier(b) - NTIP.GetTier(a); @@ -400,19 +413,21 @@ * @param {{ result: PickitResult, line: string }} result * @param {number | string} tierInfo */ - const shopReport = (item, action, result, tierInfo) => { + const shopReport = function (item, action, result, tierInfo) { action === undefined && (action = ""); tierInfo === undefined && (tierInfo = ""); console.log("ÿc8Kolbot-SoloPlayÿc0: " + action + (tierInfo ? " " + tierInfo : "")); Item.logger(action, item); - Developer.debugging.autoEquip && Item.logItem("Shopped " + action, item, result.line !== undefined ? result.line : "null"); + if (Developer.debugging.autoEquip) { + Item.logItem("Shopped " + action, item, result.line !== undefined ? result.line : "null"); + } }; /** * Buy dependancy item if it's needed * @param {ItemUnit} item */ - const checkDependancy = (item) => { + const checkDependancy = function (item) { let check = Item.hasDependancy(item); if (check) { let el = npc.getItem(check); @@ -420,15 +435,15 @@ } }; - for (let i = 0; i < items.length; i++) { - const item = items[i]; + for (let item of items) { const myGold = me.gold; const itemCost = item.getItemCost(sdk.items.cost.ToBuy); if (myGold < itemCost) continue; const { result, line } = Pickit.checkItem(item); // no tier'ed items - if (!lowLevelShop && result === Pickit.Result.SOLOWANTS && NTIP.CheckItem(item, NTIP.SoloCheckListNoTier, true).result !== Pickit.Result.UNWANTED) { + if (!lowLevelShop && result === Pickit.Result.SOLOWANTS + && NTIP.CheckItem(item, NTIP.NoTier, true).result !== Pickit.Result.UNWANTED) { try { if (Storage.Inventory.CanFit(item) && myGold >= itemCost && (myGold - itemCost > goldLimit)) { if (item.isBaseType) { @@ -451,7 +466,9 @@ let [mainTier] = [NTIP.GetTier(item)]; // we want this to be at least a 5% increase in the tier value - if (Item.hasTier(item) && Item.autoEquipCheck(item) && ((Item.getEquippedItem(item.bodyLocation().first()).tier - mainTier) / (mainTier * 100)) > 5) { + if (Item.hasTier(item) + && Item.autoEquipCheck(item) + && ((Item.getEquippedItem(item.bodyLocation().first()).tier - mainTier) / (mainTier * 100)) > 5) { shopReport(item, "AutoEquip", line, (item.prettyPrint + " Tier: " + NTIP.GetTier(item))); item.buy() && bought++; Item.autoEquip("InShop"); @@ -476,7 +493,7 @@ items = npc.getItemsEx() .filter((item) => !Town.ignoreType(item.itemType) && NTIP.GetMercTier(item) > 0) .sort((a, b) => NTIP.GetMercTier(b) - NTIP.GetMercTier(a)) - .forEach(item => { + .forEach(function (item) { const myGold = me.gold; const itemCost = item.getItemCost(sdk.items.cost.ToBuy); if (myGold < itemCost) return; @@ -514,15 +531,15 @@ if (Town.gambleIds.size === 0) { // change text to classid - for (let i = 0; i < Config.GambleItems.length; i += 1) { - if (isNaN(Config.GambleItems[i])) { - if (NTIPAliasClassID.hasOwnProperty(Config.GambleItems[i].replace(/\s+/g, "").toLowerCase())) { - Town.gambleIds.add(NTIPAliasClassID[Config.GambleItems[i].replace(/\s+/g, "").toLowerCase()]); + for (let item of Config.GambleItems) { + if (isNaN(item)) { + if (NTIPAliasClassID.hasOwnProperty(item.replace(/\s+/g, "").toLowerCase())) { + Town.gambleIds.add(NTIPAliasClassID[item.replace(/\s+/g, "").toLowerCase()]); } else { - Misc.errorReport("ÿc1Invalid gamble entry:ÿc0 " + Config.GambleItems[i]); + Misc.errorReport("ÿc1Invalid gamble entry:ÿc0 " + item); } } else { - Town.gambleIds.add(Config.GambleItems[i]); + Town.gambleIds.add(item); } } } @@ -552,10 +569,10 @@ Town.gambleIds.has(item.classid) && items.push(copyUnit(item)); } while (item.getNext()); - for (let i = 0; i < items.length; i += 1) { - if (!Storage.Inventory.CanFit(items[i])) return false; + for (let item of items) { + if (!Storage.Inventory.CanFit(item)) return false; - items[i].buy(false, true); + item.buy(false, true); let newItem = Town.getGambledItem(list); @@ -617,7 +634,10 @@ break; case "buyQuiver": let bowCheck = me.getItemsEx() - .filter(el => el.isEquipped && [sdk.items.type.Bow, sdk.items.type.Crossbow, sdk.items.type.AmazonBow].includes(el.itemType)) + .filter(function (el) { + return el.isEquipped + && [sdk.items.type.Bow, sdk.items.type.Crossbow, sdk.items.type.AmazonBow].includes(el.itemType); + }) .first(); if (bowCheck) { diff --git a/libs/SoloPlay/Functions/NTIPOverrides.js b/libs/SoloPlay/Functions/NTIPOverrides.js index 29af9a31..21d8f215 100644 --- a/libs/SoloPlay/Functions/NTIPOverrides.js +++ b/libs/SoloPlay/Functions/NTIPOverrides.js @@ -18,23 +18,40 @@ includeIfNotIncluded("SoloPlay/Functions/PrototypeOverrides.js"); NTIPAliasStat["addfireskills"] = [sdk.stats.ElemSkill, 1]; NTIPAliasStat["plusskillwhirlwind"] = [sdk.stats.NonClassSkill, sdk.skills.Whirlwind]; NTIP.MAX_TIER = 100000; -// think this might be ugly but want to work on seperating soloplay from the base so pickits don't interfere -NTIP.RuntimeCheckList = []; -NTIP.RuntimeStringArray = []; -NTIP.SoloCheckList = []; -NTIP.SoloCheckListNoTier = []; -NTIP.SoloStringArray = []; -NTIP.FinalGear = { - list: [], - strArray: [], + +/** @constructor */ +function NTIPList () { + /** @type {Array>} */ + this.list = []; + /** @type {Array<{ line: string, file: string, string: string }>} */ + this.strArray = []; +} + +NTIPList.prototype.add = function (parsedLine, info) { + this.list.push(parsedLine); + this.strArray.push(info); }; -/** - * @param {string} tierType - * @returns {(item: ItemUnit) => number} - */ +NTIPList.prototype.remove = function (index) { + this.list.splice(index, 1); + this.strArray.splice(index, 1); +}; + +NTIPList.prototype.clear = function () { + this.list = []; + this.strArray = []; +}; +// think this might be ugly but want to work on seperating soloplay from the base so pickits don't interfere +NTIP.SoloList = new NTIPList(); +NTIP.Runtime = new NTIPList(); +NTIP.FinalGear = new NTIPList(); +NTIP.NoTier = new NTIPList(); +// handle regular pickits +NTIP.CheckList = new NTIPList(); +// NTIP.CheckListNoTier = new NTIPList(); // all items in a normal pickit are treated as no tier + NTIP.generateTierFunc = function (tierType) { - return function (item) { + return /** @param {ItemUnit} item */ function (item) { let tier = -1; const updateTier = (wanted) => { @@ -46,12 +63,12 @@ NTIP.generateTierFunc = function (tierType) { }; // Go through ALL lines that describe the item - for (let i = 0; i < NTIP.SoloCheckList.length; i += 1) { - if (NTIP.SoloCheckList[i].length !== 3) { + for (let i = 0; i < NTIP.SoloList.list.length; i++) { + if (NTIP.SoloList.list[i].length !== 3) { continue; } - const [type, stat, wanted] = NTIP.SoloCheckList[i]; + const [type, stat, wanted] = NTIP.SoloList.list[i]; // If the line doesnt have a tier of this type, we dont need to call it if (typeof wanted === "object" && wanted && typeof wanted[tierType] === "function") { @@ -72,8 +89,13 @@ NTIP.generateTierFunc = function (tierType) { } } } catch (e) { - const info = NTIP.SoloStringArray[i]; - Misc.errorReport("ÿc1Pickit Tier (" + tierType + ") error! Line # ÿc2" + info.line + " ÿc1Entry: ÿc0" + info.string + " (" + info.file + ") Error message: " + e.message + " Trigger item: " + item.fname.split("\n").reverse().join(" ")); + const info = NTIP.SoloList.strArray[i]; + Misc.errorReport( + "ÿc1Pickit Tier (" + tierType + ") error! Line # ÿc2" + + info.line + " ÿc1Entry: ÿc0" + info.string + + " (" + info.file + ") Error message: " + e.message + + " Trigger item: " + item.fname.split("\n").reverse().join(" ") + ); } } } @@ -85,49 +107,43 @@ NTIP.generateTierFunc = function (tierType) { /** * @function * @param {ItemUnit} item - * @returns {number} */ NTIP.GetTier = NTIP.generateTierFunc("Tier"); /** * @function * @param {ItemUnit} item - * @returns {number} */ NTIP.GetMercTier = NTIP.generateTierFunc("Merctier"); /** * @function * @param {ItemUnit} item - * @returns {number} */ NTIP.GetCharmTier = NTIP.generateTierFunc("Charmtier"); /** * @function * @param {ItemUnit} item - * @returns {number} */ NTIP.GetSecondaryTier = NTIP.generateTierFunc("Secondarytier"); -NTIP.addLine = function (itemString) { +NTIP.addLine = function (itemString, filename = "Kolbot-SoloPlay") { + const tierdItem = itemString.toLowerCase().includes("tier"); const info = { - line: NTIP.SoloCheckList.length + 1, - file: "Kolbot-SoloPlay", + line: tierdItem + ? NTIP.SoloList.list.length + 1 + : NTIP.NoTier.list.length + 1, + file: filename, string: itemString }; const line = NTIP.ParseLineInt(itemString, info); if (line) { - if (!itemString.toLowerCase().includes("tier")) { - NTIP.SoloCheckListNoTier.push(line); - } else { - NTIP.SoloCheckListNoTier.push([false, false]); - } - - NTIP.SoloCheckList.push(line); - NTIP.SoloStringArray.push(info); + tierdItem + ? NTIP.SoloList.add(line, info) + : NTIP.NoTier.add(line, info); } return true; @@ -159,8 +175,7 @@ NTIP.buildFinalGear = function (arr) { continue; } - NTIP.FinalGear.list.push(line); - NTIP.FinalGear.strArray.push(info); + NTIP.FinalGear.add(line, info); } } @@ -171,7 +186,7 @@ NTIP.buildFinalGear = function (arr) { // so things can be deleted without affecting the entire list NTIP.addToRuntime = function (itemString) { const info = { - line: NTIP.RuntimeCheckList.length + 1, + line: NTIP.Runtime.list.length + 1, file: "Kolbot-SoloPlay-Runtime", string: itemString }; @@ -179,23 +194,18 @@ NTIP.addToRuntime = function (itemString) { const line = NTIP.ParseLineInt(itemString, info); if (line) { - NTIP.RuntimeCheckList.push(line); - NTIP.RuntimeStringArray.push(info); + NTIP.Runtime.add(line, info); } return true; }; -NTIP.resetRuntimeList = () => { - NTIP.RuntimeCheckList.length = 0; - NTIP.RuntimeStringArray.length = 0; -}; - NTIP.buildList = function (...arraystoloop) { + const filename = (new Error()).stack.match(/[^\r\n]+/g).at(1).split("\\").last() || ""; for (let arr of arraystoloop) { if (Array.isArray(arr)) { - for (let i = 0; i < arr.length; i++) { - NTIP.addLine(arr[i]); + for (let str of arr) { + NTIP.addLine(str, filename); } } } @@ -203,31 +213,36 @@ NTIP.buildList = function (...arraystoloop) { return true; }; -NTIP.hasStats = function (item, entryList = [], verbose = false) { - let hasStat = false, line = "", stats; - const list = entryList.length ? entryList : NTIP.SoloCheckList; - const stringArr = entryList.length ? stringArray : NTIP.SoloStringArray; +/** + * @param {ItemUnit} item + * @param {NTIPList} entryList + * @param {boolean} verbose + */ +NTIP.hasStats = function (item, entryList, verbose = false) { + let stats; + let hasStat = false; + let line = ""; + const list = entryList && entryList.hasOwnProperty("list") && entryList.list.length + ? entryList.list + : NTIP.SoloList.list; + const stringArr = entryList && entryList.hasOwnProperty("strArray") && entryList.strArray.length + ? entryList.strArray + : NTIP.SoloList.strArray; for (let i = 0; i < list.length; i++) { try { - // eslint-disable-next-line no-unused-vars - let [type, stat, wanted] = list[i]; + let [type, stat] = list[i]; - if (typeof type === "function") { - if (type(item)) { - if (typeof stat === "function") { - if (stat(item)) { - hasStat = true; - stats = stat; - line = stringArr[i].file + " #" + stringArr[i].line + " " + stringArr[i].string; - - break; - } - } else { - hasStat = false; + if (typeof type !== "function" || typeof stat !== "function") { + continue; + } + if (type(item)) { + if (stat(item)) { + hasStat = true; + stats = stat; + line = stringArr[i].file + " #" + stringArr[i].line + " " + stringArr[i].string; - break; - } + break; } } } catch (e) { @@ -247,12 +262,18 @@ NTIP.hasStats = function (item, entryList = [], verbose = false) { }; // this method for charms needs work -NTIP.getInvoQuantity = function (item, entryList = []) { - const list = entryList.length ? entryList : NTIP.SoloCheckList; +/** + * @param {ItemUnit} item + * @param {NTIPList} entryList + */ +NTIP.getInvoQuantity = function (item, entryList) { + const list = entryList && entryList.hasOwnProperty("list") && entryList.list.length + ? entryList.list + : NTIP.SoloList.list; - for (let i = 0; i < list.length; i++) { + for (let el of list) { try { - const [type, stat, wanted] = list[i]; + const [type, stat, wanted] = el; if (typeof type === "function") { if (type(item)) { @@ -283,12 +304,18 @@ NTIP.getInvoQuantity = function (item, entryList = []) { return -1; }; -NTIP.getMaxQuantity = function (item, entryList = []) { - const list = entryList.length ? entryList : NTIP.SoloCheckList; +/** + * @param {ItemUnit} item + * @param {NTIPList} entryList + */ +NTIP.getMaxQuantity = function (item, entryList) { + const list = entryList && entryList.hasOwnProperty("list") && entryList.list.length + ? entryList.list + : NTIP.SoloList.list; - for (let i = 0; i < list.length; i++) { + for (let el of list) { try { - let [type, stat, wanted] = list[i]; + let [type, stat, wanted] = el; if (typeof type === "function") { if (type(item)) { @@ -319,18 +346,22 @@ NTIP.getMaxQuantity = function (item, entryList = []) { return -1; }; +/** + * @param {ItemUnit} item + * @param {NTIPList} entryList + * @param {boolean} verbose + */ NTIP.CheckItem = function (item, entryList, verbose = false) { let rval = {}; let result = 0; const identified = item.getFlag(sdk.items.flags.Identified); /** - * * @param {any[]} list * @param {string[]} stringArr * @returns */ - const iterateList = (list, stringArr) => { + const iterateList = function (list, stringArr) { let i, num; for (i = 0; i < list.length; i++) { @@ -351,7 +382,9 @@ NTIP.CheckItem = function (item, entryList, verbose = false) { break; } else { // attempt at inv fix for maxquantity - if (item.getParent() && item.getParent().name === me.name && item.mode === sdk.items.mode.inStorage && num === wanted.MaxQuantity) { + if (item.getParent() && item.getParent().name === me.name + && item.mode === sdk.items.mode.inStorage + && num === wanted.MaxQuantity) { result = 1; break; @@ -379,7 +412,9 @@ NTIP.CheckItem = function (item, entryList, verbose = false) { break; } else { // attempt at inv fix for maxquantity - if (item.getParent() && item.getParent().name === me.name && item.mode === sdk.items.mode.inStorage && num === wanted.MaxQuantity) { + if (item.getParent() && item.getParent().name === me.name + && item.mode === sdk.items.mode.inStorage + && num === wanted.MaxQuantity) { result = 1; break; @@ -403,7 +438,9 @@ NTIP.CheckItem = function (item, entryList, verbose = false) { break; } else { // attempt at inv fix for maxquantity - if (item.getParent() && item.getParent().name === me.name && item.mode === sdk.items.mode.inStorage && num === wanted.MaxQuantity) { + if (item.getParent() && item.getParent().name === me.name + && item.mode === sdk.items.mode.inStorage + && num === wanted.MaxQuantity) { result = 1; break; @@ -426,9 +463,14 @@ NTIP.CheckItem = function (item, entryList, verbose = false) { showConsole(); if (!entryList) { - Misc.errorReport("ÿc1Pickit error! Line # ÿc2" + stringArr[i].line + " ÿc1Entry: ÿc0" + stringArr[i].string + " (" + stringArr[i].file + ") Error message: " + pickError.message + " Trigger item: " + item.fname.split("\n").reverse().join(" ")); - + Misc.errorReport( + "ÿc1Pickit error! Line # ÿc2" + stringArr[i].line + + " ÿc1Entry: ÿc0" + stringArr[i].string + " (" + stringArr[i].file + + ") Error message: " + pickError.message + + " Trigger item: " + item.fname.split("\n").reverse().join(" ") + ); list.splice(i, 1); // Remove the element from the list + stringArr.splice(i, 1); // Remove the element from the list } else { Misc.errorReport("ÿc1Pickit error in runeword config!"); } @@ -438,32 +480,38 @@ NTIP.CheckItem = function (item, entryList, verbose = false) { } if (verbose) { + if (!identified && result === 1) { + result = -1; + } + rval.result = result; - rval.line = (() => { + rval.line = (function () { if (stringArr[i] === undefined) return null; - return result === 1 ? stringArr[i].file + " #" + stringArr[i].line : null; + return result === 1 + ? stringArr[i].file + " #" + stringArr[i].line + " [" + stringArr[i].string + "]" + : null; })(); - if (!identified && result === 1) { - rval.result = -1; - } - return rval; } return result; }; + if (entryList && entryList.hasOwnProperty("list") && Array.isArray(entryList.list)) { + return iterateList(entryList.list, entryList.strArray); + } + const listOfLists = [ - [NTIP.SoloCheckList, NTIP.SoloStringArray], - [NTIP_CheckList, stringArray], - [NTIP.RuntimeCheckList, NTIP.RuntimeStringArray] + NTIP.SoloList, + NTIP.NoTier, + NTIP.Runtime, + NTIP.CheckList, ]; - if (Array.isArray(entryList)) return iterateList(entryList, stringArray); - for (let i = 0; i < listOfLists.length; i++) { - iterateList(listOfLists[i][0], listOfLists[i][1]); - if ((verbose && rval.result !== 0) || (!verbose && result !== 0)) { + for (let obj of listOfLists) { + iterateList(obj.list, obj.strArray); + if (verbose ? rval.result !== 0 : result !== 0) { break; } } @@ -494,40 +542,39 @@ NTIP.OpenFile = function (filepath, notify) { let lines = nipfile.readAllLines(); nipfile.close(); - for (let i = 0; i < lines.length; i += 1) { + /** + * @note removed tierd check for normal pick files as soloplay handles that + */ + for (let entry of lines) { const info = { - line: i + 1, + line: NTIP.CheckList.list.length + 1, file: filename, - string: lines[i] + string: entry }; - let line = NTIP.ParseLineInt(lines[i], info); + let line = NTIP.ParseLineInt(entry, info); if (line) { - entries += 1; - NTIP_CheckList.push(line); - - if (!lines[i].toLowerCase().match("tier")) { - NTIP_CheckListNoTier.push(line); - } else { - NTIP_CheckListNoTier.push([false, false]); - } - - stringArray.push(info); + NTIP.CheckList.add(line, info); + entries++; } } if (notify) { - console.log("ÿc4Loaded NIP: ÿc2" + filename + "ÿc4. Lines: ÿc2" + lines.length + "ÿc4. Valid entries: ÿc2" + entries + ". ÿc4Time: ÿc2" + (getTickCount() - tick) + " ms"); + console.log( + "ÿc4Loaded NIP: ÿc2" + filename + "ÿc4. Lines: ÿc2" + lines.length + + "ÿc4. Valid entries: ÿc2" + entries + + ". ÿc4Time: ÿc2" + (getTickCount() - tick) + " ms" + ); } return true; }; NTIP.ParseLineInt = function (input, info) { - let i, property, p_start, p_end, p_section, p_keyword, p_result, value; + let i, property, p_start, p_section, p_keyword, value; - p_end = input.indexOf("//"); + let p_end = input.indexOf("//"); if (p_end !== -1) { input = input.substring(0, p_end); @@ -539,7 +586,31 @@ NTIP.ParseLineInt = function (input, info) { return null; } - p_result = input.split("#"); + const _props = new Map([ + ["wsm", 'getBaseStat("items", item.classid, "speed")'], + ["weaponspeed", 'getBaseStat("items", item.classid, "speed")'], + ["minimumsockets", 'getBaseStat("items", item.classid, "gemsockets")'], + ["strreq", "item.strreq"], + ["dexreq", "item.dexreq"], + ["2handed", 'getBaseStat("items", item.classid, "2handed")'], + ["color", "item.getColor()"], + ["type", "item.itemType"], + ["name", "item.classid"], + ["classid", "item.classid"], + ["class", "item.itemclass"], + ["quality", "item.quality"], + ["level", "item.ilvl"], + ["europe", '("' + me.realm.toLowerCase() + '"===" europe")'], + ["uswest", '("' + me.realm.toLowerCase() + '"===" uswest")'], + ["useast", '("' + me.realm.toLowerCase() + '"===" useast")'], + ["asia", '("' + me.realm.toLowerCase() + '"===" asia")'], + ["ladder", "me.ladder"], + ["hardcore", "(!!me.playertype)"], + ["classic", "(!me.gametype)"], + ["distance", "(item.onGroundOrDropping && item.distance || Infinity)"], + ]); + + let p_result = input.split("#"); if (p_result[0] && p_result[0].length > 4) { p_section = p_result[0].split("["); @@ -551,47 +622,6 @@ NTIP.ParseLineInt = function (input, info) { property = p_section[i].substring(0, p_end - 1); switch (property) { - case "wsm": - case "weaponspeed": - p_result[0] += 'getBaseStat("items", item.classid, "speed")'; - - break; - case "minimumsockets": - p_result[0] += 'getBaseStat("items", item.classid, "gemsockets")'; - - break; - case "strreq": - p_result[0] += "item.strreq"; - - break; - case "dexreq": - p_result[0] += "item.dexreq"; - - break; - case "2handed": - p_result[0] += 'getBaseStat("items", item.classid, "2handed")'; - - break; - case "color": - p_result[0] += "item.getColor()"; - - break; - case "type": - p_result[0] += "item.itemType"; - - break; - case "name": - p_result[0] += "item.classid"; - - break; - case "class": - p_result[0] += "item.itemclass"; - - break; - case "quality": - p_result[0] += "item.quality"; - - break; case "flag": if (p_section[i][p_end] === "!") { p_result[0] += "!item.getFlag("; @@ -601,10 +631,6 @@ NTIP.ParseLineInt = function (input, info) { p_end += 2; - break; - case "level": - p_result[0] += "item.ilvl"; - break; case "prefix": if (p_section[i][p_end] === "!") { @@ -625,34 +651,14 @@ NTIP.ParseLineInt = function (input, info) { p_end += 2; - break; - case "europe": - case "uswest": - case "useast": - case "asia": - p_result[0] += '("' + me.realm.toLowerCase() + '"==="' + property.toLowerCase() + '")'; - - break; - case "ladder": - p_result[0] += "me.ladder"; - - break; - case "hardcore": - p_result[0] += "(!!me.playertype)"; - - break; - case "classic": - p_result[0] += "(!me.gametype)"; - - break; - case "distance": - p_result[0] += "(item.onGroundOrDropping && item.distance || Infinity)"; - break; default: - Misc.errorReport("Unknown property: " + property + " File: " + info.file + " Line: " + info.line); - - return false; + if (!_props.has(property)) { + Misc.errorReport("Unknown property: " + property + " File: " + info.file + " Line: " + info.line); + + return false; + } + p_result[0] += _props.get(property); } for (p_start = p_end; p_end < p_section[i].length; p_end += 1) { @@ -678,72 +684,66 @@ NTIP.ParseLineInt = function (input, info) { p_keyword = p_section[i].substring(p_start, p_end); if (isNaN(p_keyword)) { - switch (property) { - case "color": - if (NTIPAliasColor[p_keyword] === undefined) { - Misc.errorReport("Unknown color: " + p_keyword + " File: " + info.file + " Line: " + info.line); - - return false; - } - - p_result[0] += NTIPAliasColor[p_keyword]; - - break; - case "type": - if (NTIPAliasType[p_keyword] === undefined) { - Misc.errorReport("Unknown type: " + p_keyword + " File: " + info.file + " Line: " + info.line); + try { + switch (property) { + case "color": + if (NTIPAliasColor[p_keyword] === undefined) { + throw new Error("Unknown color: " + p_keyword + " File: " + info.file + " Line: " + info.line); + } - return false; - } + p_result[0] += NTIPAliasColor[p_keyword]; - p_result[0] += NTIPAliasType[p_keyword]; + break; + case "type": + if (NTIPAliasType[p_keyword] === undefined) { + throw new Error("Unknown type: " + p_keyword + " File: " + info.file + " Line: " + info.line); + } - break; - case "name": - if (NTIPAliasClassID[p_keyword] === undefined) { - Misc.errorReport("Unknown name: " + p_keyword + " File: " + info.file + " Line: " + info.line); + p_result[0] += NTIPAliasType[p_keyword]; - return false; - } + break; + case "name": + if (NTIPAliasClassID[p_keyword] === undefined) { + throw new Error("Unknown name: " + p_keyword + " File: " + info.file + " Line: " + info.line); + } - p_result[0] += NTIPAliasClassID[p_keyword]; + p_result[0] += NTIPAliasClassID[p_keyword]; - break; - case "class": - if (NTIPAliasClass[p_keyword] === undefined) { - Misc.errorReport("Unknown class: " + p_keyword + " File: " + info.file + " Line: " + info.line); + break; + case "class": + if (NTIPAliasClass[p_keyword] === undefined) { + throw new Error("Unknown class: " + p_keyword + " File: " + info.file + " Line: " + info.line); + } - return false; - } + p_result[0] += NTIPAliasClass[p_keyword]; - p_result[0] += NTIPAliasClass[p_keyword]; + break; + case "quality": + if (NTIPAliasQuality[p_keyword] === undefined) { + throw new Error("Unknown quality: " + p_keyword + " File: " + info.file + " Line: " + info.line); + } - break; - case "quality": - if (NTIPAliasQuality[p_keyword] === undefined) { - Misc.errorReport("Unknown quality: " + p_keyword + " File: " + info.file + " Line: " + info.line); + p_result[0] += NTIPAliasQuality[p_keyword]; - return false; - } + break; + case "flag": + if (NTIPAliasFlag[p_keyword] === undefined) { + throw new Error("Unknown flag: " + p_keyword + " File: " + info.file + " Line: " + info.line); + } - p_result[0] += NTIPAliasQuality[p_keyword]; + p_result[0] += NTIPAliasFlag[p_keyword] + ")"; - break; - case "flag": - if (NTIPAliasFlag[p_keyword] === undefined) { - Misc.errorReport("Unknown flag: " + p_keyword + " File: " + info.file + " Line: " + info.line); + break; + case "prefix": + case "suffix": + p_result[0] += "\"" + p_keyword + "\")"; - return false; + break; } + } catch (e) { + Misc.errorReport(e); - p_result[0] += NTIPAliasFlag[p_keyword] + ")"; - - break; - case "prefix": - case "suffix": - p_result[0] += "\"" + p_keyword + "\")"; - - break; + return false; } } else { if (property === "flag" || property === "prefix" || property === "suffix") { diff --git a/libs/SoloPlay/Functions/PickitOverrides.js b/libs/SoloPlay/Functions/PickitOverrides.js index 2a654385..84aabf64 100644 --- a/libs/SoloPlay/Functions/PickitOverrides.js +++ b/libs/SoloPlay/Functions/PickitOverrides.js @@ -41,40 +41,52 @@ Pickit.classicMode = me.classic; */ Pickit.checkItem = function (unit) { const rval = NTIP.CheckItem(unit, false, true); - const resultObj = (result, line = null) => ({ - result: result, - line: line - }); + const resultObj = function (result, line = null) { + return { + result: result, + line: line + }; + }; // quick return on essentials - we know they aren't going to be in the other checks if (Pickit.essentials.includes(unit.itemType)) return rval; if (!Pickit.classicMode) { - if ([sdk.items.runes.Ral, sdk.items.runes.Ort].includes(unit.classid) && Town.repairIngredientCheck(unit)) { + if ([sdk.items.runes.Ral, sdk.items.runes.Ort].includes(unit.classid) + && Town.repairIngredientCheck(unit)) { return resultObj(Pickit.Result.UTILITY); } /** * Need to redo this */ - if (CharData.skillData.bow.onSwitch && [sdk.items.type.BowQuiver, sdk.items.type.CrossbowQuiver].includes(unit.itemType) && rval === Pickit.Result.WANTED) { - if ([sdk.items.type.Bow, sdk.items.type.AmazonBow].includes(CharData.skillData.bow.bowType) && unit.itemType === sdk.items.type.BowQuiver) { + if (CharData.skillData.bow.onSwitch + && [sdk.items.type.BowQuiver, sdk.items.type.CrossbowQuiver].includes(unit.itemType) + && rval === Pickit.Result.WANTED) { + if ([sdk.items.type.Bow, sdk.items.type.AmazonBow].includes(CharData.skillData.bow.bowType) + && unit.itemType === sdk.items.type.BowQuiver) { return resultObj(Pickit.Result.SOLOWANTS, "Switch-Arrows"); - } else if (CharData.skillData.bow.bowType === sdk.items.type.Crossbow && unit.itemType === sdk.items.type.CrossbowQuiver) { + } else if (CharData.skillData.bow.bowType === sdk.items.type.Crossbow + && unit.itemType === sdk.items.type.CrossbowQuiver) { return resultObj(Pickit.Result.SOLOWANTS, "Switch-Bolts"); } } } - if (unit.classid === sdk.items.StaminaPotion && (me.charlvl < 18 || me.staminaPercent <= 85 || me.walking) && Item.getQuantityOwned(unit, true) < 2) { + if (unit.classid === sdk.items.StaminaPotion + && (me.charlvl < 18 || me.staminaPercent <= 85 || me.walking) + && Item.getQuantityOwned(unit, true) < 2) { return resultObj(Pickit.Result.WANTED, "LowStamina"); } - if (unit.classid === sdk.items.AntidotePotion && me.getState(sdk.states.Poison) && Item.getQuantityOwned(unit, true) < 2) { + if (unit.classid === sdk.items.AntidotePotion + && me.getState(sdk.states.Poison) && Item.getQuantityOwned(unit, true) < 2) { return resultObj(Pickit.Result.WANTED, "Poisoned"); } - if (unit.classid === sdk.items.ThawingPotion && [sdk.states.Frozen, sdk.states.FrozenSolid].some(state => me.getState(state)) && Item.getQuantityOwned(unit, true) < 2) { + if (unit.classid === sdk.items.ThawingPotion + && (me.getState(sdk.states.Frozen) || me.getState(sdk.states.FrozenSolid)) + && Item.getQuantityOwned(unit, true) < 2) { return resultObj(Pickit.Result.WANTED, "Frozen"); } @@ -95,7 +107,7 @@ Pickit.checkItem = function (unit) { return resultObj(Pickit.Result.SOLOWANTS, "Autoequip charm Tier: " + NTIP.GetCharmTier(unit)); } - return NTIP.CheckItem(unit, NTIP_CheckListNoTier, true); + return NTIP.CheckItem(unit, NTIP.NoTier, true) || NTIP.CheckItem(unit, NTIP.CheckList, true); } if ((NTIP.GetMercTier(unit) > 0 || NTIP.GetTier(unit) > 0 || NTIP.GetSecondaryTier(unit) > 0) && unit.identified) { @@ -111,11 +123,11 @@ Pickit.checkItem = function (unit) { return resultObj(Pickit.Result.SOLOWANTS, "Autoequip Secondary Tier: " + NTIP.GetSecondaryTier(unit)); } - return NTIP.CheckItem(unit, NTIP_CheckListNoTier, true); + return NTIP.CheckItem(unit, NTIP.NoTier, true) || NTIP.CheckItem(unit, NTIP.CheckList, true); } if (rval.result === Pickit.Result.WANTED && unit.isBaseType) { - if (NTIP.CheckItem(unit, NTIP.SoloCheckListNoTier)) { + if (NTIP.CheckItem(unit, NTIP.NoTier)) { return resultObj(Pickit.Result.SOLOWANTS, "Base Type Item"); } } @@ -132,7 +144,8 @@ Pickit.checkItem = function (unit) { if (itemValuePerSquare >= 2000) { // If total gold is less than 500k pick up anything worth 2k gold per square to sell in town. return resultObj(Pickit.Result.TRASH, "Valuable Item: " + itemValue); - } else if (itemValuePerSquare >= Pickit.minItemKeepGoldValue() && (me.gold < Config.LowGold || unit.isInInventory)) { + } else if (itemValuePerSquare >= Pickit.minItemKeepGoldValue() + && (me.gold < Config.LowGold || unit.isInInventory)) { // If total gold is less than LowGold setting pick up anything worth 10 gold per square to sell in town. return resultObj(Pickit.Result.TRASH, "LowGold Item: " + itemValue); } @@ -300,7 +313,7 @@ Pickit.canPick = function (unit) { for (i = 0; i < buffers.length; i += 1) { if (Config[buffers[i]]) { - pottype = (() => { + pottype = (function () { switch (buffers[i]) { case "HPBuffer": return sdk.items.type.HealingPotion; @@ -328,7 +341,7 @@ Pickit.canPick = function (unit) { } } - needPots > 0 && !beltCheck && Pickit.toCursorPick.push(unit.gid); + needPots > 0 && !beltCheck && _toCursorPick.add(unit.gid); } } } @@ -360,7 +373,8 @@ Pickit.canPick = function (unit) { return true; }; -Pickit.toCursorPick = []; +/** @type {Set} */ +const _toCursorPick = new Set(); /** * @override @@ -386,32 +400,30 @@ Pickit.pickItem = function (unit, status, keptLine, clearBeforePick = true) { self.color = Item.color(unit); self.gold = unit.getStat(sdk.stats.Gold); self.dist = (unit.distance || Infinity); - let canTk = (Skill.haveTK && Pickit.tkable.includes(self.type) && Pickit.toCursorPick.indexOf(unit.gid) === -1 + let canTk = (Skill.haveTK && Pickit.tkable.includes(self.type) && !_toCursorPick.has(unit.gid) && self.dist > 5 && self.dist < 20 && !checkCollision(me, unit, sdk.collision.WallOrRanged)); self.useTk = canTk && (me.mpPercent > 50); self.picked = false; } - let item, tick, gid, retry = false; const itemCount = me.itemcount; - const cancelFlags = [sdk.uiflags.Inventory, sdk.uiflags.NPCMenu, sdk.uiflags.Waypoint, sdk.uiflags.Shop, sdk.uiflags.Stash, sdk.uiflags.Cube]; + const cancelFlags = [ + sdk.uiflags.Inventory, sdk.uiflags.NPCMenu, + sdk.uiflags.Waypoint, sdk.uiflags.Shop, + sdk.uiflags.Stash, sdk.uiflags.Cube + ]; if (!unit || unit === undefined) return false; - if (unit.gid) { - gid = unit.gid; - item = Game.getItem(-1, -1, gid); - } - + let retry = false; + const gid = unit.gid; + + let item = Game.getItem(-1, -1, gid); if (!item) return false; - for (let i = 0; i < cancelFlags.length; i += 1) { - if (getUIFlag(cancelFlags[i])) { - delay(500); - me.cancel(0); - - break; - } + if (cancelFlags.some(function (flag) { return getUIFlag(flag); })) { + delay(500); + me.cancel(0); } const stats = new ItemStats(item); @@ -468,21 +480,30 @@ Pickit.pickItem = function (unit, status, keptLine, clearBeforePick = true) { let cursorUnit; itemDist = item.distance; // use packet first, if we fail and not using fast pick use click - Pickit.toCursorPick.includes(item.gid) - ? Packet.click(item, true) && (cursorUnit = Misc.poll(() => Game.getCursorUnit(), (itemDist > 10 ? 1000 : 250), 50)) && Storage.Inventory.MoveTo(cursorUnit) - : (Config.FastPick || i < 1) ? Packet.click(item) : Misc.click(0, 0, item); + _toCursorPick.has(item.gid) + ? Packet.click(item, true) + && (cursorUnit = Misc.poll(function () { + return Game.getCursorUnit(); + }, (itemDist > 10 ? 1000 : 250), 50)) + && Storage.Inventory.MoveTo(cursorUnit) + : (Config.FastPick || i < 1) + ? Packet.click(item) + : Misc.click(0, 0, item); } - tick = getTickCount(); + let tick = getTickCount(); while (getTickCount() - tick < (itemDist > 10 ? 2000 : 1000)) { item = copyUnit(item); - Pickit.toCursorPick.includes(item.gid) && Pickit.toCursorPick.remove(item.gid); + _toCursorPick.has(item.gid) && _toCursorPick.delete(item.gid); if (stats.classid === sdk.items.Gold) { if (!item.getStat(sdk.stats.Gold) || item.getStat(sdk.stats.Gold) < stats.gold) { - console.log("ÿc7Picked up " + stats.color + (item.getStat(sdk.stats.Gold) ? (item.getStat(sdk.stats.Gold) - stats.gold) : stats.gold) + " " + stats.name); - + console.log( + "ÿc7Picked up " + stats.color + + (item.getStat(sdk.stats.Gold) ? (item.getStat(sdk.stats.Gold) - stats.gold) : stats.gold) + + " " + stats.name + ); return true; } } @@ -585,11 +606,15 @@ Pickit.checkSpotForItems = function (spot, checkVsMyDist = false, range = Config const itemDistFromMe = item.distance; if (Pickit.essentials.includes(item.itemType)) { if (Pickit.checkItem(item).result && Pickit.canPick(item) && Pickit.canFit(item)) { - checkVsMyDist && itemDistFromMe < spotDist ? Pickit.essentials.push(copyUnit(item)) : itemList.push(copyUnit(item)); + checkVsMyDist && itemDistFromMe < spotDist + ? Pickit.essentials.push(copyUnit(item)) + : itemList.push(copyUnit(item)); } } else if (item.itemType === sdk.items.type.Key) { if (Pickit.canPick(item) && Pickit.checkItem(item).result) { - checkVsMyDist && itemDistFromMe < spotDist ? Pickit.pickList.push(copyUnit(item)) : itemList.push(copyUnit(item)); + checkVsMyDist && itemDistFromMe < spotDist + ? Pickit.pickList.push(copyUnit(item)) + : itemList.push(copyUnit(item)); } } else if (Pickit.checkItem(item).result) { if (checkVsMyDist && itemDistFromMe < spotDist) { @@ -612,15 +637,19 @@ Pickit.essentialList = []; Pickit.essessntialsPick = function (clearBeforePick = false, ignoreGold = false, builtList = [], once = false) { if (me.dead || me.inTown || (!Pickit.enabled && !clearBeforePick)) return false; - Pickit.essentialList.concat(builtList, Pickit.pickList).filter(i => !!i && Pickit.essentials.includes(i.itemType)); + Pickit.essentialList + .concat(builtList, Pickit.pickList) + .filter(function (i) { + return !!i && Pickit.essentials.includes(i.itemType); + }); let item = Game.getItem(); const maxDist = Skill.haveTK ? 15 : 5; if (item) { do { - if (item.onGroundOrDropping && getDistance(me, item) <= maxDist && Pickit.essentials.includes(item.itemType)) { + if (item.onGroundOrDropping && item.distance <= maxDist && Pickit.essentials.includes(item.itemType)) { if (Pickit.essentialList.some(el => el.gid === item.gid)) continue; - if (item.itemType !== sdk.items.type.Gold || getDistance(me, item) < 5) { + if (item.itemType !== sdk.items.type.Gold || item.distance < 5) { Pickit.essentialList.push(copyUnit(item)); } } @@ -699,6 +728,7 @@ Pickit.pickItems = function (range = Config.PickRange, once = false) { let needMule = false; const canUseMule = AutoMule.getInfo() && AutoMule.getInfo().hasOwnProperty("muleInfo"); + const _pots = [sdk.items.type.HealingPotion, sdk.items.type.ManaPotion, sdk.items.type.RejuvPotion]; while (!me.idle) { delay(40); @@ -710,13 +740,15 @@ Pickit.pickItems = function (range = Config.PickRange, once = false) { do { if (Pickit.ignoreList.has(item.gid)) continue; if (Pickit.pickList.some(el => el.gid === item.gid)) continue; - if (item.onGroundOrDropping && getDistance(me, item) <= range) { + if (item.onGroundOrDropping && item.distance <= range) { Pickit.pickList.push(copyUnit(item)); } } while (item.getNext()); } - if (Pickit.pickList.some(i => [sdk.items.type.HealingPotion, sdk.items.type.ManaPotion, sdk.items.type.RejuvPotion].includes(i.itemType))) { + if (Pickit.pickList.some(function (el) { + return _pots.includes(el.itemType); + })) { me.clearBelt(); Pickit.beltSize = Storage.BeltSize(); } diff --git a/libs/SoloPlay/Functions/RunewordsOverrides.js b/libs/SoloPlay/Functions/RunewordsOverrides.js index 3ee80501..0e65ddb6 100644 --- a/libs/SoloPlay/Functions/RunewordsOverrides.js +++ b/libs/SoloPlay/Functions/RunewordsOverrides.js @@ -5,7 +5,8 @@ * */ -!includeIfNotIncluded("core/Runewords.js"); +includeIfNotIncluded("core/Runewords.js"); +includeIfNotIncluded("SoloPlay/Functions/NTIPOverrides.js"); Runeword.PDiamondShield = Runeword.addRuneword( "PDiamondShield", 3, @@ -13,6 +14,47 @@ Runeword.PDiamondShield = Runeword.addRuneword( [sdk.items.type.AnyShield] ); +Runewords.pickitEntries = new NTIPList(); + +Runewords.init = function () { + if (!Config.MakeRunewords) return; + + Runewords.pickitEntries.clear(); + + // initiate pickit entries + for (let entry of Config.KeepRunewords) { + let info = { + file: "Character Config", + line: entry + }; + + let parsedLine = NTIP.ParseLineInt(entry, info); + if (parsedLine) { + Runewords.pickitEntries.add(parsedLine, info); + } + } + + // change text to classid + for (let i = 0; i < Config.Runewords.length; i += 1) { + const [runeword, base] = Config.Runewords[i]; + + if (!runeword.ladderRestricted()) { + if (isNaN(base)) { + if (NTIPAliasClassID.hasOwnProperty(base.replace(/\s+/g, "").toLowerCase())) { + Config.Runewords[i][1] = NTIPAliasClassID[base.replace(/\s+/g, "").toLowerCase()]; + } else { + Misc.errorReport("ÿc1Invalid runewords entry:ÿc0 " + base); + Config.Runewords.splice(i, 1); + + i -= 1; + } + } + } + } + + this.buildLists(); +}; + Runewords.checkRunewords = function () { // keep a const reference of our items so failed checks don't remove items from the list const itemsRef = me.findItems(-1, sdk.items.mode.inStorage); @@ -78,7 +120,8 @@ Runewords.getBase = function (runeword, base, ethFlag, reroll) { */ if ((!reroll && !item.getItem() && Item.betterBaseThanWearing(item, Developer.debugging.baseCheck)) - || (reroll && item.getItem() && !NTIP.CheckItem(item, this.pickitEntries) && !Item.autoEquipCheckMerc(item, true) && !Item.autoEquipCheck(item, true))) { + || (reroll && item.getItem() && !NTIP.CheckItem(item, this.pickitEntries) + && !Item.autoEquipCheckMerc(item, true) && !Item.autoEquipCheck(item, true))) { if (!ethFlag || (ethFlag === Roll.Eth && item.ethereal) || (ethFlag === Roll.NonEth && !item.ethereal)) { return copyUnit(item); } diff --git a/libs/SoloPlay/Functions/TownOverrides.js b/libs/SoloPlay/Functions/TownOverrides.js index a9cc7424..f95bccb3 100644 --- a/libs/SoloPlay/Functions/TownOverrides.js +++ b/libs/SoloPlay/Functions/TownOverrides.js @@ -16,10 +16,11 @@ includeIfNotIncluded("core/Town.js"); new Overrides.Override(Town, Town.drinkPots, function (orignal, type) { const objDrank = orignal(type, false); - const pots = {}; - pots[sdk.items.StaminaPotion] = "stamina"; - pots[sdk.items.AntidotePotion] = "antidote"; - pots[sdk.items.ThawingPotion] = "thawing"; + const pots = new Map([ + [sdk.items.StaminaPotion, "stamina"], + [sdk.items.AntidotePotion, "antidote"], + [sdk.items.ThawingPotion, "thawing"] + ]); try { if (objDrank.potName) { @@ -28,14 +29,18 @@ new Overrides.Override(Town, Town.drinkPots, function (orignal, type) { if (objID) { // non-english version if (!CharData.pots.has(objID)) { - typeof type === "number" ? (objID = pots[objID]) : (objID = type.toLowerCase()); + typeof type === "number" + ? (objID = pots.get(objID)) + : (objID = type.toLowerCase()); } if (!CharData.pots.get(objID).active() || CharData.pots.get(objID).timeLeft() <= 0) { CharData.pots.get(objID).tick = getTickCount(); CharData.pots.get(objID).duration = objDrank.quantity * 30 * 1000; } else { - CharData.pots.get(objID).duration += (objDrank.quantity * 30 * 1000) - (getTickCount() - CharData.pots.get(objID).tick); + CharData.pots.get(objID).duration += ( + objDrank.quantity * 30 * 1000) - (getTickCount() - CharData.pots.get(objID).tick + ); } console.log("ÿc9DrinkPotsÿc0 :: drank " + objDrank.quantity + " " + objDrank.potName + "s. Timer [" + Time.format(CharData.pots.get(objID).duration) + "]"); @@ -80,7 +85,12 @@ Town.ignoredItemTypes = [ * @returns {boolean} */ Town.systemsKeep = function (item) { - return (AutoEquip.wanted(item) || Cubing.keepItem(item) || Runewords.keepItem(item) || CraftingSystem.keepItem(item) || SoloWants.keepItem(item)); + return (AutoEquip.wanted(item) + || Cubing.keepItem(item) + || Runewords.keepItem(item) + || CraftingSystem.keepItem(item) + || SoloWants.keepItem(item) + ); }; /** @@ -89,7 +99,8 @@ Town.systemsKeep = function (item) { */ Town.needForceID = function (item) { const result = Pickit.checkItem(item); - return ([Pickit.Result.WANTED, Pickit.Result.CUBING].includes(result.result) && !item.identified && AutoEquip.hasTier(item)); + return ([Pickit.Result.WANTED, Pickit.Result.CUBING].includes(result.result) + && !item.identified && AutoEquip.hasTier(item)); }; Town.haveItemsToSell = function () { @@ -98,7 +109,10 @@ Town.haveItemsToSell = function () { let i = Town.sell.shift(); if (typeof i === "undefined") continue; let realItem = me.getItem(i.classid, -1, i.gid); - if (realItem && realItem.isInStorage && !Town.ignoreType(realItem.itemType) && realItem.sellable && !Town.systemsKeep(realItem)) { + if (realItem && realItem.isInStorage + && !Town.ignoreType(realItem.itemType) + && realItem.sellable + && !Town.systemsKeep(realItem)) { temp.push(realItem); } } @@ -117,7 +131,6 @@ Town.sellItems = function (itemList = []) { if (this.initNPC("Shop", "sell")) { while (itemList.length) { let item = itemList.shift(); - if (!item.isInStorage) continue; try { @@ -231,12 +244,14 @@ Town.identify = function () { if (me.gold < 15000 && NPCAction.cainID(true)) return true; let list = (Storage.Inventory.Compare(Config.Inventory) || []) - .filter(item => !item.identified); + .filter(function (item) { + return !item.identified; + }); if (!list.length) return false; // Avoid unnecessary NPC visits // Only unid items or sellable junk (low level) should trigger a NPC visit - if (!list.some(item => { + if (!list.some(function (item) { let identified = item.identified; if (!identified && AutoEquip.hasTier(item)) return true; return ([Pickit.Result.UNID, Pickit.Result.TRASH].includes(Pickit.checkItem(item).result)); @@ -246,7 +261,8 @@ Town.identify = function () { let tome = me.getTome(sdk.items.TomeofIdentify); // if we have a tome might as well use it - this might prevent us from having to run from one npc to another - if (tome && tome.getStat(sdk.stats.Quantity) > 0 && Town.getDistance(Town.tasks.get(me.act).Shop) > 5) { + if (tome && tome.getStat(sdk.stats.Quantity) > 0 + && Town.getDistance(Town.tasks.get(me.act).Shop) > 5) { // not in the field but oh well no need to repeat the code if (me.fieldID() && !me.getUnids().length) { return true; @@ -322,16 +338,13 @@ Town.needStash = function () { return true; } - let items = (Storage.Inventory.Compare(Config.Inventory) || []) - .filter(item => !Town.ignoreType(item.itemType) && (!item.isCharm || !CharmEquip.keptGids.has(item.gid))); - - for (let i = 0; i < items.length; i += 1) { - if (Storage.Stash.CanFit(items[i])) { - return true; - } - } - - return false; + return (Storage.Inventory.Compare(Config.Inventory) || []) + .filter(function (item) { + return !Town.ignoreType(item.itemType) && (!item.isCharm || !CharmEquip.keptGids.has(item.gid)); + }) + .some(function (item) { + return Storage.Stash.CanFit(item); + }); }; /** @@ -358,8 +371,8 @@ Town.stash = function (stashGold = true) { if (items.length > 0) { Storage.Stash.SortItems(); - items.forEach(item => { - if (this.canStash(item)) { + items.forEach(function (item) { + if (Town.canStash(item)) { const pickResult = Pickit.checkItem(item).result; switch (true) { case pickResult !== Pickit.Result.UNWANTED && pickResult !== Pickit.Result.TRASH: @@ -385,7 +398,9 @@ Town.stash = function (stashGold = true) { // Stash gold if (stashGold) { - if (me.getStat(sdk.stats.Gold) >= Config.StashGold && me.getStat(sdk.stats.GoldBank) < 25e5 && this.openStash()) { + if (me.getStat(sdk.stats.Gold) >= Config.StashGold + && me.getStat(sdk.stats.GoldBank) < 25e5 + && this.openStash()) { gold(me.getStat(sdk.stats.Gold), 3); delay(1000); // allow UI to initialize me.cancel(); @@ -425,10 +440,13 @@ Town.clearInventory = function () { console.debug("clearInventory: start clean-up remaining pots"); let sellOrDrop = []; let potsInInventory = me.getItemsEx() - .filter((p) => p.isInInventory && [ - sdk.items.type.HealingPotion, sdk.items.type.ManaPotion, sdk.items.type.RejuvPotion, - sdk.items.type.ThawingPotion, sdk.items.type.AntidotePotion, sdk.items.type.StaminaPotion - ].includes(p.itemType)); + .filter(function (p) { + return p.isInInventory && [ + sdk.items.type.HealingPotion, sdk.items.type.ManaPotion, + sdk.items.type.RejuvPotion, sdk.items.type.ThawingPotion, + sdk.items.type.AntidotePotion, sdk.items.type.StaminaPotion + ].includes(p.itemType); + }); if (potsInInventory.length > 0) { let [hp, mp, rv, specials] = [[], [], [], []]; @@ -474,7 +492,10 @@ Town.clearInventory = function () { } if (Config.FieldID.Enabled && !me.getItem(sdk.items.TomeofIdentify)) { - let scrolls = me.getItemsEx().filter(i => i.isInInventory && i.classid === sdk.items.ScrollofIdentify); + let scrolls = me.getItemsEx() + .filter(function (i) { + return i.isInInventory && i.classid === sdk.items.ScrollofIdentify; + }); while (scrolls.length > 2) { sellOrDrop.push(scrolls.shift()); @@ -483,7 +504,8 @@ Town.clearInventory = function () { // Any leftover items from a failed ID (crashed game, disconnect etc.) const ignoreTypes = [ - sdk.items.type.Book, sdk.items.type.Key, sdk.items.type.HealingPotion, sdk.items.type.ManaPotion, sdk.items.type.RejuvPotion + sdk.items.type.Book, sdk.items.type.Key, + sdk.items.type.HealingPotion, sdk.items.type.ManaPotion, sdk.items.type.RejuvPotion ]; let items = (Storage.Inventory.Compare(Config.Inventory) || []) .filter(function (item) { @@ -495,18 +517,19 @@ Town.clearInventory = function () { return false; }); - /** - * @type {ItemUnit[]} - */ + /** @type {ItemUnit[]} */ let sell = []; /** * @param {ItemUnit} item * @returns {boolean} */ - const classItemType = (item) => [ - sdk.items.type.Wand, sdk.items.type.VoodooHeads, sdk.items.type.AuricShields, sdk.items.type.PrimalHelm, sdk.items.type.Pelt - ].includes(item.itemType); + const classItemType = function (item) { + return [ + sdk.items.type.Wand, sdk.items.type.VoodooHeads, + sdk.items.type.AuricShields, sdk.items.type.PrimalHelm, sdk.items.type.Pelt + ].includes(item.itemType); + }; items.length > 0 && items.forEach(function (item) { let result = Pickit.checkItem(item).result; @@ -514,7 +537,7 @@ Town.clearInventory = function () { if ([Pickit.Result.UNWANTED, Pickit.Result.TRASH].indexOf(result) === -1) { if ((item.isBaseType && item.sockets > 0) || (classItemType(item) && item.normal && item.sockets === 0)) { if (!Item.betterThanStashed(item) && !Item.betterBaseThanWearing(item, Developer.debugging.baseCheck)) { - if (NTIP.CheckItem(item, NTIP_CheckListNoTier) === Pickit.Result.UNWANTED) { + if (NTIP.CheckItem(item, NTIP.CheckList) === Pickit.Result.UNWANTED) { result = Pickit.Result.TRASH; } } @@ -525,19 +548,24 @@ Town.clearInventory = function () { [Pickit.Result.UNWANTED, Pickit.Result.TRASH].includes(result) && sell.push(item); }); - sell = (sell.length > 0 ? sell.concat(sellOrDrop) : sellOrDrop.slice(0)); - sell.length > 0 && this.initNPC("Shop", "clearInventory") && sell.forEach(function (item) { - try { - if (getUIFlag(sdk.uiflags.Shop) || getUIFlag(sdk.uiflags.NPCMenu)) { - console.log("clearInventory sell " + item.prettyPrint); - Item.logger("Sold", item); - item.sell(); - delay(100); + sell = (sell.length > 0 + ? sell.concat(sellOrDrop) + : sellOrDrop.slice(0) + ); + if (sell.length > 0 && this.initNPC("Shop", "clearInventory")) { + sell.forEach(function (item) { + try { + if (getUIFlag(sdk.uiflags.Shop) || getUIFlag(sdk.uiflags.NPCMenu)) { + console.log("clearInventory sell " + item.prettyPrint); + Item.logger("Sold", item); + item.sell(); + delay(100); + } + } catch (e) { + console.error(e); } - } catch (e) { - console.error(e); - } - }); + }); + } Town.sell = []; @@ -548,7 +576,9 @@ Town.clearInventory = function () { Town.clearJunk = function () { let junkItems = me.getItemsEx() - .filter(i => i.isInStorage && !Town.ignoreType(i.itemType) && i.sellable && !Town.systemsKeep(i)); + .filter(function (i) { + return i.isInStorage && !Town.ignoreType(i.itemType) && i.sellable && !Town.systemsKeep(i); + }); if (!junkItems.length) return false; console.log("ÿc8Start ÿc0:: ÿc8clearJunk"); @@ -564,11 +594,13 @@ Town.clearJunk = function () { * @param {ItemUnit} item * @returns {boolean} */ - const getToItem = (str = "", item = null) => { + const getToItem = function (str = "", item = null) { if (!getUIFlag(sdk.uiflags.Stash) && item.isInStash && !Town.openStash()) { throw new Error("ÿc9" + str + "ÿc0 :: Failed to get " + item.prettyPrint + " from stash"); } - if (item.isInCube && !Cubing.emptyCube()) throw new Error("ÿc9" + str + "ÿc0 :: Failed to remove " + item.prettyPrint + " from cube"); + if (item.isInCube && !Cubing.emptyCube()) { + throw new Error("ÿc9" + str + "ÿc0 :: Failed to remove " + item.prettyPrint + " from cube"); + } return true; }; @@ -585,7 +617,8 @@ Town.clearJunk = function () { } if (pickitResult !== Pickit.Result.WANTED) { - if (!junk.identified && !Cubing.keepItem(junk) && !CraftingSystem.keepItem(junk) && junk.quality < sdk.items.quality.Set) { + if (!junk.identified && !Cubing.keepItem(junk) + && !CraftingSystem.keepItem(junk) && junk.quality < sdk.items.quality.Set) { console.log("ÿc9UnidJunkCheckÿc0 :: Junk: " + junk.prettyPrint + " Junk type: " + junk.itemType + " Pickit Result: " + pickitResult); getToItem("UnidJunkCheck", junk) && totalJunk.push(junk); @@ -622,14 +655,16 @@ Town.clearJunk = function () { if (totalJunk.length > 0) { totalJunk - .sort((a, b) => b.getItemCost(sdk.items.cost.ToSell) - a.getItemCost(sdk.items.cost.ToSell)) - .forEach(junk => { + .sort(function (a, b) { + return b.getItemCost(sdk.items.cost.ToSell) - a.getItemCost(sdk.items.cost.ToSell); + }) + .forEach(function (item) { // extra check should ensure no pickit wanted items get sold/dropped - if (NTIP.CheckItem(junk, NTIP_CheckListNoTier) === Pickit.Result.WANTED) return; - if (junk.isInInventory || (Storage.Inventory.CanFit(junk) && Storage.Inventory.MoveTo(junk))) { - junkToSell.push(junk); + if (NTIP.CheckItem(item, NTIP.CheckList) === Pickit.Result.WANTED) return; + if (item.isInInventory || (Storage.Inventory.CanFit(item) && Storage.Inventory.MoveTo(item))) { + junkToSell.push(item); } else { - junkToDrop.push(junk); + junkToDrop.push(item); } }); @@ -638,24 +673,24 @@ Town.clearJunk = function () { if (getUIFlag(sdk.uiflags.Shop) || (Config.PacketShopping && getInteractedNPC() && getInteractedNPC().itemcount > 0)) { - for (let i = 0; i < junkToSell.length; i++) { - console.log("ÿc9JunkCheckÿc0 :: Sell " + junkToSell[i].prettyPrint); - Item.logger("Sold", junkToSell[i]); - Developer.debugging.junkCheck && Item.logItem("JunkCheck Sold", junkToSell[i]); + for (let item of junkToSell) { + console.log("ÿc9JunkCheckÿc0 :: Sell " + item.prettyPrint); + Item.logger("Sold", item); + Developer.debugging.junkCheck && Item.logItem("JunkCheck Sold", item); - junkToSell[i].sell(); + item.sell(); delay(100); } } me.cancelUIFlags(); - for (let i = 0; i < junkToDrop.length; i++) { - console.log("ÿc9JunkCheckÿc0 :: Drop " + junkToDrop[i].prettyPrint); - Item.logger("Sold", junkToDrop[i]); - Developer.debugging.junkCheck && Item.logItem("JunkCheck Sold", junkToDrop[i]); + for (let item of junkToDrop) { + console.log("ÿc9JunkCheckÿc0 :: Drop " + item.prettyPrint); + Item.logger("Sold", item); + Developer.debugging.junkCheck && Item.logItem("JunkCheck Sold", item); - junkToDrop[i].drop(); + item.drop(); delay(100); } } diff --git a/libs/SoloPlay/Utils/Init.js b/libs/SoloPlay/Utils/Init.js index 8132e5ee..a5059e64 100644 --- a/libs/SoloPlay/Utils/Init.js +++ b/libs/SoloPlay/Utils/Init.js @@ -56,11 +56,10 @@ try { me.switchWeapons(sdk.player.slot.Secondary); item.drop(); + CharData.skillData.bow.resetBowData(); } finally { me.switchWeapons(sdk.player.slot.Main); } - // Item.removeItem(null, item); - CharData.skillData.bow.resetBowData(); } }); From 464a79d826818d5af63f988658ba3d315a544121 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Mon, 5 Jun 2023 14:29:10 -0400 Subject: [PATCH 130/263] Update AttackOverrides.js - change how getIntoPosition handles checking for monsters and spots. I think this still needs work but teleporters do a better job now of always picking casting locations that are safe --- libs/SoloPlay/Functions/AttackOverrides.js | 446 +++++++++++++-------- 1 file changed, 287 insertions(+), 159 deletions(-) diff --git a/libs/SoloPlay/Functions/AttackOverrides.js b/libs/SoloPlay/Functions/AttackOverrides.js index 98bf629d..f076ec41 100644 --- a/libs/SoloPlay/Functions/AttackOverrides.js +++ b/libs/SoloPlay/Functions/AttackOverrides.js @@ -43,6 +43,10 @@ Attack.init = function () { } }; +/** + * @param {Monster} unit + * @returns { { timed: number, untimed: number }} + */ Attack.decideSkill = function (unit) { let skills = { timed: -1, untimed: -1 }; if (!unit) return skills; @@ -55,26 +59,36 @@ Attack.decideSkill = function (unit) { if (Attack.checkResist(unit, checkSkill) && Attack.validSpot(unit.x, unit.y, checkSkill, classid)) { skills.timed = checkSkill; - } else if (Config.AttackSkill[5] > -1 && Attack.checkResist(unit, Config.AttackSkill[5]) && Attack.validSpot(unit.x, unit.y, Config.AttackSkill[5], classid)) { + } else if (Config.AttackSkill[5] > -1 + && Attack.checkResist(unit, Config.AttackSkill[5]) + && Attack.validSpot(unit.x, unit.y, Config.AttackSkill[5], classid)) { skills.timed = Config.AttackSkill[5]; } // Get untimed skill - checkSkill = Attack.getCustomAttack(unit) ? Attack.getCustomAttack(unit)[1] : Config.AttackSkill[index + 1]; + checkSkill = Attack.getCustomAttack(unit) + ? Attack.getCustomAttack(unit)[1] + : Config.AttackSkill[index + 1]; if (Attack.checkResist(unit, checkSkill) && Attack.validSpot(unit.x, unit.y, checkSkill)) { skills.untimed = checkSkill; - } else if (Config.AttackSkill[6] > -1 && Attack.checkResist(unit, Config.AttackSkill[6]) && Attack.validSpot(unit.x, unit.y, Config.AttackSkill[6], classid)) { + } else if (Config.AttackSkill[6] > -1 + && Attack.checkResist(unit, Config.AttackSkill[6]) + && Attack.validSpot(unit.x, unit.y, Config.AttackSkill[6], classid)) { skills.untimed = Config.AttackSkill[6]; } // Low mana timed skill - if (Config.LowManaSkill[0] > -1 && Skill.getManaCost(skills.timed) > me.mp && Attack.checkResist(unit, Config.LowManaSkill[0])) { + if (Config.LowManaSkill[0] > -1 + && Skill.getManaCost(skills.timed) > me.mp + && Attack.checkResist(unit, Config.LowManaSkill[0])) { skills.timed = Config.LowManaSkill[0]; } // Low mana untimed skill - if (Config.LowManaSkill[1] > -1 && Skill.getManaCost(skills.untimed) > me.mp && Attack.checkResist(unit, Config.LowManaSkill[1])) { + if (Config.LowManaSkill[1] > -1 + && Skill.getManaCost(skills.untimed) > me.mp + && Attack.checkResist(unit, Config.LowManaSkill[1])) { skills.untimed = Config.LowManaSkill[1]; } @@ -82,9 +96,14 @@ Attack.decideSkill = function (unit) { }; Attack.getLowerResistPercent = function () { - const calc = (level) => Math.floor(Math.min(25 + (45 * ((110 * level) / (level + 6)) / 100), 70)); + const calc = function (level) { + return Math.floor(Math.min(25 + (45 * ((110 * level) / (level + 6)) / 100), 70)); + }; if (me.expansion && CharData.skillData.haveChargedSkillOnSwitch(sdk.skills.LowerResist)) { - return calc(CharData.skillData.chargedSkillsOnSwitch.find(chargeSkill => chargeSkill.skill === sdk.skills.LowerResist).level); + return calc( + CharData.skillData.chargedSkillsOnSwitch + .find(chargeSkill => chargeSkill.skill === sdk.skills.LowerResist).level + ); } if (Skill.canUse(sdk.skills.LowerResist)) { return calc(me.getSkill(sdk.skills.LowerResist, sdk.skills.subindex.SoftPoints)); @@ -103,7 +122,10 @@ Attack.checkResist = function (unit, val = -1, maxres = 100) { if (!unit || !unit.type || unit.type === sdk.unittype.Player) return true; const damageType = typeof val === "number" ? this.getSkillElement(val) : val; - const addLowerRes = !!(Skill.canUse(sdk.skills.LowerResist) || CharData.skillData.haveChargedSkillOnSwitch(sdk.skills.LowerResist)) && unit.curseable; + const addLowerRes = !!( + (Skill.canUse(sdk.skills.LowerResist) + || CharData.skillData.haveChargedSkillOnSwitch(sdk.skills.LowerResist)) && unit.curseable + ); // Static handler if (val === sdk.skills.StaticField && this.getResist(unit, damageType) < 100) { @@ -124,10 +146,13 @@ Attack.checkResist = function (unit, val = -1, maxres = 100) { return this.getResist(unit, damageType) < maxres; } - if (this.auradin && ["physical", "fire", "cold", "lightning"].includes(damageType) && me.getState(sdk.states.Conviction) && unit.getState) { + if (this.auradin && ["physical", "fire", "cold", "lightning"].includes(damageType) + && me.getState(sdk.states.Conviction) && unit.getState) { // our main dps is not physical despite using zeal if (damageType === "physical") return true; - if (!unit.getState(sdk.states.Conviction)) return (this.getResist(unit, damageType) - (this.getConvictionPercent() / 5) < 100); + if (!unit.getState(sdk.states.Conviction)) { + return (this.getResist(unit, damageType) - (this.getConvictionPercent() / 5) < 100); + } let valid = false; @@ -161,17 +186,20 @@ Attack.canAttack = function (unit) { if (unit.isMonster) { // Unique/Champion if (unit.isSpecial) { - if (Attack.checkResist(unit, this.getSkillElement(Config.AttackSkill[1])) || Attack.checkResist(unit, this.getSkillElement(Config.AttackSkill[2]))) { + if (Attack.checkResist(unit, this.getSkillElement(Config.AttackSkill[1])) + || Attack.checkResist(unit, this.getSkillElement(Config.AttackSkill[2]))) { return true; } } else { - if (Attack.checkResist(unit, this.getSkillElement(Config.AttackSkill[3])) || Attack.checkResist(unit, this.getSkillElement(Config.AttackSkill[4]))) { + if (Attack.checkResist(unit, this.getSkillElement(Config.AttackSkill[3])) + || Attack.checkResist(unit, this.getSkillElement(Config.AttackSkill[4]))) { return true; } } if (Config.AttackSkill.length === 7) { - return Attack.checkResist(unit, this.getSkillElement(Config.AttackSkill[5])) || Attack.checkResist(unit, this.getSkillElement(Config.AttackSkill[6])); + return Attack.checkResist(unit, this.getSkillElement(Config.AttackSkill[5])) + || Attack.checkResist(unit, this.getSkillElement(Config.AttackSkill[6])); } } @@ -466,23 +494,9 @@ Attack.clearPos = function (x, y, range = 15, pickit = true) { return true; }; -Attack.buildMonsterList = function (skipBlocked = false) { - skipBlocked === true && (skipBlocked = 0x4); - let monList = []; - let monster = Game.getMonster(); - - if (monster) { - do { - monster.attackable && monList.push(copyUnit(monster)); - } while (monster.getNext()); - } - - return skipBlocked === 0x4 ? monList.filter(mob => !checkCollision(me, mob, skipBlocked)) : monList; -}; - // Clear an entire area based on settings Attack.clearLevelEx = function (givenSettings = {}) { - function RoomSort(a, b) { + function RoomSort (a, b) { return getDistance(myRoom[0], myRoom[1], a[0], a[1]) - getDistance(myRoom[0], myRoom[1], b[0], b[1]); } @@ -539,7 +553,7 @@ Attack.clearLevelEx = function (givenSettings = {}) { // Clear an entire area until area is done or level is reached Attack.clearLevelUntilLevel = function (charlvl = undefined, spectype = 0) { - function RoomSort(a, b) { + function RoomSort (a, b) { return getDistance(myRoom[0], myRoom[1], a[0], a[1]) - getDistance(myRoom[0], myRoom[1], b[0], b[1]); } @@ -601,11 +615,11 @@ Attack.clearLevelUntilLevel = function (charlvl = undefined, spectype = 0) { /** * @description Clear monsters in a section based on range and spectype or clear monsters around a boss monster - * @param {number} range - area radius to clear around - * @param {number} spectype - type of monsters to clear - * @param {number | Monster} bossId - gid, classid, or Monster unit to clear - * @param {Function} sortfunc - how to sort the monsters we are clearing, defaults to Attack.sortMonsters - * @param {boolean} pickit - Are we allowed to pick items when we are done + * @param {number} [range] - area radius to clear around + * @param {number} [spectype] - type of monsters to clear + * @param {number | Monster} [bossId] - gid, classid, or Monster unit to clear + * @param {{ (a: Monster, b: Monster) => number }} [sortfunc] - how to sort the monsters we are clearing, defaults to Attack.sortMonsters + * @param {boolean} [pickit] - Are we allowed to pick items when we are done * @returns {AttackResult} - (0) on failure, (1) on success, (4) on no operation performed * @todo * - change to passing an object @@ -613,18 +627,22 @@ Attack.clearLevelUntilLevel = function (charlvl = undefined, spectype = 0) { * - maybe include refresh call every x amount of attacks in case the we've changed position and the monster we are targetting isn't actually the right one anymore * - should we stop clearing after boss is killed if we are using bossid? */ -Attack.clear = function (range = 25, spectype = 0, bossId = false, sortfunc = undefined, pickit = true) { +Attack.clear = function (range, spectype, bossId, sortfunc, pickit = true) { while (!me.gameReady) { delay(40); } - if (typeof (range) !== "number") throw new Error("Attack.clear: range must be a number."); if (Config.AttackSkill[1] < 0 || Config.AttackSkill[3] < 0 || Attack.stopClear) return false; + range === undefined && (range = 25); + if (typeof (range) !== "number") throw new Error("Attack.clear: range must be a number."); + spectype === undefined && (spectype = 0); + bossId === undefined && (bossId = false); const canTeleport = Pather.canTeleport(); !sortfunc && (sortfunc = canTeleport ? this.sortMonsters : Attack.walkingSortMonsters); - let i, boss, orgx, orgy, start, skillCheck; - let gidAttack = []; + /** @type {Map 7 && getDistance(me, target) <= range)) && target.attackable) { + && (getDistance(target, orgx, orgy) <= range + || (this.getScarinessLevel(target) > 7 && getDistance(me, target) <= range)) && target.attackable) { Config.Dodge && me.hpPercent <= Config.DodgeHP && this.deploy(target, Config.DodgeRange, 5, 9); tick = getTickCount(); @@ -688,8 +708,9 @@ Attack.clear = function (range = 25, spectype = 0, bossId = false, sortfunc = un logged = true; console.log("ÿc7Clear ÿc0:: " + (!!target.name ? target.name : bossId)); } - const checkMobAttackCount = gidAttack.find(g => g.gid === target.gid); - const checkAttackSkill = (!!checkMobAttackCount && checkMobAttackCount.attacks > 0 && checkMobAttackCount.attacks % 3 === 0); + + let _currMon = attacks.get(target.gid); + const checkAttackSkill = (!!_currMon && _currMon.attacks > 0 && _currMon.attacks % 3 === 0); const result = ClassAttack.doAttack(target, checkAttackSkill); if (result) { @@ -703,20 +724,19 @@ Attack.clear = function (range = 25, spectype = 0, bossId = false, sortfunc = un continue; } - for (i = 0; i < gidAttack.length; i += 1) { - if (gidAttack[i].gid === target.gid) { - break; - } + if (!_currMon) { + _currMon = { attacks: 0, name: target.name }; + attacks.set(target.gid, _currMon); } - (i === gidAttack.length) && gidAttack.push({ gid: target.gid, attacks: 0, name: target.name }); - gidAttack[i].attacks += 1; + _currMon.attacks += 1; attackCount += 1; let isSpecial = target.isSpecial; let secAttack = me.barbarian ? (isSpecial ? 2 : 4) : 5; if (Config.AttackSkill[secAttack] > -1 && (!Attack.checkResist(target, Config.AttackSkill[isSpecial ? 1 : 3]) - || (me.paladin && Config.AttackSkill[isSpecial ? 1 : 3] === sdk.skills.BlessedHammer && !ClassAttack.getHammerPosition(target)))) { + || (me.paladin && Config.AttackSkill[isSpecial ? 1 : 3] === sdk.skills.BlessedHammer + && !ClassAttack.getHammerPosition(target)))) { skillCheck = Config.AttackSkill[secAttack]; } else { skillCheck = Config.AttackSkill[isSpecial ? 1 : 3]; @@ -726,7 +746,7 @@ Attack.clear = function (range = 25, spectype = 0, bossId = false, sortfunc = un switch (skillCheck) { case sdk.skills.BlessedHammer: // Tele in random direction with Blessed Hammer - if (gidAttack[i].attacks > 0 && gidAttack[i].attacks % (isSpecial ? 4 : 2) === 0) { + if (_currMon.attacks > 0 && _currMon.attacks % (isSpecial ? 4 : 2) === 0) { let coord = CollMap.getRandCoordinate(me.x, -1, 1, me.y, -1, 1, 5); Pather.moveTo(coord.x, coord.y); } @@ -734,7 +754,8 @@ Attack.clear = function (range = 25, spectype = 0, bossId = false, sortfunc = un break; default: // Flash with melee skills - if (gidAttack[i].attacks > 0 && gidAttack[i].attacks % (isSpecial ? 15 : 5) === 0 && Skill.getRange(skillCheck) < 4) { + if (_currMon.attacks > 0 && _currMon.attacks % (isSpecial ? 15 : 5) === 0 + && Skill.getRange(skillCheck) < 4) { Packet.flash(me.gid); } @@ -742,8 +763,8 @@ Attack.clear = function (range = 25, spectype = 0, bossId = false, sortfunc = un } // Skip non-unique monsters after 15 attacks, except in Throne of Destruction - if (!me.inArea(sdk.areas.ThroneofDestruction) && !isSpecial && gidAttack[i].attacks > 15) { - console.warn("ÿc1Skipping " + target.name + " " + target.gid + " " + gidAttack[i].attacks); + if (!me.inArea(sdk.areas.ThroneofDestruction) && !isSpecial && _currMon.attacks > 15) { + console.warn("ÿc1Skipping " + target.name + " " + target.gid + " " + _currMon.attacks); monsterList.shift(); } @@ -754,7 +775,7 @@ Attack.clear = function (range = 25, spectype = 0, bossId = false, sortfunc = un killedBoss = true; console.log("ÿc7Cleared ÿc0:: " + (!!target.name ? target.name : bossId) + "ÿc0 - ÿc7Duration: ÿc0" + Time.format(getTickCount() - tick)); } - attackCount -= gidAttack[i].attacks; + attackCount -= _currMon.attacks; } if (target.dead || Config.FastPick || attackCount % 5 === 0) { Config.FastPick ? Pickit.fastPick() : Pickit.essessntialsPick(false, true); @@ -813,22 +834,26 @@ Attack.clearCoordList = function (list, pick) { Attack.checkBowOnSwitch = function (firstInit = false) { const preBow = CharData.skillData.bow.onSwitch; - const checkTypes = [sdk.items.type.AmazonBow, sdk.items.type.Bow, sdk.items.type.Crossbow, sdk.items.type.BowQuiver, sdk.items.type.CrossbowQuiver]; + const _bows = [sdk.items.type.AmazonBow, sdk.items.type.Bow, sdk.items.type.Crossbow]; + const _quivers = [sdk.items.type.BowQuiver, sdk.items.type.CrossbowQuiver]; + const checkTypes = [].concat(_bows, _quivers); me.weaponswitch !== sdk.player.slot.Main && me.switchWeapons(sdk.player.slot.Main); - const items = me.getItemsEx().filter(item => item && item.isEquipped && item.isOnSwap && checkTypes.includes(item.itemType)); - if (preBow && !items.some(item => [sdk.items.type.AmazonBow, sdk.items.type.Bow, sdk.items.type.Crossbow].includes(item.itemType))) { + const items = me.getItemsEx() + .filter(function (item) { + return item && item.isEquipped && item.isOnSwap && checkTypes.includes(item.itemType); + }); + if (preBow && !items.some(item => _bows.includes(item.itemType))) { CharData.skillData.bow.resetBowData(); return; } - items.forEach(item => { - if ([sdk.items.type.AmazonBow, sdk.items.type.Bow, sdk.items.type.Crossbow].includes(item.itemType)) { + items.forEach(function (item) { + if (_bows.includes(item.itemType)) { CharData.skillData.bow.onSwitch = true; if (CharData.skillData.bow.bowGid !== item.gid) { CharData.skillData.bow.setBowInfo(item, firstInit); } - } - if ([sdk.items.type.BowQuiver, sdk.items.type.CrossbowQuiver].includes(item.itemType)) { + } else if (_quivers.includes(item.itemType)) { if (CharData.skillData.bow.quiverType !== item.itemType) { CharData.skillData.bow.setArrowInfo(item, firstInit); } @@ -837,7 +862,9 @@ Attack.checkBowOnSwitch = function (firstInit = false) { }; Attack.haveDependancy = function (itemType) { - return [sdk.items.type.AmazonBow, sdk.items.type.Bow, sdk.items.type.Crossbow].includes(itemType) ? me.getItem("aqv", sdk.items.mode.Equipped) : me.getItem("cqv", sdk.items.mode.Equipped); + return [sdk.items.type.AmazonBow, sdk.items.type.Bow, sdk.items.type.Crossbow].includes(itemType) + ? me.getItem("aqv", sdk.items.mode.Equipped) + : me.getItem("cqv", sdk.items.mode.Equipped); }; Attack.useBowOnSwitch = function (unit, skillId = 0, switchBack = true) { @@ -862,27 +889,49 @@ Attack.getCurrentChargedSkillIds = function (init = false) { for (let i = 0; i < stats[sdk.stats.ChargedSkill].length; i += 1) { if (stats[sdk.stats.ChargedSkill][i] !== undefined) { // add to total list of skillIds - if (stats[sdk.stats.ChargedSkill][i].charges > 0 && !currentChargedSkills.includes(stats[sdk.stats.ChargedSkill][i].skill)) { + if (stats[sdk.stats.ChargedSkill][i].charges > 0 + && !currentChargedSkills.includes(stats[sdk.stats.ChargedSkill][i].skill)) { currentChargedSkills.push(stats[sdk.stats.ChargedSkill][i].skill); - chargedSkills.push(chargeSkillObj(stats[sdk.stats.ChargedSkill][i].skill, stats[sdk.stats.ChargedSkill][i].level, item.gid)); + chargedSkills.push(chargeSkillObj( + stats[sdk.stats.ChargedSkill][i].skill, + stats[sdk.stats.ChargedSkill][i].level, + item.gid) + ); } // add to switch only list for use with swtich casting - if (stats[sdk.stats.ChargedSkill][i].charges > 0 && !chargedSkillsOnSwitch.some(chargeSkill => chargeSkill.skill === stats[sdk.stats.ChargedSkill][i].skill) && item.isOnSwap) { - chargedSkillsOnSwitch.push(chargeSkillObj(stats[sdk.stats.ChargedSkill][i].skill, stats[sdk.stats.ChargedSkill][i].level, item.gid)); + if (stats[sdk.stats.ChargedSkill][i].charges > 0 + && !chargedSkillsOnSwitch.some(cSk => cSk.skill === stats[sdk.stats.ChargedSkill][i].skill) + && item.isOnSwap) { + chargedSkillsOnSwitch.push(chargeSkillObj( + stats[sdk.stats.ChargedSkill][i].skill, + stats[sdk.stats.ChargedSkill][i].level, + item.gid) + ); } } } } else { // add to total list - if (stats[sdk.stats.ChargedSkill].charges > 0 && !currentChargedSkills.includes(stats[sdk.stats.ChargedSkill].skill)) { + if (stats[sdk.stats.ChargedSkill].charges > 0 + && !currentChargedSkills.includes(stats[sdk.stats.ChargedSkill].skill)) { currentChargedSkills.push(stats[sdk.stats.ChargedSkill].skill); - chargedSkills.push(chargeSkillObj(stats[sdk.stats.ChargedSkill].skill, stats[sdk.stats.ChargedSkill].level, item.gid)); + chargedSkills.push(chargeSkillObj( + stats[sdk.stats.ChargedSkill].skill, + stats[sdk.stats.ChargedSkill].level, + item.gid) + ); } // add to switch only list for use with swtich casting - if (stats[sdk.stats.ChargedSkill].charges > 0 && !chargedSkillsOnSwitch.some(chargeSkill => chargeSkill.skill === stats[sdk.stats.ChargedSkill].skill) && item.isOnSwap) { - chargedSkillsOnSwitch.push(chargeSkillObj(stats[sdk.stats.ChargedSkill].skill, stats[sdk.stats.ChargedSkill].level, item.gid)); + if (stats[sdk.stats.ChargedSkill].charges > 0 + && !chargedSkillsOnSwitch.some(cSk => cSk.skill === stats[sdk.stats.ChargedSkill].skill) + && item.isOnSwap) { + chargedSkillsOnSwitch.push(chargeSkillObj( + stats[sdk.stats.ChargedSkill].skill, + stats[sdk.stats.ChargedSkill].level, + item.gid) + ); } } } @@ -910,7 +959,8 @@ Attack.getCurrentChargedSkillIds = function (init = false) { Attack.getItemCharges = function (skillId) { if (!skillId) return false; - let chargedItems = [], validCharge = function (itemCharge) { + let chargedItems = []; + let validCharge = function (itemCharge) { return itemCharge.skill === skillId && itemCharge.charges > 1; }; @@ -1007,15 +1057,24 @@ Attack.inverseSpotDistance = function (spot, distance, otherSpot) { otherSpot === undefined && (otherSpot = me); let x = otherSpot.x, y = otherSpot.y, area = otherSpot.area; let nodes = getPath(area, x, y, spot.x, spot.y, 2, 5); - return nodes && nodes.find((node) => node.distance > distance) || { x: x, y: y }; + return nodes && nodes.find(function (node) { + return node.distance > distance; + }) || { x: x, y: y }; }; +/** + * @param {PathNode} coord + * @param {Monster} monster + * @returns {boolean} + */ Attack.shouldDodge = function (coord, monster) { return !!monster && getUnits(sdk.unittype.Missile) - // for every missle that isnt from our merc - .filter(missile => missile && monster && monster.gid === missile.owner) - // if any - .some(missile => { + .filter(function (missile) { + // for every missle that isnt from our merc + return missile && monster && monster.gid === missile.owner; + }) + .some(function (missile) { + // if any let xoff = Math.abs(coord.x - missile.targetx); let yoff = Math.abs(coord.y - missile.targety); let xdist = Math.abs(coord.x - missile.x); @@ -1026,9 +1085,17 @@ Attack.shouldDodge = function (coord, monster) { }); }; -new Overrides.Override(Attack, Attack.sortMonsters, function(orignal, unitA, unitB) { - let stateCheck = (m) => [sdk.states.Fanaticism, sdk.states.Conviction].some(state => m.getState(state)); - if ((unitA.isSpecial && stateCheck(unitA)) && (unitB.isSpecial && stateCheck(unitB))) return getDistance(me, unitA) - getDistance(me, unitB); +new Overrides.Override(Attack, Attack.sortMonsters, function (orignal, unitA, unitB) { + /** @param {Monster} m */ + const stateCheck = function (m) { + return [sdk.states.Fanaticism, sdk.states.Conviction] + .some(function (state) { + return m.getState(state); + }); + }; + if ((unitA.isSpecial && stateCheck(unitA)) && (unitB.isSpecial && stateCheck(unitB))) { + return getDistance(me, unitA) - getDistance(me, unitB); + } if (unitA.isSpecial && stateCheck(unitA)) return -1; if (unitB.isSpecial && stateCheck(unitB)) return 1; return orignal(unitA, unitB); @@ -1060,13 +1127,19 @@ Attack.walkingSortMonsters = function (unitA, unitB) { if (unitB.getState(sdk.states.Attract)) return -1; const ids = [ - sdk.monsters.OblivionKnight1, sdk.monsters.OblivionKnight2, sdk.monsters.OblivionKnight3, sdk.monsters.FallenShaman, sdk.monsters.CarverShaman, sdk.monsters.CarverShaman2, - sdk.monsters.DevilkinShaman, sdk.monsters.DevilkinShaman2, sdk.monsters.DarkShaman1, sdk.monsters.DarkShaman2, sdk.monsters.WarpedShaman, sdk.monsters.HollowOne, sdk.monsters.Guardian1, - sdk.monsters.Guardian2, sdk.monsters.Unraveler1, sdk.monsters.Unraveler2, sdk.monsters.Ancient1, sdk.monsters.BaalSubjectMummy, sdk.monsters.BloodRaven, sdk.monsters.RatManShaman, - sdk.monsters.FetishShaman, sdk.monsters.FlayerShaman1, sdk.monsters.FlayerShaman2, sdk.monsters.SoulKillerShaman1, sdk.monsters.SoulKillerShaman2, sdk.monsters.StygianDollShaman1, - sdk.monsters.StygianDollShaman2, sdk.monsters.FleshSpawner1, sdk.monsters.FleshSpawner2, sdk.monsters.StygianHag, sdk.monsters.Grotesque1, sdk.monsters.Ancient2, sdk.monsters.Ancient3, - sdk.monsters.Grotesque2, sdk.monsters.FoulCrowNest, sdk.monsters.BlackVultureNest, sdk.monsters.BloodHawkNest, sdk.monsters.BloodHookNest, - sdk.monsters.BloodWingNest, sdk.monsters.CloudStalkerNest, sdk.monsters.FeederNest, sdk.monsters.SuckerNest + sdk.monsters.OblivionKnight1, sdk.monsters.OblivionKnight2, sdk.monsters.OblivionKnight3, + sdk.monsters.FallenShaman, sdk.monsters.CarverShaman, sdk.monsters.CarverShaman2, + sdk.monsters.DevilkinShaman, sdk.monsters.DevilkinShaman2, sdk.monsters.DarkShaman1, + sdk.monsters.DarkShaman2, sdk.monsters.WarpedShaman, sdk.monsters.HollowOne, sdk.monsters.Guardian1, + sdk.monsters.Guardian2, sdk.monsters.Unraveler1, sdk.monsters.Unraveler2, + sdk.monsters.Ancient1, sdk.monsters.BaalSubjectMummy, sdk.monsters.BloodRaven, sdk.monsters.RatManShaman, + sdk.monsters.FetishShaman, sdk.monsters.FlayerShaman1, sdk.monsters.FlayerShaman2, + sdk.monsters.SoulKillerShaman1, sdk.monsters.SoulKillerShaman2, sdk.monsters.StygianDollShaman1, + sdk.monsters.StygianDollShaman2, sdk.monsters.FleshSpawner1, sdk.monsters.FleshSpawner2, + sdk.monsters.StygianHag, sdk.monsters.Grotesque1, sdk.monsters.Ancient2, sdk.monsters.Ancient3, + sdk.monsters.Grotesque2, sdk.monsters.FoulCrowNest, sdk.monsters.BlackVultureNest, + sdk.monsters.BloodHawkNest, sdk.monsters.BloodHookNest, sdk.monsters.BloodWingNest, + sdk.monsters.CloudStalkerNest, sdk.monsters.FeederNest, sdk.monsters.SuckerNest ]; if (!me.inArea(sdk.areas.ClawViperTempleLvl2) && ids.includes(unitA.classid) && ids.includes(unitB.classid)) { @@ -1087,8 +1160,9 @@ Attack.walkingSortMonsters = function (unitA, unitB) { // fallens are annoying, put them later if we have line of sight of another monster if (unitA.isFallen && unitB.isFallen) return getDistance(me, unitA) - getDistance(me, unitB); - if (!unitA.isFallen && !checkCollision(me, unitA, (sdk.collision.BlockWall | sdk.collision.LineOfSight | sdk.collision.Ranged))) return -1; - if (!unitB.isFallen && !checkCollision(me, unitA, (sdk.collision.BlockWall | sdk.collision.LineOfSight | sdk.collision.Ranged))) return 1; + const _coll = (sdk.collision.BlockWall | sdk.collision.LineOfSight | sdk.collision.Ranged); + if (!unitA.isFallen && !checkCollision(me, unitA, _coll)) return -1; + if (!unitB.isFallen && !checkCollision(me, unitA, _coll)) return 1; return getDistance(me, unitA) - getDistance(me, unitB); }; @@ -1117,7 +1191,9 @@ Attack.pwnDury = function () { } //ToDo; figure out static if (duriel.getState(sdk.states.Frozen) && duriel.distance < 7 || duriel.distance < 12) { - let safeSpot = saveSpots.sort((a, b) => getDistance(duriel, b) - getDistance(duriel, a)).first(); + let safeSpot = saveSpots.sort(function (a, b) { + return getDistance(duriel, b) - getDistance(duriel, a); + }).first(); Pather.teleportTo(safeSpot.x, safeSpot.y); } ClassAttack.doAttack(duriel, true); @@ -1151,11 +1227,15 @@ Attack.pwnDia = function () { }); } // only unique spots - return coords.filter((e, i, s) => s.indexOf(e) === i).filter(el => Attack.validSpot(el.x, el.y)); + return coords.filter(function (e, i, s) { + return s.indexOf(e) === i; + }).filter(function (el) { + return Attack.validSpot(el.x, el.y); + }); }; const checkMobs = function () { - let mobs = getUnits(sdk.unittype.Monster).filter(function(el) { + let mobs = getUnits(sdk.unittype.Monster).filter(function (el) { return !!el && el.attackable && el.classid !== sdk.monsters.Diablo && el.distance < 20; }); return mobs; @@ -1192,7 +1272,7 @@ Attack.pwnDia = function () { switch (me.classid) { case sdk.player.class.Sorceress: [manaTP, manaSK] = [Skill.getManaCost(sdk.skills.Teleport), Skill.getManaCost(Config.AttackSkill[1])]; - [manaStatic, rangeStatic] = [Skill.getManaCost(sdk.skills.StaticField), Skill.getManaCost(sdk.skills.StaticField)]; + [manaStatic, rangeStatic] = [Skill.getManaCost(sdk.skills.StaticField), Skill.getRange(sdk.skills.StaticField)]; switch (Config.AttackSkill[1]) { case sdk.skills.FrozenOrb: @@ -1234,7 +1314,9 @@ Attack.pwnDia = function () { if (getDistance(me, dia) < minDist || getDistance(me, dia) > maxDist || getTickCount() - tick > 25e3) { let spot = calculateSpots(dia, ((minRange + maxRange) / 2)) - .filter((loc) => getDistance(me, loc) > minRange && getDistance(me, loc) < maxRange /*todo, in neighbour room*/) + .filter(function (loc) { + return getDistance(me, loc) > minRange && getDistance(me, loc) < maxRange; /*todo, in neighbour room*/ + }) .filter(function (loc) { let collision = getCollision(me.area, loc.x, loc.y); // noinspection JSBitwiseOperatorUsage @@ -1270,7 +1352,9 @@ Attack.pwnDia = function () { console.log("Diablo missiles: " + diabloMissiles.length); console.log("Diablo mode:" + dia.mode); me.overhead("Dia life " + (~~(dia.hp / 128 * 100)).toString() + "%"); - if (me.mp > manaStatic + manaTP + manaTP && diabloMissiles.length < 3 && !dia.attacking && dia.hpPercent > Config.CastStatic) { + if (me.mp > manaStatic + manaTP + manaTP + && diabloMissiles.length < 3 && !dia.attacking + && dia.hpPercent > Config.CastStatic) { let [x, y] = me; ClassAttack.switchCurse(dia, true); // curse him if we can // re-check his mode @@ -1285,7 +1369,8 @@ Attack.pwnDia = function () { } Skill.cast(Config.AttackSkill[1], sdk.skills.hand.Right, dia); - if (!!dia && !checkCollision(me, dia, Coords_1.Collision.BLOCK_MISSILE) && Skill.getRange(Config.AttackSkill[2]) > 15) { + if (!!dia && !checkCollision(me, dia, Coords_1.Collision.BLOCK_MISSILE) + && Skill.getRange(Config.AttackSkill[2]) > 15) { Skill.cast(Config.AttackSkill[2], sdk.skills.hand.Right, dia); } } @@ -1344,7 +1429,8 @@ Attack.deploy = function (unit, distance = 10, spread = 5, range = 9) { }); for (let i = 0; i < grid.length; i += 1) { - if (!(CollMap.getColl(grid[i].x, grid[i].y, true) & sdk.collision.BlockWall) && !CollMap.checkColl(unit, { x: grid[i].x, y: grid[i].y }, sdk.collision.Ranged)) { + if (!(CollMap.getColl(grid[i].x, grid[i].y, true) & sdk.collision.BlockWall) + && !CollMap.checkColl(unit, { x: grid[i].x, y: grid[i].y }, sdk.collision.Ranged)) { currCount = this.getMonsterCount(grid[i].x, grid[i].y, range, monList); if (currCount < count) { @@ -1374,6 +1460,13 @@ Attack.getIntoPosition = function (unit = false, distance = 0, coll = 0, walk = if (!unit || !unit.x || !unit.y) return false; Developer.debugging.pathing && console.time("getIntoPosition"); const useTele = Pather.useTeleport(); + const name = unit.hasOwnProperty("name") ? unit.name : ""; + const angle = Math.round(Math.atan2(me.y - unit.y, me.x - unit.x) * 180 / Math.PI); + const angles = [0, 15, -15, 30, -30, 45, -45, 60, -60, 75, -75, 90, -90, 135, -135, 180]; + const caster = (force || (distance > 4 && !me.inTown && Skill.getRange(Config.AttackSkill[1]) > 8)); + const minMonCount = caster && distance < 8 ? 1 : 0; + const _coll = (sdk.collision.WallOrRanged | sdk.collision.Objects | sdk.collision.IsOnFloor); + walk === true && (walk = 1); if (distance < 4 && (!unit.hasOwnProperty("mode") || !unit.dead)) { @@ -1384,99 +1477,124 @@ Attack.getIntoPosition = function (unit = false, distance = 0, coll = 0, walk = * for necro's use of terror and barbs use of howl/leap/leapAttack/whirlwind */ // we are actually able to walk to where we want to go, hopefully prevent wall hugging - if (walk && (unit.distance < 8 || !CollMap.checkColl(me, unit, sdk.collision.WallOrRanged | sdk.collision.Objects | sdk.collision.IsOnFloor))) { + if (walk && (unit.distance < 8 || !CollMap.checkColl(me, unit, _coll))) { Pather.walkTo(unit.x, unit.y, 3); } else if (walk && (unit.distance < 4 && CollMap.checkColl(me, unit, sdk.collision.MonsterIsOnFloorDarkArea))) { console.debug("Are we in a doorway?"); return true; } else { // don't clear while trying to reposition - Pather.moveToEx(unit.x, unit.y, { clearSettings: { allowClearing: !useTele, range: useTele ? 10 : 5 } }); + Pather.moveToEx( + unit.x, unit.y, + { clearSettings: { allowClearing: !useTele, range: useTele ? 10 : 5 } } + ); } return !CollMap.checkColl(me, unit, coll); } - let cx, cy, currCount, count = 999, potentialSpot = { x: undefined, y: undefined }; - let coords = []; + let count = 999; + let potentialSpot = { x: null, y: null }; let fullDistance = distance; - const name = unit.hasOwnProperty("name") ? unit.name : ""; - const angle = Math.round(Math.atan2(me.y - unit.y, me.x - unit.x) * 180 / Math.PI); - const angles = [0, 15, -15, 30, -30, 45, -45, 60, -60, 75, -75, 90, -90, 135, -135, 180]; - - let caster = (force && !me.inTown); - - // let t = getTickCount(); for (let n = 0; n < 3; n += 1) { - const nearMobs = getUnits(sdk.unittype.Monster).filter(m => m.getStat(sdk.stats.Alignment) !== 2); + const coords = []; + const nearMobs = getUnits(sdk.unittype.Monster) + .filter(function (m) { + return m.getStat(sdk.stats.Alignment) !== 2; + }); (n > 0) && (distance -= Math.floor(fullDistance / 3 - 1)); - for (let i = 0; i < angles.length; i += 1) { - cx = Math.round((Math.cos((angle + angles[i]) * Math.PI / 180)) * distance + unit.x); - cy = Math.round((Math.sin((angle + angles[i]) * Math.PI / 180)) * distance + unit.y); - (Pather.checkSpot(cx, cy, sdk.collision.BlockWall, false)) && coords.push({ x: cx, y: cy }); + for (let currAngle of angles) { + const _angle = ((angle + currAngle) * Math.PI / 180); + let cx = Math.round((Math.cos(_angle)) * distance + unit.x); + let cy = Math.round((Math.sin(_angle)) * distance + unit.y); + + // ignore this spot as it's too close to our current position when we are forcing a new location + if (force && [cx, cy].distance < distance) continue; + if (Pather.checkSpot(cx, cy, sdk.collision.BlockWall, false)) { + coords.push({ x: cx, y: cy }); + } } + if (!coords.length) continue; - if (coords.length > 0) { - coords.sort(Sort.units); - - // If one of the valid positions is a position I am at already - and we aren't trying to force a new spot - if (!force) { - for (let i = 0; i < coords.length; i += 1) { - if ((getDistance(me, coords[i].x, coords[i].y) < 1 - && !CollMap.checkColl(unit, { x: coords[i].x, y: coords[i].y }, sdk.collision.WallOrRanged | sdk.collision.Objects | sdk.collision.IsOnFloor, 1)) - || (getDistance(me, coords[i].x, coords[i].y) <= 5 && me.getMobCount(6) > 2)) { - return true; - } + coords.sort(Sort.units); + + // If one of the valid positions is a position I am at already - and we aren't trying to force a new spot + if (!force) { + for (let coord of coords) { + if ((getDistance(me, coord.x, coord.y) < 1 + && !CollMap.checkColl(unit, { x: coord.x, y: coord.y }, _coll, 1)) + || (getDistance(me, coord.x, coord.y) <= 5 && me.getMobCount(6) > 2)) { + return true; } } + } - for (let i = 0; i < coords.length; i += 1) { - // Valid position found - no collision between the spot and the unit - if (!CollMap.checkColl({ x: coords[i].x, y: coords[i].y }, unit, coll, 1)) { - // currCount = coords[i].mobCount({ range: 7 }); - Developer.debugging.pathing && console.time("countMobs"); - currCount = nearMobs.filter(m => getDistance(coords[i].x, coords[i].y, m.x, m.y) < 8).length; - Developer.debugging.pathing && console.timeEnd("countMobs"); - - // this might be a valid spot but also check the mob count at that node - if (caster) { - potentialSpot.x === undefined && (potentialSpot = { x: coords[i].x, y: coords[i].y }); - - if (currCount < count) { - count = currCount; - potentialSpot = { x: coords[i].x, y: coords[i].y }; - Developer.debugging.pathing && console.log(sdk.colors.Blue + "CheckedSpot" + sdk.colors.Yellow + ": x: " + coords[i].x + " y: " + coords[i].y + " mob amount: " + sdk.colors.NeonGreen + count); + for (let i = 0; i < coords.length; i++) { + // Valid position found - no collision between the spot and the unit + if (!CollMap.checkColl({ x: coords[i].x, y: coords[i].y }, unit, coll, 1)) { + Developer.debugging.pathing && console.time("countMobs"); + const currCount = nearMobs + .filter(function (m) { + return getDistance(coords[i].x, coords[i].y, m.x, m.y) < 8; + }).length; + Developer.debugging.pathing && console.timeEnd("countMobs"); + + // this might be a valid spot but also check the mob count at that node + if (caster) { + potentialSpot.x === null && (potentialSpot = { x: coords[i].x, y: coords[i].y }); + + if (currCount < count) { + count = currCount; + potentialSpot = { x: coords[i].x, y: coords[i].y }; + if (Developer.debugging.pathing) { + console.log( + sdk.colors.Blue + "CheckedSpot" + sdk.colors.Yellow + ": x: " + coords[i].x + + " y: " + coords[i].y + " mob amount: " + sdk.colors.NeonGreen + count + ); } + } - if (currCount !== 0) { - Developer.debugging.pathing && console.log(sdk.colors.Red + "Not Zero, check next: currCount: " + sdk.colors.NeonGreen + " " + currCount); - continue; + if (currCount > minMonCount) { + if (Developer.debugging.pathing) { + console.log( + sdk.colors.Red + "Not Zero, check next: currCount: " + + sdk.colors.NeonGreen + " " + currCount + ); } + continue; } + } - // I am already in my optimal position - if (coords[i].distance < 3) return true; - - // we are actually able to walk to where we want to go, hopefully prevent wall hugging - if (walk && (coords[i].distance < 6 || !CollMap.checkColl(me, unit, sdk.collision.WallOrRanged | sdk.collision.Objects | sdk.collision.IsOnFloor))) { - Pather.walkTo(coords[i].x, coords[i].y, 2); - } else { - Pather.moveToEx(coords[i].x, coords[i].y, { clearSettings: { allowClearing: !useTele, range: useTele ? 10 : 5, retry: 3 } }); - } + // I am already in my optimal position + if (coords[i].distance < 3) return true; - Developer.debugging.pathing && console.log(sdk.colors.Purple + "SecondCheck :: " + sdk.colors.Yellow + "Moving to: x: " + coords[i].x + " y: " + coords[i].y + " mob amount: " + sdk.colors.NeonGreen + currCount); - Developer.debugging.pathing && console.timeEnd("getIntoPosition"); - return true; + // we are actually able to walk to where we want to go, hopefully prevent wall hugging + if (walk && (coords[i].distance < 6 || !CollMap.checkColl(me, unit, _coll))) { + Pather.walkTo(coords[i].x, coords[i].y, 2); + } else { + Pather.moveToEx( + coords[i].x, coords[i].y, + { clearSettings: { allowClearing: !useTele, range: useTele ? 10 : 5, retry: 3 } } + ); + } + if (Developer.debugging.pathing) { + console.log( + sdk.colors.Purple + "SecondCheck :: " + sdk.colors.Yellow + + "Moving to: x: " + coords[i].x + " y: " + coords[i].y + + " mob amount: " + sdk.colors.NeonGreen + currCount + ); + console.timeEnd("getIntoPosition"); } + return true; } } } - if (caster && potentialSpot.x !== undefined) { + if (caster && potentialSpot.x !== null) { if (potentialSpot.distance < 3) return true; - if ((() => { + if ((function () { if (Pather.useTeleport() && Pather.teleportTo(potentialSpot.x, potentialSpot.y)) { return true; } @@ -1491,8 +1609,14 @@ Attack.getIntoPosition = function (unit = false, distance = 0, coll = 0, walk = return Pather.moveTo(potentialSpot.x, potentialSpot.y, 1); } })()) { - Developer.debugging.pathing && console.log(sdk.colors.Orange + "DefaultCheck :: " + sdk.colors.Yellow + "Moving to: x: " + potentialSpot.x + " y: " + potentialSpot.y + " mob amount: " + sdk.colors.NeonGreen + count); - Developer.debugging.pathing && console.timeEnd("getIntoPosition"); + if (Developer.debugging.pathing) { + console.log( + sdk.colors.Orange + "DefaultCheck :: " + sdk.colors.Yellow + + "Moving to: x: " + potentialSpot.x + " y: " + potentialSpot.y + + " mob amount: " + sdk.colors.NeonGreen + count + ); + console.timeEnd("getIntoPosition"); + } return true; } } @@ -1514,5 +1638,9 @@ Attack.castableSpot = function (x = undefined, y = undefined) { return false; } - return !(result === undefined || !!(result & Coords_1.BlockBits.Casting) || !!(result & Coords_1.Collision.BLOCK_MISSILE) || (result & sdk.collision.Objects) || (result & sdk.collision.BlockWall)); + return !(result === undefined + || !!(result & Coords_1.BlockBits.Casting) + || !!(result & Coords_1.Collision.BLOCK_MISSILE) + || (result & sdk.collision.Objects) + || (result & sdk.collision.BlockWall)); }; From af7d1a8f0538e7727c8116c50b23ffcef2b60bb5 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Mon, 5 Jun 2023 14:30:56 -0400 Subject: [PATCH 131/263] mostly refactoring arrow functions are formatting --- libs/SoloPlay/Functions/AutoMuleOverrides.js | 44 +++-- .../ClassAttackOverrides/AmazonAttacks-WIP.js | 10 +- .../ClassAttackOverrides/AmazonAttacks.js | 10 +- .../ClassAttackOverrides/BarbarianAttacks.js | 161 +++++++++++++----- libs/SoloPlay/Functions/LoaderOverrides.js | 18 +- .../SoloPlay/Functions/MuleloggerOverrides.js | 34 ++-- libs/SoloPlay/Functions/PrototypeOverrides.js | 45 +++-- libs/SoloPlay/Functions/Quest.js | 16 +- libs/SoloPlay/Workers/TownChicken.js | 20 +-- 9 files changed, 250 insertions(+), 108 deletions(-) diff --git a/libs/SoloPlay/Functions/AutoMuleOverrides.js b/libs/SoloPlay/Functions/AutoMuleOverrides.js index 8bd7fac1..006c3cfb 100644 --- a/libs/SoloPlay/Functions/AutoMuleOverrides.js +++ b/libs/SoloPlay/Functions/AutoMuleOverrides.js @@ -24,26 +24,43 @@ AutoMule.getMuleItems = function () { /** * @param {ItemUnit} item */ - const questItem = (item) => [ - sdk.items.quest.KeytotheCairnStones, sdk.items.quest.ScrollofInifuss, sdk.items.quest.HoradricMalus, sdk.items.quest.WirtsLeg, - sdk.items.quest.HoradricStaff, sdk.items.quest.ShaftoftheHoradricStaff, sdk.items.quest.ViperAmulet, sdk.items.quest.Cube, - sdk.items.quest.KhalimsBrain, sdk.items.quest.KhalimsEye, sdk.items.quest.KhalimsHeart, sdk.items.quest.KhalimsFlail, - sdk.items.quest.DecoyGidbinn, sdk.items.quest.TheGidbinn, sdk.items.quest.KhalimsWill, sdk.items.quest.PotofLife, - sdk.items.quest.MephistosSoulstone, sdk.items.quest.HellForgeHammer, - sdk.items.quest.MalahsPotion, sdk.items.quest.ScrollofResistance, - ].includes(item.classid); + const questItem = function (item) { + return [ + sdk.items.quest.KeytotheCairnStones, sdk.items.quest.ScrollofInifuss, + sdk.items.quest.HoradricMalus, sdk.items.quest.WirtsLeg, + sdk.items.quest.HoradricStaff, sdk.items.quest.ShaftoftheHoradricStaff, + sdk.items.quest.ViperAmulet, sdk.items.quest.Cube, + sdk.items.quest.KhalimsBrain, sdk.items.quest.KhalimsEye, + sdk.items.quest.KhalimsHeart, sdk.items.quest.KhalimsFlail, + sdk.items.quest.DecoyGidbinn, sdk.items.quest.TheGidbinn, + sdk.items.quest.KhalimsWill, sdk.items.quest.PotofLife, + sdk.items.quest.MephistosSoulstone, sdk.items.quest.HellForgeHammer, + sdk.items.quest.MalahsPotion, sdk.items.quest.ScrollofResistance, + ].includes(item.classid); + }; /** * @param {ItemUnit} item */ - const isAKey = (item) => [sdk.items.quest.KeyofTerror, sdk.items.quest.KeyofHate, sdk.items.quest.KeyofDestruction].includes(item.classid); + const isAKey = function (item) { + return [ + sdk.items.quest.KeyofTerror, + sdk.items.quest.KeyofHate, + sdk.items.quest.KeyofDestruction + ].includes(item.classid); + }; /** * check if wanted by any of the systems * @param {ItemUnit} item * @returns {boolean} if item is wanted by various systems */ - const isWanted = (item) => (AutoMule.cubingIngredient(item) || AutoMule.runewordIngredient(item) || AutoMule.utilityIngredient(item) || SoloWants.keepItem(item)); + const isWanted = function (item) { + return (AutoMule.cubingIngredient(item) + || AutoMule.runewordIngredient(item) + || AutoMule.utilityIngredient(item) + || SoloWants.keepItem(item)); + }; // lets be more explicit about what we want to mule let items = me.getItemsEx() @@ -60,13 +77,16 @@ AutoMule.getMuleItems = function () { if (item.isInInventory && Storage.Inventory.IsLocked(item, Config.Inventory)) return false; // don't mule items wanted by one of the various systems - checks that it's not on the force mule list // might be worth it to ignore force for soloplay in this case, muleing an item we need would slow down progression - if (isWanted(item) && !AutoMule.matchItem(item, Config.AutoMule.Force.concat(Config.AutoMule.Trigger))) return false; + if (isWanted(item) && !AutoMule.matchItem(item, Config.AutoMule.Force.concat(Config.AutoMule.Trigger))) { + return false; + } // don't mule keys if part of torchsystem, again shouldn't really be used with soloplay but still including it if (isAKey(item) && TorchSystem.getFarmers() && TorchSystem.isFarmer()) return false; // we've gotten this far, mule items that are on the force list if (AutoMule.matchItem(item, Config.AutoMule.Force.concat(Config.AutoMule.Trigger))) return true; // alright that handles the basics -- now normal pickit check - return (Pickit.checkItem(item).result > 0 && NTIP.CheckItem(item, NTIP_CheckListNoTier, true).result === 1) || (item.isInStash && muleOrphans); + return (Pickit.checkItem(item).result > 0 + && NTIP.CheckItem(item, NTIP.CheckList, true).result === 1) || (item.isInStash && muleOrphans); }); return items; diff --git a/libs/SoloPlay/Functions/ClassAttackOverrides/AmazonAttacks-WIP.js b/libs/SoloPlay/Functions/ClassAttackOverrides/AmazonAttacks-WIP.js index 4874429a..5a747850 100644 --- a/libs/SoloPlay/Functions/ClassAttackOverrides/AmazonAttacks-WIP.js +++ b/libs/SoloPlay/Functions/ClassAttackOverrides/AmazonAttacks-WIP.js @@ -387,7 +387,10 @@ ClassAttack.doCast = function (unit, timedSkill, untimedSkill) { if (unit.distance > Skill.getRange(timedSkill) || checkCollision(me, unit, sdk.collision.Ranged)) { // Allow short-distance walking for melee skills - walk = Skill.getRange(timedSkill) < 4 && unit.distance < 10 && !checkCollision(me, unit, sdk.collision.BlockWall); + walk = (Skill.getRange(timedSkill) < 4 + && unit.distance < 10 + && !checkCollision(me, unit, sdk.collision.BlockWall) + ); if (!Attack.getIntoPosition(unit, Skill.getRange(timedSkill), sdk.collision.Ranged, walk)) { return Attack.Result.FAILED; @@ -405,7 +408,10 @@ ClassAttack.doCast = function (unit, timedSkill, untimedSkill) { if (unit.distance > Skill.getRange(untimedSkill) || checkCollision(me, unit, sdk.collision.Ranged)) { // Allow short-distance walking for melee skills - walk = Skill.getRange(untimedSkill) < 4 && unit.distance < 10 && !checkCollision(me, unit, sdk.collision.BlockWall); + walk = (Skill.getRange(untimedSkill) < 4 + && unit.distance < 10 + && !checkCollision(me, unit, sdk.collision.BlockWall) + ); if (!Attack.getIntoPosition(unit, Skill.getRange(untimedSkill), sdk.collision.Ranged, walk)) { return Attack.Result.FAILED; diff --git a/libs/SoloPlay/Functions/ClassAttackOverrides/AmazonAttacks.js b/libs/SoloPlay/Functions/ClassAttackOverrides/AmazonAttacks.js index b377f829..4100563a 100644 --- a/libs/SoloPlay/Functions/ClassAttackOverrides/AmazonAttacks.js +++ b/libs/SoloPlay/Functions/ClassAttackOverrides/AmazonAttacks.js @@ -404,7 +404,10 @@ ClassAttack.doCast = function (unit, timedSkill, untimedSkill) { if (unit.distance > Skill.getRange(timedSkill) || checkCollision(me, unit, sdk.collision.Ranged)) { // Allow short-distance walking for melee skills - walk = Skill.getRange(timedSkill) < 4 && unit.distance < 10 && !checkCollision(me, unit, sdk.collision.BlockWall); + walk = (Skill.getRange(timedSkill) < 4 + && unit.distance < 10 + && !checkCollision(me, unit, sdk.collision.BlockWall) + ); if (!Attack.getIntoPosition(unit, Skill.getRange(timedSkill), sdk.collision.Ranged, walk)) { return Attack.Result.FAILED; @@ -422,7 +425,10 @@ ClassAttack.doCast = function (unit, timedSkill, untimedSkill) { if (unit.distance > Skill.getRange(untimedSkill) || checkCollision(me, unit, sdk.collision.Ranged)) { // Allow short-distance walking for melee skills - walk = Skill.getRange(untimedSkill) < 4 && unit.distance < 10 && !checkCollision(me, unit, sdk.collision.BlockWall); + walk = (Skill.getRange(untimedSkill) < 4 + && unit.distance < 10 + && !checkCollision(me, unit, sdk.collision.BlockWall) + ); if (!Attack.getIntoPosition(unit, Skill.getRange(untimedSkill), sdk.collision.Ranged, walk)) { return Attack.Result.FAILED; diff --git a/libs/SoloPlay/Functions/ClassAttackOverrides/BarbarianAttacks.js b/libs/SoloPlay/Functions/ClassAttackOverrides/BarbarianAttacks.js index d5ac024f..d819b330 100644 --- a/libs/SoloPlay/Functions/ClassAttackOverrides/BarbarianAttacks.js +++ b/libs/SoloPlay/Functions/ClassAttackOverrides/BarbarianAttacks.js @@ -18,18 +18,26 @@ includeIfNotIncluded("core/Attacks/Barbarian.js"); const howlCheck = function () { let levelCheck = (me.getSkill(sdk.skills.Howl, sdk.skills.subindex.SoftPoints) + me.charlvl + 1); - return getUnits(sdk.unittype.Monster).filter(function (el) { - return (!!el && el.attackable && el.distance < 6 && el.scareable && GameData.monsterLevel(el.classid, me.area) < levelCheck && !el.isStunned - && [sdk.states.BattleCry, sdk.states.AmplifyDamage, sdk.states.Decrepify, sdk.states.Terror, sdk.states.Taunt].every(state => !el.getState(state)) - && !checkCollision(me, el, Coords_1.Collision.BLOCK_MISSILE)); - }).length > me.maxNearMonsters; + return getUnits(sdk.unittype.Monster) + .filter(function (el) { + return (!!el && el.attackable && el.distance < 6 + && el.scareable && GameData.monsterLevel(el.classid, me.area) < levelCheck && !el.isStunned + && [ + sdk.states.BattleCry, sdk.states.AmplifyDamage, + sdk.states.Decrepify, sdk.states.Terror, sdk.states.Taunt + ].every(state => !el.getState(state)) + && !checkCollision(me, el, Coords_1.Collision.BLOCK_MISSILE)); + }).length > me.maxNearMonsters; }; const battleCryCheck = function () { return getUnits(sdk.unittype.Monster).some(function (el) { if (el === undefined) return false; return (el.attackable && el.distance < 5 && el.curseable - && [sdk.states.BattleCry, sdk.states.AmplifyDamage, sdk.states.Decrepify, sdk.states.Terror, sdk.states.Taunt].every(state => !el.getState(state)) + && [ + sdk.states.BattleCry, sdk.states.AmplifyDamage, + sdk.states.Decrepify, sdk.states.Terror, sdk.states.Taunt + ].every(state => !el.getState(state)) && !checkCollision(me, el, Coords_1.Collision.BLOCK_MISSILE)); }); }; @@ -38,9 +46,15 @@ includeIfNotIncluded("core/Attacks/Barbarian.js"); return getUnits(sdk.unittype.Monster).some(function (el) { if (el === undefined) return false; return (el.attackable && el.distance < 5 && !(el.isSpecial) && el.curseable - && ![sdk.monsters.Andariel, sdk.monsters.Duriel, sdk.monsters.Mephisto, sdk.monsters.Diablo, sdk.monsters.Baal, sdk.monsters.Tentacle1, - sdk.monsters.BaalClone, sdk.monsters.KorlictheProtector, sdk.monsters.TalictheDefender, sdk.monsters.MadawctheGuardian].includes(el.classid) - && (!el.isStunned || getTickCount() - ClassAttack.warCryTick >= 1500) && !checkCollision(me, el, Coords_1.Collision.BLOCK_MISSILE)); + && ![ + sdk.monsters.Andariel, sdk.monsters.Duriel, + sdk.monsters.Mephisto, sdk.monsters.Diablo, + sdk.monsters.Baal, sdk.monsters.Tentacle1, + sdk.monsters.BaalClone, sdk.monsters.KorlictheProtector, + sdk.monsters.TalictheDefender, sdk.monsters.MadawctheGuardian + ].includes(el.classid) + && (!el.isStunned || getTickCount() - ClassAttack.warCryTick >= 1500) + && !checkCollision(me, el, Coords_1.Collision.BLOCK_MISSILE)); }); }; @@ -54,29 +68,53 @@ includeIfNotIncluded("core/Attacks/Barbarian.js"); let range = (!me.inArea(sdk.areas.ThroneofDestruction) ? 15 : 30); let rangedMobsClassIDs = [ - sdk.monsters.Afflicted, sdk.monsters.Tainted, sdk.monsters.Misshapen1, sdk.monsters.Disfigured, sdk.monsters.Damned1, sdk.monsters.Gloam1, sdk.monsters.SwampGhost, - sdk.monsters.BurningSoul2, sdk.monsters.BlackSoul1, sdk.monsters.GhoulLord1, sdk.monsters.NightLord, sdk.monsters.DarkLord1, sdk.monsters.BloodLord1, - sdk.monsters.Banished, sdk.monsters.SkeletonArcher, sdk.monsters.ReturnedArcher1, sdk.monsters.BoneArcher1, sdk.monsters.BurningDeadArcher1, sdk.monsters.HorrorArcher1, - sdk.monsters.Sexton, sdk.monsters.Cantor, sdk.monsters.Heirophant1, sdk.monsters.DoomKnight, sdk.monsters.VenomLord1, sdk.monsters.Horror1, sdk.monsters.Horror2, - sdk.monsters.Horror3, sdk.monsters.Horror4, sdk.monsters.Horror5, sdk.monsters.Lord1, sdk.monsters.Lord2, sdk.monsters.Lord3, sdk.monsters.Lord4, - sdk.monsters.Lord4, sdk.monsters.Afflicted2, sdk.monsters.Tainted, sdk.monsters.Misshapen2, sdk.monsters.Disfigured2, sdk.monsters.Damned2, sdk.monsters.DarkShaman2, + sdk.monsters.Afflicted, sdk.monsters.Tainted, + sdk.monsters.Misshapen1, sdk.monsters.Disfigured, + sdk.monsters.Damned1, sdk.monsters.Gloam1, + sdk.monsters.SwampGhost, sdk.monsters.BurningSoul2, + sdk.monsters.BlackSoul1, sdk.monsters.GhoulLord1, + sdk.monsters.NightLord, sdk.monsters.DarkLord1, sdk.monsters.BloodLord1, + sdk.monsters.Banished, sdk.monsters.SkeletonArcher, + sdk.monsters.ReturnedArcher1, sdk.monsters.BoneArcher1, + sdk.monsters.BurningDeadArcher1, sdk.monsters.HorrorArcher1, + sdk.monsters.Sexton, sdk.monsters.Cantor, + sdk.monsters.Heirophant1, sdk.monsters.DoomKnight, + sdk.monsters.VenomLord1, sdk.monsters.Horror1, sdk.monsters.Horror2, + sdk.monsters.Horror3, sdk.monsters.Horror4, + sdk.monsters.Horror5, sdk.monsters.Lord1, + sdk.monsters.Lord2, sdk.monsters.Lord3, sdk.monsters.Lord4, + sdk.monsters.Lord4, sdk.monsters.Afflicted2, + sdk.monsters.Tainted, sdk.monsters.Misshapen2, + sdk.monsters.Disfigured2, sdk.monsters.Damned2, sdk.monsters.DarkShaman2, sdk.monsters.DevilkinShaman, sdk.monsters.DarkShaman2, sdk.monsters.DarkLord2 ]; let dangerousAndSummoners = [ - sdk.monsters.Dominus2, sdk.monsters.Witch1, sdk.monsters.VileWitch2, sdk.monsters.Gloam2, sdk.monsters.BlackSoul2, sdk.monsters.BurningSoul1, - sdk.monsters.FallenShaman, sdk.monsters.CarverShaman2, sdk.monsters.DevilkinShaman2, sdk.monsters.DarkShaman1, sdk.monsters.HollowOne, sdk.monsters.Guardian1, - sdk.monsters.Unraveler1, sdk.monsters.Ancient1, sdk.monsters.BaalSubjectMummy, sdk.monsters.Council4, sdk.monsters.VenomLord2, sdk.monsters.Ancient2, - sdk.monsters.Ancient3, sdk.monsters.Succubusexp1, sdk.monsters.VileTemptress, sdk.monsters.StygianHarlot, sdk.monsters.Temptress1, sdk.monsters.Temptress2, - sdk.monsters.Dominus1, sdk.monsters.VileWitch1, sdk.monsters.StygianFury, sdk.monsters.Witch2, sdk.monsters.Witch3 + sdk.monsters.Dominus2, sdk.monsters.Witch1, + sdk.monsters.VileWitch2, sdk.monsters.Gloam2, + sdk.monsters.BlackSoul2, sdk.monsters.BurningSoul1, + sdk.monsters.FallenShaman, sdk.monsters.CarverShaman2, + sdk.monsters.DevilkinShaman2, sdk.monsters.DarkShaman1, + sdk.monsters.HollowOne, sdk.monsters.Guardian1, + sdk.monsters.Unraveler1, sdk.monsters.Ancient1, + sdk.monsters.BaalSubjectMummy, sdk.monsters.Council4, + sdk.monsters.VenomLord2, sdk.monsters.Ancient2, + sdk.monsters.Ancient3, sdk.monsters.Succubusexp1, + sdk.monsters.VileTemptress, sdk.monsters.StygianHarlot, + sdk.monsters.Temptress1, sdk.monsters.Temptress2, + sdk.monsters.Dominus1, sdk.monsters.VileWitch1, + sdk.monsters.StygianFury, sdk.monsters.Witch2, sdk.monsters.Witch3 ]; - [sdk.areas.RiverofFlame, sdk.areas.ChaosSanctuary].includes(me.area) && rangedMobsClassIDs.push(sdk.monsters.Strangler1, sdk.monsters.StormCaster1); + if ([sdk.areas.RiverofFlame, sdk.areas.ChaosSanctuary].includes(me.area)) { + rangedMobsClassIDs.push(sdk.monsters.Strangler1, sdk.monsters.StormCaster1); + } let list = getUnits(sdk.unittype.Monster) .filter(function (mob) { return ([sdk.monsters.spectype.All, sdk.monsters.spectype.Minion].includes(mob.spectype) && [sdk.states.BattleCry, sdk.states.Decrepify, sdk.states.Taunt].every(state => !mob.getState(state)) - && ((rangedMobsClassIDs.includes(mob.classid) && mob.distance <= range) || (dangerousAndSummoners.includes(mob.classid) && mob.distance <= 30))); + && ((rangedMobsClassIDs.includes(mob.classid) && mob.distance <= range) + || (dangerousAndSummoners.includes(mob.classid) && mob.distance <= 30))); }) .sort(Sort.units); @@ -93,7 +131,11 @@ includeIfNotIncluded("core/Attacks/Barbarian.js"); Skill.cast(sdk.skills.WarCry, sdk.skills.hand.Right); } - if (!!currMob && !currMob.dead && [sdk.states.Terror, sdk.states.BattleCry, sdk.states.Decrepify, sdk.states.Taunt].every(state => !currMob.getState(state)) + if (!!currMob && !currMob.dead + && [ + sdk.states.Terror, sdk.states.BattleCry, + sdk.states.Decrepify, sdk.states.Taunt + ].every(state => !currMob.getState(state)) && data.taunt.mana < me.mp && !Coords_1.isBlockedBetween(me, currMob)) { me.overhead("Taunting: " + currMob.name + " | classid: " + currMob.classid); Skill.cast(sdk.skills.Taunt, sdk.skills.hand.Right, currMob); @@ -128,24 +170,31 @@ includeIfNotIncluded("core/Attacks/Barbarian.js"); } const index = (unit.isSpecial || unit.isPlayer) ? 1 : 3; - let attackSkill = Attack.getCustomAttack(unit) ? Attack.getCustomAttack(unit)[0] : Config.AttackSkill[index]; + let attackSkill = Attack.getCustomAttack(unit) + ? Attack.getCustomAttack(unit)[0] + : Config.AttackSkill[index]; if (!Attack.checkResist(unit, attackSkill)) { attackSkill = -1; - if (Config.AttackSkill[index + 1] > -1 && Skill.canUse(Config.AttackSkill[index + 1]) && Attack.checkResist(unit, Config.AttackSkill[index + 1])) { + if (Config.AttackSkill[index + 1] > -1 + && Skill.canUse(Config.AttackSkill[index + 1]) + && Attack.checkResist(unit, Config.AttackSkill[index + 1])) { attackSkill = Config.AttackSkill[index + 1]; } } if (me.expansion && index === 1 && !unit.dead) { - if (CharData.skillData.haveChargedSkill(sdk.skills.SlowMissiles) && unit.getEnchant(sdk.enchant.LightningEnchanted) && !unit.getState(sdk.states.SlowMissiles) && unit.curseable && + if (CharData.skillData.haveChargedSkill(sdk.skills.SlowMissiles) + && unit.getEnchant(sdk.enchant.LightningEnchanted) + && !unit.getState(sdk.states.SlowMissiles) && unit.curseable && (gold > 500000 && !unit.isBoss) && !checkCollision(me, unit, sdk.collision.Ranged)) { // Cast slow missiles Attack.castCharges(sdk.skills.SlowMissiles, unit); } - if (CharData.skillData.haveChargedSkill(sdk.skills.InnerSight) && !unit.getState(sdk.states.InnerSight) && unit.curseable && + if (CharData.skillData.haveChargedSkill(sdk.skills.InnerSight) + && !unit.getState(sdk.states.InnerSight) && unit.curseable && gold > 500000 && !checkCollision(me, unit, sdk.collision.Ranged)) { // Cast slow missiles Attack.castCharges(sdk.skills.InnerSight, unit); @@ -186,18 +235,22 @@ includeIfNotIncluded("core/Attacks/Barbarian.js"); // console.debug(data); // Low mana skill - if (Skill.getManaCost(attackSkill) > me.mp && Config.LowManaSkill[0] > -1 && Attack.checkResist(unit, Config.LowManaSkill[0])) { + if (Skill.getManaCost(attackSkill) > me.mp + && Config.LowManaSkill[0] > -1 + && Attack.checkResist(unit, Config.LowManaSkill[0])) { attackSkill = Config.LowManaSkill[0]; } - if ([sdk.skills.DoubleSwing, sdk.skills.DoubleThrow, sdk.skills.Frenzy].includes(attackSkill) && !me.dualWielding || !Skill.canUse(attackSkill)) { + if ([sdk.skills.DoubleSwing, sdk.skills.DoubleThrow, sdk.skills.Frenzy].includes(attackSkill) + && !me.dualWielding || !Skill.canUse(attackSkill)) { let oneHandSk = [data.bash, data.stun, data.concentrate, data.leapAttack, data.whirlwind] .filter((skill) => skill.have && me.mp > skill.mana) .sort((a, b) => GameData.physicalAttackDamage(b.skill) - GameData.physicalAttackDamage(a.skill)).first(); attackSkill = oneHandSk ? oneHandSk.skill : 0; } - if (data.howl.have && attackSkill !== sdk.skills.Whirlwind && data.howl.mana < me.mp && howlCheck() && me.hpPercent <= 85) { + if (data.howl.have && attackSkill !== sdk.skills.Whirlwind + && data.howl.mana < me.mp && howlCheck() && me.hpPercent <= 85) { data.grimWard.have ? this.grimWard(6) : Skill.cast(sdk.skills.Howl, sdk.skills.hand.Right); } @@ -205,7 +258,10 @@ includeIfNotIncluded("core/Attacks/Barbarian.js"); if (!unit.dead && data.battleCry.have && !me.skillDelay) { // Unit not already in Battle Cry, decrepify, terror, or taunt state. Don't want to overwrite helpful cureses - if ([sdk.states.BattleCry, sdk.states.Decrepify, sdk.states.Terror, sdk.states.Taunt].every(state => !unit.getState(state))) { + if ([ + sdk.states.BattleCry, sdk.states.Decrepify, + sdk.states.Terror, sdk.states.Taunt + ].every(state => !unit.getState(state))) { if (unit.distance > data.battleCry.range || checkCollision(me, unit, sdk.collision.Ranged)) { if (!Attack.getIntoPosition(unit, data.battleCry.range, sdk.collision.Ranged)) { return Attack.Result.FAILED; @@ -222,13 +278,17 @@ includeIfNotIncluded("core/Attacks/Barbarian.js"); // TODO: write GameData.killableSummonsByWarCry if (data.warCry.have && data.warCry.mana < me.mp && !me.skillDelay && warCryCheck()) { - data.switchCast ? Skill.switchCast(sdk.skills.WarCry, { hand: 0 }) : Skill.cast(sdk.skills.WarCry, sdk.skills.hand.Right, unit); + data.switchCast + ? Skill.switchCast(sdk.skills.WarCry, { hand: 0 }) + : Skill.cast(sdk.skills.WarCry, sdk.skills.hand.Right, unit); this.warCryTick = getTickCount(); } // Probably going to get rid of preattack - if (preattack && Config.AttackSkill[0] > 0 && Config.AttackSkill[0] !== sdk.skills.WarCry && Skill.canUse(Config.AttackSkill[0]) - && Attack.checkResist(unit, Attack.getSkillElement(Config.AttackSkill[0])) && (Skill.getManaCost(Config.AttackSkill[0]) < me.mp) + if (preattack && Config.AttackSkill[0] > 0 + && Config.AttackSkill[0] !== sdk.skills.WarCry && Skill.canUse(Config.AttackSkill[0]) + && Attack.checkResist(unit, Attack.getSkillElement(Config.AttackSkill[0])) + && (Skill.getManaCost(Config.AttackSkill[0]) < me.mp) && (!me.getState(sdk.states.SkillDelay) || !Skill.isTimed(Config.AttackSkill[0]))) { if (unit.distance > Skill.getRange(Config.AttackSkill[0]) || checkCollision(me, unit, sdk.collision.Ranged)) { if (!Attack.getIntoPosition(unit, Skill.getRange(Config.AttackSkill[0]), sdk.collision.Ranged)) { @@ -243,11 +303,17 @@ includeIfNotIncluded("core/Attacks/Barbarian.js"); if (index === 1) { if (data.howl.have && attackSkill !== sdk.skills.Whirlwind && data.howl.mana < me.mp && howlCheck()) { - data.grimWard.have ? this.grimWard(6) : !data.warCry.have ? Skill.cast(sdk.skills.Howl, Skill.getHand(sdk.skills.Howl)) : null; + data.grimWard.have + ? this.grimWard(6) + : !data.warCry.have + ? Skill.cast(sdk.skills.Howl, Skill.getHand(sdk.skills.Howl)) + : null; } } - if (attackSkill === sdk.skills.DoubleThrow && (me.getWeaponQuantity() <= 3 || me.getWeaponQuantity(sdk.body.LeftArm) <= 3) && data.secondary.have) { + if (attackSkill === sdk.skills.DoubleThrow + && (me.getWeaponQuantity() <= 3 || me.getWeaponQuantity(sdk.body.LeftArm) <= 3) + && data.secondary.have) { attackSkill = data.secondary.skill; } @@ -261,8 +327,6 @@ includeIfNotIncluded("core/Attacks/Barbarian.js"); // No attack skill if (attackSkill < 0 || !data) return Attack.Result.CANTATTACK; - let walk; - switch (attackSkill) { case sdk.skills.Whirlwind: if (unit.distance > Skill.getRange(attackSkill) || checkCollision(me, unit, sdk.collision.BlockWall)) { @@ -280,7 +344,11 @@ includeIfNotIncluded("core/Attacks/Barbarian.js"); } if (unit.distance > Skill.getRange(attackSkill) || checkCollision(me, unit, sdk.collision.Ranged)) { - walk = (Skill.getRange(attackSkill) < 4 && unit.distance < 10 && !checkCollision(me, unit, sdk.collision.BlockWall)); + let walk = ( + Skill.getRange(attackSkill) < 4 + && unit.distance < 10 + && !checkCollision(me, unit, sdk.collision.BlockWall) + ); // think this should be re-written in pather with some form of leap pathing similar to teleport // leap/leap attack is incredibly useful because we can leap straight to chaos or over mobs/doors/some walls ect @@ -301,7 +369,8 @@ includeIfNotIncluded("core/Attacks/Barbarian.js"); Skill.cast(sdk.skills.Frenzy, Skill.getHand(sdk.skills.Frenzy), unit); } - if (!unit.dead && attackSkill === sdk.skills.Berserk && data.concentrate.have && me.mp > data.concentrate.mana) { + if (!unit.dead && attackSkill === sdk.skills.Berserk + && data.concentrate.have && me.mp > data.concentrate.mana) { Skill.cast(sdk.skills.Concentrate, Skill.getHand(sdk.skills.Concentrate), unit); } @@ -429,8 +498,12 @@ includeIfNotIncluded("core/Attacks/Barbarian.js"); if (!unit || !unit.dead) return false; let corpseList = getUnits(sdk.unittype.Monster) - .filter(mon => mon.dead && mon.distance < 30 && getDistance(mon, unit) <= range && this.checkCorpse(mon)) - .sort(((a, b) => getDistance(a, unit) - getDistance(b, unit))); + .filter(function (mon) { + return mon.dead && mon.distance < 30 && getDistance(mon, unit) <= range && this.checkCorpse(mon); + }) + .sort(function (a, b) { + return getDistance(a, unit) - getDistance(b, unit); + }); for (let corpse of corpseList) { // corpseList uses copyUnit, so we need to get the actual corpse @@ -439,7 +512,9 @@ includeIfNotIncluded("core/Attacks/Barbarian.js"); if (checkCorpse && this.checkCorpse(checkCorpse)) { for (let j = 0; j < 3; j += 1) { if (Skill.cast(sdk.skills.GrimWard, sdk.skills.hand.Right, checkCorpse)) { - if (Misc.poll(() => checkCorpse.getState(sdk.states.CorpseNoSelect), 1000)) { + if (Misc.poll(function () { + return checkCorpse.getState(sdk.states.CorpseNoSelect); + }, 1000)) { return true; } } diff --git a/libs/SoloPlay/Functions/LoaderOverrides.js b/libs/SoloPlay/Functions/LoaderOverrides.js index d6e07b3d..39b3de5d 100644 --- a/libs/SoloPlay/Functions/LoaderOverrides.js +++ b/libs/SoloPlay/Functions/LoaderOverrides.js @@ -68,16 +68,21 @@ Loader.run = function () { (j === 5) && myPrint("script " + scriptName + " failed."); } catch (e) { - console.warn("ÿc8Kolbot-SoloPlayÿc0: ", (typeof e === "object" ? e.message : e)); console.error(e); + // console.log("ÿc8Kolbot-SoloPlayÿc0: ", e); } finally { SoloIndex.doneList.push(scriptName); // skip logging if we didn't actually finish it - !SoloIndex.retryList.includes(scriptName) && Developer.logPerformance && Tracker.script(tick, scriptName, currentExp); + if (!SoloIndex.retryList.includes(scriptName) && Developer.logPerformance) { + Tracker.script(tick, scriptName, currentExp); + } console.log("ÿc8Kolbot-SoloPlayÿc0: Old maxgametime: " + Time.format(me.maxgametime)); me.maxgametime += (getTickCount() - tick); console.log("ÿc8Kolbot-SoloPlayÿc0: New maxgametime: " + Time.format(me.maxgametime)); - console.log("ÿc8Kolbot-SoloPlayÿc0 :: ÿc8" + scriptName + "ÿc0 - ÿc7Duration: ÿc0" + Time.format(getTickCount() - tick)); + console.log( + "ÿc8Kolbot-SoloPlayÿc0 :: ÿc8" + scriptName + + "ÿc0 - ÿc7Duration: ÿc0" + Time.format(getTickCount() - tick) + ); // remove script function from function scope, so it can be cleared by GC if (this.scriptIndex < SoloIndex.scripts.length) { @@ -86,7 +91,7 @@ Loader.run = function () { } if (me.sorceress && me.hell && scriptName === "bloodraven" && me.charlvl < 68) { - console.debug("End-run, we are not ready to keep pushing yet"); + console.info(false, "End-run, we are not ready to keep pushing yet"); break; } @@ -152,7 +157,10 @@ Loader.runScript = function (script, configOverride) { currentExp = me.getStat(sdk.stats.Experience); if (global[script]()) { - console.log(mainScriptStr + "ÿc7" + script + " :: ÿc0Complete ÿc0- ÿc7Duration: ÿc0" + (Time.format(getTickCount() - tick))); + console.log( + mainScriptStr + "ÿc7" + script + + " :: ÿc0Complete ÿc0- ÿc7Duration: ÿc0" + (Time.format(getTickCount() - tick)) + ); } } } catch (e) { diff --git a/libs/SoloPlay/Functions/MuleloggerOverrides.js b/libs/SoloPlay/Functions/MuleloggerOverrides.js index 17f2dbed..a98ceaed 100644 --- a/libs/SoloPlay/Functions/MuleloggerOverrides.js +++ b/libs/SoloPlay/Functions/MuleloggerOverrides.js @@ -24,7 +24,7 @@ MuleLogger.logItem = function (unit, logIlvl, type = "Player") { let color = (unit.getColor() || -1); let code = Item.getItemCode(unit); - if (NTIP.GetMercTier(unit) > 0 || NTIP.GetTier(unit) > 0 || NTIP.GetCharmTier(unit) > 0 || NTIP.GetSecondaryTier(unit) > 0) { + if (AutoEquip.hasTier(unit)) { if (unit.mode === sdk.items.mode.inStorage && type === "Player") { if (unit.isCharm) { desc += ("\n\\xffc0Autoequip charm tier: " + NTIP.GetCharmTier(unit)); @@ -71,9 +71,14 @@ MuleLogger.logEquippedItems = function () { let folder, string, parsedItem; let realm = me.realm || "Single Player"; let finalString = ""; - let items = me.getItemsEx().filter(item => item.isEquipped || item.isEquippedCharm || (item.isInStorage && item.itemType === sdk.items.type.Rune)); + let items = me.getItemsEx() + .filter(function (item) { + return item.isEquipped || item.isEquippedCharm || (item.isInStorage && item.itemType === sdk.items.type.Rune); + }) + .sort(function (a, b) { + return b.itemType - a.itemType; + }); if (!items || !items.length) return; - items.sort((a, b) => b.itemType - a.itemType); if (!FileTools.exists("mules/" + realm)) { folder = dopen("mules"); @@ -90,20 +95,20 @@ MuleLogger.logEquippedItems = function () { folder.create(me.account); } - for (let i = 0; i < items.length; i += 1) { - parsedItem = this.logItem(items[i], true, "Player"); + for (let item of items) { + parsedItem = this.logItem(item, true, "Player"); // Always put name on Char Viewer items !parsedItem.header && (parsedItem.header = (me.account || "Single Player") + " / " + me.name); // Remove itemtype_ prefix from the name parsedItem.title = parsedItem.title.substr(parsedItem.title.indexOf("_") + 1); - switch (items[i].mode) { + switch (item.mode) { case sdk.items.mode.inStorage: - parsedItem.title += ((items[i].isInInventory && items[i].isEquippedCharm) ? " (equipped charm)" : " (in stash)"); + parsedItem.title += ((item.isInInventory && item.isEquippedCharm) ? " (equipped charm)" : " (in stash)"); break; case sdk.items.mode.Equipped: - parsedItem.title += (items[i].isOnSwap ? " (secondary equipped)" : " (equipped)"); + parsedItem.title += (item.isOnSwap ? " (secondary equipped)" : " (equipped)"); break; } @@ -118,8 +123,8 @@ MuleLogger.logEquippedItems = function () { if (merc) { items = merc.getItemsEx(); - for (let i = 0; i < items.length; i += 1) { - parsedItem = this.logItem(items[i], true, "Merc"); + for (let item of items) { + parsedItem = this.logItem(item, true, "Merc"); parsedItem.title += " (merc)"; string = JSON.stringify(parsedItem); @@ -133,6 +138,13 @@ MuleLogger.logEquippedItems = function () { // hccl = hardcore classic ladder // scnl = softcore expan nonladder - FileTools.writeText("mules/" + realm + "/" + "Kolbot-SoloPlay/" + me.account + "/" + charClass + "-" + me.profile + "-" + me.name + "." + ( me.playertype ? "hc" : "sc" ) + (me.classic ? "c" : "" ) + ( me.ladder > 0 ? "l" : "nl" ) + ".txt", finalString); + FileTools.writeText( + "mules/" + realm + + "/" + "Kolbot-SoloPlay/" + + me.account + "/" + charClass + "-" + me.profile + "-" + me.name + + "." + ( me.playertype ? "hc" : "sc" ) + (me.classic ? "c" : "" ) + ( me.ladder > 0 ? "l" : "nl" ) + + ".txt", + finalString + ); console.log("Item logging done."); }; diff --git a/libs/SoloPlay/Functions/PrototypeOverrides.js b/libs/SoloPlay/Functions/PrototypeOverrides.js index 5965b692..aa2961d6 100644 --- a/libs/SoloPlay/Functions/PrototypeOverrides.js +++ b/libs/SoloPlay/Functions/PrototypeOverrides.js @@ -26,7 +26,10 @@ if (!Unit.prototype.hasOwnProperty("isGem")) { Object.defineProperty(Unit.prototype, "isGem", { get: function () { if (this.type !== sdk.unittype.Item) return false; - return (this.itemType >= sdk.items.type.Amethyst && this.itemType <= sdk.items.type.Skull); + return ( + this.itemType >= sdk.items.type.Amethyst + && this.itemType <= sdk.items.type.Skull + ); }, }); } @@ -87,8 +90,10 @@ if (!Unit.prototype.hasOwnProperty("isBaseType")) { Object.defineProperty(Unit.prototype, "isBaseType", { get: function () { if (this.type !== sdk.unittype.Item) return false; - return [sdk.items.quality.Normal, sdk.items.quality.Superior].includes(this.quality) && !this.questItem && !this.isRuneword - && getBaseStat("items", this.classid, "gemsockets") > 0 && [sdk.items.type.Ring, sdk.items.type.Amulet].indexOf(this.itemType) === -1; + return [sdk.items.quality.Normal, sdk.items.quality.Superior].includes(this.quality) + && !this.questItem && !this.isRuneword + && getBaseStat("items", this.classid, "gemsockets") > 0 + && [sdk.items.type.Ring, sdk.items.type.Amulet].indexOf(this.itemType) === -1; } }); } @@ -97,8 +102,12 @@ if (!Unit.prototype.hasOwnProperty("rawStrength")) { Object.defineProperty(Unit.prototype, "rawStrength", { get: function () { const lvl = this.getStat(sdk.stats.Level); - const rawBonus = (i) => i.getStat(sdk.stats.Strength); - const perLvlBonus = (i) => lvl * i.getStat(sdk.stats.PerLevelStrength) / 8; + const rawBonus = function (i) { + return i.getStat(sdk.stats.Strength); + }; + const perLvlBonus = function (i) { + return lvl * i.getStat(sdk.stats.PerLevelStrength) / 8; + }; const bonus = ~~(this.getItemsEx() .filter((i) => i.isEquipped || i.isEquippedCharm) .map((i) => rawBonus(i) + perLvlBonus(i)) @@ -112,8 +121,12 @@ if (!Unit.prototype.hasOwnProperty("rawDexterity")) { Object.defineProperty(Unit.prototype, "rawDexterity", { get: function () { const lvl = this.getStat(sdk.stats.Level); - const rawBonus = (i) => i.getStat(sdk.stats.Dexterity); - const perLvlBonus = (i) => lvl * i.getStat(sdk.stats.PerLevelDexterity) / 8; + const rawBonus = function (i) { + return i.getStat(sdk.stats.Dexterity); + }; + const perLvlBonus = function (i) { + return lvl * i.getStat(sdk.stats.PerLevelDexterity) / 8; + }; const bonus = ~~(this.getItemsEx() .filter((i) => i.isEquipped || i.isEquippedCharm) .map((i) => rawBonus(i) + perLvlBonus(i)) @@ -244,8 +257,10 @@ if (!Unit.prototype.hasOwnProperty("quantityPercent")) { * @param {number} difficulty */ Unit.prototype.getResPenalty = function (difficulty) { - difficulty > 2 && (difficulty = 2); - return me.gametype === sdk.game.gametype.Classic ? [0, 20, 50][difficulty] : [0, 40, 100][difficulty]; + difficulty > 2 && (difficulty = sdk.difficulty.Hell); + return me.gametype === sdk.game.gametype.Classic + ? [0, 20, 50][difficulty] + : [0, 40, 100][difficulty]; }; Unit.prototype.getItemType = function () { @@ -361,7 +376,7 @@ Unit.prototype.castChargedSkillEx = function (...args) { let chargedItems = []; - CharData.skillData.chargedSkills.forEach(chargeSkill => { + CharData.skillData.chargedSkills.forEach(function (chargeSkill) { if (chargeSkill.skill === skillId) { console.debug(chargeSkill); let item = me.getItem(-1, sdk.items.mode.Equipped, chargeSkill.gid); @@ -379,7 +394,9 @@ Unit.prototype.castChargedSkillEx = function (...args) { } let chargedItem = chargedItems - .sort((a, b) => a.charge.level - b.charge.level) + .sort(function (a, b) { + return b.charge.level - a.charge.level; + }) .first().item; // Check if item with charges is equipped on the switch spot @@ -497,7 +514,7 @@ Unit.prototype.castSwitchChargedSkill = function (...args) { /** @type {{ charge: number, level: number, item: ItemUnit }[]} */ let chargedItems = []; - CharData.skillData.chargedSkillsOnSwitch.forEach(chargeSkill => { + CharData.skillData.chargedSkillsOnSwitch.forEach(function (chargeSkill) { if (chargeSkill.skill === skillId) { console.debug(chargeSkill); let item = me.getItem(-1, sdk.items.mode.Equipped, chargeSkill.gid); @@ -517,7 +534,9 @@ Unit.prototype.castSwitchChargedSkill = function (...args) { me.weaponswitch === 0 && me.switchWeapons(1); let chargedItem = chargedItems - .sort((a, b) => a.charge.level - b.charge.level) + .sort(function (a, b) { + return b.charge.level - a.charge.level; + }) .first().item; return chargedItem.castChargedSkillEx.apply(chargedItem, args); } diff --git a/libs/SoloPlay/Functions/Quest.js b/libs/SoloPlay/Functions/Quest.js index 5c5a79e1..e7925ccc 100644 --- a/libs/SoloPlay/Functions/Quest.js +++ b/libs/SoloPlay/Functions/Quest.js @@ -10,10 +10,10 @@ const Quest = { preReqs: function () { /** * @param {string} task - * @param {() => boolean} req + * @param {function(): boolean} req * @returns {boolean} */ - const getReq = (task, req = () => true) => { + const getReq = function (task, req = () => true) { for (let i = 0; i < 5 && !req(); i++) { Loader.runScript(task); } @@ -97,7 +97,14 @@ const Quest = { let orifice = Misc.poll(() => Game.getObject(sdk.objects.HoradricStaffHolder)); if (!orifice) return false; - let hstaff = (me.getItem(sdk.items.quest.HoradricStaff) || Quest.cubeItems(sdk.items.quest.HoradricStaff, sdk.items.quest.ShaftoftheHoradricStaff, sdk.items.quest.ViperAmulet)); + let hstaff = ( + me.getItem(sdk.items.quest.HoradricStaff) + || Quest.cubeItems( + sdk.items.quest.HoradricStaff, + sdk.items.quest.ShaftoftheHoradricStaff, + sdk.items.quest.ViperAmulet + ) + ); if (hstaff) { if (hstaff.location !== sdk.storage.Inventory) { @@ -340,7 +347,8 @@ const Quest = { Misc.checkQuest(sdk.quest.id.Respec, sdk.quest.states.Completed); delay(10 + me.ping * 2); - if (me.respec || (me.getStat(sdk.stats.NewSkills) > preSkillAmount && me.getStat(sdk.stats.StatPts) > preStatAmount)) { + if (me.respec || (me.getStat(sdk.stats.NewSkills) > preSkillAmount + && me.getStat(sdk.stats.StatPts) > preStatAmount)) { me.data.currentBuild = CharInfo.getActiveBuild(); me.data[sdk.difficulty.nameOf(me.diff).toLowerCase()].respecUsed = true; CharData.updateData("me", me.data); diff --git a/libs/SoloPlay/Workers/TownChicken.js b/libs/SoloPlay/Workers/TownChicken.js index c2c48045..2d5bf172 100644 --- a/libs/SoloPlay/Workers/TownChicken.js +++ b/libs/SoloPlay/Workers/TownChicken.js @@ -323,10 +323,12 @@ Config.DebugMode.Stack = true; let waitTick = getTickCount(); let potTick = getTickCount(); + let _recursion = false; // Start Worker.runInBackground.TownChicken = function () { if (getTickCount() - waitTick < 100 || SoloEvents.townChicken.disabled) return true; + if (_recursion) return true; waitTick = getTickCount(); if (me.inTown) return true; @@ -354,22 +356,7 @@ if (shouldChicken) { let t4 = getTickCount(); try { - // const recentChicks = lastChickens - // .slice(Math.max(lastChickens.length - 3), lastChickens.length - 1); - - // const stopCurrentScript = recentChicks.length >= 2 && recentChicks - // .every(([count]) => getTickCount() - count < Time.minutes(1)); - - // lastChickens.push([getTickCount(), me.area, me.x, me.y]); - - // if (stopCurrentScript) { - // myPrint("ÿc8TownChicken :: ÿc0Too many chickens on this script, move to next :: " + me.gold); - // goToTown(); - // // scriptBroadcast("nextScript"); - // me.emit("nextScript"); - // return true; - // } - + _recursion = true; myPrint("ÿc8TownChicken :: ÿc0Going to town. Initial Gold :: " + me.gold); [Attack.stopClear, SoloEvents.townChicken.running] = [true, true]; @@ -393,6 +380,7 @@ return false; } finally { + _recursion = false; Packet.flash(me.gid, 100); console.log("ÿc8TownChicken :: Took: " + Time.format(getTickCount() - t4) + " to visit town. Ending Gold :: " + me.gold); [Attack.stopClear, SoloEvents.townChicken.running, townCheck] = [false, false, false]; From f9038f426e900528f847e68c88758a963b506a73 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Mon, 5 Jun 2023 14:32:24 -0400 Subject: [PATCH 132/263] Update NecromancerAttacks.js - fix a typo in doCurse - increase range for teeth - formatting --- .../NecromancerAttacks.js | 93 ++++++++++++------- 1 file changed, 61 insertions(+), 32 deletions(-) diff --git a/libs/SoloPlay/Functions/ClassAttackOverrides/NecromancerAttacks.js b/libs/SoloPlay/Functions/ClassAttackOverrides/NecromancerAttacks.js index 046df2e5..6b45e64f 100644 --- a/libs/SoloPlay/Functions/ClassAttackOverrides/NecromancerAttacks.js +++ b/libs/SoloPlay/Functions/ClassAttackOverrides/NecromancerAttacks.js @@ -106,16 +106,17 @@ includeIfNotIncluded("core/Attacks/Necromancer.js"); ]; })(); - /** - * @param {Monster} unit - * @returns {boolean} - */ + /** @param {Monster} unit */ const doCurse = function (unit) { if (unit === undefined || unit.dead || !unit.curseable) return false; let curse = (curseIndex - .filter(c => c.have() && c.useIf(unit)) - .sort((a, b) => a.priority - b.priority) + .filter(function (c) { + return c.have() && c.useIf(unit); + }) + .sort(function (a, b) { + return a.priority - b.priority; + }) .find(c => c.manaCost() < me.mp) || false); if (curse && !unit.getState(curse.state)) { @@ -125,7 +126,7 @@ includeIfNotIncluded("core/Attacks/Necromancer.js"); } else { me.overhead(unit.name + " is blocked, skipping attempt to curse"); let [timed, untimed] = unit.isSpecial ? [1, 2] : [3, 5]; - ClassAttack.doCast(unit, timed, untimed); + ClassAttack.doCast(unit, Config.AttackSkill[timed], Config.AttackSkill[untimed]); } } @@ -206,7 +207,8 @@ includeIfNotIncluded("core/Attacks/Necromancer.js"); // maybe this should return an object with basic skill info besides the skillId. e.g timed, mana, range, and hand const skills = Attack.decideSkill(unit); - const switchBowAttack = (unit) => { + /** @param {Monster} unit */ + const switchBowAttack = function (unit) { if (Attack.getIntoPosition(unit, 20, sdk.collision.Ranged)) { try { const checkForShamans = unit.isFallen && !me.inArea(sdk.areas.BloodMoor); @@ -214,16 +216,23 @@ includeIfNotIncluded("core/Attacks/Necromancer.js"); if (checkForShamans && !once) { // before we waste time let's see if there is a shaman we should kill const shaman = getUnits(sdk.unittype.Monster) - .filter(mon => mon.distance < 20 && mon.isShaman && mon.attackable) - .sort((a, b) => a.distance - b.distance).first(); + .filter(function (mon) { + return mon.distance < 20 && mon.isShaman && mon.attackable; + }) + .sort(function (a, b) { + return a.distance - b.distance; + }).first(); if (shaman) return ClassAttack.doAttack(shaman, null, true); } if (!Attack.useBowOnSwitch(unit, sdk.skills.Attack, i === 5)) return Attack.Result.FAILED; if (unit.distance < 8 || me.inDanger()) { if (once) return Attack.Result.FAILED; let closeMob = getUnits(sdk.unittype.Monster) - .filter(mon => mon.distance < 10 && mon.attackable && mon.gid !== gid) - .sort(Attack.walkingSortMonsters).first(); + .filter(function (mon) { + return mon.distance < 10 && mon.attackable && mon.gid !== gid; + }) + .sort(Attack.walkingSortMonsters) + .first(); if (closeMob) return ClassAttack.doAttack(closeMob, null, true); } } @@ -300,24 +309,30 @@ includeIfNotIncluded("core/Attacks/Necromancer.js"); return result; }; - // Returns: 0 - fail, 1 - success, 2 - no valid attack skills + /** + * @param {Monster} unit + * @param {number} timedSkill + * @param {number} untimedSkill + * @returns {AttackResult} + */ ClassAttack.doCast = function (unit, timedSkill, untimedSkill) { // No valid skills can be found - if (timedSkill < 0 && untimedSkill < 0) return Attack.Result.CANTATTACK; - + if (timedSkill < 0 && untimedSkill < 0) { + return Attack.Result.CANTATTACK; + } // Check for bodies to exploit for CorpseExplosion before committing to an attack for non-summoner type necros if (Config.Skeletons + Config.SkeletonMages + Config.Revives === 0) { this.checkCorpseNearMonster(unit) && this.explodeCorpses(unit); } + let walk; let lowMana = true; - let walk, timedSkillRange, untimedSkillRange; if (timedSkill > -1 && (!me.getState(sdk.states.SkillDelay) || !Skill.isTimed(timedSkill)) && me.mp > Skill.getManaCost(timedSkill)) { lowMana = false; - timedSkillRange = Skill.getRange(timedSkill); + let timedSkillRange = Skill.getRange(timedSkill); switch (timedSkill) { case sdk.skills.PoisonNova: @@ -345,18 +360,24 @@ includeIfNotIncluded("core/Attacks/Necromancer.js"); break; default: - if (timedSkillRange < 4 && !Attack.validSpot(unit.x, unit.y)) return Attack.Result.FAILED; - + if (timedSkillRange < 4 && !Attack.validSpot(unit.x, unit.y)) { + return Attack.Result.FAILED; + } if (timedSkill === sdk.skills.Teeth) { let _coll = (sdk.collision.BlockMissile | sdk.collision.BlockWall | sdk.collision.Casting); - timedSkillRange = me.getMobCount(6, _coll) <= 3 ? 6 : timedSkillRange; + timedSkillRange = me.getMobCount(6, _coll) <= 3 ? 8 : timedSkillRange; } if (unit.distance > timedSkillRange || checkCollision(me, unit, sdk.collision.Ranged)) { // Allow short-distance walking for melee skills - walk = timedSkillRange < 4 && unit.distance < 10 && !checkCollision(me, unit, sdk.collision.BlockWall); - - if (!Attack.getIntoPosition(unit, timedSkillRange, sdk.collision.Ranged, walk)) return Attack.Result.FAILED; + walk = ( + timedSkillRange < 4 + && unit.distance < 10 + && !checkCollision(me, unit, sdk.collision.BlockWall) + ); + if (!Attack.getIntoPosition(unit, timedSkillRange, sdk.collision.Ranged, walk)) { + return Attack.Result.FAILED; + } } if (!unit.dead) { @@ -374,13 +395,15 @@ includeIfNotIncluded("core/Attacks/Necromancer.js"); if (untimedSkill > -1 && me.mp > Skill.getManaCost(untimedSkill)) { lowMana = false; - untimedSkillRange = Skill.getRange(untimedSkill); - - if (untimedSkillRange < 4 && !Attack.validSpot(unit.x, unit.y)) return Attack.Result.FAILED; + let untimedSkillRange = Skill.getRange(untimedSkill); + if (untimedSkillRange < 4 && !Attack.validSpot(unit.x, unit.y)) { + return Attack.Result.FAILED; + } if (unit.distance > untimedSkillRange || checkCollision(me, unit, sdk.collision.Ranged)) { // Allow short-distance walking for melee skills - walk = (Skill.getRange(untimedSkill) < 4 + walk = ( + untimedSkillRange < 4 && unit.distance < 10 && !checkCollision(me, unit, sdk.collision.BlockWall) ); @@ -405,15 +428,19 @@ includeIfNotIncluded("core/Attacks/Necromancer.js"); return lowMana ? Attack.Result.NEEDMANA : Attack.Result.SUCCESS; }; + /** @param {Monster} unit */ ClassAttack.farCast = function (unit) { let timedSkill = Config.AttackSkill[1]; let untimedSkill = Config.AttackSkill[2]; // No valid skills can be found - if (timedSkill < 0 && untimedSkill < 0) return Attack.Result.CANTATTACK; - + if (timedSkill < 0 && untimedSkill < 0) { + return Attack.Result.CANTATTACK; + } // Far to low a range for far casting - if (Skill.getRange(timedSkill) < 4 && Skill.getRange(untimedSkill) < 4) return Attack.Result.CANTATTACK; + if (Skill.getRange(timedSkill) < 4 && Skill.getRange(untimedSkill) < 4) { + return Attack.Result.CANTATTACK; + } // Bone prison if (unit.distance > 10 @@ -447,8 +474,9 @@ includeIfNotIncluded("core/Attacks/Necromancer.js"); } if (untimedSkill > -1) { - if (Skill.getRange(untimedSkill) < 4 && !Attack.validSpot(unit.x, unit.y)) return Attack.Result.FAILED; - + if (Skill.getRange(untimedSkill) < 4 && !Attack.validSpot(unit.x, unit.y)) { + return Attack.Result.FAILED; + } if (!unit.dead && !checkCollision(me, unit, sdk.collision.Ranged)) { Skill.cast(untimedSkill, Skill.getHand(untimedSkill), unit); } @@ -461,6 +489,7 @@ includeIfNotIncluded("core/Attacks/Necromancer.js"); return Attack.Result.SUCCESS; }; + /** @param {Monster} unit */ ClassAttack.explodeCorpses = function (unit) { if (Config.ExplodeCorpses === 0 || unit.dead) return false; From 4f694146205b35652d6f4b75af6c703948949171 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Mon, 5 Jun 2023 14:33:05 -0400 Subject: [PATCH 133/263] Update SorceressAttacks.js - formatting --- .../ClassAttackOverrides/SorceressAttacks.js | 159 +++++++++++++----- 1 file changed, 116 insertions(+), 43 deletions(-) diff --git a/libs/SoloPlay/Functions/ClassAttackOverrides/SorceressAttacks.js b/libs/SoloPlay/Functions/ClassAttackOverrides/SorceressAttacks.js index 6d202dc3..ff79587e 100644 --- a/libs/SoloPlay/Functions/ClassAttackOverrides/SorceressAttacks.js +++ b/libs/SoloPlay/Functions/ClassAttackOverrides/SorceressAttacks.js @@ -29,7 +29,8 @@ includeIfNotIncluded("core/Attacks/Sorceress.js"); if (mob) { do { if (mob.distance < 7 && ![sdk.monsters.Andariel].includes(mob.classid) && mob.attackable - && !mob.isChilled && Attack.checkResist(mob, "cold") && !checkCollision(me, mob, Coords_1.Collision.BLOCK_MISSILE)) { + && !mob.isChilled && Attack.checkResist(mob, "cold") + && !checkCollision(me, mob, Coords_1.Collision.BLOCK_MISSILE)) { return true; } } while (mob.getNext()); @@ -122,8 +123,8 @@ includeIfNotIncluded("core/Attacks/Sorceress.js"); * @param {number} currLvl * @todo decide when AttackData need to be re-initialized becasue doing it every attack is a waste */ - const initAttackData = (currLvl = me.charlvl) => { - AttackDataKeys.forEach(sk => { + const initAttackData = function (currLvl = me.charlvl) { + AttackDataKeys.forEach(function (sk) { if (currLvl >= AttackData[sk].reqLvl) { AttackData[sk].assignValues(); } @@ -134,8 +135,8 @@ includeIfNotIncluded("core/Attacks/Sorceress.js"); * Helper function to init damage value for unit * @param {Monster} unit */ - const setDamageValues = (unit) => { - AttackDataKeys.forEach(sk => { + const setDamageValues = function (unit) { + AttackDataKeys.forEach(function (sk) { if (AttackData[sk].have) { AttackData[sk].calcDmg(unit); } @@ -147,7 +148,7 @@ includeIfNotIncluded("core/Attacks/Sorceress.js"); * @param {ClassData} skill * @returns {boolean} */ - const isHighestDmg = (skill) => { + const isHighestDmg = function (skill) { for (let key of AttackDataKeys) { if (AttackData[key].dmg > skill.dmg) { return false; @@ -160,6 +161,10 @@ includeIfNotIncluded("core/Attacks/Sorceress.js"); * Used to handle times when there isn't a valid skill we can use, to prevent throwing error */ const DummyData = new ClassData(-1, -1); + /** + * Makes checking cases with it easier + */ + const TELEPORT = new ClassData(sdk.skills.Teleport, 40); /** * @param {Monster} unit @@ -167,7 +172,10 @@ includeIfNotIncluded("core/Attacks/Sorceress.js"); * @todo keep track of when, what, and who we last casted on to prevent spamming charged skills in a short period of time */ ClassAttack.switchCurse = function (unit, force) { - if (CharData.skillData.haveChargedSkill([sdk.skills.SlowMissiles, sdk.skills.LowerResist, sdk.skills.Weaken]) && unit.curseable) { + if (!CharData.skillData.haveChargedSkill([sdk.skills.SlowMissiles, sdk.skills.LowerResist, sdk.skills.Weaken])) { + return; + } + if (unit.curseable) { const gold = me.gold; const isBoss = unit.isBoss; const dangerZone = [sdk.areas.ChaosSanctuary, sdk.areas.ThroneofDestruction].includes(me.area); @@ -203,12 +211,13 @@ includeIfNotIncluded("core/Attacks/Sorceress.js"); /** * @param {Monster} unit + * @param {boolean} [checkDelay] * @returns {dataObj} */ - ClassAttack.decideDistanceSkill = function (unit) { + ClassAttack.decideDistanceSkill = function (unit, checkDelay = false) { const currLvl = me.charlvl; let selected = AttackDataKeys - .filter(sk => { + .filter(function (sk) { if (currLvl < AttackData[sk].reqLvl || AttackData[sk].range < 20) return false; AttackData[sk].assignValues(); if (!AttackData[sk].have) return false; @@ -222,11 +231,15 @@ includeIfNotIncluded("core/Attacks/Sorceress.js"); * and we don't need to move to cast the non-timed skill. * 4) Anything else? */ - return AttackData[sk].dmg > 0 /* && (!AttackData[k].timed || !me.skillDelay) */; + return AttackData[sk].dmg > 0 && (!checkDelay || (!AttackData[sk].timed || !me.skillDelay)); + }) + .sort(function (a, b) { + return AttackData[b].dmg - AttackData[a].dmg; }) - .sort((a, b) => AttackData[b].dmg - AttackData[a].dmg) .first(); - return typeof AttackData[selected] === "object" ? AttackData[selected] : DummyData; + return typeof AttackData[selected] === "object" + ? AttackData[selected] + : DummyData; }; /** @@ -237,7 +250,9 @@ includeIfNotIncluded("core/Attacks/Sorceress.js"); * @returns {AttackResult} */ ClassAttack.doAttack = function (unit, recheckSkill = false, once = false) { - Developer.debugging.skills && console.log(sdk.colors.Green + "Test Start-----------------------------------------//"); + if (Developer.debugging.skills) { + console.log(sdk.colors.Green + "Test Start-----------------------------------------//"); + } // unit became invalidated if (!unit || !unit.attackable) return Attack.Result.SUCCESS; @@ -269,6 +284,7 @@ includeIfNotIncluded("core/Attacks/Sorceress.js"); } initAttackData(currLvl); + TELEPORT.assignValues(); if (AttackData.FrostNova.have) { if (me.mp > AttackData.FrostNova.mana) { @@ -276,7 +292,12 @@ includeIfNotIncluded("core/Attacks/Sorceress.js"); let ticktwo = getTickCount(); // if the nova cause the death of any monsters around us, its worth it if (GameData.calculateKillableFallensByFrostNova() > 0) { - Developer.debugging.skills && console.log("took " + ((getTickCount() - ticktwo) / 1000) + " seconds to check calculateKillableFallensByFrostNova. frost nova will kill fallens"); + if (Developer.debugging.skills) { + console.log( + "took " + ((getTickCount() - ticktwo) / 1000) + + " seconds to check calculateKillableFallensByFrostNova. frost nova will kill fallens" + ); + } Skill.cast(sdk.skills.FrostNova, sdk.skills.hand.Right); } } @@ -313,13 +334,18 @@ includeIfNotIncluded("core/Attacks/Sorceress.js"); // should this return afterwards since the calulations will now be different? if (AttackData.StaticField.have && (AttackData.StaticField.mana * 3) < me.mp) { let closeMobCheck = getUnits(sdk.unittype.Monster) - .filter(unit => !!unit && unit.attackable && unit.distance < AttackData.StaticField.range) - .find(unit => Attack.checkResist(unit, "lightning") && unit.hpPercent > Config.CastStatic); + .filter(function (unit) { + return !!unit && unit.attackable && unit.distance < AttackData.StaticField.range; + }) + .find(function (unit) { + return Attack.checkResist(unit, "lightning") && unit.hpPercent > Config.CastStatic; + }); if (!!closeMobCheck && isHighestDmg(AttackData.StaticField) && !Coords_1.isBlockedBetween(me, closeMobCheck)) { Developer.debugging.skills && console.log("STATIC"); // check if we should use battle cry from cta if we have it battleCryCheck(closeMobCheck); - Skill.cast(sdk.skills.StaticField, sdk.skills.hand.Right, closeMobCheck) && Skill.cast(sdk.skills.StaticField, sdk.skills.hand.Right, closeMobCheck); + [sdk.skills.StaticField, sdk.skills.StaticField] + .every(skill => Skill.cast(skill, sdk.skills.hand.Right, closeMobCheck)); rebuild = true; } } @@ -333,21 +359,29 @@ includeIfNotIncluded("core/Attacks/Sorceress.js"); * @todo static field is a good skill but if we are currently out of range, check how dangerous it is to tele to spot before choosing that as our skill */ let sortedList = AttackDataKeys - .filter(k => AttackData[k].have && me.mp > AttackData[k].mana - && (!AttackData[k].timed || !me.skillDelay) && (AttackData[k].skill !== sdk.skills.StaticField || !recheckSkill)) - .sort((a, b) => AttackData[b].dmg - AttackData[a].dmg); + .filter(function (k) { + return (AttackData[k].have && me.mp > AttackData[k].mana + && (!AttackData[k].timed || !me.skillDelay) + && (AttackData[k].skill !== sdk.skills.StaticField || !recheckSkill)); + }) + .sort(function (a, b) { + return AttackData[b].dmg - AttackData[a].dmg; + }); if (!sortedList.length) return Attack.Result.FAILED; // A bit ugly but handle static and charged bolt here let skillCheck = ( - (AttackData[sortedList[0]].skill === sdk.skills.StaticField && unit.distance > AttackData.StaticField.range && me.inDanger(unit, 15)) + (AttackData[sortedList[0]].skill === sdk.skills.StaticField + && unit.distance > AttackData.StaticField.range && me.inDanger(unit, 15)) || (AttackData[sortedList[0]].skill === sdk.skills.ChargedBolt && recheckSkill) ) ? sortedList.at(1) : sortedList.at(0); /** @type {ClassData} */ - let selectedSkill = typeof AttackData[skillCheck] === "object" ? AttackData[skillCheck] : DummyData; + let selectedSkill = typeof AttackData[skillCheck] === "object" + ? AttackData[skillCheck] + : DummyData; switch (selectedSkill.skill) { case sdk.skills.ChargedBolt: @@ -364,7 +398,8 @@ includeIfNotIncluded("core/Attacks/Sorceress.js"); break; case sdk.skills.Attack: - if (!me.normal || (me.charlvl > 6 && !me.checkForMobs({ range: 10, coll: (sdk.collision.BlockWall | sdk.collision.ClosedDoor) }))) { + if (!me.normal || (me.charlvl > 6 + && !me.checkForMobs({ range: 10, coll: (sdk.collision.BlockWall | sdk.collision.ClosedDoor) }))) { selectedSkill = DummyData; } } @@ -373,24 +408,32 @@ includeIfNotIncluded("core/Attacks/Sorceress.js"); * @param {Monster} unit * @returns {AttackResult} */ - const switchBowAttack = (unit) => { + const switchBowAttack = function (unit) { if (Attack.getIntoPosition(unit, 20, sdk.collision.Ranged)) { try { const checkForShamans = unit.isFallen && !me.inArea(sdk.areas.BloodMoor); for (let i = 0; i < 5 && unit.attackable; i++) { if (checkForShamans && !once) { - // before we waste time let's see if there is a shaman we should kill + // before we waste time let's see if there is a shaman we should kill const shaman = getUnits(sdk.unittype.Monster) - .filter(mon => mon.distance < 20 && mon.isShaman && mon.attackable) - .sort((a, b) => a.distance - b.distance).first(); + .filter(function (mon) { + return mon.distance < 20 && mon.isShaman && mon.attackable; + }) + .sort(function (a, b) { + return a.distance - b.distance; + }) + .first(); if (shaman) return ClassAttack.doAttack(shaman, null, true); } if (!Attack.useBowOnSwitch(unit, sdk.skills.Attack, i === 5)) return Attack.Result.FAILED; if (unit.distance < 8 || me.inDanger()) { if (once) return Attack.Result.FAILED; let closeMob = getUnits(sdk.unittype.Monster) - .filter(mon => mon.distance < 10 && mon.attackable && mon.gid !== gid) - .sort(Attack.walkingSortMonsters).first(); + .filter(function (mon) { + return mon.distance < 10 && mon.attackable && mon.gid !== gid; + }) + .sort(Attack.walkingSortMonsters) + .first(); if (closeMob) return ClassAttack.doAttack(closeMob, null, true); } } @@ -405,7 +448,8 @@ includeIfNotIncluded("core/Attacks/Sorceress.js"); && (index !== 1 || !unit.name.includes(getLocaleString(sdk.locale.text.Ghostly))) && ([-1, sdk.skills.Attack].includes(selectedSkill.skill) || selectedSkill.mana > me.mp - || (selectedSkill.mana * 3 > me.mp && [sdk.skills.FireBolt, sdk.skills.ChargedBolt].includes(selectedSkill.skill)))) { + || (selectedSkill.mana * 3 > me.mp + && [sdk.skills.FireBolt, sdk.skills.ChargedBolt].includes(selectedSkill.skill)))) { if (switchBowAttack(unit) === Attack.Result.SUCCESS) return Attack.Result.SUCCESS; } @@ -418,11 +462,21 @@ includeIfNotIncluded("core/Attacks/Sorceress.js"); switch (result) { case Attack.Result.FAILED: - Developer.debugging.skills && console.log(sdk.colors.Red + "Fail Test End----Time elasped[" + ((getTickCount() - tick) / 1000) + " seconds]----------------------//"); + if (Developer.debugging.skills) { + console.log( + sdk.colors.Red + "Fail Test End----Time elasped[" + + ((getTickCount() - tick) / 1000) + " seconds]----------------------//" + ); + } return Attack.Result.FAILED; case Attack.Result.SUCCESS: - Developer.debugging.skills && console.log(sdk.colors.Red + "Sucess Test End----Time elasped[" + ((getTickCount() - tick) / 1000) + " seconds]----------------------//"); + if (Developer.debugging.skills) { + console.log( + sdk.colors.Red + "Sucess Test End----Time elasped[" + + ((getTickCount() - tick) / 1000) + " seconds]----------------------//" + ); + } return Attack.Result.SUCCESS; case Attack.Result.CANTATTACK: // Try to telestomp @@ -453,12 +507,15 @@ includeIfNotIncluded("core/Attacks/Sorceress.js"); !!spot && Pather.walkTo(spot.x, spot.y); } - if (Attack.checkResist(unit, "lightning") && AttackData.StaticField.have && unit.hpPercent > Config.CastStatic) { + if (Attack.checkResist(unit, "lightning") && AttackData.StaticField.have + && unit.hpPercent > Config.CastStatic) { Skill.cast(sdk.skills.StaticField, sdk.skills.hand.Right); } let closeMob = Attack.getNearestMonster({ skipGid: gid }); - !!closeMob ? this.doCast(closeMob, selectedSkill) : haveTK && Skill.cast(sdk.skills.Telekinesis, sdk.skills.hand.Right, unit); + !!closeMob + ? this.doCast(closeMob, selectedSkill) + : haveTK && Packet.telekinesis(unit); } return Attack.Result.SUCCESS; @@ -488,13 +545,15 @@ includeIfNotIncluded("core/Attacks/Sorceress.js"); if (skill < 0) return Attack.Result.CANTATTACK; // print damage values - Developer.debugging.skills && choosenSkill.have && console.log(sdk.colors.Yellow + "(Selected Main :: " + getSkillById(skill) + ") DMG: " + choosenSkill.dmg); + if (Developer.debugging.skills && choosenSkill.have) { + console.log(sdk.colors.Yellow + "(Selected Main :: " + getSkillById(skill) + ") DMG: " + choosenSkill.dmg); + } if (![sdk.skills.FrostNova, sdk.skills.Nova, sdk.skills.StaticField].includes(skill)) { // need like a potential danger check, sometimes while me might not be immeadiate danger because there aren't a whole // lot of monsters around, we can suddenly be in danger if a ranged monsters hits us or if one of the monsters near us // does a lot of damage quickly - if (Skill.canUse(sdk.skills.Teleport) && me.mp > Skill.getManaCost(sdk.skills.Teleport) + mana && me.inDanger()) { + if (TELEPORT.have && me.mp > TELEPORT.mana + mana && me.inDanger()) { //console.log("FINDING NEW SPOT"); Attack.getIntoPosition(unit, range, 0 | Coords_1.BlockBits.LineOfSight @@ -509,7 +568,7 @@ includeIfNotIncluded("core/Attacks/Sorceress.js"); } } - if (skill > -1 && (!me.skillDelay || !timed)) { + if (!me.skillDelay || !timed) { let ranged = range > 4; if (skill === sdk.skills.ChargedBolt && !unit.hasEnchant(sdk.enchant.ManaBurn, sdk.enchant.ColdEnchanted)) { @@ -520,7 +579,8 @@ includeIfNotIncluded("core/Attacks/Sorceress.js"); if (me.hpPercent < 50 && me.mode !== sdk.player.mode.GettingHit && !me.checkForMobs({ range: 12 })) { console.log("Low health but safe right now, going to delay a bit"); let tick = getTickCount(); - const howLongToDelay = Config.AttackSkill.some(sk => sk > 1 && Skill.canUse(sk)) ? Time.seconds(2) : Time.seconds(1); + const howLongToDelay = Config.AttackSkill + .some(sk => sk > 1 && Skill.canUse(sk)) ? Time.seconds(2) : Time.seconds(1); while (getTickCount() - tick < howLongToDelay) { if (me.mode === sdk.player.mode.GettingHit) { @@ -559,11 +619,14 @@ includeIfNotIncluded("core/Attacks/Sorceress.js"); } if (unit.distance > range || Coords_1.isBlockedBetween(me, unit)) { - // Allow short-distance walking for melee skills - let walk = (range < 4 || (skill === sdk.skills.ChargedBolt && range === 7)) && unit.distance < 10 && !checkCollision(me, unit, Coords_1.BlockBits.BlockWall); + // Allow short-distance walking for melee skills + let walk = (range < 4 || (skill === sdk.skills.ChargedBolt && range === 7)) + && unit.distance < 10 && !checkCollision(me, unit, Coords_1.BlockBits.BlockWall); if (ranged) { - if (!Attack.getIntoPosition(unit, range, Coords_1.Collision.BLOCK_MISSILE, walk)) return Attack.Result.FAILED; + if (!Attack.getIntoPosition(unit, range, Coords_1.Collision.BLOCK_MISSILE, walk)) { + return Attack.Result.FAILED; + } } else if (!Attack.getIntoPosition(unit, range, Coords_1.BlockBits.Ranged, walk)) { return Attack.Result.FAILED; } @@ -579,7 +642,9 @@ includeIfNotIncluded("core/Attacks/Sorceress.js"); if (!Misc.poll(() => unit.dead || unit.hp < preHealth, 300, 50)) { cRetry++; // we still might of missed so pick another coord - if (!Attack.getIntoPosition(unit, (range - cRetry), Coords_1.Collision.BLOCK_MISSILE, true)) return Attack.Result.FAILED; + if (!Attack.getIntoPosition(unit, (range - cRetry), Coords_1.Collision.BLOCK_MISSILE, true)) { + return Attack.Result.FAILED; + } !unit.dead && Skill.cast(skill, Skill.getHand(skill), unit.x, unit.y); } else { break; @@ -592,11 +657,19 @@ includeIfNotIncluded("core/Attacks/Sorceress.js"); if (!unit.dead) { // if we are already in close then it might be worth it to use battle cry if we have it battleCryCheck(unit); + // if we are in danger then don't cast and move + if ((unit.distance <= 3 && !unit.isStunned && !unit.isFrozen) || me.inDanger()) { + // don't cast, just run + Attack.deploy(unit, 25, 5, 9); + return Attack.Result.FAILED; + } Skill.cast(skill, Skill.getHand(skill), unit); if (!Misc.poll(() => unit.dead || unit.hp < preHealth, 200, 50)) { sRetry++; // we still might of missed so pick another coord - if (!Attack.getIntoPosition(unit, (range - sRetry), Coords_1.Collision.BLOCK_MISSILE, true)) return Attack.Result.FAILED; + if (!Attack.getIntoPosition(unit, (range - sRetry), Coords_1.Collision.BLOCK_MISSILE, true)) { + return Attack.Result.FAILED; + } !unit.dead && Skill.cast(skill, Skill.getHand(skill), unit); } From 16989fa4c4f0871565b3f06739cd550312df2380 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Mon, 5 Jun 2023 14:37:36 -0400 Subject: [PATCH 134/263] Update PatherOverrides.js - fix charged teleport casting - changed clearToExit to accept setting overrides - changed moveToExit to accept setting overrides - made changeAct work going either direction with acts - removed some debugging statements and cleaned up some of the logging --- libs/SoloPlay/Functions/PatherOverrides.js | 557 +++++++++++++-------- 1 file changed, 359 insertions(+), 198 deletions(-) diff --git a/libs/SoloPlay/Functions/PatherOverrides.js b/libs/SoloPlay/Functions/PatherOverrides.js index c236dfb5..7b3b8be5 100644 --- a/libs/SoloPlay/Functions/PatherOverrides.js +++ b/libs/SoloPlay/Functions/PatherOverrides.js @@ -17,12 +17,16 @@ Pather.inAnnoyingArea = function (currArea, includeArcane = false) { }; /** - * @param {Object} arg - * @param {boolean} arg.canTele - * @param {boolean} arg.clearPath - * @param {number} arg.range - * @param {number} arg.specType - * @param {boolean} [arg.allowClearing] + * @typedef {Object} clearSettings + * @property {boolean} canTele + * @property {boolean} clearPath + * @property {number} range + * @property {number} specType + * @property {boolean} [allowClearing] + */ + +/** + * @param {clearSettings} arg * @returns {void} * @todo * - clean this up @@ -34,15 +38,26 @@ NodeAction.killMonsters = function (arg = {}) { const myArea = me.area; // I don't think this is even needed anymore, pretty sure I fixed wall hugging. todo - check it const pallyAnnoyingAreas = [ - sdk.areas.DenofEvil, sdk.areas.CaveLvl1, sdk.areas.UndergroundPassageLvl1, sdk.areas.HoleLvl1, sdk.areas.PitLvl1, sdk.areas.CaveLvl2, - sdk.areas.UndergroundPassageLvl2, sdk.areas.PitLvl2, sdk.areas.HoleLvl2, sdk.areas.DisusedFane, sdk.areas.RuinedTemple, - sdk.areas.ForgottenReliquary, sdk.areas.ForgottenTemple, sdk.areas.RuinedFane, sdk.areas.DisusedReliquary + sdk.areas.DenofEvil, sdk.areas.CaveLvl1, + sdk.areas.UndergroundPassageLvl1, sdk.areas.HoleLvl1, + sdk.areas.PitLvl1, sdk.areas.CaveLvl2, + sdk.areas.UndergroundPassageLvl2, sdk.areas.PitLvl2, + sdk.areas.HoleLvl2, sdk.areas.DisusedFane, + sdk.areas.RuinedTemple, sdk.areas.ForgottenReliquary, + sdk.areas.ForgottenTemple, sdk.areas.RuinedFane, sdk.areas.DisusedReliquary ]; const summonerAreas = [ - sdk.areas.DenofEvil, sdk.areas.ColdPlains, sdk.areas.StonyField, sdk.areas.Tristram, sdk.areas.DarkWood, sdk.areas.BlackMarsh, - sdk.areas.OuterCloister, sdk.areas.Barracks, sdk.areas.Cathedral, sdk.areas.CatacombsLvl4, sdk.areas.HallsoftheDeadLvl1, sdk.areas.HallsoftheDeadLvl2, - sdk.areas.HallsoftheDeadLvl3, sdk.areas.ValleyofSnakes, sdk.areas.ClawViperTempleLvl1, sdk.areas.TalRashasTomb1, sdk.areas.TalRashasTomb2, - sdk.areas.TalRashasTomb3, sdk.areas.TalRashasTomb4, sdk.areas.TalRashasTomb5, sdk.areas.TalRashasTomb6, sdk.areas.TalRashasTomb7 + sdk.areas.DenofEvil, sdk.areas.ColdPlains, + sdk.areas.StonyField, sdk.areas.Tristram, + sdk.areas.DarkWood, sdk.areas.BlackMarsh, + sdk.areas.OuterCloister, sdk.areas.Barracks, + sdk.areas.Cathedral, sdk.areas.CatacombsLvl4, + sdk.areas.HallsoftheDeadLvl1, sdk.areas.HallsoftheDeadLvl2, + sdk.areas.HallsoftheDeadLvl3, sdk.areas.ValleyofSnakes, + sdk.areas.ClawViperTempleLvl1, sdk.areas.TalRashasTomb1, + sdk.areas.TalRashasTomb2, sdk.areas.TalRashasTomb3, + sdk.areas.TalRashasTomb4, sdk.areas.TalRashasTomb5, + sdk.areas.TalRashasTomb6, sdk.areas.TalRashasTomb7 ]; // sanityCheck from isid0re - added paladin specific areas - theBGuy - a mess.. sigh if (Pather.inAnnoyingArea(myArea, true) || (me.paladin && pallyAnnoyingAreas.includes(myArea))) { @@ -56,26 +71,42 @@ NodeAction.killMonsters = function (arg = {}) { * - ignore dolls when walking unless absolutely necessary because we are blocked */ if (!arg.canTele && arg.clearPath !== false) { - let monList = []; + /** @type {Array} */ + const monList = []; + /** @param {Monster} mon */ + const addToMonList = function (mon) { + monList.push(mon); + }; + let _coll = (sdk.collision.BlockWall | sdk.collision.LineOfSight | sdk.collision.Ranged); + if (me.inArea(sdk.areas.BloodMoor)) { - monList = getUnits(sdk.unittype.Monster) - .filter(mon => mon.attackable && mon.distance < 30 && !mon.isFallen - && !checkCollision(me, mon, (sdk.collision.BlockWall | sdk.collision.LineOfSight | sdk.collision.Ranged))); - monList.length > 0 && Attack.clearList(monList); + getUnits(sdk.unittype.Monster) + .filter(function (mon) { + return mon.attackable && mon.distance < 30 + && !mon.isFallen && !checkCollision(me, mon, _coll); + }) + .forEach(addToMonList); } if (summonerAreas.includes(myArea)) { - monList = getUnits(sdk.unittype.Monster) - .filter(mon => mon.attackable && mon.distance < 30 && (mon.isUnraveler || mon.isShaman) - && !checkCollision(me, mon, (sdk.collision.BlockWall | sdk.collision.LineOfSight | sdk.collision.Ranged))); - monList.length > 0 && Attack.clearList(monList); + getUnits(sdk.unittype.Monster) + .filter(function (mon) { + return mon.attackable && mon.distance < 30 + && (mon.isUnraveler || mon.isShaman) && !checkCollision(me, mon, _coll); + }) + .forEach(addToMonList); } if ([sdk.areas.StonyField, sdk.areas.BlackMarsh, sdk.areas.FarOasis].includes(me.area)) { // monster nest's are good exp - monList = getUnits(sdk.unittype.Monster).filter(mon => mon.attackable && mon.distance < 35 && mon.isMonsterNest); - monList.length > 0 && Attack.clearList(monList); + getUnits(sdk.unittype.Monster) + .filter(function (mon) { + return mon.attackable && mon.distance < 35 && mon.isMonsterNest; + }) + .forEach(addToMonList); } + // need to write a way to consider current path + monList.length > 0 && Attack.clearList(monList); } if (arg.clearPath !== false) { @@ -83,12 +114,14 @@ NodeAction.killMonsters = function (arg = {}) { } }; -NodeAction.popChests = function () { - const range = Pather.useTeleport() ? 25 : 15; +/** @param {clearSettings} arg */ +NodeAction.popChests = function (arg = {}) { + const range = arg.canTele ? 25 : 15; Config.OpenChests.Enabled && Misc.openChests(range); Misc.useWell(range); }; +/** @param {clearSettings} arg */ NodeAction.pickItems = function (arg = {}) { if (arg.hasOwnProperty("allowPicking") && !arg.allowPicking) return; @@ -96,10 +129,13 @@ NodeAction.pickItems = function (arg = {}) { if (item) { const maxDist = Skill.haveTK ? 15 : 5; - const regPickRange = Pather.canTeleport() ? Config.PickRange : 8; + const regPickRange = arg.canTele ? Config.PickRange : 8; const maxRange = Math.max(maxDist, regPickRange); const totalList = [].concat(Pickit.essentialList, Pickit.pickList); - const filterJunk = (item) => !!item && item.onGroundOrDropping; + /** @param {ItemUnit} item */ + const filterJunk = function (item) { + return !!item && item.onGroundOrDropping; + }; do { if (item.onGroundOrDropping) { @@ -153,7 +189,9 @@ Pather.forceRun = false; let [x, y] = coords.apply(this); const settings = Object.assign({}, { range: 5, - coll: (sdk.collision.BlockWall | sdk.collision.ClosedDoor | sdk.collision.LineOfSight | sdk.collision.BlockMissile), + coll: ( + sdk.collision.BlockWall | sdk.collision.ClosedDoor | sdk.collision.LineOfSight | sdk.collision.BlockMissile + ), type: 0, ignoreClassids: [], }, givenSettings); @@ -174,17 +212,18 @@ Pather.checkForTeleCharges = function () { Pather.canUseTeleCharges = function () { if (me.classic || me.inTown || me.shapeshifted) return false; - // Charges are costly so make sure we have enough gold to handle repairs unless we are in maggot lair since thats a pita and worth the gold spent + // Charges are costly so make sure we have enough gold to handle repairs + // unless we are in maggot lair since thats a pita and worth the gold spent if (me.gold < 500000 && !Pather.inAnnoyingArea(me.area)) return false; return this.haveTeleCharges; }; Pather.teleportTo = function (x, y, maxRange = 5) { - Developer.debugging.pathing && console.log("Mob Count at next node: " + [x, y].mobCount()); + // Developer.debugging.pathing && console.log("Mob Count at next node: " + [x, y].mobCount()); for (let i = 0; i < 3; i += 1) { - Skill.setSkill(sdk.skills.Teleport, sdk.skills.hand.Right) && Packet.castSkill(sdk.skills.hand.Right, x, y); + if (!Packet.teleport(x, y)) continue; let tick = getTickCount(); let pingDelay = i === 0 ? 250 : me.getPingDelay(); @@ -203,30 +242,32 @@ Pather.teleportTo = function (x, y, maxRange = 5) { Pather.teleUsingCharges = function (x, y, maxRange = 5) { let orgSlot = me.weaponswitch; - for (let i = 0; i < 3; i++) { - me.castChargedSkill(sdk.skills.Teleport, x, y); - let tick = getTickCount(); + try { + for (let i = 0; i < 3; i++) { + me.castChargedSkillEx(sdk.skills.Teleport, x, y); + let tick = getTickCount(); - while (getTickCount() - tick < Math.max(500, me.ping * 2 + 200)) { - if (getDistance(me.x, me.y, x, y) < maxRange) { - me.weaponswitch !== orgSlot && me.switchWeapons(orgSlot); - return true; + while (getTickCount() - tick < Math.max(500, me.ping * 2 + 200)) { + if (getDistance(me.x, me.y, x, y) < maxRange) { + me.weaponswitch !== orgSlot && me.switchWeapons(orgSlot); + return true; + } + + delay(10); } + } - delay(10); + if (me.gold > me.getRepairCost() * 3 && me.canTpToTown()) { + console.debug("Tele-Charge repair"); + Town.visitTown(true); + } else { + this.haveTeleCharges = false; } - } - if (me.gold > me.getRepairCost() * 3 && me.canTpToTown()) { - console.debug("Tele-Charge repair"); - Town.visitTown(true); - } else { - this.haveTeleCharges = false; + return false; + } finally { + me.weaponswitch !== orgSlot && me.switchWeapons(orgSlot); } - - me.weaponswitch !== orgSlot && me.switchWeapons(orgSlot); - - return false; }; Pather.checkWP = function (area = 0, keepMenuOpen = false) { @@ -246,7 +287,7 @@ Pather.checkWP = function (area = 0, keepMenuOpen = false) { if (wp && wp.area === me.area) { if (useTK) { wp.distance > 21 && Pather.moveNearUnit(wp, 20); - Skill.cast(sdk.skills.Telekinesis, sdk.skills.hand.Right, wp); + Packet.telekinesis(wp); } else { wp.distance > 7 && this.moveToUnit(wp); Misc.click(0, 0, wp); @@ -279,20 +320,14 @@ Pather.checkWP = function (area = 0, keepMenuOpen = false) { return getWaypoint(Pather.wpAreas.indexOf(area)); }; -Pather.changeAct = function () { - let act = me.act + 1; - let [npc, loc] = (() => { - switch (act) { - case 2: - return ["Warriv", sdk.areas.LutGholein]; - case 3: - return ["Meshif", sdk.areas.KurastDocktown]; - case 5: - return ["Tyrael", sdk.areas.Harrogath]; - default: - return ["", 0]; - } - })(); +Pather.changeAct = function (act = me.act + 1) { + const npcTravel = new Map([ + [1, ["Warriv", sdk.areas.RogueEncampment]], + [2, [(me.act === 1 ? "Warriv" : "Meshif"), sdk.areas.LutGholein]], + [3, ["Meshif", sdk.areas.KurastDocktown]], + [5, ["Tyrael", sdk.areas.Harrogath]], + ]); + let [npc, loc] = npcTravel.get(act); if (!npc) return false; !me.inTown && Town.goToTown(); @@ -311,8 +346,13 @@ Pather.changeAct = function () { if (npcUnit) { for (let i = 0; i < 5; i++) { - sendPacket(1, 56, 4, 0, 4, npcUnit.gid, 4, loc); - delay(500 + pingDelay); + new PacketBuilder() + .byte(sdk.packets.send.EntityAction) + .dword(0) + .dword(npcUnit.gid) + .dword(loc) + .send(); + delay(1000); if (me.act === act) { break; @@ -330,9 +370,7 @@ Pather.changeAct = function () { }; Pather.clearUIFlags = function () { - while (!me.gameReady) { - delay(3); - } + while (!me.gameReady) delay(3); for (let i = 0; i < Pather.cancelFlags.length; i++) { if (getUIFlag(Pather.cancelFlags[i]) && me.cancel()) { @@ -390,44 +428,59 @@ Pather.move = function (target, givenSettings = {}) { (target instanceof PresetUnit) && (target = target.realCoords()); if (settings.minDist > 3) { - target = Pather.spotOnDistance(target, settings.minDist, { returnSpotOnError: settings.returnSpotOnError, reductionType: (me.inTown ? 0 : 2) }); + target = Pather.spotOnDistance( + target, + settings.minDist, + { returnSpotOnError: settings.returnSpotOnError, reductionType: (me.inTown ? 0 : 2) } + ); + } + + /** @constructor */ + function PathAction () { + this.at = 0; + /** @type {PathNode} */ + this.node = { x: null, y: null }; } let fail = 0; + let invalidCheck = false; + let cbCheck = false; let node = { x: target.x, y: target.y }; - const leaped = { - at: 0, - /** @type {PathNode} */ - from: { x: null, y: null } - }; - const whirled = { - at: 0, - /** @type {PathNode} */ - from: { x: null, y: null } - }; - const cleared = { - at: 0, - /** @type {PathNode} */ - where: { x: null, y: null } - }; - let [invalidCheck] = [false]; + const leaped = new PathAction(); + const whirled = new PathAction(); + const cleared = new PathAction(); Pather.clearUIFlags(); if (typeof target.x !== "number" || typeof target.y !== "number") return false; - if (getDistance(me, target) < 2 && !CollMap.checkColl(me, target, Coords_1.Collision.BLOCK_MISSILE, 5)) return true; + if (getDistance(me, target) < 2 && !CollMap.checkColl(me, target, Coords_1.Collision.BLOCK_MISSILE, 5)) { + return true; + } - const useTeleport = settings.allowTeleport && (getDistance(me, target) > 15 || me.diff || me.act > 3) && Pather.useTeleport(); + const useTeleport = ( + settings.allowTeleport + && (target.distance > 15 || me.diff || me.act > 3) + && Pather.useTeleport() + ); settings.clearSettings.canTele = useTeleport; const useChargedTele = settings.allowTeleport && Pather.canUseTeleCharges(); const usingTele = (useTeleport || useChargedTele); const tpMana = Skill.getManaCost(sdk.skills.Teleport); const annoyingArea = Pather.inAnnoyingArea(me.area); - let path = getPath(me.area, target.x, target.y, me.x, me.y, usingTele ? 1 : 0, usingTele ? (annoyingArea ? 30 : Pather.teleDistance) : Pather.walkDistance); + let path = getPath( + me.area, + target.x, target.y, + me.x, me.y, + usingTele ? 1 : 0, + usingTele ? (annoyingArea ? 30 : Pather.teleDistance) : Pather.walkDistance + ); if (!path) throw new Error("move: Failed to generate path."); - // need to work on a better force clearing method but for now just have all walkers clear unless we specifically are forcing them not to (like while repositioning) - settings.allowClearing && !settings.clearSettings.clearPath && !useTeleport && (settings.clearSettings.clearPath = true); + // need to work on a better force clearing method but for now just have all walkers clear unless + // we specifically are forcing them not to (like while repositioning) + if (!useTeleport && settings.allowClearing && !settings.clearSettings.clearPath) { + settings.clearSettings.clearPath = true; + } if (settings.retry <= 3 && target.distance > useTeleport ? 120 : 60) { settings.retry = 10; @@ -438,7 +491,9 @@ Pather.move = function (target, givenSettings = {}) { /** @type {Array} */ let areaImmunities = GameData.areaImmunities(me.area); if (areaImmunities.length) { - let mySkElems = Config.AttackSkill.filter(sk => sk > 0).map(sk => Attack.getSkillElement(sk)); + let mySkElems = Config.AttackSkill + .filter(sk => sk > 0) + .map(sk => Attack.getSkillElement(sk)); // this area has monsters that are immune to our elements. This is a basic check for now // a better way would probably be per list built to check the ratio of immunes to non? if (mySkElems.length && mySkElems.every(elem => areaImmunities.includes(elem))) { @@ -452,7 +507,7 @@ Pather.move = function (target, givenSettings = {}) { path.reverse(); settings.pop && path.pop(); PathDebug.drawPath(path); - useTeleport && Config.TeleSwitch && path.length > 5 && me.switchWeapons(Attack.getPrimarySlot() ^ 1); + useTeleport && Config.TeleSwitch && path.length > 5 && me.switchToPrimary(); while (path.length > 0) { // Abort if dead @@ -464,21 +519,21 @@ Pather.move = function (target, givenSettings = {}) { node = path.shift(); if (typeof settings.callback === "function" && settings.callback()) { - // console.debug("Callback function passed. Ending path."); - useTeleport && Config.TeleSwitch && me.switchWeapons(Attack.getPrimarySlot() ^ 1); - PathDebug.removeHooks(); - return true; + cbCheck = true; + break; } if (getDistance(me, node) > 2) { // Make life in Maggot Lair easier - fail >= 3 && fail % 3 === 0 && !Attack.validSpot(node.x, node.y) && (invalidCheck = true); + if (fail >= 3 && fail % 3 === 0 && !Attack.validSpot(node.x, node.y)) { + invalidCheck = true; + } // Make life in Maggot Lair easier - should this include arcane as well? if (annoyingArea || invalidCheck) { let adjustedNode = Pather.getNearestWalkable(node.x, node.y, 15, 3, sdk.collision.BlockWalk); if (adjustedNode) { - [node.x, node.y] = [adjustedNode[0], adjustedNode[1]]; + [node.x, node.y] = adjustedNode; invalidCheck && (invalidCheck = false); } @@ -496,8 +551,9 @@ Pather.move = function (target, givenSettings = {}) { try { Pather.recursion = false; /** - * @todo We need to pass our path in so we can fix the recursion issues of running forward on our path only to return to the old node and continue - * we should instead perform the actions in a way that moves us forward on our path ensuring we haven't skipped anything in the process as well + * @todo We need to pass our path in so we can fix the recursion issues of running forward on our path + * only to return to the old node and continue we should instead perform the actions in a way that moves + * us forward on our path ensuring we haven't skipped anything in the process as well * for long paths maybe generate a coordinate list of shrines/chests and have action hooks for them */ NodeAction.go(settings.clearSettings); @@ -509,39 +565,37 @@ Pather.move = function (target, givenSettings = {}) { // lets try and find the nearest node that brings us close to our goal /** @type {PathNode} */ let nearestNode = Pather.currentWalkingPath.length > 0 && Pather.currentWalkingPath - .filter(el => !!el && el.x !== node.x && el.y !== node.y) - .sort((a, b) => { - if (a.distance < b.distance && getDistance(a, lastNode) < getDistance(b, lastNode)) return -1; - if (a.distance > b.distance && getDistance(a, lastNode) > getDistance(b, lastNode)) return 1; - return a.distance - b.distance; + .filter(function (el) { + return !!el && el.x !== node.x && el.y !== node.y; + }) + .sort(function (a, b) { + const [aDist, bDist] = [a.distance, b.distance]; + const [aLastDist, bLastDist] = [getDistance(a, lastNode), getDistance(b, lastNode)]; + if (aDist < bDist && aLastDist < bLastDist) return -1; + if (aDist > bDist && aLastDist > bLastDist) return 1; + return aDist - bDist; }) - .find(pNode => pNode.distance > 5); + .find(function (pNode) { + return pNode.distance > 5; + }); if (node.distance < 40) { let goBack = false; - let foundNode = false; // lets see if it's worth walking back to old node Pickit.checkSpotForItems(node, true) && (goBack = true); // @todo check shrines/chests in proximity to old node vs next node - // let otherObjects = getUnits(sdk.unittype.Object).filter(el => getDistance()); if (goBack) { // console.debug("Going back to old node. Distance: " + node.distance); - } else if (nearestNode && nearestNode.distance > 5 && node.distance > 5 && Math.percentDifference(node.distance, nearestNode.distance) > 5/* && 100 / node.distance * nearestNode.distance < 95 */) { - // console.debug("Moving to next node. Distance: " + nearestNode.distance); + } else if (nearestNode && nearestNode.distance > 5 && node.distance > 5 + && Math.percentDifference(node.distance, nearestNode.distance) > 5) { let newIndex = path.findIndex(node => nearestNode.x === node.x && nearestNode.y === node.y); if (newIndex > -1) { - // console.debug("Found new path index: " + newIndex + " of currentPathLen: " + path.length); path = path.slice(newIndex); node = path.shift(); - foundNode = true; - // console.debug("New path length: " + path.length); - } else { - // console.debug("Couldn't find new path index"); } } if (node.distance > 5) { - // !foundNode && console.debug("Path Recursion :: Returning to position " + node.x + "/" + node.y + " distance: " + node.distance); Pather.move(node, settings); } } else { @@ -574,41 +628,46 @@ Pather.move = function (target, givenSettings = {}) { // if we are allowed to clear if (settings.allowClearing) { // Don't go berserk on longer paths - also check that there are even mobs blocking us - if (cleared.at === 0 || getTickCount() - cleared.at > Time.seconds(3) && cleared.where.distance > 5 && me.checkForMobs({ range: 10 })) { + if (cleared.at === 0 || getTickCount() - cleared.at > Time.seconds(3) + && cleared.node.distance > 5 && me.checkForMobs({ range: 10 })) { // only set that we cleared if we actually killed at least 1 mob if (Attack.clear(10, null, null, null, settings.allowPicking)) { // console.debug("Cleared Node"); cleared.at = getTickCount(); - [cleared.where.x, cleared.where.y] = [node.x, node.y]; + [cleared.node.x, cleared.node.y] = [node.x, node.y]; } } } // Leap can be helpful on long paths but make sure we don't spam it if (Skill.canUse(sdk.skills.LeapAttack)) { - // we can use leapAttack, now lets see if we should - either haven't used it yet or it's been long enough since last time - if (leaped.at === 0 || getTickCount() - leaped.at > Time.seconds(3) || leaped.from.distance > 5 || me.checkForMobs({ range: 6 })) { + // we can use leapAttack, now lets see if we should - either haven't used it yet + // or it's been long enough since last time + if (leaped.at === 0 || getTickCount() - leaped.at > Time.seconds(3) + || leaped.node.distance > 5 || me.checkForMobs({ range: 6 })) { // alright now if we have actually casted it set the values so we know if (Skill.cast(sdk.skills.LeapAttack, sdk.skills.hand.Right, node.x, node.y)) { leaped.at = getTickCount(); - [leaped.from.x, leaped.from.y] = [node.x, node.y]; + [leaped.node.x, leaped.node.y] = [node.x, node.y]; } } } /** - * whirlwind can be useful as well, implement it. - * Things to consider: - * 1) Can we cast whirlwind on the node? Is it blocked by something other than monsters. - * 2) If we can't cast on that node, is there another node between us and it that would work? - */ + * whirlwind can be useful as well, implement it. + * Things to consider: + * 1) Can we cast whirlwind on the node? Is it blocked by something other than monsters. + * 2) If we can't cast on that node, is there another node between us and it that would work? + */ if (Skill.canUse(sdk.skills.Whirlwind)) { - // we can use whirlwind, now lets see if we should - either haven't used it yet or it's been long enough since last time - if (whirled.at === 0 || getTickCount() - whirled.at > Time.seconds(3) || whirled.from.distance > 5 || me.checkForMobs({ range: 6 })) { + // we can use whirlwind, now lets see if we should - either haven't used it yet + // or it's been long enough since last time + if (whirled.at === 0 || getTickCount() - whirled.at > Time.seconds(3) + || whirled.node.distance > 5 || me.checkForMobs({ range: 6 })) { // alright now if we have actually casted it set the values so we know if (Skill.cast(sdk.skills.Whirlwind, sdk.skills.hand.Right, node.x, node.y)) { whirled.at = getTickCount(); - [whirled.from.x, whirled.from.y] = [node.x, node.y]; + [whirled.node.x, whirled.node.y] = [node.x, node.y]; } } } @@ -616,7 +675,13 @@ Pather.move = function (target, givenSettings = {}) { } // Reduce node distance in new path - path = getPath(me.area, target.x, target.y, me.x, me.y, useTeleport ? 1 : 0, useTeleport ? rand(25, 35) : rand(10, 15)); + path = getPath( + me.area, + target.x, target.y, + me.x, me.y, + useTeleport ? 1 : 0, + useTeleport ? rand(25, 35) : rand(10, 15) + ); if (!path) throw new Error("moveTo: Failed to generate path."); path.reverse(); @@ -641,25 +706,27 @@ Pather.move = function (target, givenSettings = {}) { } } - /** - * @todo handle passing in a callback function - */ - delay(5); } - useTeleport && Config.TeleSwitch && me.switchWeapons(Attack.getPrimarySlot() ^ 1); + useTeleport && Config.TeleSwitch && me.switchToPrimary(); PathDebug.removeHooks(); - return getDistance(me, node.x, node.y) < 5; + return cbCheck || getDistance(me, node.x, node.y) < 5; }; Pather.moveNear = function (x, y, minDist, givenSettings = {}) { - return Pather.move({ x: x, y: y }, Object.assign({ minDist: minDist }, givenSettings)); + return Pather.move( + { x: x, y: y }, + Object.assign({ minDist: minDist }, givenSettings) + ); }; Pather.moveTo = function (x, y, retry, clearPath = true, pop = false) { - return Pather.move({ x: x, y: y }, { retry: retry, pop: pop, clearSettings: { clearPath: clearPath } }); + return Pather.move( + { x: x, y: y }, + { retry: retry, pop: pop, clearSettings: { clearPath: clearPath } } + ); }; Pather.moveToLoc = function (target, givenSettings = {}) { @@ -670,8 +737,97 @@ Pather.moveToEx = function (x, y, givenSettings = {}) { return Pather.move({ x: x, y: y }, givenSettings); }; +/** + * @param {number} targetArea - area id or array of area ids to move to + * @param {boolean} [use] - enter target area or last area in the array + * @param {pathSettings} givenSettings + */ +Pather.moveToExit = function (targetArea, use, givenSettings = {}) { + if (targetArea === undefined) return false; + + const areas = Array.isArray(targetArea) + ? targetArea + : [targetArea]; + const finalDest = areas.last(); + const finalDestName = getAreaName(finalDest); + console.info(true, "ÿc7MyArea: ÿc0" + getAreaName(me.area) + " ÿc7TargetArea: ÿc0" + finalDestName, "moveToExit"); + + me.inArea(areas.first()) && areas.shift(); + + for (let currTarget of areas) { + console.info(null, getAreaName(me.area) + "ÿc8 --> ÿc0" + getAreaName(currTarget)); + + const area = Misc.poll(() => getArea(me.area)); + if (!area) throw new Error("moveToExit: error in getArea()"); + + /** @type {Array} */ + const exits = (area.exits || []); + if (!exits.length) return false; + + let checkExits = []; + for (let exit of exits) { + if (!exit.hasOwnProperty("target") || exit.target !== currTarget) continue; + checkExits.push(exit); + } + + if (checkExits.length > 0) { + // if there are multiple exits to the same location find the closest one + let currExit = checkExits.length > 1 + ? (() => { + let useExit = checkExits.shift(); // assign the first exit as a possible result + let dist = getDistance(me.x, me.y, useExit.x, useExit.y); + while (checkExits.length > 0) { + let exitDist = getDistance(me.x, me.y, checkExits[0].x, checkExits[0].y); + if (exitDist < dist) { + useExit = checkExits[0]; + dist = exitDist; + } + checkExits.shift(); + } + return useExit; + })() + : checkExits[0]; + let dest = this.getNearestWalkable(currExit.x, currExit.y, 5, 1); + if (!dest) return false; + + for (let retry = 0; retry < 3; retry++) { + if (this.moveToEx(dest[0], dest[1], givenSettings)) { + break; + } + + delay(200); + console.log("ÿc7(moveToExit) :: ÿc0Retry: " + (retry + 1)); + Misc.poll(() => me.gameReady, 1000, 200); + } + + if (use || currTarget !== finalDest) { + switch (currExit.type) { + case 1: // walk through + let targetRoom = this.getNearestRoom(currTarget); + // might need adjustments + if (!targetRoom) return false; + this.moveToEx(targetRoom[0], targetRoom[1], givenSettings); + + break; + case 2: // stairs + if (!this.openExit(currTarget) && !this.useUnit(sdk.unittype.Stairs, currExit.tileid, currTarget)) { + return false; + } + + break; + } + } + } + } + + console.info(false, "ÿc7targetArea: ÿc0" + finalDestName + " ÿc7myArea: ÿc0" + getAreaName(me.area), "moveToExit"); + delay(300); + + return (use && finalDest ? me.area === finalDest : true); +}; + // Add check in case "random" to return false if bot doesn't have cold plains wp yet -Pather.useWaypoint = function useWaypoint(targetArea, check = false, getWP = false) { +Pather.useWaypoint = function useWaypoint (targetArea, check = false) { switch (targetArea) { case undefined: throw new Error("useWaypoint: Invalid targetArea parameter: " + targetArea); @@ -687,29 +843,40 @@ Pather.useWaypoint = function useWaypoint(targetArea, check = false, getWP = fal break; } - let wpTick = getTickCount(); + console.time("useWaypoint"); - for (let i = 0; i < 12; i += 1) { + MainLoop: + for (let i = 0; i < 12; i++) { if (me.area === targetArea || me.dead) { break; } if (me.inTown) { - if (me.inArea(sdk.areas.LutGholein)) { + if (me.inArea(sdk.areas.LutGholein) && targetArea === sdk.areas.KurastDocktown) { + let npc = Game.getNPC(NPC.Meshif); + + if (!!npc && npc.distance < 50) { + if (!Pather.changeAct(3)) throw new Error("Failed to go to act 3 using Meshif"); + break; + } + } else if (me.inArea(sdk.areas.LutGholein)) { let npc = Game.getNPC(NPC.Warriv); if (!!npc && npc.distance < 50) { - if (npc && npc.openMenu()) { - Misc.useMenu(sdk.menu.GoWest); + if (!Pather.changeAct(1)) throw new Error("Failed to go to act 1 using Warriv"); + } + } else if (me.inArea(sdk.areas.KurastDocktown) && targetArea === sdk.areas.LutGholein) { + let npc = Game.getNPC(NPC.Meshif); - if (!Misc.poll(() => me.gameReady && me.inArea(sdk.areas.RogueEncampment), 2000, 100)) { - throw new Error("Failed to go to act 1 using Warriv"); - } - } + if (!!npc && npc.distance < 50) { + if (!Pather.changeAct(2)) throw new Error("Failed to go to act 2 using Meshif"); + break; } } - !getUIFlag(sdk.uiflags.Waypoint) && Town.getDistance("waypoint") > (Skill.haveTK ? 20 : 5) && Town.move("waypoint"); + if (!getUIFlag(sdk.uiflags.Waypoint) && Town.getDistance("waypoint") > (Skill.haveTK ? 20 : 5)) { + Town.move("waypoint"); + } } let wp = Game.getObject("waypoint"); @@ -721,7 +888,7 @@ Pather.useWaypoint = function useWaypoint(targetArea, check = false, getWP = fal if (useTK && !getUIFlag(sdk.uiflags.Waypoint)) { wp.distance > 21 && Pather.moveNearUnit(wp, 20); i > 1 && checkCollision(me, wp, sdk.collision.Ranged) && Attack.getIntoPosition(wp, 20, sdk.collision.Ranged); - Skill.cast(sdk.skills.Telekinesis, sdk.skills.hand.Right, wp); + Packet.telekinesis(wp); } else if (!me.inTown && wp.distance > 7) { this.moveToUnit(wp); } @@ -745,30 +912,15 @@ Pather.useWaypoint = function useWaypoint(targetArea, check = false, getWP = fal switch (targetArea) { case "random": - let retry = 0; - - while (true) { - targetArea = this.nonTownWpAreas[rand(0, this.nonTownWpAreas.length - 1)]; - - // get a valid wp, avoid towns - if (getWaypoint(this.wpAreas.indexOf(targetArea))) { + let validWps = this.nonTownWpAreas + .filter(area => getWaypoint(this.wpAreas.indexOf(area))); + if (!validWps.length) { + if (me.inTown && Pather.moveToExit(me.area + 1, true)) { break; } - - // no valid areas, get the cold plains wp - // maybe just walk out of town instead? - if (retry >= 10) { - if (!getWaypoint(this.wpAreas.indexOf(sdk.areas.ColdPlains)) && me.cancel()) { - me.overhead("Trying to get the waypoint"); - if (this.getWP(sdk.areas.ColdPlains)) return true; - - throw new Error("Pather.useWaypoint: Failed to go to waypoint " + targetArea); - } - } - - retry++; - delay(25); + throw new Error("Pather.useWaypoint: Failed to go to waypoint " + targetArea); } + targetArea = validWps.random(); break; case null: @@ -810,9 +962,8 @@ Pather.useWaypoint = function useWaypoint(targetArea, check = false, getWP = fal while (getTickCount() - tick < Math.max(Math.round((i + 1) * 1000 / (i / 5 + 1)), pingDelay * 4)) { if (me.area === targetArea) { delay(1500); - console.log("ÿc7End ÿc8(useWaypoint) ÿc0:: ÿc7targetArea: ÿc0" + getAreaName(targetArea) + " ÿc7myArea: ÿc0" + getAreaName(me.area) + "ÿc0 - ÿc7Duration: ÿc0" + (Time.format(getTickCount() - wpTick))); - return true; + break MainLoop; } delay(30); @@ -841,44 +992,54 @@ Pather.useWaypoint = function useWaypoint(targetArea, check = false, getWP = fal if (me.area === targetArea) { delay(500); - console.log("ÿc7End ÿc8(useWaypoint) ÿc0:: ÿc7targetArea: ÿc0" + getAreaName(targetArea) + " ÿc7myArea: ÿc0" + getAreaName(me.area) + "ÿc0 - ÿc7Duration: ÿc0" + (Time.format(getTickCount() - wpTick))); - + console.info( + false, + "ÿc7targetArea: ÿc0" + getAreaName(targetArea) + " ÿc7myArea: ÿc0" + getAreaName(me.area), + "useWaypoint" + ); return true; } throw new Error("useWaypoint: Failed to use waypoint to " + targetArea); }; -// credit - Legacy Autosmurf -Pather.clearToExit = function (currentarea, targetarea, cleartype = true) { - let tick = getTickCount(); +/** + * @param {number} currentarea + * @param {number} targetarea + * @param {pathSettings} givenSettings + * @returns {boolean} + */ +Pather.clearToExit = function (currentarea, targetarea, givenSettings = {}) { let retry = 0; - console.log("ÿc8Kolbot-SoloPlayÿc0: Start clearToExit. ÿc8Currently in: ÿc0" + getAreaName(me.area) + "ÿc8Clearing to: ÿc0" + getAreaName(targetarea)); + const targetName = getAreaName(targetarea); + console.info(true, getAreaName(me.area) + "ÿc8 --> ÿc0" + targetName, "clearToExit"); me.area !== currentarea && Pather.journeyTo(currentarea); - if (me.area !== targetarea) { - do { - try { - Pather.moveToExit(targetarea, true, cleartype); - } catch (e) { - console.debug("Caught Error: ", e.message ? e.message : e); - } + if (typeof givenSettings === "boolean") { + givenSettings = { allowClearing: givenSettings }; + } - delay(500); - Misc.poll(() => me.gameReady, 1000, 100); - - if (retry > 5) { - console.log("ÿc8Kolbot-SoloPlayÿc0: clearToExit. ÿc2Failed to move to: ÿc0" + getAreaName(targetarea)); + while (me.area !== targetarea) { + try { + Pather.moveToExit(targetarea, true, givenSettings); + } catch (e) { + console.error(e); + } - break; - } + delay(500); + Misc.poll(() => me.gameReady, 1000, 100); + + if (retry > 5) { + console.error("ÿc2Failed to move to: ÿc0" + targetName); + + break; + } - retry++; - } while (me.area !== targetarea); + retry++; } - console.log("ÿc8Kolbot-SoloPlayÿc0: End clearToExit. Time elapsed: " + Time.format(getTickCount() - tick)); + console.info(false, "", "clearToExit"); return (me.area === targetarea); }; From ec60905b87545137f05be7a0685e13d721cd0658 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Mon, 5 Jun 2023 14:39:10 -0400 Subject: [PATCH 135/263] Update SoloIndex.js - fix barb getting stuck at lvl 8 --- libs/SoloPlay/Tools/SoloIndex.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/libs/SoloPlay/Tools/SoloIndex.js b/libs/SoloPlay/Tools/SoloIndex.js index a85cd9ef..b69d8332 100644 --- a/libs/SoloPlay/Tools/SoloIndex.js +++ b/libs/SoloPlay/Tools/SoloIndex.js @@ -112,7 +112,7 @@ const SoloIndex = { }, shouldRun: function () { switch (true) { - case (me.normal && (!me.tristram || me.charlvl < (me.classic || !me.barbarian ? 12 : 6) || Check.brokeAf())): + case (me.normal && (!me.tristram || me.charlvl < (me.classic || !me.barbarian ? 12 : 8) || Check.brokeAf())): case (me.nightmare && ((!me.tristram && me.charlvl < 43) || Check.brokeAf())): case (me.hell && ((!me.tristram && me.diffCompleted) || !this.skipIf())): return true; @@ -141,6 +141,7 @@ const SoloIndex = { let needRunes = Check.runes(); switch (true) { case (me.normal && (needRunes || Check.brokeAf())): // todo - better determination for low gold + case (me.barbarian && me.charlvl > 8 && me.charlvl < 12): // better for barb than trist runs case (me.barbarian && me.hell && me.checkItem({ name: sdk.locale.items.Lawbringer }).have): case (!me.normal && (Pather.canTeleport() || me.charlvl < 60)): return true; @@ -150,7 +151,7 @@ const SoloIndex = { }, "smith": { preReq: function () { - return me.charlvl > 8; + return me.charlvl > 6; }, skipIf: function () { // todo - test leveling/experience potential From 5e83cc195bb75cc9251b05f6cd91c2bae91d37da Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Mon, 5 Jun 2023 21:27:12 -0400 Subject: [PATCH 136/263] Update PatherOverrides.js - hopefully fixed tele charge repair check --- libs/SoloPlay/Functions/PatherOverrides.js | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/libs/SoloPlay/Functions/PatherOverrides.js b/libs/SoloPlay/Functions/PatherOverrides.js index 7b3b8be5..f5b750bb 100644 --- a/libs/SoloPlay/Functions/PatherOverrides.js +++ b/libs/SoloPlay/Functions/PatherOverrides.js @@ -249,7 +249,6 @@ Pather.teleUsingCharges = function (x, y, maxRange = 5) { while (getTickCount() - tick < Math.max(500, me.ping * 2 + 200)) { if (getDistance(me.x, me.y, x, y) < maxRange) { - me.weaponswitch !== orgSlot && me.switchWeapons(orgSlot); return true; } @@ -257,11 +256,15 @@ Pather.teleUsingCharges = function (x, y, maxRange = 5) { } } - if (me.gold > me.getRepairCost() * 3 && me.canTpToTown()) { - console.debug("Tele-Charge repair"); - Town.visitTown(true); - } else { - this.haveTeleCharges = false; + if (CharData.skillData.haveChargedSkill(sdk.skills.Teleport) + && !Attack.getItemCharges(sdk.skills.Teleport)) { + + if (me.gold > me.getRepairCost() * 3 && me.canTpToTown()) { + console.debug("Tele-Charge repair"); + Town.visitTown(true); + } else { + this.haveTeleCharges = false; + } } return false; From 2b947a707118531058d81243fc044f5169aae722 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Mon, 5 Jun 2023 21:29:05 -0400 Subject: [PATCH 137/263] Refactoring mostly - cleaned up the charge skill related checks. I had a whole lot of duplicated code --- libs/SoloPlay/Functions/AttackOverrides.js | 126 ++++++++---------- libs/SoloPlay/Functions/PrototypeOverrides.js | 28 ++-- libs/SoloPlay/Tools/CharData.js | 31 +++-- libs/SoloPlay/index.d.ts | 28 ++-- 4 files changed, 108 insertions(+), 105 deletions(-) diff --git a/libs/SoloPlay/Functions/AttackOverrides.js b/libs/SoloPlay/Functions/AttackOverrides.js index f076ec41..4cdd6ad2 100644 --- a/libs/SoloPlay/Functions/AttackOverrides.js +++ b/libs/SoloPlay/Functions/AttackOverrides.js @@ -875,64 +875,56 @@ Attack.useBowOnSwitch = function (unit, skillId = 0, switchBack = true) { // maybe store the copyUnit of the item or at least gid so we don't need to iterate through all our items to find the one with the charged skill when we need it Attack.getCurrentChargedSkillIds = function (init = false) { - const chargeSkillObj = (skill, level, gid) => ({ skill: skill, level: level, gid: gid }); - let [currentChargedSkills, chargedSkills, chargedSkillsOnSwitch] = [[], [], []]; + /** + * @typedef {Object} Charge + * @property {number} skill + * @property {number} level + * @property {number} charges + * @property {number} maxcharges + */ + + /** + * @constructor + * @param {Charge} charge + * @param {number} gid + */ + function ChargedSkill (charge, gid) { + this.skill = charge.skill; + this.level = charge.level; + this.charges = charge.charges; + this.maxcharges = charge.maxcharges; + this.gid = gid; + } + + /** @type {Array} */ + const currentChargedSkills = []; + /** @type {Array[]} */ + const [chargedSkills, chargedSkillsOnSwitch] = [[], []]; // Item must be equipped - removed charms as I don't think at any point using hydra from torch has ever been worth it me.getItemsEx(-1) .filter(item => item && ((item.isEquipped /* && !item.rare */))) .forEach(function (item) { let stats = item.getStat(-2); + if (!stats.hasOwnProperty(sdk.stats.ChargedSkill)) return; + + /** @type {Array | Charge} */ + let charges = stats[sdk.stats.ChargedSkill]; + // simplfy calc by making it an array if it isn't already + if (!(charges instanceof Array)) charges = [charges]; + + for (let charge of charges) { + // add to total list of skillIds + if (charge.charges > 0 && !currentChargedSkills.includes(charge.skill)) { + currentChargedSkills.push(charge.skill); + chargedSkills.push(new ChargedSkill(charge, item.gid)); + } - if (stats.hasOwnProperty(sdk.stats.ChargedSkill)) { - if (stats[sdk.stats.ChargedSkill] instanceof Array) { - for (let i = 0; i < stats[sdk.stats.ChargedSkill].length; i += 1) { - if (stats[sdk.stats.ChargedSkill][i] !== undefined) { - // add to total list of skillIds - if (stats[sdk.stats.ChargedSkill][i].charges > 0 - && !currentChargedSkills.includes(stats[sdk.stats.ChargedSkill][i].skill)) { - currentChargedSkills.push(stats[sdk.stats.ChargedSkill][i].skill); - chargedSkills.push(chargeSkillObj( - stats[sdk.stats.ChargedSkill][i].skill, - stats[sdk.stats.ChargedSkill][i].level, - item.gid) - ); - } - - // add to switch only list for use with swtich casting - if (stats[sdk.stats.ChargedSkill][i].charges > 0 - && !chargedSkillsOnSwitch.some(cSk => cSk.skill === stats[sdk.stats.ChargedSkill][i].skill) - && item.isOnSwap) { - chargedSkillsOnSwitch.push(chargeSkillObj( - stats[sdk.stats.ChargedSkill][i].skill, - stats[sdk.stats.ChargedSkill][i].level, - item.gid) - ); - } - } - } - } else { - // add to total list - if (stats[sdk.stats.ChargedSkill].charges > 0 - && !currentChargedSkills.includes(stats[sdk.stats.ChargedSkill].skill)) { - currentChargedSkills.push(stats[sdk.stats.ChargedSkill].skill); - chargedSkills.push(chargeSkillObj( - stats[sdk.stats.ChargedSkill].skill, - stats[sdk.stats.ChargedSkill].level, - item.gid) - ); - } - - // add to switch only list for use with swtich casting - if (stats[sdk.stats.ChargedSkill].charges > 0 - && !chargedSkillsOnSwitch.some(cSk => cSk.skill === stats[sdk.stats.ChargedSkill].skill) - && item.isOnSwap) { - chargedSkillsOnSwitch.push(chargeSkillObj( - stats[sdk.stats.ChargedSkill].skill, - stats[sdk.stats.ChargedSkill].level, - item.gid) - ); - } + // add to switch only list for use with swtich casting + if (charge.charges > 0 + && !chargedSkillsOnSwitch.some(cSk => cSk.skill === charge.skill) + && item.isOnSwap) { + chargedSkillsOnSwitch.push(new ChargedSkill(charge, item.gid)); } } }); @@ -944,7 +936,6 @@ Attack.getCurrentChargedSkillIds = function (init = false) { case Object.keys(Misc.recursiveSearch(chargedSkillsOnSwitch, CharData.skillData.chargedSkillsOnSwitch)).length > 0: case Object.keys(Misc.recursiveSearch(chargedSkills, CharData.skillData.chargedSkills)).length > 0: CharData.skillData.init(currentChargedSkills, chargedSkills, chargedSkillsOnSwitch); - !init && CharData.skillData.update(); break; } } @@ -964,26 +955,25 @@ Attack.getItemCharges = function (skillId) { return itemCharge.skill === skillId && itemCharge.charges > 1; }; - // Item must equipped, or a charm in inventory + // Item must equipped, or a ~charm in inventory~ removed charms as I don't think at any point using hydra from torch has ever been worth it me.getItemsEx(-1) - .filter(item => item && (item.isEquipped && !item.rare || (item.isInInventory && item.isCharm))) + .filter(item => item && (item.isEquipped && !item.rare)) .forEach(function (item) { let stats = item.getStat(-2); - - if (stats.hasOwnProperty(sdk.stats.ChargedSkill)) { - if (stats[sdk.stats.ChargedSkill] instanceof Array) { - stats = stats[sdk.stats.ChargedSkill].filter(validCharge); - stats.length && chargedItems.push({ - charge: stats.first(), + if (!stats.hasOwnProperty(sdk.stats.ChargedSkill)) return; + + /** @type {Array | Charge} */ + let charges = stats[sdk.stats.ChargedSkill]; + // simplfy calc by making it an array if it isn't already + if (!(charges instanceof Array)) charges = [charges]; + + for (let charge of charges) { + if (validCharge(charge)) { + chargedItems.push({ + skill: charge.skill, + charge: charge.charges, item: item }); - } else { - if (stats[sdk.stats.ChargedSkill].skill === skillId && stats[sdk.stats.ChargedSkill].charges > 1) { - chargedItems.push({ - charge: stats[sdk.stats.ChargedSkill].charges, - item: item - }); - } } } }); @@ -1025,7 +1015,7 @@ Attack.switchCastCharges = function (skillId, unit) { try { me.castSwitchChargedSkill(skillId, unit) && delay(25); } finally { - me.weaponswitch === 1 && me.switchWeapons(0); + me.switchToPrimary(); } return true; diff --git a/libs/SoloPlay/Functions/PrototypeOverrides.js b/libs/SoloPlay/Functions/PrototypeOverrides.js index aa2961d6..0553c0a7 100644 --- a/libs/SoloPlay/Functions/PrototypeOverrides.js +++ b/libs/SoloPlay/Functions/PrototypeOverrides.js @@ -376,8 +376,9 @@ Unit.prototype.castChargedSkillEx = function (...args) { let chargedItems = []; - CharData.skillData.chargedSkills.forEach(function (chargeSkill) { - if (chargeSkill.skill === skillId) { + CharData.skillData.chargedSkills + .forEach(function (chargeSkill) { + if (chargeSkill.skill !== skillId) return; console.debug(chargeSkill); let item = me.getItem(-1, sdk.items.mode.Equipped, chargeSkill.gid); !!item && chargedItems.push({ @@ -385,14 +386,14 @@ Unit.prototype.castChargedSkillEx = function (...args) { level: chargeSkill.level, item: item }); - } - }); + }); if (chargedItems.length === 0) { console.log("ÿc9CastChargedSkillÿc0 :: Don't have the charged skill (" + skillId + "), or not enough charges"); return false; } + /** @type {ItemUnit} */ let chargedItem = chargedItems .sort(function (a, b) { return b.charge.level - a.charge.level; @@ -415,7 +416,9 @@ Unit.prototype.castChargedSkillEx = function (...args) { if (charge instanceof Array) { // Filter out all other charged skills charge = charge - .filter(item => (item && item.skill === skillId) && !!item.charges) + .filter(function (item) { + return (item && item.skill === skillId) && !!item.charges; + }) .first(); } else { if (charge.skill !== skillId || !charge.charges) { @@ -429,12 +432,15 @@ Unit.prototype.castChargedSkillEx = function (...args) { if (charge) { const usePacket = ([ - sdk.skills.Valkyrie, sdk.skills.Decoy, sdk.skills.RaiseSkeleton, sdk.skills.ClayGolem, - sdk.skills.RaiseSkeletalMage, sdk.skills.BloodGolem, sdk.skills.Shout, - sdk.skills.IronGolem, sdk.skills.Revive, sdk.skills.Werewolf, sdk.skills.Werebear, - sdk.skills.OakSage, sdk.skills.SpiritWolf, sdk.skills.PoisonCreeper, sdk.skills.BattleOrders, - sdk.skills.SummonDireWolf, sdk.skills.Grizzly, sdk.skills.HeartofWolverine, sdk.skills.SpiritofBarbs, - sdk.skills.ShadowMaster, sdk.skills.ShadowWarrior, sdk.skills.BattleCommand, + sdk.skills.Teleport, sdk.skills.Valkyrie, sdk.skills.Decoy, + sdk.skills.RaiseSkeleton, sdk.skills.ClayGolem, + sdk.skills.RaiseSkeletalMage, sdk.skills.BloodGolem, + sdk.skills.IronGolem, sdk.skills.Revive, + sdk.skills.Werewolf, sdk.skills.Werebear, + sdk.skills.OakSage, sdk.skills.SpiritWolf, sdk.skills.PoisonCreeper, + sdk.skills.SummonDireWolf, sdk.skills.Grizzly, + sdk.skills.HeartofWolverine, sdk.skills.SpiritofBarbs, + sdk.skills.ShadowMaster, sdk.skills.ShadowWarrior ].indexOf(skillId) === -1); if (!usePacket) { diff --git a/libs/SoloPlay/Tools/CharData.js b/libs/SoloPlay/Tools/CharData.js index e310975c..66edaacd 100644 --- a/libs/SoloPlay/Tools/CharData.js +++ b/libs/SoloPlay/Tools/CharData.js @@ -136,7 +136,7 @@ const CharData = (function () { Charm.prototype.count = function () { let [curr, max] = [0, 0]; - Object.keys(me.data.charms).forEach(cKey => { + Object.keys(me.data.charms).forEach(function (cKey) { if (me.data.charms[cKey].classid === this.classid) { curr += me.data.charms[cKey].have.length; max += me.data.charms[cKey].max; @@ -149,15 +149,14 @@ const CharData = (function () { }; }; /** @type {Map skillid.includes(s)); + return this.currentChargedSkills + .some(function (s) { + return skillid.includes(s); + }); }, haveChargedSkillOnSwitch: function (skillid = 0) { - return this.chargedSkillsOnSwitch.some(chargeSkill => chargeSkill.skill === skillid); + return this.chargedSkillsOnSwitch + .some(function (chargeSkill) { + return chargeSkill.skill === skillid; + }); } }, diff --git a/libs/SoloPlay/index.d.ts b/libs/SoloPlay/index.d.ts index b0fb808f..7b77dda8 100644 --- a/libs/SoloPlay/index.d.ts +++ b/libs/SoloPlay/index.d.ts @@ -113,19 +113,6 @@ declare global { twoHandedCheck: (strict?: boolean) => boolean; } - // type EquippedItem = { - // classid: number; - // name: string; - // fname: string; - // quality: number; - // prefixnum: number; - // suffixnum: number; - // itemType: number; - // strreq: number; - // dexreq: number; - // sockets: number; - // getStat: (stat: number, subid: number) => number; - // }; type EquippedMap = Map; interface MeType { @@ -238,6 +225,13 @@ declare global { actMap: Map; } + type Charge = { + skill: number; + level: number; + charges: number; + maxcharges: number; + }; + namespace Mercenary { let minCost: number; @@ -278,6 +272,10 @@ declare global { function switchCast(skillId: number, givenSettings: { hand?: number, x?: number, y?: number, switchBack?: boolean, oSkill?: boolean }): boolean; } + namespace Pather { + function clearToExit(currentarea: number, targetarea: number, givenSettings: pathSettings): boolean; + } + namespace CharData { const filePath: string; const threads: string[]; @@ -360,5 +358,9 @@ declare global { namespace Town { function doChores(repair?: boolean, givenTasks?: extraTasks): boolean; } + + namespace LocationAction { + function run(): void; + } } export{}; From ac9126ebf52999d96accfc36a9c14eca644c374e Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Mon, 5 Jun 2023 21:31:33 -0400 Subject: [PATCH 138/263] Add tele staff on switch for necro --- .../necromancer/necromancer.BoneBuild.js | 31 ++++++---- libs/SoloPlay/Config/Necromancer.js | 59 +++++++++++++++---- 2 files changed, 67 insertions(+), 23 deletions(-) diff --git a/libs/SoloPlay/BuildFiles/necromancer/necromancer.BoneBuild.js b/libs/SoloPlay/BuildFiles/necromancer/necromancer.BoneBuild.js index edda36ac..5edeb29f 100644 --- a/libs/SoloPlay/BuildFiles/necromancer/necromancer.BoneBuild.js +++ b/libs/SoloPlay/BuildFiles/necromancer/necromancer.BoneBuild.js @@ -12,7 +12,10 @@ caster: true, skillstab: sdk.skills.tabs.PoisonandBone, wantedskills: [sdk.skills.BoneSpirit, sdk.skills.BoneSpear, sdk.skills.Teeth], - usefulskills: [sdk.skills.AmplifyDamage, sdk.skills.BoneArmor, sdk.skills.Decrepify, sdk.skills.BoneWall, sdk.skills.BonePrison], + usefulskills: [ + sdk.skills.AmplifyDamage, sdk.skills.BoneArmor, + sdk.skills.Decrepify, sdk.skills.BoneWall, sdk.skills.BonePrison + ], precastSkills: [sdk.skills.BoneArmor], wantedMerc: MercData[sdk.skills.Might], skills: [ @@ -35,7 +38,8 @@ have: [], classid: sdk.items.SmallCharm, stats: function (check) { - return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MaxHp) === 20); + return (!check.unique && check.classid === this.classid + && check.allRes === 5 && check.getStat(sdk.stats.MaxHp) === 20); } }, @@ -44,7 +48,8 @@ have: [], classid: sdk.items.SmallCharm, stats: function (check) { - return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MagicBonus) === 7); + return (!check.unique && check.classid === this.classid + && check.allRes === 5 && check.getStat(sdk.stats.MagicBonus) === 7); } }, @@ -53,7 +58,8 @@ have: [], classid: sdk.items.SmallCharm, stats: function (check) { - return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.FHR) === 5); + return (!check.unique && check.classid === this.classid + && check.allRes === 5 && check.getStat(sdk.stats.FHR) === 5); } }, @@ -62,7 +68,8 @@ have: [], classid: sdk.items.GrandCharm, stats: function (check) { - return (!check.unique && check.classid === this.classid && check.getStat(sdk.stats.AddSkillTab, sdk.skills.tabs.PoisonandBone) === 1 + return (!check.unique && check.classid === this.classid + && check.getStat(sdk.stats.AddSkillTab, sdk.skills.tabs.PoisonandBone) === 1 && check.getStat(sdk.stats.MaxHp) >= 40); } }, @@ -119,8 +126,8 @@ // Gloves - Magefist "[name] == lightgauntlets && [quality] == unique # [fcr] >= 20 && [addfireskills] == 1 # [tier] == tierscore(item, 100000)", ] : [ - // Weapon - "([type] == wand || [type] == sword && ([quality] >= magic || [flag] == runeword) || [type] == knife && [quality] >= magic) && [flag] != ethereal # [secondarymindamage] == 0 && [itemchargedskill] >= 0 # [tier] == tierscore(item)", + // Weapon - White + "[type] == wand && [flag] == runeword # [skillbonespear] == 8 && [skillbonespirit] == 6 # [tier] == 100000", // Helmet - Harlequin's Crest "[name] == shako && [quality] == unique && [flag] != ethereal # [damageresist] == 10 # [tier] == tierscore(item, 100000)", // Belt - Arach's @@ -131,8 +138,8 @@ "[name] == battleboots && [quality] == unique && [flag] != ethereal # [itemmagicbonus] >= 50 # [tier] == tierscore(item, 5000)", // Armor - Enigma "[type] == armor && [flag] != ethereal && [flag] == runeword # [itemallskills] == 2 # [tier] == 100000", - // Shield - "([type] == shield && ([quality] >= magic || [flag] == runeword) || [type] == voodooheads) && [flag] != ethereal # [itemchargedskill] >= 0 # [tier] == tierscore(item)", + // Shield - Perfect Spirit Monarch + "[name] == monarch && [flag] == runeword # [fcr] == 35 && [maxmana] == 112 # [tier] == 100000", // Final Gloves - Perfect 2x Upp'ed Magefist "[name] == crusadergauntlets && [quality] == unique && [flag] != ethereal # [enhanceddefense] == 30 && [addfireskills] == 1 # [tier] == 110000", // Gloves - 2x Upp'ed Magefist @@ -148,8 +155,6 @@ "[type] == ring && [quality] == unique # [itemmaxmanapercent] == 25 # [tier] == 100000", // Switch - CTA "[minimumsockets] >= 5 && [flag] == runeword # [plusskillbattleorders] >= 1 # [secondarytier] == 100000", - // Switch - Spirit - "[name] == monarch && [flag] == runeword # [fcr] >= 25 && [maxmana] >= 89 # [secondarytier] == 110000", // Merc Armor - Fortitude "[type] == armor && [flag] == runeword # [enhanceddefense] >= 200 && [enhanceddamage] >= 300 # [merctier] == 100000", // Merc Final Helmet - Eth Andy's @@ -159,6 +164,10 @@ ]; NTIP.buildList(finalGear); + if (me.expansion && me.equipped.get(sdk.body.RightArmSecondary).prefixnum === sdk.locale.items.CalltoArms) { + // Switch - Spirit + NTIP.addLine("[name] == monarch && [flag] == runeword # [fcr] >= 25 && [maxmana] >= 89 # [secondarytier] == 110000"); + } NTIP.buildFinalGear(finalGear); return build; diff --git a/libs/SoloPlay/Config/Necromancer.js b/libs/SoloPlay/Config/Necromancer.js index 0c97118d..837e581e 100644 --- a/libs/SoloPlay/Config/Necromancer.js +++ b/libs/SoloPlay/Config/Necromancer.js @@ -19,6 +19,8 @@ includeIfNotIncluded("SoloPlay/Functions/MiscOverrides.js"); includeIfNotIncluded("SoloPlay/Functions/Globals.js"); + const LADDER_ENABLED = (me.ladder || Developer.addLadderRW); + SetUp.include(); SetUp.config(); @@ -90,14 +92,42 @@ ? sdk.skills.PoisonExplosion : 0; - Config.imbueables = [ - { name: sdk.items.DemonHead, condition: () => (me.normal && me.expansion) }, - { name: sdk.items.HierophantTrophy, condition: () => (!me.normal && (me.charlvl < 66 || me.trueStr < 106) && me.expansion) }, - { name: sdk.items.BloodlordSkull, condition: () => (me.equipped.get(sdk.body.LeftArm).tier < 1000 && me.expansion) }, - { name: sdk.items.Belt, condition: () => (me.normal && (me.equipped.get(sdk.body.LeftArm).tier > 1000 || me.classic)) }, - { name: sdk.items.MeshBelt, condition: () => (!me.normal && me.charlvl < 46 && me.trueStr > 58 && (me.equipped.get(sdk.body.LeftArm).tier > 1000 || me.classic)) }, - { name: sdk.items.SpiderwebSash, condition: () => (!me.normal && me.trueStr > 50 && (me.equipped.get(sdk.body.LeftArm).tier > 1000 || me.classic)) }, - ].filter((item) => item.condition()); + Config.imbueables = (function () { + /** + * @param {number} name + * @param {function(): boolean} condition + */ + const _imbueObj = (name, condition) => ({ name: name, condition: condition }); + + return [ + _imbueObj( + sdk.items.DemonHead, + () => (me.normal && me.expansion) + ), + _imbueObj( + sdk.items.HierophantTrophy, + () => (!me.normal && (me.charlvl < 66 || me.trueStr < 106) && me.expansion) + ), + _imbueObj( + sdk.items.BloodlordSkull, + () => (me.equipped.get(sdk.body.LeftArm).tier < 1000 && me.expansion) + ), + _imbueObj( + sdk.items.Belt, + () => (me.normal && (me.equipped.get(sdk.body.LeftArm).tier > 1000 || me.classic)) + ), + _imbueObj( + sdk.items.MeshBelt, + () => (!me.normal && me.charlvl < 46 && me.trueStr > 58 + && (me.equipped.get(sdk.body.LeftArm).tier > 1000 || me.classic)) + ), + _imbueObj( + sdk.items.SpiderwebSash, + () => (!me.normal && me.trueStr > 50 + && (me.equipped.get(sdk.body.LeftArm).tier > 1000 || me.classic)) + ), + ].filter((item) => item.condition()); + })(); let imbueArr = SetUp.imbueItems(); @@ -148,6 +178,7 @@ // Call to Arms if (!me.checkItem({ name: sdk.locale.items.CalltoArms }).have) { + NTIP.addLine("[type] == staff && [quality] == Magic # [itemchargedskill] == 54 # [secondarytier] == 50000 + chargeditemscore(item, 54)"); includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/CallToArms.js"); } @@ -167,21 +198,25 @@ } // Spirit Sword - if ((me.ladder || Developer.addLadderRW) && me.equipped.get(sdk.body.RightArm).tier < 777) { + if (LADDER_ENABLED && me.equipped.get(sdk.body.RightArm).tier < 777) { includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/SpiritSword.js"); } // Spirit shield - if ((me.ladder || Developer.addLadderRW) && (me.equipped.get(sdk.body.LeftArm).tier < 1000 || me.equipped.get(sdk.body.LeftArmSecondary).prefixnum !== sdk.locale.items.Spirit)) { + if (LADDER_ENABLED + && (me.equipped.get(sdk.body.LeftArm).tier < 1000 + || (me.equipped.get(sdk.body.LeftArmSecondary).prefixnum !== sdk.locale.items.Spirit) + && me.equipped.get(sdk.body.RightArmSecondary).prefixnum === sdk.locale.items.CalltoArms)) { includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/SpiritShield.js"); } // Merc Insight - if ((me.ladder || Developer.addLadderRW) && Item.getMercEquipped(sdk.body.RightArm).tier < 3600) { + if (LADDER_ENABLED && Item.getMercEquipped(sdk.body.RightArm).tier < 3600) { includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/MercInsight.js"); } - if (!me.haveSome([{ name: sdk.locale.items.Enigma }, { name: sdk.locale.items.Bone }]) && me.equipped.get(sdk.body.Armor).tier < 650) { + if (!me.haveSome([{ name: sdk.locale.items.Enigma }, { name: sdk.locale.items.Bone }]) + && me.equipped.get(sdk.body.Armor).tier < 650) { includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/Bone.js"); } From 54d05ce7b8c36fbc2f0e891fa6d6fd1eec42256a Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Mon, 5 Jun 2023 21:32:33 -0400 Subject: [PATCH 139/263] Update necromancer.LevelingBuild.js - put some points into mana during leveling build, it was struggling real bad directly after respec due this --- .../necromancer/necromancer.LevelingBuild.js | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/libs/SoloPlay/BuildFiles/necromancer/necromancer.LevelingBuild.js b/libs/SoloPlay/BuildFiles/necromancer/necromancer.LevelingBuild.js index 79488587..58773b7c 100644 --- a/libs/SoloPlay/BuildFiles/necromancer/necromancer.LevelingBuild.js +++ b/libs/SoloPlay/BuildFiles/necromancer/necromancer.LevelingBuild.js @@ -13,7 +13,11 @@ caster: true, skillstab: sdk.skills.tabs.PoisonandBone, wantedskills: [sdk.skills.CorpseExplosion, sdk.skills.BoneSpear], - usefulskills: [sdk.skills.AmplifyDamage, sdk.skills.BoneArmor, sdk.skills.Decrepify, sdk.skills.BoneWall, sdk.skills.BonePrison, sdk.skills.BoneSpirit, sdk.skills.Teeth], + usefulskills: [ + sdk.skills.AmplifyDamage, sdk.skills.BoneArmor, + sdk.skills.Decrepify, sdk.skills.BoneWall, + sdk.skills.BonePrison, sdk.skills.BoneSpirit, sdk.skills.Teeth + ], wantedMerc: MercData[sdk.skills.Might], skills: [ // Total skills at respec = 29 @@ -33,7 +37,10 @@ stats: [], active: function () { - return (me.charlvl > CharInfo.respecOne && me.charlvl > CharInfo.respecTwo && me.checkSkill(sdk.skills.BonePrison, sdk.skills.subindex.HardPoints) && !Check.finalBuild().active()); + return (me.charlvl > CharInfo.respecOne + && me.charlvl > CharInfo.respecTwo + && me.checkSkill(sdk.skills.BonePrison, sdk.skills.subindex.HardPoints) + && !Check.finalBuild().active()); }, AutoBuildTemplate: { @@ -58,8 +65,10 @@ ? [ ["dexterity", 51], ["strength", 80], ["energy", 100], ["vitality", "all"] ] : [ - ["strength", 48], ["vitality", 165], ["strength", 61], - ["vitality", 252], ["strength", 156], ["vitality", "all"] + ["strength", 48], ["energy", 50], + ["vitality", 100], ["strength", 61], + ["vitality", 165], ["vitality", 252], + ["strength", 156], ["vitality", "all"] ]; return build; From ae2325ec6426b6be8f2f921e07f6863d63916f40 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Mon, 5 Jun 2023 23:45:59 -0400 Subject: [PATCH 140/263] Update NecromancerAttacks.js - keep bone armor active, todo is test using worker to check if we need to precast - fix typo, smartCurse no longer exists - change deploy to getIntoPosition, still need better control over how bots move during this action --- .../ClassAttackOverrides/NecromancerAttacks.js | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/libs/SoloPlay/Functions/ClassAttackOverrides/NecromancerAttacks.js b/libs/SoloPlay/Functions/ClassAttackOverrides/NecromancerAttacks.js index 6b45e64f..275f0f70 100644 --- a/libs/SoloPlay/Functions/ClassAttackOverrides/NecromancerAttacks.js +++ b/libs/SoloPlay/Functions/ClassAttackOverrides/NecromancerAttacks.js @@ -197,8 +197,10 @@ includeIfNotIncluded("core/Attacks/Necromancer.js"); if (me.expansion && index === 1 && !unit.dead) { if (CharData.skillData.haveChargedSkill(sdk.skills.SlowMissiles) - && unit.getEnchant(sdk.enchant.LightningEnchanted) && !unit.getState(sdk.states.SlowMissiles) - && unit.curseable && (gold > 500000 && !unit.isBoss) && !checkCollision(me, unit, sdk.collision.Ranged)) { + && unit.getEnchant(sdk.enchant.LightningEnchanted) + && !unit.getState(sdk.states.SlowMissiles) + && unit.curseable && (gold > 500000 && !unit.isBoss) + && !checkCollision(me, unit, sdk.collision.Ranged)) { // Cast slow missiles Attack.castCharges(sdk.skills.SlowMissiles, unit); } @@ -294,7 +296,7 @@ includeIfNotIncluded("core/Attacks/Necromancer.js"); Config.ActiveSummon && this.raiseArmy(); this.explodeCorpses(unit); - this.smartCurse(unit); + doCurse(unit); let closeMob = Attack.getNearestMonster({ skipGid: gid }); if (!!closeMob) { let findSkill = Attack.decideSkill(closeMob); @@ -325,6 +327,11 @@ includeIfNotIncluded("core/Attacks/Necromancer.js"); this.checkCorpseNearMonster(unit) && this.explodeCorpses(unit); } + if (Skill.canUse(sdk.skills.BoneArmor) && !me.getState(sdk.states.BoneArmor)) { + // make sure we keep this up + Skill.cast(sdk.skills.BoneArmor, sdk.skills.hand.Right); + } + let walk; let lowMana = true; @@ -383,7 +390,7 @@ includeIfNotIncluded("core/Attacks/Necromancer.js"); if (!unit.dead) { // Try to find better spot if (unit.distance < 4 && timedSkillRange > 6) { - Attack.deploy(unit, 4, 5, 9); + Attack.getIntoPosition(unit, timedSkillRange, sdk.collision.BlockMissile, true); } Skill.cast(timedSkill, Skill.getHand(timedSkill), unit); From f44f25bb9123fa714f58e3011cdd738f0944e9be Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Wed, 7 Jun 2023 12:37:39 -0400 Subject: [PATCH 141/263] ensure merc data stays updated - every time we revive the merc check against our stored data to ensure it's accuracy --- libs/SoloPlay/Functions/NPCAction.js | 41 +++++++++++++++++++++++++++- libs/SoloPlay/Tools/CharData.js | 31 ++++++++++++++++----- 2 files changed, 64 insertions(+), 8 deletions(-) diff --git a/libs/SoloPlay/Functions/NPCAction.js b/libs/SoloPlay/Functions/NPCAction.js index 6b854567..c4a54f50 100644 --- a/libs/SoloPlay/Functions/NPCAction.js +++ b/libs/SoloPlay/Functions/NPCAction.js @@ -696,11 +696,50 @@ } } + let merc = null; let tick = getTickCount(); while (getTickCount() - tick < 2000) { - if (me.getMercEx()) { + if ((merc = me.getMercEx())) { delay(Math.max(750, me.ping * 2)); + // check stats and update if necessary + let _temp = copyObj(me.data.merc); + let mercInfo = Mercenary.getMercInfo(merc); + if (mercInfo.classid !== me.data.merc.classid) { + me.data.merc.classid = mercInfo.classid; + } + if (mercInfo.act !== me.data.merc.act) { + me.data.merc.act = mercInfo.act; + } + if (mercInfo.difficulty !== me.data.merc.difficulty) { + me.data.merc.difficulty = mercInfo.difficulty; + } + if (merc.charlvl !== me.data.merc.level) { + me.data.merc.level = merc.charlvl; + } + if (merc.rawStrength !== me.data.merc.strength) { + me.data.merc.strength = merc.rawStrength; + } + if (merc.rawDexterity !== me.data.merc.dexterity) { + me.data.merc.dexterity = merc.rawDexterity; + } + + if (merc.classid !== sdk.mercs.Guard) { + try { + if (mercInfo.skillName !== me.data.merc.skillName) { + me.data.merc.skillName = mercInfo.skillName; + me.data.merc.skill = MercData.findByName(me.data.merc.skillName, me.data.merc.act).skill; + } + } catch (e) { + // + } + } + let changed = Misc.recursiveSearch(me.data.merc, _temp); + + if (Object.keys(changed).length > 0) { + CharData.updateData("merc", me.data.merc); + } + me.cancel(); break MainLoop; } diff --git a/libs/SoloPlay/Tools/CharData.js b/libs/SoloPlay/Tools/CharData.js index 66edaacd..83b5769d 100644 --- a/libs/SoloPlay/Tools/CharData.js +++ b/libs/SoloPlay/Tools/CharData.js @@ -94,6 +94,7 @@ const CharData = (function () { act: 1, classid: sdk.mercs.Rogue, difficulty: sdk.difficulty.Normal, + level: 1, strength: 0, dexterity: 0, skill: 0, @@ -187,13 +188,29 @@ const CharData = (function () { }; /** @type {Map} */ - const _buffPots = new Map(); - _buffPots.set(sdk.items.StaminaPotion, new BuffPot(sdk.states.StaminaPot, - () => Skill.canUse(sdk.skills.Vigor) || Pather.canTeleport()) - ); - _buffPots.set(sdk.items.ThawingPotion, new BuffPot(sdk.states.Thawing, () => me.coldRes < 75)); - _buffPots.set(sdk.items.AntidotePotion, new BuffPot(sdk.states.Antidote, () => me.poisonRes < 75)); - + const _buffPots = new Map([ + [ + sdk.items.StaminaPotion, + new BuffPot(sdk.states.StaminaPot, + function () { + return Skill.canUse(sdk.skills.Vigor) || Pather.canTeleport(); + }) + ], + [ + sdk.items.ThawingPotion, + new BuffPot(sdk.states.Thawing, + function () { + return me.coldRes < 75; + }) + ], + [ + sdk.items.AntidotePotion, + new BuffPot(sdk.states.Antidote, + function () { + return me.poisonRes < 75; + }) + ], + ]); // hacky for now - just to handle the old way of accessing buff pots _buffPots.set("stamina", _buffPots.get(sdk.items.StaminaPotion)); _buffPots.set("thawing", _buffPots.get(sdk.items.ThawingPotion)); From 2e3495cb3e086051344881d250d7fea0f9b79c17 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Wed, 7 Jun 2023 12:39:00 -0400 Subject: [PATCH 142/263] cleanup mostly - still replacing arrow functions when I see them --- libs/SoloPlay/Functions/AttackOverrides.js | 6 +- libs/SoloPlay/Functions/Globals.js | 20 +-- libs/SoloPlay/Functions/ItemOverrides.js | 11 +- libs/SoloPlay/Functions/Me.js | 134 ++++++++++++++++----- libs/SoloPlay/Functions/Mercenary.js | 52 ++++++-- libs/SoloPlay/Functions/MiscOverrides.js | 17 ++- 6 files changed, 182 insertions(+), 58 deletions(-) diff --git a/libs/SoloPlay/Functions/AttackOverrides.js b/libs/SoloPlay/Functions/AttackOverrides.js index 4cdd6ad2..c511b26b 100644 --- a/libs/SoloPlay/Functions/AttackOverrides.js +++ b/libs/SoloPlay/Functions/AttackOverrides.js @@ -251,7 +251,11 @@ Attack.openChests = function (range, x, y) { Attack.killTarget = function (name) { if (!name || Config.AttackSkill[1] < 0) return false; typeof name === "string" && (name = name.toLowerCase()); - let target = (typeof name === "object" ? name : Misc.poll(() => Game.getMonster(name), 2000, 100)); + let target = (typeof name === "object" + ? name + : Misc.poll(function () { + return Game.getMonster(name); + }, 2000, 100)); if (!target) { console.warn("ÿc8KillTargetÿc0 :: " + name + " not found. Performing Attack.Clear(25)"); diff --git a/libs/SoloPlay/Functions/Globals.js b/libs/SoloPlay/Functions/Globals.js index 3862f86e..39f665bd 100644 --- a/libs/SoloPlay/Functions/Globals.js +++ b/libs/SoloPlay/Functions/Globals.js @@ -124,7 +124,7 @@ const SetUp = { let currDiffStr = sdk.difficulty.nameOf(me.diff).toLowerCase(); - if (sdk.difficulty.Difficulties.indexOf(me.data.highestDifficulty) < sdk.difficulty.Difficulties.indexOf(sdk.difficulty.nameOf(me.diff))) { + if (sdk.difficulty.Difficulties.indexOf(me.data.highestDifficulty) < me.diff) { me.data.highestDifficulty = sdk.difficulty.nameOf(me.diff); } @@ -150,13 +150,18 @@ const SetUp = { } // merc check - if (me.getMercEx()) { + /** @type {MercUnit} */ + let merc = me.getMercEx(); + if (merc) { // TODO: figure out how to ensure we are already using the right merc to prevent re-hiring // can't do an aura check as merc auras are bugged, only useful info from getUnit is the classid - let merc = me.getMercEx(); let mercItems = merc.getItemsEx(); let preLength = me.data.merc.gear.length; - let check = me.data.merc.gear.filter(i => mercItems.some(item => item.prefixnum === i)); + let check = me.data.merc.gear.filter(function (i) { + return mercItems.some(function (item) { + return item.prefixnum === i; + }); + }); if (check !== preLength) { mUpdate = true; @@ -200,10 +205,9 @@ const SetUp = { const finalCharmKeys = Object.keys(me.data.charms); // gids change from game to game so reset our list - for (let i = 0; i < finalCharmKeys.length; i++) { - let cKey = finalCharmKeys[i]; - if (me.data.charms[cKey].have.length) { - me.data.charms[cKey].have = []; + for (let key of finalCharmKeys) { + if (me.data.charms[key].have.length) { + me.data.charms[key].have = []; cUpdate = true; } } diff --git a/libs/SoloPlay/Functions/ItemOverrides.js b/libs/SoloPlay/Functions/ItemOverrides.js index 7e09e059..ab91ae72 100644 --- a/libs/SoloPlay/Functions/ItemOverrides.js +++ b/libs/SoloPlay/Functions/ItemOverrides.js @@ -162,8 +162,15 @@ Item.autoEquipCheck = function (item, basicCheck = false) { */ const checkForBetterItem = function (item) { let betterItem = me.getItemsEx() - .filter(el => el.isInStorage && el.gid !== item.gid && el.identified && Item.getBodyLoc(el).includes(loc)) - .sort((a, b) => NTIP.GetTier(b) - NTIP.GetTier(a)) + .filter(function (el) { + return el.isInStorage + && el.gid !== item.gid + && el.identified + && Item.getBodyLoc(el).includes(loc); + }) + .sort(function (a, b) { + return NTIP.GetTier(b) - NTIP.GetTier(a); + }) .find(el => NTIP.GetTier(el) > tier); return !!betterItem; }; diff --git a/libs/SoloPlay/Functions/Me.js b/libs/SoloPlay/Functions/Me.js index 8f156e0e..139b0049 100644 --- a/libs/SoloPlay/Functions/Me.js +++ b/libs/SoloPlay/Functions/Me.js @@ -23,8 +23,14 @@ if (!me.hasOwnProperty("dualWielding")) { get: function () { // only classes that can duel wield if (!me.assassin && !me.barbarian) return false; - let items = me.getItemsEx().filter((item) => item.isEquipped && item.isOnMain); - return !!items.length && items.length >= 2 && items.every((item) => !item.isShield && !getBaseStat("items", item.classid, "block")); + let items = me.getItemsEx() + .filter(function (item) { + return item.isEquipped && item.isOnMain; + }); + return items.length >= 2 + && items.every(function (item) { + return !item.isShield && !getBaseStat("items", item.classid, "block"); + }); } }); } @@ -364,7 +370,9 @@ if (!me.hasOwnProperty("equipped")) { || (item._bodylocation !== item.location && me.weaponswitch !== sdk.player.slot.Secondary))) { // item has changed - find the new item let newItem = me.getItemsEx() - .filter((el) => el.isEquipped && el.bodylocation === bodylocation) + .filter(function (el) { + return el.isEquipped && el.bodylocation === bodylocation; + }) .first(); bodyMap.set(bodylocation, new EquippedItem(newItem)); } @@ -391,8 +399,12 @@ if (!me.hasOwnProperty("equipped")) { */ init: function () { me.getItemsEx() - .filter(item => item.isEquipped) - .forEach(item => bodyMap.set(item.bodylocation, new EquippedItem(item))); + .filter(function (item) { + return item.isEquipped; + }) + .forEach(function (item) { + bodyMap.set(item.bodylocation, new EquippedItem(item)); + }); }, }; })(); @@ -415,14 +427,19 @@ me.canTpToTown = function () { me.getMercEx = function () { if (!Config.UseMerc || me.classic || me.mercrevivecost) return null; - let merc = Misc.poll(() => me.getMerc(), 250, 50); + let merc = Misc.poll(function () { + return me.getMerc(); + }, 250, 50); return !!merc && !merc.dead ? merc : null; }; me.getEquippedItem = function (bodyLoc) { if (!bodyLoc) return null; - let equippedItem = me.getItemsEx().filter(i => i.isEquipped && i.bodylocation === bodyLoc); + let equippedItem = me.getItemsEx() + .filter(function (item) { + return item.isEquipped && item.bodylocation === bodyLoc; + }); if (!equippedItem.length) return null; return equippedItem.first(); }; @@ -502,10 +519,21 @@ me.cleanUpInvoPotions = function (beltSize) { if (needCleanup) { const potsInInventory = me.getItemsEx() - .filter((p) => p.isInInventory && [sdk.items.type.HealingPotion, sdk.items.type.ManaPotion, sdk.items.type.RejuvPotion].includes(p.itemType)) - .sort((a, b) => a.itemType - b.itemType); + .filter(function (p) { + return p.isInInventory + && [ + sdk.items.type.HealingPotion, + sdk.items.type.ManaPotion, + sdk.items.type.RejuvPotion + ].includes(p.itemType); + }) + .sort(function (a, b) { + return a.itemType - b.itemType; + }); - potsInInventory.length > 0 && console.debug("We have potions in our invo, put them in belt before we perform townchicken check"); + if (potsInInventory.length > 0) { + console.debug("We have potions in our invo, put them in belt before we perform townchicken check"); + } // Start interating over all the pots we have in our inventory beltSize > 1 && potsInInventory.forEach(function (p) { let moved = false; @@ -517,12 +545,16 @@ me.cleanUpInvoPotions = function (beltSize) { // Pick up the potion and put it in belt if the column is empty, and we don't have any other columns empty // prevents shift-clicking potion into wrong column if (freeSpace[i] === beltSize || freeSpace.some((spot) => spot === beltSize)) { - let x = freeSpace[i] === beltSize ? i : (beltCapRef[i] - (freeSpace[i] * 4)); + let x = freeSpace[i] === beltSize + ? i + : (beltCapRef[i] - (freeSpace[i] * 4)); Packet.placeInBelt(p, x); } else { clickItemAndWait(sdk.clicktypes.click.item.ShiftLeft, p.x, p.y, p.location); } - Misc.poll(() => !me.itemoncursor, 300, 30); + Misc.poll(function () { + return !me.itemoncursor; + }, 300, 30); moved = Storage.Belt.checkColumns(beltSize)[i] === freeSpace[i] - 1; } Cubing.cursorCheck(); @@ -538,7 +570,9 @@ me.cleanUpScrolls = function (tome, scrollId) { let cleanedUp = 0; let myScrolls = me.getItemsEx() - .filter(el => el.isInInventory && el.classid === scrollId); + .filter(function (el) { + return el.isInInventory && el.classid === scrollId; + }); if (myScrolls.length) { try { @@ -548,12 +582,18 @@ me.cleanUpScrolls = function (tome, scrollId) { Misc.useMenu(sdk.menu.Trade) || me.cancelUIFlags(); } - myScrolls.forEach(el => { + myScrolls.forEach(function (el) { if (tome && tome.getStat(sdk.stats.Quantity) < 20) { let currQuantity = tome.getStat(sdk.stats.Quantity); if (el.toCursor()) { - new PacketBuilder().byte(sdk.packets.send.ScrollToMe).dword(el.gid).dword(tome.gid).send(); - Misc.poll(() => !me.itemoncursor, 100, 25); + new PacketBuilder() + .byte(sdk.packets.send.ScrollToMe) + .dword(el.gid) + .dword(tome.gid) + .send(); + Misc.poll(function () { + return !me.itemoncursor; + }, 100, 25); if (tome.getStat(sdk.stats.Quantity) > currQuantity) { console.info(null, "Placed scroll in tome"); @@ -588,8 +628,11 @@ me.needPotions = function () { beltSize === 1 && me.cleanUpInvoPotions(beltSize); // now check what's in our belt me.getItemsEx(-1, sdk.items.mode.inBelt) - .filter(p => [sdk.items.type.HealingPotion, sdk.items.type.ManaPotion].includes(p.itemType) && p.x < 4) - .forEach(p => { + .filter(function (p) { + return p.x < 4 + && [sdk.items.type.HealingPotion, sdk.items.type.ManaPotion].includes(p.itemType); + }) + .forEach(function (p) { if (p.itemType === sdk.items.type.HealingPotion) { pots.hp.push(copyUnit(p)); } else if (p.itemType === sdk.items.type.ManaPotion) { @@ -674,10 +717,18 @@ me.clearBelt = function () { me.getIdTool = function () { let items = me.getItemsEx() - .filter((i) => i.isInInventory && [sdk.items.ScrollofIdentify, sdk.items.TomeofIdentify].includes(i.classid)); - let scroll = items.find((i) => i.isInInventory && i.classid === sdk.items.ScrollofIdentify); + .filter(function (i) { + return i.isInInventory + && [sdk.items.ScrollofIdentify, sdk.items.TomeofIdentify].includes(i.classid); + }); + if (!items.length) return null; + let scroll = items.find(function (i) { + return i.isInInventory && i.classid === sdk.items.ScrollofIdentify; + }); if (scroll) return scroll; - let tome = items.find((i) => i.isInInventory && i.classid === sdk.items.TomeofIdentify); + let tome = items.find(function (i) { + return i.isInInventory && i.classid === sdk.items.TomeofIdentify; + }); if (tome && tome.getStat(sdk.stats.Quantity) > 0) return tome; return null; @@ -685,11 +736,17 @@ me.getIdTool = function () { me.getTpTool = function () { let items = me.getItemsEx(-1, sdk.items.mode.inStorage) - .filter((i) => i.isInInventory && [sdk.items.ScrollofTownPortal, sdk.items.TomeofTownPortal].includes(i.classid)); + .filter(function (i) { + return i.isInInventory && [sdk.items.ScrollofTownPortal, sdk.items.TomeofTownPortal].includes(i.classid); + }); if (!items.length) return null; - let tome = items.find((i) => i.classid === sdk.items.TomeofTownPortal && i.getStat(sdk.stats.Quantity) > 0); + let tome = items.find(function (i) { + return i.classid === sdk.items.TomeofTownPortal && i.getStat(sdk.stats.Quantity) > 0; + }); if (tome) return tome; - let scroll = items.find((i) => i.classid === sdk.items.ScrollofTownPortal); + let scroll = items.find(function (i) { + return i.classid === sdk.items.ScrollofTownPortal; + }); if (scroll) return scroll; return null; }; @@ -738,7 +795,11 @@ me.fieldID = function () { }; me.getWeaponQuantity = function (weaponLoc = sdk.body.RightArm) { - let currItem = me.getItemsEx(-1, sdk.items.mode.Equipped).filter(i => i.bodylocation === weaponLoc).first(); + let currItem = me.getItemsEx(-1, sdk.items.mode.Equipped) + .filter(function (i) { + return i.bodylocation === weaponLoc; + }) + .first(); return !!currItem ? currItem.getStat(sdk.stats.Quantity) : 0; }; @@ -808,7 +869,7 @@ me.needRepair = function () { let bowCheck = Attack.usingBow(); let switchBowCheck = CharData.skillData.bow.onSwitch; let canAfford = me.gold >= me.getRepairCost(); - !bowCheck && switchBowCheck && (bowCheck = (() => { + !bowCheck && switchBowCheck && (bowCheck = (function () { switch (CharData.skillData.bow.bowType) { case sdk.items.type.Bow: case sdk.items.type.AmazonBow: @@ -821,7 +882,7 @@ me.needRepair = function () { })()); if (bowCheck) { - let [quiver, inventoryQuiver] = (() => { + let [quiver, inventoryQuiver] = (function () { switch (bowCheck) { case "crossbow": return [me.getItem("cqv", sdk.items.mode.Equipped), me.getItem("cqv", sdk.items.mode.inStorage)]; @@ -833,12 +894,21 @@ me.needRepair = function () { // Out of arrows/bolts if (!quiver) { - inventoryQuiver ? switchBowCheck ? Item.secondaryEquip(inventoryQuiver, sdk.body.LeftArmSecondary) : Item.equip(inventoryQuiver, 5) : repairAction.push("buyQuiver") && repairAction.push("buyQuiver"); + inventoryQuiver + ? switchBowCheck + ? Item.secondaryEquip(inventoryQuiver, sdk.body.LeftArmSecondary) + : Item.equip(inventoryQuiver, 5) + : repairAction.push("buyQuiver") && repairAction.push("buyQuiver"); } else { let quantity = quiver.getStat(sdk.stats.Quantity); - if (typeof quantity === "number" && quantity * 100 / getBaseStat("items", quiver.classid, "maxstack") <= Config.RepairPercent) { - inventoryQuiver ? switchBowCheck ? Item.secondaryEquip(inventoryQuiver, sdk.body.LeftArmSecondary) : Item.equip(inventoryQuiver, 5) : repairAction.push("buyQuiver") && repairAction.push("buyQuiver"); + if (typeof quantity === "number" + && quantity * 100 / getBaseStat("items", quiver.classid, "maxstack") <= Config.RepairPercent) { + inventoryQuiver + ? switchBowCheck + ? Item.secondaryEquip(inventoryQuiver, sdk.body.LeftArmSecondary) + : Item.equip(inventoryQuiver, 5) + : repairAction.push("buyQuiver") && repairAction.push("buyQuiver"); } } } @@ -854,7 +924,9 @@ me.needRepair = function () { me.needMerc = function () { if (me.classic || !Config.UseMerc || me.gold < me.mercrevivecost || me.mercrevivecost === 0) return false; - Misc.poll(() => me.gameReady, 1000, 100); + Misc.poll(function () { + return me.gameReady; + }, 1000, 100); // me.getMerc() might return null if called right after taking a portal, that's why there's retry attempts for (let i = 0; i < 3; i += 1) { let merc = me.getMercEx(); diff --git a/libs/SoloPlay/Functions/Mercenary.js b/libs/SoloPlay/Functions/Mercenary.js index 00c369d0..4623f48c 100644 --- a/libs/SoloPlay/Functions/Mercenary.js +++ b/libs/SoloPlay/Functions/Mercenary.js @@ -78,20 +78,29 @@ const Mercenary = { * @returns {string} */ getMercSkill: function (merc) { - !merc && (merc = Misc.poll(() => me.getMerc(), 1000, 50)); + !merc && (merc = Misc.poll(function () { + return me.getMerc(); + }, 1000, 50)); if (!merc) return false; - let mercSkill = (() => { + let mercSkill = (function () { switch (merc.classid) { case sdk.mercs.Rogue: - return [sdk.skills.FireArrow, sdk.skills.ColdArrow].find(s => merc.getSkill(s, sdk.skills.subindex.HardPoints)); + return [ + sdk.skills.FireArrow, + sdk.skills.ColdArrow + ].find(s => merc.getSkill(s, sdk.skills.subindex.HardPoints)); case sdk.mercs.Guard: let checkStat = merc.getStat(sdk.stats.ModifierListSkill); // if ([sdk.skills.Meditation, sdk.skills.Conviction, sdk.skills.Concentration, sdk.skills.HolyFire].includes(checkStat)) { // return [sdk.skills.Prayer, sdk.skills.BlessedAim, sdk.skills.Defiance].find(s => merc.getSkill(s, sdk.skills.subindex.HardPoints)); // } - if (![sdk.skills.Prayer, sdk.skills.BlessedAim, sdk.skills.Defiance, sdk.skills.HolyFreeze, sdk.skills.Might, sdk.skills.Thorns].includes(checkStat)) { + if (![ + sdk.skills.Prayer, sdk.skills.BlessedAim, + sdk.skills.Defiance, sdk.skills.HolyFreeze, + sdk.skills.Might, sdk.skills.Thorns + ].includes(checkStat)) { // check items for aura granting one then subtract it's skillId - merc.getItemsEx().forEach(item => { + merc.getItemsEx().forEach(function (item) { if (!item.unique && !item.runeword) return false; switch (true) { case (item.getStat(sdk.stats.SkillOnAura, sdk.skills.Meditation)): @@ -112,7 +121,11 @@ const Mercenary = { } return checkStat >= sdk.skills.Might ? checkStat : 0; case sdk.mercs.IronWolf: - return [sdk.skills.IceBlast, sdk.skills.FireBall, sdk.skills.Lightning].find(s => merc.getSkill(s, sdk.skills.subindex.HardPoints)); + return [ + sdk.skills.IceBlast, + sdk.skills.FireBall, + sdk.skills.Lightning + ].find(s => merc.getSkill(s, sdk.skills.subindex.HardPoints)); case sdk.mercs.A5Barb: return sdk.skills.Bash; default: @@ -128,7 +141,9 @@ const Mercenary = { * @returns {number} */ getMercDifficulty: function (merc) { - !merc && (merc = Misc.poll(() => me.getMerc(), 1000, 50)); + !merc && (merc = Misc.poll(function () { + return me.getMerc(); + }, 1000, 50)); if (!merc) return false; if (merc.classid !== sdk.mercs.Guard) return sdk.difficulty.Normal; @@ -149,7 +164,9 @@ const Mercenary = { * @returns {number} */ getMercAct: function (merc) { - !merc && (merc = Misc.poll(() => me.getMerc(), 1000, 50)); + !merc && (merc = Misc.poll(function () { + return me.getMerc(); + }, 1000, 50)); if (!merc) return 0; return MercData.actMap.get(merc.classid) || 0; }, @@ -158,7 +175,9 @@ const Mercenary = { * @param {MercUnit} merc */ getMercInfo: function (merc) { - !merc && (merc = Misc.poll(() => me.getMerc(), 1000, 50)); + !merc && (merc = Misc.poll(function () { + return me.getMerc(); + }, 1000, 50)); if (!merc) return { classid: 0, act: 0, difficulty: 0, type: "" }; return { classid: merc.classid, @@ -219,7 +238,9 @@ const Mercenary = { // lets check what our current actually merc is /** @type {MercUnit} */ - let checkMyMerc = Misc.poll(() => me.getMerc(), 50, 500); + let checkMyMerc = Misc.poll(function () { + return me.getMerc(); + }, 50, 500); const wantedSkill = (mercAct === 1 ? "Fire Arrow" === wantedMerc.skillName @@ -266,8 +287,15 @@ const Mercenary = { if (!MercLib_1.default.length) throw new Error("No mercs found"); let wantedMerc = MercLib_1.default - .filter((merc) => merc.skills.some((skill) => (skill === null || skill === void 0 ? void 0 : skill.name) === wantedSkill)) - .sort((a, b) => b.level - a.level) + .filter(function (merc) { + return merc.skills + .some(function (skill) { + return (skill === null || skill === void 0 ? void 0 : skill.name) === wantedSkill; + }); + }) + .sort(function (a, b) { + return b.level - a.level; + }) .first(); if (!wantedMerc) throw new Error("No merc found with skill " + wantedSkill); if (wantedMerc.cost > me.gold) { diff --git a/libs/SoloPlay/Functions/MiscOverrides.js b/libs/SoloPlay/Functions/MiscOverrides.js index 2ff15fb6..262fce4f 100644 --- a/libs/SoloPlay/Functions/MiscOverrides.js +++ b/libs/SoloPlay/Functions/MiscOverrides.js @@ -317,7 +317,8 @@ Misc.scanShrines = function (range, ignore = []) { // todo - check to make sure we can actually get the shrine for ones without states // can't grab a health shrine if we are in perfect health, can't grab mana shrine if our mana is maxed if (index === -1 || i <= index || this.shrineStates[i] === 0) { - if (shrine.objtype === Config.ScanShrines[i] && (Pather.useTeleport() || !checkCollision(me, shrine, sdk.collision.WallOrRanged))) { + if (shrine.objtype === Config.ScanShrines[i] + && (Pather.useTeleport() || !checkCollision(me, shrine, sdk.collision.WallOrRanged))) { this.getShrine(shrine); // Gem shrine - pick gem @@ -447,7 +448,11 @@ Misc.unsocketItem = function (item) { try { // failed to move any of the items to the cube - if (!Storage.Cube.MoveTo(item) || !Storage.Cube.MoveTo(hel) || !Storage.Cube.MoveTo(scroll)) throw "Failed to move items to cube"; + if (!Storage.Cube.MoveTo(item) + || !Storage.Cube.MoveTo(hel) + || !Storage.Cube.MoveTo(scroll)) { + throw new Error("Failed to move items to cube"); + } // probably only happens on server crash if (!Cubing.openCube()) throw "Failed to open cube"; @@ -480,8 +485,12 @@ Misc.checkItemsForSocketing = function () { if (me.classic || !me.getQuest(sdk.quest.id.SiegeOnHarrogath, sdk.quest.states.ReqComplete)) return false; let items = me.getItemsEx() - .filter(item => item.sockets === 0 && getBaseStat("items", item.classid, "gemsockets") > 0) - .sort((a, b) => NTIP.GetTier(b) - NTIP.GetTier(a)); + .filter(function (item) { + return item.sockets === 0 && getBaseStat("items", item.classid, "gemsockets") > 0; + }) + .sort(function (a, b) { + return NTIP.GetTier(b) - NTIP.GetTier(a); + }); for (let i = 0; i < items.length; i++) { let curr = Config.socketables.find(({ classid }) => items[i].classid === classid); From 64c60cc321df2edf7a1d7ebfa82e8fcd6e93e46e Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Wed, 7 Jun 2023 12:40:10 -0400 Subject: [PATCH 143/263] Update PatherOverrides.js - add teleported pathaction, mostly this is for using charged tele but handle both --- libs/SoloPlay/Functions/PatherOverrides.js | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/libs/SoloPlay/Functions/PatherOverrides.js b/libs/SoloPlay/Functions/PatherOverrides.js index f5b750bb..ecb2cc81 100644 --- a/libs/SoloPlay/Functions/PatherOverrides.js +++ b/libs/SoloPlay/Functions/PatherOverrides.js @@ -452,6 +452,7 @@ Pather.move = function (target, givenSettings = {}) { const leaped = new PathAction(); const whirled = new PathAction(); const cleared = new PathAction(); + const teleported = new PathAction(); Pather.clearUIFlags(); @@ -674,6 +675,17 @@ Pather.move = function (target, givenSettings = {}) { } } } + + if (usingTele) { + if (teleported.at === 0 || getTickCount() - teleported.at > Time.seconds(3) + || teleported.node.distance > 5 || me.checkForMobs({ range: 6 })) { + // alright now if we have actually casted it set the values so we know + if (useTeleport ? Pather.teleportTo(node.x, node.y) : Pather.teleUsingCharges(node.x, node.y)) { + teleported.at = getTickCount(); + [teleported.node.x, teleported.node.y] = [node.x, node.y]; + } + } + } } } From 0dea4c250fa60483458cc2077317cff8dfe4c70a Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Wed, 7 Jun 2023 12:56:22 -0400 Subject: [PATCH 144/263] Update ItemOverrides.js - fix logging to item viewer --- libs/SoloPlay/Functions/ItemOverrides.js | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/libs/SoloPlay/Functions/ItemOverrides.js b/libs/SoloPlay/Functions/ItemOverrides.js index ab91ae72..982a49d8 100644 --- a/libs/SoloPlay/Functions/ItemOverrides.js +++ b/libs/SoloPlay/Functions/ItemOverrides.js @@ -874,7 +874,13 @@ Item.removeItemsMerc = function () { return !!mercenary.getItem(); }; -// Log kept item stats in the manager. +/** + * Log kept item stats in the manager. + * @param {string} action + * @param {ItemUnit} unit + * @param {string} keptLine + * @param {boolean} force + */ Item.logItem = function (action, unit, keptLine, force) { if (!this.useItemLog || unit === undefined || !unit || !unit.fname) return false; if (!Config.LogKeys && ["pk1", "pk2", "pk3"].includes(unit.code)) return false; @@ -939,7 +945,10 @@ Item.logItem = function (action, unit, keptLine, force) { } while (sock.getNext()); } - keptLine && (desc += ("\n\\xffc0Line: " + keptLine)); + if (keptLine) { + keptLine.includes("[") && (keptLine = keptLine.split("[")[0].trim()); + desc += ("\n\\xffc0Line: " + keptLine); + } desc += "$" + (unit.getFlag(sdk.items.flags.Ethereal) ? ":eth" : ""); let itemObj = { From 90e1a35639a79450ea4419614d6f6ca9e5da3dc2 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Wed, 7 Jun 2023 18:56:42 -0400 Subject: [PATCH 145/263] Update Globals.js - better checking for merc during init --- libs/SoloPlay/Functions/Globals.js | 34 +++++++++++++++++++++++------- 1 file changed, 26 insertions(+), 8 deletions(-) diff --git a/libs/SoloPlay/Functions/Globals.js b/libs/SoloPlay/Functions/Globals.js index 39f665bd..c84060dc 100644 --- a/libs/SoloPlay/Functions/Globals.js +++ b/libs/SoloPlay/Functions/Globals.js @@ -155,6 +155,7 @@ const SetUp = { if (merc) { // TODO: figure out how to ensure we are already using the right merc to prevent re-hiring // can't do an aura check as merc auras are bugged, only useful info from getUnit is the classid + let _tempMerc = copyObj(me.data.merc); let mercItems = merc.getItemsEx(); let preLength = me.data.merc.gear.length; let check = me.data.merc.gear.filter(function (i) { @@ -169,9 +170,24 @@ const SetUp = { } let mercInfo = Mercenary.getMercInfo(merc); - mercInfo.classid !== me.data.merc.classid && (me.data.merc.classid = mercInfo.classid); - mercInfo.act !== me.data.merc.act && (me.data.merc.act = mercInfo.act); - mercInfo.difficulty !== me.data.merc.difficulty && (me.data.merc.difficulty = mercInfo.difficulty); + if (mercInfo.classid !== me.data.merc.classid) { + me.data.merc.classid = mercInfo.classid; + } + if (mercInfo.act !== me.data.merc.act) { + me.data.merc.act = mercInfo.act; + } + if (mercInfo.difficulty !== me.data.merc.difficulty) { + me.data.merc.difficulty = mercInfo.difficulty; + } + if (merc.charlvl !== me.data.merc.level) { + me.data.merc.level = merc.charlvl; + } + if (merc.rawStrength !== me.data.merc.strength) { + me.data.merc.strength = merc.rawStrength; + } + if (merc.rawDexterity !== me.data.merc.dexterity) { + me.data.merc.dexterity = merc.rawDexterity; + } if (merc.classid !== sdk.mercs.Guard) { try { @@ -190,6 +206,12 @@ const SetUp = { // // only if we have enough gold on hand to hire said merc // // return to our orignal difficulty afterwards // } + let changed = Misc.recursiveSearch(me.data.merc, _tempMerc); + + if (Object.keys(changed).length > 0) { + // CharData.updateData("merc", me.data.merc); + mUpdate = true; + } } // charm check @@ -215,15 +237,11 @@ const SetUp = { if (!!me.shenk && me.data[currDiffStr].socketUsed === false) { me.data[currDiffStr].socketUsed = true; } - - if (mUpdate) { - CharData.updateData("merc", me.data); - } } let changed = Misc.recursiveSearch(me.data, temp); - if (Object.keys(changed).length > 0 || cUpdate) { + if (cUpdate || mUpdate || Object.keys(changed).length > 0) { CharData.updateData("me", me.data); } }, From 6b8a04fe5233b601ed455927f942803e4bd77151 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Thu, 8 Jun 2023 18:40:54 -0400 Subject: [PATCH 146/263] Rebuild areadata module - general updates with the change to areadata's structure - add `me.haveWaypoint` --- libs/SoloPlay/Functions/Globals.js | 2 - libs/SoloPlay/Functions/PatherOverrides.js | 58 +- libs/SoloPlay/Modules/GameData/AreaData.js | 1457 +++++++++++++++++--- libs/SoloPlay/Modules/GameData/GameData.js | 236 +++- libs/SoloPlay/index.d.ts | 74 + 5 files changed, 1602 insertions(+), 225 deletions(-) diff --git a/libs/SoloPlay/Functions/Globals.js b/libs/SoloPlay/Functions/Globals.js index c84060dc..ab23cae0 100644 --- a/libs/SoloPlay/Functions/Globals.js +++ b/libs/SoloPlay/Functions/Globals.js @@ -26,8 +26,6 @@ const Coords_1 = require("../Modules/Coords"); const PotData = require("../Modules/GameData/PotData"); /** @global */ const GameData = require("../Modules/GameData/GameData"); -/** @global */ -const AreaData = require("../Modules/GameData/AreaData"); const MYCLASSNAME = sdk.player.class.nameOf(me.classid).toLowerCase(); includeIfNotIncluded("SoloPlay/BuildFiles/" + MYCLASSNAME + "/" + MYCLASSNAME + ".js"); diff --git a/libs/SoloPlay/Functions/PatherOverrides.js b/libs/SoloPlay/Functions/PatherOverrides.js index ecb2cc81..8eea141a 100644 --- a/libs/SoloPlay/Functions/PatherOverrides.js +++ b/libs/SoloPlay/Functions/PatherOverrides.js @@ -10,6 +10,20 @@ includeIfNotIncluded("core/Pather.js"); Developer.debugging.pathing && (PathDebug.enableHooks = true); +/** @global */ +const AreaData = require("../Modules/GameData/AreaData"); + +/** + * Easier way to check if you have a waypoint + * @param {number} area + * @returns {boolean} + */ +me.haveWaypoint = function (area) { + let checkArea = AreaData.get(area); + if (!checkArea || !checkArea.hasWaypoint()) return false; + return getWaypoint(AreaData.wps.get(area)); +}; + Pather.inAnnoyingArea = function (currArea, includeArcane = false) { const areas = [sdk.areas.MaggotLairLvl1, sdk.areas.MaggotLairLvl2, sdk.areas.MaggotLairLvl3]; includeArcane && areas.push(sdk.areas.ArcaneSanctuary); @@ -279,7 +293,7 @@ Pather.checkWP = function (area = 0, keepMenuOpen = false) { } // only do this if we haven't initialzed our wp data - if (!getWaypoint(Pather.wpAreas.indexOf(area)) && !Pather.initialized) { + if (!me.haveWaypoint(area) && !Pather.initialized) { me.inTown && !getUIFlag(sdk.uiflags.Waypoint) && Town.move("waypoint"); for (let i = 0; i < 15; i++) { @@ -317,10 +331,10 @@ Pather.checkWP = function (area = 0, keepMenuOpen = false) { } } // go ahead and close out of wp menu if we don't have the wp - !getWaypoint(Pather.wpAreas.indexOf(area)) && getUIFlag(sdk.uiflags.Waypoint) && me.cancel(); + !me.haveWaypoint(area) && getUIFlag(sdk.uiflags.Waypoint) && me.cancel(); } - return getWaypoint(Pather.wpAreas.indexOf(area)); + return me.haveWaypoint(area); }; Pather.changeAct = function (act = me.act + 1) { @@ -503,7 +517,7 @@ Pather.move = function (target, givenSettings = {}) { if (mySkElems.length && mySkElems.every(elem => areaImmunities.includes(elem))) { settings.clearSettings.clearPath = false; } - } else if (AreaData[me.area].hasMonsterType(sdk.monsters.type.UndeadFetish)) { + } else if (AreaData.get(me.area).hasMonsterType(sdk.monsters.type.UndeadFetish)) { settings.clearSettings.clearPath = false; } } @@ -635,7 +649,7 @@ Pather.move = function (target, givenSettings = {}) { if (cleared.at === 0 || getTickCount() - cleared.at > Time.seconds(3) && cleared.node.distance > 5 && me.checkForMobs({ range: 10 })) { // only set that we cleared if we actually killed at least 1 mob - if (Attack.clear(10, null, null, null, settings.allowPicking)) { + if (Attack.clear(10, null, null, null, settings.allowPicking) === Attack.Result.SUCCESS) { // console.debug("Cleared Node"); cleared.at = getTickCount(); [cleared.node.x, cleared.node.y] = [node.x, node.y]; @@ -772,11 +786,12 @@ Pather.moveToExit = function (targetArea, use, givenSettings = {}) { for (let currTarget of areas) { console.info(null, getAreaName(me.area) + "ÿc8 --> ÿc0" + getAreaName(currTarget)); - const area = Misc.poll(() => getArea(me.area)); - if (!area) throw new Error("moveToExit: error in getArea()"); + // const area = Misc.poll(() => getArea(me.area)); + // if (!area) throw new Error("moveToExit: error in getArea()"); /** @type {Array} */ - const exits = (area.exits || []); + const exits = AreaData.get(me.area).getExits(); + // const exits = (area.exits || []); if (!exits.length) return false; let checkExits = []; @@ -788,7 +803,7 @@ Pather.moveToExit = function (targetArea, use, givenSettings = {}) { if (checkExits.length > 0) { // if there are multiple exits to the same location find the closest one let currExit = checkExits.length > 1 - ? (() => { + ? (function () { let useExit = checkExits.shift(); // assign the first exit as a possible result let dist = getDistance(me.x, me.y, useExit.x, useExit.y); while (checkExits.length > 0) { @@ -804,15 +819,18 @@ Pather.moveToExit = function (targetArea, use, givenSettings = {}) { : checkExits[0]; let dest = this.getNearestWalkable(currExit.x, currExit.y, 5, 1); if (!dest) return false; + const node = { x: dest[0], y: dest[1] }; for (let retry = 0; retry < 3; retry++) { - if (this.moveToEx(dest[0], dest[1], givenSettings)) { + if (this.move(node, givenSettings)) { break; } delay(200); console.log("ÿc7(moveToExit) :: ÿc0Retry: " + (retry + 1)); - Misc.poll(() => me.gameReady, 1000, 200); + Misc.poll(function () { + return me.gameReady; + }, 1000, 200); } if (use || currTarget !== finalDest) { @@ -821,7 +839,7 @@ Pather.moveToExit = function (targetArea, use, givenSettings = {}) { let targetRoom = this.getNearestRoom(currTarget); // might need adjustments if (!targetRoom) return false; - this.moveToEx(targetRoom[0], targetRoom[1], givenSettings); + this.move({ x: targetRoom[0], y: targetRoom[1] }, givenSettings); break; case 2: // stairs @@ -853,7 +871,7 @@ Pather.useWaypoint = function useWaypoint (targetArea, check = false) { break; default: if (typeof targetArea !== "number") throw new Error("useWaypoint: Invalid targetArea parameter"); - if (!this.wpAreas.includes(targetArea)) throw new Error("useWaypoint: Invalid area"); + if (!AreaData.wps.has(targetArea)) throw new Error("useWaypoint: Invalid area"); break; } @@ -902,7 +920,9 @@ Pather.useWaypoint = function useWaypoint (targetArea, check = false) { if (useTK && !getUIFlag(sdk.uiflags.Waypoint)) { wp.distance > 21 && Pather.moveNearUnit(wp, 20); - i > 1 && checkCollision(me, wp, sdk.collision.Ranged) && Attack.getIntoPosition(wp, 20, sdk.collision.Ranged); + if (i > 1 && checkCollision(me, wp, sdk.collision.Ranged)) { + Attack.getIntoPosition(wp, 20, sdk.collision.Ranged); + } Packet.telekinesis(wp); } else if (!me.inTown && wp.distance > 7) { this.moveToUnit(wp); @@ -944,7 +964,7 @@ Pather.useWaypoint = function useWaypoint (targetArea, check = false) { return true; } - if (!getWaypoint(this.wpAreas.indexOf(targetArea)) && me.cancel()) { + if (!me.haveWaypoint(targetArea) && me.cancel()) { me.overhead("Trying to get the waypoint"); if (this.getWP(targetArea)) return true; @@ -1061,6 +1081,10 @@ Pather.clearToExit = function (currentarea, targetarea, givenSettings = {}) { Pather.getWalkDistance = function (x, y, area = me.area, xx = me.x, yy = me.y, reductionType = 2, radius = 5) { // distance between node x and x-1 return (getPath(area, x, y, xx, yy, reductionType, radius) || []) - .map((e, i, s) => i && getDistance(s[i - 1], e) || 0) - .reduce((acc, cur) => acc + cur, 0) || Infinity; + .map(function (e, i, s) { + return i && getDistance(s[i - 1], e) || 0; + }) + .reduce(function (acc, cur) { + return acc + cur; + }, 0) || Infinity; }; diff --git a/libs/SoloPlay/Modules/GameData/AreaData.js b/libs/SoloPlay/Modules/GameData/AreaData.js index e0ef7053..07e7364d 100644 --- a/libs/SoloPlay/Modules/GameData/AreaData.js +++ b/libs/SoloPlay/Modules/GameData/AreaData.js @@ -1,205 +1,1346 @@ /** - * @module - * @param {Object} module - * @param {function} require - */ +* @filename AreaData.js +* @author theBGuy +* @credits Nishimura-Katsuo (orignal module), kolton (data from pather.js) +* @desc area data library +* +*/ + (function (module, require) { - const MonsterData = require("./MonsterData"); - const SUPER = [0, 0, 0, 1, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 1, 0, 0, 1, 0, 0, 0, 1, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 1, 1, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 0, 0, 0, 3, 0, 1, 0, 0, 0, 0, 0, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 1, 0, 1, 4, 0, 2, 3, 1, 0, 1, 1, 0, 0, 0, 1, 3, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 5, 1, 1, 1, 1, 3]; - const AREA_LOCALE_STRING = [5389, 5055, 5054, 5053, 5052, 5051, 5050, 5049, 5048, 5047, 5046, 5045, 5044, 5043, 5042, 5041, 5040, 5039, 5038, 5037, 5036, 5035, 5034, 5033, 5032, 5031, 5030, 5029, 5028, 5027, 5026, 5025, 5024, 5023, 5022, 5021, 5020, 5019, 5018, 788, 852, 851, 850, 849, 848, 847, 846, 845, 844, 843, 842, 841, 840, 839, 838, 837, 836, 835, 834, 833, 832, 831, 830, 829, 828, 827, 826, 826, 826, 826, 826, 826, 826, 825, 824, 820, 819, 818, 817, 816, 815, 814, 813, 812, 810, 811, 809, 808, 806, 805, 807, 804, 845, 844, 803, 802, 801, 800, 799, 798, 797, 796, 795, 790, 792, 793, 794, 791, 789, 22646, 22647, 22648, 22649, 22650, 22651, 22652, 22653, 22654, 22655, 22656, 22657, 22658, 22659, 22660, 22662, 21865, 21866, 21867, 22663, 22664, 22665, 22667, 22666, 5389, 5389, 5389, 5018]; + const MonsterData = require("../../../core/GameData/MonsterData"); + const ShrineData = require("../../../core/GameData/ShrineData"); + const QuestData = require("../../../core/GameData/QuestData"); const MONSTER_KEYS = [ ["mon1", "mon2", "mon3", "mon4", "mon5", "mon6", "mon7", "mon8", "mon9", "mon10"], ["nmon1", "nmon2", "nmon3", "nmon4", "nmon5", "nmon6", "nmon7", "nmon8", "nmon9", "nmon10"], ][me.diff && 1]; // mon is for normal, nmon is for nm/hell, umon is specific to picking champion/uniques in normal - const LocaleStringName = require("./LocaleStringID").LocaleStringName; const AREA_INDEX_COUNT = 137; - + /** - * @typedef AreaDataObj - * @type {object} - * @property {number} Super = number of super uniques present in this area - * @property {number} Index = areaID - * @property {number} Act = act this area is in [0-4] - * @property {number} MonsterDensity = value used to determine monster population density - * @property {number} ChampionPacks.Min = minimum number of champion or unique packs that spawn here - * @property {number} ChampionPacks.Max = maximum number of champion or unique packs that spawn here - * @property {number} Waypoint = number in waypoint menu that leads to this area - * @property {number} Level = level of area (use GameData.areaLevel) - * @property {number} Size.x = width of area - * @property {number} Size.y = depth of area - * @property {number} Monsters = array of monsters that can spawn in this area - * @property {number} LocaleString = locale string index for getLocaleString + * @todo Still need to handle exits */ + const AreaData = (function () { + /** @type {Map} */ + const _map = new Map(); + /** @type {Map} */ + const wps = new Map(); + + /** + * @typedef {Object} AreaInterface + * @property {number[]} [previousArea] + * @property {number[]} [nextArea] + * @property {number[]} [presetMonsters] + * @property {number[]} [presetChests] + * @property {number[]} [poi] + * @property {function(): boolean} [preReq] + */ + + /** @type {Map} */ + const _areaData = new Map([ + // Act 1 + [sdk.areas.RogueEncampment, { + nextArea: [sdk.areas.BloodMoor], + preReq: function () { + return true; + }, // always able to access + }], + [sdk.areas.BloodMoor, { + previousArea: [sdk.areas.RogueEncampment], + nextArea: [sdk.areas.ColdPlains, sdk.areas.DenofEvil], + presetChests: [sdk.objects.SuperChest], + }], + [sdk.areas.ColdPlains, { + previousArea: [sdk.areas.BloodMoor], + nextArea: [sdk.areas.StonyField, sdk.areas.BurialGrounds, sdk.areas.CaveLvl1], + }], + [sdk.areas.StonyField, { + previousArea: [sdk.areas.ColdPlains], + nextArea: [sdk.areas.UndergroundPassageLvl1, sdk.areas.Tristram], + presetMonsters: [sdk.monsters.preset.Rakanishu], + poi: [ + sdk.objects.StoneAlpha, sdk.objects.StoneBeta, + sdk.objects.StoneGamma, sdk.objects.StoneDelta, + sdk.objects.StoneLambda, sdk.objects.StoneTheta, + sdk.objects.MoldyTome, + ], + }], + [sdk.areas.DarkWood, { + previousArea: [sdk.areas.UndergroundPassageLvl1], + nextArea: [sdk.areas.BlackMarsh], + presetMonsters: [sdk.monsters.preset.TreeheadWoodFist], + poi: [sdk.objects.InifussTree], + }], + [sdk.areas.BlackMarsh, { + previousArea: [sdk.areas.DarkWood], + nextArea: [sdk.areas.ForgottenTower, sdk.areas.HoleLvl1], + }], + [sdk.areas.TamoeHighland, { + previousArea: [sdk.areas.BlackMarsh], + nextArea: [sdk.areas.MonasteryGate, sdk.areas.PitLvl1], + }], + [sdk.areas.DenofEvil, { + previousArea: [sdk.areas.BloodMoor], + presetMonsters: [sdk.monsters.preset.Corpsefire], + }], + [sdk.areas.CaveLvl1, { + previousArea: [sdk.areas.ColdPlains], + nextArea: [sdk.areas.CaveLvl2], + presetMonsters: [sdk.monsters.preset.Coldcrow], + }], + [sdk.areas.UndergroundPassageLvl1, { + previousArea: [sdk.areas.StonyField], + nextArea: [sdk.areas.UndergroundPassageLvl2, sdk.areas.DarkWood], + }], + [sdk.areas.HoleLvl1, { + previousArea: [sdk.areas.BlackMarsh], + nextArea: [sdk.areas.HoleLvl2], + }], + [sdk.areas.PitLvl1, { + previousArea: [sdk.areas.TamoeHighland], + nextArea: [sdk.areas.PitLvl2], + }], + [sdk.areas.CaveLvl2, { + previousArea: [sdk.areas.CaveLvl1], + presetChests: [sdk.objects.SmallSparklyChest], + }], + [sdk.areas.UndergroundPassageLvl2, { + previousArea: [sdk.areas.UndergroundPassageLvl1], + presetChests: [sdk.objects.SmallSparklyChest], + }], + [sdk.areas.HoleLvl2, { + previousArea: [sdk.areas.HoleLvl1], + presetChests: [sdk.objects.SmallSparklyChest], + }], + [sdk.areas.PitLvl2, { + previousArea: [sdk.areas.PitLvl1], + presetChests: [sdk.objects.SmallSparklyChest], + }], + [sdk.areas.BurialGrounds, { + previousArea: [sdk.areas.ColdPlains], + nextArea: [sdk.areas.Crypt, sdk.areas.Mausoleum], + presetMonsters: [sdk.monsters.preset.BloodRaven], + }], + [sdk.areas.Crypt, { + previousArea: [sdk.areas.BurialGrounds], + presetMonsters: [sdk.monsters.preset.Bonebreak], + presetChests: [sdk.objects.SmallSparklyChest], + }], + [sdk.areas.Mausoleum, { + previousArea: [sdk.areas.BurialGrounds], + presetChests: [sdk.objects.SmallSparklyChest], + }], + [sdk.areas.ForgottenTower, { + previousArea: [sdk.areas.BlackMarsh], + nextArea: [sdk.areas.TowerCellarLvl1], + }], + [sdk.areas.TowerCellarLvl1, { + previousArea: [sdk.areas.ForgottenTower], + nextArea: [sdk.areas.TowerCellarLvl2], + }], + [sdk.areas.TowerCellarLvl2, { + previousArea: [sdk.areas.TowerCellarLvl1], + nextArea: [sdk.areas.TowerCellarLvl3], + }], + [sdk.areas.TowerCellarLvl3, { + previousArea: [sdk.areas.TowerCellarLvl2], + nextArea: [sdk.areas.TowerCellarLvl4], + }], + [sdk.areas.TowerCellarLvl4, { + previousArea: [sdk.areas.TowerCellarLvl3], + nextArea: [sdk.areas.TowerCellarLvl5], + }], + [sdk.areas.TowerCellarLvl5, { + previousArea: [sdk.areas.TowerCellarLvl4], + presetMonsters: [sdk.monsters.preset.TheCountess], + presetChests: [sdk.objects.SuperChest], + }], + [sdk.areas.MonasteryGate, { + previousArea: [sdk.areas.TamoeHighland], + nextArea: [sdk.areas.OuterCloister], + }], + [sdk.areas.OuterCloister, { + previousArea: [sdk.areas.MonasteryGate], + nextArea: [sdk.areas.Barracks], + }], + [sdk.areas.Barracks, { + previousArea: [sdk.areas.OuterCloister], + nextArea: [sdk.areas.JailLvl1], + presetMonsters: [sdk.monsters.preset.TheSmith], + poi: [sdk.quest.chest.MalusHolder], + }], + [sdk.areas.JailLvl1, { + previousArea: [sdk.areas.Barracks], + nextArea: [sdk.areas.JailLvl2], + }], + [sdk.areas.JailLvl2, { + previousArea: [sdk.areas.JailLvl1], + nextArea: [sdk.areas.JailLvl3], + presetMonsters: [sdk.monsters.preset.PitspawnFouldog], + }], + [sdk.areas.JailLvl3, { + previousArea: [sdk.areas.JailLvl2], + nextArea: [sdk.areas.InnerCloister], + }], + [sdk.areas.InnerCloister, { + previousArea: [sdk.areas.JailLvl3], + nextArea: [sdk.areas.Cathedral], + }], + [sdk.areas.Cathedral, { + previousArea: [sdk.areas.InnerCloister], + nextArea: [sdk.areas.CatacombsLvl1], + presetMonsters: [sdk.monsters.preset.BoneAsh], + }], + [sdk.areas.CatacombsLvl1, { + previousArea: [sdk.areas.Cathedral], + nextArea: [sdk.areas.CatacombsLvl2], + }], + [sdk.areas.CatacombsLvl2, { + previousArea: [sdk.areas.CatacombsLvl1], + nextArea: [sdk.areas.CatacombsLvl3], + }], + [sdk.areas.CatacombsLvl3, { + previousArea: [sdk.areas.CatacombsLvl2], + nextArea: [sdk.areas.CatacombsLvl4], + }], + [sdk.areas.CatacombsLvl4, { + previousArea: [sdk.areas.CatacombsLvl3], + presetMonsters: [sdk.monsters.Andariel], + }], + [sdk.areas.Tristram, { + previousArea: [sdk.areas.StonyField], + presetMonsters: [sdk.monsters.preset.Griswold], + presetChests: [sdk.quest.chest.Wirt], + poi: [sdk.quest.chest.CainsJail], + preReq: function () { + let quest = QuestData.get(sdk.quest.id.TheSearchForCain); + // what to do if its in a state of unable to complete but the portal is open because it's someone elses game? + return quest.complete() || quest.checkState(4, true); + }, + }], + [sdk.areas.MooMooFarm, { + previousArea: [sdk.areas.RogueEncampment], + presetMonsters: [sdk.monsters.preset.TheCowKing], + }], + // Act 2 + [sdk.areas.LutGholein, { + nextArea: [sdk.areas.A2SewersLvl1, sdk.areas.RockyWaste, sdk.areas.HaremLvl1], + preReq: function () { + return QuestData.get(sdk.quest.id.AbleToGotoActII).complete(); + }, + }], + [sdk.areas.A2SewersLvl1, { + previousArea: [sdk.areas.LutGholein], + nextArea: [sdk.areas.A2SewersLvl2], + }], + [sdk.areas.A2SewersLvl2, { + previousArea: [sdk.areas.A2SewersLvl1], + nextArea: [sdk.areas.A2SewersLvl3], + }], + [sdk.areas.A2SewersLvl3, { + previousArea: [sdk.areas.A2SewersLvl2], + presetMonsters: [sdk.monsters.preset.Radament], + presetChests: [sdk.quest.chest.HoradricScrollChest], + }], + [sdk.areas.RockyWaste, { + previousArea: [sdk.areas.LutGholein], + nextArea: [sdk.areas.DryHills, sdk.areas.StonyTombLvl1], + }], + [sdk.areas.DryHills, { + previousArea: [sdk.areas.RockyWaste], + nextArea: [sdk.areas.FarOasis, sdk.areas.HallsoftheDeadLvl1], + }], + [sdk.areas.FarOasis, { + previousArea: [sdk.areas.DryHills], + nextArea: [sdk.areas.LostCity, sdk.areas.MaggotLairLvl1], + presetMonsters: [sdk.monsters.preset.Beetleburst], + }], + [sdk.areas.LostCity, { + previousArea: [sdk.areas.FarOasis], + nextArea: [sdk.areas.ValleyofSnakes, sdk.areas.AncientTunnels], + presetMonsters: [sdk.monsters.preset.DarkElder], + presetChests: [sdk.objects.SuperChest], + }], + [sdk.areas.ValleyofSnakes, { + previousArea: [sdk.areas.LostCity], + nextArea: [sdk.areas.ClawViperTempleLvl1], + }], + [sdk.areas.ClawViperTempleLvl1, { + previousArea: [sdk.areas.ValleyofSnakes], + nextArea: [sdk.areas.ClawViperTempleLvl2], + }], + [sdk.areas.ClawViperTempleLvl2, { + previousArea: [sdk.areas.ClawViperTempleLvl1], + presetMonsters: [sdk.monsters.preset.Fangskin], + presetChests: [sdk.quest.chest.ViperAmuletChest], + }], + [sdk.areas.StonyTombLvl1, { + previousArea: [sdk.areas.RockyWaste], + nextArea: [sdk.areas.StonyTombLvl2], + }], + [sdk.areas.StonyTombLvl2, { + previousArea: [sdk.areas.StonyTombLvl1], + presetMonsters: [sdk.monsters.preset.CreepingFeature], + presetChests: [sdk.objects.SmallSparklyChest], + }], + [sdk.areas.HallsoftheDeadLvl1, { + previousArea: [sdk.areas.DryHills], + nextArea: [sdk.areas.HallsoftheDeadLvl2], + }], + [sdk.areas.HallsoftheDeadLvl2, { + previousArea: [sdk.areas.HallsoftheDeadLvl1], + nextArea: [sdk.areas.HallsoftheDeadLvl3], + }], + [sdk.areas.HallsoftheDeadLvl3, { + previousArea: [sdk.areas.HallsoftheDeadLvl2], + presetMonsters: [sdk.monsters.preset.BloodwitchtheWild], + presetChests: [sdk.quest.chest.HoradricCubeChest], + }], + [sdk.areas.MaggotLairLvl1, { + previousArea: [sdk.areas.FarOasis], + nextArea: [sdk.areas.MaggotLairLvl2], + }], + [sdk.areas.MaggotLairLvl2, { + previousArea: [sdk.areas.MaggotLairLvl1], + nextArea: [sdk.areas.MaggotLairLvl3], + }], + [sdk.areas.MaggotLairLvl3, { + previousArea: [sdk.areas.MaggotLairLvl2], + presetMonsters: [sdk.monsters.preset.ColdwormtheBurrower], + presetChests: [sdk.quest.chest.ShaftoftheHoradricStaffChest], + }], + [sdk.areas.AncientTunnels, { + previousArea: [sdk.areas.LostCity], + presetChests: [sdk.objects.SmallSparklyChest], + }], + [sdk.areas.HaremLvl1, { + previousArea: [sdk.areas.LutGholein], + nextArea: [sdk.areas.HaremLvl2], + preReq: function () { + return QuestData.get(sdk.quest.id.TheTaintedSun).complete(); + }, + }], + [sdk.areas.HaremLvl2, { + previousArea: [sdk.areas.HaremLvl1], + nextArea: [sdk.areas.PalaceCellarLvl1], + preReq: function () { + return QuestData.get(sdk.quest.id.TheTaintedSun).complete(); + }, + }], + [sdk.areas.PalaceCellarLvl1, { + previousArea: [sdk.areas.HaremLvl2], + nextArea: [sdk.areas.PalaceCellarLvl2], + preReq: function () { + return QuestData.get(sdk.quest.id.TheTaintedSun).complete(); + }, + }], + [sdk.areas.PalaceCellarLvl2, { + previousArea: [sdk.areas.PalaceCellarLvl1], + nextArea: [sdk.areas.PalaceCellarLvl3], + preReq: function () { + return QuestData.get(sdk.quest.id.TheTaintedSun).complete(); + }, + }], + [sdk.areas.PalaceCellarLvl3, { + previousArea: [sdk.areas.PalaceCellarLvl2], + nextArea: [sdk.areas.ArcaneSanctuary], + presetMonsters: [sdk.monsters.preset.FireEye], + // poi the portal - add this later + preReq: function () { + return QuestData.get(sdk.quest.id.TheTaintedSun).complete(); + }, + }], + [sdk.areas.ArcaneSanctuary, { + previousArea: [sdk.areas.PalaceCellarLvl3], + nextArea: [sdk.areas.CanyonofMagic], + presetMonsters: [sdk.monsters.preset.TheSummoner], + poi: [sdk.quest.chest.Journal], + preReq: function () { + return QuestData.get(sdk.quest.id.TheTaintedSun).complete(); + }, + }], + [sdk.areas.CanyonofMagic, { + nextArea: [ + sdk.areas.TalRashasTomb1, sdk.areas.TalRashasTomb2, sdk.areas.TalRashasTomb3, sdk.areas.TalRashasTomb4, + sdk.areas.TalRashasTomb5, sdk.areas.TalRashasTomb6, sdk.areas.TalRashasTomb7, + ], + preReq: function () { + return QuestData.get(sdk.quest.id.TheSummoner).complete(); + }, + }], + [sdk.areas.TalRashasTomb1, { + previousArea: [sdk.areas.CanyonofMagic], + presetChests: [sdk.objects.SmallSparklyChest], + preReq: function () { + return QuestData.get(sdk.quest.id.TheSummoner).complete(); + }, + }], + [sdk.areas.TalRashasTomb2, { + previousArea: [sdk.areas.CanyonofMagic], + presetChests: [sdk.objects.SmallSparklyChest], + preReq: function () { + return QuestData.get(sdk.quest.id.TheSummoner).complete(); + }, + }], + [sdk.areas.TalRashasTomb3, { + previousArea: [sdk.areas.CanyonofMagic], + presetChests: [sdk.objects.SmallSparklyChest], + preReq: function () { + return QuestData.get(sdk.quest.id.TheSummoner).complete(); + }, + }], + [sdk.areas.TalRashasTomb4, { + previousArea: [sdk.areas.CanyonofMagic], + presetChests: [sdk.objects.SmallSparklyChest], + preReq: function () { + return QuestData.get(sdk.quest.id.TheSummoner).complete(); + }, + }], + [sdk.areas.TalRashasTomb5, { + previousArea: [sdk.areas.CanyonofMagic], + presetChests: [sdk.objects.SmallSparklyChest], + preReq: function () { + return QuestData.get(sdk.quest.id.TheSummoner).complete(); + }, + }], + [sdk.areas.TalRashasTomb6, { + previousArea: [sdk.areas.CanyonofMagic], + presetChests: [sdk.objects.SmallSparklyChest], + preReq: function () { + return QuestData.get(sdk.quest.id.TheSummoner).complete(); + }, + }], + [sdk.areas.TalRashasTomb7, { + previousArea: [sdk.areas.CanyonofMagic], + presetChests: [sdk.objects.SmallSparklyChest], + preReq: function () { + return QuestData.get(sdk.quest.id.TheSummoner).complete(); + }, + }], + [sdk.areas.DurielsLair, { + presetMonsters: [sdk.monsters.Duriel], + preReq: function () { + return QuestData.get(sdk.quest.id.TheSummoner).complete(); + }, + }], + // Act 3 + [sdk.areas.KurastDocktown, { + nextArea: [sdk.areas.SpiderForest], + preReq: function () { + return QuestData.get(sdk.quest.id.AbleToGotoActIII).complete(); + }, + }], + [sdk.areas.SpiderForest, { + previousArea: [sdk.areas.KurastDocktown], + nextArea: [sdk.areas.GreatMarsh], + }], + [sdk.areas.GreatMarsh, { + previousArea: [sdk.areas.SpiderForest], + presetChests: [sdk.objects.SmallSparklyChest], + }], + [sdk.areas.SpiderCave, { + previousArea: [sdk.areas.SpiderForest], + presetChests: [sdk.objects.SmallSparklyChest], + }], + [sdk.areas.SpiderCavern, { + previousArea: [sdk.areas.SpiderForest], + presetMonsters: [sdk.monsters.preset.SszarktheBurning], + presetChests: [sdk.quest.chest.KhalimsEyeChest], + }], + [sdk.areas.FlayerJungle, { + previousArea: [sdk.areas.SpiderForest, sdk.areas.GreatMarsh], + nextArea: [sdk.areas.LowerKurast, sdk.areas.FlayerDungeonLvl1, sdk.areas.SwampyPitLvl1], + presetMonsters: [sdk.monsters.preset.Stormtree], + poi: [sdk.quest.chest.GidbinnAltar], + }], + [sdk.areas.SwampyPitLvl1, { + previousArea: [sdk.areas.FlayerJungle], + nextArea: [sdk.areas.SwampyPitLvl2], + }], + [sdk.areas.SwampyPitLvl2, { + previousArea: [sdk.areas.SwampyPitLvl1], + nextArea: [sdk.areas.SwampyPitLvl3], + }], + [sdk.areas.SwampyPitLvl3, { + previousArea: [sdk.areas.SwampyPitLvl2], + presetChests: [sdk.objects.SmallSparklyChest], + }], + [sdk.areas.FlayerDungeonLvl1, { + previousArea: [sdk.areas.FlayerJungle], + nextArea: [sdk.areas.FlayerDungeonLvl2], + }], + [sdk.areas.FlayerDungeonLvl2, { + previousArea: [sdk.areas.FlayerDungeonLvl1], + nextArea: [sdk.areas.FlayerDungeonLvl3], + }], + [sdk.areas.FlayerDungeonLvl3, { + previousArea: [sdk.areas.FlayerDungeonLvl2], + presetMonsters: [sdk.monsters.preset.WitchDoctorEndugu], + presetChests: [sdk.quest.chest.KhalimsBrainChest], + }], + [sdk.areas.LowerKurast, { + previousArea: [sdk.areas.FlayerJungle], + nextArea: [sdk.areas.KurastBazaar], + presetChests: [sdk.objects.SuperChest], + }], + [sdk.areas.KurastBazaar, { + previousArea: [sdk.areas.LowerKurast], + nextArea: [ + sdk.areas.UpperKurast, sdk.areas.RuinedTemple, + sdk.areas.DisusedFane, sdk.areas.A3SewersLvl1 + ], + }], + [sdk.areas.RuinedTemple, { + previousArea: [sdk.areas.KurastBazaar], + presetMonsters: [sdk.monsters.preset.BattlemaidSarina], + presetChests: [sdk.objects.SmallSparklyChest], + poi: [sdk.quest.chest.LamEsensTomeHolder], + }], + [sdk.areas.DisusedFane, { + previousArea: [sdk.areas.KurastBazaar], + presetChests: [sdk.objects.SmallSparklyChest], + }], + [sdk.areas.A3SewersLvl1, { + previousArea: [sdk.areas.KurastBazaar], + nextArea: [sdk.areas.A3SewersLvl2], + presetMonsters: [sdk.monsters.preset.IcehawkRiftwing], + poi: [sdk.objects.SewerLever], + }], + [sdk.areas.A3SewersLvl2, { + previousArea: [sdk.areas.A3SewersLvl1], + presetChests: [sdk.quest.chest.KhalimsHeartChest], + }], + [sdk.areas.UpperKurast, { + previousArea: [sdk.areas.KurastBazaar], + nextArea: [ + sdk.areas.KurastCauseway, + sdk.areas.ForgottenReliquary, + sdk.areas.ForgottenTemple], + }], + [sdk.areas.ForgottenReliquary, { + previousArea: [sdk.areas.UpperKurast], + presetChests: [sdk.objects.SmallSparklyChest], + }], + [sdk.areas.ForgottenTemple, { + previousArea: [sdk.areas.UpperKurast], + presetChests: [sdk.objects.SmallSparklyChest], + }], + [sdk.areas.KurastCauseway, { + previousArea: [sdk.areas.UpperKurast], + nextArea: [sdk.areas.Travincal, sdk.areas.RuinedFane, sdk.areas.DisusedReliquary], + }], + [sdk.areas.DisusedReliquary, { + previousArea: [sdk.areas.KurastCauseway], + presetChests: [sdk.objects.SmallSparklyChest], + }], + [sdk.areas.RuinedFane, { + previousArea: [sdk.areas.KurastCauseway], + presetChests: [sdk.objects.SmallSparklyChest], + }], + [sdk.areas.Travincal, { + previousArea: [sdk.areas.KurastCauseway], + nextArea: [sdk.areas.DuranceofHateLvl1], + presetMonsters: [ + sdk.monsters.preset.IsmailVilehand, + sdk.monsters.preset.GelebFlamefinger, + sdk.monsters.preset.ToorcIcefist + ], + poi: [sdk.objects.CompellingOrb, sdk.objects.DuranceEntryStairs], + }], + [sdk.areas.DuranceofHateLvl1, { + previousArea: [sdk.areas.Travincal], + nextArea: [sdk.areas.DuranceofHateLvl2], + preReq: function () { + return QuestData.get(sdk.quest.id.TheBlackenedTemple).complete() + || QuestData.get(sdk.quest.id.KhalimsWill).complete(); + }, + }], + [sdk.areas.DuranceofHateLvl2, { + previousArea: [sdk.areas.DuranceofHateLvl1], + nextArea: [sdk.areas.DuranceofHateLvl3], + preReq: function () { + return QuestData.get(sdk.quest.id.TheBlackenedTemple).complete() + || QuestData.get(sdk.quest.id.KhalimsWill).complete(); + }, + }], + [sdk.areas.DuranceofHateLvl3, { + previousArea: [sdk.areas.DuranceofHateLvl2], + nextArea: [sdk.areas.PandemoniumFortress], + presetMonsters: [ + sdk.monsters.preset.BremmSparkfist, + sdk.monsters.preset.WyandVoidfinger, + sdk.monsters.preset.MafferDragonhand, + sdk.monsters.Mephisto + ], + presetChests: [sdk.objects.SuperChest], + poi: [sdk.objects.RedPortalToAct4], + preReq: function () { + return QuestData.get(sdk.quest.id.TheBlackenedTemple).complete() + || QuestData.get(sdk.quest.id.KhalimsWill).complete(); + }, + }], + // Act 4 + [sdk.areas.PandemoniumFortress, { + nextArea: [sdk.areas.OuterSteppes], + poi: [sdk.objects.RedPortalToAct5], + preReq: function () { + return QuestData.get(sdk.quest.id.TheGuardian).complete() + || QuestData.get(sdk.quest.id.AbleToGotoActIV).complete(); + }, + }], + [sdk.areas.OuterSteppes, { + previousArea: [sdk.areas.PandemoniumFortress], + nextArea: [sdk.areas.PlainsofDespair], + }], + [sdk.areas.PlainsofDespair, { + previousArea: [sdk.areas.OuterSteppes], + nextArea: [sdk.areas.CityoftheDamned], + presetMonsters: [sdk.monsters.preset.Izual], + }], + [sdk.areas.CityoftheDamned, { + previousArea: [sdk.areas.PlainsofDespair], + nextArea: [sdk.areas.RiverofFlame], + }], + [sdk.areas.RiverofFlame, { + previousArea: [sdk.areas.CityoftheDamned], + nextArea: [sdk.areas.ChaosSanctuary], + presetMonsters: [sdk.monsters.preset.Hephasto], + poi: [sdk.quest.chest.HellForge], + }], + [sdk.areas.ChaosSanctuary, { + previousArea: [sdk.areas.RiverofFlame], + presetMonsters: [ + sdk.monsters.preset.GrandVizierofChaos, + sdk.monsters.preset.LordDeSeis, + sdk.monsters.preset.InfectorofSouls, + sdk.monsters.Diablo + ], + poi: [ + sdk.objects.DiabloSealSeis, sdk.objects.DiabloStar, + sdk.objects.DiabloSealInfector, sdk.objects.DiabloSealInfector2, + sdk.objects.DiabloSealVizier, sdk.objects.DiabloSealVizier2, + ], + }], + // Act 5 + [sdk.areas.Harrogath, { + nextArea: [sdk.areas.BloodyFoothills, sdk.areas.NihlathaksTemple], + poi: [sdk.objects.Act5Gate], + preReq: function () { + return QuestData.get(sdk.quest.id.AbleToGotoActV).complete(); + }, + }], + [sdk.areas.NihlathaksTemple, { + previousArea: [sdk.areas.Harrogath], + nextArea: [sdk.areas.HallsofAnguish], + presetMonsters: [sdk.monsters.preset.Pindleskin], + preReq: function () { + return QuestData.get(sdk.quest.id.PrisonofIce).complete(true); + }, + }], + [sdk.areas.HallsofAnguish, { + previousArea: [sdk.areas.NihlathaksTemple], + nextArea: [sdk.areas.HallsofPain], + presetChests: [sdk.objects.LargeSparklyChest], + preReq: function () { + return QuestData.get(sdk.quest.id.PrisonofIce).complete(true); + }, + }], + [sdk.areas.HallsofPain, { + previousArea: [sdk.areas.HallsofAnguish], + nextArea: [sdk.areas.HallsofVaught], + presetChests: [sdk.objects.LargeSparklyChest], + preReq: function () { + return QuestData.get(sdk.quest.id.PrisonofIce).complete(true); + }, + }], + [sdk.areas.HallsofVaught, { + previousArea: [sdk.areas.HallsofPain], + presetMonsters: [sdk.monsters.preset.Nihlathak], + poi: [sdk.objects.NihlathaksPlatform], + preReq: function () { + return QuestData.get(sdk.quest.id.PrisonofIce).complete(true); + }, + }], + [sdk.areas.BloodyFoothills, { + previousArea: [sdk.areas.Harrogath], + nextArea: [sdk.areas.FrigidHighlands], + presetMonsters: [ + sdk.monsters.preset.DacFarren, + sdk.monsters.preset.ShenktheOverseer + ], + }], + [sdk.areas.FrigidHighlands, { + previousArea: [sdk.areas.BloodyFoothills], + nextArea: [sdk.areas.ArreatPlateau, sdk.areas.Abaddon], + presetMonsters: [ + sdk.monsters.preset.EldritchtheRectifier, + sdk.monsters.preset.EyebacktheUnleashed, + sdk.monsters.preset.SharpToothSayer + ], + presetChests: [sdk.objects.LargeSparklyChest], + }], + [sdk.areas.Abaddon, { + previousArea: [sdk.areas.FrigidHighlands], + presetChests: [sdk.objects.SmallSparklyChest], + }], + [sdk.areas.ArreatPlateau, { + previousArea: [sdk.areas.FrigidHighlands], + nextArea: [sdk.areas.CrystalizedPassage, sdk.areas.PitofAcheron], + presetMonsters: [sdk.monsters.preset.ThreshSocket], + }], + [sdk.areas.PitofAcheron, { + previousArea: [sdk.areas.ArreatPlateau], + presetChests: [sdk.objects.SmallSparklyChest], + }], + [sdk.areas.CrystalizedPassage, { + previousArea: [sdk.areas.ArreatPlateau], + nextArea: [sdk.areas.GlacialTrail, sdk.areas.FrozenRiver], + }], + [sdk.areas.FrozenRiver, { + previousArea: [sdk.areas.CrystalizedPassage], + presetMonsters: [sdk.monsters.preset.Frozenstein], + poi: [sdk.objects.FrozenAnyasPlatform], + }], + [sdk.areas.GlacialTrail, { + previousArea: [sdk.areas.CrystalizedPassage], + nextArea: [sdk.areas.FrozenTundra, sdk.areas.DrifterCavern], + presetMonsters: [sdk.monsters.preset.BonesawBreaker], + presetChests: [sdk.objects.LargeSparklyChest], + }], + [sdk.areas.DrifterCavern, { + previousArea: [sdk.areas.GlacialTrail], + presetChests: [sdk.objects.SmallSparklyChest], + }], + [sdk.areas.FrozenTundra, { + previousArea: [sdk.areas.GlacialTrail], + nextArea: [sdk.areas.AncientsWay, sdk.areas.InfernalPit], + }], + [sdk.areas.InfernalPit, { + previousArea: [sdk.areas.FrozenTundra], + presetChests: [sdk.objects.SmallSparklyChest], + }], + [sdk.areas.AncientsWay, { + previousArea: [sdk.areas.FrozenTundra], + nextArea: [sdk.areas.ArreatSummit, sdk.areas.IcyCellar], + }], + [sdk.areas.IcyCellar, { + previousArea: [sdk.areas.AncientsWay], + presetMonsters: [sdk.monsters.preset.SnapchipShatter], + presetChests: [sdk.objects.SmallSparklyChest], + }], + [sdk.areas.ArreatSummit, { + previousArea: [sdk.areas.AncientsWay], + nextArea: [sdk.areas.WorldstoneLvl1], + presetMonsters: [ + sdk.monsters.preset.TalictheDefender, + sdk.monsters.preset.MadawctheGuardian, + sdk.monsters.preset.KorlictheProtector + ], + poi: [ + sdk.objects.KorlictheProtectorStatue, + sdk.objects.MadawctheGuardianStatue, + sdk.objects.TalictheDefenderStatue, + sdk.objects.AncientsAltar, + sdk.objects.AncientsDoor, + ], + }], + [sdk.areas.WorldstoneLvl1, { + previousArea: [sdk.areas.ArreatSummit], + nextArea: [sdk.areas.WorldstoneLvl2], + preReq: function () { + return QuestData.get(sdk.quest.id.RiteofPassage).complete(); + }, + }], + [sdk.areas.WorldstoneLvl2, { + previousArea: [sdk.areas.WorldstoneLvl1], + nextArea: [sdk.areas.WorldstoneLvl3], + preReq: function () { + return QuestData.get(sdk.quest.id.RiteofPassage).complete(); + }, + }], + [sdk.areas.WorldstoneLvl3, { + previousArea: [sdk.areas.WorldstoneLvl2], + nextArea: [sdk.areas.ThroneofDestruction], + preReq: function () { + return QuestData.get(sdk.quest.id.RiteofPassage).complete(); + }, + }], + [sdk.areas.ThroneofDestruction, { + previousArea: [sdk.areas.WorldstoneLvl3], + nextArea: [sdk.areas.WorldstoneChamber], + presetMonsters: [ + sdk.monsters.preset.ColenzotheAnnihilator, + sdk.monsters.preset.AchmeltheCursed, + sdk.monsters.preset.BartuctheBloody, + sdk.monsters.preset.VentartheUnholy, + sdk.monsters.preset.ListertheTormentor + ], + poi: [sdk.objects.WorldstonePortal], + preReq: function () { + return QuestData.get(sdk.quest.id.RiteofPassage).complete(); + }, + }], + [sdk.areas.WorldstoneChamber, { + previousArea: [sdk.areas.ThroneofDestruction], + presetMonsters: [sdk.monsters.Baal], + preReq: function () { + return QuestData.get(sdk.quest.id.RiteofPassage).complete(); + }, + }], + [sdk.areas.MatronsDen, { + // previousArea: [sdk.areas.Harrogath], + presetMonsters: [sdk.monsters.Lilith], + presetChests: [sdk.objects.SmallSparklyChest], + }], + [sdk.areas.FurnaceofPain, { + // previousArea: [sdk.areas.Harrogath], + presetMonsters: [sdk.monsters.UberIzual], + presetChests: [sdk.objects.SmallSparklyChest], + }], + [sdk.areas.ForgottenSands, { + // previousArea: [sdk.areas.Harrogath], + presetMonsters: [sdk.monsters.UberDuriel], + }], + [sdk.areas.UberTristram, { + // previousArea: [sdk.areas.Harrogath], + presetMonsters: [ + sdk.monsters.UberDiablo, + sdk.monsters.UberMephisto, + sdk.monsters.UberBaal + ], + }], + ]); - /** @type {AreaDataObj[]} */ - const AreaData = new Array(AREA_INDEX_COUNT); - - for (let i = 0; i < AreaData.length; i++) { - let index = i; - AreaData[i] = ({ - Super: SUPER[index], - Index: index, - Act: getBaseStat("levels", index, "Act"), - MonsterDensity: getBaseStat("levels", index, ["MonDen", "MonDen(N)", "MonDen(H)"][me.diff]), - ChampionPacks: ({ - Min: getBaseStat("levels", index, ["MonUMin", "MonUMin(N)", "MonUMin(H)"][me.diff]), - Max: getBaseStat("levels", index, ["MonUMax", "MonUMax(N)", "MonUMax(H)"][me.diff]) - }), - Waypoint: getBaseStat("levels", index, "Waypoint"), - Level: getBaseStat("levels", index, ["MonLvl1Ex", "MonLvl2Ex", "MonLvl3Ex"][me.diff]), - Size: (() => { - if (index === 111) { // frigid highlands doesn't specify size, manual measurement - return { x: 210, y: 710 }; - } + /** @type {Map this.regenTime; + }; + + /** + * @constructor + * @param {Exit} exit + */ + function ExitInstance (exit) { + this.x = exit.x; + this.y = exit.y; + this.target = exit.target; + this.type = exit.type; + this.tileid = exit.tileid; + } + + /** + * @constructor + * @param {number} index + */ + function AreaDataInstance (index) { + let _aData = _areaData.get(index); + + this.LocaleString = getAreaName(index); + this.Index = index; // why index and not area id? + this.Act = getBaseStat("levels", index, "Act") + 1; + this.Level = getBaseStat("levels", index, ["MonLvl1Ex", "MonLvl2Ex", "MonLvl3Ex"][me.diff]); + this.Size = (function () { + // frigid highlands doesn't specify size, manual measurement + if (index === sdk.areas.FrigidHighlands) return { x: 210, y: 710 }; + + // arreat plateau doesn't specify size, manual measurement + if (index === sdk.areas.ArreatPlateau) return { x: 690, y: 230 }; return { x: getBaseStat("leveldefs", index, ["SizeX", "SizeX(N)", "SizeX(H)"][me.diff]), y: getBaseStat("leveldefs", index, ["SizeY", "SizeY(N)", "SizeY(H)"][me.diff]) }; - })(), - Monsters: (MONSTER_KEYS.map(key => getBaseStat("levels", index, key)).filter(key => key !== 65535)), - /** - * Check if this area has a monster of a certain type - * @function - * @param {number} type - monster type to check for - * @returns {boolean} - */ - hasMonsterType: function (type) { - return this.Monsters.some(monId => MonsterData[monId].Type === type); - }, - /** - * Iterate through each monster in this area and apply a callback function - * @function - * @param {function} cb - callback function to apply to each monster + })(); + this.SuperUnique = (_aData.presetMonsters || []); + this.Monsters = (MONSTER_KEYS + .map(function (key) { + return getBaseStat("levels", index, key); + }) + .filter(function (key) { + return key !== 65535; + }) + ); + this.MonsterDensity = getBaseStat("levels", index, ["MonDen", "MonDen(N)", "MonDen(H)"][me.diff]); + this.ChampionPacks = { + Min: getBaseStat("levels", index, ["MonUMin", "MonUMin(N)", "MonUMin(H)"][me.diff]), + Max: getBaseStat("levels", index, ["MonUMax", "MonUMax(N)", "MonUMax(H)"][me.diff]) + }; + this.SuperChests = (_aData.presetChests || []); + this.Poi = (_aData.poi || []); + this.QuestPreReq = (_aData.preReq || null); + /** + * @private + * @type {PresetObjectUnit | null} */ - forEachMonster: function (cb) { - if (typeof cb === "function") { - this.Monsters.forEach(monID => { - cb(MonsterData[monID], MonsterData[monID].Rarity * (MonsterData[monID].GroupCount.Min + MonsterData[monID].GroupCount.Max) / 2); + this._Waypoint = null; + let wp = getBaseStat("levels", index, "Waypoint"); + if (wp !== 255) { + wps.set(this.Index, wp); + } + /** @type {Array} */ + this.Shrines = []; + /** @type {Array} */ + this.Chests = []; + /** @type {number[]} */ + this.NextArea = (_aData.nextArea || []); + /** @type {number[]} */ + this.PreviousArea = (_aData.previousArea || []); + /** @type {Array} */ + this.Exits = []; + /** @private */ + this._Accessible = false; + } + + /** + * Check if this area has a monster of a certain type + * @param {number} type - monster type to check for + * @returns {boolean} + */ + AreaDataInstance.prototype.hasMonsterType = function (type) { + return this.Monsters.some(function (monId) { + return MonsterData[monId].Type === type; + }); + }; + /** + * Iterate through each monster in this area and apply a callback function + * @param {function} cb - callback function to apply to each monster + */ + AreaDataInstance.prototype.forEachMonster = function (cb) { + if (typeof cb === "function") { + this.Monsters.forEach(function (monID) { + const _monster = MonsterData[monID]; + return cb( + _monster, + _monster.Rarity * (_monster.GroupCount.Min + _monster.GroupCount.Max) / 2 + ); + }); + } + }; + /** + * Iterate through each monster and minion in this area and apply a callback function + * @param {function} cb - callback function to apply to each monster + */ + AreaDataInstance.prototype.forEachMonsterAndMinion = function (cb) { + if (typeof cb === "function") { + this.Monsters.forEach(function (monID) { + const _monster = MonsterData[monID]; + let rarity = _monster.Rarity * (_monster.GroupCount.Min + _monster.GroupCount.Max) / 2; + cb(_monster, rarity, null); + _monster.Minions.forEach(function (minionID) { + // eslint-disable-next-line max-len + let minionrarity = (_monster.Rarity * (_monster.MinionCount.Min + _monster.MinionCount.Max) / 2 / _monster.Minions.length); + cb(MonsterData[minionID], minionrarity, _monster); }); + }); + } + }; + /** + * Check whether this area is accessible by quest pre-reqs + * @this AreaDataInstance + * @returns {boolean} + */ + AreaDataInstance.prototype.canAccess = function () { + if (this._Accessible) return true; + let check = this.QuestPreReq || _areaData.get(sdk.areas.townOfAct(this.Act)).preReq; + if (check()) { + this._Accessible = true; + } + return this._Accessible; + }; + /** + * Get town of area + */ + AreaDataInstance.prototype.townArea = function () { + return _map.get(sdk.areas.townOfAct(this.Act)); + }; + /** + * Get exits of the area + * @function + */ + AreaDataInstance.prototype.getExits = function () { + if (this.Exits.length) return this.Exits; + const _areaId = this.Index; + /** @type {Area} */ + const area = Misc.poll(function () { + return getArea(_areaId); + }); + if (!area) return []; + this.Exits = area.exits + .map(function (exit) { + return new ExitInstance(exit); + }); + if (specialTransit.has(_areaId)) { + let _specialExit = specialTransit.get(_areaId)(); + if (_specialExit) { + this.Exits.push(_specialExit); } - }, - /** - * Iterate through each monster and minion in this area and apply a callback function - * @function - * @param {function} cb - callback function to apply to each monster - */ - forEachMonsterAndMinion: function (cb) { - if (typeof cb === "function") { - this.Monsters.forEach(monID => { - let rarity = MonsterData[monID].Rarity * (MonsterData[monID].GroupCount.Min + MonsterData[monID].GroupCount.Max) / 2; - cb(MonsterData[monID], rarity, null); - MonsterData[monID].Minions.forEach(minionID => { - let minionrarity = MonsterData[monID].Rarity * (MonsterData[monID].MinionCount.Min + MonsterData[monID].MinionCount.Max) / 2 / MonsterData[monID].Minions.length; - cb(MonsterData[minionID], minionrarity, MonsterData[monID]); - }); + } + return this.Exits; + }; + /** + * @private + * @param {PresetUnit} wp + */ + AreaDataInstance.prototype.setWaypoint = function (wp) { + if (wp && wp instanceof PresetUnit) { + this._Waypoint = wp.realCoords(); + this._Waypoint.classid = wp.id; + } + }; + /** + * Get wp of area if it exists + * @this {AreaDataInstance} + * @returns {PresetObjectUnit | null} + */ + AreaDataInstance.prototype.waypointCoords = function () { + if (this._Waypoint) return this._Waypoint; + if (!this.hasWaypoint()) return null; + // check first that we are currently in the same act + if (me.act !== this.Act) return null; + // try to find the wp + try { + const _areaId = this.Index; + let wp = Game.getPresetObjects(_areaId) + .filter(function (preset) { + return sdk.waypoints.Ids.includes(preset.id); + }) + .find(function (preset) { + return preset.level === _areaId; }); + if (wp) { + this.setWaypoint(wp); + return this._Waypoint; } - }, - LocaleString: getLocaleString(AREA_LOCALE_STRING[index]), - InternalName: LocaleStringName[AREA_LOCALE_STRING[index]], - /** - * Check if area is a town area - * @function - */ - townArea: function () { - return AreaData[[sdk.areas.RogueEncampment, sdk.areas.LutGholein, sdk.areas.KurastDocktown, sdk.areas.PandemoniumFortress, sdk.areas.Harrogath][this.Act]]; - }, - /** - * @function - */ - haveWaypoint: function () { - // get the last area that got a WP - let wpArea = this.nearestWaypointArea(); - - // If you dont need a wp, we want at least the town's wp - return getWaypoint(Pather.wpAreas.indexOf(wpArea || this.townArea().Index)); - }, - /** - * Find nearest waypoint in area - * @function - */ - nearestWaypointArea: function () { - // plot toward this are - const plot = Pather.plotCourse(this.Index, this.townArea().Index); - - // get the last area that got a WP - return plot.course.filter(el => Pather.wpAreas.indexOf(el) > -1).last(); - }, - /** - * @function - * @return {PresetUnit|undefined} - */ - waypointPreset: function () { - const wpIDs = [119, 145, 156, 157, 237, 238, 288, 323, 324, 398, 402, 429, 494, 496, 511, 539]; - for (let i = 0, preset, wpArea = this.nearestWaypointArea(); i < wpIDs.length || preset; i++) { - if ((preset = Game.getPresetObject(wpArea, wpIDs[i]))) { - return preset; - } - } + } catch (e) { + console.error(e); + } + return null; + }; + /** + * Check if this area as a waypoint + * @returns {boolean} + */ + AreaDataInstance.prototype.hasWaypoint = function () { + return wps.has(this.Index); + }; + /** + * Find nearest waypoint in area + * @returns {number} + * @todo Fix this, the nearest waypoint could be the next area but this only + * checks up to the current area. + */ + AreaDataInstance.prototype.nearestWaypointArea = function () { + if (this.hasWaypoint() && this.waypointCoords()) return this.Index; + if (!Pather.plotCourse_openedWpMenu) { + return [].concat(this.PreviousArea, this.NextArea) + .find(function (area) { + return wps.has(area) && _map.get(area).waypointCoords(); + }); + } + // plot toward this area + const plot = Pather.plotCourse(this.Index, this.townArea().Index); - return undefined; - }, - }); - } + // get the last area that got a WP + return plot.course.filter(function (el) { + return wps.has(el); + }).last(); + }; + /** + * @this {AreaDataInstance} + * @return {PresetObjectUnit | null} + * @todo Check more than just the direct next area for a waypoint, consider the example of Tamoe Highland, + * where the waypoint isn't the direct next area (Monastery Gate) but the next area after that (Outer Cloister) + * we may be in a spot where we are actually closer to Outer Cloisers wp than Black Marsh wp + */ + AreaDataInstance.prototype.nearestWaypointCoords = function () { + let waypoints = []; + let dist = Infinity; + let prev = this.PreviousArea.first(); + let next = this.NextArea.filter(function (el) { + return wps.has(el); + }).first(); - /** - * @property {function} AreaData.findByName - * @param {string} whatToFind - * @returns - */ - AreaData.findByName = function (whatToFind) { - let matches = AreaData.map(area => [Math.min(whatToFind.diffCount(area.LocaleString), whatToFind.diffCount(area.InternalName)), area]).sort((a, b) => a[0] - b[0]); + // check our current area + if (this._Waypoint) { + if (this._Waypoint.distance < 60) return this._Waypoint; + dist = this._Waypoint.distance; + waypoints.push(this._Waypoint); + } - return matches[0][1]; - }; + // check the previous area + if (_map.get(prev) && _map.get(prev).waypointCoords()) { + let wp = _map.get(prev).waypointCoords(); + if (wp.distance < dist) { + dist = wp.distance; + if (dist < 60) return wp; + waypoints.push(wp); + } + } - AreaData.dungeons = { - DenOfEvil: [sdk.areas.DenofEvil], + // check the next area + if (_map.get(next) && _map.get(next).waypointCoords()) { + let wp = _map.get(next).waypointCoords(); + if (wp.distance < dist) { + dist = wp.distance; + if (dist < 60) return wp; + waypoints.push(wp); + } + } - Hole: [sdk.areas.HoleLvl1, sdk.areas.HoleLvl2, ], + // this returns the nearest waypoint from town to the current area + let wpArea = this.nearestWaypointArea(); + if (wpArea === prev || wpArea === next) { + return waypoints.find(function (el) { + return el.distance === dist; + }); + } + + let check = _map.get(wpArea).waypointCoords(); + + if (check.distance < dist) { + return check; + } - Pit: [sdk.areas.PitLvl1, sdk.areas.PitLvl2], + return null; + }; + /** + * Get the chests in this area + * @todo Add support for chests that are not preset objects + * @todo Add opened property to chests so we can ignore them as chests don't regen + * @returns {Array} + */ + AreaDataInstance.prototype.getChests = function () { + if (this.Chests.length) return this.Chests; + + try { + let chests = Game.getPresetObjects(this.Index) + .filter(function (preset) { + return sdk.objects.chestIds.includes(preset.id); + }); + if (chests.length) { + this.Chests = chests.map(function (preset) { + return preset.realCoords(); + }); + return this.Chests; + } + } catch (e) { + console.error(e); + } - Cave: [sdk.areas.CaveLvl1, sdk.areas.CaveLvl2], + return []; + }; - UndergroundPassage: [sdk.areas.UndergroundPassageLvl1, sdk.areas.UndergroundPassageLvl2, ], + /** + * Add shrine to our list of seen shrines for this area + * @param {ObjectUnit} shrine + */ + AreaDataInstance.prototype.addShrine = function (shrine) { + if (!shrine || !ShrineData.has(shrine.objtype)) return; + // we've already added this shrine + if (this.Shrines.find((s) => s.gid === shrine.gid)) return; + this.Shrines.push(new Shrine(shrine)); + }; + /** + * Update a shrine to our list of seen shrines for this area + * @param {ObjectUnit} shrine + */ + AreaDataInstance.prototype.updateShrine = function (shrine) { + if (!shrine || !ShrineData.has(shrine.objtype)) return; + // We don't already have this shrine, so add it + if (!this.Shrines.find(s => s.gid === shrine.gid)) { + this.Shrines.push(new Shrine(shrine)); + } else { + this.Shrines.find(s => s.gid === shrine.gid).interactedAt = getTickCount(); + } + }; + /** + * Get the shrines we have seen in this area + * @returns {Array} + */ + AreaDataInstance.prototype.getShrines = function () { + if (this.Shrines.length) return this.Shrines; + + // this only works for a1 and a2 /: + // try { + // let shrines = Game.getPresetObjects(this.Index); + // if (shrines) { + // shrines = shrines.filter(preset => sdk.shrines.Ids.includes(preset.id)); + // if (shrines) { + // this.Shrines = shrines.map(preset => preset.realCoords()); + // } + // } + // } catch (e) { + // console.error(e); - Cellar: [sdk.areas.TowerCellarLvl1, sdk.areas.TowerCellarLvl2, sdk.areas.TowerCellarLvl3, sdk.areas.TowerCellarLvl4, sdk.areas.TowerCellarLvl5, ], + // return []; + // } - // act 2 - A2Sewers: [sdk.areas.A2SewersLvl1, sdk.areas.A2SewersLvl2, sdk.areas.A2SewersLvl3, ], + return []; + }; - StonyTomb: [sdk.areas.StonyTombLvl1, sdk.areas.StonyTombLvl2, ], + for (let i = 1; i < AREA_INDEX_COUNT; i++) { + _map.set(i, (new AreaDataInstance(i))); + } - HallsOfDead: [sdk.areas.HallsoftheDeadLvl1, sdk.areas.HallsoftheDeadLvl2, sdk.areas.HallsoftheDeadLvl3, ], + /** @type {Array} */ + const _nonTownWps = wps.keys() + .filter(function (a) { + return !sdk.areas.Towns.includes(a); + }); - ClawViperTemple: [sdk.areas.ClawViperTempleLvl1, sdk.areas.ClawViperTempleLvl2, ], + return { + wps: wps, - MaggotLair: [sdk.areas.MaggotLairLvl1, sdk.areas.MaggotLairLvl2, sdk.areas.MaggotLairLvl3, ], + set: function (key, value) { + _map.set(key, value); + }, + + get: function (key) { + return _map.get(key); + }, - Tombs: [sdk.areas.TalRashasTomb1, sdk.areas.TalRashasTomb2, sdk.areas.TalRashasTomb3, sdk.areas.TalRashasTomb4, sdk.areas.TalRashasTomb5, sdk.areas.TalRashasTomb6, sdk.areas.TalRashasTomb7, ], + has: function (key) { + return _map.has(key); + }, - // act 3 - Swamp: [sdk.areas.SwampyPitLvl1, sdk.areas.SwampyPitLvl2, sdk.areas.SwampyPitLvl3, ], + forEach: function (callbackFn, thisArg) { + thisArg = thisArg || this; + for (let [key, value] of _map.entries()) { + callbackFn.call(thisArg, value, key, this); + } + }, - FlayerDungeon: [sdk.areas.FlayerDungeonLvl1, sdk.areas.FlayerDungeonLvl2, sdk.areas.FlayerDungeonLvl3, ], + keys: function () { + return _map.keys(); + }, - A3Sewers: [sdk.areas.A3SewersLvl1, sdk.areas.A3SewersLvl2, ], + entries: function () { + return _map.entries(); + }, - HighLevelForgottenTemples: [sdk.areas.ForgottenTemple, sdk.areas.RuinedFane, sdk.areas.DisusedReliquary], + randomWpArea: function (checkValid = false) { + return checkValid + ? _nonTownWps.filter(function (a) { + return me.haveWaypoint(a); + }).random() + : _nonTownWps.random(); + }, - LowLevelForgottenTemples: [sdk.areas.RuinedTemple, sdk.areas.DisusedFane, sdk.areas.ForgottenReliquary], + getAreasWithShrine: function (shrineType) { + let areas = []; - // act 4 has no areas like that + _map.forEach(function (area) { + if (area.getShrines().find(s => s.type === shrineType && s.useable())) { + areas.push(area); + } + }); - // act 5 - RedPortalPits: [sdk.areas.Abaddon, sdk.areas.PitofAcheron, sdk.areas.InfernalPit, ], - }; + return areas; + }, + }; + })(); module.exports = AreaData; })(module, require); diff --git a/libs/SoloPlay/Modules/GameData/GameData.js b/libs/SoloPlay/Modules/GameData/GameData.js index 860ed0f1..551b42aa 100644 --- a/libs/SoloPlay/Modules/GameData/GameData.js +++ b/libs/SoloPlay/Modules/GameData/GameData.js @@ -8,21 +8,21 @@ */ // todo - remove the magic numbers here (function (module, require) { - const MonsterData = require("./MonsterData"); + const MonsterData = require("../../../core/GameData/MonsterData"); const AreaData = require("./AreaData"); const MissileData = require("./MissileData"); const Coords_1 = require("../Coords"); const sdk = require("../../../modules/sdk"); - function isAlive(unit) { + function isAlive (unit) { return Boolean(unit && unit.hp); } - function isEnemy(unit) { + function isEnemy (unit) { return Boolean(unit && isAlive(unit) && unit.getStat(sdk.stats.Alignment) !== 2 && typeof unit.classid === "number" && MonsterData[unit.classid].Killable); } - function onGround(item) { + function onGround (item) { return item.onGroundOrDropping; } @@ -31,10 +31,15 @@ townAreas: [0, 1, 40, 75, 103, 109], HPLookup: [["1", "1", "1"], ["7", "107", "830"], ["9", "113", "852"], ["12", "120", "875"], ["15", "125", "897"], ["17", "132", "920"], ["20", "139", "942"], ["23", "145", "965"], ["27", "152", "987"], ["31", "157", "1010"], ["35", "164", "1032"], ["36", "171", "1055"], ["40", "177", "1077"], ["44", "184", "1100"], ["48", "189", "1122"], ["52", "196", "1145"], ["56", "203", "1167"], ["60", "209", "1190"], ["64", "216", "1212"], ["68", "221", "1235"], ["73", "228", "1257"], ["78", "236", "1280"], ["84", "243", "1302"], ["89", "248", "1325"], ["94", "255", "1347"], ["100", "261", "1370"], ["106", "268", "1392"], ["113", "275", "1415"], ["120", "280", "1437"], ["126", "287", "1460"], ["134", "320", "1482"], ["142", "355", "1505"], ["150", "388", "1527"], ["158", "423", "1550"], ["166", "456", "1572"], ["174", "491", "1595"], ["182", "525", "1617"], ["190", "559", "1640"], ["198", "593", "1662"], ["206", "627", "1685"], ["215", "661", "1707"], ["225", "696", "1730"], ["234", "729", "1752"], ["243", "764", "1775"], ["253", "797", "1797"], ["262", "832", "1820"], ["271", "867", "1842"], ["281", "900", "1865"], ["290", "935", "1887"], ["299", "968", "1910"], ["310", "1003", "1932"], ["321", "1037", "1955"], ["331", "1071", "1977"], ["342", "1105", "2000"], ["352", "1139", "2030"], ["363", "1173", "2075"], ["374", "1208", "2135"], ["384", "1241", "2222"], ["395", "1276", "2308"], ["406", "1309", "2394"], ["418", "1344", "2480"], ["430", "1379", "2567"], ["442", "1412", "2653"], ["454", "1447", "2739"], ["466", "1480", "2825"], ["477", "1515", "2912"], ["489", "1549", "2998"], ["501", "1583", "3084"], ["513", "1617", "3170"], ["525", "1651", "3257"], ["539", "1685", "3343"], ["552", "1720", "3429"], ["565", "1753", "3515"], ["579", "1788", "3602"], ["592", "1821", "3688"], ["605", "1856", "3774"], ["618", "1891", "3860"], ["632", "1924", "3947"], ["645", "1959", "4033"], ["658", "1992", "4119"], ["673", "2027", "4205"], ["688", "2061", "4292"], ["702", "2095", "4378"], ["717", "2129", "4464"], ["732", "2163", "4550"], ["746", "2197", "4637"], ["761", "2232", "4723"], ["775", "2265", "4809"], ["790", "2300", "4895"], ["805", "2333", "4982"], ["821", "2368", "5068"], ["837", "2403", "5154"], ["853", "2436", "5240"], ["868", "2471", "5327"], ["884", "2504", "5413"], ["900", "2539", "5499"], ["916", "2573", "5585"], ["932", "2607", "5672"], ["948", "2641", "5758"], ["964", "2675", "5844"], ["982", "2709", "5930"], ["999", "2744", "6017"], ["1016", "2777", "6103"], ["1033", "2812", "6189"], ["1051", "2845", "6275"], ["1068", "2880", "6362"], ["1085", "2915", "6448"], ["1103", "2948", "6534"], ["1120", "2983", "6620"], ["1137", "3016", "6707"], ["10000", "10000", "10000"]], monsterLevel: function (monsterID, areaID) { - return me.diff ? AreaData.hasOwnProperty(areaID) && AreaData[areaID].Level : MonsterData.hasOwnProperty(monsterID) && MonsterData[monsterID].Level; // levels on nm/hell are determined by area, not by monster data + return (me.diff + ? AreaData.has(areaID) && AreaData.get(areaID).Level + : MonsterData.hasOwnProperty(monsterID) && MonsterData[monsterID].Level); // levels on nm/hell are determined by area, not by monster data }, monsterExp: function (monsterID, areaID, adjustLevel = 0) { - return Experience.monsterExp[Math.min(Experience.monsterExp.length - 1, this.monsterLevel(monsterID, areaID) + adjustLevel)][me.diff] * MonsterData[monsterID].ExperienceModifier / 100; + return Experience.monsterExp[Math.min( + Experience.monsterExp.length - 1, + this.monsterLevel(monsterID, areaID) + adjustLevel + )][me.diff] * MonsterData[monsterID].ExperienceModifier / 100; }, eliteExp: function (monsterID, areaID) { return this.monsterExp(monsterID, areaID, 2) * 3; @@ -53,20 +58,26 @@ }, monsterMaxDmg: function (monsterID, areaID, adjustLevel = 0) { let level = this.monsterLevel(monsterID, areaID) + adjustLevel; - return Math.max.apply(null, [MonsterData[monsterID].Attack1MaxDmg, MonsterData[monsterID].Attack2MaxDmg, MonsterData[monsterID].Skill1MaxDmg]) * level / 100 * this.monsterDamageModifier(); + let { Attack1MaxDmg, Attack2MaxDmg, Skill1MaxDmg } = MonsterData[monsterID]; + return Math.max.apply( + null, [Attack1MaxDmg, Attack2MaxDmg, Skill1MaxDmg] + ) * level / 100 * this.monsterDamageModifier(); }, // https://www.diabloii.net/forums/threads/monster-damage-increase-per-player-count.570346/ monsterAttack1AvgDmg: function (monsterID, areaID, adjustLevel = 0) { let level = this.monsterLevel(monsterID, areaID) + adjustLevel; - return ((MonsterData[monsterID].Attack1MinDmg + MonsterData[monsterID].Attack1MaxDmg) / 2) * level / 100 * this.monsterDamageModifier(); + let { Attack1MinDmg, Attack1MaxDmg } = MonsterData[monsterID]; + return ((Attack1MinDmg + Attack1MaxDmg) / 2) * level / 100 * this.monsterDamageModifier(); }, monsterAttack2AvgDmg: function (monsterID, areaID, adjustLevel = 0) { let level = this.monsterLevel(monsterID, areaID) + adjustLevel; - return ((MonsterData[monsterID].Attack2MinDmg + MonsterData[monsterID].Attack2MaxDmg) / 2) * level / 100 * this.monsterDamageModifier(); + let { Attack2MinDmg, Attack2MaxDmg } = MonsterData[monsterID]; + return ((Attack2MinDmg + Attack2MaxDmg) / 2) * level / 100 * this.monsterDamageModifier(); }, monsterSkill1AvgDmg: function (monsterID, areaID, adjustLevel = 0) { let level = this.monsterLevel(monsterID, areaID) + adjustLevel; - return ((MonsterData[monsterID].Skill1MinDmg + MonsterData[monsterID].Skill1MaxDmg) / 2) * level / 100 * this.monsterDamageModifier(); + let { Skill1MinDmg, Skill1MaxDmg } = MonsterData[monsterID]; + return ((Skill1MinDmg + Skill1MaxDmg) / 2) * level / 100 * this.monsterDamageModifier(); }, monsterAvgDmg: function (monsterID, areaID, adjustLevel = 0) { let attack1 = this.monsterAttack1AvgDmg(monsterID, areaID, adjustLevel); @@ -77,14 +88,17 @@ if (!dmgs.length) return 0; return dmgs.reduce((acc, v) => acc + v) / dmgs.length; }, - averagePackSize: monsterID => (MonsterData[monsterID].GroupCount.Min + MonsterData[monsterID].MinionCount.Min + MonsterData[monsterID].GroupCount.Max + MonsterData[monsterID].MinionCount.Max) / 2, + averagePackSize: function (monsterID) { + let { GroupCount, MinionCount } = MonsterData[monsterID]; + return (GroupCount.Min + MinionCount.Min + GroupCount.Max + MinionCount.Max) / 2; + }, areaLevel: function (areaID) { // levels on nm/hell are determined by area, not by monster data - if (me.diff) return AreaData[areaID].Level; + if (me.diff) return AreaData.get(areaID).Level; let levels = 0, total = 0; - AreaData[areaID].forEachMonsterAndMinion((mon, rarity) => { + AreaData.get(areaID).forEachMonsterAndMinion(function (mon, rarity) { levels += mon.Level * rarity; total += rarity; }); @@ -94,7 +108,7 @@ areaImmunities: function (areaID) { let resists = { Physical: 0, Magic: 0, Fire: 0, Lightning: 0, Cold: 0, Poison: 0 }; - AreaData[areaID].forEachMonsterAndMinion(mon => { + AreaData.get(areaID).forEachMonsterAndMinion(function (mon) { for (let k in resists) { resists[k] = Math.max(resists[k], mon[k]); } @@ -165,7 +179,10 @@ } } while (party.getNext()); - return Math.floor(exp * this.levelModifier(level, this.monsterLevel(monsterID, areaID)) * this.multiplayerModifier(gamesize) * level / total); + return Math.floor( + exp * this.levelModifier(level, this.monsterLevel(monsterID, areaID)) + * this.multiplayerModifier(gamesize) * level / total + ); }, baseLevel: function (...skillIDs) { return skillIDs.reduce((total, skillID) => total + GameData.myReference.getSkill(skillID, 0), 0); @@ -307,24 +324,106 @@ }, baseSkillDamage: function (skillID) { // TODO: rework skill damage to use both damage fields let l = this.skillLevel(skillID), m = this.skillMult[skillID] || 1; - let dmgFields = [["MinDam", "MinLevDam1", "MinLevDam2", "MinLevDam3", "MinLevDam4", "MinLevDam5", "MaxDam", "MaxLevDam1", "MaxLevDam2", "MaxLevDam3", "MaxLevDam4", "MaxLevDam5"], ["EMin", "EMinLev1", "EMinLev2", "EMinLev3", "EMinLev4", "EMinLev5", "EMax", "EMaxLev1", "EMaxLev2", "EMaxLev3", "EMaxLev4", "EMaxLev5"]]; + let dmgFields = [ + [ + "MinDam", "MinLevDam1", + "MinLevDam2", "MinLevDam3", + "MinLevDam4", "MinLevDam5", + "MaxDam", "MaxLevDam1", + "MaxLevDam2", "MaxLevDam3", + "MaxLevDam4", "MaxLevDam5" + ], + [ + "EMin", "EMinLev1", + "EMinLev2", "EMinLev3", + "EMinLev4", "EMinLev5", + "EMax", "EMaxLev1", + "EMaxLev2", "EMaxLev3", + "EMaxLev4", "EMaxLev5" + ] + ]; if (skillID === 70) { return { type: "Physical", - pmin: this.stagedDamage(l, getBaseStat("skills", skillID, dmgFields[1][0]), getBaseStat("skills", skillID, dmgFields[1][1]), getBaseStat("skills", skillID, dmgFields[1][2]), getBaseStat("skills", skillID, dmgFields[1][3]), getBaseStat("skills", skillID, dmgFields[1][4]), getBaseStat("skills", skillID, dmgFields[1][5]), getBaseStat("skills", skillID, "HitShift"), m), - pmax: this.stagedDamage(l, getBaseStat("skills", skillID, dmgFields[1][0]), getBaseStat("skills", skillID, dmgFields[1][1]), getBaseStat("skills", skillID, dmgFields[1][2]), getBaseStat("skills", skillID, dmgFields[1][3]), getBaseStat("skills", skillID, dmgFields[1][4]), getBaseStat("skills", skillID, dmgFields[1][5]), getBaseStat("skills", skillID, "HitShift"), m), - min: 0, max: 0 + pmin: this.stagedDamage( + l, + getBaseStat("skills", skillID, dmgFields[1][0]), + getBaseStat("skills", skillID, dmgFields[1][1]), + getBaseStat("skills", skillID, dmgFields[1][2]), + getBaseStat("skills", skillID, dmgFields[1][3]), + getBaseStat("skills", skillID, dmgFields[1][4]), + getBaseStat("skills", skillID, dmgFields[1][5]), + getBaseStat("skills", skillID, "HitShift"), + m + ), + pmax: this.stagedDamage( + l, + getBaseStat("skills", skillID, dmgFields[1][0]), + getBaseStat("skills", skillID, dmgFields[1][1]), + getBaseStat("skills", skillID, dmgFields[1][2]), + getBaseStat("skills", skillID, dmgFields[1][3]), + getBaseStat("skills", skillID, dmgFields[1][4]), + getBaseStat("skills", skillID, dmgFields[1][5]), + getBaseStat("skills", skillID, "HitShift"), + m + ), + min: 0, + max: 0 }; } else { let type = getBaseStat("skills", skillID, "EType"); return { type: this.damageTypes[type], - pmin: this.stagedDamage(l, getBaseStat("skills", skillID, dmgFields[0][0]), getBaseStat("skills", skillID, dmgFields[0][1]), getBaseStat("skills", skillID, dmgFields[0][2]), getBaseStat("skills", skillID, dmgFields[0][3]), getBaseStat("skills", skillID, dmgFields[0][4]), getBaseStat("skills", skillID, dmgFields[0][5]), getBaseStat("skills", skillID, "HitShift"), m), - pmax: this.stagedDamage(l, getBaseStat("skills", skillID, dmgFields[0][6]), getBaseStat("skills", skillID, dmgFields[0][7]), getBaseStat("skills", skillID, dmgFields[0][8]), getBaseStat("skills", skillID, dmgFields[0][9]), getBaseStat("skills", skillID, dmgFields[0][10]), getBaseStat("skills", skillID, dmgFields[0][11]), getBaseStat("skills", skillID, "HitShift"), m), - min: type ? this.stagedDamage(l, getBaseStat("skills", skillID, dmgFields[1][0]), getBaseStat("skills", skillID, dmgFields[1][1]), getBaseStat("skills", skillID, dmgFields[1][2]), getBaseStat("skills", skillID, dmgFields[1][3]), getBaseStat("skills", skillID, dmgFields[1][4]), getBaseStat("skills", skillID, dmgFields[1][5]), getBaseStat("skills", skillID, "HitShift"), m) : 0, - max: type ? this.stagedDamage(l, getBaseStat("skills", skillID, dmgFields[1][6]), getBaseStat("skills", skillID, dmgFields[1][7]), getBaseStat("skills", skillID, dmgFields[1][8]), getBaseStat("skills", skillID, dmgFields[1][9]), getBaseStat("skills", skillID, dmgFields[1][10]), getBaseStat("skills", skillID, dmgFields[1][11]), getBaseStat("skills", skillID, "HitShift"), m) : 0 + pmin: this.stagedDamage( + l, + getBaseStat("skills", skillID, dmgFields[0][0]), + getBaseStat("skills", skillID, dmgFields[0][1]), + getBaseStat("skills", skillID, dmgFields[0][2]), + getBaseStat("skills", skillID, dmgFields[0][3]), + getBaseStat("skills", skillID, dmgFields[0][4]), + getBaseStat("skills", skillID, dmgFields[0][5]), + getBaseStat("skills", skillID, "HitShift"), + m + ), + pmax: this.stagedDamage( + l, + getBaseStat("skills", skillID, dmgFields[0][6]), + getBaseStat("skills", skillID, dmgFields[0][7]), + getBaseStat("skills", skillID, dmgFields[0][8]), + getBaseStat("skills", skillID, dmgFields[0][9]), + getBaseStat("skills", skillID, dmgFields[0][10]), + getBaseStat("skills", skillID, dmgFields[0][11]), + getBaseStat("skills", skillID, "HitShift"), + m + ), + min: type + ? this.stagedDamage( + l, + getBaseStat("skills", skillID, dmgFields[1][0]), + getBaseStat("skills", skillID, dmgFields[1][1]), + getBaseStat("skills", skillID, dmgFields[1][2]), + getBaseStat("skills", skillID, dmgFields[1][3]), + getBaseStat("skills", skillID, dmgFields[1][4]), + getBaseStat("skills", skillID, dmgFields[1][5]), + getBaseStat("skills", skillID, "HitShift"), + m + ) + : 0, + max: type + ? this.stagedDamage( + l, + getBaseStat("skills", skillID, dmgFields[1][6]), + getBaseStat("skills", skillID, dmgFields[1][7]), + getBaseStat("skills", skillID, dmgFields[1][8]), + getBaseStat("skills", skillID, dmgFields[1][9]), + getBaseStat("skills", skillID, dmgFields[1][10]), + getBaseStat("skills", skillID, dmgFields[1][11]), + getBaseStat("skills", skillID, "HitShift"), + m + ) + : 0 }; } }, @@ -430,7 +529,7 @@ return 0; }, physicalAttackDamage: function (skillID) { - let dmg = (() => { + let dmg = (function () { switch (skillID) { case sdk.skills.Bash: return 45 + (5 + GameData.myReference.getSkill(skillID, 1)) + (5 * GameData.myReference.getSkill(sdk.skills.Stun, 0)); @@ -517,7 +616,9 @@ break; } - if (target.gid !== unit.gid && getDistance(unit, this.novaLike[skillID] ? GameData.myReference : target) <= radius && isEnemy(unit)) { + if (target.gid !== unit.gid + && getDistance(unit, this.novaLike[skillID] ? GameData.myReference : target) <= radius + && isEnemy(unit)) { aps++; if (unit.isSpecial) { @@ -779,7 +880,11 @@ */ avgSkillDamage: function (skillID, unit) { if (skillID === undefined || unit === undefined || !skillID || !unit || !Skill.canUse(skillID)) return 0; - const ampDmg = Skill.canUse(sdk.skills.AmplifyDamage) ? 100 : (Skill.canUse(sdk.skills.Decrepify) ? 50 : 0); + const ampDmg = Skill.canUse(sdk.skills.AmplifyDamage) + ? 100 + : (Skill.canUse(sdk.skills.Decrepify) + ? 50 + : 0); /** * @@ -820,7 +925,9 @@ */ const calculateSplashDamage = function (skill, splash, target) { return getUnits(sdk.unittype.Monster) - .filter((mon) => mon.attackable && getDistance(target, mon) < splash) + .filter(function (mon) { + return mon.attackable && getDistance(target, mon) < splash; + }) .reduce(function (acc, cur) { let _a = GameData.skillDamage(skill, cur); return acc + getTotalDmg(_a, cur); @@ -842,8 +949,12 @@ break; } let units = getUnits(sdk.unittype.Monster) - .filter((mon) => mon.attackable && getDistance(mon, target) < range) - .sort((a, b) => getDistance(target, a) - getDistance(target, b)); + .filter(function (mon) { + return mon.attackable && getDistance(mon, target) < range; + }) + .sort(function (a, b) { + return getDistance(target, a) - getDistance(target, b); + }); if (units.length === 1) { rawDmg = GameData.skillDamage(skill, target); return getTotalDmg(rawDmg, target); @@ -926,7 +1037,9 @@ // sum the total in the range of the volcano missiles // what about monsters just directly on the volcano? return getUnits(sdk.unittype.Monster) - .filter((mon) => mon.attackable && getDistance(unit, mon) < 15) + .filter(function (mon) { + return mon.attackable && getDistance(unit, mon) < 15; + }) .reduce(function (acc, cur) { return acc + getTotalDmg(missleDmg, cur); }, getTotalDmg(baseDmg, unit)); @@ -1104,12 +1217,23 @@ 279: 9, }, preAttackable: [ - sdk.skills.MagicArrow, sdk.skills.FireArrow, sdk.skills.MultipleShot, sdk.skills.ExplodingArrow, sdk.skills.IceArrow, sdk.skills.GuidedArrow, sdk.skills.ImmolationArrow, sdk.skills.Strafe, + sdk.skills.MagicArrow, sdk.skills.FireArrow, + sdk.skills.MultipleShot, sdk.skills.ExplodingArrow, + sdk.skills.IceArrow, sdk.skills.GuidedArrow, + sdk.skills.ImmolationArrow, sdk.skills.Strafe, sdk.skills.PlagueJavelin, sdk.skills.LightningFury, - sdk.skills.FireBolt, sdk.skills.Inferno, sdk.skills.Blaze, sdk.skills.FireBall, sdk.skills.FireWall, sdk.skills.Meteor, sdk.skills.Hydra, - sdk.skills.ChargedBolt, sdk.skills.Nova, sdk.skills.Lightning, sdk.skills.ChainLightning, - sdk.skills.IceBolt, sdk.skills.FrostNova, sdk.skills.IceBlast, sdk.skills.Blizzard, sdk.skills.FrozenOrb, - sdk.skills.AmplifyDamage, sdk.skills.DimVision, sdk.skills.Weaken, sdk.skills.IronMaiden, sdk.skills.Terror, sdk.skills.Confuse, sdk.skills.LifeTap, sdk.skills.Attract, sdk.skills.Decrepify, sdk.skills.LowerResist, + sdk.skills.FireBolt, sdk.skills.Inferno, + sdk.skills.Blaze, sdk.skills.FireBall, + sdk.skills.FireWall, sdk.skills.Meteor, sdk.skills.Hydra, + sdk.skills.ChargedBolt, sdk.skills.Nova, + sdk.skills.Lightning, sdk.skills.ChainLightning, + sdk.skills.IceBolt, sdk.skills.FrostNova, + sdk.skills.IceBlast, sdk.skills.Blizzard, sdk.skills.FrozenOrb, + sdk.skills.AmplifyDamage, sdk.skills.DimVision, + sdk.skills.Weaken, sdk.skills.IronMaiden, + sdk.skills.Terror, sdk.skills.Confuse, + sdk.skills.LifeTap, sdk.skills.Attract, + sdk.skills.Decrepify, sdk.skills.LowerResist, sdk.skills.Teeth, sdk.skills.BoneSpear, sdk.skills.PoisonNova, sdk.skills.BlessedHammer, sdk.skills.WarCry, @@ -1121,7 +1245,8 @@ return stat ? (unit.getStat ? unit.getStat(stat) : MonsterData[unit][type]) : 0; }, getConviction: function () { - let merc = GameData.myReference.getMerc(), sl = this.skillLevel(123); // conviction + let merc = GameData.myReference.getMerc(); + let sl = this.skillLevel(123); // conviction if (( // Either me, or merc is wearing a conviction merc && merc.getItemsEx().filter(item => item.getPrefix(sdk.locale.items.Infinity)).first() || GameData.myReference.getItemsEx(-1, 1).filter(item => item.getPrefix(sdk.locale.items.Infinity)).first())) { @@ -1405,14 +1530,14 @@ skills = skills || this.allSkillDamage(); - AreaData[areaID].forEachMonsterAndMinion((mon, rarity, parent) => { + AreaData.get(areaID).forEachMonsterAndMinion(function (mon, rarity, parent) { effortpool += rarity * this.monsterEffort(mon.Index, areaID, skills, parent && parent.Index).effort; raritypool += rarity; dmgAcc += rarity * this.monsterAvgDmg(mon.Index, areaID); }); - // console.debug('avg dmg '+ AreaData[areaID].LocaleString+' -- ' + dmgAcc+' -- ' + avgDmg); + // console.debug('avg dmg '+ AreaData.get(areaID).LocaleString+' -- ' + dmgAcc+' -- ' + avgDmg); return (raritypool ? effortpool / raritypool : Infinity); }, @@ -1422,7 +1547,7 @@ let effortpool = 0, raritypool = 0, dmgAcc = 0; skills = skills || this.allSkillDamage(); - AreaData[areaID].forEachMonsterAndMinion((mon, rarity, parent) => { + AreaData.get(areaID).forEachMonsterAndMinion((mon, rarity, parent) => { effortpool += rarity * this.monsterExp(mon.Index, areaID) * this.levelModifier(GameData.myReference.charlvl, this.monsterLevel(mon.Index, areaID)) / this.monsterEffort(mon.Index, areaID, skills, parent && parent.Index).effort; raritypool += rarity; @@ -1868,12 +1993,19 @@ } }; - function calculateKillableFallensByFrostNova() { + function calculateKillableFallensByFrostNova () { if (!Skill.canUse(sdk.skills.FrostNova)) return 0; - let fallens = [sdk.monsters.Fallen, sdk.monsters.Carver2, sdk.monsters.Devilkin2, sdk.monsters.DarkOne1, sdk.monsters.WarpedFallen, sdk.monsters.Carver1, sdk.monsters.Devilkin, sdk.monsters.DarkOne2]; + const fallens = [ + sdk.monsters.Fallen, sdk.monsters.Carver2, + sdk.monsters.Devilkin2, sdk.monsters.DarkOne1, + sdk.monsters.WarpedFallen, sdk.monsters.Carver1, + sdk.monsters.Devilkin, sdk.monsters.DarkOne2 + ]; let area = me.area; return getUnits(sdk.unittype.Monster) - .filter(unit => !!unit && fallens.includes(unit.classid) && unit.distance < 7) + .filter(function (unit) { + return !!unit && fallens.includes(unit.classid) && unit.distance < 7; + }) .filter(function (unit) { return unit.attackable && typeof unit.x === "number" // happens if monster despawns @@ -1892,14 +2024,20 @@ }, 0); } - function calculateKillableSummonsByNova() { + function calculateKillableSummonsByNova () { if (!Skill.canUse(sdk.skills.Nova)) return 0; - let summons = [ - sdk.monsters.Fallen, sdk.monsters.Carver2, sdk.monsters.Devilkin2, sdk.monsters.DarkOne1, sdk.monsters.WarpedFallen, sdk.monsters.Carver1, sdk.monsters.Devilkin, sdk.monsters.DarkOne2, - sdk.monsters.BurningDead, sdk.monsters.Returned1, sdk.monsters.Returned2, sdk.monsters.BoneWarrior1, sdk.monsters.BoneWarrior2 + const summons = [ + sdk.monsters.Fallen, sdk.monsters.Carver2, + sdk.monsters.Devilkin2, sdk.monsters.DarkOne1, + sdk.monsters.WarpedFallen, sdk.monsters.Carver1, + sdk.monsters.Devilkin, sdk.monsters.DarkOne2, + sdk.monsters.BurningDead, sdk.monsters.Returned1, + sdk.monsters.Returned2, sdk.monsters.BoneWarrior1, sdk.monsters.BoneWarrior2 ]; return getUnits(sdk.unittype.Monster) - .filter(unit => !!unit && summons.includes(unit.classid) && unit.distance < 7) + .filter(function (unit) { + return !!unit && summons.includes(unit.classid) && unit.distance < 7; + }) .filter(function (unit) { return unit.attackable && typeof unit.x === "number" // happens if monster despawns @@ -1919,7 +2057,9 @@ Object.defineProperty(Unit.prototype, "currentVelocity", { get: function () { if (!this.isMoving || this.isFrozen) return 0; - const velocity = this.isRunning ? MonsterData[this.classid].Run : MonsterData[this.classid].Velocity; + const velocity = this.isRunning + ? MonsterData[this.classid].Run + : MonsterData[this.classid].Velocity; if (this.isChilled) { let malus = MonsterData[this.classid].ColdEffect; (malus > 0) && (malus = malus - 256); diff --git a/libs/SoloPlay/index.d.ts b/libs/SoloPlay/index.d.ts index 7b77dda8..62b4bb60 100644 --- a/libs/SoloPlay/index.d.ts +++ b/libs/SoloPlay/index.d.ts @@ -362,5 +362,79 @@ declare global { namespace LocationAction { function run(): void; } + + class ShrineInstance { + constructor (shrine: ObjectUnit); + + type: number; + classid: number; + state: number; + duration: number; + regenTime: number; + area: number; + x: number; + y: number; + gid: number; + interactedAt: number; + + useable(): boolean; + } + class AreaDataInstance { + constructor (index: number); + + LocaleString: string; + Index: number; + Act: number; + Level: number; + Size: { + x: number; + y: number; + }; + SuperUnique: number[]; + Monsters: number[]; + MonsterDensity: number; + ChampionPacks: { + Min: number; + Max: number; + }; + private _Waypoint: PresetObjectUnit | null; + Shrines: ShrineInstance[]; + Chests: PresetObjectUnit[]; + + hasMonsterType (type: number): boolean; + forEachMonster (callback: (monster: number) => void): void; + forEachMonsterAndMinion (callback: (monster: number) => void): void; + canAccess(): boolean; + townArea(): AreaDataInstance; + getExits(): Exit[]; + setWaypoint(wp: PresetUnit): void; + waypointCoords(): PresetObjectUnit | null; + haveWaypoint(): boolean; + hasWaypoint(): boolean; + nearestWaypointArea(): number; + nearestWaypointCoords(): PresetObjectUnit | null; + getChests(): PresetObjectUnit[]; + addShrine(shrine: ObjectUnit): void; + updateShrine(shrine: ObjectUnit): void; + getShrines(): ShrineInstance[]; + } + + namespace AreaData { + /** @private */ + const _map: Map; + const wps: Map; + const nextAreas: Map; + const previousAreas: Map; + + function set(key: number, value: AreaDataInstance): void; + function get(key: number): AreaDataInstance | undefined; + function has(key: number): boolean; + function forEach(callback: (value: AreaDataInstance, key: number) => void): void; + /** + * @description returns a random non town wp area + */ + function randomWpArea(checkValid: boolean): number; + function getAreasWithShrine(shrineType: number): AreaDataInstance[]; + } } export{}; From 2e8bcc00ff02e790302de28f07838bf48e49c9d0 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Thu, 8 Jun 2023 18:42:00 -0400 Subject: [PATCH 147/263] formatting updates - should also fix bug where bot fails to get equipped item so it tries to equip something lower tier --- libs/SoloPlay/Functions/AutoMuleOverrides.js | 2 +- libs/SoloPlay/Functions/ItemOverrides.js | 24 +++++++++++++++---- libs/SoloPlay/Functions/PrototypeOverrides.js | 10 ++++---- 3 files changed, 27 insertions(+), 9 deletions(-) diff --git a/libs/SoloPlay/Functions/AutoMuleOverrides.js b/libs/SoloPlay/Functions/AutoMuleOverrides.js index 006c3cfb..d865f1b8 100644 --- a/libs/SoloPlay/Functions/AutoMuleOverrides.js +++ b/libs/SoloPlay/Functions/AutoMuleOverrides.js @@ -86,7 +86,7 @@ AutoMule.getMuleItems = function () { if (AutoMule.matchItem(item, Config.AutoMule.Force.concat(Config.AutoMule.Trigger))) return true; // alright that handles the basics -- now normal pickit check return (Pickit.checkItem(item).result > 0 - && NTIP.CheckItem(item, NTIP.CheckList, true).result === 1) || (item.isInStash && muleOrphans); + && NTIP.CheckItem(item, NTIP.CheckList) === 1) || (item.isInStash && muleOrphans); }); return items; diff --git a/libs/SoloPlay/Functions/ItemOverrides.js b/libs/SoloPlay/Functions/ItemOverrides.js index 982a49d8..0fb78f74 100644 --- a/libs/SoloPlay/Functions/ItemOverrides.js +++ b/libs/SoloPlay/Functions/ItemOverrides.js @@ -272,9 +272,14 @@ Item.autoEquip = function (task = "") { }; // stash'd unid check - let unids = items.filter(item => !item.identified && item.isInStash); + let unids = items + .filter(function (item) { + return !item.identified && item.isInStash; + }); if (unids.length && NPCAction.fillTome(sdk.items.TomeofIdentify, true)) { - unids.forEach(item => Item.identify(item)); + unids.forEach(function (item) { + Item.identify(item); + }); } // ring check - sometimes a higher tier ring ends up on the wrong finger causing a rollback loop @@ -296,7 +301,8 @@ Item.autoEquip = function (task = "") { if (!item.isInStorage) continue; let tier = NTIP.GetTier(item); if (tier <= 0) continue; - let bodyLoc = this.getBodyLoc(item); + /** @type {Array} */ + const bodyLoc = this.getBodyLoc(item); for (let loc of bodyLoc) { const equippedItem = me.equipped.get(loc); @@ -365,9 +371,19 @@ Item.equip = function (item, bodyLoc) { if (item.isInCube && !Cubing.openCube()) return false; let currentEquipped = me.getItemsEx() - .filter(item => item.isEquipped && item.bodylocation === bodyLoc) + .filter(function (item) { + return item.isEquipped && item.bodylocation === bodyLoc; + }) .first(); if (currentEquipped) { + if (NTIP.GetTier(currentEquipped) > NTIP.GetTier(item)) { + console.debug( + "ÿc9Equipÿc0 ::\n" + + "ÿc2: Tried to equip lower tier'd item. " + item.prettyPrint + "\n" + ); + console.debug(me.equipped.get(bodyLoc)); + return false; + } console.debug( "ÿc9Equipÿc0 ::\n" + "ÿc9 - Equippingÿc0: " + item.prettyPrint + "\n" diff --git a/libs/SoloPlay/Functions/PrototypeOverrides.js b/libs/SoloPlay/Functions/PrototypeOverrides.js index 0553c0a7..bd1125b0 100644 --- a/libs/SoloPlay/Functions/PrototypeOverrides.js +++ b/libs/SoloPlay/Functions/PrototypeOverrides.js @@ -379,7 +379,6 @@ Unit.prototype.castChargedSkillEx = function (...args) { CharData.skillData.chargedSkills .forEach(function (chargeSkill) { if (chargeSkill.skill !== skillId) return; - console.debug(chargeSkill); let item = me.getItem(-1, sdk.items.mode.Equipped, chargeSkill.gid); !!item && chargedItems.push({ charge: chargeSkill.skill, @@ -389,7 +388,9 @@ Unit.prototype.castChargedSkillEx = function (...args) { }); if (chargedItems.length === 0) { - console.log("ÿc9CastChargedSkillÿc0 :: Don't have the charged skill (" + skillId + "), or not enough charges"); + console.error( + "ÿc9CastChargedSkillÿc0 :: Don't have the charged skill (" + skillId + "), or not enough charges" + ); return false; } @@ -522,7 +523,6 @@ Unit.prototype.castSwitchChargedSkill = function (...args) { CharData.skillData.chargedSkillsOnSwitch.forEach(function (chargeSkill) { if (chargeSkill.skill === skillId) { - console.debug(chargeSkill); let item = me.getItem(-1, sdk.items.mode.Equipped, chargeSkill.gid); !!item && chargedItems.push({ charge: chargeSkill.skill, @@ -533,7 +533,9 @@ Unit.prototype.castSwitchChargedSkill = function (...args) { }); if (chargedItems.length === 0) { - console.log("ÿc9SwitchCastChargedSkillÿc0 :: Don't have the charged skill (" + skillId + "), or not enough charges"); + console.error( + "ÿc9SwitchCastChargedSkillÿc0 :: Don't have the charged skill (" + skillId + "), or not enough charges" + ); return false; } From 0f427ae8a10ea038502efaec37d5385383bdedd1 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Thu, 8 Jun 2023 18:42:56 -0400 Subject: [PATCH 148/263] Update AttackOverrides.js - formatting mostly - working to control the attack recursion that happens when walking characters try to reposition to attack --- libs/SoloPlay/Functions/AttackOverrides.js | 139 +++++++++++++-------- 1 file changed, 85 insertions(+), 54 deletions(-) diff --git a/libs/SoloPlay/Functions/AttackOverrides.js b/libs/SoloPlay/Functions/AttackOverrides.js index c511b26b..7b927235 100644 --- a/libs/SoloPlay/Functions/AttackOverrides.js +++ b/libs/SoloPlay/Functions/AttackOverrides.js @@ -124,7 +124,8 @@ Attack.checkResist = function (unit, val = -1, maxres = 100) { const damageType = typeof val === "number" ? this.getSkillElement(val) : val; const addLowerRes = !!( (Skill.canUse(sdk.skills.LowerResist) - || CharData.skillData.haveChargedSkillOnSwitch(sdk.skills.LowerResist)) && unit.curseable + || CharData.skillData.haveChargedSkillOnSwitch(sdk.skills.LowerResist)) + && unit.curseable ); // Static handler @@ -310,8 +311,12 @@ Attack.killTarget = function (name) { } } - Config.Dodge && me.hpPercent <= Config.DodgeHP && this.deploy(target, Config.DodgeRange, 5, 9); - attackCount > 0 && attackCount % 15 === 0 && Skill.getRange(Config.AttackSkill[1]) < 4 && Packet.flash(me.gid); + if (Config.Dodge && me.hpPercent <= Config.DodgeHP) { + this.deploy(target, Config.DodgeRange, 5, 9); + } + if (attackCount > 0 && attackCount % 15 === 0) { + Skill.getRange(Config.AttackSkill[1]) < 4 && Packet.flash(me.gid); + } me.overhead("KillTarget: " + target.name + " health " + target.hpPercent + " % left"); let result = ClassAttack.doAttack(target, attackCount % 15 === 0); @@ -385,8 +390,11 @@ Attack.clearPos = function (x, y, range = 15, pickit = true) { if (typeof (range) !== "number") throw new Error("Attack.clear: range must be a number."); if (Config.AttackSkill[1] < 0 || Config.AttackSkill[3] < 0 || Attack.stopClear || !x || !y) return false; - let i, start, skillCheck; - let [monsterList, gidAttack] = [[], []]; + /** @type {Map} */ + let monsterList = []; + let start = false; let [retry, attackCount] = [0, 0]; let target = Game.getMonster(); @@ -394,7 +402,8 @@ Attack.clearPos = function (x, y, range = 15, pickit = true) { do { if (target.attackable && !this.skipCheck(target) && this.canAttack(target)) { // Speed optimization - don't go through monster list until there's at least one within clear range - if (!start && getDistance(target, x, y) <= range && (Pather.useTeleport() || !checkCollision(me, target, sdk.collision.WallOrRanged))) { + if (!start && getDistance(target, x, y) <= range + && (Pather.useTeleport() || !checkCollision(me, target, sdk.collision.WallOrRanged))) { start = true; } @@ -410,11 +419,16 @@ Attack.clearPos = function (x, y, range = 15, pickit = true) { target = copyUnit(monsterList[0]); if (target.x !== undefined - && (getDistance(target, x, y) <= range || (this.getScarinessLevel(target) > 7 && getDistance(me, target) <= range)) && target.attackable) { - Config.Dodge && me.hpPercent <= Config.DodgeHP && this.deploy(target, Config.DodgeRange, 5, 9); - let checkMobAttackCount = gidAttack.find(g => g.gid === target.gid); - let checkAttackSkill = (!!checkMobAttackCount && checkMobAttackCount.attacks > 0 && checkMobAttackCount.attacks % 3 === 0); - let result = ClassAttack.doAttack(target, checkAttackSkill); + && (getDistance(target, x, y) <= range + || (this.getScarinessLevel(target) > 7 && getDistance(me, target) <= range)) + && target.attackable) { + if (Config.Dodge && me.hpPercent <= Config.DodgeHP) { + this.deploy(target, Config.DodgeRange, 5, 9); + } + + let _currMon = attacks.get(target.gid); + const checkAttackSkill = (!!_currMon && _currMon.attacks > 0 && _currMon.attacks % 3 === 0); + const result = ClassAttack.doAttack(target, checkAttackSkill); if (result) { retry = 0; @@ -427,20 +441,20 @@ Attack.clearPos = function (x, y, range = 15, pickit = true) { continue; } - for (i = 0; i < gidAttack.length; i += 1) { - if (gidAttack[i].gid === target.gid) { - break; - } + if (!_currMon) { + _currMon = { attacks: 0, name: target.name }; + attacks.set(target.gid, _currMon); } - (i === gidAttack.length) && gidAttack.push({ gid: target.gid, attacks: 0, name: target.name }); - gidAttack[i].attacks += 1; + _currMon.attacks += 1; attackCount += 1; - let isSpecial = target.isSpecial; - let secAttack = me.barbarian ? (isSpecial ? 2 : 4) : 5; + let skillCheck; + const isSpecial = target.isSpecial; + const secAttack = me.barbarian ? (isSpecial ? 2 : 4) : 5; if (Config.AttackSkill[secAttack] > -1 && (!Attack.checkResist(target, Config.AttackSkill[isSpecial ? 1 : 3]) - || (me.paladin && Config.AttackSkill[isSpecial ? 1 : 3] === sdk.skills.BlessedHammer && !ClassAttack.getHammerPosition(target)))) { + || (me.paladin && Config.AttackSkill[isSpecial ? 1 : 3] === sdk.skills.BlessedHammer + && !ClassAttack.getHammerPosition(target)))) { skillCheck = Config.AttackSkill[secAttack]; } else { skillCheck = Config.AttackSkill[isSpecial ? 1 : 3]; @@ -450,7 +464,7 @@ Attack.clearPos = function (x, y, range = 15, pickit = true) { switch (skillCheck) { case sdk.skills.BlessedHammer: // Tele in random direction with Blessed Hammer - if (gidAttack[i].attacks > 0 && gidAttack[i].attacks % (isSpecial ? 4 : 2) === 0) { + if (_currMon.attacks > 0 && _currMon.attacks % (isSpecial ? 4 : 2) === 0) { let coord = CollMap.getRandCoordinate(me.x, -1, 1, me.y, -1, 1, 5); Pather.moveTo(coord.x, coord.y); } @@ -458,7 +472,8 @@ Attack.clearPos = function (x, y, range = 15, pickit = true) { break; default: // Flash with melee skills - if (gidAttack[i].attacks > 0 && gidAttack[i].attacks % (isSpecial ? 15 : 5) === 0 && Skill.getRange(skillCheck) < 4) { + if (_currMon.attacks > 0 && _currMon.attacks % (isSpecial ? 15 : 5) === 0 + && Skill.getRange(skillCheck) < 4) { Packet.flash(me.gid); } @@ -466,8 +481,8 @@ Attack.clearPos = function (x, y, range = 15, pickit = true) { } // Skip non-unique monsters after 15 attacks, except in Throne of Destruction - if (!me.inArea(sdk.areas.ThroneofDestruction) && !isSpecial && gidAttack[i].attacks > 15) { - console.warn("ÿc1Skipping " + target.name + " " + target.gid + " " + gidAttack[i].attacks); + if (!me.inArea(sdk.areas.ThroneofDestruction) && !isSpecial && _currMon.attacks > 15) { + console.warn("ÿc1Skipping " + target.name + " " + target.gid + " " + _currMon.attacks); monsterList.shift(); } @@ -491,8 +506,10 @@ Attack.clearPos = function (x, y, range = 15, pickit = true) { pickit && Attack.openChests(range, x, y); attackCount > 0 && pickit && Pickit.pickItems(); if ([x, y].distance > 5) { - Developer.debugging.pathing && console.debug("Returning to position " + x + "/" + y + " distance: " + [x, y].distance); - Pather.moveTo(x, y); + if (Developer.debugging.pathing) { + console.debug("Returning to position " + x + "/" + y + " distance: " + [x, y].distance); + } + Pather.move({ x: x, y: y }, { allowClearing: false }); } return true; @@ -777,7 +794,10 @@ Attack.clear = function (range, spectype, bossId, sortfunc, pickit = true) { clearResult = Attack.Result.SUCCESS; if (boss && boss.gid === target.gid) { killedBoss = true; - console.log("ÿc7Cleared ÿc0:: " + (!!target.name ? target.name : bossId) + "ÿc0 - ÿc7Duration: ÿc0" + Time.format(getTickCount() - tick)); + console.log( + "ÿc7Cleared ÿc0:: " + (!!target.name ? target.name : bossId) + + "ÿc0 - ÿc7Duration: ÿc0" + Time.format(getTickCount() - tick) + ); } attackCount -= _currMon.attacks; } @@ -1101,7 +1121,9 @@ new Overrides.Override(Attack, Attack.sortMonsters, function (orignal, unitA, un */ Attack.walkingSortMonsters = function (unitA, unitB) { // sort main bosses first - if ((unitA.isPrimeEvil) && (unitB.isPrimeEvil)) return getDistance(me, unitA) - getDistance(me, unitB); + if ((unitA.isPrimeEvil) && (unitB.isPrimeEvil)) { + return getDistance(me, unitA) - getDistance(me, unitB); + } if (unitA.isPrimeEvil) return -1; if (unitB.isPrimeEvil) return 1; @@ -1138,7 +1160,9 @@ Attack.walkingSortMonsters = function (unitA, unitB) { if (!me.inArea(sdk.areas.ClawViperTempleLvl2) && ids.includes(unitA.classid) && ids.includes(unitB.classid)) { // Kill "scary" uniques first (like Bishibosh) - if ((unitA.isUnique) && (unitB.isUnique)) return getDistance(me, unitA) - getDistance(me, unitB); + if ((unitA.isUnique) && (unitB.isUnique)) { + return getDistance(me, unitA) - getDistance(me, unitB); + } if (unitA.isUnique) return -1; if (unitB.isUnique) return 1; @@ -1148,12 +1172,16 @@ Attack.walkingSortMonsters = function (unitA, unitB) { if (ids.includes(unitA.classid)) return -1; if (ids.includes(unitB.classid)) return 1; - if ((unitA.isSuperUnique) && (unitB.isSuperUnique)) return getDistance(me, unitA) - getDistance(me, unitB); + if ((unitA.isSuperUnique) && (unitB.isSuperUnique)) { + return getDistance(me, unitA) - getDistance(me, unitB); + } if (unitA.isSuperUnique) return -1; if (unitB.isSuperUnique) return 1; // fallens are annoying, put them later if we have line of sight of another monster - if (unitA.isFallen && unitB.isFallen) return getDistance(me, unitA) - getDistance(me, unitB); + if (unitA.isFallen && unitB.isFallen) { + return getDistance(me, unitA) - getDistance(me, unitB); + } const _coll = (sdk.collision.BlockWall | sdk.collision.LineOfSight | sdk.collision.Ranged); if (!unitA.isFallen && !checkCollision(me, unitA, _coll)) return -1; if (!unitB.isFallen && !checkCollision(me, unitA, _coll)) return 1; @@ -1320,11 +1348,15 @@ Attack.pwnDia = function () { // noinspection JSBitwiseOperatorUsage return !(collision & (Coords_1.BlockBits.BlockWall)); }) - .sort((a, b) => getDistance(me, a) - getDistance(me, b)) + .sort(function (a, b) { + return getDistance(me, a) - getDistance(me, b); + }) .first(); tick = getTickCount(); if (spot !== undefined) { - shouldWalk(spot) ? Pather.walkTo(spot.x, spot.y) : Pather.moveTo(spot.x, spot.y, 15, false); + shouldWalk(spot) + ? Pather.walkTo(spot.x, spot.y) + : Pather.move(spot, { allowClearing: false, retry: 15 }); } } @@ -1438,7 +1470,9 @@ Attack.deploy = function (unit, distance = 10, spread = 5, range = 9) { } } - return typeof index === "number" ? Pather.moveTo(grid[index].x, grid[index].y, 0) : false; + return typeof index === "number" + ? Pather.move(grid[index], { allowClearing: false }) + : false; }; /** @@ -1449,6 +1483,7 @@ Attack.deploy = function (unit, distance = 10, spread = 5, range = 9) { * @param {boolean} walk * @param {boolean} force * @returns {boolean} + * @todo maybe recursion check? */ Attack.getIntoPosition = function (unit = false, distance = 0, coll = 0, walk = false, force = false) { if (!unit || !unit.x || !unit.y) return false; @@ -1460,6 +1495,7 @@ Attack.getIntoPosition = function (unit = false, distance = 0, coll = 0, walk = const caster = (force || (distance > 4 && !me.inTown && Skill.getRange(Config.AttackSkill[1]) > 8)); const minMonCount = caster && distance < 8 ? 1 : 0; const _coll = (sdk.collision.WallOrRanged | sdk.collision.Objects | sdk.collision.IsOnFloor); + const _pathSettings = { clearSettings: { allowClearing: !useTele, range: useTele ? 10 : 5, retry: 5 } }; walk === true && (walk = 1); @@ -1478,10 +1514,7 @@ Attack.getIntoPosition = function (unit = false, distance = 0, coll = 0, walk = return true; } else { // don't clear while trying to reposition - Pather.moveToEx( - unit.x, unit.y, - { clearSettings: { allowClearing: !useTele, range: useTele ? 10 : 5 } } - ); + Pather.move(unit, _pathSettings); } return !CollMap.checkColl(me, unit, coll); @@ -1525,27 +1558,27 @@ Attack.getIntoPosition = function (unit = false, distance = 0, coll = 0, walk = } } - for (let i = 0; i < coords.length; i++) { + for (let coord of coords) { // Valid position found - no collision between the spot and the unit - if (!CollMap.checkColl({ x: coords[i].x, y: coords[i].y }, unit, coll, 1)) { + if (!CollMap.checkColl({ x: coord.x, y: coord.y }, unit, coll, 1)) { Developer.debugging.pathing && console.time("countMobs"); const currCount = nearMobs .filter(function (m) { - return getDistance(coords[i].x, coords[i].y, m.x, m.y) < 8; + return getDistance(coord.x, coord.y, m.x, m.y) < 8; }).length; Developer.debugging.pathing && console.timeEnd("countMobs"); // this might be a valid spot but also check the mob count at that node if (caster) { - potentialSpot.x === null && (potentialSpot = { x: coords[i].x, y: coords[i].y }); + potentialSpot.x === null && (potentialSpot = { x: coord.x, y: coord.y }); if (currCount < count) { count = currCount; - potentialSpot = { x: coords[i].x, y: coords[i].y }; + potentialSpot = { x: coord.x, y: coord.y }; if (Developer.debugging.pathing) { console.log( - sdk.colors.Blue + "CheckedSpot" + sdk.colors.Yellow + ": x: " + coords[i].x - + " y: " + coords[i].y + " mob amount: " + sdk.colors.NeonGreen + count + sdk.colors.Blue + "CheckedSpot" + sdk.colors.Yellow + ": x: " + coord.x + + " y: " + coord.y + " mob amount: " + sdk.colors.NeonGreen + count ); } } @@ -1562,21 +1595,18 @@ Attack.getIntoPosition = function (unit = false, distance = 0, coll = 0, walk = } // I am already in my optimal position - if (coords[i].distance < 3) return true; + if (coord.distance < 3) return true; // we are actually able to walk to where we want to go, hopefully prevent wall hugging - if (walk && (coords[i].distance < 6 || !CollMap.checkColl(me, unit, _coll))) { - Pather.walkTo(coords[i].x, coords[i].y, 2); + if (walk && (coord.distance < 6 || !CollMap.checkColl(me, unit, _coll))) { + Pather.walkTo(coord.x, coord.y, 2); } else { - Pather.moveToEx( - coords[i].x, coords[i].y, - { clearSettings: { allowClearing: !useTele, range: useTele ? 10 : 5, retry: 3 } } - ); + Pather.move(coord, _pathSettings); } if (Developer.debugging.pathing) { console.log( sdk.colors.Purple + "SecondCheck :: " + sdk.colors.Yellow - + "Moving to: x: " + coords[i].x + " y: " + coords[i].y + + "Moving to: x: " + coord.x + " y: " + coord.y + " mob amount: " + sdk.colors.NeonGreen + currCount ); console.timeEnd("getIntoPosition"); @@ -1600,7 +1630,8 @@ Attack.getIntoPosition = function (unit = false, distance = 0, coll = 0, walk = if (potentialSpot.distance < 6 && !CollMap.checkColl(me, potentialSpot, sdk.collision.WallOrRanged)) { return Pather.walkTo(potentialSpot.x, potentialSpot.y, 2); } - return Pather.moveTo(potentialSpot.x, potentialSpot.y, 1); + // return Pather.moveTo(potentialSpot.x, potentialSpot.y, 1); + return Pather.move(potentialSpot, _pathSettings); } })()) { if (Developer.debugging.pathing) { From d03116050af55e44436af6e0d67afced9fde2402 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Thu, 8 Jun 2023 18:43:40 -0400 Subject: [PATCH 149/263] Update Me.js - formatting --- libs/SoloPlay/Functions/Me.js | 32 +++++++++++++++++++------------- 1 file changed, 19 insertions(+), 13 deletions(-) diff --git a/libs/SoloPlay/Functions/Me.js b/libs/SoloPlay/Functions/Me.js index 139b0049..ceaf0415 100644 --- a/libs/SoloPlay/Functions/Me.js +++ b/libs/SoloPlay/Functions/Me.js @@ -240,7 +240,7 @@ if (!me.hasOwnProperty("data")) { if (!me.hasOwnProperty("equipped")) { me.equipped = (function () { - const defaultsMap = new Map([ + const _defaultsMap = new Map([ ["gid", -1], ["classid", -1], ["name", ""], @@ -266,8 +266,14 @@ if (!me.hasOwnProperty("equipped")) { this._item = (item instanceof Unit) ? copyUnit(item) : null; - this._gid = item ? item.gid : -1; - this._bodylocation = item ? item.bodylocation : -1; + /** @type {number} */ + this._gid = item + ? item.gid + : -1; + /** @type {number} */ + this._bodylocation = item + ? item.bodylocation + : -1; return new Proxy(this, { get: function (target, prop) { @@ -279,7 +285,7 @@ if (!me.hasOwnProperty("equipped")) { return target._item[prop]; } - return defaultsMap.get(prop); + return _defaultsMap.get(prop); } }); } @@ -340,7 +346,7 @@ if (!me.hasOwnProperty("equipped")) { }; /** @type {Map} */ - const bodyMap = new Map([ + const _bodyMap = new Map([ [sdk.body.Head, new EquippedItem()], [sdk.body.Neck, new EquippedItem()], [sdk.body.Armor, new EquippedItem()], @@ -363,8 +369,8 @@ if (!me.hasOwnProperty("equipped")) { * @returns {EquippedItem} */ get: function (bodylocation) { - // if (!bodyMap.has(bodylocation)) return _dummy; - let item = bodyMap.get(bodylocation); + // if (!_bodyMap.has(bodylocation)) return _dummy; + let item = _bodyMap.get(bodylocation); if (item._gid === -1 || (item._gid !== item.gid || (item._bodylocation !== item.location && me.weaponswitch !== sdk.player.slot.Secondary))) { @@ -374,24 +380,24 @@ if (!me.hasOwnProperty("equipped")) { return el.isEquipped && el.bodylocation === bodylocation; }) .first(); - bodyMap.set(bodylocation, new EquippedItem(newItem)); + _bodyMap.set(bodylocation, new EquippedItem(newItem)); } - return bodyMap.get(bodylocation); + return _bodyMap.get(bodylocation); }, /** * @param {number} bodylocation * @returns {boolean} */ has: function (bodylocation) { - return bodyMap.has(bodylocation); + return _bodyMap.has(bodylocation); }, /** * @param {number} bodylocation * @param {ItemUnit} item */ set: function (bodylocation, item) { - if (bodyMap.has(bodylocation) && item instanceof Unit) { - bodyMap.set(bodylocation, new EquippedItem(item)); + if (_bodyMap.has(bodylocation) && item instanceof Unit) { + _bodyMap.set(bodylocation, new EquippedItem(item)); } }, /** @@ -403,7 +409,7 @@ if (!me.hasOwnProperty("equipped")) { return item.isEquipped; }) .forEach(function (item) { - bodyMap.set(item.bodylocation, new EquippedItem(item)); + _bodyMap.set(item.bodylocation, new EquippedItem(item)); }); }, }; From 26e851e511e7f306fd130ca349443bf132162725 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Thu, 8 Jun 2023 18:44:30 -0400 Subject: [PATCH 150/263] Update MiscOverrides.js - add use of ShrineData module, and new areadata module. Working to improve shrine hunting --- libs/SoloPlay/Functions/MiscOverrides.js | 291 ++++++++++++++--------- 1 file changed, 184 insertions(+), 107 deletions(-) diff --git a/libs/SoloPlay/Functions/MiscOverrides.js b/libs/SoloPlay/Functions/MiscOverrides.js index 262fce4f..1450c377 100644 --- a/libs/SoloPlay/Functions/MiscOverrides.js +++ b/libs/SoloPlay/Functions/MiscOverrides.js @@ -7,46 +7,38 @@ */ includeIfNotIncluded("core/Misc.js"); +const ShrineData = require("../../core/GameData/ShrineData"); Misc.openChestsEnabled = true; Misc.screenshotErrors = true; -Misc.presetChestIds = [ - 5, 6, 87, 104, 105, 106, 107, 143, 140, 141, 144, 146, 147, 148, 176, 177, 181, 183, 198, 240, 241, - 242, 243, 329, 330, 331, 332, 333, 334, 335, 336, 354, 355, 356, 371, 387, 389, 390, 391, 397, 405, - 406, 407, 413, 420, 424, 425, 430, 431, 432, 433, 454, 455, 501, 502, 504, 505, 580, 581 -]; /** * @override + * @template T * @param {number} area * @param {number[]} chestIds - * @param {Function} [sort] + * @param {function(T, T): number} [sort] * @returns {boolean} */ Misc.openChestsInArea = function (area, chestIds = [], sort = undefined) { !area && (area = me.area); + typeof sort !== "function" && (sort = Sort.units); area !== me.area && Pather.journeyTo(area); + !chestIds.length && (chestIds = sdk.objects.chestIds.slice(0)); - let presetUnits = Game.getPresetObjects(area); - if (!presetUnits) return false; - - !chestIds.length && (chestIds = Misc.presetChestIds.slice(0)); - - let coords = []; - - while (presetUnits.length > 0) { - if (chestIds.includes(presetUnits[0].id)) { - coords.push({ - x: presetUnits[0].roomx * 5 + presetUnits[0].x, - y: presetUnits[0].roomy * 5 + presetUnits[0].y - }); - } + const presetUnits = Game.getPresetObjects(area) + .filter(function (preset) { + return chestIds.includes(preset.id); + }); + if (!presetUnits.length) return false; - presetUnits.shift(); - } + let coords = presetUnits + .map(function (preset) { + return preset.realCoords(); + }); while (coords.length) { - coords.sort(sort ? sort : Sort.units); + coords.sort(sort); Pather.moveToUnit(coords[0], 1, 2); this.openChests(20); @@ -133,7 +125,12 @@ Misc.openChests = function (range = 15) { me.baal && containers.push("evilurn"); let unitList = getUnits(sdk.unittype.Object) - .filter(c => c.name && c.mode === sdk.objects.mode.Inactive && c.distance <= range && containers.includes(c.name.toLowerCase())); + .filter(function (c) { + return c.name + && c.mode === sdk.objects.mode.Inactive + && c.distance <= range + && containers.includes(c.name.toLowerCase()); + }); while (unitList.length > 0) { unitList.sort(Sort.units); @@ -156,7 +153,15 @@ Misc.openChests = function (range = 15) { // that needs a handler as well though, if we aren't clearing and are just pathing (tele char) opening a chest and moving on is fine } - if (unit && (Pather.useTeleport() || !checkCollision(me, unit, sdk.collision.WallOrRanged)) && this.openChest(unit)) { + /** + * @todo + * - evaluate actual walking distance to chest, as if it's far out of the way it maybe be better to skip it + * especially early on when we are trying to get to the next area + */ + + if (unit + && (Pather.useTeleport() || !checkCollision(me, unit, sdk.collision.WallOrRanged)) + && this.openChest(unit)) { Pickit.pickItems(); } } @@ -174,7 +179,9 @@ Misc.getWell = function (unit) { for (let i = 0; i < 3; i++) { if (Skill.useTK(unit) && i < 2) { unit.distance > 21 && Pather.moveNearUnit(unit, 20); - checkCollision(me, unit, sdk.collision.Ranged) && Attack.getIntoPosition(unit, 20, sdk.collision.Ranged); + if (checkCollision(me, unit, sdk.collision.Ranged)) { + Attack.getIntoPosition(unit, 20, sdk.collision.Ranged); + } Packet.telekinesis(unit); } else { if (unit.distance < 4 || Pather.moveToUnit(unit, 3, 0)) { @@ -192,7 +199,12 @@ Misc.getWell = function (unit) { Misc.useWell = function (range = 15) { // I'm in perfect health, don't need this shit if (me.hpPercent >= 95 && me.mpPercent >= 95 && me.staminaPercent >= 50 - && [sdk.states.Frozen, sdk.states.Poison, sdk.states.AmplifyDamage, sdk.states.Decrepify].every((states) => !me.getState(states))) { + && [ + sdk.states.Frozen, sdk.states.Poison, + sdk.states.AmplifyDamage, sdk.states.Decrepify + ].every(function (states) { + return !me.getState(states); + })) { return true; } @@ -214,30 +226,44 @@ Misc.useWell = function (range = 15) { return true; }; -const shrineMap = new Map(); -shrineMap.set(sdk.shrines.Refilling, 0); -shrineMap.set(sdk.shrines.Health, 0); -shrineMap.set(sdk.shrines.Mana, 0); -shrineMap.set(sdk.shrines.HealthExchange, 0); -shrineMap.set(sdk.shrines.ManaExchange, 0); -shrineMap.set(sdk.shrines.Armor, sdk.states.ShrineArmor); -shrineMap.set(sdk.shrines.Combat, sdk.states.ShrineCombat); -shrineMap.set(sdk.shrines.ResistFire, sdk.states.ShrineResFire); -shrineMap.set(sdk.shrines.ResistCold, sdk.states.ShrineResCold); -shrineMap.set(sdk.shrines.ResistLightning, sdk.states.ShrineResLighting); -shrineMap.set(sdk.shrines.ResistPoison, sdk.states.ShrineResPoison); -shrineMap.set(sdk.shrines.Skill, sdk.states.ShrineSkill); -shrineMap.set(sdk.shrines.ManaRecharge, sdk.states.ShrineManaRegen); -shrineMap.set(sdk.shrines.Stamina, sdk.states.ShrineStamina); -shrineMap.set(sdk.shrines.Experience, sdk.states.ShrineExperience); -shrineMap.set(sdk.shrines.Enirhs, 0); -shrineMap.set(sdk.shrines.Portal, 0); -shrineMap.set(sdk.shrines.Gem, 0); -shrineMap.set(sdk.shrines.Fire, 0); -shrineMap.set(sdk.shrines.Monster, 0); -shrineMap.set(sdk.shrines.Exploding, 0); -shrineMap.set(sdk.shrines.Poison, 0); +/** + * Use a shrine Unit + * @param {ObjectUnit} unit + * @returns {boolean} + */ +Misc.getShrine = function (unit) { + if (unit.mode === sdk.objects.mode.Active) return false; + AreaData.get(me.area).addShrine(unit); + + for (let i = 0; i < 3; i++) { + if (Skill.useTK(unit) && i < 2) { + unit.distance > 21 && Pather.moveNearUnit(unit, 20); + if (!Packet.telekinesis(unit)) { + Attack.getIntoPosition(unit, 20, sdk.collision.WallOrRanged); + } + } else { + if (getDistance(me, unit) < 4 || Pather.moveToUnit(unit, 3, 0)) { + Misc.click(0, 0, unit); + } + } + + if (Misc.poll(() => unit.mode, 1000, 40)) { + AreaData.get(me.area).updateShrine(unit); + if (unit.objtype === sdk.shrines.Gem) { + Pickit.pickItems(); + } + return true; + } + } + + return false; +}; +/** + * @param {number} range + * @param {number[]} ignore + * @returns {boolean} + */ Misc.scanShrines = function (range, ignore = []) { if (!Config.ScanShrines.length) return false; @@ -247,7 +273,7 @@ Misc.scanShrines = function (range, ignore = []) { /** @type {ObjectUnit[]} */ let shrineList = []; - const rangeCheck = (shrineType) => { + const rangeCheck = function (shrineType) { switch (true) { case shrineType === sdk.shrines.Refilling && (me.hpPercent < 50 || me.mpPercent < 50 || me.staminaPercent < 50): case shrineType === sdk.shrines.Mana && me.mpPercent < 50: @@ -267,12 +293,12 @@ Misc.scanShrines = function (range, ignore = []) { } // Initiate shrine states - if (!this.shrineStates) { - this.shrineStates = []; + if (!Misc.shrineStates) { + Misc.shrineStates = []; let i = 0; for (let shrine of Config.ScanShrines) { if (shrine > 0) { - this.shrineStates[i] = shrineMap.get(shrine); + Misc.shrineStates[i] = ShrineData.getState(shrine); i++; } } @@ -292,7 +318,7 @@ Misc.scanShrines = function (range, ignore = []) { // Build a list of nearby shrines do { - if (shrine.name.toLowerCase().includes("shrine") && shrineMap.has(shrine.objtype) + if (shrine.name.toLowerCase().includes("shrine") && ShrineData.has(shrine.objtype) && shrine.mode === sdk.objects.mode.Inactive && !ignore.includes(shrine.objtype) && getDistance(me.x, me.y, shrine.x, shrine.y) <= rangeCheck(shrine.objtype)) { shrineList.push(copyUnit(shrine)); @@ -334,8 +360,6 @@ Misc.scanShrines = function (range, ignore = []) { return true; }; -Misc.presetShrineIds = [2, 81, 83]; - /** * Check all shrines in area and get the first one of specified type * @param {number} area @@ -346,29 +370,38 @@ Misc.presetShrineIds = [2, 81, 83]; * of getUnit and can see the shrine type so we know whether to continue moving to it or not. */ Misc.getShrinesInArea = function (area, type, use) { + if (!area || !AreaData.has(area)) return false; let shrineLocs = []; let result = false; - let unit = Game.getPresetObjects(area); + let units = Game.getPresetObjects(area) + .filter(function (preset) { + return sdk.shrines.Presets.includes(preset.id); + }); - if (unit) { - for (let i = 0; i < unit.length; i += 1) { - if (Misc.presetShrineIds.includes(unit[i].id)) { - Misc.presetShrineIds.push([unit[i].roomx * 5 + unit[i].x, unit[i].roomy * 5 + unit[i].y]); - } + if (units.length) { + for (let shrine of units) { + shrineLocs.push(shrine.realCoords()); } + } else if (AreaData.get(area).getShrines().length) { + shrineLocs = AreaData.get(area) + .getShrines() + .filter(function (shrine) { + return shrine.useable(); + }); + } else { + return false; } try { NodeAction.shrinesToIgnore.push(type); while (shrineLocs.length > 0) { - shrineLocs.sort(Sort.points); + shrineLocs.sort(Sort.units); let coords = shrineLocs.shift(); - Pather.moveToEx(coords[0], coords[1], { minDist: Skill.haveTK ? 20 : 5, callback: () => { + Pather.move(coords, { minDist: Skill.haveTK ? 20 : 5, callback: function () { let shrine = Game.getObject("shrine"); - // for now until I write a proper isShrineWanted check, get close enough that nodeaction checks it - return !!shrine && shrine.x === coords[0] && shrine.y === coords[1] && shrine.distance <= 15; + return !!shrine && shrine.x === coords.x && shrine.y === coords.y; } }); let shrine = Game.getObject("shrine"); @@ -387,7 +420,9 @@ Misc.getShrinesInArea = function (area, type, use) { return true; } - if (use && type >= sdk.shrines.Armor && type <= sdk.shrines.Experience && me.getState(type + 122)) { + if (use && type >= sdk.shrines.Armor + && type <= sdk.shrines.Experience + && me.getState(type + 122)) { return true; } } @@ -404,25 +439,46 @@ Misc.getShrinesInArea = function (area, type, use) { Misc.getExpShrine = function (shrineLocs = []) { if (me.getState(sdk.states.ShrineExperience)) return true; - for (let get = 0; get < shrineLocs.length; get++) { + for (let area of shrineLocs) { me.overhead("Looking for xp shrine"); - if (shrineLocs[get] === sdk.areas.BloodMoor) { - Pather.journeyTo(shrineLocs[get]); + if (area === sdk.areas.BloodMoor) { + Pather.journeyTo(area); } else { - Pather.checkWP(shrineLocs[get], true) ? Pather.useWaypoint(shrineLocs[get]) : Pather.getWP(shrineLocs[get]); + Pather.checkWP(area, true) + ? Pather.useWaypoint(area) + : Pather.getWP(area); } Precast.doPrecast(true); - Misc.getShrinesInArea(shrineLocs[get], sdk.shrines.Experience, true); + Misc.getShrinesInArea(area, sdk.shrines.Experience, true); if (me.getState(sdk.states.ShrineExperience)) { - break; + return true; } !me.inTown && Town.goToTown(); } + // this needs work but idea is we can leverage the shrine data gathered during regular script actions + // to find the closest xp shrine to us and go to it without having to search a bunch of different areas + // let _xpShrineAreas = AreaData.getAreasWithShrine(sdk.shrines.Experience); + // if (_xpShrineAreas.length) { + // for (let area of _xpShrineAreas) { + // me.overhead("Looking for xp shrine"); + // Pather.journeyTo(area.Index); + // let _shrine = area.Shrines.find(function (shrine) { + // return shrine.Type === sdk.shrines.Experience; + // }); + // Pather.move(_shrine, { minDist: Skill.haveTK ? 20 : 5, callback: function () { + // let shrine = Game.getObject(-1, sdk.objects.mode.Inactive, _shrine.gid); + // return !!shrine && shrine.x === _shrine.x && shrine.y === _shrine.y; + // } }); + // if (Misc.getShrine(Game.getObject(-1, sdk.objects.mode.Inactive, _shrine.gid))) { + // return true; + // } + // } + // } return true; }; @@ -457,7 +513,7 @@ Misc.unsocketItem = function (item) { // probably only happens on server crash if (!Cubing.openCube()) throw "Failed to open cube"; - myPrint("ÿc4Removing sockets from: ÿc0" + item.fname.split("\n").reverse().join(" ").replace(/ÿc[0-9!"+<;.*]/, "")); + myPrint("ÿc4Removing sockets from: ÿc0" + item.prettyPrint); transmute(); delay(500); // unsocketing an item causes loss of reference, so re-find our item @@ -492,10 +548,10 @@ Misc.checkItemsForSocketing = function () { return NTIP.GetTier(b) - NTIP.GetTier(a); }); - for (let i = 0; i < items.length; i++) { - let curr = Config.socketables.find(({ classid }) => items[i].classid === classid); - if (curr && curr.condition(items[i]) && curr.useSocketQuest) { - return items[i]; + for (let item of items) { + let curr = Config.socketables.find(({ classid }) => item.classid === classid); + if (curr && curr.condition(item) && curr.useSocketQuest) { + return item; } } @@ -505,11 +561,15 @@ Misc.checkItemsForSocketing = function () { Misc.checkItemsForImbueing = function () { if (!me.getQuest(sdk.quest.id.ToolsoftheTrade, sdk.quest.states.ReqComplete)) return false; - let items = me.getItemsEx().filter(item => item.sockets === 0 && (item.normal || item.superior)); + let items = me.getItemsEx() + .filter(function (item) { + return item.sockets === 0 && (item.normal || item.superior); + }); - for (let i = 0; i < items.length; i++) { - if (Config.imbueables.some(item => item.name === items[i].classid && Item.canEquip(items[i]))) { - return items[i]; + for (let item of items) { + if (Config.imbueables + .some(imbueable => imbueable.name === item.classid && Item.canEquip(item))) { + return item; } } @@ -544,12 +604,16 @@ Misc.addSocketablesToItem = function (item, runes = []) { if (!Town.openStash()) return false; - for (let i = 0; i < runes.length; i++) { - let rune = runes[i]; + for (let rune of runes) { if (!rune.toCursor()) return false; for (let i = 0; i < 3; i += 1) { - sendPacket(1, sdk.packets.send.InsertSocketItem, 4, rune.gid, 4, item.gid); + new PacketBuilder() + .byte(sdk.packets.send.InsertSocketItem) + .dword(rune.gid) + .dword(item.gid) + .send(); + let tick = getTickCount(); while (getTickCount() - tick < 2000) { @@ -593,7 +657,10 @@ Misc.getSocketables = function (item, itemInfo) { let sockets = item.sockets; let openSockets = sockets - preSockets; let { classid, quality } = item; - let socketables = me.getItemsEx().filter(item => item.isInsertable); + let socketables = me.getItemsEx() + .filter(function (item) { + return item.isInsertable; + }); if (!socketables || (!allowTemp && openSockets === 0)) return false; @@ -603,17 +670,22 @@ Misc.getSocketables = function (item, itemInfo) { // filter out all items that aren't the gem type we are looking for // then sort the highest classid (better gems first) let myItems = me.getItemsEx() - .filter(item => item.itemType === gem.itemType) - .sort((a, b) => b.classid - a.classid); + .filter(function (item) { + return item.itemType === gem.itemType; + }) + .sort(function (a, b) { + return b.classid - a.classid; + }); - for (let i = 0; i < myItems.length; i++) { - if (!checkList.includes(myItems[i])) return true; + for (let item of myItems) { + if (!checkList.includes(item)) return true; } return false; } - if (!itemInfo.hasOwnProperty("socketWith") || (itemInfo.hasOwnProperty("socketWith") && itemInfo.socketWith.length === 0)) { + if (!itemInfo.hasOwnProperty("socketWith") + || (itemInfo.hasOwnProperty("socketWith") && itemInfo.socketWith.length === 0)) { itemtype = item.getItemType(); if (!itemtype) return false; gemType = ["Helmet", "Armor"].includes(itemtype) @@ -718,45 +790,50 @@ Misc.getSocketables = function (item, itemInfo) { Misc.checkSocketables = function () { let items = me.getItemsEx() - .filter(item => item.sockets > 0 && AutoEquip.hasTier(item) - && (item.quality >= sdk.items.quality.Magic || ((item.normal || item.superior) && item.isEquipped))) - .sort((a, b) => NTIP.GetTier(b) - NTIP.GetTier(a)); + .filter(function (item) { + return item.sockets > 0 && AutoEquip.hasTier(item) + && (item.quality >= sdk.items.quality.Magic + || ((item.normal || item.superior) && item.isEquipped)); + }) + .sort(function (a, b) { + return NTIP.GetTier(b) - NTIP.GetTier(a); + }); if (!items) return; - for (let i = 0; i < items.length; i++) { - let sockets = items[i].sockets; + for (let item of items) { + let sockets = item.sockets; - switch (items[i].quality) { + switch (item.quality) { case sdk.items.quality.Normal: case sdk.items.quality.Superior: case sdk.items.quality.Magic: case sdk.items.quality.Rare: case sdk.items.quality.Crafted: // no need to check anything else if already socketed - if (items[i].getItemsEx().length === sockets) { + if (item.getItemsEx().length === sockets) { continue; } // Any magic, rare, or crafted item with open sockets - if (items[i].isEquipped && [sdk.body.Head, sdk.body.Armor, sdk.body.RightArm, sdk.body.LeftArm].includes(items[i].bodylocation)) { - Misc.getSocketables(items[i]); + if (item.isEquipped && [sdk.body.Head, sdk.body.Armor, sdk.body.RightArm, sdk.body.LeftArm].includes(item.bodylocation)) { + Misc.getSocketables(item); } break; case sdk.items.quality.Set: case sdk.items.quality.Unique: { - let curr = Config.socketables.find(({ classid }) => items[i].classid === classid); + let curr = Config.socketables.find(({ classid }) => item.classid === classid); // item is already socketed and we don't use temp socketables on this item - if ((!curr || (curr && !curr.temp)) && items[i].getItemsEx().length === sockets) { + if ((!curr || (curr && !curr.temp)) && item.getItemsEx().length === sockets) { continue; } - if (curr && curr.condition(items[i])) { - Misc.getSocketables(items[i], curr); - } else if (items[i].isEquipped) { - Misc.getSocketables(items[i]); + if (curr && curr.condition(item)) { + Misc.getSocketables(item, curr); + } else if (item.isEquipped) { + Misc.getSocketables(item); } } From fe26e50e54d9181e2a55f316bde7f336d4da6a22 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Thu, 8 Jun 2023 18:45:11 -0400 Subject: [PATCH 151/263] Update NecromancerAttacks.js - better collision checking --- .../NecromancerAttacks.js | 24 +++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/libs/SoloPlay/Functions/ClassAttackOverrides/NecromancerAttacks.js b/libs/SoloPlay/Functions/ClassAttackOverrides/NecromancerAttacks.js index 275f0f70..40f8c964 100644 --- a/libs/SoloPlay/Functions/ClassAttackOverrides/NecromancerAttacks.js +++ b/libs/SoloPlay/Functions/ClassAttackOverrides/NecromancerAttacks.js @@ -138,6 +138,8 @@ includeIfNotIncluded("core/Attacks/Necromancer.js"); /** * @todo * - bonemancer specific check for using bonespear vs bone spirit + * - classdata structure for attacks, necro is fairly simple as there are + * only 5 attack skills, maybe take into account fireball OSkill from trangs */ // TODO: clean this up @@ -365,24 +367,42 @@ includeIfNotIncluded("core/Attacks/Necromancer.js"); delay(300); + break; + case sdk.skills.Attack: + case sdk.skills.PoisonDagger: + if (!Attack.validSpot(unit.x, unit.y)) { + return Attack.Result.FAILED; + } + + if (unit.distance > Skill.getRange(timedSkill) || checkCollision(me, unit, sdk.collision.WallOrRanged)) { + if (!Attack.getIntoPosition(unit, Skill.getRange(timedSkill), sdk.collision.WallOrRanged, true)) { + return Attack.Result.FAILED; + } + } + + if (unit.attackable) { + Skill.cast(timedSkill, Skill.getHand(timedSkill), unit); + } + break; default: if (timedSkillRange < 4 && !Attack.validSpot(unit.x, unit.y)) { return Attack.Result.FAILED; } + if (timedSkill === sdk.skills.Teeth) { let _coll = (sdk.collision.BlockMissile | sdk.collision.BlockWall | sdk.collision.Casting); timedSkillRange = me.getMobCount(6, _coll) <= 3 ? 8 : timedSkillRange; } - if (unit.distance > timedSkillRange || checkCollision(me, unit, sdk.collision.Ranged)) { + if (unit.distance > timedSkillRange || checkCollision(me, unit, sdk.collision.BlockMissile)) { // Allow short-distance walking for melee skills walk = ( timedSkillRange < 4 && unit.distance < 10 && !checkCollision(me, unit, sdk.collision.BlockWall) ); - if (!Attack.getIntoPosition(unit, timedSkillRange, sdk.collision.Ranged, walk)) { + if (!Attack.getIntoPosition(unit, timedSkillRange, sdk.collision.BlockMissile, walk)) { return Attack.Result.FAILED; } } From c44e89aaca509e1e802a4666f1c9e5903c41ab0b Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Thu, 8 Jun 2023 18:52:56 -0400 Subject: [PATCH 152/263] script updates - get cata2 wp during boneash if we don't have it. During normal we usually hit level 12 which is what we want for andy while getting the wp - move to cata 4 and then check if we have reached level 12 yet. If we didn't hit it while getting the wp, this usually does it and speeds up the 1-12 push by not having us make a new game - add creepingfeature script, hit it before getting the amulet - check were we are while attempting to move to the amulet chest, sometimes repositioning to attack gets us there --- libs/SoloPlay/Scripts/amulet.js | 13 +++--- libs/SoloPlay/Scripts/andariel.js | 9 +++- libs/SoloPlay/Scripts/beetleburst.js | 55 +++++++++++++++++++++++- libs/SoloPlay/Scripts/boneash.js | 11 ++++- libs/SoloPlay/Scripts/creepingfeature.js | 18 ++++++++ libs/SoloPlay/Scripts/den.js | 47 ++++++++++++++------ libs/SoloPlay/Tools/SoloIndex.js | 20 +++++++-- 7 files changed, 148 insertions(+), 25 deletions(-) create mode 100644 libs/SoloPlay/Scripts/creepingfeature.js diff --git a/libs/SoloPlay/Scripts/amulet.js b/libs/SoloPlay/Scripts/amulet.js index faa990fe..cea7fda0 100644 --- a/libs/SoloPlay/Scripts/amulet.js +++ b/libs/SoloPlay/Scripts/amulet.js @@ -9,7 +9,9 @@ function amulet () { Town.doChores(false, { fullChores: true }); myPrint("starting amulet"); - Pather.checkWP(sdk.areas.LostCity, true) ? Pather.useWaypoint(sdk.areas.LostCity) : Pather.getWP(sdk.areas.LostCity); + Pather.checkWP(sdk.areas.LostCity, true) + ? Pather.useWaypoint(sdk.areas.LostCity) + : Pather.getWP(sdk.areas.LostCity); Precast.doPrecast(true); Pather.moveToExit([sdk.areas.ValleyofSnakes, sdk.areas.ClawViperTempleLvl1, sdk.areas.ClawViperTempleLvl2], true); Precast.doPrecast(true); @@ -17,10 +19,11 @@ function amulet () { if (!Pather.useTeleport()) { // change this to be array loop, sometimes bot gets lucky to have a clearish path to the chest but // then because Attack.clear on nodeaction we move from the chest even though we were there and the vipers can't get to the altar - Pather.moveTo(15065, 14047); - Pather.moveTo(15063, 14066); - Pather.moveTo(15051, 14066); - Pather.moveTo(15045, 14051); + [[15065, 14047], [15063, 14066], [15051, 14066], [15045, 14051]] + .some(function (pos) { + Pather.moveTo(pos[0], pos[1]); + return [15045, 14051].distance < 5 || me.getItem(sdk.items.quest.ViperAmulet); + }); } else { Pather.moveTo(15045, 14051, null, false); } diff --git a/libs/SoloPlay/Scripts/andariel.js b/libs/SoloPlay/Scripts/andariel.js index 044b4d9b..774fc41c 100644 --- a/libs/SoloPlay/Scripts/andariel.js +++ b/libs/SoloPlay/Scripts/andariel.js @@ -18,10 +18,17 @@ function andariel () { let questBug = (!me.normal && !me.andariel); - Pather.checkWP(sdk.areas.CatacombsLvl2, true) ? Pather.useWaypoint(sdk.areas.CatacombsLvl2) : Pather.getWP(sdk.areas.CatacombsLvl2); + Pather.checkWP(sdk.areas.CatacombsLvl2, true) + ? Pather.useWaypoint(sdk.areas.CatacombsLvl2) + : Pather.getWP(sdk.areas.CatacombsLvl2); Precast.doPrecast(true); Pather.moveToExit([sdk.areas.CatacombsLvl3, sdk.areas.CatacombsLvl4], true); + if (me.charlvl < 12) { + myPrint("Still to early, NG"); + return true; + } + if (me.poisonRes < 75) { Town.doChores(true, { thawing: me.coldRes < 75, antidote: true }); Pather.usePortal(sdk.areas.CatacombsLvl4); diff --git a/libs/SoloPlay/Scripts/beetleburst.js b/libs/SoloPlay/Scripts/beetleburst.js index 8c7e93c7..877e84cb 100644 --- a/libs/SoloPlay/Scripts/beetleburst.js +++ b/libs/SoloPlay/Scripts/beetleburst.js @@ -1,18 +1,69 @@ /** * @filename beetleburst.js -* @author isid0re +* @author isid0re, theBGuy * @desc kill beetleburst for exp * */ function beetleburst () { + // const clearBeetles = function () { + // let room = getRoom(); + // if (!room) return false; + + // const rooms = []; + // /** @param {Monster} monster */ + // const check = function (monster) { + // return [ + // sdk.monsters.DungSoldier, + // sdk.monsters.DeathBeetle, + // sdk.monsters.BlackVultureNest + // ].includes(monster.classid) && monster.distance <= 30; + // }; + + // do { + // rooms.push([room.x * 5 + room.xsize / 2, room.y * 5 + room.ysize / 2]); + // } while (room.getNext()); + + // while (rooms.length > 0) { + // rooms.sort(Sort.points); + // room = rooms.shift(); + + // let result = Pather.getNearestWalkable(room[0], room[1], 15, 2); + + // if (result) { + // Pather.moveTo(result[0], result[1], 3); + + // let monList = Attack.buildMonsterList(check); + // if (!monList.length) continue; + + // if (!Attack.clearList(monList)) { + // return false; + // } + // } + // } + + // return true; + // }; + Town.doChores(); myPrint("ÿc8Kolbot-SoloPlayÿc0: starting beetleburst"); - Pather.checkWP(sdk.areas.FarOasis, true) ? Pather.useWaypoint(sdk.areas.FarOasis) : Pather.getWP(sdk.areas.FarOasis); + Pather.checkWP(sdk.areas.FarOasis, true) + ? Pather.useWaypoint(sdk.areas.FarOasis) + : Pather.getWP(sdk.areas.FarOasis); Precast.doPrecast(true); + + // clearBeetles(); Pather.moveToPreset(me.area, sdk.unittype.Monster, sdk.monsters.preset.Beetleburst); Attack.clear(15, 0, getLocaleString(sdk.locale.monsters.Beetleburst)); + + if (!me.haveWaypoint(sdk.areas.LostCity)) { + Pather.moveToExit(sdk.areas.LostCity, true); + if (Pather.moveToPresetMonster(me.area, sdk.monsters.preset.DarkElder)) { + Attack.clear(15, 0, getLocaleString(sdk.locale.monsters.DarkElder)); + } + Pather.getWP(sdk.areas.LostCity); + } return true; } diff --git a/libs/SoloPlay/Scripts/boneash.js b/libs/SoloPlay/Scripts/boneash.js index 125c7e89..8efedf7a 100644 --- a/libs/SoloPlay/Scripts/boneash.js +++ b/libs/SoloPlay/Scripts/boneash.js @@ -9,11 +9,20 @@ function boneash () { Town.doChores(false, { fullChores: true }); myPrint("starting boneash"); - Pather.checkWP(sdk.areas.InnerCloister, true) ? Pather.useWaypoint(sdk.areas.InnerCloister) : Pather.getWP(sdk.areas.InnerCloister); + Pather.checkWP(sdk.areas.InnerCloister, true) + ? Pather.useWaypoint(sdk.areas.InnerCloister) + : Pather.getWP(sdk.areas.InnerCloister); Precast.doPrecast(true); Pather.clearToExit(sdk.areas.InnerCloister, sdk.areas.Cathedral, true); Pather.moveTo(20047, 4898); Attack.killTarget(getLocaleString(sdk.locale.monsters.BoneAsh)); + if (!me.haveWaypoint(sdk.areas.CatacombsLvl2)) { + myPrint("Getting cata 2 wp"); + Pather.moveToExit([sdk.areas.CatacombsLvl1, sdk.areas.CatacombsLvl2], true); + Pather.getWP(sdk.areas.CatacombsLvl2); + Pather.useWaypoint(sdk.areas.RogueEncampment); + } + return true; } diff --git a/libs/SoloPlay/Scripts/creepingfeature.js b/libs/SoloPlay/Scripts/creepingfeature.js new file mode 100644 index 00000000..e1311e94 --- /dev/null +++ b/libs/SoloPlay/Scripts/creepingfeature.js @@ -0,0 +1,18 @@ +/** +* @filename creepingfeature.js +* @author theBGuy +* @desc kill Creeping Feature +* +*/ + +function creepingfeature () { + Town.doChores(); + Town.goToTown(2); + + Pather.journeyTo(sdk.areas.StonyTombLvl2); + Pather.moveToPreset(sdk.areas.StonyTombLvl2, sdk.unittype.Monster, sdk.monsters.preset.CreepingFeature); + Attack.clear(15, 0, getLocaleString(sdk.locale.monsters.CreepingFeature)); + Pickit.pickItems(); + + return true; +} diff --git a/libs/SoloPlay/Scripts/den.js b/libs/SoloPlay/Scripts/den.js index e4d0ceaa..5d0099ab 100644 --- a/libs/SoloPlay/Scripts/den.js +++ b/libs/SoloPlay/Scripts/den.js @@ -19,7 +19,9 @@ function den () { myPrint("starting den"); - me.gold > 500 && Town.initNPC("repair", "shopItems") && NPCAction.shopItems(500, [sdk.items.type.Bow, sdk.items.type.Crossbow, sdk.items.type.Belt]); + if (me.charlvl < 10 && me.gold > 500 && Town.initNPC("repair", "shopItems")) { + NPCAction.shopItems(500, [sdk.items.type.Bow, sdk.items.type.Crossbow, sdk.items.type.Belt]); + } me.gold > 1000 && Town.buyPots(12, "stamina", true); if (!Pather.checkWP(sdk.areas.ColdPlains) || me.charlvl < 4) { @@ -30,21 +32,32 @@ function den () { let cPlains = Pather.getExitCoords(me.area, sdk.areas.ColdPlains); // sort by the ones closest to us but also by distance to cold plains exit so we end up there Game.getPresetObjects(me.area) - .filter(el => Misc.presetShrineIds.includes(el.id) || Misc.presetChestIds.includes(el.id)) - .sort((a, b) => { - let [aX, aY] = [a.roomx * 5 + a.x, a.roomy * 5 + a.y]; - let [bX, bY] = [b.roomx * 5 + b.x, b.roomy * 5 + b.y]; - if ([aX, aY].distance < [bX, bY].distance && getDistance(aX, aY, cPlains.x, cPlains.y) > getDistance(bX, bY, cPlains.x, cPlains.y)) { + .filter(function (el) { + return sdk.shrines.Presets.includes(el.id) || sdk.objects.chestIds.includes(el.id); + }) + .sort(function (a, b) { + const [aX, aY] = [a.roomx * 5 + a.x, a.roomy * 5 + a.y]; + const [bX, bY] = [b.roomx * 5 + b.x, b.roomy * 5 + b.y]; + const [aDistMe, bDistMe] = [[aX, aY].distance, [bX, bY].distance]; + const [aDistExit, bDistExit] = [ + getDistance(aX, aY, cPlains.x, cPlains.y), + getDistance(bX, bY, cPlains.x, cPlains.y) + ]; + if (aDistMe < bDistMe && aDistExit > bDistExit) { return -1; } - if ([aX, aY].distance > [bX, bY].distance && getDistance(aX, aY, cPlains.x, cPlains.y) < getDistance(bX, bY, cPlains.x, cPlains.y)) { + if (aDistMe > bDistMe && aDistExit < bDistExit) { return 1; } - return [aX, aY].distance - [bX, bY].distance; + return aDistMe - bDistMe; }) - .forEach(el => Pather.moveNearUnit(el, 7)); + .forEach(function (el) { + Pather.moveNearUnit(el, 7); + }); } else { - Pather.moveNearPreset(sdk.areas.BloodMoor, sdk.unittype.Object, sdk.objects.SuperChest, 8) && Misc.openChests(8); + if (Pather.moveNearPreset(sdk.areas.BloodMoor, sdk.unittype.Object, sdk.objects.SuperChest, 8)) { + Misc.openChests(8); + } } } catch (e) { console.warn(e.message ? e.message : e); @@ -52,12 +65,20 @@ function den () { Pather.getWP(sdk.areas.ColdPlains); // check if we need to do chores - if so use waypoint to town (preserves portal if we made one at den) - return to cold plains using waypoint - Storage.Inventory.UsedSpacePercent() > 50 && Pather.useWaypoint(sdk.areas.RogueEncampment) && Town.doChores() && Pather.useWaypoint(sdk.areas.ColdPlains); + (Storage.Inventory.UsedSpacePercent() > 50 + && Pather.useWaypoint(sdk.areas.RogueEncampment) + && Town.doChores() + && Pather.useWaypoint(sdk.areas.ColdPlains)); } if (me.charlvl < 8) { - me.sorceress && me.charlvl >= 2 && me.charlvl < 8 && Loader.skipTown.push("bishibosh") && Loader.runScript("bishibosh"); - me.charlvl < 6 && Loader.skipTown.push("cave") && Loader.runScript("cave"); + if (me.sorceress && me.charlvl >= 2 && me.charlvl < 8 + && Loader.skipTown.push("bishibosh")) { + Loader.runScript("bishibosh"); + } + if (me.charlvl < 6 && Loader.skipTown.push("cave")) { + Loader.runScript("cave"); + } } if (me.charlvl < 8 || me.gold < 1000) { diff --git a/libs/SoloPlay/Tools/SoloIndex.js b/libs/SoloPlay/Tools/SoloIndex.js index b69d8332..b1e45d89 100644 --- a/libs/SoloPlay/Tools/SoloIndex.js +++ b/libs/SoloPlay/Tools/SoloIndex.js @@ -22,7 +22,8 @@ const SoloIndex = { "corpsefire", "mausoleum", "den", "bishibosh", "bloodraven", "tristram", "treehead", "countess", "smith", "pits", "jail", "boneash", "andariel", "a1chests", "cows", // Act 2 - "cube", "radament", "amulet", "summoner", "maggotlair", "tombs", "ancienttunnels", "staff", "duriel", + "cube", "radament", "creepingfeature", "beetleburst", "amulet", "summoner", + "maggotlair", "tombs", "ancienttunnels", "staff", "duriel", // Act 3 "lamessen", "templeruns", "lowerkurast", "eye", "heart", "brain", "travincal", "mephisto", // Act 4 @@ -58,7 +59,8 @@ const SoloIndex = { }, "den": { skipIf: function () { - return me.den; + /* xp is bad in den so only run it once we can blast through*/ + return me.den || (me.charlvl > 8 && me.charlvl < 12); }, shouldRun: function () { if (this.skipIf()) return false; @@ -215,7 +217,7 @@ const SoloIndex = { }, "andariel": { skipIf: function () { - if (me.charlvl < 12) return true; + if (me.charlvl < 11) return true; if (!me.andariel) return false; if (me.hell && me.amazon && SetUp.currentBuild !== SetUp.finalBuild) return true; return false; @@ -271,6 +273,18 @@ const SoloIndex = { return true; } }, + "creepingfeature": { + preReq: function () { + return (me.accessToAct(2)); + }, + skipIf: function () { + return (me.charlvl < 12 || me.charlvl > 20); + }, + shouldRun: function () { + if (!this.preReq() || this.skipIf()) return false; + return true; + } + }, "cube": { preReq: function () { return me.accessToAct(2); From 067fc632bb5c8845a4e2329ffd2ed381b860849b Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Thu, 8 Jun 2023 23:21:28 -0400 Subject: [PATCH 153/263] Fix inability to equip quest item - last fix for equipping lower tier'd items broke ability to equip quest items as they have no tier value. Use equip prototype instead --- libs/SoloPlay/Functions/ItemOverrides.js | 2 +- libs/SoloPlay/Functions/Quest.js | 7 ++++++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/libs/SoloPlay/Functions/ItemOverrides.js b/libs/SoloPlay/Functions/ItemOverrides.js index 0fb78f74..dd715302 100644 --- a/libs/SoloPlay/Functions/ItemOverrides.js +++ b/libs/SoloPlay/Functions/ItemOverrides.js @@ -375,7 +375,7 @@ Item.equip = function (item, bodyLoc) { return item.isEquipped && item.bodylocation === bodyLoc; }) .first(); - if (currentEquipped) { + if (currentEquipped && !item.questItem) { if (NTIP.GetTier(currentEquipped) > NTIP.GetTier(item)) { console.debug( "ÿc9Equipÿc0 ::\n" diff --git a/libs/SoloPlay/Functions/Quest.js b/libs/SoloPlay/Functions/Quest.js index e7925ccc..c3ea4029 100644 --- a/libs/SoloPlay/Functions/Quest.js +++ b/libs/SoloPlay/Functions/Quest.js @@ -222,8 +222,13 @@ const Quest = { if (questItem) { me.dualWielding && Item.removeItem(sdk.body.LeftArm); + if (questItem.isInStash && !Town.openStash()) { + console.log("ÿc8Kolbot-SoloPlayÿc0: failed to open stash. (Quest.equipItem)"); + Item.autoEquip(); + return false; + } - if (!Item.equip(questItem, loc)) { + if (!questItem.equip(loc)) { Pickit.pickItems(); console.log("ÿc8Kolbot-SoloPlayÿc0: failed to equip " + classid + " .(Quest.equipItem)"); } From ae28a19411a7adeac6c5ba1966c6f67c5d249c64 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Mon, 12 Jun 2023 00:22:01 -0400 Subject: [PATCH 154/263] Update Tracker.js - small bit of formatting --- libs/SoloPlay/Tools/Tracker.js | 35 ++++++++++++++++++++++++++-------- 1 file changed, 27 insertions(+), 8 deletions(-) diff --git a/libs/SoloPlay/Tools/Tracker.js b/libs/SoloPlay/Tools/Tracker.js index d41ae78e..f117dd84 100644 --- a/libs/SoloPlay/Tools/Tracker.js +++ b/libs/SoloPlay/Tools/Tracker.js @@ -14,11 +14,19 @@ const Tracker = { LPPath: "libs/SoloPlay/Data/" + me.profile + "/" + me.profile + "-LevelingPerformance.csv", SPPath: "libs/SoloPlay/Data/" + me.profile + "/" + me.profile + "-ScriptPerformance.csv", // Leveling Performance - LPHeader: "Total Time,InGame Time,Split Time,Area,Character Level,Gained EXP,Gained EXP/Minute,Difficulty,Gold,Fire Resist,Cold Resist,Light Resist,Poison Resist,Current Build" + "\n", + LPHeader: "Total Time,InGame,Split Time,Area,Charlevel,Gained EXP,EXP/Minute,Difficulty,Gold,Fire Resist,Cold Resist,Light Resist,Poison Resist,Current Build" + "\n", // Script Performance - SPHeader: "Total Time,InGame Time,Sequence Time,Sequence,Character Level,Gained EXP,Gained EXP/Minute,EXP Gain %,Difficulty,Gold,Fire Resist,Cold Resist,Light Resist,Poison Resist,Current Build" + "\n", + SPHeader: "Total Time,InGame,Sequence,Script,Charlevel,Gained EXP,EXP/Minute,EXP Gain %,Difficulty,Gold,Fire Resist,Cold Resist,Light Resist,Poison Resist,Current Build" + "\n", tick: 0, - default: { + /** + * @typedef {Object} GameTracker + * @property {number} Total - Total time spent in game + * @property {number} InGame - Total time spent in game + * @property {number} OOG - Total time spent out of game + * @property {number} LastLevel - Time Last level reached + * @property {number} LastSave - Time Last save occurred + */ + _default: { "Total": 0, "InGame": 0, "OOG": 0, @@ -27,7 +35,7 @@ const Tracker = { }, initialize: function () { - const GameTracker = Object.assign({}, this.default); + const GameTracker = Object.assign({}, this._default); // Create Files if (!FileTools.exists("libs/SoloPlay/Data/" + me.profile)) { @@ -42,6 +50,10 @@ const Tracker = { return true; }, + /** + * @param {string} path + * @returns {GameTracker} + */ getObj: function (path) { let obj, OBJstring = FileAction.read(path); @@ -65,6 +77,10 @@ const Tracker = { return false; }, + /** + * @param {string} jsonPath + * @returns {GameTracker} + */ readObj: function (jsonPath) { let obj = this.getObj(jsonPath); return clone(obj); @@ -88,7 +104,7 @@ const Tracker = { }, resetGameTime: function () { - Tracker.writeObj(Object.assign({}, this.default), this.GTPath); + Tracker.writeObj(Object.assign({}, this._default), this.GTPath); }, reset: function () { @@ -135,13 +151,16 @@ const Tracker = { const currLevel = me.charlvl; const diffString = sdk.difficulty.nameOf(me.diff); const gainAMT = me.getStat(sdk.stats.Experience) - startexp; - const gainTime = gainAMT / (scriptTime / 60000); - const gainPercent = currLevel === 99 ? 0 : (gainAMT * 100 / Experience.nextExp[currLevel]).toFixed(6); + const gainTime = (gainAMT / (scriptTime / 60000)).toFixed(2); + const gainPercent = currLevel === 99 + ? 0 + : (gainAMT * 100 / Experience.nextExp[currLevel]).toFixed(2); const currentBuild = SetUp.currentBuild; const [GOLD, FR, CR, LR, PR] = [me.gold, me.realFR, me.realCR, me.realLR, me.realPR]; const string = ( Time.format(GameTracker.Total) + "," + Time.format(GameTracker.InGame) + "," + Time.format(scriptTime) - + "," + subscript + "," + currLevel + "," + gainAMT + "," + gainTime + "," + gainPercent + "," + diffString + + "," + subscript + "," + currLevel + "," + (gainAMT).toFixed(2) + + "," + gainTime + "," + gainPercent + "," + diffString + "," + GOLD + "," + FR + "," + CR + "," + LR + "," + PR + "," + currentBuild + "\n" ); From 5786674fc4572586c8e08f075af3f2d4625079a8 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Mon, 12 Jun 2023 11:53:22 -0400 Subject: [PATCH 155/263] Update default.dbj - formatting mostly - for soloplay and manualplay modes, directly kill the default thread. Hopefully fixes rare bug where default doesn't actually stop when we return from it --- default.dbj | 23 ++++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/default.dbj b/default.dbj index fe7eb367..2e1800ab 100644 --- a/default.dbj +++ b/default.dbj @@ -38,17 +38,22 @@ function main () { clearAllEvents(); // remove any event listeners from game crash // load heartbeat if it isn't already running - !getScript("threads/heartbeat.js") && load("threads/heartbeat.js"); + let _heartbeat = getScript("threads/heartbeat.js"); + if (!_heartbeat || !_heartbeat.running) { + load("threads/heartbeat.js"); + } // SoloPlay runs in it's own thread - check to ensure it exists in the files if (getScript("D2BotSoloPlay.dbj") && FileTools.exists("libs/SoloPlay/SoloPlay.js")) { load("libs/SoloPlay/SoloPlay.js"); + getScript(true).stop(); // kill this thread return true; } // map mode runs in it's own thread if (getScript("d2botmap.dbj")) { load("libs/manualplay/threads/mapthread.js"); + getScript(true).stop(); // kill this thread return true; } @@ -57,7 +62,9 @@ function main () { // don't load default for dropper/mules if (getScript("D2BotDropper.dbj") || getScript("D2BotMule.dbj")) { - FileTools.exists("libs/ItemDB.js") && include("ItemDB.js"); + if (FileTools.exists("libs/systems/dropper/ItemDB.js")) { + include("systems/dropper/ItemDB.js"); + } load("threads/AreaWatcher.js"); while (me.ingame) { @@ -145,7 +152,9 @@ function main () { // Load threads load("threads/ToolsThread.js"); - (Config.TownCheck || Config.TownHP > 0 || Config.TownMP > 0) && load("threads/TownChicken.js"); + if (Config.TownCheck || Config.TownHP > 0 || Config.TownMP > 0) { + load("threads/TownChicken.js"); + } if (Config.DebugMode.Stack && FileTools.exists("libs/modules/Guard.js")) { require("libs/modules/Guard"); @@ -179,8 +188,12 @@ function main () { if (Config.DebugMode.Memory) { delay(2000); getThreads() - .sort((a, b) => b.memory - a.memory) - .forEach(thread => console.debug(thread)); + .sort(function (a, b) { + return b.memory - a.memory; + }) + .forEach(function (thread) { + console.debug(thread); + }); } } From 387ad2af25c16d66cd56cac31e34fc3ef8e8fee2 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Thu, 15 Jun 2023 16:17:46 -0400 Subject: [PATCH 156/263] Update README.md - added paypal donate link as I've been asked about it a couple times --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index cfe98f64..2980b0bf 100644 --- a/README.md +++ b/README.md @@ -209,6 +209,8 @@ Kolbot-SoloPlay was built off the base structure of SoloLeveling by isid0re. Aut ## Contributing Pull requests are welcome. For major changes, please open an issue first to discuss what you would like to change. +[![Donate](https://img.shields.io/badge/PayPal-Donate-blue.svg?style=for-the-badge&logo=paypal)](https://www.paypal.com/donate/?business=Y9S4AWX9QUZXW&no_recurring=0¤cy_code=USD) + ## License [GPL-3.0](https://choosealicense.com/licenses/gpl-3.0/) From 0c76ba5e8dc1dc4c4a9c534358fd4d6da5e40e4b Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Thu, 15 Jun 2023 16:46:58 -0400 Subject: [PATCH 157/263] Update README.md - update one of the pictures since D2BotSoloCleaner no longer exists --- README.md | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 2980b0bf..c48155b3 100644 --- a/README.md +++ b/README.md @@ -119,7 +119,8 @@ In expansion, it transitions to the final build when final gear requirements are | 1.| Download Kolbot here: [github.com/blizzhackers/kolbot](https://github.com/blizzhackers/kolbot). |![blizzhackers github](https://i.imgur.com/RksqKEA.jpg) | | 2.| Click the green button to Download SoloPlay. |![enter image description here](https://i.imgur.com/cNqZDbW.jpg) | | 3.a| Copy and paste the following: `default.dbj`, `D2BotSoloPlay.dbj`, and the entire `\libs` folder into `\d2bs\kolbot\`.| ![kolbot](https://i.imgur.com/WNxJOhq.png) | -|3.b|A successful installation will show 1 new file in the folder: `D2BotSoloPlay.dbj` and look similar to the following image|![image](https://user-images.githubusercontent.com/60308670/131760184-ba777302-908e-4247-b9b7-1c9331028b2c.png)| 4.| Select Add for new a Kolbot Profile. | ![Add-profile.jpg](https://imgur.com/tHs9ZoH.jpg)| +|3.b|A successful installation will show 1 new file in the folder: `D2BotSoloPlay.dbj` and look similar to the following image|![image](https://github.com/blizzhackers/kolbot-SoloPlay/assets/60308670/a85713f4-df22-4aa8-937d-21cba4366b0c) +| 4.| Select Add for new a Kolbot Profile. | ![Add-profile.jpg](https://imgur.com/tHs9ZoH.jpg)| | 4.a| Select and Input a profile name. See the **[Possible Profile Name Choices](#possible-profile-names)** below for a list of available options. | ![extract into](https://i.imgur.com/2YcGKVH.png) | | 4.b| ***Optional*** Input your account name. If no name than a random account is created. | | | 4.c|***Optional*** Input your account password. If no name than a random password is created. | | @@ -193,8 +194,13 @@ https://youtu.be/qYHUw6nNn74 - If you have any questions please join me on my discord https://discord.gg/5pjTC2zH6N +## Contributing +Pull requests are welcome. For major changes, please open an issue first to discuss what you would like to change. + ![Kolbot-SoloPlay Paladin](https://user-images.githubusercontent.com/60308670/165398785-8ef1afd7-d232-4bc4-a23e-a1f1321ce0ed.png) +[![Donate](https://img.shields.io/badge/PayPal-Donate-blue.svg?style=for-the-badge&logo=paypal)](https://www.paypal.com/donate/?business=Y9S4AWX9QUZXW&no_recurring=0¤cy_code=USD) + ## Statistics (will become filled out as data becomes available) | Level | Amazon | Sorceress | Necromancer | Paladin | Barbarian | Druid | Assassin | |:------:|:------:|:-------:|:-------:|:------:|:------:|:------:|:-----:| @@ -206,10 +212,6 @@ https://discord.gg/5pjTC2zH6N ## Brief History Kolbot-SoloPlay was built off the base structure of SoloLeveling by isid0re. Autoplay scripts/systems aren't a new concept, some to note are sonic, autoplay, and AutoSorc. None of the existing ones were able to do other character classes though so SoloLeveling was created by modding Questing.js. Almost from the beginning, Isid0re and I were bouncing ideas off each other. At that time, I was working on a separate project. We discussed ideas that helped both of our projects. I officially joined in around 4 months or so after the Github repo went public and was actively involved in the project until 6/30/2021. I contributed updates including but not limited to: item based respec, the overlay, logging equipped items, showing tier values on items, many bug fixes, sorting, D2BotSoloCleaner, performance tracking, ect. Due to some personal conflicts between isid0re and myself, I decided to create GuysSoloLeveling to have all of my ideas in one place. On 6/30/2021 I created this repo and on 7/13/2021 I made it public. On 9/1/2021, I changed the name to Kolbot-SoloPlay after some major changes in structure and continue to update to make SoloPlay the best leveling system for legacy diablo 2. -## Contributing -Pull requests are welcome. For major changes, please open an issue first to discuss what you would like to change. - -[![Donate](https://img.shields.io/badge/PayPal-Donate-blue.svg?style=for-the-badge&logo=paypal)](https://www.paypal.com/donate/?business=Y9S4AWX9QUZXW&no_recurring=0¤cy_code=USD) ## License [GPL-3.0](https://choosealicense.com/licenses/gpl-3.0/) From df5798b05cc0aefd4cb69954dca6f5e644a0a50b Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Sat, 17 Jun 2023 10:45:16 -0400 Subject: [PATCH 158/263] Create fireeye.js --- libs/SoloPlay/Scripts/fireeye.js | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 libs/SoloPlay/Scripts/fireeye.js diff --git a/libs/SoloPlay/Scripts/fireeye.js b/libs/SoloPlay/Scripts/fireeye.js new file mode 100644 index 00000000..8ceed54e --- /dev/null +++ b/libs/SoloPlay/Scripts/fireeye.js @@ -0,0 +1,25 @@ +/** +* @filename fireeye.js +* @author theBGuy +* @desc kill fireye in palace cellar lvl 3 +* +*/ + +function fireeye () { + Town.doChores(false, { fullChores: true }); + myPrint("starting fireeye"); + + if (!me.summoner && !Misc.checkQuest(sdk.quest.id.TheArcaneSanctuary, 3/* talked to Jerhyn */)) { + Town.npcInteract("jerhyn"); + } + + Pather.checkWP(sdk.areas.ArcaneSanctuary, true) + ? Pather.useWaypoint(sdk.areas.ArcaneSanctuary) + : Pather.getWP(sdk.areas.ArcaneSanctuary); + Precast.doPrecast(true); + + if (!Pather.usePortal(null)) throw new Error("Failed to move to Fire Eye"); + Attack.clear(15, 0, getLocaleString(sdk.locale.monsters.FireEye)); + + return true; +} From b8eae08543316bc0f91ae903e73d818b972a299b Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Sat, 17 Jun 2023 10:46:11 -0400 Subject: [PATCH 159/263] Update baal.js - better positioning for necro so he can use bone prison. - preattack with dimvision --- libs/SoloPlay/Scripts/baal.js | 65 ++++++++++++++++++++++++++--------- 1 file changed, 49 insertions(+), 16 deletions(-) diff --git a/libs/SoloPlay/Scripts/baal.js b/libs/SoloPlay/Scripts/baal.js index d987e79b..7f6d6095 100644 --- a/libs/SoloPlay/Scripts/baal.js +++ b/libs/SoloPlay/Scripts/baal.js @@ -11,7 +11,6 @@ function baal () { Config.BossPriority = false; let decoyTick = 0; - let decoyDuration = (me.amazon ? Skill.getDuration(sdk.skills.Decoy) : 0); const preattack = function () { switch (me.classid) { @@ -19,7 +18,7 @@ function baal () { if (Skill.canUse(sdk.skills.Decoy)) { let decoy = Game.getMonster(sdk.summons.Dopplezon); - if (!decoy || (getTickCount() - decoyTick >= decoyDuration)) { + if (!decoy || (getTickCount() - decoyTick >= Skill.getDuration(sdk.skills.Decoy))) { Skill.cast(sdk.skills.Decoy, sdk.skills.hand.Right, 15092, 5028); decoyTick = getTickCount(); } @@ -28,13 +27,29 @@ function baal () { break; case sdk.player.class.Sorceress: if ([sdk.skills.Meteor, sdk.skills.Blizzard, sdk.skills.FrozenOrb].includes(Config.AttackSkill[1])) { - !me.skillDelay ? Skill.cast(Config.AttackSkill[1], sdk.skills.hand.Right, 15093, 5024) : delay(50); + !me.skillDelay + ? Skill.cast(Config.AttackSkill[1], sdk.skills.hand.Right, 15093, 5024) + : delay(50); + } + + return true; + case sdk.player.class.Necromancer: + if (Config.AttackSkill[3] === sdk.skills.PoisonNova) { + if ([15093, 5029].distance > 3) { + Pather.moveTo(15093, 5029); + } + + Skill.cast(Config.AttackSkill[3], sdk.skills.hand.Left); + } else if (Skill.canUse(sdk.skills.DimVision)) { + Skill.cast(sdk.skills.DimVision, sdk.skills.hand.Right, 15093, 5024); } return true; case sdk.player.class.Paladin: if (Config.AttackSkill[3] !== sdk.skills.BlessedHammer) return false; - [15093, 5029].distance > 3 && Pather.moveTo(15093, 5029); + if ([15093, 5029].distance > 3) { + Pather.moveTo(15093, 5029); + } Config.AttackSkill[4] > 0 && Skill.setSkill(Config.AttackSkill[4], sdk.skills.hand.Right); Skill.cast(Config.AttackSkill[3], sdk.skills.hand.Left); @@ -113,7 +128,7 @@ function baal () { if (getTickCount() - tick < Time.seconds(7)) { if (Skill.canUse(sdk.skills.Cleansing) && me.getState(sdk.states.Poison)) { Skill.setSkill(sdk.skills.Cleansing, sdk.skills.hand.Right); - Misc.poll(() => { + Misc.poll(function () { if (Config.AttackSkill[3] === sdk.skills.BlessedHammer) { Skill.cast(Config.AttackSkill[3], sdk.skills.hand.Left); } @@ -137,40 +152,58 @@ function baal () { switch (me.classid) { case sdk.player.class.Amazon: case sdk.player.class.Sorceress: - case sdk.player.class.Necromancer: case sdk.player.class.Assassin: - [15116, 5026].distance > 3 && Pather.moveTo(15116, 5026); + if ([15116, 5026].distance > 3) { + Pather.moveTo(15116, 5026); + } + break; + case sdk.player.class.Necromancer: + if (Config.AttackSkill[3] === sdk.skills.BoneSpear) { + if ([15115, 5047].distance > 3) { + Pather.moveTo(15115, 5047); + } + } else if (Config.AttackSkill[3] === sdk.skills.PoisonNova) { + if ([15094, 5029].distance > 3) { + Pather.moveTo(15094, 5029); + } + } break; case sdk.player.class.Paladin: if (Config.AttackSkill[3] === sdk.skills.BlessedHammer) { - [15094, 5029].distance > 3 && Pather.moveTo(15094, 5029); + if ([15094, 5029].distance > 3) { + Pather.moveTo(15094, 5029); + } break; } // eslint-disable-next-line no-fallthrough case sdk.player.class.Druid: if ([sdk.skills.Fissure, sdk.skills.Volcano].includes(Config.AttackSkill[3])) { - [15116, 5026].distance > 3 && Pather.moveTo(15116, 5026); - + if ([15116, 5026].distance > 3) { + Pather.moveTo(15116, 5026); + } break; } if (Config.AttackSkill[3] === sdk.skills.Tornado) { - [15094, 5029].distance > 3 && Pather.moveTo(15106, 5041); - + if ([15094, 5029].distance > 3) { + Pather.moveTo(15106, 5041); + } break; } // eslint-disable-next-line no-fallthrough case sdk.player.class.Barbarian: - [15101, 5045].distance > 3 && Pather.moveTo(15101, 5045); - + if ([15101, 5045].distance > 3) { + Pather.moveTo(15101, 5045); + } break; } // If we've been in the throne for 30 minutes that's way too long - if (getTickCount() - totalTick > Time.minutes(30)) return false; - + if (getTickCount() - totalTick > Time.minutes(30)) { + return false; + } delay(10); } From 6817a985767eff8874d0172c759d2c49a5d5c398 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Sat, 17 Jun 2023 10:47:07 -0400 Subject: [PATCH 160/263] Update brain.js - kill endugu when we get the brain --- libs/SoloPlay/Scripts/brain.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/libs/SoloPlay/Scripts/brain.js b/libs/SoloPlay/Scripts/brain.js index 77c3b0bd..c1ef03a7 100644 --- a/libs/SoloPlay/Scripts/brain.js +++ b/libs/SoloPlay/Scripts/brain.js @@ -9,7 +9,9 @@ function brain () { Town.doChores(false, { fullChores: true }); myPrint("starting brain"); - Pather.checkWP(sdk.areas.FlayerJungle, true) ? Pather.useWaypoint(sdk.areas.FlayerJungle) : Pather.getWP(sdk.areas.FlayerJungle); + Pather.checkWP(sdk.areas.FlayerJungle, true) + ? Pather.useWaypoint(sdk.areas.FlayerJungle) + : Pather.getWP(sdk.areas.FlayerJungle); Precast.doPrecast(true); Pather.clearToExit(sdk.areas.FlayerJungle, sdk.areas.FlayerDungeonLvl1, Pather.useTeleport()); Pather.clearToExit(sdk.areas.FlayerDungeonLvl1, sdk.areas.FlayerDungeonLvl2, Pather.useTeleport()); @@ -19,7 +21,7 @@ function brain () { console.log("ÿc8Kolbot-SoloPlayÿc0: Failed to get the Brain"); } - Attack.clear(0x7); + Attack.kill(getLocaleString(sdk.locale.monsters.WitchDoctorEndugu)); Quest.collectItem(sdk.items.quest.KhalimsBrain, sdk.quest.chest.KhalimsBrainChest); Quest.stashItem(sdk.items.quest.KhalimsBrain); From 2de4bbcf0a5228cad6a8e4671f8a40eda5d6f7e6 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Sat, 17 Jun 2023 10:48:04 -0400 Subject: [PATCH 161/263] Update maggotlair.js - kill coldworm --- libs/SoloPlay/Scripts/maggotlair.js | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/libs/SoloPlay/Scripts/maggotlair.js b/libs/SoloPlay/Scripts/maggotlair.js index bfe75480..402954ae 100644 --- a/libs/SoloPlay/Scripts/maggotlair.js +++ b/libs/SoloPlay/Scripts/maggotlair.js @@ -30,7 +30,9 @@ function maggotlair () { if (monster) { do { - if (monster.isBeetle && monster.distance <= 30 && monster.attackable) { + if ((monster.isBeetle || monster.isSpecial) + && monster.distance <= 30 + && monster.attackable) { monList.push(copyUnit(monster)); } } while (monster.getNext()); @@ -47,7 +49,9 @@ function maggotlair () { Town.doChores(false, { fullChores: true }); myPrint("starting maggot lair beetle bursting"); - Pather.checkWP(sdk.areas.FarOasis, true) ? Pather.useWaypoint(sdk.areas.FarOasis) : Pather.getWP(sdk.areas.FarOasis); + Pather.checkWP(sdk.areas.FarOasis, true) + ? Pather.useWaypoint(sdk.areas.FarOasis) + : Pather.getWP(sdk.areas.FarOasis); Precast.doPrecast(true); [sdk.areas.MaggotLairLvl1, sdk.areas.MaggotLairLvl2, sdk.areas.MaggotLairLvl3].forEach((mLair, i) => { Pather.moveToExit(mLair, true) && clearBeetles(); From 0d8ea458dbd3c8239f848b61b6d9104d4ece37b3 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Sat, 17 Jun 2023 10:48:41 -0400 Subject: [PATCH 162/263] Update cave.js - scenic route for better early survivability --- libs/SoloPlay/Scripts/cave.js | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/libs/SoloPlay/Scripts/cave.js b/libs/SoloPlay/Scripts/cave.js index 4e54006e..0b525076 100644 --- a/libs/SoloPlay/Scripts/cave.js +++ b/libs/SoloPlay/Scripts/cave.js @@ -7,6 +7,32 @@ function cave () { !me.inArea(sdk.areas.ColdPlains) && Pather.journeyTo(sdk.areas.ColdPlains); + if (me.charlvl <= 3) { + // scenic route + [ + Pather.getExitCoords(me.area, sdk.areas.StonyField), + Pather.getExitCoords(me.area, sdk.areas.BurialGrounds) + ].sort(function (a, b) { + return [a.x, a.y].distance - [b.x, b.y].distance; + }).forEach(function (el) { + Pather.moveTo(el.x, el.y); + }); + Pather.moveToExit(sdk.areas.ColdPlains, true); + } else if (me.charlvl <= 5 && !me.haveWaypoint(sdk.areas.StonyField)) { + const cLvl1 = Pather.getExitCoords(me.area, sdk.areas.CaveLvl1); + const sFields = Pather.getExitCoords(me.area, sdk.areas.StonyField); + const plainsWpCoords = AreaData.get(sdk.areas.ColdPlains).waypointCoords(); + const wpToCave = getDistance(cLvl1.x, cLvl1.y, plainsWpCoords.x, plainsWpCoords.y); + // const wpToStony = getDistance(sFields.x, sFields.y, plainsWpCoords.x, plainsWpCoords.y); + const stonyToCave = getDistance(cLvl1.x, cLvl1.y, sFields.x, sFields.y); + Pather.moveToExit(sdk.areas.StonyField, true); + Pather.getWP(sdk.areas.StonyField); + if (sFields.distance + stonyToCave < wpToCave) { + Pather.moveToExit(sdk.areas.ColdPlains, true); + } else { + Pather.useWaypoint(sdk.areas.ColdPlains); + } + } Pather.moveToExit([sdk.areas.CaveLvl1, sdk.areas.CaveLvl2], true, true); // coords from sonic From 9a24cdd9574ce8a42cfc392b7e0116a3f16c157b Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Sat, 17 Jun 2023 10:50:48 -0400 Subject: [PATCH 163/263] formatting mostly --- libs/SoloPlay/Scripts/a1chests.js | 36 +++++------ libs/SoloPlay/Scripts/a5chests.js | 37 ++++++----- libs/SoloPlay/Scripts/ancienttunnels.js | 8 ++- libs/SoloPlay/Scripts/bloodraven.js | 7 +- libs/SoloPlay/Scripts/corpsefire.js | 2 +- libs/SoloPlay/Scripts/eye.js | 4 +- libs/SoloPlay/Scripts/getkeys.js | 2 +- libs/SoloPlay/Scripts/heart.js | 4 +- libs/SoloPlay/Scripts/hephasto.js | 2 +- libs/SoloPlay/Scripts/nith.js | 2 +- libs/SoloPlay/Scripts/orgtorch.js | 2 +- libs/SoloPlay/Scripts/river.js | 2 +- libs/SoloPlay/Scripts/summoner.js | 4 +- libs/SoloPlay/Scripts/templeruns.js | 85 +++++++++++++++---------- libs/SoloPlay/Scripts/tombs.js | 9 ++- libs/SoloPlay/Scripts/travincal.js | 13 +++- libs/SoloPlay/Scripts/treehead.js | 2 +- libs/SoloPlay/Scripts/worldstone.js | 2 +- 18 files changed, 136 insertions(+), 87 deletions(-) diff --git a/libs/SoloPlay/Scripts/a1chests.js b/libs/SoloPlay/Scripts/a1chests.js index 69a35e54..0feebf60 100644 --- a/libs/SoloPlay/Scripts/a1chests.js +++ b/libs/SoloPlay/Scripts/a1chests.js @@ -5,29 +5,27 @@ * */ -function a1chests() { - const areas = [sdk.areas.CaveLvl2, sdk.areas.UndergroundPassageLvl2, sdk.areas.HoleLvl2, sdk.areas.PitLvl2]; - +function a1chests () { myPrint("starting a1 chests"); Town.doChores(); - for (let i = 0; i < areas.length; i++) { - try { - // Don't run pits for its chest, when it was cleared during the pits script - if ((SoloIndex.doneList.includes("pits") || me.barbarian) && areas[i] === sdk.areas.PitLvl2) { - continue; - } + [sdk.areas.CaveLvl2, sdk.areas.UndergroundPassageLvl2, sdk.areas.HoleLvl2, sdk.areas.PitLvl2] + .forEach(function (area) { + try { + // Don't run pits for its chest, when it was cleared during the pits script + if ((SoloIndex.doneList.includes("pits") || me.barbarian) && area === sdk.areas.PitLvl2) { + return; + } - myPrint("Moving to " + getAreaName(areas[i])); - Pather.journeyTo(areas[i]); - Precast.doPrecast(); - Misc.openChestsInArea(areas[i]); - Town.doChores(); - } catch (e) { - console.debug("ÿc8Kolbot-SoloPlayÿc0: Failed to move to " + getAreaName(areas[i]), e); - continue; - } - } + myPrint("Moving to " + getAreaName(area)); + Pather.journeyTo(area); + Precast.doPrecast(); + Misc.openChestsInArea(area); + Town.doChores(); + } catch (e) { + console.log("ÿc8Kolbot-SoloPlayÿc0: Failed to move to " + getAreaName(area), e); + } + }); return true; } diff --git a/libs/SoloPlay/Scripts/a5chests.js b/libs/SoloPlay/Scripts/a5chests.js index f21efd6e..525b830e 100644 --- a/libs/SoloPlay/Scripts/a5chests.js +++ b/libs/SoloPlay/Scripts/a5chests.js @@ -5,30 +5,35 @@ * */ -function a5chests() { - const areas = [sdk.areas.Abaddon, sdk.areas.PitofAcheron, sdk.areas.InfernalPit, sdk.areas.GlacialTrail, sdk.areas.DrifterCavern, sdk.areas.IcyCellar]; - +function a5chests () { + const areas = []; + + if (me.nightmare && !Pather.canTeleport()) { + me.charlvl >= 70 + ? areas.push(sdk.areas.DrifterCavern, sdk.areas.IcyCellar) + : areas.push(sdk.areas.GlacialTrail, sdk.areas.DrifterCavern, sdk.areas.IcyCellar); + } else { + areas.push( + sdk.areas.Abaddon, sdk.areas.PitofAcheron, + sdk.areas.InfernalPit, sdk.areas.GlacialTrail, + sdk.areas.DrifterCavern, sdk.areas.IcyCellar + ); + } + myPrint("starting a5 chests"); Town.doChores(); - for (let i = 0; i < areas.length; i++) { + areas.forEach(function (area) { try { - if (!Pather.canTeleport() && me.nightmare && [sdk.areas.Abaddon, sdk.areas.PitofAcheron, sdk.areas.InfernalPit].includes(areas[i])) { - continue; - } else if (!Pather.canTeleport() && me.nightmare && me.charlvl >= 70 && [sdk.areas.Abaddon, sdk.areas.PitofAcheron, sdk.areas.InfernalPit, sdk.areas.GlacialTrail, sdk.areas.DrifterCavern].includes(areas[i])) { - continue; - } - - myPrint("Moving to " + getAreaName(areas[i])); - Pather.journeyTo(areas[i]); + myPrint("Moving to " + getAreaName(area)); + Pather.journeyTo(area); Precast.doPrecast(); - Misc.openChestsInArea(areas[i]); + Misc.openChestsInArea(area); Town.doChores(); } catch (e) { - console.debug("ÿc8Kolbot-SoloPlayÿc0: Failed to move to " + getAreaName(areas[i]), e); - continue; + console.log("ÿc8Kolbot-SoloPlayÿc0: Failed to move to " + getAreaName(area), e); } - } + }); return true; } diff --git a/libs/SoloPlay/Scripts/ancienttunnels.js b/libs/SoloPlay/Scripts/ancienttunnels.js index fb6473ea..ef03848c 100644 --- a/libs/SoloPlay/Scripts/ancienttunnels.js +++ b/libs/SoloPlay/Scripts/ancienttunnels.js @@ -16,13 +16,17 @@ function ancienttunnels () { Attack.clearLevel(); } else { try { - Pather.moveToPreset(me.area, sdk.unittype.Object, sdk.objects.SuperChest) && Misc.openChests(5) && Pickit.pickItems(); + if (Pather.moveToPresetObject(me.area, sdk.objects.SuperChest)) { + Misc.openChests(5) && Pickit.pickItems(); + } } catch (e) { console.error(e); } try { - Pather.moveToPreset(me.area, sdk.unittype.Monster, sdk.monsters.preset.DarkElder) && Attack.clear(15, 0, getLocaleString(sdk.locale.monsters.DarkElder)); + if (Pather.moveToPresetMonster(me.area, sdk.monsters.preset.DarkElder)) { + Attack.clear(15, 0, getLocaleString(sdk.locale.monsters.DarkElder)); + } } catch (e) { console.warn("ÿc8Kolbot-SoloPlayÿc0: Failed to kill Dark Elder"); } diff --git a/libs/SoloPlay/Scripts/bloodraven.js b/libs/SoloPlay/Scripts/bloodraven.js index e30ee9e2..2f4385b3 100644 --- a/libs/SoloPlay/Scripts/bloodraven.js +++ b/libs/SoloPlay/Scripts/bloodraven.js @@ -24,7 +24,9 @@ function bloodraven () { } else { Pather.useWaypoint(sdk.areas.ColdPlains); if (me.charlvl < 5) { - SoloIndex.doneList.includes("cave") ? Attack.clearLevelUntilLevel(5) : Loader.skipTown.push("cave") && Loader.runScript("cave"); + SoloIndex.doneList.includes("cave") + ? Attack.clearLevelUntilLevel(5) + : Loader.skipTown.push("cave") && Loader.runScript("cave"); } } } @@ -63,7 +65,8 @@ function bloodraven () { break; case sdk.game.gametype.Expansion: - if ((me.charlvl < 80 || me.charlvl > 85) && !((me.sorceress || me.druid || me.assassin) && me.equipped.get(sdk.body.RightArm).tier < 100000)) { + if ((me.charlvl < 80 || me.charlvl > 85) + && !((me.sorceress || me.druid || me.assassin) && me.equipped.get(sdk.body.RightArm).tier < 100000)) { return true; } diff --git a/libs/SoloPlay/Scripts/corpsefire.js b/libs/SoloPlay/Scripts/corpsefire.js index 26dad08d..ad6851a3 100644 --- a/libs/SoloPlay/Scripts/corpsefire.js +++ b/libs/SoloPlay/Scripts/corpsefire.js @@ -5,7 +5,7 @@ * */ -function corpsefire() { +function corpsefire () { myPrint("starting corpsefire"); Town.doChores(null, { thawing: me.coldRes < 75, antidote: me.poisonRes < 75 }); diff --git a/libs/SoloPlay/Scripts/eye.js b/libs/SoloPlay/Scripts/eye.js index efc5614e..ea09b613 100644 --- a/libs/SoloPlay/Scripts/eye.js +++ b/libs/SoloPlay/Scripts/eye.js @@ -9,7 +9,9 @@ function eye () { Town.doChores(false, { fullChores: true }); myPrint("starting eye"); - Pather.checkWP(sdk.areas.SpiderForest, true) ? Pather.useWaypoint(sdk.areas.SpiderForest) : Pather.getWP(sdk.areas.SpiderForest); + Pather.checkWP(sdk.areas.SpiderForest, true) + ? Pather.useWaypoint(sdk.areas.SpiderForest) + : Pather.getWP(sdk.areas.SpiderForest); Precast.doPrecast(true); if (!Pather.moveToExit([sdk.areas.SpiderForest, sdk.areas.SpiderCavern], true)) { diff --git a/libs/SoloPlay/Scripts/getkeys.js b/libs/SoloPlay/Scripts/getkeys.js index 4d0fe8df..11144355 100644 --- a/libs/SoloPlay/Scripts/getkeys.js +++ b/libs/SoloPlay/Scripts/getkeys.js @@ -5,7 +5,7 @@ * */ -function getkeys() { +function getkeys () { Town.doChores(); if (!me.findItems(sdk.items.quest.KeyofTerror) || me.findItems(sdk.items.quest.KeyofTerror).length < 3) { diff --git a/libs/SoloPlay/Scripts/heart.js b/libs/SoloPlay/Scripts/heart.js index 8e08e76d..ca54b913 100644 --- a/libs/SoloPlay/Scripts/heart.js +++ b/libs/SoloPlay/Scripts/heart.js @@ -9,7 +9,9 @@ function heart () { Town.doChores(false, { fullChores: true }); myPrint("starting heart"); - Pather.checkWP(sdk.areas.KurastBazaar, true) ? Pather.useWaypoint(sdk.areas.KurastBazaar) : Pather.getWP(sdk.areas.KurastBazaar); + Pather.checkWP(sdk.areas.KurastBazaar, true) + ? Pather.useWaypoint(sdk.areas.KurastBazaar) + : Pather.getWP(sdk.areas.KurastBazaar); Precast.doPrecast(true); if (!Pather.journeyTo(sdk.areas.A3SewersLvl2) diff --git a/libs/SoloPlay/Scripts/hephasto.js b/libs/SoloPlay/Scripts/hephasto.js index 966ec48d..2aac4bbd 100644 --- a/libs/SoloPlay/Scripts/hephasto.js +++ b/libs/SoloPlay/Scripts/hephasto.js @@ -5,7 +5,7 @@ * */ -function hephasto() { +function hephasto () { myPrint("starting hephasto"); Town.doChores(null, { thawing: me.coldRes < 75, antidote: me.poisonRes < 75 }); diff --git a/libs/SoloPlay/Scripts/nith.js b/libs/SoloPlay/Scripts/nith.js index e07dfe22..9c8730b9 100644 --- a/libs/SoloPlay/Scripts/nith.js +++ b/libs/SoloPlay/Scripts/nith.js @@ -6,7 +6,7 @@ * */ -function nith() { +function nith () { Town.doChores(); myPrint("starting nith"); diff --git a/libs/SoloPlay/Scripts/orgtorch.js b/libs/SoloPlay/Scripts/orgtorch.js index 59f0ccf7..9a98fc46 100644 --- a/libs/SoloPlay/Scripts/orgtorch.js +++ b/libs/SoloPlay/Scripts/orgtorch.js @@ -5,7 +5,7 @@ * */ -function orgtorch() { +function orgtorch () { this.doneAreas = []; // Identify & mule diff --git a/libs/SoloPlay/Scripts/river.js b/libs/SoloPlay/Scripts/river.js index 95d4c260..c85bfda4 100644 --- a/libs/SoloPlay/Scripts/river.js +++ b/libs/SoloPlay/Scripts/river.js @@ -5,7 +5,7 @@ * */ -function river() { +function river () { myPrint("starting river"); Town.doChores(null, { thawing: me.coldRes < 75, antidote: me.poisonRes < 75 }); diff --git a/libs/SoloPlay/Scripts/summoner.js b/libs/SoloPlay/Scripts/summoner.js index 99da5772..4224c17c 100644 --- a/libs/SoloPlay/Scripts/summoner.js +++ b/libs/SoloPlay/Scripts/summoner.js @@ -75,7 +75,9 @@ function summoner () { Town.npcInteract("jerhyn"); } - Pather.checkWP(sdk.areas.ArcaneSanctuary, true) ? Pather.useWaypoint(sdk.areas.ArcaneSanctuary) : Pather.getWP(sdk.areas.ArcaneSanctuary); + Pather.checkWP(sdk.areas.ArcaneSanctuary, true) + ? Pather.useWaypoint(sdk.areas.ArcaneSanctuary) + : Pather.getWP(sdk.areas.ArcaneSanctuary); Precast.doPrecast(true); teleportPads(); diff --git a/libs/SoloPlay/Scripts/templeruns.js b/libs/SoloPlay/Scripts/templeruns.js index ab186fbd..ad9f1f4c 100644 --- a/libs/SoloPlay/Scripts/templeruns.js +++ b/libs/SoloPlay/Scripts/templeruns.js @@ -1,7 +1,6 @@ /** * @filename templeruns.js -* @author isid0re, theBGuy -* @author Xcon +* @author theBGuy * @desc temple runs for exp * */ @@ -9,43 +8,65 @@ function templeruns () { myPrint("starting temple runs"); // todo - calculate effort required to clear temple and decide whether to run it or move to next - const temples = [ - [sdk.areas.FlayerJungle, sdk.areas.LowerKurast], [sdk.areas.KurastBazaar, sdk.areas.RuinedTemple], - [sdk.areas.KurastBazaar, sdk.areas.DisusedFane], [sdk.areas.UpperKurast, sdk.areas.ForgottenReliquary], - [sdk.areas.UpperKurast, sdk.areas.ForgottenTemple], [sdk.areas.UpperKurast, sdk.areas.KurastCauseway, sdk.areas.RuinedFane], [sdk.areas.UpperKurast, sdk.areas.KurastCauseway, sdk.areas.DisusedReliquary] - ]; Town.doChores(false, { fullChores: true }); + Pather.useWaypoint(sdk.areas.LowerKurast); + Misc.openChestsInArea(sdk.areas.LowerKurast); - for (let run = 0; run < temples.length; run++) { - Pather.checkWP(temples[run][0], true) ? Pather.useWaypoint(temples[run][0]) : Pather.getWP(temples[run][0]); - Precast.doPrecast(true); - - if (Pather.moveToExit(temples[run], true, true)) { - if (me.inArea(sdk.areas.LowerKurast)) { - Misc.openChestsInArea(sdk.areas.LowerKurast); - } else if (me.inArea(sdk.areas.RuinedTemple) && !me.lamessen) { - me.overhead("lamessen"); - Pather.moveToPreset(sdk.areas.RuinedTemple, sdk.unittype.Object, sdk.quest.chest.LamEsensTomeHolder); - Quest.collectItem(sdk.quest.item.LamEsensTome, sdk.quest.chest.LamEsensTomeHolder); - Quest.unfinishedQuests(); - } else { - Attack.clearLevel(0xF); + // START + [ + { + base: sdk.areas.KurastBazaar, + temples: [sdk.areas.RuinedTemple, sdk.areas.DisusedFane] + }, + { + base: sdk.areas.UpperKurast, + temples: [sdk.areas.ForgottenReliquary, sdk.areas.ForgottenTemple] + }, + { + base: sdk.areas.KurastCauseway, + temples: [sdk.areas.RuinedFane, sdk.areas.DisusedReliquary] + }, + ].forEach(function (area) { + try { + if (!me.inArea(area.base)) { + if (!Pather.moveToExit(area.base, true)) throw new Error("Failed to change area"); + } + const precastTimeout = getTickCount() + Time.minutes(2); + if (Pather.wpAreas.includes(area.base) + && !getWaypoint(Pather.wpAreas.indexOf(area.base))) { + Pather.getWP(area.base); } + /** @type {Map precastTimeout)); + }); + } catch (e) { + console.error(e); } + }); + if (!Pather.checkWP(sdk.areas.Travincal)) { + Pather.getWP(sdk.areas.Travincal); Town.goToTown(); } - if (!me.inTown) { - Town.goToTown(); - - if (!me.mephisto) { - if (!Pather.checkWP(sdk.areas.Travincal)) { - Pather.getWP(sdk.areas.Travincal); - Town.goToTown(); - } - } - } - return true; } diff --git a/libs/SoloPlay/Scripts/tombs.js b/libs/SoloPlay/Scripts/tombs.js index d6baf725..098aff93 100644 --- a/libs/SoloPlay/Scripts/tombs.js +++ b/libs/SoloPlay/Scripts/tombs.js @@ -25,14 +25,19 @@ function tombs () { for (let number = 0; number < tombID.length; number++) { if (SoloIndex.index.duriel.skipIf()) return true; - Pather.checkWP(sdk.areas.CanyonofMagic, true) ? Pather.useWaypoint(sdk.areas.CanyonofMagic) : Pather.getWP(sdk.areas.CanyonofMagic); + Pather.checkWP(sdk.areas.CanyonofMagic, true) + ? Pather.useWaypoint(sdk.areas.CanyonofMagic) + : Pather.getWP(sdk.areas.CanyonofMagic); Precast.doPrecast(true); if (Pather.moveToExit(tombID[number], true, true)) { me.overhead("Tomb #" + (number + 1)); const duryTomb = getRoom().correcttomb === me.area; - let obj = Game.getPresetObject(me.area, (!duryTomb ? sdk.objects.SmallSparklyChest : sdk.objects.HoradricStaffHolder)); + let obj = Game.getPresetObject( + me.area, + (!duryTomb ? sdk.objects.SmallSparklyChest : sdk.objects.HoradricStaffHolder) + ); !!obj && Pather.moveToUnit(obj); Attack.clear(50); diff --git a/libs/SoloPlay/Scripts/travincal.js b/libs/SoloPlay/Scripts/travincal.js index 64be8054..3ca2dea6 100644 --- a/libs/SoloPlay/Scripts/travincal.js +++ b/libs/SoloPlay/Scripts/travincal.js @@ -10,7 +10,9 @@ function travincal () { Town.doChores(false, { fullChores: true }); myPrint("starting travincal"); - Pather.checkWP(sdk.areas.Travincal, true) ? Pather.useWaypoint(sdk.areas.Travincal) : Pather.getWP(sdk.areas.Travincal); + Pather.checkWP(sdk.areas.Travincal, true) + ? Pather.useWaypoint(sdk.areas.Travincal) + : Pather.getWP(sdk.areas.Travincal); Precast.doPrecast(true); const council = { @@ -41,8 +43,13 @@ function travincal () { // cube flail to will if (!me.getItem(sdk.items.quest.KhalimsWill) && me.getItem(sdk.items.quest.KhalimsFlail)) { - Quest.cubeItems(sdk.items.quest.KhalimsWill, - sdk.quest.item.KhalimsEye, sdk.quest.item.KhalimsHeart, sdk.quest.item.KhalimsBrain, sdk.quest.item.KhalimsFlail); + Quest.cubeItems( + sdk.items.quest.KhalimsWill, + sdk.quest.item.KhalimsEye, + sdk.quest.item.KhalimsHeart, + sdk.quest.item.KhalimsBrain, + sdk.quest.item.KhalimsFlail + ); delay(250 + me.ping); } diff --git a/libs/SoloPlay/Scripts/treehead.js b/libs/SoloPlay/Scripts/treehead.js index 415bc2ce..e4c23d2a 100644 --- a/libs/SoloPlay/Scripts/treehead.js +++ b/libs/SoloPlay/Scripts/treehead.js @@ -5,7 +5,7 @@ * */ -function treehead() { +function treehead () { Town.doChores(); Pather.useWaypoint(sdk.areas.DarkWood); Precast.doPrecast(true); diff --git a/libs/SoloPlay/Scripts/worldstone.js b/libs/SoloPlay/Scripts/worldstone.js index 03a651de..40d564c2 100644 --- a/libs/SoloPlay/Scripts/worldstone.js +++ b/libs/SoloPlay/Scripts/worldstone.js @@ -5,7 +5,7 @@ * */ -function worldstone() { +function worldstone () { myPrint("starting worldstone"); Town.doChores(null, { thawing: me.coldRes < 75, antidote: me.poisonRes < 75 }); From 5158f79d01db6a3420fb24c40514af2af5b08fc9 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Sat, 17 Jun 2023 10:51:33 -0400 Subject: [PATCH 164/263] Update countess.js - better wait time determination for the chest opening after completing the quest --- libs/SoloPlay/Scripts/countess.js | 25 +++++++++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) diff --git a/libs/SoloPlay/Scripts/countess.js b/libs/SoloPlay/Scripts/countess.js index d2da958b..e63b5bb0 100644 --- a/libs/SoloPlay/Scripts/countess.js +++ b/libs/SoloPlay/Scripts/countess.js @@ -14,7 +14,9 @@ function countess () { Town.doChores(false, { fullChores: true }); myPrint("starting countess"); - Pather.checkWP(sdk.areas.BlackMarsh, true) ? Pather.useWaypoint(sdk.areas.BlackMarsh) : Pather.getWP(sdk.areas.BlackMarsh); + Pather.checkWP(sdk.areas.BlackMarsh, true) + ? Pather.useWaypoint(sdk.areas.BlackMarsh) + : Pather.getWP(sdk.areas.BlackMarsh); Precast.doPrecast(true); let forQuest = !Misc.checkQuest(sdk.quest.id.ForgottenTower, sdk.quest.states.Completed); @@ -37,7 +39,26 @@ function countess () { if (forQuest) { Pather.moveToPreset(me.area, sdk.unittype.Object, sdk.objects.SuperChest); - delay(3000); + let _items = new Set(); + let _oldSize = 0; + let itemEvent = function (gid, mode) { + if (gid > 0 && mode === 0) { + _items.add(gid); + } + }; + try { + addEventListener("itemaction", itemEvent); + Misc.poll(function () { + if (_items.size > _oldSize) { + _oldSize = _items.size; + return false; + } + + return _items.size > 0 && _items.size === _oldSize; + }, Time.seconds(5), Time.seconds(1)); + } finally { + removeEventListener("itemaction", itemEvent); + } } } catch (err) { console.log("ÿc8Kolbot-SoloPlayÿc0: Failed to kill Countess: " + err); From f817553067eab771d43504356905de64c43b61d0 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Sun, 25 Jun 2023 10:36:59 -0400 Subject: [PATCH 165/263] Update AttackOverrides.js - handle undefined charge, still unsure how this happens though --- libs/SoloPlay/Functions/AttackOverrides.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/libs/SoloPlay/Functions/AttackOverrides.js b/libs/SoloPlay/Functions/AttackOverrides.js index 7b927235..4e28b200 100644 --- a/libs/SoloPlay/Functions/AttackOverrides.js +++ b/libs/SoloPlay/Functions/AttackOverrides.js @@ -938,6 +938,7 @@ Attack.getCurrentChargedSkillIds = function (init = false) { if (!(charges instanceof Array)) charges = [charges]; for (let charge of charges) { + if (!charge) continue; // add to total list of skillIds if (charge.charges > 0 && !currentChargedSkills.includes(charge.skill)) { currentChargedSkills.push(charge.skill); @@ -992,6 +993,7 @@ Attack.getItemCharges = function (skillId) { if (!(charges instanceof Array)) charges = [charges]; for (let charge of charges) { + if (!charge) continue; if (validCharge(charge)) { chargedItems.push({ skill: charge.skill, From 7413777e6effe9217a51e2447575282fc3fb94ce Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Wed, 28 Jun 2023 19:06:33 -0400 Subject: [PATCH 166/263] Update AutoMuleOverrides.js - fix AutoMule.matchItem for new NTIPList method --- libs/SoloPlay/Functions/AutoMuleOverrides.js | 35 ++++++++++++++++++-- 1 file changed, 33 insertions(+), 2 deletions(-) diff --git a/libs/SoloPlay/Functions/AutoMuleOverrides.js b/libs/SoloPlay/Functions/AutoMuleOverrides.js index d865f1b8..2d0df6ad 100644 --- a/libs/SoloPlay/Functions/AutoMuleOverrides.js +++ b/libs/SoloPlay/Functions/AutoMuleOverrides.js @@ -12,6 +12,36 @@ includeIfNotIncluded("systems/automule/Automule.js"); +/** +* @param {ItemUnit} item +* @param {string[] | number[]} list +* @returns {boolean} +*/ +AutoMule.matchItem = function (item, list) { + let parsedPickit = new NTIPList(); + let classIDs = []; + + for (let i = 0; i < list.length; i += 1) { + let info = { + file: "Character Config", + line: list[i] + }; + + // classids + if (typeof list[i] === "number") { + classIDs.push(list[i]); + } else if (typeof list[i] === "string") { + // pickit entries + let parsedLine = NTIP.ParseLineInt(list[i], info); + if (parsedLine) { + parsedPickit.add(parsedLine, info); + } + } + } + + return (classIDs.includes(item.classid) || NTIP.CheckItem(item, parsedPickit)); +}; + AutoMule.getMuleItems = function () { let info = this.getInfo(); @@ -62,6 +92,8 @@ AutoMule.getMuleItems = function () { || SoloWants.keepItem(item)); }; + const checkTorchSystem = TorchSystem.getFarmers() && TorchSystem.isFarmer(); + // lets be more explicit about what we want to mule let items = me.getItemsEx() .filter(function (item) { @@ -76,12 +108,11 @@ AutoMule.getMuleItems = function () { // don't mule items in locked spots - not exactly applicable for soloplay but including it if (item.isInInventory && Storage.Inventory.IsLocked(item, Config.Inventory)) return false; // don't mule items wanted by one of the various systems - checks that it's not on the force mule list - // might be worth it to ignore force for soloplay in this case, muleing an item we need would slow down progression if (isWanted(item) && !AutoMule.matchItem(item, Config.AutoMule.Force.concat(Config.AutoMule.Trigger))) { return false; } // don't mule keys if part of torchsystem, again shouldn't really be used with soloplay but still including it - if (isAKey(item) && TorchSystem.getFarmers() && TorchSystem.isFarmer()) return false; + if (isAKey(item) && checkTorchSystem) return false; // we've gotten this far, mule items that are on the force list if (AutoMule.matchItem(item, Config.AutoMule.Force.concat(Config.AutoMule.Trigger))) return true; // alright that handles the basics -- now normal pickit check From 608e30fa2164e522a3b9fdd5d79cba770d965a3a Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Thu, 29 Jun 2023 15:46:22 -0400 Subject: [PATCH 167/263] Update PickitOverrides.js - some general cleanup/refactoring --- libs/SoloPlay/Functions/PickitOverrides.js | 162 ++++++++++++--------- 1 file changed, 92 insertions(+), 70 deletions(-) diff --git a/libs/SoloPlay/Functions/PickitOverrides.js b/libs/SoloPlay/Functions/PickitOverrides.js index 84aabf64..d4dc3aac 100644 --- a/libs/SoloPlay/Functions/PickitOverrides.js +++ b/libs/SoloPlay/Functions/PickitOverrides.js @@ -110,7 +110,7 @@ Pickit.checkItem = function (unit) { return NTIP.CheckItem(unit, NTIP.NoTier, true) || NTIP.CheckItem(unit, NTIP.CheckList, true); } - if ((NTIP.GetMercTier(unit) > 0 || NTIP.GetTier(unit) > 0 || NTIP.GetSecondaryTier(unit) > 0) && unit.identified) { + if (AutoEquip.hasTier(unit) && unit.identified) { if (Item.autoEquipCheck(unit)) { return resultObj(Pickit.Result.SOLOWANTS, "Autoequip Tier: " + NTIP.GetTier(unit)); } @@ -123,7 +123,7 @@ Pickit.checkItem = function (unit) { return resultObj(Pickit.Result.SOLOWANTS, "Autoequip Secondary Tier: " + NTIP.GetSecondaryTier(unit)); } - return NTIP.CheckItem(unit, NTIP.NoTier, true) || NTIP.CheckItem(unit, NTIP.CheckList, true); + return NTIP.CheckItem(unit, NTIP.CheckList, true); } if (rval.result === Pickit.Result.WANTED && unit.isBaseType) { @@ -133,7 +133,9 @@ Pickit.checkItem = function (unit) { } // LowGold - if (rval.result === Pickit.Result.UNWANTED && !Town.ignoredItemTypes.includes(unit.itemType) && !unit.questItem + if (rval.result === Pickit.Result.UNWANTED + && (!Town.ignoreType(unit.itemType) || me.data.level <= 3) + && !unit.questItem && (unit.isInInventory || (me.gold < Config.LowGold || me.gold < 500000))) { // Gold doesn't take up room, just pick it up if (unit.classid === sdk.items.Gold) return resultObj(Pickit.Result.TRASH); @@ -177,7 +179,9 @@ Pickit.amountOfPotsNeeded = function () { _a); if (hpMax > 0 || mpMax > 0 || rvMax > 0) { me.getItemsEx() - .filter((pot) => potTypes.includes(pot.itemType) && (pot.isInBelt || pot.isInInventory)) + .filter(function (pot) { + return potTypes.includes(pot.itemType) && (pot.isInBelt || pot.isInInventory); + }) .forEach(function (pot) { needed[pot.itemType][pot.location] -= 1; }); @@ -230,7 +234,7 @@ Pickit.canPick = function (unit) { // TODO: clean this up - let tome, charm, i, potion, needPots, buffers, pottype, myKey, key; + let tome, charm, myKey, key; switch (unit.itemType) { case sdk.items.type.Gold: @@ -288,18 +292,28 @@ Pickit.canPick = function (unit) { case sdk.items.type.HealingPotion: case sdk.items.type.ManaPotion: case sdk.items.type.RejuvPotion: - needPots = 0; - - for (i = 0; i < 4; i += 1) { - if (typeof unit.code === "string" && unit.code.includes(Config.BeltColumn[i])) { + let needPots = 0; + + const _pots = new Map([ + [sdk.items.type.HealingPotion, { count: 0 }], + [sdk.items.type.ManaPotion, { count: 0 }], + [sdk.items.type.RejuvPotion, { count: 0 }], + [sdk.items.type.AntidotePotion, { count: 0 }], + [sdk.items.type.StaminaPotion, { count: 0 }], + [sdk.items.type.ThawingPotion, { count: 0 }], + ]); + + for (let column of Config.BeltColumn) { + if (unit.code.includes(column)) { needPots += this.beltSize; } } - potion = me.getItem(-1, sdk.items.mode.inBelt); + let potion = me.getItem(-1, sdk.items.mode.inBelt); if (potion) { do { + _pots.get(potion.itemType).count += 1; if (potion.itemType === unit.itemType) { needPots -= 1; } @@ -309,39 +323,38 @@ Pickit.canPick = function (unit) { // re-do this to pick items to cursor if we don't want them in our belt then place them in invo let beltCheck = this.checkBelt(); if (needPots < 1) { - buffers = ["HPBuffer", "MPBuffer", "RejuvBuffer"]; - - for (i = 0; i < buffers.length; i += 1) { - if (Config[buffers[i]]) { - pottype = (function () { - switch (buffers[i]) { - case "HPBuffer": - return sdk.items.type.HealingPotion; - case "MPBuffer": - return sdk.items.type.ManaPotion; - case "RejuvBuffer": - return sdk.items.type.RejuvPotion; - default: - return -1; - } - })(); - - if (unit.itemType === pottype) { - if (!Storage.Inventory.CanFit(unit)) return false; - - needPots = Config[buffers[i]]; - potion = me.getItem(-1, sdk.items.mode.inStorage); - - if (potion) { - do { - if (potion.itemType === pottype && potion.isInInventory) { + const _buffers = new Map([ + ["HPBuffer", { type: sdk.items.type.HealingPotion, amount: Config.HPBuffer }], + ["MPBuffer", { type: sdk.items.type.ManaPotion, amount: Config.MPBuffer }], + ["RejuvBuffer", { type: sdk.items.type.RejuvPotion, amount: Config.RejuvBuffer }] + ]); + + for (let buffer of _buffers) { + if (buffer[1].amount <= 0) continue; + if (buffer[1].type === unit.itemType) { + if (!Storage.Inventory.CanFit(unit)) return false; + needPots = buffer[1].amount; + potion = me.getItem(-1, sdk.items.mode.inStorage); + + if (potion) { + do { + if (potion.isInInventory && _pots.has(potion.itemType)) { + _pots.get(potion.itemType).count += 1; + if (potion.itemType === buffer[1].type) { needPots -= 1; } - } while (potion.getNext()); + } + } while (potion.getNext()); + } + + if (needPots > 0) { + !beltCheck && _toCursorPick.add(unit.gid); + } else { + if (_pots.get(unit.itemType).count < 8 + && Storage.Inventory.CanFit(unit)) { + return true; } } - - needPots > 0 && !beltCheck && _toCursorPick.add(unit.gid); } } } @@ -351,7 +364,8 @@ Pickit.canPick = function (unit) { if (potion) { do { - if (potion.itemType === unit.itemType && (potion.isInInventory || potion.isInBelt)) { + if (potion.itemType === unit.itemType + && (potion.isInInventory || potion.isInBelt)) { if (potion.classid < unit.classid) { potion.use(); needPots += 1; @@ -400,8 +414,12 @@ Pickit.pickItem = function (unit, status, keptLine, clearBeforePick = true) { self.color = Item.color(unit); self.gold = unit.getStat(sdk.stats.Gold); self.dist = (unit.distance || Infinity); - let canTk = (Skill.haveTK && Pickit.tkable.includes(self.type) && !_toCursorPick.has(unit.gid) - && self.dist > 5 && self.dist < 20 && !checkCollision(me, unit, sdk.collision.WallOrRanged)); + let canTk = ( + Skill.haveTK && Pickit.tkable.includes(self.type) + && !_toCursorPick.has(unit.gid) + && self.dist > 5 && self.dist < 20 + && !checkCollision(me, unit, sdk.collision.WallOrRanged) + ); self.useTk = canTk && (me.mpPercent > 50); self.picked = false; } @@ -412,10 +430,6 @@ Pickit.pickItem = function (unit, status, keptLine, clearBeforePick = true) { sdk.uiflags.Waypoint, sdk.uiflags.Shop, sdk.uiflags.Stash, sdk.uiflags.Cube ]; - - if (!unit || unit === undefined) return false; - - let retry = false; const gid = unit.gid; let item = Game.getItem(-1, -1, gid); @@ -426,8 +440,11 @@ Pickit.pickItem = function (unit, status, keptLine, clearBeforePick = true) { me.cancel(0); } + let retry = false; const stats = new ItemStats(item); - const tkMana = stats.useTk ? Skill.getManaCost(sdk.skills.Telekinesis) * 2 : Infinity; + const tkMana = stats.useTk + ? Skill.getManaCost(sdk.skills.Telekinesis) * 2 + : Infinity; MainLoop: for (let i = 0; i < 3; i += 1) { @@ -647,7 +664,9 @@ Pickit.essessntialsPick = function (clearBeforePick = false, ignoreGold = false, if (item) { do { - if (item.onGroundOrDropping && item.distance <= maxDist && Pickit.essentials.includes(item.itemType)) { + if (item.onGroundOrDropping + && item.distance <= maxDist + && Pickit.essentials.includes(item.itemType)) { if (Pickit.essentialList.some(el => el.gid === item.gid)) continue; if (item.itemType !== sdk.items.type.Gold || item.distance < 5) { Pickit.essentialList.push(copyUnit(item)); @@ -658,10 +677,6 @@ Pickit.essessntialsPick = function (clearBeforePick = false, ignoreGold = false, if (!Pickit.essentialList.length) return true; - while (!me.idle) { - delay(40); - } - while (Pickit.essentialList.length > 0) { if (me.dead || !Pickit.enabled) return false; @@ -730,10 +745,6 @@ Pickit.pickItems = function (range = Config.PickRange, once = false) { const canUseMule = AutoMule.getInfo() && AutoMule.getInfo().hasOwnProperty("muleInfo"); const _pots = [sdk.items.type.HealingPotion, sdk.items.type.ManaPotion, sdk.items.type.RejuvPotion]; - while (!me.idle) { - delay(40); - } - let item = Game.getItem(); if (item) { @@ -764,24 +775,35 @@ Pickit.pickItems = function (range = Config.PickRange, once = false) { continue; } + // get the real item + const _item = Game.getItem(-1, -1, currItem.gid); + if (!_item || copyUnit(_item).x === undefined) { + Pickit.pickList.shift(); + + continue; + } + // Check if the item unit is still valid and if it's on ground or being dropped // Don't pick items behind walls/obstacles when walking - if (copyUnit(currItem).x !== undefined && currItem.onGroundOrDropping - && (Pather.useTeleport() || me.inTown || !checkCollision(me, currItem, sdk.collision.BlockWall))) { + if (_item.onGroundOrDropping + && (Pather.useTeleport() + || me.inTown + || !checkCollision(me, _item, sdk.collision.BlockWall))) { // Check if the item should be picked - let status = this.checkItem(currItem); + let status = this.checkItem(_item); - if (status.result && Pickit.canPick(currItem)) { - let canFit = (Storage.Inventory.CanFit(currItem) || Pickit.canFit(currItem)); + if (status.result && Pickit.canPick(_item)) { + let canFit = (Storage.Inventory.CanFit(_item) || Pickit.canFit(_item)); // Field id when our used space is above a certain percent or if we are full try to make room with FieldID - if (Config.FieldID.Enabled && (!canFit || Storage.Inventory.UsedSpacePercent() > Config.FieldID.UsedSpace)) { - me.fieldID() && (canFit = (currItem.gid !== undefined && Storage.Inventory.CanFit(currItem))); + if (Config.FieldID.Enabled + && (!canFit || Storage.Inventory.UsedSpacePercent() > Config.FieldID.UsedSpace)) { + me.fieldID() && (canFit = (_item.gid !== undefined && Storage.Inventory.CanFit(_item))); } if (!canFit && !me.checkForMobs({ range: 10 })) { me.sortInventory(); - canFit = (Storage.Inventory.CanFit(currItem) || Pickit.canFit(currItem)); + canFit = (Storage.Inventory.CanFit(_item) || Pickit.canFit(_item)); } // Try to make room by selling items in town @@ -808,17 +830,17 @@ Pickit.pickItems = function (range = Config.PickRange, once = false) { } // Town visit failed - abort - console.warn("Failed to visit town. ÿc7Not enough room for " + Item.color(currItem) + currItem.name); + console.warn("Failed to visit town. ÿc7Not enough room for " + Item.color(_item) + _item.name); return false; } // Can't make room - trigger automule - if (copyUnit(currItem).x !== undefined) { - Item.logger("No room for", currItem); - console.warn("ÿc7Not enough room for " + Item.color(currItem) + currItem.name); + if (copyUnit(_item).x !== undefined) { + Item.logger("No room for", _item); + console.warn("ÿc7Not enough room for " + Item.color(_item) + _item.name); // ignore the item now - Pickit.ignoreList.add(currItem.gid); + Pickit.ignoreList.add(_item.gid); needMule = true; break; @@ -827,7 +849,7 @@ Pickit.pickItems = function (range = Config.PickRange, once = false) { // Item can fit - pick it up if (canFit) { - let picked = this.pickItem(currItem, status.result, status.line); + let picked = this.pickItem(_item, status.result, status.line); if (picked && once) return true; } } From ffae3b1b0a2a33ec89a73fc66e2805a20bb9099c Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Fri, 30 Jun 2023 11:22:05 -0400 Subject: [PATCH 168/263] Update PickitOverrides.js - should fix keeping pickit wanted charms --- libs/SoloPlay/Functions/PickitOverrides.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libs/SoloPlay/Functions/PickitOverrides.js b/libs/SoloPlay/Functions/PickitOverrides.js index d4dc3aac..a1f53831 100644 --- a/libs/SoloPlay/Functions/PickitOverrides.js +++ b/libs/SoloPlay/Functions/PickitOverrides.js @@ -107,7 +107,7 @@ Pickit.checkItem = function (unit) { return resultObj(Pickit.Result.SOLOWANTS, "Autoequip charm Tier: " + NTIP.GetCharmTier(unit)); } - return NTIP.CheckItem(unit, NTIP.NoTier, true) || NTIP.CheckItem(unit, NTIP.CheckList, true); + return NTIP.CheckItem(unit, NTIP.CheckList, true); } if (AutoEquip.hasTier(unit) && unit.identified) { From 8c76cd285d02ce8564928e536c4899f9cfb2a445 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Sat, 1 Jul 2023 15:14:32 -0400 Subject: [PATCH 169/263] Update PickitOverrides.js - small fix for readability over the transpiled code --- libs/SoloPlay/Functions/PickitOverrides.js | 37 +++++++++++----------- 1 file changed, 19 insertions(+), 18 deletions(-) diff --git a/libs/SoloPlay/Functions/PickitOverrides.js b/libs/SoloPlay/Functions/PickitOverrides.js index a1f53831..5924924e 100644 --- a/libs/SoloPlay/Functions/PickitOverrides.js +++ b/libs/SoloPlay/Functions/PickitOverrides.js @@ -158,25 +158,22 @@ Pickit.checkItem = function (unit) { // @jaenster Pickit.amountOfPotsNeeded = function () { - let _a, _b, _c, _d; + /** + * @constructor + * @param {number} max + */ + function NeededPots (max) { + this[sdk.storage.Belt] = 0; + this[sdk.storage.Inventory] = max; + } let potTypes = [sdk.items.type.HealingPotion, sdk.items.type.ManaPotion, sdk.items.type.RejuvPotion]; let hpMax = (Array.isArray(Config.HPBuffer) ? Config.HPBuffer[1] : Config.HPBuffer); let mpMax = (Array.isArray(Config.MPBuffer) ? Config.MPBuffer[1] : Config.MPBuffer); let rvMax = (Array.isArray(Config.RejuvBuffer) ? Config.RejuvBuffer[1] : Config.RejuvBuffer); - let needed = (_a = {}, - _a[sdk.items.type.HealingPotion] = (_b = {}, - _b[sdk.storage.Belt] = 0, - _b[sdk.storage.Inventory] = hpMax, - _b), - _a[sdk.items.type.ManaPotion] = (_c = {}, - _c[sdk.storage.Belt] = 0, - _c[sdk.storage.Inventory] = mpMax, - _c), - _a[sdk.items.type.RejuvPotion] = (_d = {}, - _d[sdk.storage.Belt] = 0, - _d[sdk.storage.Inventory] = rvMax, - _d), - _a); + const needed = {}; + needed[sdk.items.type.HealingPotion] = new NeededPots(hpMax); + needed[sdk.items.type.ManaPotion] = new NeededPots(mpMax); + needed[sdk.items.type.RejuvPotion] = new NeededPots(rvMax); if (hpMax > 0 || mpMax > 0 || rvMax > 0) { me.getItemsEx() .filter(function (pot) { @@ -188,9 +185,13 @@ Pickit.amountOfPotsNeeded = function () { } let missing = Storage.Belt.checkColumns(Pickit.beltSize); Config.BeltColumn.forEach(function (column, index) { - if (column === "hp") {needed[sdk.items.type.HealingPotion][sdk.storage.Belt] = missing[index];} - if (column === "mp") {needed[sdk.items.type.ManaPotion][sdk.storage.Belt] = missing[index];} - if (column === "rv") {needed[sdk.items.type.RejuvPotion][sdk.storage.Belt] = missing[index];} + if (column === "hp") { + needed[sdk.items.type.HealingPotion][sdk.storage.Belt] = missing[index]; + } else if (column === "mp") { + needed[sdk.items.type.ManaPotion][sdk.storage.Belt] = missing[index]; + } else if (column === "rv") { + needed[sdk.items.type.RejuvPotion][sdk.storage.Belt] = missing[index]; + } }); return needed; }; From f9a316c4cb2aa589551bd31e7dbc98f35d027543 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Thu, 6 Jul 2023 18:24:45 -0400 Subject: [PATCH 170/263] Update NPCAction.js - don't change acts to gamble, it wastes time when we can do it later on - fix typo in `NPCAction.repair`, wrap in try-finally to ensure we switch back to primary slot --- libs/SoloPlay/Functions/NPCAction.js | 51 +++++++++++++++++----------- 1 file changed, 32 insertions(+), 19 deletions(-) diff --git a/libs/SoloPlay/Functions/NPCAction.js b/libs/SoloPlay/Functions/NPCAction.js index c4a54f50..8ae99cb1 100644 --- a/libs/SoloPlay/Functions/NPCAction.js +++ b/libs/SoloPlay/Functions/NPCAction.js @@ -65,7 +65,7 @@ let [needPots, needBuffer, specialCheck] = [false, true, false]; let col = Storage.Belt.checkColumns(beltSize); - const getNeededBuffer = () => { + const getNeededBuffer = function () { [buffer.hp, buffer.mp] = [0, 0]; me.getItemsEx().filter(function (p) { return p.isInInventory && [sdk.items.type.HealingPotion, sdk.items.type.ManaPotion].includes(p.itemType); @@ -491,8 +491,12 @@ // merc tier'ed items if (haveMerc && !lowLevelShop) { items = npc.getItemsEx() - .filter((item) => !Town.ignoreType(item.itemType) && NTIP.GetMercTier(item) > 0) - .sort((a, b) => NTIP.GetMercTier(b) - NTIP.GetMercTier(a)) + .filter(function (item) { + return !Town.ignoreType(item.itemType) && NTIP.GetMercTier(item) > 0; + }) + .sort(function (a, b) { + return NTIP.GetMercTier(b) - NTIP.GetMercTier(a); + }) .forEach(function (item) { const myGold = me.gold; const itemCost = item.getItemCost(sdk.items.cost.ToBuy); @@ -547,7 +551,13 @@ if (Town.gambleIds.size === 0) return true; // avoid Alkor - me.act === 3 && Town.goToTown(me.accessToAct(4) ? 4 : 2); + if (me.act === 3 + || (Town.getDistance(Town.tasks.get(me.act).Gamble) > 25 && me.gold < Config.GambleGoldStart * 1.5)) { + // avoid changing towns as its time wasting + wantedTasks.add("gamble"); + return true; + } + // me.act === 3 && Town.goToTown(me.accessToAct(4) ? 4 : 2); let npc = Town.initNPC("Gamble", "gamble"); if (!npc) return false; @@ -623,8 +633,8 @@ force && repairAction.indexOf("repair") === -1 && repairAction.push("repair"); if (!repairAction || !repairAction.length) return false; - for (let i = 0; i < repairAction.length; i += 1) { - switch (repairAction[i]) { + for (let action of repairAction) { + switch (action) { case "repair": me.act === 3 && Town.goToTown(me.accessToAct(4) ? 4 : 2); npc = Town.initNPC("Repair", "repair"); @@ -641,19 +651,22 @@ .first(); if (bowCheck) { - let quiverType = bowCheck.itemType === sdk.items.type.Crossbow ? sdk.items.Bolts : sdk.items.Arrows; - let onSwitch = bowCheck.isOnSwap; - onSwitch && me.switchWeapons(sdk.player.slot.Secondary); - - npc = Town.initNPC("Repair", "buyQuiver"); - if (!npc) return false; - - let myQuiver = me.getItem(quiver, sdk.items.mode.Equipped); - !!myQuiver && myQuiver.sell(); - - let quiver = npc.getItem(quiverType); - !!quiver && quiver.buy(); - onSwitch && me.switchWeapons(sdk.player.slot.Main); + const quiverType = bowCheck.itemType === sdk.items.type.Crossbow + ? sdk.items.Bolts : sdk.items.Arrows; + const onSwitch = bowCheck.isOnSwap; + try { + onSwitch && me.switchWeapons(sdk.player.slot.Secondary); + npc = Town.initNPC("Repair", "buyQuiver"); + if (!npc) return false; + + let myQuiver = me.getItem(quiverType, sdk.items.mode.Equipped); + !!myQuiver && myQuiver.sell(); + + let quiver = npc.getItem(quiverType); + !!quiver && quiver.buy(); + } finally { + onSwitch && me.switchWeapons(sdk.player.slot.Main); + } } break; From 08e8fbdceca882626efb07259011ca8cb2b85108 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Sat, 8 Jul 2023 09:24:59 -0400 Subject: [PATCH 171/263] Update TownOverrides.js - add wantedTasks --- libs/SoloPlay/Functions/TownOverrides.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/libs/SoloPlay/Functions/TownOverrides.js b/libs/SoloPlay/Functions/TownOverrides.js index f95bccb3..450f014d 100644 --- a/libs/SoloPlay/Functions/TownOverrides.js +++ b/libs/SoloPlay/Functions/TownOverrides.js @@ -14,6 +14,8 @@ includeIfNotIncluded("core/Town.js"); +const wantedTasks = new Set(); + new Overrides.Override(Town, Town.drinkPots, function (orignal, type) { const objDrank = orignal(type, false); const pots = new Map([ From def0ef6c51af1f90d3515e253b6ed498a0333037 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Sun, 9 Jul 2023 19:35:19 -0400 Subject: [PATCH 172/263] Update SoloIndex.js - add fireeye - sorc skips cube until level 18 - skip tristram if not ready and have been in game long enough to start new game - cleanup - add LK runs for sorc in hell until ready to do temples --- libs/SoloPlay/Tools/SoloIndex.js | 75 +++++++++++++++++++++++++++----- 1 file changed, 64 insertions(+), 11 deletions(-) diff --git a/libs/SoloPlay/Tools/SoloIndex.js b/libs/SoloPlay/Tools/SoloIndex.js index b1e45d89..a1011535 100644 --- a/libs/SoloPlay/Tools/SoloIndex.js +++ b/libs/SoloPlay/Tools/SoloIndex.js @@ -23,7 +23,7 @@ const SoloIndex = { "countess", "smith", "pits", "jail", "boneash", "andariel", "a1chests", "cows", // Act 2 "cube", "radament", "creepingfeature", "beetleburst", "amulet", "summoner", - "maggotlair", "tombs", "ancienttunnels", "staff", "duriel", + "fireeye", "maggotlair", "tombs", "ancienttunnels", "staff", "duriel", // Act 3 "lamessen", "templeruns", "lowerkurast", "eye", "heart", "brain", "travincal", "mephisto", // Act 4 @@ -84,7 +84,10 @@ const SoloIndex = { skipIf: function () { if (me.hell) { // too many light immunes - although come back to this cause maybe just kill raven - return (["Lightning", "Trapsin", "Javazon"].includes(SetUp.currentBuild) || (me.amazon && SetUp.currentBuild !== SetUp.finalBuild)); + return ( + ["Lightning", "Trapsin", "Javazon"].includes(SetUp.currentBuild) + || (me.amazon && SetUp.currentBuild !== SetUp.finalBuild) + ); } return false; }, @@ -102,6 +105,10 @@ const SoloIndex = { }, "tristram": { skipIf: function () { + if (me.charlvl < 6 && (getTickCount() - me.gamestarttime) > Time.minutes(15)) { + // re-run cave in the next game as underground passage can be tough + return true; + } switch (me.classid) { case sdk.player.class.Paladin: return me.accessToAct(3) || Attack.auradin || me.checkItem({ name: sdk.locale.items.Enigma }).have; @@ -290,7 +297,7 @@ const SoloIndex = { return me.accessToAct(2); }, skipIf: function () { - return me.cube; + return me.cube || (me.sorceress && me.charlvl < 18/* Wait until we have tele*/); }, shouldRun: function () { if (!this.preReq() || this.skipIf()) return false; @@ -366,6 +373,20 @@ const SoloIndex = { return true; } }, + "fireeye": { + preReq: function () { + return me.accessToAct(2) && me.getQuest(sdk.quest.id.TheTaintedSun, sdk.quest.states.Completed) === 1; + }, + skipIf: function () { + return SoloIndex.doneList.includes("summoner") + || (me.charlvl < 16 || me.charlvl > 23); + }, + shouldRun: function () { + // does summoner have leveling potential? + if (!this.preReq() || this.skipIf()) return false; + return true; + } + }, "summoner": { preReq: function () { return me.accessToAct(2) && me.getQuest(sdk.quest.id.TheTaintedSun, sdk.quest.states.Completed) === 1; @@ -438,7 +459,10 @@ const SoloIndex = { shouldRun: function () { if (!this.preReq() || this.skipIf()) return false; switch (true) { - case (me.normal && ((me.charlvl > 18 && me.charlvl < 25) || (me.charlvl >= 25 && !me.diffCompleted && Check.brokeAf()))): + case (me.normal && ( + (me.charlvl > 18 && me.charlvl < 25) + || (me.charlvl >= 25 && !me.diffCompleted && Check.brokeAf()) + )): case (me.nightmare && me.charlvl < 50): case (me.hell && !me.classic && me.charlvl > 80): return true; @@ -463,11 +487,15 @@ const SoloIndex = { return me.accessToAct(3); }, skipIf: function () { - return (!me.barbarian); + return (!me.barbarian && !me.sorceress); }, shouldRun: function () { if (!this.preReq() || this.skipIf()) return false; - return (me.nightmare && me.charlvl >= 50 && !me.checkItem({ name: sdk.locale.items.VoiceofReason }).have); + if (me.sorceress && me.hell && me.charlvl < 90) return true; + if (me.barbarian && me.nightmare && me.charlvl >= 50) { + return !me.checkItem({ name: sdk.locale.items.VoiceofReason }).have; + } + return false; } }, "heart": { @@ -507,7 +535,9 @@ const SoloIndex = { case !me.travincal: case (me.charlvl < 25 || (me.charlvl >= 25 && me.normal && !me.diffCompleted && Check.brokeAf())): case (me.nightmare && !me.diablo && me.barbarian && !me.checkItem({ name: sdk.locale.items.Lawbringer }).have): - case (me.hell && me.paladin && me.charlvl > 85 && (!Attack.auradin || !me.checkItem({ name: sdk.locale.items.Enigma }).have)): + case (me.hell && me.paladin && me.charlvl > 85 && ( + !Attack.auradin || !me.checkItem({ name: sdk.locale.items.Enigma }).have + )): return true; } return false; @@ -552,8 +582,13 @@ const SoloIndex = { }, "river": { preReq: function () { + if (!me.accessToAct(4)) return false; const cLvl = me.charlvl; - return (me.accessToAct(4) && ((me.normal && cLvl >= 24) || (me.nightmare && cLvl >= 40) || (me.hell && cLvl >= 80))); + return ( + (me.normal && cLvl >= 24) + || (me.nightmare && cLvl >= 40) + || (me.hell && cLvl >= 80) + ); }, skipIf: function () { return (me.diablo || me.normal); @@ -570,8 +605,13 @@ const SoloIndex = { }, "hephasto": { preReq: function () { + if (!me.accessToAct(4)) return false; const cLvl = me.charlvl; - return (me.accessToAct(4) && ((me.normal && cLvl >= 24) || (me.nightmare && cLvl >= 40) || (me.hell && cLvl >= 80))); + return ( + (me.normal && cLvl >= 24) + || (me.nightmare && cLvl >= 40) + || (me.hell && cLvl >= 80) + ); }, skipIf: function () { return (!me.barbarian || me.normal || me.diablo); @@ -587,8 +627,16 @@ const SoloIndex = { }, "hellforge": { preReq: function () { + if (!me.accessToAct(4)) return false; const cLvl = me.charlvl; - return (me.accessToAct(4) && (me.classic || me.anya) && ((me.normal && cLvl >= 24) || (me.nightmare && cLvl >= 40) || (me.hell && cLvl >= 80))); + return ( + (me.classic || me.anya) + && ( + (me.normal && cLvl >= 24) + || (me.nightmare && cLvl >= 40) + || (me.hell && cLvl >= 80) + ) + ); }, skipIf: function () { return (me.hellforge || me.getQuest(sdk.quest.id.HellsForge, sdk.quest.states.ReqComplete)); @@ -600,8 +648,13 @@ const SoloIndex = { }, "diablo": { preReq: function () { + if (!me.accessToAct(4)) return false; const cLvl = me.charlvl; - return (me.accessToAct(4) && ((me.normal && cLvl >= 24) || (me.nightmare && cLvl >= 40) || (me.hell && cLvl >= 80))); + return ( + (me.normal && cLvl >= 24) + || (me.nightmare && cLvl >= 40) + || (me.hell && cLvl >= (me.sorceress ? 85 : 80)) + ); }, skipIf: function () { return false; From d7f5cbfe91d450ab8144528f2777ca76355dafb2 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Sun, 9 Jul 2023 19:37:59 -0400 Subject: [PATCH 173/263] General cleanup --- libs/SoloPlay/Functions/AutoBuild.js | 8 ++- .../ClassAttackOverrides/AmazonAttacks.js | 2 +- libs/SoloPlay/Functions/DynamicTiers.js | 24 +++++-- libs/SoloPlay/Functions/Quest.js | 64 ++++++++++++++----- libs/SoloPlay/Functions/SkillOverrides.js | 4 +- libs/SoloPlay/Modules/Events.js | 10 ++- 6 files changed, 83 insertions(+), 29 deletions(-) diff --git a/libs/SoloPlay/Functions/AutoBuild.js b/libs/SoloPlay/Functions/AutoBuild.js index 23854ce9..1168942d 100644 --- a/libs/SoloPlay/Functions/AutoBuild.js +++ b/libs/SoloPlay/Functions/AutoBuild.js @@ -12,8 +12,12 @@ const AutoBuild = new function AutoBuild () { const debug = !!Config.AutoBuild.DebugMode; const verbose = !!Config.AutoBuild.Verbose; - const log = (message) => FileTools.appendText(getLogFilename(), message + "\n"); - const getCurrentScript = () => getScript(true).name.toLowerCase(); + const log = function (message) { + FileTools.appendText(getLogFilename(), message + "\n"); + }; + const getCurrentScript = function () { + return getScript(true).name.toLowerCase(); + }; const buildTemplate = me.currentBuild.AutoBuildTemplate; let configUpdateLevel = 0; diff --git a/libs/SoloPlay/Functions/ClassAttackOverrides/AmazonAttacks.js b/libs/SoloPlay/Functions/ClassAttackOverrides/AmazonAttacks.js index 4100563a..c2b6d23c 100644 --- a/libs/SoloPlay/Functions/ClassAttackOverrides/AmazonAttacks.js +++ b/libs/SoloPlay/Functions/ClassAttackOverrides/AmazonAttacks.js @@ -332,7 +332,7 @@ ClassAttack.afterAttack = function () { Town.visitTown(true); } - this.lightFuryTick = 0; + ClassAttack.lightFuryTick = 0; }; /** diff --git a/libs/SoloPlay/Functions/DynamicTiers.js b/libs/SoloPlay/Functions/DynamicTiers.js index fd117ff6..28c0a7f1 100644 --- a/libs/SoloPlay/Functions/DynamicTiers.js +++ b/libs/SoloPlay/Functions/DynamicTiers.js @@ -442,10 +442,14 @@ } return ([ - sdk.stats.MaxFireResist, sdk.stats.MaxLightResist, sdk.stats.MaxColdResist, sdk.stats.MaxPoisonResist, - sdk.stats.AbsorbFire, sdk.stats.AbsorbLight, sdk.stats.AbsorbMagic, sdk.stats.AbsorbCold, - sdk.stats.AbsorbFirePercent, sdk.stats.AbsorbLightPercent, sdk.stats.AbsorbMagicPercent, sdk.stats.AbsorbColdPercent, - sdk.stats.NormalDamageReduction, sdk.stats.DamageResist, sdk.stats.MagicDamageReduction, sdk.stats.MagicResist + sdk.stats.MaxFireResist, sdk.stats.MaxLightResist, + sdk.stats.MaxColdResist, sdk.stats.MaxPoisonResist, + sdk.stats.AbsorbFire, sdk.stats.AbsorbLight, + sdk.stats.AbsorbMagic, sdk.stats.AbsorbCold, + sdk.stats.AbsorbFirePercent, sdk.stats.AbsorbLightPercent, + sdk.stats.AbsorbMagicPercent, sdk.stats.AbsorbColdPercent, + sdk.stats.NormalDamageReduction, sdk.stats.DamageResist, + sdk.stats.MagicDamageReduction, sdk.stats.MagicResist ].reduce((acc, stat) => acc + item.getStatEx(stat) * _tierWeights.res.get(stat), resistRating)); }; @@ -458,7 +462,8 @@ if (!buildInfo.caster || Config.AttackSkill.includes(sdk.skills.Attack) || Config.LowManaSkill.includes(sdk.skills.Attack) - || ([sdk.items.type.Bow, sdk.items.type.AmazonBow, sdk.items.type.Crossbow].includes(item.itemType) && CharData.skillData.bow.onSwitch)) { + || ([sdk.items.type.Bow, sdk.items.type.AmazonBow, sdk.items.type.Crossbow].includes(item.itemType) + && CharData.skillData.bow.onSwitch)) { let meleeRating = 0; const eleDmgWeight = 0.5; const eleDmgModifer = [sdk.items.type.Ring, sdk.items.type.Amulet].includes(item.itemType) ? 2 : 1; @@ -482,7 +487,9 @@ const skillsScore = function () { let skillsRating = [ - [sdk.stats.AllSkills, -1], [sdk.stats.AddClassSkills, me.classid], [sdk.stats.AddSkillTab, buildInfo.tabSkills], + [sdk.stats.AllSkills, -1], + [sdk.stats.AddClassSkills, me.classid], + [sdk.stats.AddSkillTab, buildInfo.tabSkills], ].reduce((acc, [stat, subId]) => acc + item.getStatEx(stat, subId) * _tierWeights.skill.get(stat), 0); (!buildInfo.caster && item.getItemType() === "Weapon") && (skillsRating /= 4); const _misc = { wanted: 40, useful: 35 }; @@ -597,7 +604,10 @@ const charmscore = function (item) { if (me.data.charmGids.includes(item.gid)) return 1000; // depending on invo space it might be worth it early on to keep 1 or 2 non-skiller grandcharms - @todo test that out - if (!item.unique && item.classid === sdk.items.GrandCharm && !me.getSkillTabs().some(s => item.getStatEx(sdk.stats.AddSkillTab, s))) return -1; + if (!item.unique && item.classid === sdk.items.GrandCharm + && !me.getSkillTabs().some(s => item.getStatEx(sdk.stats.AddSkillTab, s))) { + return -1; + } const buildInfo = Check.currentBuild(); let charmRating = 1; diff --git a/libs/SoloPlay/Functions/Quest.js b/libs/SoloPlay/Functions/Quest.js index c3ea4029..9450c13f 100644 --- a/libs/SoloPlay/Functions/Quest.js +++ b/libs/SoloPlay/Functions/Quest.js @@ -21,21 +21,25 @@ const Quest = { }; if (me.accessToAct(2)) { - !me.cube && getReq("cube", () => me.cube); + !me.cube && getReq("cube", function () { return me.cube; }); if (!me.staff && !me.horadricstaff) { - !me.amulet && getReq("amulet", () => me.amulet); - !me.shaft && getReq("staff", () => me.shaft); + !me.amulet && getReq("amulet", function () { return me.amulet; }); + !me.shaft && getReq("staff", function () { return me.shaft; }); } } if (me.accessToAct(3) && !me.travincal && !me.khalimswill) { - !me.eye && getReq("eye", () => me.eye); - !me.heart && getReq("heart", () => me.heart); - !me.brain && getReq("brain", () => me.brain); + !me.eye && getReq("eye", function () { return me.eye; }); + !me.heart && getReq("heart", function () { return me.heart; }); + !me.brain && getReq("brain", function () { return me.brain; }); } }, + /** + * @param {number} outcome + * @param {...number} classids + */ cubeItems: function (outcome, ...classids) { if (me.getItem(outcome) || outcome === sdk.quest.item.HoradricStaff && me.horadricstaff @@ -62,7 +66,9 @@ const Quest = { } } - Misc.poll(() => Cubing.openCube(), 5000, 1000); + Misc.poll(function () { + return Cubing.openCube(); + }, Time.seconds(5), 1000); let wantedItem; let tick = getTickCount(); @@ -94,7 +100,9 @@ const Quest = { if (me.horadricstaff) return true; let tick = getTickCount(); - let orifice = Misc.poll(() => Game.getObject(sdk.objects.HoradricStaffHolder)); + let orifice = Misc.poll(function () { + return Game.getObject(sdk.objects.HoradricStaffHolder); + }); if (!orifice) return false; let hstaff = ( @@ -176,10 +184,11 @@ const Quest = { return true; }, + /** @param {number} classid */ stashItem: function (classid) { let questItem = typeof classid === "object" ? classid : me.getItem(classid); if (!questItem) return false; - myPrint("Stashing: " + questItem.fname.split("\n").reverse().join(" ")); + myPrint("Stashing: " + questItem.prettyPrint); !me.inTown && Town.goToTown(); Town.openStash(); @@ -203,7 +212,9 @@ const Quest = { if (!chest || !Misc.openChest(chest)) return false; } - let questItem = Misc.poll(() => Game.getItem(classid), 3000, 100 + me.ping); + let questItem = Misc.poll(function () { + return Game.getItem(classid); + }, 3000, 100 + me.ping); if (Storage.Inventory.CanFit(questItem)) { Pickit.pickItem(questItem); @@ -216,6 +227,10 @@ const Quest = { return me.getItem(classid); }, + /** + * @param {number} classid + * @param {number} loc + */ equipItem: function (classid, loc) { let questItem = me.getItem(classid); !getUIFlag(sdk.uiflags.Stash) && me.cancel(); @@ -259,6 +274,7 @@ const Quest = { return questItem.bodylocation === loc; }, + /** @param {number} classid */ smashSomething: function (classid) { let tool = classid === sdk.objects.CompellingOrb ? sdk.items.quest.KhalimsWill @@ -273,7 +289,9 @@ const Quest = { let questTool = me.getItem(tool); while (me.getItem(tool)) { - smashable.distance > 4 && Pather.moveToEx(smashable.x, smashable.y, { clearSettings: { allowClearing: false } }); + if (smashable.distance > 4) { + Pather.moveToEx(smashable.x, smashable.y, { clearSettings: { allowClearing: false } }); + } Skill.cast(sdk.skills.Attack, sdk.skills.hand.Right, smashable); smashable.interact(); @@ -305,13 +323,17 @@ const Quest = { !me.inTown && Town.goToTown(); npcName = npcName.capitalize(true); Town.move(NPC[npcName]); - let npc = Misc.poll(() => Game.getNPC(NPC[npcName])); + let npc = Misc.poll(function () { + return Game.getNPC(NPC[npcName]); + }); Packet.flash(me.gid); delay(1 + me.ping * 2); if (npc && npc.openMenu()) { - action.forEach(menuOption => Misc.useMenu(menuOption) && delay(100 + me.ping)); + action.forEach(function (menuOption) { + Misc.useMenu(menuOption) && delay(100 + me.ping); + }); return true; } @@ -370,6 +392,7 @@ const Quest = { }, // Credit dzik or laz unsure who for this + /** @param {ItemUnit} item */ useSocketQuest: function (item = undefined) { if (SetUp.finalBuild === "Socketmule") return false; @@ -446,6 +469,7 @@ const Quest = { }, // Credit whoever did useSocketQuest, I modified that to come up with this + /** @param {ItemUnit} item */ useImbueQuest: function (item = undefined) { if (SetUp.finalBuild === "Imbuemule") return false; @@ -546,7 +570,7 @@ const Quest = { let book = me.getItem(sdk.quest.item.BookofSkill); if (book) { book.isInStash && Town.openStash() && delay(300); - Misc.poll(() => { + Misc.poll(function () { book.use(); if (me.getStat(sdk.stats.NewSkills) > 0) { console.log("ÿc8Kolbot-SoloPlayÿc0: used Radament skill book"); @@ -582,7 +606,14 @@ const Quest = { !me.inTown && Town.goToTown(3); tome.isInStash && Town.openStash() && Storage.Inventory.MoveTo(tome) && delay(300); Town.npcInteract("alkor") && delay(300); - me.getStat(sdk.stats.StatPts) > 0 && AutoStat.init(Config.AutoStat.Build, Config.AutoStat.Save, Config.AutoStat.BlockChance, Config.AutoStat.UseBulk); + if (me.getStat(sdk.stats.StatPts) > 0) { + AutoStat.init( + Config.AutoStat.Build, + Config.AutoStat.Save, + Config.AutoStat.BlockChance, + Config.AutoStat.UseBulk + ); + } console.log("ÿc8Kolbot-SoloPlayÿc0: LamEssen Tome completed"); } @@ -613,7 +644,8 @@ const Quest = { } // Killed council but haven't talked to cain - if (!Misc.checkQuest(sdk.quest.id.TheBlackenedTemple, sdk.quest.states.Completed) && Misc.checkQuest(sdk.quest.id.TheBlackenedTemple, 4)) { + if (!Misc.checkQuest(sdk.quest.id.TheBlackenedTemple, sdk.quest.states.Completed) + && Misc.checkQuest(sdk.quest.id.TheBlackenedTemple, 4)) { me.overhead("Finishing Travincal by talking to cain"); Town.goToTown(3) && Town.npcInteract("cain") && delay(300); me.cancel(); diff --git a/libs/SoloPlay/Functions/SkillOverrides.js b/libs/SoloPlay/Functions/SkillOverrides.js index 67383604..3f735318 100644 --- a/libs/SoloPlay/Functions/SkillOverrides.js +++ b/libs/SoloPlay/Functions/SkillOverrides.js @@ -52,7 +52,9 @@ Skill.cast = function (skillId, hand, x, y, item) { // Check mana cost, charged skills don't use mana if (!item && this.getManaCost(skillId) > me.mp) { // Maybe delay on ALL skills that we don't have enough mana for? - if (Config.AttackSkill.concat([sdk.skills.StaticField, sdk.skills.Teleport]).concat(Config.LowManaSkill).includes(skillId)) { + if (Config.AttackSkill + .concat([sdk.skills.StaticField, sdk.skills.Teleport]) + .concat(Config.LowManaSkill).includes(skillId)) { console.debug("Skill: " + getSkillById(skillId) + " manaCost: " + this.getManaCost(skillId) + " myMana: " + me.mp); delay(300); } diff --git a/libs/SoloPlay/Modules/Events.js b/libs/SoloPlay/Modules/Events.js index ed3f0084..a85082b7 100644 --- a/libs/SoloPlay/Modules/Events.js +++ b/libs/SoloPlay/Modules/Events.js @@ -26,7 +26,7 @@ }; // eslint-disable-next-line no-var var Events = /** @class */ (function () { - function Events() { + function Events () { } // Generic type S to give to EventHandler to typehint this function gets the same this as where the event is registered Events.prototype.on = function (key, handler, handlerType) { @@ -78,7 +78,13 @@ let onceSet = ((_a = onceHandlers.get(this)) === null || _a === void 0 ? void 0 : _a.get(key)); let restSet = ((_b = handlers.get(this)) === null || _b === void 0 ? void 0 : _b.get(key)); // store callbacks in a set to avoid duplicate handlers - let callbacks = __spreadArray(__spreadArray([], (onceSet && onceSet.splice(0, onceSet.length) || [])), restSet ? restSet : []); + let callbacks = __spreadArray( + __spreadArray( + [], + (onceSet && onceSet.splice(0, onceSet.length) || []) + ), + restSet ? restSet : [] + ); callbacks.forEach(function (el) { return el.apply(_this, args); }); From 41df06de3717b5c7f9ba656a366bc6244c734edc Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Sun, 9 Jul 2023 19:38:36 -0400 Subject: [PATCH 174/263] Update AutoMuleOverrides.js - can't mule on singleplayer --- libs/SoloPlay/Functions/AutoMuleOverrides.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/libs/SoloPlay/Functions/AutoMuleOverrides.js b/libs/SoloPlay/Functions/AutoMuleOverrides.js index 2d0df6ad..6f1a2333 100644 --- a/libs/SoloPlay/Functions/AutoMuleOverrides.js +++ b/libs/SoloPlay/Functions/AutoMuleOverrides.js @@ -43,6 +43,8 @@ AutoMule.matchItem = function (item, list) { }; AutoMule.getMuleItems = function () { + // can't mule on single player + if (!me.gameserverip) return []; let info = this.getInfo(); if (!info || !info.hasOwnProperty("muleInfo")) { From 9d93bf58c48b2d2ef06fd1078937a8503a40cd4e Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Sun, 9 Jul 2023 19:38:55 -0400 Subject: [PATCH 175/263] Update Globals.js - don't stash gold so often, tends to waste time --- libs/SoloPlay/Functions/Globals.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libs/SoloPlay/Functions/Globals.js b/libs/SoloPlay/Functions/Globals.js index ab23cae0..1b71b2f1 100644 --- a/libs/SoloPlay/Functions/Globals.js +++ b/libs/SoloPlay/Functions/Globals.js @@ -573,7 +573,7 @@ const SetUp = { Config.HealStatus = true; Config.UseMerc = me.expansion; Config.MercWatch = SetUp.mercwatch; - Config.StashGold = me.charlvl * 100; + Config.StashGold = me.charlvl * 1000; Config.ClearInvOnStart = false; /* Inventory buffers and lock configuration. */ From a3617a2a1ac0307bc5617249873841a416ea51b7 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Sun, 9 Jul 2023 19:40:58 -0400 Subject: [PATCH 176/263] Update LoaderOverrides.js - cleaner --- libs/SoloPlay/Functions/LoaderOverrides.js | 96 +++++++++++----------- 1 file changed, 48 insertions(+), 48 deletions(-) diff --git a/libs/SoloPlay/Functions/LoaderOverrides.js b/libs/SoloPlay/Functions/LoaderOverrides.js index 39b3de5d..d9da0627 100644 --- a/libs/SoloPlay/Functions/LoaderOverrides.js +++ b/libs/SoloPlay/Functions/LoaderOverrides.js @@ -37,6 +37,7 @@ Loader.loadScripts = function () { Loader.run = function () { let updatedDifficulty = Check.nextDifficulty(); + const _toolsThread = "libs/SoloPlay/Threads/ToolsThread.js"; if (updatedDifficulty) { CharData.updateData("me", "setDifficulty", updatedDifficulty); !me.realm && Messaging.sendToScript("D2BotSoloPlay.dbj", "diffChange"); @@ -46,63 +47,62 @@ Loader.run = function () { !me.inTown && Town.goToTown(); Check.checkSpecialCase(); const scriptName = SoloIndex.scripts[this.scriptIndex]; + if (!SoloIndex.index[scriptName]) continue; + if (!SoloIndex.index[scriptName].shouldRun()) continue; - if (SoloIndex.index[scriptName] !== undefined && SoloIndex.index[scriptName].shouldRun()) { - let j; - let tick; - let currentExp; + let j; + let tick; + let currentExp; - try { - includeIfNotIncluded("SoloPlay/Scripts/" + scriptName + ".js"); + try { + includeIfNotIncluded("SoloPlay/Scripts/" + scriptName + ".js"); - tick = getTickCount(); - currentExp = me.getStat(sdk.stats.Experience); - Messaging.sendToScript("libs/SoloPlay/Threads/ToolsThread.js", JSON.stringify({ currScript: scriptName })); - DataFile.updateStats("lastScript", scriptName); + tick = getTickCount(); + currentExp = me.getStat(sdk.stats.Experience); + Messaging.sendToScript(_toolsThread, JSON.stringify({ currScript: scriptName })); + DataFile.updateStats("lastScript", scriptName); - for (j = 0; j < 5; j += 1) { - if (global[scriptName]()) { - break; - } - } - - (j === 5) && myPrint("script " + scriptName + " failed."); - } catch (e) { - console.error(e); - // console.log("ÿc8Kolbot-SoloPlayÿc0: ", e); - } finally { - SoloIndex.doneList.push(scriptName); - // skip logging if we didn't actually finish it - if (!SoloIndex.retryList.includes(scriptName) && Developer.logPerformance) { - Tracker.script(tick, scriptName, currentExp); - } - console.log("ÿc8Kolbot-SoloPlayÿc0: Old maxgametime: " + Time.format(me.maxgametime)); - me.maxgametime += (getTickCount() - tick); - console.log("ÿc8Kolbot-SoloPlayÿc0: New maxgametime: " + Time.format(me.maxgametime)); - console.log( - "ÿc8Kolbot-SoloPlayÿc0 :: ÿc8" + scriptName - + "ÿc0 - ÿc7Duration: ÿc0" + Time.format(getTickCount() - tick) - ); - - // remove script function from function scope, so it can be cleared by GC - if (this.scriptIndex < SoloIndex.scripts.length) { - delete global[scriptName]; + for (j = 0; j < 5; j += 1) { + if (global[scriptName]()) { + break; } } - if (me.sorceress && me.hell && scriptName === "bloodraven" && me.charlvl < 68) { - console.info(false, "End-run, we are not ready to keep pushing yet"); - - break; + (j === 5) && myPrint("script " + scriptName + " failed."); + } catch (e) { + console.error(e); + } finally { + SoloIndex.doneList.push(scriptName); + // skip logging if we didn't actually finish it + if (!SoloIndex.retryList.includes(scriptName) && Developer.logPerformance) { + Tracker.script(tick, scriptName, currentExp); + } + console.log("ÿc8Kolbot-SoloPlayÿc0: Old maxgametime: " + Time.format(me.maxgametime)); + me.maxgametime += (getTickCount() - tick); + console.log("ÿc8Kolbot-SoloPlayÿc0: New maxgametime: " + Time.format(me.maxgametime)); + console.log( + "ÿc8Kolbot-SoloPlayÿc0 :: ÿc8" + scriptName + + "ÿc0 - ÿc7Duration: ÿc0" + Time.format(getTickCount() - tick) + ); + + // remove script function from function scope, so it can be cleared by GC + if (this.scriptIndex < SoloIndex.scripts.length) { + delete global[scriptName]; } + } - if (me.dead) { - // not sure how we got here but we are dead, why did toolsthread not quit lets check it - let tThread = getScript("libs/SoloPlay/Threads/ToolsThread.js"); - if (!tThread || !tThread.running) { - // well that explains why, toolsthread seems to have crashed lets restart it so we quit properly - load("libs/SoloPlay/Threads/ToolsThread.js"); - } + if (me.sorceress && me.hell && scriptName === "bloodraven" && me.charlvl < 68) { + console.info(false, "End-run, we are not ready to keep pushing yet"); + + break; + } + + if (me.dead) { + // not sure how we got here but we are dead, why did toolsthread not quit lets check it + let tThread = getScript("libs/SoloPlay/Threads/ToolsThread.js"); + if (!tThread || !tThread.running) { + // well that explains why, toolsthread seems to have crashed lets restart it so we quit properly + load("libs/SoloPlay/Threads/ToolsThread.js"); } } } From 459351bde97da3a419d09a54118d5d23f1902ed3 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Sun, 9 Jul 2023 19:49:36 -0400 Subject: [PATCH 177/263] Picking more often - Increases the amount the bot picks to help grab items faster and to prevent movement recursion - cleaned up the failure to move cases with PathAction.prototype.update - When we have to go to town to make room for an item, go through the rest of our list for items we don't need to make room for. This helps for gold and can help to skip running to shop to buy potions when there were potions on the groun next to us - add callback to clearPos when used during pickit. Sometimes we end up grabbnig the item and no longer need to keep clearing --- libs/SoloPlay/Functions/AttackOverrides.js | 497 ++++++++++++++++----- libs/SoloPlay/Functions/PatherOverrides.js | 128 ++++-- libs/SoloPlay/Functions/PickitOverrides.js | 66 +-- 3 files changed, 487 insertions(+), 204 deletions(-) diff --git a/libs/SoloPlay/Functions/AttackOverrides.js b/libs/SoloPlay/Functions/AttackOverrides.js index 4e28b200..c3f7aac1 100644 --- a/libs/SoloPlay/Functions/AttackOverrides.js +++ b/libs/SoloPlay/Functions/AttackOverrides.js @@ -55,9 +55,12 @@ Attack.decideSkill = function (unit) { const classid = unit.classid; // Get timed skill - let checkSkill = Attack.getCustomAttack(unit) ? Attack.getCustomAttack(unit)[0] : Config.AttackSkill[index]; + let checkSkill = Attack.getCustomAttack(unit) + ? Attack.getCustomAttack(unit)[0] + : Config.AttackSkill[index]; - if (Attack.checkResist(unit, checkSkill) && Attack.validSpot(unit.x, unit.y, checkSkill, classid)) { + if (Attack.checkResist(unit, checkSkill) + && Attack.validSpot(unit.x, unit.y, checkSkill, classid)) { skills.timed = checkSkill; } else if (Config.AttackSkill[5] > -1 && Attack.checkResist(unit, Config.AttackSkill[5]) @@ -70,7 +73,8 @@ Attack.decideSkill = function (unit) { ? Attack.getCustomAttack(unit)[1] : Config.AttackSkill[index + 1]; - if (Attack.checkResist(unit, checkSkill) && Attack.validSpot(unit.x, unit.y, checkSkill)) { + if (Attack.checkResist(unit, checkSkill) + && Attack.validSpot(unit.x, unit.y, checkSkill)) { skills.untimed = checkSkill; } else if (Config.AttackSkill[6] > -1 && Attack.checkResist(unit, Config.AttackSkill[6]) @@ -215,23 +219,60 @@ Attack.canAttack = function (unit) { */ Attack.openChests = function (range, x, y) { if (!Config.OpenChests.Enabled || !Misc.openChestsEnabled) return false; - range === undefined && (range = 10); + range === undefined && (range = 5); x === undefined && (x = me.x); y === undefined && (y = me.y); - if (me.getMobCount(range) > 1) return false; + /** @param {ObjectUnit} chest */ + const openChest = function (chest) { + // Skip invalid/open and Countess chests + if (!chest || chest.x === 12526 || chest.x === 12565 || chest.mode) return false; + // locked chest, no keys + if (!me.assassin && unit.islocked + && !me.findItem(sdk.items.Key, sdk.items.mode.inStorage, sdk.storage.Inventory)) { + return false; + } + if (Pather.getWalkDistance((chest.x + 1), (chest.y + 2)) > 10) { + // chest is too far away + console.debug("Chest too far away"); + return false; + } + if ([(chest.x + 1), (chest.y + 2)].distance > 5) { + Pather.walkTo(chest.x + 1, chest.y + 2, 3); + } + Packet.entityInteract(chest); - let list = []; - const ids = ["chest", "chest3", "weaponrack", "armorstand"]; + return Misc.poll(function () { + return !chest || chest.mode !== sdk.objects.mode.Inactive; + }, 300, 10); + }; + const containers = [ + "chest", "loose rock", "hidden stash", "loose boulder", + "corpseonstick", "casket", "armorstand", "weaponrack", + "holeanim", "roguecorpse", "corpse", "tomb2", "tomb3", "chest3", + "skeleton", "guardcorpse", "sarcophagus", "object2", + "cocoon", "hollow log", "hungskeleton", + "bonechest", "woodchestl", "woodchestr", + "burialchestr", "burialchestl", "chestl", + "chestr", "groundtomb", "tomb3l", "tomb1l", + "deadperson", "deadperson2", "groundtombl", "casket", "barrel", "ratnest", + "goo pile", "largeurn", "urn", "jug", "basket", "stash", + "pillar", "skullpile", "skull pile", + "jar3", "jar2", "jar1", "barrel wilderness", + "explodingchest", "icecavejar1", "icecavejar2", "icecavejar3", + "icecavejar4", "evilurn" + ]; + const list = []; let unit = Game.getObject(); if (unit) { do { if (unit.name - && getDistance(unit, x, y) <= range && unit.mode === sdk.objects.mode.Inactive - && ids.includes(unit.name.toLowerCase()) - && unit.getMobCount(10) === 0) { + && containers.includes(unit.name.toLowerCase()) + && getDistance(unit, x, y) <= range + && !checkCollision(me, unit, sdk.collision.BlockWalk) + /* && unit.getMobCount(10) === 0 */) { list.push(copyUnit(unit)); } } while (unit.getNext()); @@ -239,7 +280,7 @@ Attack.openChests = function (range, x, y) { while (list.length) { list.sort(Sort.units); - Misc.openChest(list.shift()) && Pickit.pickItems(); + openChest(list.shift()) && Pickit.pickItems(5); } return true; @@ -341,8 +382,8 @@ Attack.killTarget = function (name) { lastLoc = { x: me.x, y: me.y }; attackCount++; - if (target.dead || Config.FastPick || (attackCount > 0 && attackCount % 5 === 0)) { - Config.FastPick ? Pickit.fastPick() : Pickit.essessntialsPick(false, true); + if (target.dead || Config.FastPick || attackCount % 5 === 0) { + Config.FastPick ? Pickit.fastPick() : Pickit.essessntialsPick(false); } } @@ -380,48 +421,211 @@ Attack.clearLocations = function (list = []) { * @param {number} y * @param {number} range * @param {boolean} pickit + * @param {function(): boolean} [cb] * @returns {boolean} */ -Attack.clearPos = function (x, y, range = 15, pickit = true) { - while (!me.gameReady) { - delay(40); - } +Attack.clearPos = function (x, y, range = 15, pickit = true, cb = null) { + /** @type {PathNode} */ + const _startPos = { x: x, y: y }; + const _mobCount = _startPos.mobCount({ range: range }); + console.debug("ÿc7ClearPosÿc0 :: " + _mobCount + " monsters around " + _startPos.x + ", " + _startPos.y); + try { + if (typeof (range) !== "number") throw new Error("Attack.clear: range must be a number."); + if (Config.AttackSkill[1] < 0 || Config.AttackSkill[3] < 0 || Attack.stopClear || !x || !y) return false; + NodeAction.enabled = false; - if (typeof (range) !== "number") throw new Error("Attack.clear: range must be a number."); - if (Config.AttackSkill[1] < 0 || Config.AttackSkill[3] < 0 || Attack.stopClear || !x || !y) return false; + while (!me.gameReady) { + delay(40); + } - /** @type {Map} */ - let monsterList = []; - let start = false; - let [retry, attackCount] = [0, 0]; - let target = Game.getMonster(); + /** @type {Map} */ + const monsterList = []; + let start = false; + let [retry, attackCount] = [0, 0]; + let target = Game.getMonster(); + + if (target) { + do { + if (target.attackable && !this.skipCheck(target) && this.canAttack(target)) { + // Speed optimization - don't go through monster list until there's at least one within clear range + if (!start && getDistance(target, _startPos) <= range + && (Pather.useTeleport() || !checkCollision(me, target, sdk.collision.BlockWalk))) { + start = true; + } - if (target) { - do { - if (target.attackable && !this.skipCheck(target) && this.canAttack(target)) { - // Speed optimization - don't go through monster list until there's at least one within clear range - if (!start && getDistance(target, x, y) <= range - && (Pather.useTeleport() || !checkCollision(me, target, sdk.collision.WallOrRanged))) { - start = true; + monsterList.push(copyUnit(target)); } + } while (target.getNext()); + } - monsterList.push(copyUnit(target)); + while (start && monsterList.length > 0 && attackCount < 300) { + if (me.dead || Attack.stopClear) return false; + + monsterList.sort(this.sortMonsters); + target = copyUnit(monsterList[0]); + + if (target.x !== undefined + && (getDistance(target, _startPos) <= range + || (this.getScarinessLevel(target) > 7 && getDistance(me, target) <= range)) + && target.attackable) { + if (Config.Dodge && me.hpPercent <= Config.DodgeHP) { + this.deploy(target, Config.DodgeRange, 5, 9); + } + + let _currMon = attacks.get(target.gid); + const checkAttackSkill = (!!_currMon && _currMon.attacks > 0 && _currMon.attacks % 3 === 0); + const result = ClassAttack.doAttack(target, checkAttackSkill); + + if (result) { + retry = 0; + + if (result === this.Result.CANTATTACK) { + monsterList.shift(); + + continue; + } else if (result === this.Result.NEEDMANA) { + continue; + } + + if (!_currMon) { + _currMon = { attacks: 0, name: target.name }; + attacks.set(target.gid, _currMon); + } + + _currMon.attacks += 1; + attackCount += 1; + let skillCheck; + const isSpecial = target.isSpecial; + const secAttack = me.barbarian ? (isSpecial ? 2 : 4) : 5; + let checkSkill = Config.AttackSkill[isSpecial ? 1 : 3]; + let hammerCheck = me.paladin && checkSkill === sdk.skills.BlessedHammer; + + if (Config.AttackSkill[secAttack] > -1 && (!Attack.checkResist(target, checkSkill) + || (hammerCheck && !ClassAttack.getHammerPosition(target)))) { + skillCheck = Config.AttackSkill[secAttack]; + } else { + skillCheck = checkSkill; + } + + // Desync/bad position handler + switch (skillCheck) { + case sdk.skills.BlessedHammer: + // Tele in random direction with Blessed Hammer + if (_currMon.attacks > 0 && _currMon.attacks % (isSpecial ? 4 : 2) === 0) { + let coord = CollMap.getRandCoordinate(me.x, -1, 1, me.y, -1, 1, 5); + Pather.moveTo(coord.x, coord.y); + } + + break; + default: + // Flash with melee skills + if (_currMon.attacks > 0 && _currMon.attacks % (isSpecial ? 15 : 5) === 0 + && Skill.getRange(skillCheck) < 4) { + Packet.flash(me.gid); + } + + break; + } + + // Skip non-unique monsters after 15 attacks, except in Throne of Destruction + if (!me.inArea(sdk.areas.ThroneofDestruction) && !isSpecial && _currMon.attacks > 15) { + console.warn("ÿc1Skipping " + target.name + " " + target.gid + " " + _currMon.attacks); + monsterList.shift(); + } + + if (Config.FastPick || attackCount % 5 === 0) { + Config.FastPick + ? Pickit.fastPick() + : target.dead + ? Pickit.pickItems(6) + : Pickit.essessntialsPick(false); + } else { + Pickit.pickItems(4); + } + Attack.openChests(); + // if (_startPos.distance > 15) { + // Pather.move( + // _startPos, + // { allowNodeActions: false, allowClearing: false, minDist: 10 } + // ); + // } + if (typeof cb === "function" && cb()) { + break; + } + } else { + if (retry++ > 3) { + monsterList.shift(); + retry = 0; + } + + Packet.flash(me.gid); + } + } else { + monsterList.shift(); } - } while (target.getNext()); + } + + if (attackCount > 0) { + ClassAttack.afterAttack(pickit); + if (pickit) { + Attack.openChests(range, x, y); + Pickit.pickItems(); + } + } + if (Developer.debugging.pathing) { + console.debug("Returning to position " + x + "/" + y + " distance: " + [x, y].distance); + } + if (_startPos.distance > 15) { + Pather.move( + _startPos, + { allowNodeActions: false, allowClearing: false, minDist: 10 } + ); + } + // if ([x, y].distance > 5) { + // // todo - generate path and create hooks along it so we don't waste this movement time + // Pather.move({ x: x, y: y }, { allowClearing: false }); + // } + console.debug("Start count: " + _mobCount + " End count: " + _startPos.mobCount({ range: range })); + return _startPos.mobCount() < _mobCount; + } finally { + NodeAction.enabled = true; } +}; - while (start && monsterList.length > 0 && attackCount < 300) { - if (me.dead || Attack.stopClear) return false; +/** + * @description Clear an already formed array of monstas + * @param {Function | Array} mainArg + * @param {Function} [sortFunc] + * @param {boolean} [refresh] + * @returns {boolean} + */ +Attack.clearList = function (mainArg, sortFunc, refresh) { + if (typeof mainArg !== "function" && !Array.isArray(mainArg)) { + throw new Error("clearList: Invalid argument"); + } + /** @type {Map 0 && attackCount < Config.MaxAttackCount) { + if (me.dead) return false; - if (target.x !== undefined - && (getDistance(target, x, y) <= range - || (this.getScarinessLevel(target) > 7 && getDistance(me, target) <= range)) - && target.attackable) { + if (refresh && attackCount > 0 && attackCount % refresh === 0) { + monsterList = mainArg.call(); + } + + monsterList.sort(sortFunc); + let target = copyUnit(monsterList[0]); + + if (target.x !== undefined && target.attackable) { if (Config.Dodge && me.hpPercent <= Config.DodgeHP) { this.deploy(target, Config.DodgeRange, 5, 9); } @@ -447,17 +651,17 @@ Attack.clearPos = function (x, y, range = 15, pickit = true) { } _currMon.attacks += 1; - attackCount += 1; let skillCheck; const isSpecial = target.isSpecial; const secAttack = me.barbarian ? (isSpecial ? 2 : 4) : 5; + let checkSkill = Config.AttackSkill[isSpecial ? 1 : 3]; + let hammerCheck = me.paladin && checkSkill === sdk.skills.BlessedHammer; - if (Config.AttackSkill[secAttack] > -1 && (!Attack.checkResist(target, Config.AttackSkill[isSpecial ? 1 : 3]) - || (me.paladin && Config.AttackSkill[isSpecial ? 1 : 3] === sdk.skills.BlessedHammer - && !ClassAttack.getHammerPosition(target)))) { + if (Config.AttackSkill[secAttack] > -1 && (!Attack.checkResist(target, checkSkill) + || (hammerCheck && !ClassAttack.getHammerPosition(target)))) { skillCheck = Config.AttackSkill[secAttack]; } else { - skillCheck = Config.AttackSkill[isSpecial ? 1 : 3]; + skillCheck = checkSkill; } // Desync/bad position handler @@ -486,9 +690,18 @@ Attack.clearPos = function (x, y, range = 15, pickit = true) { monsterList.shift(); } - if (target.dead || Config.FastPick || attackCount % 5 === 0) { - Config.FastPick ? Pickit.fastPick() : Pickit.essessntialsPick(false, true); + attackCount += 1; + + if (Config.FastPick || attackCount % 5 === 0) { + Config.FastPick + ? Pickit.fastPick() + : target.dead + ? Pickit.pickItems(6) + : Pickit.essessntialsPick(false); + } else { + Pickit.pickItems(4); } + Attack.openChests(); } else { if (retry++ > 3) { monsterList.shift(); @@ -502,14 +715,12 @@ Attack.clearPos = function (x, y, range = 15, pickit = true) { } } - ClassAttack.afterAttack(pickit); - pickit && Attack.openChests(range, x, y); - attackCount > 0 && pickit && Pickit.pickItems(); - if ([x, y].distance > 5) { - if (Developer.debugging.pathing) { - console.debug("Returning to position " + x + "/" + y + " distance: " + [x, y].distance); - } - Pather.move({ x: x, y: y }, { allowClearing: false }); + if (attackCount > 0) { + ClassAttack.afterAttack(true); + this.openChests(Config.OpenChests.Range); + Pickit.pickItems(); + } else { + Precast.doPrecast(false); // we didn't attack anything but check if we need to precast. TODO: better method of keeping track of precast skills } return true; @@ -584,6 +795,8 @@ Attack.clearLevelUntilLevel = function (charlvl = undefined, spectype = 0) { !charlvl && (charlvl = me.charlvl + 1); let result, myRoom, previousArea; let rooms = []; + /** @type {PathNode} */ + let node = { x: null, y: null }; const currentArea = getArea().id; do { @@ -609,7 +822,8 @@ Attack.clearLevelUntilLevel = function (charlvl = undefined, spectype = 0) { result = Pather.getNearestWalkable(room[0], room[1], 18, 3); if (result) { - Pather.moveTo(result[0], result[1], 3, spectype); + node = { x: result[0], y: result[1] }; + Pather.move(node, { retry: 3, clearSettings: { spectype: spectype } }); previousArea = result; if (Attack.stopClear) { @@ -627,7 +841,8 @@ Attack.clearLevelUntilLevel = function (charlvl = undefined, spectype = 0) { } } else if (currentArea !== getArea().id) { // Make sure bot does not get stuck in different area. - Pather.moveTo(previousArea[0], previousArea[1], 3, spectype); + node = { x: previousArea[0], y: previousArea[1] }; + Pather.move(node, { retry: 3, clearSettings: { spectype: spectype } }); } } @@ -649,11 +864,12 @@ Attack.clearLevelUntilLevel = function (charlvl = undefined, spectype = 0) { * - should we stop clearing after boss is killed if we are using bossid? */ Attack.clear = function (range, spectype, bossId, sortfunc, pickit = true) { + if (Config.AttackSkill[1] < 0 || Config.AttackSkill[3] < 0 || Attack.stopClear) return false; + while (!me.gameReady) { delay(40); } - if (Config.AttackSkill[1] < 0 || Config.AttackSkill[3] < 0 || Attack.stopClear) return false; range === undefined && (range = 25); if (typeof (range) !== "number") throw new Error("Attack.clear: range must be a number."); spectype === undefined && (spectype = 0); @@ -691,7 +907,7 @@ Attack.clear = function (range, spectype, bossId, sortfunc, pickit = true) { ({ orgx, orgy } = { orgx: me.x, orgy: me.y }); } - let monsterList = []; + const monsterList = []; let target = Game.getMonster(); if (target) { @@ -699,7 +915,7 @@ Attack.clear = function (range, spectype, bossId, sortfunc, pickit = true) { if ((!spectype || (target.spectype & spectype)) && target.attackable && !this.skipCheck(target)) { // Speed optimization - don't go through monster list until there's at least one within clear range if (!start && getDistance(target, orgx, orgy) <= range - && (canTeleport || !checkCollision(me, target, sdk.collision.BlockWall))) { + && (canTeleport || !checkCollision(me, target, sdk.collision.BlockWalk))) { start = true; } @@ -718,11 +934,17 @@ Attack.clear = function (range, spectype, bossId, sortfunc, pickit = true) { boss && (({ orgx, orgy } = { orgx: boss.x, orgy: boss.y })); monsterList.sort(sortfunc); target = copyUnit(monsterList[0]); + + if ((target.x === undefined || !target.attackable) && monsterList.shift()) { + // console.debug("Attack.clear: Invalid target, skipping | ", target); + continue; + } - if (target.x !== undefined - && (getDistance(target, orgx, orgy) <= range - || (this.getScarinessLevel(target) > 7 && getDistance(me, target) <= range)) && target.attackable) { - Config.Dodge && me.hpPercent <= Config.DodgeHP && this.deploy(target, Config.DodgeRange, 5, 9); + if ((getDistance(target, orgx, orgy) <= range + || (this.getScarinessLevel(target) > 7 && target.distance <= range))) { + if (Config.Dodge && me.hpPercent <= Config.DodgeHP) { + this.deploy(target, Config.DodgeRange, 5, 9); + } tick = getTickCount(); if (!logged && boss && boss.gid === target.gid) { @@ -735,6 +957,7 @@ Attack.clear = function (range, spectype, bossId, sortfunc, pickit = true) { const result = ClassAttack.doAttack(target, checkAttackSkill); if (result) { + // console.debug("Attack.clear: " + result); retry = 0; if (result === this.Result.CANTATTACK) { @@ -754,13 +977,14 @@ Attack.clear = function (range, spectype, bossId, sortfunc, pickit = true) { attackCount += 1; let isSpecial = target.isSpecial; let secAttack = me.barbarian ? (isSpecial ? 2 : 4) : 5; + let checkSkill = Config.AttackSkill[isSpecial ? 1 : 3]; + let hammerCheck = me.paladin && checkSkill === sdk.skills.BlessedHammer; - if (Config.AttackSkill[secAttack] > -1 && (!Attack.checkResist(target, Config.AttackSkill[isSpecial ? 1 : 3]) - || (me.paladin && Config.AttackSkill[isSpecial ? 1 : 3] === sdk.skills.BlessedHammer - && !ClassAttack.getHammerPosition(target)))) { + if (Config.AttackSkill[secAttack] > -1 && (!Attack.checkResist(target, checkSkill) + || (hammerCheck && !ClassAttack.getHammerPosition(target)))) { skillCheck = Config.AttackSkill[secAttack]; } else { - skillCheck = Config.AttackSkill[isSpecial ? 1 : 3]; + skillCheck = checkSkill; } // Desync/bad position handler @@ -790,6 +1014,16 @@ Attack.clear = function (range, spectype, bossId, sortfunc, pickit = true) { } // we cleared this monster so subtract amount of attacks from current attack count in order to prevent pre-maturely ending clear + if (Config.FastPick || attackCount % 5 === 0) { + Config.FastPick + ? Pickit.fastPick() + : target.dead + ? Pickit.pickItems(6) + : Pickit.essessntialsPick(false); + } else { + Pickit.pickItems(4); + } + Attack.openChests(4); if (target.dead) { clearResult = Attack.Result.SUCCESS; if (boss && boss.gid === target.gid) { @@ -801,9 +1035,6 @@ Attack.clear = function (range, spectype, bossId, sortfunc, pickit = true) { } attackCount -= _currMon.attacks; } - if (target.dead || Config.FastPick || attackCount % 5 === 0) { - Config.FastPick ? Pickit.fastPick() : Pickit.essessntialsPick(false, true); - } } else { if (Coords_1.isBlockedBetween(me, target)) { let collCheck = Coords_1.getCollisionBetweenCoords(me.x, me.y, target.x, target.y); @@ -811,6 +1042,8 @@ Attack.clear = function (range, spectype, bossId, sortfunc, pickit = true) { console.log("ÿc1Skipping " + target.name + " because they are blocked. Collision: " + collCheck.toString(16)); monsterList.shift(); retry = 0; + + continue; } } @@ -826,9 +1059,11 @@ Attack.clear = function (range, spectype, bossId, sortfunc, pickit = true) { } } - ClassAttack.afterAttack(pickit); - this.openChests(range, orgx, orgy); - attackCount > 0 && pickit && Pickit.pickItems(); + if (attackCount > 0) { + ClassAttack.afterAttack(pickit); + this.openChests(range, orgx, orgy); + pickit && Pickit.pickItems(); + } if (boss && !killedBoss) { // check if boss corpse is around @@ -927,7 +1162,7 @@ Attack.getCurrentChargedSkillIds = function (init = false) { // Item must be equipped - removed charms as I don't think at any point using hydra from torch has ever been worth it me.getItemsEx(-1) - .filter(item => item && ((item.isEquipped /* && !item.rare */))) + .filter(item => item && ((item.isEquipped && !item.rare))) .forEach(function (item) { let stats = item.getStat(-2); if (!stats.hasOwnProperty(sdk.stats.ChargedSkill)) return; @@ -975,36 +1210,30 @@ Attack.getCurrentChargedSkillIds = function (init = false) { Attack.getItemCharges = function (skillId) { if (!skillId) return false; - let chargedItems = []; + /** @param {Charge} itemCharge */ let validCharge = function (itemCharge) { return itemCharge.skill === skillId && itemCharge.charges > 1; }; + + let items = me.getItemsEx() + .filter(function (item) { + return item && item.isEquipped && !item.rare; + }); + for (let item of items) { + let stats = item.getStat(-2); + if (!stats.hasOwnProperty(sdk.stats.ChargedSkill)) continue; - // Item must equipped, or a ~charm in inventory~ removed charms as I don't think at any point using hydra from torch has ever been worth it - me.getItemsEx(-1) - .filter(item => item && (item.isEquipped && !item.rare)) - .forEach(function (item) { - let stats = item.getStat(-2); - if (!stats.hasOwnProperty(sdk.stats.ChargedSkill)) return; - - /** @type {Array | Charge} */ - let charges = stats[sdk.stats.ChargedSkill]; - // simplfy calc by making it an array if it isn't already - if (!(charges instanceof Array)) charges = [charges]; + /** @type {Array | Charge} */ + let charges = stats[sdk.stats.ChargedSkill]; + // simplfy calc by making it an array if it isn't already + if (!(charges instanceof Array)) charges = [charges]; - for (let charge of charges) { - if (!charge) continue; - if (validCharge(charge)) { - chargedItems.push({ - skill: charge.skill, - charge: charge.charges, - item: item - }); - } - } - }); + for (let charge of charges) { + if (validCharge(charge)) return true; + } + } - return !!(chargedItems.length > 0); + return false; }; /** @@ -1101,21 +1330,31 @@ Attack.shouldDodge = function (coord, monster) { }); }; -new Overrides.Override(Attack, Attack.sortMonsters, function (orignal, unitA, unitB) { - /** @param {Monster} m */ - const stateCheck = function (m) { - return [sdk.states.Fanaticism, sdk.states.Conviction] - .some(function (state) { - return m.getState(state); - }); - }; - if ((unitA.isSpecial && stateCheck(unitA)) && (unitB.isSpecial && stateCheck(unitB))) { - return getDistance(me, unitA) - getDistance(me, unitB); +new Overrides.Override(Attack, + Attack.sortMonsters, + /** + * @override + * @param {(a: Monster, b: Monster) => number} orignal + * @param {Monster} unitA + * @param {Monster} unitB + * @returns {number} + */ + function (orignal, unitA, unitB) { + /** @param {Monster} m */ + const stateCheck = function (m) { + return [sdk.states.Fanaticism, sdk.states.Conviction] + .some(function (state) { + return m.getState(state); + }); + }; + if ((unitA.isSpecial && stateCheck(unitA)) && (unitB.isSpecial && stateCheck(unitB))) { + return getDistance(me, unitA) - getDistance(me, unitB); + } + if (unitA.isSpecial && stateCheck(unitA)) return -1; + if (unitB.isSpecial && stateCheck(unitB)) return 1; + return orignal(unitA, unitB); } - if (unitA.isSpecial && stateCheck(unitA)) return -1; - if (unitB.isSpecial && stateCheck(unitB)) return 1; - return orignal(unitA, unitB); -}).apply(); +).apply(); /** * @param {Monster} unitA @@ -1131,11 +1370,12 @@ Attack.walkingSortMonsters = function (unitA, unitB) { // Barb optimization if (me.barbarian) { - if (!Attack.checkResist(unitA, Attack.getSkillElement(Config.AttackSkill[(unitA.isSpecial) ? 1 : 3]))) { + let skillElm = Attack.getSkillElement(Config.AttackSkill[(unitA.isSpecial) ? 1 : 3]); + if (!Attack.checkResist(unitA, skillElm)) { return 1; } - if (!Attack.checkResist(unitB, Attack.getSkillElement(Config.AttackSkill[(unitB.isSpecial) ? 1 : 3]))) { + if (!Attack.checkResist(unitB, skillElm)) { return -1; } } @@ -1160,7 +1400,9 @@ Attack.walkingSortMonsters = function (unitA, unitB) { sdk.monsters.CloudStalkerNest, sdk.monsters.FeederNest, sdk.monsters.SuckerNest ]; - if (!me.inArea(sdk.areas.ClawViperTempleLvl2) && ids.includes(unitA.classid) && ids.includes(unitB.classid)) { + if (!me.inArea(sdk.areas.ClawViperTempleLvl2) + && ids.includes(unitA.classid) + && ids.includes(unitB.classid)) { // Kill "scary" uniques first (like Bishibosh) if ((unitA.isUnique) && (unitB.isUnique)) { return getDistance(me, unitA) - getDistance(me, unitB); @@ -1439,9 +1681,15 @@ Attack.pwnAncients = function () { // @todo fillout }; +/** + * @param {Monster} unit + * @param {number} distance + * @param {number} spread + * @param {number} range + * @returns {boolean} + */ Attack.deploy = function (unit, distance = 10, spread = 5, range = 9) { - if (arguments.length < 4) throw new Error("deploy: Not enough arguments supplied"); - + !unit && (unit = me); let index, currCount; let count = 999; let monList = (this.buildMonsterList() || []).sort(Sort.units); @@ -1456,6 +1704,11 @@ Attack.deploy = function (unit, distance = 10, spread = 5, range = 9) { return getDistance(b.x, b.y, unit.x, unit.y) - getDistance(a.x, a.y, unit.x, unit.y); }); + let lines = new WeakMap(); + for (let { x, y } of grid) { + lines.set(new Line(x + 1, y + 1, x, y, 0x62, true)); + } + for (let i = 0; i < grid.length; i += 1) { if (!(CollMap.getColl(grid[i].x, grid[i].y, true) & sdk.collision.BlockWall) && !CollMap.checkColl(unit, { x: grid[i].x, y: grid[i].y }, sdk.collision.Ranged)) { @@ -1473,7 +1726,7 @@ Attack.deploy = function (unit, distance = 10, spread = 5, range = 9) { } return typeof index === "number" - ? Pather.move(grid[index], { allowClearing: false }) + ? Pather.move(grid[index], { allowNodeActions: false }) : false; }; diff --git a/libs/SoloPlay/Functions/PatherOverrides.js b/libs/SoloPlay/Functions/PatherOverrides.js index 8eea141a..ca21e8d0 100644 --- a/libs/SoloPlay/Functions/PatherOverrides.js +++ b/libs/SoloPlay/Functions/PatherOverrides.js @@ -156,10 +156,17 @@ NodeAction.pickItems = function (arg = {}) { const itemDist = getDistance(me, item); if (itemDist > maxRange) continue; if (totalList.some(el => el.gid === item.gid)) continue; - if (Pickit.essentials.includes(item.itemType)) { - if (itemDist <= maxDist && (item.itemType !== sdk.items.type.Gold || itemDist < 5) - && Pickit.checkItem(item).result && Pickit.canPick(item) && Pickit.canFit(item)) { - Pickit.essentialList.push(copyUnit(item)); + if (item.itemType === sdk.items.type.Gold && Pickit.canPick(item)) { + itemDist < 5 + ? Pickit.pickItem(item) + : Pickit.essentialList.push(copyUnit(item)); + } else if (Pickit.essentials.includes(item.itemType)) { + if (itemDist <= maxDist) { + if (Pickit.checkItem(item).result && Pickit.canPick(item) && Pickit.canFit(item)) { + itemDist < 5 + ? Pickit.pickItem(item) + : Pickit.essentialList.push(copyUnit(item)); + } } } else if (itemDist <= regPickRange && item.itemType === sdk.items.type.Key) { if (Pickit.canPick(item) && Pickit.checkItem(item).result) { @@ -173,7 +180,7 @@ NodeAction.pickItems = function (arg = {}) { Pickit.essentialList.length > 0 && (Pickit.essentialList = Pickit.essentialList.filter(filterJunk)); Pickit.pickList.length > 0 && (Pickit.pickList = Pickit.pickList.filter(filterJunk)); - Pickit.essentialList.length > 0 && Pickit.essessntialsPick(false, false); + Pickit.essentialList.length > 0 && Pickit.essessntialsPick(false); Pickit.pickList.length > 0 && Pickit.pickItems(regPickRange); } }; @@ -405,7 +412,7 @@ Pather.currentWalkingPath = []; /** * @param {PathNode | Unit | PresetUnit} target - * @param {pathSettings} givenSettings + * @param {PathSettings} givenSettings * @returns {boolean} */ Pather.move = function (target, givenSettings = {}) { @@ -433,8 +440,12 @@ Pather.move = function (target, givenSettings = {}) { const clearSettings = Object.assign({ canTele: false, clearPath: false, - range: typeof Config.ClearPath.Range === "number" ? Config.ClearPath.Range : 10, - specType: typeof Config.ClearPath.Spectype === "number" ? Config.ClearPath.Spectype : 0, + range: ( + typeof Config.ClearPath.Range === "number" ? Config.ClearPath.Range : 10 + ), + specType: ( + typeof Config.ClearPath.Spectype === "number" ? Config.ClearPath.Spectype : 0 + ), sort: Attack.sortMonsters, }, settings.clearSettings); // set settings.clearSettings equal to the now properly asssigned clearSettings @@ -459,6 +470,13 @@ Pather.move = function (target, givenSettings = {}) { this.node = { x: null, y: null }; } + /** @param {PathNode} node */ + PathAction.prototype.update = function (node) { + this.at = getTickCount(); + this.node.x = node.x; + this.node.y = node.y; + }; + let fail = 0; let invalidCheck = false; let cbCheck = false; @@ -467,11 +485,12 @@ Pather.move = function (target, givenSettings = {}) { const whirled = new PathAction(); const cleared = new PathAction(); const teleported = new PathAction(); + const picked = new PathAction(); Pather.clearUIFlags(); if (typeof target.x !== "number" || typeof target.y !== "number") return false; - if (getDistance(me, target) < 2 && !CollMap.checkColl(me, target, Coords_1.Collision.BLOCK_MISSILE, 5)) { + if (target.distance < 2 && !CollMap.checkColl(me, target, sdk.collision.BlockMissile, 5)) { return true; } @@ -525,15 +544,21 @@ Pather.move = function (target, givenSettings = {}) { path.reverse(); settings.pop && path.pop(); PathDebug.drawPath(path); - useTeleport && Config.TeleSwitch && path.length > 5 && me.switchToPrimary(); + if (useTeleport && Config.TeleSwitch && path.length > 5) { + me.switchWeapons(Attack.getPrimarySlot() ^ 1); + } while (path.length > 0) { // Abort if dead if (me.dead) return false; // main path - Pather.recursion && (Pather.currentWalkingPath = path); + if (Pather.recursion) { + Pather.currentWalkingPath = path; + PathDebug.drawPath(Pather.currentWalkingPath); + } Pather.clearUIFlags(); + /** @type {PathNode} */ node = path.shift(); if (typeof settings.callback === "function" && settings.callback()) { @@ -623,40 +648,23 @@ Pather.move = function (target, givenSettings = {}) { } finally { Pather.recursion = true; } + } else { + if (!me.inTown && settings.allowPicking) { + if (picked.node.distance > 10) { + Pickit.essessntialsPick(false); + picked.update(node); + } + } } } } else { if (!me.inTown) { - if (!useTeleport && settings.allowClearing) { - let tempRange = (annoyingArea ? 5 : 10); - // allowed to clear so lets see if any mobs are around us - if (me.checkForMobs({ range: tempRange, coll: sdk.collision.BlockWalk })) { - // there are at least some, but lets only continue to next iteration if we actually killed something - if (Attack.clear(tempRange, null, null, null, settings.allowPicking) === Attack.Result.SUCCESS) { - // console.debug("Cleared Node"); - continue; - } - } - } if (!useTeleport && (Pather.openDoors(node.x, node.y) || Pather.kickBarrels(node.x, node.y))) { + console.debug("Failed to walk to node, but opened door/barrel"); continue; } - if (fail > 0 && (!useTeleport || tpMana > me.mp)) { - // if we are allowed to clear - if (settings.allowClearing) { - // Don't go berserk on longer paths - also check that there are even mobs blocking us - if (cleared.at === 0 || getTickCount() - cleared.at > Time.seconds(3) - && cleared.node.distance > 5 && me.checkForMobs({ range: 10 })) { - // only set that we cleared if we actually killed at least 1 mob - if (Attack.clear(10, null, null, null, settings.allowPicking) === Attack.Result.SUCCESS) { - // console.debug("Cleared Node"); - cleared.at = getTickCount(); - [cleared.node.x, cleared.node.y] = [node.x, node.y]; - } - } - } - + if (/* fail > 0 && */(!useTeleport || tpMana > me.mp)) { // Leap can be helpful on long paths but make sure we don't spam it if (Skill.canUse(sdk.skills.LeapAttack)) { // we can use leapAttack, now lets see if we should - either haven't used it yet @@ -665,8 +673,8 @@ Pather.move = function (target, givenSettings = {}) { || leaped.node.distance > 5 || me.checkForMobs({ range: 6 })) { // alright now if we have actually casted it set the values so we know if (Skill.cast(sdk.skills.LeapAttack, sdk.skills.hand.Right, node.x, node.y)) { - leaped.at = getTickCount(); - [leaped.node.x, leaped.node.y] = [node.x, node.y]; + leaped.update(node); + if (node.distance < 5) continue; // sucessfully cleared obstacle } } } @@ -684,8 +692,8 @@ Pather.move = function (target, givenSettings = {}) { || whirled.node.distance > 5 || me.checkForMobs({ range: 6 })) { // alright now if we have actually casted it set the values so we know if (Skill.cast(sdk.skills.Whirlwind, sdk.skills.hand.Right, node.x, node.y)) { - whirled.at = getTickCount(); - [whirled.node.x, whirled.node.y] = [node.x, node.y]; + whirled.update(node); + if (node.distance < 5) continue; // sucessfully cleared obstacle } } } @@ -695,8 +703,26 @@ Pather.move = function (target, givenSettings = {}) { || teleported.node.distance > 5 || me.checkForMobs({ range: 6 })) { // alright now if we have actually casted it set the values so we know if (useTeleport ? Pather.teleportTo(node.x, node.y) : Pather.teleUsingCharges(node.x, node.y)) { - teleported.at = getTickCount(); - [teleported.node.x, teleported.node.y] = [node.x, node.y]; + teleported.update(node); + if (node.distance < 5) continue; // sucessfully cleared obstacle + } + } + } + + // if we are allowed to clear + if (settings.allowClearing) { + // Don't go berserk on longer paths - also check that there are even mobs blocking us + if (cleared.at === 0 || getTickCount() - cleared.at > Time.seconds(3) + && cleared.node.distance > 5 && me.checkForMobs({ range: 10 })) { + // only set that we cleared if we actually killed at least 1 mob + if (Attack.clearPos( + node.x, node.y, 10, + settings.allowPicking, + function () { + return node.distance < 5; + })) { + cleared.update(node); + if (node.distance < 5) continue; // sucessfully cleared obstacle } } } @@ -738,7 +764,7 @@ Pather.move = function (target, givenSettings = {}) { delay(5); } - useTeleport && Config.TeleSwitch && me.switchToPrimary(); + me.switchToPrimary(); PathDebug.removeHooks(); return cbCheck || getDistance(me, node.x, node.y) < 5; @@ -786,9 +812,6 @@ Pather.moveToExit = function (targetArea, use, givenSettings = {}) { for (let currTarget of areas) { console.info(null, getAreaName(me.area) + "ÿc8 --> ÿc0" + getAreaName(currTarget)); - // const area = Misc.poll(() => getArea(me.area)); - // if (!area) throw new Error("moveToExit: error in getArea()"); - /** @type {Array} */ const exits = AreaData.get(me.area).getExits(); // const exits = (area.exits || []); @@ -1063,7 +1086,9 @@ Pather.clearToExit = function (currentarea, targetarea, givenSettings = {}) { } delay(500); - Misc.poll(() => me.gameReady, 1000, 100); + Misc.poll(function () { + return me.gameReady; + }, 1000, 100); if (retry > 5) { console.error("ÿc2Failed to move to: ÿc0" + targetName); @@ -1078,7 +1103,12 @@ Pather.clearToExit = function (currentarea, targetarea, givenSettings = {}) { return (me.area === targetarea); }; -Pather.getWalkDistance = function (x, y, area = me.area, xx = me.x, yy = me.y, reductionType = 2, radius = 5) { +Pather.getWalkDistance = function (x, y, area, xx, yy, reductionType, radius) { + area === undefined && (area = me.area); + xx === undefined && (xx = me.x); + yy === undefined && (yy = me.y); + reductionType === undefined && (reductionType = 2); + radius === undefined && (radius = 5); // distance between node x and x-1 return (getPath(area, x, y, xx, yy, reductionType, radius) || []) .map(function (e, i, s) { diff --git a/libs/SoloPlay/Functions/PickitOverrides.js b/libs/SoloPlay/Functions/PickitOverrides.js index 5924924e..19793353 100644 --- a/libs/SoloPlay/Functions/PickitOverrides.js +++ b/libs/SoloPlay/Functions/PickitOverrides.js @@ -396,9 +396,14 @@ const _toCursorPick = new Set(); * @param {ItemUnit} unit * @param {PickitResult} status * @param {string} keptLine - * @param {boolean} clearBeforePick + * @param {{ allowClear: boolean, allowMove: boolean }} givenSettings */ -Pickit.pickItem = function (unit, status, keptLine, clearBeforePick = true) { +Pickit.pickItem = function (unit, status, keptLine, givenSettings) { + if (!unit || unit === undefined) return false; + const _pickSettings = Object.assign({ + allowClear: true, + allowMove: true + }, givenSettings); /** * @constructor * @param {ItemUnit} unit @@ -469,18 +474,23 @@ Pickit.pickItem = function (unit, status, keptLine, clearBeforePick = true) { } else { let checkItem = false; const maxDist = (Config.FastPick || i < 1) ? 8 : 5; - if (item.distance > maxDist || checkCollision(me, item, sdk.collision.BlockWall)) { + if (_pickSettings.allowMove + && item.distance > maxDist || checkCollision(me, item, sdk.collision.BlockWall)) { let coll = (sdk.collision.BlockWall | sdk.collision.Objects | sdk.collision.ClosedDoor); - if (!clearBeforePick && me.checkForMobs({ range: 5, coll: coll })) { + if (!_pickSettings.allowClear && me.checkForMobs({ range: 5, coll: coll })) { continue; } - if (clearBeforePick && item.checkForMobs({ range: 8, coll: coll })) { + if (_pickSettings.allowClear && item.checkForMobs({ range: 8, coll: coll })) { try { console.log("ÿc8PickItemÿc0 :: Clearing area around item I want to pick"); Pickit.enabled = false; // Don't pick while trying to clear - Attack.clearPos(item.x, item.y, 10, false); + Attack.clearPos(item.x, item.y, 10, false, function () { + if (!copyUnit(item).x) return true; + if (!item.onGroundOrDropping || me.getItem(-1, -1, gid)) return true; + return !item.checkForMobs({ range: 8, coll: coll }); + }); } finally { Pickit.enabled = true; // Reset value } @@ -648,11 +658,12 @@ Pickit.checkSpotForItems = function (spot, checkVsMyDist = false, range = Config return itemList.length > 3; }; +/** @type {ItemUnit[]} */ Pickit.pickList = []; Pickit.essentialList = []; // Might need to do a global list so this function and pickItems see the same items to prevent an item from being in both -Pickit.essessntialsPick = function (clearBeforePick = false, ignoreGold = false, builtList = [], once = false) { +Pickit.essessntialsPick = function (clearBeforePick = false, builtList = [], once = false) { if (me.dead || me.inTown || (!Pickit.enabled && !clearBeforePick)) return false; Pickit.essentialList @@ -701,33 +712,14 @@ Pickit.essessntialsPick = function (clearBeforePick = false, ignoreGold = false, me.fieldID() && (canFit = (currItem.gid !== undefined && Storage.Inventory.CanFit(currItem))); } - // Try to make room by selling items in town - if (!canFit) { - // Check if any of the current inventory items can be stashed or need to be identified and eventually sold to make room - if (this.canMakeRoom()) { - console.log("ÿc7Trying to make room for " + Item.color(currItem) + currItem.name); - - // Go to town and do town chores - if (Town.visitTown()) { - // Recursive check after going to town. We need to remake item list because gids can change. - // Called only if room can be made so it shouldn't error out or block anything. - return this.essessntialsPick(clearBeforePick, ignoreGold, builtList, once); - } - - // Town visit failed - abort - console.log("ÿc7Unable to make room for " + Item.color(currItem) + currItem.name); - - return false; - } - - // Can't make room - Item.logger("No room for", currItem); - console.log("ÿc7Not enough room for " + Item.color(currItem) + currItem.name); - } - // Item can fit - pick it up if (canFit) { - let picked = this.pickItem(currItem, status.result, status.line, clearBeforePick); + let picked = this.pickItem( + currItem, + status.result, + status.line + "(essentials)", + { allowClear: clearBeforePick } + ); if (picked && once) return true; } } @@ -811,7 +803,15 @@ Pickit.pickItems = function (range = Config.PickRange, once = false) { if (!canFit) { // Check if any of the current inventory items can be stashed or need to be identified and eventually sold to make room if (this.canMakeRoom()) { - console.log("ÿc7Trying to make room for " + Item.color(currItem) + currItem.name); + // if we are going to have to go to town anyway then grab what is near us + Pickit.pickList + .filter(function (el) { + return el.distance <= 5 && (Storage.Inventory.CanFit(el) || Pickit.canFit(el)); + }).forEach(function (el) { + let _result = Pickit.checkItem(el); + return Pickit.pickItem(el, _result.result, _result.line + "(quick)", { allowMove: false }); + }); + console.log("ÿc7Trying to make room for " + Item.color(_item) + _item.name); /** * @todo From c35f03b5ba0ffe430f86297af0b39208c6294f6c Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Sun, 9 Jul 2023 19:51:20 -0400 Subject: [PATCH 178/263] Update ItemOverrides.js - mostly cleanup - fixed typo for merc, was using canEquip instead of canEquipMerc - typo for checking FinalGear - add secondary tier value to log output --- libs/SoloPlay/Functions/ItemOverrides.js | 69 +++++++++++++----------- 1 file changed, 38 insertions(+), 31 deletions(-) diff --git a/libs/SoloPlay/Functions/ItemOverrides.js b/libs/SoloPlay/Functions/ItemOverrides.js index dd715302..508ce1ef 100644 --- a/libs/SoloPlay/Functions/ItemOverrides.js +++ b/libs/SoloPlay/Functions/ItemOverrides.js @@ -71,7 +71,7 @@ Item.identify = function (item) { if (idTool) { item.isInStash && Town.openStash(); - return Town.identifyItem(item, idTool); + return Packet.identifyItem(item, idTool); } return false; }; @@ -116,7 +116,12 @@ Item.getBodyLoc = function (item) { */ Item.canEquip = function (item) { if (!item || item.type !== sdk.unittype.Item || !item.identified) return false; - return me.charlvl >= item.getStat(sdk.stats.LevelReq) && me.trueStr >= item.strreq && me.trueDex >= item.dexreq; + return ( + me.charlvl >= item.lvlreq + && me.trueStr >= item.strreq + && me.trueDex >= item.dexreq + && me.classid === item.charclass + ); }; /** @@ -144,7 +149,9 @@ Item.autoEquipCheck = function (item, basicCheck = false) { } else if (tier > equippedItem.tier && (basicCheck ? true : this.canEquip(item) || !item.identified)) { if (Item.canEquip(item)) { if (item.twoHanded && !me.barbarian) { - if (tier < me.equipped.get(sdk.body.RightArm).tier + me.equipped.get(sdk.body.LeftArm).tier) return false; + if (tier < me.equipped.get(sdk.body.RightArm).tier + me.equipped.get(sdk.body.LeftArm).tier) { + return false; + } } if (!me.barbarian && loc === sdk.body.LeftArm && me.equipped.get(loc).tier === -1) { @@ -175,12 +182,12 @@ Item.autoEquipCheck = function (item, basicCheck = false) { return !!betterItem; }; // keep wanted final gear items - if (NTIP.CheckItem(item, NTIP.FinalGear.list) === Pickit.Result.WANTED) { + if (NTIP.CheckItem(item, NTIP.FinalGear) === Pickit.Result.WANTED) { // don't horde items we can't equip return !checkForBetterItem(item); } - let [lvlReq, strReq, dexReq] = [item.getStat(sdk.stats.LevelReq), item.strreq, item.dexreq]; + let [lvlReq, strReq, dexReq] = [item.lvlreq, item.strreq, item.dexreq]; // todo - bit hacky, better way would be to track what stats are going to be allocated next if ((lvlReq - me.charlvl > 5) || (strReq - me.trueStr > 10) || (dexReq - me.trueDex > 10)) { @@ -307,9 +314,9 @@ Item.autoEquip = function (task = "") { for (let loc of bodyLoc) { const equippedItem = me.equipped.get(loc); if (equippedItem.classid === sdk.items.quest.KhalimsWill) continue; + if (!item.identified && !Item.identify(item)) continue; // rings are special if (item.itemType === sdk.items.type.Ring) { - Item.identify(item); // have to pass in the specific location tier = tierscore(item, 1, loc); @@ -323,8 +330,7 @@ Item.autoEquip = function (task = "") { } else { if (tier > equippedItem.tier) { console.debug("EquippedItem :: " + equippedItem.prettyPrint + " |ÿc0 Tier: " + equippedItem.tier); - Item.identify(item); - + if (item.twoHanded && !me.barbarian) { if (tier < me.equipped.get(sdk.body.RightArm).tier + me.equipped.get(sdk.body.LeftArm).tier) { continue; @@ -364,7 +370,7 @@ Item.equip = function (item, bodyLoc) { if (!this.canEquip(item)) return false; // Already equipped in the right slot - if (item.mode === sdk.items.mode.Equipped && item.bodylocation === bodyLoc) return true; + if (item.isEquipped && item.bodylocation === bodyLoc) return true; // failed to open stash if (item.isInStash && !Town.openStash()) return false; // failed to open cube @@ -541,10 +547,6 @@ Item.secondaryEquip = function (item, bodyLoc) { !!cursorItem && !cursorItem.shouldKeep() && cursorItem.drop(); } - if (Item.hasDependancy(item) && me.needRepair() && me.inTown) { - NPCAction.repair(true); - } - return true; } } @@ -568,7 +570,8 @@ Item.autoEquipCheckSecondary = function (item) { let bodyLoc = Item.getSecondaryBodyLoc(item); for (let loc of bodyLoc) { - if (tier > me.equipped.get(loc).secondaryTier && (Item.canEquip(item) || !item.identified)) { + if (tier > me.equipped.get(loc).secondaryTier + && (Item.canEquip(item) || !item.identified)) { return true; } } @@ -659,7 +662,7 @@ Item.canEquipMerc = function (item, bodyLoc) { let curr = Item.getMercEquipped(bodyLoc); // Higher requirements - if (item.getStat(sdk.stats.LevelReq) > mercenary.getStat(sdk.stats.Level) + if (item.lvlreq > mercenary.getStat(sdk.stats.Level) || item.dexreq > mercenary.getStat(sdk.stats.Dexterity) - curr.dex || item.strreq > mercenary.getStat(sdk.stats.Strength) - curr.str) { return false; @@ -801,7 +804,7 @@ Item.autoEquipCheckMerc = function (item, basicCheck = false) { return true; } else if (basicCheck) { // keep wanted final gear items - if (NTIP.CheckItem(item, NTIP.FinalGear.list) === Pickit.Result.WANTED) { + if (NTIP.CheckItem(item, NTIP.FinalGear) === Pickit.Result.WANTED) { return true; } @@ -825,7 +828,7 @@ Item.autoEquipMerc = function () { if (!items.length) return false; function sortEq (a, b) { - let [prioA, prioB] = [Item.canEquip(a), Item.canEquip(b)]; + let [prioA, prioB] = [Item.canEquipMerc(a), Item.canEquipMerc(b)]; if (prioA && prioB) return NTIP.GetMercTier(b) - NTIP.GetMercTier(a); if (prioA) return -1; if (prioB) return 1; @@ -843,7 +846,7 @@ Item.autoEquipMerc = function () { const name = item.name; for (let loc of bodyLoc) { - if ([sdk.storage.Inventory, sdk.storage.Stash].includes(item.location) && tier > Item.getMercEquipped(loc).tier) { + if (item.isInStorage && tier > Item.getMercEquipped(loc).tier) { Item.identify(item); console.log("Merc " + name); @@ -868,11 +871,12 @@ Item.removeItemsMerc = function () { let mercenary = me.getMercEx(); if (!mercenary) return true; // Sort items so we try to keep the highest tier'd items in case space in our invo is limited - let items = mercenary.getItemsEx().sort((a, b) => NTIP.GetMercTier(b) - NTIP.GetMercTier(a)); - - if (items) { - for (let i = 0; i < items.length; i++) { - clickItem(sdk.clicktypes.click.item.Mercenary, items[i].bodylocation); + mercenary.getItemsEx() + .sort(function (a, b) { + return NTIP.GetMercTier(b) - NTIP.GetMercTier(a); + }) + .forEach(function (item) { + clickItem(sdk.clicktypes.click.item.Mercenary, item.bodylocation); delay(500 + me.ping * 2); let cursorItem = Game.getCursorUnit(); @@ -884,10 +888,8 @@ Item.removeItemsMerc = function () { cursorItem.drop(); } } - } - } - - return !!mercenary.getItem(); + }); + return mercenary.getItemsEx().length === 0; }; /** @@ -907,8 +909,8 @@ Item.logItem = function (action, unit, keptLine, force) { if (!Config.LogLowGems && ["gcv", "gcy", "gcb", "gcg", "gcr", "gcw", "skc", "gfv", "gfy", "gfb", "gfg", "gfr", "gfw", "skf", "gsv", "gsy", "gsb", "gsg", "gsr", "gsw", "sku"].includes(unit.code)) return false; if (!Config.LogHighGems && ["gzv", "gly", "glb", "glg", "glr", "glw", "skl", "gpv", "gpy", "gpb", "gpg", "gpr", "gpw", "skz"].includes(unit.code)) return false; - for (let i = 0; i < Config.SkipLogging.length; i++) { - if (Config.SkipLogging[i] === unit.classid || Config.SkipLogging[i] === unit.code) return false; + for (let skipCode of Config.SkipLogging) { + if (skipCode === unit.classid || skipCode === unit.code) return false; } let lastArea; @@ -928,8 +930,13 @@ Item.logItem = function (action, unit, keptLine, force) { if (!action.match("kept", "i") && !action.match("Shopped") && hasTier) { if (!mercCheck) { - NTIP.GetCharmTier(unit) > 0 && (desc += ("\n\\xffc0Autoequip charm tier: " + NTIP.GetCharmTier(unit))); - NTIP.GetTier(unit) > 0 && (desc += ("\n\\xffc0Autoequip char tier: " + NTIP.GetTier(unit))); + if (unit.isCharm) { + NTIP.GetCharmTier(unit) > 0 && (desc += ("\n\\xffc0Autoequip charm tier: " + NTIP.GetCharmTier(unit))); + } else { + let [mainTier, secTier] = [NTIP.GetTier(unit), NTIP.GetSecondaryTier(unit)]; + mainTier > 0 && (desc += ("\n\\xffc0Autoequip tier: " + mainTier)); + secTier > 0 && (desc += ("\n\\xffc0Autoequip Secondary tier: " + secTier)); + } } else { desc += ("\n\\xffc0Autoequip merc tier: " + NTIP.GetMercTier(unit)); } From 297f0f41ace7aab2b6696da756eed6fcf5a18666 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Sun, 9 Jul 2023 19:52:33 -0400 Subject: [PATCH 179/263] Update SoloEvents.js - define custom moveTo, as the normal Pather.move has a lot of other checks we don't need - add lightning avoidance for dclone - fix baalskip monster check --- libs/SoloPlay/Functions/SoloEvents.js | 226 +++++++++++++++++++++----- 1 file changed, 189 insertions(+), 37 deletions(-) diff --git a/libs/SoloPlay/Functions/SoloEvents.js b/libs/SoloPlay/Functions/SoloEvents.js index 0fd6cbac..a6ad3fc3 100644 --- a/libs/SoloPlay/Functions/SoloEvents.js +++ b/libs/SoloPlay/Functions/SoloEvents.js @@ -224,46 +224,52 @@ Pather.moveToPreset(me.area, sdk.unittype.Monster, sdk.monsters.preset.Corpsefire, 0, 0, false, true); } - Attack.killTarget(sdk.monsters.DiabloClone); - Pickit.pickItems(); + try { + console.log("Added dia lightning listener"); + addEventListener("gamepacket", SoloEvents.diaEvent); - let newAnni = Game.getItem(sdk.items.SmallCharm, sdk.items.mode.onGround); - let oldAnni = me.findItem(sdk.items.SmallCharm, sdk.items.mode.inStorage, -1, sdk.items.quality.Unique); + Attack.killTarget(sdk.monsters.DiabloClone); + Pickit.pickItems(); - if (newAnni && oldAnni) { - this.sendToList({ profile: me.profile, ladder: me.ladder }, 60); + let newAnni = Game.getItem(sdk.items.SmallCharm, sdk.items.mode.onGround); + let oldAnni = me.findItem(sdk.items.SmallCharm, sdk.items.mode.inStorage, -1, sdk.items.quality.Unique); - let tick = getTickCount(); + if (newAnni && oldAnni) { + this.sendToList({ profile: me.profile, ladder: me.ladder }, 60); - while (getTickCount() - tick < 10000) { - me.overhead("Waiting to see if I get a response from other profiles"); + let tick = getTickCount(); - if (this.profileResponded) { - me.overhead("Recieved response, dropping old Annihilus in Rogue Encampment"); - break; + while (getTickCount() - tick < 10000) { + me.overhead("Waiting to see if I get a response from other profiles"); + + if (this.profileResponded) { + me.overhead("Recieved response, dropping old Annihilus in Rogue Encampment"); + break; + } + + delay(50); } - delay(50); - } + if (newAnni && oldAnni && this.profileResponded) { + this.dropCharm(oldAnni); + } else { + me.overhead("No response from other profiles"); + } - if (newAnni && oldAnni && this.profileResponded) { - this.dropCharm(oldAnni); - } else { - me.overhead("No response from other profiles"); + SoloEvents.profileResponded = false; + Pickit.pickItems(); } - SoloEvents.profileResponded = false; - Pickit.pickItems(); - } - - if ((newAnni && oldAnni && !this.profileResponded) && AutoMule.getInfo() && AutoMule.getInfo().hasOwnProperty("torchMuleInfo")) { - scriptBroadcast("muleAnni"); + if ((newAnni && oldAnni && !this.profileResponded) && AutoMule.getInfo() && AutoMule.getInfo().hasOwnProperty("torchMuleInfo")) { + scriptBroadcast("muleAnni"); + } + } finally { + removeEventListener("gamepacket", SoloEvents.diaEvent); + // Move back to where we orignally where + Pather.journeyTo(orginalLocation.area); + Pather.moveTo(orginalLocation.x, orginalLocation.y); + SoloEvents.cloneWalked = false; } - - // Move back to where we orignally where - Pather.journeyTo(orginalLocation.area); - Pather.moveTo(orginalLocation.x, orginalLocation.y); - SoloEvents.cloneWalked = false; }, moveSettings: { @@ -271,13 +277,154 @@ allowClearing: false, allowPicking: false, allowTown: false, + allowNodeActions: false, retry: 10, }, moveTo: function (x, y, givenSettings) { // Abort if dead if (me.dead) return false; - return Pather.move({ x: x, y: y }, Object.assign({}, SoloEvents.moveSettings, givenSettings)); + /** + * assign settings + * @type {pathSettings} + */ + const settings = Object.assign({}, { + allowTeleport: false, + minDist: 3, + retry: 10, + pop: false, + returnSpotOnError: true, + callback: null, + }, givenSettings); + + /** @type {PathNode} */ + let node = { x: x, y: y }; + + if (settings.minDist > 3) { + node = Pather.spotOnDistance( + node, + settings.minDist, + { returnSpotOnError: settings.returnSpotOnError, reductionType: (me.inTown ? 0 : 2) } + ); + } + + let fail = 0; + let invalidCheck = false; + let cbCheck = false; + + Pather.clearUIFlags(); + + if (typeof node.x !== "number" || typeof node.y !== "number") return false; + if (node.distance < 2 && !CollMap.checkColl(me, node, sdk.collision.BlockMissile, 5)) { + return true; + } + + const useTeleport = ( + settings.allowTeleport + && (node.distance > 15 || me.diff || me.act > 3) + && Pather.useTeleport() + ); + const useChargedTele = settings.allowTeleport && Pather.canUseTeleCharges(); + const usingTele = (useTeleport || useChargedTele); + const tpMana = Skill.getManaCost(sdk.skills.Teleport); + const annoyingArea = Pather.inAnnoyingArea(me.area); + let path = getPath( + me.area, + node.x, node.y, + me.x, me.y, + usingTele ? 1 : 0, + usingTele ? (annoyingArea ? 30 : Pather.teleDistance) : Pather.walkDistance + ); + if (!path) throw new Error("move: Failed to generate path."); + + path.reverse(); + settings.pop && path.pop(); + PathDebug.drawPath(path); + if (useTeleport && Config.TeleSwitch && path.length > 5) { + me.switchWeapons(Attack.getPrimarySlot() ^ 1); + } + + while (path.length > 0) { + // Abort if dead + if (me.dead) return false; + // main path + if (Pather.recursion) { + Pather.currentWalkingPath = path; + PathDebug.drawPath(Pather.currentWalkingPath); + } + Pather.clearUIFlags(); + + /** @type {PathNode} */ + node = path.shift(); + + if (typeof settings.callback === "function" && settings.callback()) { + cbCheck = true; + break; + } + + if (getDistance(me, node) > 2) { + // Make life in Maggot Lair easier + if (fail >= 3 && fail % 3 === 0 && !Attack.validSpot(node.x, node.y)) { + invalidCheck = true; + } + // Make life in Maggot Lair easier - should this include arcane as well? + if (annoyingArea || invalidCheck) { + let adjustedNode = Pather.getNearestWalkable(node.x, node.y, 15, 3, sdk.collision.BlockWalk); + + if (adjustedNode) { + [node.x, node.y] = adjustedNode; + invalidCheck && (invalidCheck = false); + } + } + + if (useTeleport && tpMana <= me.mp + ? Pather.teleportTo(node.x, node.y) + : useChargedTele && (getDistance(me, node) >= 15 || me.inArea(sdk.areas.ThroneofDestruction)) + ? Pather.teleUsingCharges(node.x, node.y) + : Pather.walkTo(node.x, node.y, (fail > 0 || me.inTown) ? 2 : 4)) { + // we successfully moved to the node + } else { + if (!me.inTown) { + if (!useTeleport && (Pather.openDoors(node.x, node.y) || Pather.kickBarrels(node.x, node.y))) { + console.debug("Failed to walk to node, but opened door/barrel"); + continue; + } + } + + // Reduce node distance in new path + path = getPath( + me.area, + node.x, node.y, + me.x, me.y, + useTeleport ? 1 : 0, + useTeleport ? rand(25, 35) : rand(10, 15) + ); + if (!path) throw new Error("moveTo: Failed to generate path."); + + path.reverse(); + PathDebug.drawPath(path); + settings.pop && path.pop(); + + if (fail > 0) { + console.debug("move retry " + fail); + Packet.flash(me.gid); + + if (fail >= settings.retry) { + console.log("Failed move: Retry = " + settings.retry); + break; + } + } + fail++; + } + } + + delay(5); + } + + me.switchToPrimary(); + PathDebug.removeHooks(); + + return cbCheck || getDistance(me, node.x, node.y) < 5; }, skip: function () { @@ -303,7 +450,8 @@ } tick = getTickCount(); - this.moveTo(15099, 5078); // Re-enter throne + // this.moveTo(15099, 5078); // Re-enter throne + Pather.walkTo(15099, 5078, 2); // 2 second delay (2000ms) while (getTickCount() - tick < 2000) { @@ -311,19 +459,24 @@ } this.moveTo(15099, 5078); + + let skipFailed = getUnits(sdk.unittype.Monster) + .some(mon => mon.attackable + && mon.x >= 15072 && mon.x <= 15118 + && mon.y >= 5002 && mon.y <= 5073 + ); + myPrint("skip " + (!skipFailed ? "worked" : "failed")); } finally { // Re-enable [Precast.enabled, Pickit.enabled] = [true, true]; SoloEvents.townChicken.disabled = false; } - - let skipWorked = getUnits(sdk.unittype.Monster) - .some(mon => mon.attackable && mon.x >= 15072 && mon.x <= 15118 && mon.y >= 5002 && mon.y <= 5079); - myPrint("skip " + (skipWorked ? "worked" : "failed")); }, dodge: function () { - let diablo = Game.getMonster(sdk.monsters.Diablo); + let diablo = me.inArea(sdk.areas.ChaosSanctuary) + ? Game.getMonster(sdk.monsters.Diablo) + : Game.getMonster(sdk.monsters.DiabloClone); // Credit @Jaenster const shouldDodge = function (coord) { return !!diablo && getUnits(sdk.unittype.Missile) @@ -410,7 +563,6 @@ if (!bytes.length) return; // dia lightning if (bytes[0] === 0x4C && bytes[6] === 193) { - // Messaging.sendToScript(SoloEvents.filePath, "dodge"); me.emit("soloEvent", "dodge"); } }, From 5fcb91598dcf4146ec9aa431910316d9945d3865 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Sun, 9 Jul 2023 19:53:01 -0400 Subject: [PATCH 180/263] Update nith.js - don't use halls wp if we don't already have it --- libs/SoloPlay/Scripts/nith.js | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/libs/SoloPlay/Scripts/nith.js b/libs/SoloPlay/Scripts/nith.js index 9c8730b9..5948cb8b 100644 --- a/libs/SoloPlay/Scripts/nith.js +++ b/libs/SoloPlay/Scripts/nith.js @@ -7,10 +7,16 @@ */ function nith () { - Town.doChores(); + me.inTown && Town.doChores(); myPrint("starting nith"); - Pather.checkWP(sdk.areas.HallsofPain, true) ? Pather.useWaypoint(sdk.areas.HallsofPain) : Pather.getWP(sdk.areas.HallsofPain); + if (Pather.checkWP(sdk.areas.HallsofPain, true)) { + Pather.useWaypoint(sdk.areas.HallsofPain); + } else { + if (Pather.journeyTo(sdk.areas.NihlathaksTemple)) { + Pather.moveToExit([sdk.areas.HallsofAnguish, sdk.areas.HallsofPain], true); + } + } Precast.doPrecast(false); if (!Pather.moveToExit(sdk.areas.HallsofVaught, true)) { @@ -19,14 +25,14 @@ function nith () { return true; } - Pather.moveToPreset(me.area, sdk.unittype.Object, sdk.objects.NihlathaksPlatform); - // Stop script in hardcore mode if vipers are found - if (me.hardcore && Game.getMonster(sdk.monsters.TombViper2)) { - console.log("Tomb Vipers found."); - - return true; - } + // faster detection of TombVipers + Pather.moveToPresetObject(me.area, sdk.objects.NihlathaksPlatform, { callback: () => { + if (me.hardcore && Game.getMonster(sdk.monsters.TombViper2)) { + console.log("Tomb Vipers found."); + throw new ScriptError("Tomb Vipers found."); + } + } }); Attack.killTarget(sdk.monsters.Nihlathak); Pickit.pickItems(); From ade8d14b5a6688cebd2457d26c9137636e738eee Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Sun, 9 Jul 2023 19:55:40 -0400 Subject: [PATCH 181/263] Update Me.js - add switchToPrimary/Secondary - override needHealing to check if it's convenient for us to heal - add needBelt/BufferPots --- libs/SoloPlay/Functions/Me.js | 97 ++++++++++++++++++++++++++-- libs/SoloPlay/Workers/TownChicken.js | 2 +- 2 files changed, 93 insertions(+), 6 deletions(-) diff --git a/libs/SoloPlay/Functions/Me.js b/libs/SoloPlay/Functions/Me.js index ceaf0415..c1c22174 100644 --- a/libs/SoloPlay/Functions/Me.js +++ b/libs/SoloPlay/Functions/Me.js @@ -509,6 +509,43 @@ me.inDanger = function (checkLoc, range) { */ me.checkSkill = (skillId = 0, subId = 0) => !!me.getSkill(skillId, subId); +me.switchToPrimary = function () { + if (me.classic) return true; + return me.switchWeapons(sdk.player.slot.Main); +}; + +me.switchToSecondary = function () { + if (me.classic) return true; + return me.switchWeapons(sdk.player.slot.Secondary); +}; + +/** + * @description Check if healing is needed, based on character config + * @returns {boolean} + */ +me.needHealing = function () { + if (me.hpPercent <= Config.HealHP || me.mpPercent <= Config.HealMP) { + if (me.hpPercent >= 90 && me.mpPercent >= 90 + && Town.getDistance(Town.tasks.get(me.act).Heal) > 10) { + // it's not convenient to heal right now, and we aren't in danger + return false; + } + return true; + } + if (!Config.HealStatus) return false; + // Status effects + return ([ + sdk.states.Poison, + sdk.states.AmplifyDamage, + sdk.states.Frozen, + sdk.states.Weaken, + sdk.states.Decrepify, + sdk.states.LowerResist + ].some(function (state) { + return me.getState(state); + })); +}; + me.cleanUpInvoPotions = function (beltSize) { beltSize === undefined && (beltSize = Storage.BeltSize()); const beltMax = (beltSize * 4); @@ -619,7 +656,7 @@ me.cleanUpScrolls = function (tome, scrollId) { return cleanedUp; }; -me.needPotions = function () { +me.needBeltPots = function () { // we aren't using MinColumn if none of the values are set if (!Config.MinColumn.some(el => el > 0)) return false; // no hp pots or mp pots in Config.BeltColumn (who uses only rejuv pots?) @@ -679,6 +716,41 @@ me.needPotions = function () { return false; }; +me.needBufferPots = function () { + // not using buffers + if (Config.HPBuffer < 0 && Config.MPBuffer < 0) return false; + + // Start + if (me.charlvl > 2 && me.gold > 1000) { + const pots = { hp: 0, mp: 0, }; + const beltSize = Storage.BeltSize(); + + // only run this bit if we aren't wearing a belt for now + beltSize === 1 && me.cleanUpInvoPotions(beltSize); + // now check what's in our belt + me.getItemsEx() + .filter(function (p) { + return p.isInInventory + && [sdk.items.type.HealingPotion, sdk.items.type.ManaPotion].includes(p.itemType); + }) + .forEach(function (p) { + if (p.itemType === sdk.items.type.HealingPotion) { + pots.hp++; + } else if (p.itemType === sdk.items.type.ManaPotion) { + pots.mp++; + } + }); + + return (pots.mp < Config.MPBuffer || pots.hp < Config.HPBuffer); + } + + return false; +}; + +me.needPotions = function () { + return me.needBeltPots() || me.needBufferPots(); +}; + me.clearBelt = function () { let item = me.getItem(-1, sdk.items.mode.inBelt); let clearList = []; @@ -832,7 +904,8 @@ me.getItemsForRepair = function (repairPercent, chargedItems) { let quantity = item.getStat(sdk.stats.Quantity); // Stat 254 = increased stack size - if (typeof quantity === "number" && quantity * 100 / (getBaseStat("items", item.classid, "maxstack") + item.getStat(sdk.stats.ExtraStack)) <= repairPercent) { + if (typeof quantity === "number" + && quantity * 100 / (getBaseStat("items", item.classid, "maxstack") + item.getStat(sdk.stats.ExtraStack)) <= repairPercent) { itemList.push(copyUnit(item)); } @@ -854,7 +927,8 @@ me.getItemsForRepair = function (repairPercent, chargedItems) { if (typeof (charge) === "object") { if (charge instanceof Array) { for (let i = 0; i < charge.length; i += 1) { - if (charge[i] !== undefined && charge[i].hasOwnProperty("charges") && charge[i].charges * 100 / charge[i].maxcharges <= repairPercent) { + if (charge[i] !== undefined && charge[i].hasOwnProperty("charges") + && charge[i].charges * 100 / charge[i].maxcharges <= repairPercent) { itemList.push(copyUnit(item)); } } @@ -874,6 +948,10 @@ me.needRepair = function () { let repairAction = []; let bowCheck = Attack.usingBow(); let switchBowCheck = CharData.skillData.bow.onSwitch; + if (getInteractedNPC() && !getUIFlag(sdk.uiflags.Shop)) { + // fix crash with d2bs + me.cancel(); + } let canAfford = me.gold >= me.getRepairCost(); !bowCheck && switchBowCheck && (bowCheck = (function () { switch (CharData.skillData.bow.bowType) { @@ -891,10 +969,16 @@ me.needRepair = function () { let [quiver, inventoryQuiver] = (function () { switch (bowCheck) { case "crossbow": - return [me.getItem("cqv", sdk.items.mode.Equipped), me.getItem("cqv", sdk.items.mode.inStorage)]; + return [ + me.getItem("cqv", sdk.items.mode.Equipped), + me.getItem("cqv", sdk.items.mode.inStorage) + ]; case "bow": default: - return [me.getItem("aqv", sdk.items.mode.Equipped), me.getItem("aqv", sdk.items.mode.inStorage)]; + return [ + me.getItem("aqv", sdk.items.mode.Equipped), + me.getItem("aqv", sdk.items.mode.inStorage) + ]; } })(); @@ -905,6 +989,9 @@ me.needRepair = function () { ? Item.secondaryEquip(inventoryQuiver, sdk.body.LeftArmSecondary) : Item.equip(inventoryQuiver, 5) : repairAction.push("buyQuiver") && repairAction.push("buyQuiver"); + if (me.gold < 200) { + return []; + } } else { let quantity = quiver.getStat(sdk.stats.Quantity); diff --git a/libs/SoloPlay/Workers/TownChicken.js b/libs/SoloPlay/Workers/TownChicken.js index 2d5bf172..323f2aff 100644 --- a/libs/SoloPlay/Workers/TownChicken.js +++ b/libs/SoloPlay/Workers/TownChicken.js @@ -348,7 +348,7 @@ if (!Config.TownCheck) return true; // can we chicken? if (!me.canTpToTown()) return true; - if (me.needPotions() || (Config.OpenChests.Enabled && Town.needKeys())) { + if (me.needBeltPots() || (Config.OpenChests.Enabled && Town.needKeys())) { shouldChicken = true; } } From d56e1135aa642abd0a6dcbeb8462e61843c6feef Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Sun, 9 Jul 2023 19:58:02 -0400 Subject: [PATCH 182/263] Better checks for npc interactions - Check if we needPotions before using gamble npc to sell items - sort potions before drinking excess ones so we don't drink the higher pots - don't initNpc to sell if the list is empty --- libs/SoloPlay/Functions/NPCAction.js | 10 +- libs/SoloPlay/Functions/TownOverrides.js | 201 +++++++++++++++++++++-- 2 files changed, 195 insertions(+), 16 deletions(-) diff --git a/libs/SoloPlay/Functions/NPCAction.js b/libs/SoloPlay/Functions/NPCAction.js index 8ae99cb1..1cb85156 100644 --- a/libs/SoloPlay/Functions/NPCAction.js +++ b/libs/SoloPlay/Functions/NPCAction.js @@ -68,7 +68,8 @@ const getNeededBuffer = function () { [buffer.hp, buffer.mp] = [0, 0]; me.getItemsEx().filter(function (p) { - return p.isInInventory && [sdk.items.type.HealingPotion, sdk.items.type.ManaPotion].includes(p.itemType); + return p.isInInventory + && [sdk.items.type.HealingPotion, sdk.items.type.ManaPotion].includes(p.itemType); }).forEach(function (p) { switch (p.itemType) { case sdk.items.type.HealingPotion: @@ -120,6 +121,11 @@ pAct >= 4 ? me.act < 4 && Town.goToTown(4) : pAct > me.act && Town.goToTown(pAct); } + console.debug( + "Buying potions, needPots: " + needPots + + " needBuffer: " + needBuffer + + " specialCheck: " + specialCheck + ); let npc = Town.initNPC("Shop", "buyPotions"); if (!npc) return false; @@ -629,7 +635,7 @@ if (Town.cubeRepair()) return true; let npc; - let repairAction = Town.needRepair(); + let repairAction = me.needRepair(); force && repairAction.indexOf("repair") === -1 && repairAction.push("repair"); if (!repairAction || !repairAction.length) return false; diff --git a/libs/SoloPlay/Functions/TownOverrides.js b/libs/SoloPlay/Functions/TownOverrides.js index 450f014d..1886436c 100644 --- a/libs/SoloPlay/Functions/TownOverrides.js +++ b/libs/SoloPlay/Functions/TownOverrides.js @@ -57,10 +57,6 @@ new Overrides.Override(Town, Town.drinkPots, function (orignal, type) { }).apply(); // ugly for now but proxy the functions I moved to Me.js in case somewhere the base functions are being used -Town.getIdTool = () => me.getIdTool(); -Town.getTpTool = () => me.getTpTool(); -Town.needPotions = () => me.needPotions(); -Town.fieldID = () => me.fieldID(); Town.getItemsForRepair = (repairPercent, chargedItems) => me.getItemsForRepair(repairPercent, chargedItems); Town.needRepair = () => me.needRepair(); Town.buyPotions = () => NPCAction.buyPotions(); @@ -81,6 +77,155 @@ Town.ignoredItemTypes = [ sdk.items.type.AntidotePotion, sdk.items.type.ThawingPotion ]; +/** + * @description Start a task and return the NPC Unit + * @param {string} task + * @param {string} reason + * @returns {boolean | Unit} + */ +Town.initNPC = function (task = "", reason = "undefined") { + console.info(true, reason, "initNPC"); + task = task.capitalize(false); + + delay(250); + + /** @type {NPCUnit} */ + let npc = null; + let wantedNpc = Town.tasks.get(me.act)[task] !== undefined + ? Town.tasks.get(me.act)[task] + : "undefined"; + let justUseClosest = (["clearInventory", "sell"].includes(reason) && !me.getUnids().length); + + if (getUIFlag(sdk.uiflags.NPCMenu)) { + console.debug("Currently interacting with an npc"); + npc = getInteractedNPC(); + } + + try { + if (npc) { + let npcName = npc.name.toLowerCase(); + if (!justUseClosest && ((npcName !== wantedNpc) + // Jamella gamble fix + || (task === "Gamble" && npcName === NPC.Jamella))) { + me.cancelUIFlags(); + npc = null; + } + } else { + me.cancelUIFlags(); + } + + /** + * we are just trying to clear our inventory, use the closest npc + * Things to conisder: + * - what if we have unid items? Should we use cain if he is closer than the npc with scrolls? + * - what is our next task? + * - would it be faster to change acts and use the closest npc? + */ + if (justUseClosest) { + let choices = new Set(); + let npcs = Town.tasks.get(me.act); + let _needPots = me.needPotions(); + let _needRepair = me.needRepair().length > 0; + if (_needPots && _needRepair) { + if (me.act === 2) { + choices = new Set([npcs.Key, npcs.Repair]); + } else { + choices = new Set([npcs.Key, npcs.Repair, npcs.Gamble, npcs.Shop]); + // todo - handle when we are in normal and current act < 4 + // if we are going to go to a4 for potions anyway we should go ahead and change act + } + } else if (!_needPots && _needRepair) { + choices.add(npcs.Repair); + } else if (!_needPots && !_needRepair) { + choices = new Set([npcs.Key, npcs.Repair, npcs.Gamble, npcs.Shop]); + } + if (choices.size) { + console.log("closest npc choices", choices); + wantedNpc = Array.from(choices.values()).sort(function (a, b) { + return Town.getDistance(a) - Town.getDistance(b); + }).first(); + console.debug("Choosing closest npc", wantedNpc); + } + } + + if (task === "Heal" && me.act === 2) { + // lets see if we are closer to Atma than Fara + if (Town.getDistance(NPC.Atma) < Town.getDistance(NPC.Fara)) { + wantedNpc = NPC.Atma; + } + } + + if (!npc && wantedNpc !== "undefined") { + npc = Game.getNPC(wantedNpc); + + if (!npc && this.move(wantedNpc)) { + npc = Game.getNPC(wantedNpc); + } + } + + if (!npc || npc.area !== me.area || (!getUIFlag(sdk.uiflags.NPCMenu) && !npc.openMenu())) { + throw new Error("Couldn't interact with npc"); + } + + delay(40); + + switch (task) { + case "Shop": + case "Repair": + case "Gamble": + if (!getUIFlag(sdk.uiflags.Shop) && !npc.startTrade(task)) { + throw new Error("Failed to complete " + reason + " at " + npc.name); + } + break; + case "Key": + if (!getUIFlag(sdk.uiflags.Shop) && !npc.startTrade(me.act === 3 ? "Repair" : "Shop")) { + throw new Error("Failed to complete " + reason + " at " + npc.name); + } + break; + case "CainID": + Misc.useMenu(sdk.menu.IdentifyItems); + me.cancelUIFlags(); + + break; + case "Heal": + if (String.isEqual(npc.name, NPC.Atma)) { + // prevent crash due to atma not being a shoppable npc + me.cancelUIFlags(); + } + break; + } + + console.info(false, "Did " + reason + " at " + npc.name, "initNPC"); + } catch (e) { + console.error(e); + + if (!!e.message && e.message === "Couldn't interact with npc") { + // getUnit bug probably, lets see if going to different act helps + let highestAct = me.highestAct; + if (highestAct === 1) return false; // can't go to any of the other acts + let myAct = me.act; + let potentialActs = [1, 2, 3, 4, 5].filter(a => a <= highestAct && a !== myAct); + let goTo = potentialActs[rand(0, potentialActs.length - 1)]; + Config.DebugMode.Town && console.debug("Going to Act " + goTo + " to see if it fixes getUnit bug"); + Town.goToTown(goTo); + } + + return false; + } + + Misc.poll(() => me.gameReady, 2000, 250); + + if (task === "Heal") { + Config.DebugMode.Town && console.debug("Checking if we are frozen"); + if (me.getState(sdk.states.Frozen)) { + console.log("We are frozen, lets unfreeze real quick with some thawing pots"); + Town.buyPots(2, sdk.items.ThawingPotion, true, true, npc); + } + } + + return npc; +}; + /** * Check if any of the systems need this item * @param {ItemUnit} item @@ -129,6 +274,7 @@ Town.haveItemsToSell = function () { */ Town.sellItems = function (itemList = []) { !itemList.length && (itemList = Town.sell); + if (!itemList.length) return true; if (this.initNPC("Shop", "sell")) { while (itemList.length) { @@ -184,7 +330,12 @@ Town.itemResult = function (item, result, system = "", sell = false) { case Pickit.Result.SOLOWANTS: Item.logger("Kept", item); Item.logItem("Kept", item, result.line); - system === "Field" && ((Item.autoEquipCheck(item) && Item.autoEquip("Field")) || (Item.autoEquipCheckSecondary(item) && Item.autoEquipSecondary("Field"))); + if (system === "Field") { + ( + (Item.autoEquipCheck(item) && Item.autoEquip("Field")) + || (Item.autoEquipCheckSecondary(item) && Item.autoEquipSecondary("Field")) + ); + } break; case Pickit.Result.UNID: @@ -471,6 +622,19 @@ Town.clearInventory = function () { })(potsInInventory.shift()); } + /** + * @param {ItemUnit} a + * @param {ItemUnit} b + * @returns {number} + */ + let sortPots = function (a, b) { + return a.classid - b.classid; + }; + // ensures when clearing invo we don't sell high pots before low pots + hp.sort(sortPots); + mp.sort(sortPots); + rv.sort(sortPots); + // Cleanup healing potions while (hp.length > Config.HPBuffer) { sellOrDrop.push(hp.shift()); @@ -573,6 +737,10 @@ Town.clearInventory = function () { console.log("ÿc8Exit clearInventory ÿc0- ÿc7Duration: ÿc0" + Time.format(getTickCount() - clearInvoTick)); + if (wantedTasks.has("gamble") && Town.getDistance(Town.tasks.get(me.act).Gamble) < 10) { + NPCAction.gamble() && wantedTasks.delete("gamble"); + } + return true; }; @@ -728,7 +896,8 @@ Town.doChores = function (repair = false, givenTasks = {}) { console.info(true); console.time("doChores"); - console.debug("doChores Inital Gold :: " + me.gold); + let _startGold = me.gold; + console.debug("doChores Inital Gold :: " + _startGold); !me.inTown && Town.goToTown(); @@ -743,7 +912,7 @@ Town.doChores = function (repair = false, givenTasks = {}) { * @todo light chores if last chores was < minute? 2 minutes idk yet */ - me.switchWeapons(Attack.getPrimarySlot()); + me.switchToPrimary(); extraTasks.fullChores && Quest.unfinishedQuests(); // Use cainId if we are low on gold or we are closer to him than the shopNPC @@ -755,12 +924,12 @@ Town.doChores = function (repair = false, givenTasks = {}) { } // maybe a check if need healing first, as we might have just used a potion - this.heal(); - this.identify(); - this.clearInventory(); + Town.heal(); + Town.identify(); + Town.clearInventory(); Town.fillTomes(); NPCAction.buyPotions(); - this.buyKeys(); + Town.buyKeys(); extraTasks.thawing && CharData.pots.get("thawing").need() && Town.buyPots(12, "Thawing", true); extraTasks.antidote && CharData.pots.get("antidote").need() && Town.buyPots(12, "Antidote", true); extraTasks.stamina && Town.buyPots(12, "Stamina", true); @@ -781,8 +950,8 @@ Town.doChores = function (repair = false, givenTasks = {}) { Mercenary.hireMerc(); Item.autoEquipMerc(); Town.haveItemsToSell() && Town.sellItems() && me.cancelUIFlags(); - this.clearJunk(); - this.stash(); + Town.clearJunk(); + Town.stash(); // check pots again, we might have enough gold now if we didn't before me.needPotions() && NPCAction.buyPotions() && me.cancelUIFlags(); @@ -803,9 +972,13 @@ Town.doChores = function (repair = false, givenTasks = {}) { } delay(300); - console.debug("doChores Ending Gold :: " + me.gold); + console.debug( + "doChores Ending Gold :: " + me.gold + + " ÿc8(ÿc7" + (me.gold - _startGold) + "ÿc8)" + ); console.info(false, null, "doChores"); Town.lastChores = getTickCount(); + wantedTasks.clear(); return true; }; From cd7b7d3a4ed4f8160ac22b41d463e3831923421e Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Sun, 9 Jul 2023 20:00:29 -0400 Subject: [PATCH 183/263] Update SorceressAttacks.js - temp fix for rubberbanding with dodge and static --- .../ClassAttackOverrides/SorceressAttacks.js | 33 ++++++++++++------- 1 file changed, 21 insertions(+), 12 deletions(-) diff --git a/libs/SoloPlay/Functions/ClassAttackOverrides/SorceressAttacks.js b/libs/SoloPlay/Functions/ClassAttackOverrides/SorceressAttacks.js index ff79587e..72e9a492 100644 --- a/libs/SoloPlay/Functions/ClassAttackOverrides/SorceressAttacks.js +++ b/libs/SoloPlay/Functions/ClassAttackOverrides/SorceressAttacks.js @@ -543,7 +543,6 @@ includeIfNotIncluded("core/Attacks/Sorceress.js"); } // No valid skills can be found if (skill < 0) return Attack.Result.CANTATTACK; - // print damage values if (Developer.debugging.skills && choosenSkill.have) { console.log(sdk.colors.Yellow + "(Selected Main :: " + getSkillById(skill) + ") DMG: " + choosenSkill.dmg); @@ -571,7 +570,8 @@ includeIfNotIncluded("core/Attacks/Sorceress.js"); if (!me.skillDelay || !timed) { let ranged = range > 4; - if (skill === sdk.skills.ChargedBolt && !unit.hasEnchant(sdk.enchant.ManaBurn, sdk.enchant.ColdEnchanted)) { + if (skill === sdk.skills.ChargedBolt + && !unit.hasEnchant(sdk.enchant.ManaBurn, sdk.enchant.ColdEnchanted)) { unit.getMobCount(6, Coords_1.Collision.BLOCK_MISSILE) < 3 && (range = 7); } @@ -595,7 +595,9 @@ includeIfNotIncluded("core/Attacks/Sorceress.js"); } } - if (range < 4 && !Attack.validSpot(unit.x, unit.y)) return Attack.Result.FAILED; + if (range < 4 && !Attack.validSpot(unit.x, unit.y)) { + return Attack.Result.FAILED; + } // Only delay if there are no mobs in our immediate area if (mana > me.mp && !me.checkForMobs({ range: 12 })) { @@ -620,8 +622,10 @@ includeIfNotIncluded("core/Attacks/Sorceress.js"); if (unit.distance > range || Coords_1.isBlockedBetween(me, unit)) { // Allow short-distance walking for melee skills - let walk = (range < 4 || (skill === sdk.skills.ChargedBolt && range === 7)) - && unit.distance < 10 && !checkCollision(me, unit, Coords_1.BlockBits.BlockWall); + let walk = ( + (range < 4 || (skill === sdk.skills.ChargedBolt && range === 7)) + && unit.distance < 10 && !checkCollision(me, unit, sdk.collision.BlockWalk) + ); if (ranged) { if (!Attack.getIntoPosition(unit, range, Coords_1.Collision.BLOCK_MISSILE, walk)) { @@ -657,12 +661,13 @@ includeIfNotIncluded("core/Attacks/Sorceress.js"); if (!unit.dead) { // if we are already in close then it might be worth it to use battle cry if we have it battleCryCheck(unit); - // if we are in danger then don't cast and move - if ((unit.distance <= 3 && !unit.isStunned && !unit.isFrozen) || me.inDanger()) { - // don't cast, just run - Attack.deploy(unit, 25, 5, 9); - return Attack.Result.FAILED; - } + // if we are in danger then don't cast and move - this is causing too much rubberbanding + // if (!unit.isPrimeEvil + // && (unit.distance <= 3 && !unit.isStunned && !unit.isFrozen) || me.inDanger()) { + // // don't cast, just run + // Attack.deploy(unit, 25, 5, 9); + // return Attack.Result.FAILED; + // } Skill.cast(skill, Skill.getHand(skill), unit); if (!Misc.poll(() => unit.dead || unit.hp < preHealth, 200, 50)) { sRetry++; @@ -701,7 +706,11 @@ includeIfNotIncluded("core/Attacks/Sorceress.js"); if ([sdk.skills.FireBolt, sdk.skills.IceBolt].includes(skill)) { let preHealth = unit.hp; let missileDelay = GameData.timeTillMissleImpact(skill, unit); - missileDelay > 0 && Misc.poll(() => unit.dead || unit.hp < preHealth, missileDelay, 50); + if (missileDelay > 0) { + Misc.poll(function () { + return unit.dead || unit.hp < preHealth; + }, missileDelay, 50); + } delay(50); } } From 2a0b3cf68ff2696a8c468ba72180687b4326b0e6 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Sun, 9 Jul 2023 20:01:31 -0400 Subject: [PATCH 184/263] update monsterdata - changed the structure --- libs/SoloPlay/Modules/GameData/AreaData.js | 10 +- libs/SoloPlay/Modules/GameData/GameData.js | 299 ++++++++++++------ libs/SoloPlay/Modules/GameData/MonsterData.js | 202 ++++++------ 3 files changed, 307 insertions(+), 204 deletions(-) diff --git a/libs/SoloPlay/Modules/GameData/AreaData.js b/libs/SoloPlay/Modules/GameData/AreaData.js index 07e7364d..a5bfa5b8 100644 --- a/libs/SoloPlay/Modules/GameData/AreaData.js +++ b/libs/SoloPlay/Modules/GameData/AreaData.js @@ -7,7 +7,7 @@ */ (function (module, require) { - const MonsterData = require("../../../core/GameData/MonsterData"); + const MonsterData = require("./MonsterData"); const ShrineData = require("../../../core/GameData/ShrineData"); const QuestData = require("../../../core/GameData/QuestData"); const MONSTER_KEYS = [ @@ -1000,7 +1000,7 @@ */ AreaDataInstance.prototype.hasMonsterType = function (type) { return this.Monsters.some(function (monId) { - return MonsterData[monId].Type === type; + return MonsterData.get(monId).Type === type; }); }; /** @@ -1010,7 +1010,7 @@ AreaDataInstance.prototype.forEachMonster = function (cb) { if (typeof cb === "function") { this.Monsters.forEach(function (monID) { - const _monster = MonsterData[monID]; + const _monster = MonsterData.get(monID); return cb( _monster, _monster.Rarity * (_monster.GroupCount.Min + _monster.GroupCount.Max) / 2 @@ -1025,13 +1025,13 @@ AreaDataInstance.prototype.forEachMonsterAndMinion = function (cb) { if (typeof cb === "function") { this.Monsters.forEach(function (monID) { - const _monster = MonsterData[monID]; + const _monster = MonsterData.get(monID); let rarity = _monster.Rarity * (_monster.GroupCount.Min + _monster.GroupCount.Max) / 2; cb(_monster, rarity, null); _monster.Minions.forEach(function (minionID) { // eslint-disable-next-line max-len let minionrarity = (_monster.Rarity * (_monster.MinionCount.Min + _monster.MinionCount.Max) / 2 / _monster.Minions.length); - cb(MonsterData[minionID], minionrarity, _monster); + cb(MonsterData.get(minionID), minionrarity, _monster); }); }); } diff --git a/libs/SoloPlay/Modules/GameData/GameData.js b/libs/SoloPlay/Modules/GameData/GameData.js index 551b42aa..d3053c6a 100644 --- a/libs/SoloPlay/Modules/GameData/GameData.js +++ b/libs/SoloPlay/Modules/GameData/GameData.js @@ -1,3 +1,4 @@ +/* eslint-disable max-len */ /* eslint-disable no-unused-vars */ /* eslint-disable no-irregular-whitespace */ /** @@ -8,20 +9,86 @@ */ // todo - remove the magic numbers here (function (module, require) { - const MonsterData = require("../../../core/GameData/MonsterData"); + const MonsterData = require("./MonsterData"); const AreaData = require("./AreaData"); const MissileData = require("./MissileData"); const Coords_1 = require("../Coords"); const sdk = require("../../../modules/sdk"); - + const HPLookup = [ + ["1", "1", "1"], ["7", "107", "830"], + ["9", "113", "852"], ["12", "120", "875"], + ["15", "125", "897"], ["17", "132", "920"], + ["20", "139", "942"], ["23", "145", "965"], + ["27", "152", "987"], ["31", "157", "1010"], + ["35", "164", "1032"], ["36", "171", "1055"], + ["40", "177", "1077"], ["44", "184", "1100"], + ["48", "189", "1122"], ["52", "196", "1145"], + ["56", "203", "1167"], ["60", "209", "1190"], + ["64", "216", "1212"], ["68", "221", "1235"], + ["73", "228", "1257"], ["78", "236", "1280"], + ["84", "243", "1302"], ["89", "248", "1325"], + ["94", "255", "1347"], ["100", "261", "1370"], + ["106", "268", "1392"], ["113", "275", "1415"], + ["120", "280", "1437"], ["126", "287", "1460"], + ["134", "320", "1482"], ["142", "355", "1505"], + ["150", "388", "1527"], ["158", "423", "1550"], + ["166", "456", "1572"], ["174", "491", "1595"], + ["182", "525", "1617"], ["190", "559", "1640"], + ["198", "593", "1662"], ["206", "627", "1685"], + ["215", "661", "1707"], ["225", "696", "1730"], + ["234", "729", "1752"], ["243", "764", "1775"], + ["253", "797", "1797"], ["262", "832", "1820"], + ["271", "867", "1842"], ["281", "900", "1865"], + ["290", "935", "1887"], ["299", "968", "1910"], + ["310", "1003", "1932"], ["321", "1037", "1955"], + ["331", "1071", "1977"], ["342", "1105", "2000"], + ["352", "1139", "2030"], ["363", "1173", "2075"], + ["374", "1208", "2135"], ["384", "1241", "2222"], + ["395", "1276", "2308"], ["406", "1309", "2394"], + ["418", "1344", "2480"], ["430", "1379", "2567"], + ["442", "1412", "2653"], ["454", "1447", "2739"], + ["466", "1480", "2825"], ["477", "1515", "2912"], + ["489", "1549", "2998"], ["501", "1583", "3084"], + ["513", "1617", "3170"], ["525", "1651", "3257"], + ["539", "1685", "3343"], ["552", "1720", "3429"], + ["565", "1753", "3515"], ["579", "1788", "3602"], + ["592", "1821", "3688"], ["605", "1856", "3774"], + ["618", "1891", "3860"], ["632", "1924", "3947"], + ["645", "1959", "4033"], ["658", "1992", "4119"], + ["673", "2027", "4205"], ["688", "2061", "4292"], + ["702", "2095", "4378"], ["717", "2129", "4464"], + ["732", "2163", "4550"], ["746", "2197", "4637"], + ["761", "2232", "4723"], ["775", "2265", "4809"], + ["790", "2300", "4895"], ["805", "2333", "4982"], + ["821", "2368", "5068"], ["837", "2403", "5154"], + ["853", "2436", "5240"], ["868", "2471", "5327"], + ["884", "2504", "5413"], ["900", "2539", "5499"], + ["916", "2573", "5585"], ["932", "2607", "5672"], + ["948", "2641", "5758"], ["964", "2675", "5844"], + ["982", "2709", "5930"], ["999", "2744", "6017"], + ["1016", "2777", "6103"], ["1033", "2812", "6189"], + ["1051", "2845", "6275"], ["1068", "2880", "6362"], + ["1085", "2915", "6448"], ["1103", "2948", "6534"], + ["1120", "2983", "6620"], ["1137", "3016", "6707"], + ["10000", "10000", "10000"] + ]; + + /** @param {Monster} unit */ function isAlive (unit) { return Boolean(unit && unit.hp); } + /** @param {Monster} unit */ function isEnemy (unit) { - return Boolean(unit && isAlive(unit) && unit.getStat(sdk.stats.Alignment) !== 2 && typeof unit.classid === "number" && MonsterData[unit.classid].Killable); + return Boolean( + unit && isAlive(unit) + && unit.getStat(sdk.stats.Alignment) !== 2 + && typeof unit.classid === "number" + && MonsterData.get(unit.classid).Killable + ); } + /** @param {ItemUnit} item */ function onGround (item) { return item.onGroundOrDropping; } @@ -29,26 +96,46 @@ const GameData = { myReference: me, townAreas: [0, 1, 40, 75, 103, 109], - HPLookup: [["1", "1", "1"], ["7", "107", "830"], ["9", "113", "852"], ["12", "120", "875"], ["15", "125", "897"], ["17", "132", "920"], ["20", "139", "942"], ["23", "145", "965"], ["27", "152", "987"], ["31", "157", "1010"], ["35", "164", "1032"], ["36", "171", "1055"], ["40", "177", "1077"], ["44", "184", "1100"], ["48", "189", "1122"], ["52", "196", "1145"], ["56", "203", "1167"], ["60", "209", "1190"], ["64", "216", "1212"], ["68", "221", "1235"], ["73", "228", "1257"], ["78", "236", "1280"], ["84", "243", "1302"], ["89", "248", "1325"], ["94", "255", "1347"], ["100", "261", "1370"], ["106", "268", "1392"], ["113", "275", "1415"], ["120", "280", "1437"], ["126", "287", "1460"], ["134", "320", "1482"], ["142", "355", "1505"], ["150", "388", "1527"], ["158", "423", "1550"], ["166", "456", "1572"], ["174", "491", "1595"], ["182", "525", "1617"], ["190", "559", "1640"], ["198", "593", "1662"], ["206", "627", "1685"], ["215", "661", "1707"], ["225", "696", "1730"], ["234", "729", "1752"], ["243", "764", "1775"], ["253", "797", "1797"], ["262", "832", "1820"], ["271", "867", "1842"], ["281", "900", "1865"], ["290", "935", "1887"], ["299", "968", "1910"], ["310", "1003", "1932"], ["321", "1037", "1955"], ["331", "1071", "1977"], ["342", "1105", "2000"], ["352", "1139", "2030"], ["363", "1173", "2075"], ["374", "1208", "2135"], ["384", "1241", "2222"], ["395", "1276", "2308"], ["406", "1309", "2394"], ["418", "1344", "2480"], ["430", "1379", "2567"], ["442", "1412", "2653"], ["454", "1447", "2739"], ["466", "1480", "2825"], ["477", "1515", "2912"], ["489", "1549", "2998"], ["501", "1583", "3084"], ["513", "1617", "3170"], ["525", "1651", "3257"], ["539", "1685", "3343"], ["552", "1720", "3429"], ["565", "1753", "3515"], ["579", "1788", "3602"], ["592", "1821", "3688"], ["605", "1856", "3774"], ["618", "1891", "3860"], ["632", "1924", "3947"], ["645", "1959", "4033"], ["658", "1992", "4119"], ["673", "2027", "4205"], ["688", "2061", "4292"], ["702", "2095", "4378"], ["717", "2129", "4464"], ["732", "2163", "4550"], ["746", "2197", "4637"], ["761", "2232", "4723"], ["775", "2265", "4809"], ["790", "2300", "4895"], ["805", "2333", "4982"], ["821", "2368", "5068"], ["837", "2403", "5154"], ["853", "2436", "5240"], ["868", "2471", "5327"], ["884", "2504", "5413"], ["900", "2539", "5499"], ["916", "2573", "5585"], ["932", "2607", "5672"], ["948", "2641", "5758"], ["964", "2675", "5844"], ["982", "2709", "5930"], ["999", "2744", "6017"], ["1016", "2777", "6103"], ["1033", "2812", "6189"], ["1051", "2845", "6275"], ["1068", "2880", "6362"], ["1085", "2915", "6448"], ["1103", "2948", "6534"], ["1120", "2983", "6620"], ["1137", "3016", "6707"], ["10000", "10000", "10000"]], monsterLevel: function (monsterID, areaID) { return (me.diff ? AreaData.has(areaID) && AreaData.get(areaID).Level - : MonsterData.hasOwnProperty(monsterID) && MonsterData[monsterID].Level); // levels on nm/hell are determined by area, not by monster data + : MonsterData.has(monsterID) && MonsterData.get(monsterID).Level); // levels on nm/hell are determined by area, not by monster data }, + /** + * @param {number} monsterID + * @param {number} areaID + * @param {number} adjustLevel + */ monsterExp: function (monsterID, areaID, adjustLevel = 0) { + const mLvl = this.monsterLevel(monsterID, areaID) + adjustLevel; + const { ExperienceModifier } = MonsterData.get(monsterID); return Experience.monsterExp[Math.min( Experience.monsterExp.length - 1, - this.monsterLevel(monsterID, areaID) + adjustLevel - )][me.diff] * MonsterData[monsterID].ExperienceModifier / 100; + mLvl + )][me.diff] * ExperienceModifier / 100; }, eliteExp: function (monsterID, areaID) { return this.monsterExp(monsterID, areaID, 2) * 3; }, + /** + * @param {number} monsterID + * @param {number} areaID + * @param {number} adjustLevel + */ monsterAvgHP: function (monsterID, areaID, adjustLevel = 0) { - return this.HPLookup[Math.min(this.HPLookup.length - 1, this.monsterLevel(monsterID, areaID) + adjustLevel)][me.diff] * (getBaseStat("monstats", monsterID, "minHP") + getBaseStat("monstats", monsterID, "maxHP")) / 200; + const { MinHp, MaxHp } = MonsterData.get(monsterID); + const mLvl = this.monsterLevel(monsterID, areaID) + adjustLevel; + return HPLookup[Math.min(HPLookup.length - 1, mLvl)][me.diff] * (MinHp + MaxHp) / 200; }, + /** + * @param {number} monsterID + * @param {number} areaID + * @param {number} adjustLevel + */ monsterMaxHP: function (monsterID, areaID, adjustLevel = 0) { - return this.HPLookup[Math.min(this.HPLookup.length - 1, this.monsterLevel(monsterID, areaID) + adjustLevel)][me.diff] * getBaseStat("monstats", monsterID, "maxHP") / 100; + const mLvl = this.monsterLevel(monsterID, areaID) + adjustLevel; + const { MaxHp } = MonsterData.get(monsterID); + return HPLookup[Math.min(HPLookup.length - 1, mLvl)][me.diff] * MaxHp / 100; }, eliteAvgHP: function (monsterID, areaID) { return (6 - me.diff) / 2 * this.monsterAvgHP(monsterID, areaID, 2); @@ -58,7 +145,7 @@ }, monsterMaxDmg: function (monsterID, areaID, adjustLevel = 0) { let level = this.monsterLevel(monsterID, areaID) + adjustLevel; - let { Attack1MaxDmg, Attack2MaxDmg, Skill1MaxDmg } = MonsterData[monsterID]; + let { Attack1MaxDmg, Attack2MaxDmg, Skill1MaxDmg } = MonsterData.get(monsterID); return Math.max.apply( null, [Attack1MaxDmg, Attack2MaxDmg, Skill1MaxDmg] ) * level / 100 * this.monsterDamageModifier(); @@ -66,17 +153,17 @@ // https://www.diabloii.net/forums/threads/monster-damage-increase-per-player-count.570346/ monsterAttack1AvgDmg: function (monsterID, areaID, adjustLevel = 0) { let level = this.monsterLevel(monsterID, areaID) + adjustLevel; - let { Attack1MinDmg, Attack1MaxDmg } = MonsterData[monsterID]; + let { Attack1MinDmg, Attack1MaxDmg } = MonsterData.get(monsterID); return ((Attack1MinDmg + Attack1MaxDmg) / 2) * level / 100 * this.monsterDamageModifier(); }, monsterAttack2AvgDmg: function (monsterID, areaID, adjustLevel = 0) { let level = this.monsterLevel(monsterID, areaID) + adjustLevel; - let { Attack2MinDmg, Attack2MaxDmg } = MonsterData[monsterID]; + let { Attack2MinDmg, Attack2MaxDmg } = MonsterData.get(monsterID); return ((Attack2MinDmg + Attack2MaxDmg) / 2) * level / 100 * this.monsterDamageModifier(); }, monsterSkill1AvgDmg: function (monsterID, areaID, adjustLevel = 0) { let level = this.monsterLevel(monsterID, areaID) + adjustLevel; - let { Skill1MinDmg, Skill1MaxDmg } = MonsterData[monsterID]; + let { Skill1MinDmg, Skill1MaxDmg } = MonsterData.get(monsterID); return ((Skill1MinDmg + Skill1MaxDmg) / 2) * level / 100 * this.monsterDamageModifier(); }, monsterAvgDmg: function (monsterID, areaID, adjustLevel = 0) { @@ -89,7 +176,7 @@ return dmgs.reduce((acc, v) => acc + v) / dmgs.length; }, averagePackSize: function (monsterID) { - let { GroupCount, MinionCount } = MonsterData[monsterID]; + let { GroupCount, MinionCount } = MonsterData.get(monsterID); return (GroupCount.Min + MinionCount.Min + GroupCount.Max + MinionCount.Max) / 2; }, areaLevel: function (areaID) { @@ -220,7 +307,6 @@ }, damageTypes: ["Physical", "Fire", "Lightning", "Magic", "Cold", "Poison", "?", "?", "?", "Physical"], // 9 is Stun, but stun isn't an element synergyCalc: { // TODO: add melee skill damage and synergies - they are poop - // sorc fire spells 36: [47, 0.16, 56, 0.16], // fire bolt 41: [37, 0.13], // inferno @@ -651,7 +737,15 @@ */ skillDamage: function (skillID, unit) { // TODO: caluclate basic attack damage - if (skillID === sdk.skills.Attack) return { type: "Physical", pmin: 2, pmax: 8, min: 0, max: 0 }; // short sword, no reqs + if (skillID === sdk.skills.Attack) { + return { + type: "Physical", + pmin: (GameData.myReference.getStat(sdk.stats.MinDamage) || 2), + pmax: (GameData.myReference.getStat(sdk.stats.MaxDamage) || 8), + min: 0, + max: 0 + }; // short sword, no reqs + } if (this.skillLevel(skillID) < 1) { return { @@ -879,7 +973,7 @@ * currently it would check our stats, then check amp and conviction - those could all be pre-built as they aren't going to change */ avgSkillDamage: function (skillID, unit) { - if (skillID === undefined || unit === undefined || !skillID || !unit || !Skill.canUse(skillID)) return 0; + if (!skillID || !unit || !Skill.canUse(skillID)) return 0; const ampDmg = Skill.canUse(sdk.skills.AmplifyDamage) ? 100 : (Skill.canUse(sdk.skills.Decrepify) @@ -893,7 +987,9 @@ * @returns */ const getTotalDmg = function (skillData, unit) { - const isUndead = (typeof unit === "number" ? MonsterData[unit].Undead : MonsterData[unit.classid].Undead); + const isUndead = (typeof unit === "number" + ? MonsterData.get(unit).Undead + : MonsterData.get(unit.classid).Undead); const conviction = GameData.getConviction(); let totalDmg = 0; let avgPDmg = (skillData.pmin + skillData.pmax) / 2; @@ -1242,7 +1338,7 @@ ], monsterResist: function (unit, type) { let stat = this.resistMap[type]; - return stat ? (unit.getStat ? unit.getStat(stat) : MonsterData[unit][type]) : 0; + return stat ? (unit.getStat ? unit.getStat(stat) : MonsterData.get(unit)[type]) : 0; }, getConviction: function () { let merc = GameData.myReference.getMerc(); @@ -1266,7 +1362,7 @@ const useCooldown = (typeof unit === "number" ? false : Boolean(me.skillDelay)); const hp = this.monsterMaxHP(typeof unit === "number" ? unit : unit.classid, areaID); const conviction = this.getConviction(), ampDmg = this.getAmp(); - const isUndead = (typeof unit === "number" ? MonsterData[unit].Undead : MonsterData[unit.classid].Undead); + const isUndead = (typeof unit === "number" ? MonsterData.get(unit).Undead : MonsterData.get(unit.classid).Undead); skillDamageInfo = skillDamageInfo || this.allSkillDamage(unit); console.debug(unit, "---", hp); // if (conviction && unit instanceof Unit && !unit.getState(sdk.states.Conviction)) conviction = 0; //ToDo; enable when fixed telestomp @@ -1408,7 +1504,7 @@ let eret = { effort: Infinity, skill: -1, type: "Physical" }; let hp = this.monsterMaxHP(typeof unit === "number" ? unit : unit.classid, areaID); let conviction = this.getConviction(), ampDmg = this.getAmp(); - let isUndead = (typeof unit === "number" ? MonsterData[unit].Undead : MonsterData[unit.classid].Undead); + let isUndead = (typeof unit === "number" ? MonsterData.get(unit).Undead : MonsterData.get(unit.classid).Undead); let skillDamageInfo = this.allSkillDamage(unit); for (let sk in skillDamageInfo) { @@ -2058,10 +2154,10 @@ get: function () { if (!this.isMoving || this.isFrozen) return 0; const velocity = this.isRunning - ? MonsterData[this.classid].Run - : MonsterData[this.classid].Velocity; + ? MonsterData.get(this.classid).Run + : MonsterData.get(this.classid).Velocity; if (this.isChilled) { - let malus = MonsterData[this.classid].ColdEffect; + let malus = MonsterData.get(this.classid).ColdEffect; (malus > 0) && (malus = malus - 256); return Math.max(1, ~~(velocity * (1 + malus))); } @@ -2069,90 +2165,95 @@ } }); + /** + * @param {number} skillId + * @param {Monster} monster + * @returns {PathNode} + */ function targetPointForSkill (skillId, monster) { - if (monster === undefined || skillId === undefined || !monster.attackable) return null; + if (!monster || skillId === undefined || !monster.attackable) return null; let missileName = getBaseStat("skills", skillId, "cltmissile"); let missile = MissileData[missileName]; if (!missile) { missileName = getBaseStat("skills", skillId, "srvmissile"); missile = MissileData[missileName]; } - if (missile && missile.velocity > 0) { - if (monster.isMoving && (monster.targetx !== me.x || monster.targety !== me.y)) { - let startX = monster.x, startY = monster.y; - // tiles per second velocities - // ToDo: is monster slowed by freeze or something ? - let monsterVelocityTPS = monster.currentVelocity; - let missileVelocityTPS = missile.velocity; - // tiles per frame velocities - let monsterVelocityTPF = monsterVelocityTPS / 25; - let missileVelocityTPF = missileVelocityTPS / 25; - //console.log("monster is moving to "+monster.targetx+", "+monster.targety + " at speed "+monsterVelocity); - let path = getPath(monster.area, startX, startY, monster.targetx, monster.targety, 2, 1); - if (path && path.length) { - // path is reversed from target to monster, we will check from last path position (target) to monster position - path.reverse(); - let [diffS, diffF, found] = [0, 0, 0]; - let time = { missile: {}, monster: {} }; - for (let i = 0; i < path.length; i++) { - let pos = path[i]; - // ToDo : does missile spawn at me position ? - let distanceForMissile = getDistance(me, pos); - if (distanceForMissile > missile.range) { - // too far for missile to reach this position - continue; - } - let distanceForMonster = getDistance({ x: startX, y: startY }, pos); - let timeForMonsterF = distanceForMonster / monsterVelocityTPF; - // time in seconds - // let castTimeS = GameData.castingDuration(skillId); - // let timeForMissileS = distanceForMissile / missileVelocityTPS + castTimeS; - // time in frames - let castTimeF = me.castingFrames(skillId); - let timeForMissileF = distanceForMissile / missileVelocityTPF + castTimeF; - // let timeForMonsterS = distanceForMonster / monsterVelocityTPS; - // Todo: missile and monster size - // diff seconds - // diffS = timeForMissileS-timeForMonsterS; - // diff frames - diffF = timeForMissileF - timeForMonsterF; - // diff > 0 : missile will reach pos after monster - // diff < 0 : missile will reach pos before monster - // console.log("time for monster to reach "+pos+" = "+timeForMonster); - // console.log("time for missile to reach "+pos+" = "+timeForMissile); - // console.log("diff = "+diff) - if (i === 0 && diffF >= 0) { - // last path position and missile is late, we can't predict next monster target, shoot at last path position - // it may fail because monster may be moving at other target while missile is arriving - // console.log("missile will be too late"); - found = pos; - // time.missile.seconds = timeForMissileS; - time.missile.frames = timeForMissileF; - // time.monster.seconds = timeForMonsterS; - time.monster.frames = timeForMonsterF; - break; - } - // the number of frames needed for unit to move 1 tile - let timeToMoveOneTileMonsterF = 1 / monsterVelocityTPF; - // let timeToMoveOneTileMissileF = 1 / missileVelocityTPF; - // while missile is travelling, monster will continue to move - // if the difference is greater than the time a monster will move 1 tile, the missile will miss - // todo: monster size, missile size - if (diffF >= -1 * timeToMoveOneTileMonsterF && diffF <= 1 * timeToMoveOneTileMonsterF) { - found = pos; - // time.missile.seconds = timeForMissileS; - time.missile.frames = timeForMissileF; - // time.monster.seconds = timeForMonsterS; - time.monster.frames = timeForMonsterF; - break; - } + if (!missile || missile.velocity <= 0) return null; + if (monster.isMoving && (monster.targetx !== me.x || monster.targety !== me.y)) { + let startX = monster.x; + let startY = monster.y; + // tiles per second velocities + // ToDo: is monster slowed by freeze or something ? + let monsterVelocityTPS = monster.currentVelocity; + let missileVelocityTPS = missile.velocity; + // tiles per frame velocities + let monsterVelocityTPF = monsterVelocityTPS / 25; + let missileVelocityTPF = missileVelocityTPS / 25; + //console.log("monster is moving to "+monster.targetx+", "+monster.targety + " at speed "+monsterVelocity); + let path = getPath(monster.area, startX, startY, monster.targetx, monster.targety, 2, 1); + if (path && path.length) { + // path is reversed from target to monster, we will check from last path position (target) to monster position + path.reverse(); + let [diffS, diffF, found] = [0, 0, 0]; + let time = { missile: {}, monster: {} }; + for (let i = 0; i < path.length; i++) { + let pos = path[i]; + // ToDo : does missile spawn at me position ? + let distanceForMissile = getDistance(me, pos); + if (distanceForMissile > missile.range) { + // too far for missile to reach this position + continue; } - if (found) { - // console.log("missile will hit monster in "+time.missile.seconds+" ("+time.missile.frames+") at "+found.x+", "+found.y); - // console.log("time for monster = "+time.monster.seconds+ " ("+time.monster.frames+")") - // console.log("diff missile-monster = "+diffS+ " ("+diffF+")"); - return found; + let distanceForMonster = getDistance({ x: startX, y: startY }, pos); + let timeForMonsterF = distanceForMonster / monsterVelocityTPF; + // time in seconds + // let castTimeS = GameData.castingDuration(skillId); + // let timeForMissileS = distanceForMissile / missileVelocityTPS + castTimeS; + // time in frames + let castTimeF = me.castingFrames(skillId); + let timeForMissileF = distanceForMissile / missileVelocityTPF + castTimeF; + // let timeForMonsterS = distanceForMonster / monsterVelocityTPS; + // Todo: missile and monster size + // diff seconds + // diffS = timeForMissileS-timeForMonsterS; + // diff frames + diffF = timeForMissileF - timeForMonsterF; + // diff > 0 : missile will reach pos after monster + // diff < 0 : missile will reach pos before monster + // console.log("time for monster to reach "+pos+" = "+timeForMonster); + // console.log("time for missile to reach "+pos+" = "+timeForMissile); + // console.log("diff = "+diff) + if (i === 0 && diffF >= 0) { + // last path position and missile is late, we can't predict next monster target, shoot at last path position + // it may fail because monster may be moving at other target while missile is arriving + // console.log("missile will be too late"); + found = pos; + // time.missile.seconds = timeForMissileS; + time.missile.frames = timeForMissileF; + // time.monster.seconds = timeForMonsterS; + time.monster.frames = timeForMonsterF; + break; } + // the number of frames needed for unit to move 1 tile + let timeToMoveOneTileMonsterF = 1 / monsterVelocityTPF; + // let timeToMoveOneTileMissileF = 1 / missileVelocityTPF; + // while missile is travelling, monster will continue to move + // if the difference is greater than the time a monster will move 1 tile, the missile will miss + // todo: monster size, missile size + if (diffF >= -1 * timeToMoveOneTileMonsterF && diffF <= 1 * timeToMoveOneTileMonsterF) { + found = pos; + // time.missile.seconds = timeForMissileS; + time.missile.frames = timeForMissileF; + // time.monster.seconds = timeForMonsterS; + time.monster.frames = timeForMonsterF; + break; + } + } + if (found) { + // console.log("missile will hit monster in "+time.missile.seconds+" ("+time.missile.frames+") at "+found.x+", "+found.y); + // console.log("time for monster = "+time.monster.seconds+ " ("+time.monster.frames+")") + // console.log("diff missile-monster = "+diffS+ " ("+diffF+")"); + return found; } } } diff --git a/libs/SoloPlay/Modules/GameData/MonsterData.js b/libs/SoloPlay/Modules/GameData/MonsterData.js index 9cb9f401..b50221b5 100644 --- a/libs/SoloPlay/Modules/GameData/MonsterData.js +++ b/libs/SoloPlay/Modules/GameData/MonsterData.js @@ -1,109 +1,111 @@ (function (module, require) { - const LocaleStringName = require("./LocaleStringID").LocaleStringName; - const MONSTER_INDEX_COUNT = 770; - /** - * @typedef MonsterDataObj - * @type {object} - * @property {number} Index = Index of this monster - * @property {number} ClassID = classid of this monster - * @property {number} Type = Type of monster - * @property {number} Level = Level of this monster in normal (use GameData.monsterLevel to find monster levels) - * @property {boolean} Ranged = if monster is ranged - * @property {number} Rarity = weight of this monster in level generation - * @property {number} Threat = threat level used by mercs - * @property {number} Align = alignment of unit (determines what it will attack) - * @property {boolean} Melee = if monster is melee - * @property {boolean} NPC = if unit is NPC - * @property {boolean} Demon = if monster is demon - * @property {boolean} Flying = if monster is flying - * @property {boolean} Boss = if monster is a boss - * @property {boolean} ActBoss = if monster is act boss - * @property {boolean} Killable = if monster can be killed - * @property {boolean} Convertable = if monster is affected by convert or mind blast - * @property {boolean} NeverCount = if not counted as a minion - * @property {number} DeathDamage = explodes on death - * @property {number} Regeneration = hp regeneration - * @property {number} LocaleString = locale string index for getLocaleString - * @property {number} ExperienceModifier = percent of base monster exp this unit rewards when killed - * @property {number} Undead = 2 if greater undead, 1 if lesser undead, 0 if neither - * @property {number} Drain = drain effectiveness percent - * @property {number} Block = block percent - * @property {number} Physical = physical resist - * @property {number} Magic = magic resist - * @property {number} Fire = fire resist - * @property {number} Lightning = lightning resist - * @property {number} Poison = poison resist - * @property {number[]} Minions = array of minions that can spawn with this unit - * @property {number} MinionCount.Min = minimum number of minions that can spawn with this unit - * @property {number} MinionCount.Max = maximum number of minions that can spawn with this unit - */ - - /** @type {MonsterDataObj[]} */ - const MonsterData = Array(MONSTER_INDEX_COUNT); + const MonsterData = (function () { + const LocaleStringName = require("./LocaleStringID").LocaleStringName; + const MONSTER_INDEX_COUNT = 770; + /** @type {Map} */ + const _monsterData = new Map(); - for (let i = 0; i < MonsterData.length; i++) { - let index = i; - - MonsterData[i] = ({ - Index: index, - ClassID: index, - Type: getBaseStat("monstats", index, "MonType"), - Level: getBaseStat("monstats", index, "Level"), // normal only, nm/hell are determined by area's LevelEx - Ranged: getBaseStat("monstats", index, "RangedType"), - Rarity: getBaseStat("monstats", index, "Rarity"), - Threat: getBaseStat("monstats", index, "threat"), - PetIgnore: getBaseStat("monstats", index, "petignore"), - Align: getBaseStat("monstats", index, "Align"), - Melee: getBaseStat("monstats", index, "isMelee"), - NPC: getBaseStat("monstats", index, "npc"), - Demon: getBaseStat("monstats", index, "demon"), - Flying: getBaseStat("monstats", index, "flying"), - Boss: getBaseStat("monstats", index, "boss"), - ActBoss: getBaseStat("monstats", index, "primeevil"), - Killable: getBaseStat("monstats", index, "killable"), - Convertable: getBaseStat("monstats", index, "switchai"), - NeverCount: getBaseStat("monstats", index, "neverCount"), - DeathDamage: getBaseStat("monstats", index, "deathDmg"), - Regeneration: getBaseStat("monstats", index, "DamageRegen"), - LocaleString: getLocaleString(getBaseStat("monstats", index, "NameStr")), - InternalName: LocaleStringName[getBaseStat("monstats", index, "NameStr")], - ExperienceModifier: getBaseStat("monstats", index, ["Exp", "Exp(N)", "Exp(H)"][me.diff]), - Undead: (getBaseStat("monstats", index, "hUndead") && 2) | (getBaseStat("monstats", index, "lUndead") && 1), - Drain: getBaseStat("monstats", index, ["Drain", "Drain(N)", "Drain(H)"][me.diff]), - Block: getBaseStat("monstats", index, ["ToBlock", "ToBlock(N)", "ToBlock(H)"][me.diff]), - Physical: getBaseStat("monstats", index, ["ResDm", "ResDm(N)", "ResDm(H)"][me.diff]), - Magic: getBaseStat("monstats", index, ["ResMa", "ResMa(N)", "ResMa(H)"][me.diff]), - Fire: getBaseStat("monstats", index, ["ResFi", "ResFi(N)", "ResFi(H)"][me.diff]), - Lightning: getBaseStat("monstats", index, ["ResLi", "ResLi(N)", "ResLi(H)"][me.diff]), - Cold: getBaseStat("monstats", index, ["ResCo", "ResCo(N)", "ResCo(H)"][me.diff]), - Poison: getBaseStat("monstats", index, ["ResPo", "ResPo(N)", "ResPo(H)"][me.diff]), - Minions: ([getBaseStat("monstats", index, "minion1"), getBaseStat("monstats", index, "minion2")].filter(mon => mon !== 65535)), - GroupCount: ({ + /** + * @constructor + * @param {number} index + */ + function MonsterObj (index) { + /** @type {number} */ + this.ClassID = index; + /** @type {number} */ + this.Type = getBaseStat("monstats", index, "MonType"); + /** @type {number} */ + this.Level = getBaseStat("monstats", index, "Level"); // normal only, nm/hell are determined by area's LevelEx + this.Ranged = getBaseStat("monstats", index, "RangedType"); + this.Rarity = getBaseStat("monstats", index, "Rarity"); + this.Threat = getBaseStat("monstats", index, "threat"); + this.PetIgnore = getBaseStat("monstats", index, "petignore"); + this.Align = getBaseStat("monstats", index, "Align"); + this.Melee = getBaseStat("monstats", index, "isMelee"); + this.NPC = getBaseStat("monstats", index, "npc"); + this.Demon = getBaseStat("monstats", index, "demon"); + this.Flying = getBaseStat("monstats", index, "flying"); + this.Boss = getBaseStat("monstats", index, "boss"); + this.ActBoss = getBaseStat("monstats", index, "primeevil"); + this.Killable = getBaseStat("monstats", index, "killable"); + this.Convertable = getBaseStat("monstats", index, "switchai"); + this.NeverCount = getBaseStat("monstats", index, "neverCount"); + this.DeathDamage = getBaseStat("monstats", index, "deathDmg"); + this.Regeneration = getBaseStat("monstats", index, "DamageRegen"); + /** @type {string} */ + this.LocaleString = getLocaleString(getBaseStat("monstats", index, "NameStr")); + /** @type {string} */ + this.InternalName = LocaleStringName[getBaseStat("monstats", index, "NameStr")]; + this.ExperienceModifier = getBaseStat("monstats", index, ["Exp", "Exp(N)", "Exp(H)"][me.diff]); + this.Undead = (getBaseStat("monstats", index, "hUndead") && 2) | (getBaseStat("monstats", index, "lUndead") && 1); + this.Drain = getBaseStat("monstats", index, ["Drain", "Drain(N)", "Drain(H)"][me.diff]); + this.Block = getBaseStat("monstats", index, ["ToBlock", "ToBlock(N)", "ToBlock(H)"][me.diff]); + this.Physical = getBaseStat("monstats", index, ["ResDm", "ResDm(N)", "ResDm(H)"][me.diff]); + this.Magic = getBaseStat("monstats", index, ["ResMa", "ResMa(N)", "ResMa(H)"][me.diff]); + this.Fire = getBaseStat("monstats", index, ["ResFi", "ResFi(N)", "ResFi(H)"][me.diff]); + this.Lightning = getBaseStat("monstats", index, ["ResLi", "ResLi(N)", "ResLi(H)"][me.diff]); + this.Cold = getBaseStat("monstats", index, ["ResCo", "ResCo(N)", "ResCo(H)"][me.diff]); + this.Poison = getBaseStat("monstats", index, ["ResPo", "ResPo(N)", "ResPo(H)"][me.diff]); + this.Minions = ([ + getBaseStat("monstats", index, "minion1"), getBaseStat("monstats", index, "minion2") + ].filter(mon => mon !== 65535)); + this.MinionCount = ({ + Min: getBaseStat("monstats", index, "minion1"), + Max: getBaseStat("monstats", index, "minion2") + }); + this.GroupCount = ({ Min: getBaseStat("monstats", index, "MinGrp"), Max: getBaseStat("monstats", index, "MaxGrp") - }), - MinionCount: ({ - Min: getBaseStat("monstats", index, "PartyMin"), - Max: getBaseStat("monstats", index, "PartyMax") - }), - Velocity: getBaseStat("monstats", index, "Velocity"), - Run: getBaseStat("monstats", index, "Run"), - SizeX: getBaseStat("monstats", index, "SizeX"), - SizeY: getBaseStat("monstats", index, "SizeY"), - Attack1MinDmg: getBaseStat("monstats", index, ["A1MinD", "A1MinD(N)", "A1MinD(H)"][me.diff]), - Attack1MaxDmg: getBaseStat("monstats", index, ["A1MaxD", "A1MaxD(N)", "A1MaxD(H)"][me.diff]), - Attack2MinDmg: getBaseStat("monstats", index, ["A2MinD", "A2MinD(N)", "A2MinD(H)"][me.diff]), - Attack2MaxDmg: getBaseStat("monstats", index, ["A2MaxD", "A2MaxD(N)", "A2MaxD(H)"][me.diff]), - Skill1MinDmg: getBaseStat("monstats", index, ["S1MinD", "S1MinD(N)", "S1MinD(H)"][me.diff]), - Skill1MaxDmg: getBaseStat("monstats", index, ["S1MaxD", "S1MaxD(N)", "S1MaxD(H)"][me.diff]), - }); - } + }); + this.Velocity = getBaseStat("monstats", index, "Velocity"); + this.Run = getBaseStat("monstats", index, "Run"); + this.SizeX = getBaseStat("monstats", index, "SizeX"); + this.SizeY = getBaseStat("monstats", index, "SizeY"); + this.Attack1MinDmg = getBaseStat("monstats", index, ["A1MinD", "A1MinD(N)", "A1MinD(H)"][me.diff]); + this.Attack1MaxDmg = getBaseStat("monstats", index, ["A1MaxD", "A1MaxD(N)", "A1MaxD(H)"][me.diff]); + this.Attack2MinDmg = getBaseStat("monstats", index, ["A2MinD", "A2MinD(N)", "A2MinD(H)"][me.diff]); + this.Attack2MaxDmg = getBaseStat("monstats", index, ["A2MaxD", "A2MaxD(N)", "A2MaxD(H)"][me.diff]); + this.Skill1MinDmg = getBaseStat("monstats", index, ["S1MinD", "S1MinD(N)", "S1MinD(H)"][me.diff]); + this.Skill1MaxDmg = getBaseStat("monstats", index, ["S1MaxD", "S1MaxD(N)", "S1MaxD(H)"][me.diff]); + this.MinHp = getBaseStat("monstats", index, "minHP"); + this.MaxHp = getBaseStat("monstats", index, "maxHP"); + } - MonsterData.findByName = function (whatToFind) { - let matches = MonsterData.map(mon => [Math.min(whatToFind.diffCount(mon.LocaleString), whatToFind.diffCount(mon.InternalName)), mon]).sort((a, b) => a[0] - b[0]); + for (let i = 0; i < MONSTER_INDEX_COUNT; i++) { + _monsterData.set(i, new MonsterObj(i)); + } - return matches[0][1]; - }; + return { + /** @param {number} classid */ + has: function (classid) { + return _monsterData.has(classid); + }, + /** @param {number} classid */ + get: function (classid) { + return _monsterData.get(classid); + }, + + /** @param {string} whatToFind */ + findByName: function (whatToFind) { + let matches = []; + for (let [, mon] of _monsterData) { + let _diffcount = Math.min( + whatToFind.diffCount(mon.LocaleString), + whatToFind.diffCount(mon.InternalName) + ); + if (_diffcount === 0) { + return mon; + } + matches.push([_diffcount, mon]); + } + return matches + .sort(function (a, b) { + return a[0] - b[0]; + }).first()[1]; + + }, + }; + })(); module.exports = MonsterData; })(module, require); From 3d3c218e91ab6af9f82f0bd6b43350d817587cc7 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Mon, 10 Jul 2023 12:43:08 -0400 Subject: [PATCH 185/263] Update Guard.js - fix crash due to no main function in thread - increase stack trace to 22 --- libs/SoloPlay/Modules/Guard.js | 33 +++++++++++++++++++++++---------- 1 file changed, 23 insertions(+), 10 deletions(-) diff --git a/libs/SoloPlay/Modules/Guard.js b/libs/SoloPlay/Modules/Guard.js index 6c9bd54d..b0037324 100644 --- a/libs/SoloPlay/Modules/Guard.js +++ b/libs/SoloPlay/Modules/Guard.js @@ -1,4 +1,4 @@ -(function (module, require, thread) { +(function (module, require, thread, globalThis) { "use strict"; const _Messaging = require("../../modules/Messaging"); const Worker = require("../../modules/Worker"); @@ -19,25 +19,27 @@ * @constructor * @param {function():string} callback */ - function UpdateableText(callback) { + function UpdateableText (callback) { let element = new Text(callback(), self.x + 15, self.y + (7 * self.hooks.length), 0, 12, 0); self.hooks.push(element); this.update = () => { element.text = callback(); - element.visible = [sdk.uiflags.Inventory, + element.visible = [ + sdk.uiflags.Inventory, sdk.uiflags.SkillWindow, sdk.uiflags.TradePrompt, - sdk.uiflags.QuickSkill].every(f => !getUIFlag(f)); + sdk.uiflags.QuickSkill + ].every(function (f) { + return !getUIFlag(f); + }); }; } this.hooks = []; this.x = 500; this.y = 600 - (400 + (self.hooks.length * 15)); - // this.box = new Box(this.x-2, this.y-20, 250, (self.hooks.length * 15), 0, 0.2); - - for (let i = 0; i < 20; i++) { + for (let i = 0; i < 22; i++) { (i => this.hooks.push(new UpdateableText(() => stack && stack.length > i && stack[i] || "")))(i); } @@ -61,12 +63,17 @@ let quiting = false; addEventListener("scriptmsg", data => data === "quit" && (quiting = true)); - while (!quiting) delay(1000); + // eslint-disable-next-line dot-notation + globalThis["main"] = function () { + while (!quiting) delay(3); + //@ts-ignore + getScript(true).stop(); + }; break; } case "started": { let sendStack = getTickCount(); - Worker.push(function highPrio() { + Worker.push(function highPrio () { Worker.push(highPrio); if ((getTickCount() - sendStack) < 200 || (sendStack = getTickCount()) && false) return true; _Messaging.send({ Guard: { stack: (new Error).stack } }); @@ -80,4 +87,10 @@ } } -}).call(null, typeof module === "object" && module || {}, typeof require === "undefined" && (include("require.js") && require) || require, getScript.startAsThread()); +}).call( + null, + typeof module === "object" && module || {}, + typeof require === "undefined" && (include("require.js") && require) || require, + getScript.startAsThread(), + [].filter.constructor("return this")() +); From 199a00badfbbac115160f63381181d3bf470086e Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Mon, 10 Jul 2023 15:04:34 -0400 Subject: [PATCH 186/263] Update LoaderOverrides.js - handle skipping town --- libs/SoloPlay/Functions/LoaderOverrides.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libs/SoloPlay/Functions/LoaderOverrides.js b/libs/SoloPlay/Functions/LoaderOverrides.js index d9da0627..3db38681 100644 --- a/libs/SoloPlay/Functions/LoaderOverrides.js +++ b/libs/SoloPlay/Functions/LoaderOverrides.js @@ -44,9 +44,9 @@ Loader.run = function () { } for (this.scriptIndex = 0; this.scriptIndex < SoloIndex.scripts.length; this.scriptIndex++) { - !me.inTown && Town.goToTown(); - Check.checkSpecialCase(); const scriptName = SoloIndex.scripts[this.scriptIndex]; + !me.inTown && !Loader.skipTown.includes(scriptName) && Town.goToTown(); + Check.checkSpecialCase(); if (!SoloIndex.index[scriptName]) continue; if (!SoloIndex.index[scriptName].shouldRun()) continue; From 2303df2207a61d2f2b2ee0d893f975cd06a3caf0 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Mon, 10 Jul 2023 15:06:27 -0400 Subject: [PATCH 187/263] add `me.getOwned` - cleaned up haveRunes, moved from prototypes.js to me.js - getOwned makes it easy to get all items of certain properties --- libs/SoloPlay/Functions/Me.js | 56 +++++++++++++++++++ libs/SoloPlay/Functions/PrototypeOverrides.js | 29 ---------- 2 files changed, 56 insertions(+), 29 deletions(-) diff --git a/libs/SoloPlay/Functions/Me.js b/libs/SoloPlay/Functions/Me.js index c1c22174..7fbbcaa7 100644 --- a/libs/SoloPlay/Functions/Me.js +++ b/libs/SoloPlay/Functions/Me.js @@ -1038,3 +1038,59 @@ me.sortInventory = function () { SetUp.sortSettings.ItemsSortedFromRight ); }; + +/** + * @description Returns boolean if we have all the runes given by itemInfo array + * @param {number[]} itemInfo - Array of rune classids + * @returns Boolean + */ +me.haveRunes = function (itemInfo = []) { + if (!Array.isArray(itemInfo) || typeof itemInfo[0] !== "number") return false; + let itemList = this.getItemsEx() + .filter(i => i.isInStorage && i.itemType === sdk.items.type.Rune); + if (!itemList.length || itemList.length < itemInfo.length) return false; + const checkedGids = new Set(); + + return itemInfo.every(function (rune) { + return itemList.some(function (item) { + if (item.classid === rune && !checkedGids.has(item.gid)) { + checkedGids.add(item.gid); + return true; + } + return false; + }); + }); +}; + +/** + * Get a list of items that match the given criteria. + * @param {ItemUnit | { + * itemType?: number, + * classid?: number, + * quality?: number, + * sockets?: number, + * location?: number, + * ethereal?: boolean, + * }} itemInfo + * @param {boolean} skipSame + * @returns {ItemUnit[]} + */ +me.getOwned = function (itemInfo = {}, skipSame = false) { + let itemList = []; + let item = me.getItem(); + + if (item) { + do { + if (itemInfo.itemType !== undefined && itemInfo.itemType !== item.itemType) continue; + if (itemInfo.classid !== undefined && itemInfo.classid !== item.classid) continue; + if (itemInfo.quality !== undefined && itemInfo.quality !== item.quality) continue; + if (itemInfo.sockets !== undefined && itemInfo.sockets !== item.sockets) continue; + if (itemInfo.location !== undefined && itemInfo.location !== item.location) continue; + if (itemInfo.ethereal !== undefined && itemInfo.ethereal !== item.ethereal) continue; + if (skipSame && itemInfo.gid !== undefined && itemInfo.gid !== item.gid) continue; + itemList.push(copyUnit(item)); + } while (item.getNext()); + } + + return itemList; +}; diff --git a/libs/SoloPlay/Functions/PrototypeOverrides.js b/libs/SoloPlay/Functions/PrototypeOverrides.js index bd1125b0..6c23721d 100644 --- a/libs/SoloPlay/Functions/PrototypeOverrides.js +++ b/libs/SoloPlay/Functions/PrototypeOverrides.js @@ -850,35 +850,6 @@ Unit.prototype.getStatEx = function (id, subid) { return this.getStat(id, subid); }; -/** - * @description Returns boolean if we have all the runes given by itemInfo array - * @param {number[]} itemInfo - Array of rune classids - * @returns Boolean - */ -Unit.prototype.haveRunes = function (itemInfo = []) { - if (this === undefined || this.type > 1) return false; - if (!Array.isArray(itemInfo) || typeof itemInfo[0] !== "number") return false; - let itemList = this.getItemsEx().filter(i => i.isInStorage && i.itemType === sdk.items.type.Rune); - if (!itemList.length || itemList.length < itemInfo.length) return false; - let checkedGids = []; - - for (let i = 0; i < itemInfo.length; i++) { - let rCheck = itemInfo[i]; - - if (!itemList.some(i => { - if (i.classid === rCheck && checkedGids.indexOf(i.gid) === -1) { - checkedGids.push(i.gid); - return true; - } - return false; - })) { - return false; - } - } - - return true; -}; - Unit.prototype.getMobs = function ({ range, coll, type }) { if (this === undefined) return []; const _this = this; From 024c884efe4d3f6674aa2ecb70f5cd90a6644581 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Mon, 10 Jul 2023 15:08:03 -0400 Subject: [PATCH 188/263] Update MiscOverrides.js - track our last shrine, so we know how much time is left on it --- libs/SoloPlay/Functions/MiscOverrides.js | 56 ++++++++++++++++++------ 1 file changed, 42 insertions(+), 14 deletions(-) diff --git a/libs/SoloPlay/Functions/MiscOverrides.js b/libs/SoloPlay/Functions/MiscOverrides.js index 1450c377..1fd344b3 100644 --- a/libs/SoloPlay/Functions/MiscOverrides.js +++ b/libs/SoloPlay/Functions/MiscOverrides.js @@ -63,7 +63,10 @@ Misc.openChest = function (unit) { // Skip invalid/open and Countess chests if (!unit || unit.x === 12526 || unit.x === 12565 || unit.mode) return false; // locked chest, no keys - if (!me.assassin && unit.islocked && !me.findItem(sdk.items.Key, sdk.items.mode.inStorage, sdk.storage.Inventory)) return false; + if (!me.assassin && unit.islocked + && !me.findItem(sdk.items.Key, sdk.items.mode.inStorage, sdk.storage.Inventory)) { + return false; + } let specialChest = sdk.quest.chests.includes(unit.classid); @@ -160,7 +163,7 @@ Misc.openChests = function (range = 15) { */ if (unit - && (Pather.useTeleport() || !checkCollision(me, unit, sdk.collision.WallOrRanged)) + && (Pather.useTeleport() || !checkCollision(me, unit, sdk.collision.BlockWalk)) && this.openChest(unit)) { Pickit.pickItems(); } @@ -189,7 +192,7 @@ Misc.getWell = function (unit) { } } - if (Misc.poll(() => unit.mode, 1000, 50)) return true; + if (Misc.poll(function () { return unit.mode; }, 1000, 50)) return true; Packet.flash(me.gid); } @@ -226,6 +229,33 @@ Misc.useWell = function (range = 15) { return true; }; +Misc.lastShrine = new function () { + this.tick = 0; + this.duration = 0; + this.type = -1; + this.state = -1; + + /** @param {ObjectUnit} unit */ + this.update = function (unit) { + if (!unit || !unit.hasOwnProperty("objtype")) return; + // we only care about tracking shrines with states + if (!ShrineData.getState(unit.objtype)) return; + this.tick = getTickCount(); + this.type = unit.objtype; + this.duration = ShrineData.getDuration(unit.objtype); + this.state = ShrineData.getState(unit.objtype); + }; + + this.remaining = function () { + return this.duration - (getTickCount() - this.tick); + }; + + this.isMyCurrentState = function () { + if (this.state <= 0) return false; + return me.getState(this.state); + }; +}; + /** * Use a shrine Unit * @param {ObjectUnit} unit @@ -234,6 +264,10 @@ Misc.useWell = function (range = 15) { Misc.getShrine = function (unit) { if (unit.mode === sdk.objects.mode.Active) return false; AreaData.get(me.area).addShrine(unit); + if (Misc.lastShrine.remaining() > Time.seconds(30) && Misc.lastShrine.isMyCurrentState()) { + // skip for now, don't waste the shrine we have active + return false; + } for (let i = 0; i < 3; i++) { if (Skill.useTK(unit) && i < 2) { @@ -249,6 +283,7 @@ Misc.getShrine = function (unit) { if (Misc.poll(() => unit.mode, 1000, 40)) { AreaData.get(me.area).updateShrine(unit); + Misc.lastShrine.update(unit); if (unit.objtype === sdk.shrines.Gem) { Pickit.pickItems(); } @@ -314,8 +349,6 @@ Misc.scanShrines = function (range, ignore = []) { * Fix for a3/a5 shrines */ if (shrine) { - let index = -1; - // Build a list of nearby shrines do { if (shrine.name.toLowerCase().includes("shrine") && ShrineData.has(shrine.objtype) @@ -324,17 +357,12 @@ Misc.scanShrines = function (range, ignore = []) { shrineList.push(copyUnit(shrine)); } } while (shrine.getNext()); - if (!shrineList.length) return false; // Check if we have a shrine state, store its index if yes - for (let i = 0; i < this.shrineStates.length; i += 1) { - if (me.getState(this.shrineStates[i])) { - index = i; - - break; - } - } + const index = Misc.shrineStates.findIndex(function (state) { + return state > 0 && me.getState(state); + }); for (let i = 0; i < Config.ScanShrines.length; i += 1) { for (let shrine of shrineList) { @@ -415,7 +443,7 @@ Misc.getShrinesInArea = function (area, type, use) { result = true; if (type === sdk.shrines.Gem) { - Pickit.pickItems(); + Pickit.pickItems(5); } return true; } From abc07e731136c4a3dcf0eddb947a4036c8b62bd3 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Mon, 10 Jul 2023 15:09:26 -0400 Subject: [PATCH 189/263] Update TownOverrides.js - handle act 2 where lysander also sells pots if we are looking for closest npc --- libs/SoloPlay/Functions/TownOverrides.js | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/libs/SoloPlay/Functions/TownOverrides.js b/libs/SoloPlay/Functions/TownOverrides.js index 1886436c..9a8cd467 100644 --- a/libs/SoloPlay/Functions/TownOverrides.js +++ b/libs/SoloPlay/Functions/TownOverrides.js @@ -94,7 +94,10 @@ Town.initNPC = function (task = "", reason = "undefined") { let wantedNpc = Town.tasks.get(me.act)[task] !== undefined ? Town.tasks.get(me.act)[task] : "undefined"; - let justUseClosest = (["clearInventory", "sell"].includes(reason) && !me.getUnids().length); + const justUseClosest = ( + ["clearinventory", "sell"].includes(reason.toLowerCase()) + && !me.getUnids().length + ); if (getUIFlag(sdk.uiflags.NPCMenu)) { console.debug("Currently interacting with an npc"); @@ -136,6 +139,11 @@ Town.initNPC = function (task = "", reason = "undefined") { } } else if (!_needPots && _needRepair) { choices.add(npcs.Repair); + } else if (_needPots && !_needRepair) { + choices.add(npcs.Shop); + if (me.act === 2) { + choices.add(npcs.Key); + } } else if (!_needPots && !_needRepair) { choices = new Set([npcs.Key, npcs.Repair, npcs.Gamble, npcs.Shop]); } From 63485a30a136fe8cb1f78b909711492e6f8a8a6f Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Mon, 10 Jul 2023 15:17:45 -0400 Subject: [PATCH 190/263] oog cleanup/refactor - some general cleanup. Mostly wrapping some of the methods in try-finally to always reset blockmouse - changed locations from object to a map --- D2BotSoloPlay.dbj | 8 +- libs/SoloPlay/Tools/OOGOverrides.js | 1200 ++++++++++++++++----------- 2 files changed, 725 insertions(+), 483 deletions(-) diff --git a/D2BotSoloPlay.dbj b/D2BotSoloPlay.dbj index c528cc45..a2a2017d 100644 --- a/D2BotSoloPlay.dbj +++ b/D2BotSoloPlay.dbj @@ -5,6 +5,7 @@ * * * @typedef {import("./sdk/globals")} +* @typedef {import("./libs/SoloPlay/globals")} */ // No touchy! @@ -140,12 +141,7 @@ function main () { Starter.inGame && oogTick === 0 && (oogTick = getTickCount()); Starter.isUp = "no"; - try { - let loc = getLocation(); - (locations[loc] !== undefined) && locations[loc](loc); - } catch (e) { - console.error(e, "LOCATION: " + getLocation()); - } + LocationAction.run(); delay(1000); } } diff --git a/libs/SoloPlay/Tools/OOGOverrides.js b/libs/SoloPlay/Tools/OOGOverrides.js index 8e6d1d5b..fb3180c6 100644 --- a/libs/SoloPlay/Tools/OOGOverrides.js +++ b/libs/SoloPlay/Tools/OOGOverrides.js @@ -17,8 +17,13 @@ includeIfNotIncluded("OOG.js"); }; })([].filter.constructor("return this")(), login); -const locations = {}; -(function() { +const LocationAction = { + run: function () { + // placeholder + } +}; + +(function () { let joinInfo; Starter.Config.StopOnDeadHardcore = false; @@ -46,14 +51,15 @@ const locations = {}; case 1638: try { /** @type {Map} */ - const classMap = new Map(); - classMap.set("ZON", "amazon"); - classMap.set("SOR", "sorceress"); - classMap.set("NEC", "necromancer"); - classMap.set("PAL", "paladin"); - classMap.set("BAR", "barbarian"); - classMap.set("DRU", "druid"); - classMap.set("SIN", "assassin"); + const classMap = new Map([ + ["ZON", "amazon"], + ["SOR", "sorceress"], + ["NEC", "necromancer"], + ["PAL", "paladin"], + ["BAR", "barbarian"], + ["DRU", "druid"], + ["SIN", "assassin"], + ]); let [modePrefix, charClass] = me.profile.toUpperCase().split("-"); if (!modePrefix || !charClass || !(charClass = classMap.get(charClass.substring(0, 3)))) { @@ -169,129 +175,144 @@ const locations = {}; } }).apply(); - ControlAction.scrollDown = function () { - me.blockMouse = true; - for (let i = 0; i < 4; i++) { - sendKey(sdk.keys.code.DownArrow); + /** @param {number} [amount] */ + const scrollDown = function (amount = 4) { + try { + me.blockMouse = true; + for (let i = 0; i < amount; i++) { + sendKey(sdk.keys.code.DownArrow); + } + } finally { + me.blockMouse = false; } - me.blockMouse = false; }; + /** + * @typedef {Object} CharInfo + * @property {string} [account] + * @property {string} [password] + * @property {string} [realm] + * @property {string} charClass + * @property {string} charName + * @property {boolean} ladder + * @property {boolean} expansion + * @property {boolean} hardcore + */ + + /** + * @param {CharInfo} info + * @returns {boolean} + */ ControlAction.makeCharacter = function (info) { - const NameGen = require("./NameGen"); - !info.charClass && (info.charClass = "barbarian"); - !info.charName && (info.charName = NameGen()); - me.blockMouse = true; - - let clickCoords = []; - let soloStats = CharData.getStats(); - let timeout = getTickCount() + Time.minutes(5); - - /** @type {Map 1) { - CharData.delete(false); - CharData.updateData("me", "finalBuild", Starter.profileInfo.tag); - Developer.logPerformance && Tracker.initialize(); - } - - D2Bot.updateStatus("Making Character: " + info.charName); - - // cycle until in lobby - while (getLocation() !== sdk.game.locations.Lobby && !me.ingame) { - switch (getLocation()) { - case sdk.game.locations.CharSelect: - case sdk.game.locations.CharSelectConnecting: - case sdk.game.locations.CharSelectNoChars: - let control = Controls.CharSelectCreate.control; - - // Create Character greyed out - if (control && control.disabled === sdk.game.controls.Disabled) { - me.blockMouse = false; + try { + const NameGen = require("./NameGen"); + !info.charClass && (info.charClass = "barbarian"); + !info.charName && (info.charName = NameGen()); + me.blockMouse = true; + + let clickCoords = []; + let soloStats = CharData.getStats(); + let timeout = getTickCount() + Time.minutes(5); + + /** @type {Map 1) { + CharData.delete(false); + CharData.updateData("me", "finalBuild", Starter.profileInfo.tag); + Developer.logPerformance && Tracker.initialize(); + } - return false; - } + D2Bot.updateStatus("Making Character: " + info.charName); - Controls.CharSelectCreate.click(); + // cycle until in lobby + while (getLocation() !== sdk.game.locations.Lobby && !me.ingame) { + switch (getLocation()) { + case sdk.game.locations.CharSelect: + case sdk.game.locations.CharSelectConnecting: + case sdk.game.locations.CharSelectNoChars: + let control = Controls.CharSelectCreate.control; - break; - case sdk.game.locations.LobbyPleaseWait: - D2Bot.restart(); // single player error on finding character + // Create Character greyed out + if (control && control.disabled === sdk.game.controls.Disabled) { + return false; + } - break; - case sdk.game.locations.CharacterCreate: - clickCoords = coords.get(info.charClass.toLowerCase()) || coords.get("paladin"); - getControl().click(clickCoords[0], clickCoords[1]); - delay(500); + Controls.CharSelectCreate.click(); - break; - case sdk.game.locations.NewCharSelected: - // hardcore char warning - if (Controls.CharCreateHCWarningOk.control) { - Controls.CharCreateHCWarningOk.click(); - } else { - Controls.CharCreateCharName.setText(info.charName); + break; + case sdk.game.locations.LobbyPleaseWait: + D2Bot.restart(); // single player error on finding character - if (!info.expansion) { - // @credit isid0re - if (["druid", "assassin"].includes(info.charClass)) { - D2Bot.printToConsole("Error in profile name. Expansion characters cannot be made in classic", sdk.colors.D2Bot.Red); - D2Bot.stop(); + break; + case sdk.game.locations.CharacterCreate: + clickCoords = coords.get(info.charClass.toLowerCase()) || coords.get("paladin"); + getControl().click(clickCoords[0], clickCoords[1]); + delay(500); - return false; - } + break; + case sdk.game.locations.NewCharSelected: + // hardcore char warning + if (Controls.CharCreateHCWarningOk.control) { + Controls.CharCreateHCWarningOk.click(); + } else { + Controls.CharCreateCharName.setText(info.charName); - Controls.CharCreateExpansion.click(); + !info.expansion && Controls.CharCreateExpansion.click(); + !info.ladder && Controls.CharCreateLadder.click(); + info.hardcore && Controls.CharCreateHardcore.click(); + Controls.BottomRightOk.click(); } - !info.ladder && Controls.CharCreateLadder.click(); - info.hardcore && Controls.CharCreateHardcore.click(); - Controls.BottomRightOk.click(); - } + break; + case sdk.game.locations.OkCenteredErrorPopUp: + // char name exists (text box 4, 268, 320, 264, 120) + ControlAction.timeoutDelay("Character Name exists: " + info.charName + ". Making new Name.", 5e3); + Starter.profileInfo.charName = info.charName = NameGen(); + Controls.OkCentered.click(); + D2Bot.updateStatus("Making Character: " + info.charName); - break; - case sdk.game.locations.OkCenteredErrorPopUp: - // char name exists (text box 4, 268, 320, 264, 120) - ControlAction.timeoutDelay("Character Name exists: " + info.charName + ". Making new Name.", 5e3); - Starter.profileInfo.charName = info.charName = NameGen(); - Controls.OkCentered.click(); - D2Bot.updateStatus("Making Character: " + info.charName); + break; + default: + console.debug("Unknown Location: " + getLocation()); + break; + } - break; - default: - break; - } + if (getTickCount() > timeout) { + D2Bot.printToConsole( + "Failed to create character: " + info.charName + + " Location: " + getLocation(), + sdk.colors.D2Bot.Red + ); + return false; + } - // Singleplayer loop break fix. - if (me.ingame) { - break; + delay(500); } - if (getTickCount() > timeout) { - D2Bot.printToConsole("Failed to create character: " + info.charName + " Location: " + getLocation(), sdk.colors.D2Bot.Red); - return false; - } + D2Bot.setProfile(null, null, info.charName, "Normal"); + let gamename = info.charName.substring(0, 7) + "-" + Starter.randomString(3, false) + "-"; + DataFile.updateStats("gameName", gamename); - delay(500); + return true; + } finally { + me.blockMouse = false; } - - me.blockMouse = false; - D2Bot.setProfile(null, null, info.charName, "Normal"); - let gamename = Starter.profileInfo.charName.substring(0, 7) + "-" + Starter.randomString(3, false) + "-"; - DataFile.updateStats("gameName", gamename); - - return true; }; + /** + * @param {CharInfo} info + * @returns {Control} + */ ControlAction.findCharacter = function (info) { let count = 0; let singlePlayer = ![sdk.game.gametype.OpenBattlenet, sdk.game.gametype.BattleNet].includes(Profile().type); @@ -350,7 +371,7 @@ const locations = {}; // check for additional characters up to 24 (online) or 999 offline (no character limit cap) if (count > 0 && count % 8 === 0) { if (Controls.CharSelectChar6.click()) { - this.scrollDown(); + scrollDown(); let check = Controls.CharSelectCharInfo0.control; if (firstCheck && check) { @@ -370,177 +391,181 @@ const locations = {}; return false; }; + /** + * @param {CharInfo} info + * @param {boolean} startFromTop + * @returns {boolean} + */ ControlAction.loginCharacter = function (info, startFromTop = true) { - me.blockMouse = true; + try { + me.blockMouse = true; + // start from beginning of the char list + startFromTop && sendKey(sdk.keys.code.Home); + + MainLoop: + // cycle until in lobby or in game + while (getLocation() !== sdk.game.locations.Lobby) { + switch (getLocation()) { + case sdk.game.locations.SplashScreen: + case sdk.game.locations.MainMenu: + case sdk.game.locations.Login: + if (getLocation() === sdk.game.locations.MainMenu + && Profile().type === sdk.game.profiletype.SinglePlayer + && Controls.SinglePlayer.click()) { + Starter.checkDifficulty(); + break; + } else if (Starter.BNET) { + Starter.LocationEvents.login(); + } - // start from beginning of the char list - startFromTop && sendKey(sdk.keys.code.Home); - - MainLoop: - // cycle until in lobby or in game - while (getLocation() !== sdk.game.locations.Lobby) { - switch (getLocation()) { - case sdk.game.locations.SplashScreen: - case sdk.game.locations.MainMenu: - case sdk.game.locations.Login: - if (getLocation() === sdk.game.locations.MainMenu - && Profile().type === sdk.game.profiletype.SinglePlayer - && Controls.SinglePlayer.click()) { - Starter.checkDifficulty(); break; - } else if (Starter.BNET) { - Starter.LocationEvents.login(); - } + case sdk.game.locations.CharSelect: + let control = ControlAction.findCharacter(info); - break; - case sdk.game.locations.CharSelect: - let control = ControlAction.findCharacter(info); + if (control) { + control.click(); + Controls.BottomRightOk.click(); - if (control) { - control.click(); - Controls.BottomRightOk.click(); - me.blockMouse = false; - - if (getLocation() === sdk.game.locations.SelectDifficultySP) { - try { - Starter.LocationEvents.selectDifficultySP(); - Starter.locationTimeout(Time.seconds(3), sdk.game.locations.SelectDifficultySP); - } catch (err) { - break MainLoop; - } + if (getLocation() === sdk.game.locations.SelectDifficultySP) { + try { + Starter.LocationEvents.selectDifficultySP(); + Starter.locationTimeout(Time.seconds(3), sdk.game.locations.SelectDifficultySP); + } catch (err) { + break MainLoop; + } - if (me.ingame) { - return true; + if (me.ingame) { + return true; + } } + + return true; + } else if (getLocation() !== sdk.game.locations.CharSelect) { + break; } - return true; - } else if (getLocation() !== sdk.game.locations.CharSelect) { + break MainLoop; + case sdk.game.locations.CharSelectNoChars: + Controls.BottomLeftExit.click(); // why exit rather than returning false? + + break; + case sdk.game.locations.Disconnected: + case sdk.game.locations.OkCenteredErrorPopUp: + break MainLoop; + default: break; } - break MainLoop; - case sdk.game.locations.CharSelectNoChars: - Controls.BottomLeftExit.click(); // why exit rather than returning false? - - break; - case sdk.game.locations.Disconnected: - case sdk.game.locations.OkCenteredErrorPopUp: - break MainLoop; - default: - break; + delay(100); } - delay(100); + return false; + } finally { + me.blockMouse = false; } - - me.blockMouse = false; - - return false; }; - // need open bnet check + /** + * @todo add open bnet check + * @param {CharInfo} info + * @returns {boolean} + */ ControlAction.makeAccount = function (info) { - me.blockMouse = true; + try { + me.blockMouse = true; + D2Bot.updateStatus("Making Account: " + info.account); - let tick; - let realms = { "uswest": 0, "useast": 1, "asia": 2, "europe": 3 }; - D2Bot.updateStatus("Making Account: " + info.account); + // cycle until in empty char screen + while (getLocation() !== sdk.game.locations.CharSelectNoChars) { + switch (getLocation()) { + case sdk.game.locations.MainMenu: + ControlAction.clickRealm(ControlAction.realms[info.realm]); + Controls.BattleNet.click(); - // cycle until in empty char screen - while (getLocation() !== sdk.game.locations.CharSelectNoChars) { - switch (getLocation()) { - case sdk.game.locations.MainMenu: - ControlAction.clickRealm(realms[info.realm]); - Controls.BattleNet.click(); + break; + case sdk.game.locations.Login: + Controls.CreateNewAccount.click(); - break; - case sdk.game.locations.Login: - Controls.CreateNewAccount.click(); + break; + case sdk.game.locations.LoginError: + case sdk.game.locations.LoginUnableToConnect: + return false; + case sdk.game.locations.SplashScreen: + Controls.SplashScreen.click(); - break; - case sdk.game.locations.LoginError: - case sdk.game.locations.LoginUnableToConnect: - return false; - case sdk.game.locations.SplashScreen: - Controls.SplashScreen.click(); + break; + case sdk.game.locations.MainMenuConnecting: + let tick = getTickCount(); - break; - case sdk.game.locations.MainMenuConnecting: - tick = getTickCount(); + while (getLocation() === sdk.game.locations.MainMenuConnecting) { + if (getTickCount() - tick > 10000) { + Controls.LoginCancelWait.click(); + } - while (getLocation() === sdk.game.locations.MainMenuConnecting) { - if (getTickCount() - tick > 10000) { - Controls.LoginCancelWait.click(); + delay(500); } - delay(500); - } + break; + case sdk.game.locations.CharacterCreate: + Controls.BottomLeftExit.click(); - break; - case sdk.game.locations.CharacterCreate: - Controls.BottomLeftExit.click(); + break; + case sdk.game.locations.OkCenteredErrorPopUp: + info.account = ""; + info.password = ""; + D2Bot.setProfile(info.account, info.password); + D2Bot.restart(true); - break; - case sdk.game.locations.OkCenteredErrorPopUp: - info.account = ""; - info.password = ""; - D2Bot.setProfile(info.account, info.password); - D2Bot.restart(true); + break; + case sdk.game.locations.TermsOfUse: + Controls.TermsOfUseAgree.click(); - break; - case sdk.game.locations.TermsOfUse: - Controls.TermsOfUseAgree.click(); + break; + case sdk.game.locations.CreateNewAccount: + Controls.EnterAccountName.setText(info.account); + Controls.EnterAccountPassword.setText(info.password); + Controls.ConfirmPassword.setText(info.password); + Controls.BottomRightOk.click(); - break; - case sdk.game.locations.CreateNewAccount: - Controls.EnterAccountName.setText(info.account); - Controls.EnterAccountPassword.setText(info.password); - Controls.ConfirmPassword.setText(info.password); - Controls.BottomRightOk.click(); + break; + case sdk.game.locations.PleaseRead: + Controls.PleaseReadOk.click(); - break; - case sdk.game.locations.PleaseRead: - Controls.PleaseReadOk.click(); + break; + case sdk.game.locations.RegisterEmail: + let { profiles, realms } = Developer.setEmail; + if (Developer.setEmail.enabled + && (!profiles.length || profiles.includes(me.profile)) + && (!realms.length || realms.includes(Profile().gateway.toLowerCase()))) { + ControlAction.setEmail(); + } else { + Controls.EmailDontRegisterContinue.control + ? Controls.EmailDontRegisterContinue.click() + : Controls.EmailDontRegister.click(); + } - break; - case sdk.game.locations.RegisterEmail: - if (Developer.setEmail.enabled - && (!Developer.setEmail.profiles.length || Developer.setEmail.profiles.includes(me.profile)) - && (!Developer.setEmail.realms.length || Developer.setEmail.realms.includes(Profile().gateway.toLowerCase()))) { - ControlAction.setEmail(); - } else { - Controls.EmailDontRegisterContinue.control ? Controls.EmailDontRegisterContinue.click() : Controls.EmailDontRegister.click(); + break; + default: + break; } - break; - default: - break; + delay(100); } - delay(100); + return true; + } finally { + me.blockMouse = false; } - - me.blockMouse = false; - - return true; }; + /** + * @param {CharInfo} info + * @returns {boolean} + */ ControlAction.deleteAndRemakeChar = function (info) { - /** @type {Control} */ - let control = ControlAction.findCharacter(info); - if (!control) return false; - let cInfo = control.getText(); - console.debug(cInfo); - - me.blockMouse = true; - - control.click(); - Controls.CharSelectDelete.click(); - delay(500); - Controls.PopupYes.click(); - - me.blockMouse = false; + if (!ControlAction.deleteCharacter(info)) { + return false; + } // Delete old files - leaving csv file's for now as I don't think they interfere with the overlay CharData.delete(true); @@ -554,6 +579,10 @@ const locations = {}; return ControlAction.makeCharacter(Starter.profileInfo); }; + /** + * @param {CharInfo} info + * @returns {void} + */ ControlAction.saveInfo = function (info) { // Data-file already exists if (FileTools.exists("logs/Kolbot-SoloPlay/" + info.realm + "/" + info.charClass + "-" + info.charClass + "-" + info.charName + ".json")) { @@ -577,94 +606,93 @@ const locations = {}; } }; + /** + * @param {CharInfo} info + * @returns {boolean} + */ ControlAction.loginAccount = function (info) { - me.blockMouse = true; - - let locTick; - let tick = getTickCount(); - let realms = { "uswest": 0, "useast": 1, "asia": 2, "europe": 3 }; + try { + me.blockMouse = true; + let tick = getTickCount(); - MainLoop: - while (true) { - switch (getLocation()) { - case sdk.game.locations.PreSplash: - break; - case sdk.game.locations.MainMenu: - info.realm && ControlAction.clickRealm(realms[info.realm]); - Controls.BattleNet.click(); + MainLoop: + while (true) { + switch (getLocation()) { + case sdk.game.locations.PreSplash: + break; + case sdk.game.locations.MainMenu: + info.realm && ControlAction.clickRealm(ControlAction.realms[info.realm]); + Controls.BattleNet.click(); - break; - case sdk.game.locations.Login: - Controls.EnterAccountName.setText(info.account); - Controls.EnterAccountPassword.setText(info.password); - Controls.Login.click(); + break; + case sdk.game.locations.Login: + Controls.EnterAccountName.setText(info.account); + Controls.EnterAccountPassword.setText(info.password); + Controls.Login.click(); - break; - case sdk.game.locations.LoginUnableToConnect: - case sdk.game.locations.RealmDown: - // Unable to connect, let the caller handle it. - me.blockMouse = false; + break; + case sdk.game.locations.LoginUnableToConnect: + case sdk.game.locations.RealmDown: + // Unable to connect, let the caller handle it. + return false; + case sdk.game.locations.CharSelect: + break MainLoop; + case sdk.game.locations.SplashScreen: + Controls.SplashScreen.click(); - return false; - case sdk.game.locations.CharSelect: - break MainLoop; - case sdk.game.locations.SplashScreen: - Controls.SplashScreen.click(); + break; + case sdk.game.locations.CharSelectPleaseWait: + case sdk.game.locations.MainMenuConnecting: + case sdk.game.locations.CharSelectConnecting: + break; + case sdk.game.locations.CharSelectNoChars: + // make sure we're not on connecting screen + let locTick = getTickCount(); - break; - case sdk.game.locations.CharSelectPleaseWait: - case sdk.game.locations.MainMenuConnecting: - case sdk.game.locations.CharSelectConnecting: - break; - case sdk.game.locations.CharSelectNoChars: - // make sure we're not on connecting screen - locTick = getTickCount(); + while (getTickCount() - locTick < 3000 && getLocation() === sdk.game.locations.CharSelectNoChars) { + delay(25); + } - while (getTickCount() - locTick < 3000 && getLocation() === sdk.game.locations.CharSelectNoChars) { - delay(25); - } + if (getLocation() === sdk.game.locations.CharSelectConnecting) { + break; + } - if (getLocation() === sdk.game.locations.CharSelectConnecting) { + break MainLoop; // break if we're sure we're on empty char screen + case sdk.game.locations.Lobby: + case sdk.game.locations.LobbyChat: + // somehow we are in the lobby? + Control.LobbyQuit.click(); + break; - } + default: + console.log(getLocation()); - break MainLoop; // break if we're sure we're on empty char screen - case sdk.game.locations.Lobby: - case sdk.game.locations.LobbyChat: - // somehow we are in the lobby? - Control.LobbyQuit.click(); - - break; - default: - console.log(getLocation()); + return false; + } - me.blockMouse = false; + if (getTickCount() - tick >= 20000) { + return false; + } - return false; + delay(100); } - if (getTickCount() - tick >= 20000) { - return false; - } + delay(1000); - delay(100); + return getLocation() === sdk.game.locations.CharSelect || getLocation() === sdk.game.locations.CharSelectNoChars; + } finally { + me.blockMouse = false; } - - delay(1000); - - me.blockMouse = false; - - return getLocation() === sdk.game.locations.CharSelect || getLocation() === sdk.game.locations.CharSelectNoChars; }; Starter.randomNumberString = function (len) { len === undefined && (len = rand(2, 5)); let rval = ""; - let vals = "0123456789"; + const vals = "0123456789".split(""); for (let i = 0; i < len; i += 1) { - rval += vals[rand(0, vals.length - 1)]; + rval += vals.random(); } return rval; @@ -685,7 +713,13 @@ const locations = {}; Starter.BNET = ([sdk.game.profiletype.Battlenet, sdk.game.profiletype.OpenBattlenet].includes(Profile().type)); Starter.LocationEvents.oogCheck = function () { - return (AutoMule.outOfGameCheck() || TorchSystem.outOfGameCheck() || Gambling.outOfGameCheck() || CraftingSystem.outOfGameCheck() || SoloEvents.outOfGameCheck()); + return ( + AutoMule.outOfGameCheck() + || TorchSystem.outOfGameCheck() + || Gambling.outOfGameCheck() + || CraftingSystem.outOfGameCheck() + || SoloEvents.outOfGameCheck() + ); }; Starter.checkDifficulty = function () { @@ -719,7 +753,9 @@ const locations = {}; } // Multiple realm botting fix in case of R/D or disconnect - Starter.firstLogin && getLocation() === sdk.game.locations.Login && Controls.BottomLeftExit.click(); + if (Starter.firstLogin && getLocation() === sdk.game.locations.Login) { + Controls.BottomLeftExit.click(); + } D2Bot.updateStatus("Logging In"); @@ -759,8 +795,12 @@ const locations = {}; // new account if (Starter.profileInfo.account === "") { if (Starter.Config.GlobalAccount || Starter.Config.GlobalAccountPassword) { - Starter.profileInfo.account = Starter.Config.GlobalAccount.length > 0 ? Starter.Config.GlobalAccount + Starter.randomNumberString(Starter.Config.AccountSuffixLength) : Starter.randomString(12, true); - Starter.profileInfo.password = Starter.Config.GlobalAccountPassword.length > 0 ? Starter.Config.GlobalAccountPassword : Starter.randomString(12, true); + Starter.profileInfo.account = Starter.Config.GlobalAccount.length > 0 + ? Starter.Config.GlobalAccount + Starter.randomNumberString(Starter.Config.AccountSuffixLength) + : Starter.randomString(12, true); + Starter.profileInfo.password = Starter.Config.GlobalAccountPassword.length > 0 + ? Starter.Config.GlobalAccountPassword + : Starter.randomString(12, true); try { if (Starter.profileInfo.account.length > 15) throw new Error("Account name exceeds MAXIMUM length (15). Please enter a shorter name or reduce the AccountSuffixLength under StarterConfig"); @@ -835,16 +875,17 @@ const locations = {}; Starter.accountExists = false; Starter.LocationEvents.loginError = function () { + let cdkeyError = false; + let defaultPrint = true; let string = ""; - let text = Controls.LoginErrorText.getText(); + let text = getLocation() === sdk.game.locations.LoginError + ? Controls.LoginErrorText.getText() + : Controls.LoginCdKeyInUseBy.getText(); if (text) { - for (let i = 0; i < text.length; i++) { + for (let i = 0; i < text.length; i += 1) { string += text[i]; - - if (i !== text.length - 1) { - string += " "; - } + i !== text.length - 1 && (string += " "); } switch (string) { @@ -895,77 +936,69 @@ const locations = {}; cdkeyError = true; } else { Controls.UnableToConnectOk.click(); - ControlAction.timeoutDelay("LoD key in use", Starter.Config.CDKeyInUseDelay * 6e4); + ControlAction.timeoutDelay("key in use", Starter.Config.CDKeyInUseDelay * 6e4); return; } break; - case getLocaleString(5202): // cd key intended for another product - case getLocaleString(10915): // lod key intended for another product - D2Bot.updateStatus("Invalid CDKey"); - D2Bot.printToConsole("Invalid CDKey: " + Starter.gameInfo.mpq, sdk.colors.D2Bot.Gold); - D2Bot.CDKeyDisabled(); - - if (Starter.gameInfo.switchKeys) { - ControlAction.timeoutDelay("Key switch delay", Starter.Config.SwitchKeyDelay * 1000); - D2Bot.restart(true); - } else { - D2Bot.stop(); - } - - break; - case getLocaleString(5199): - D2Bot.updateStatus("Disabled CDKey"); - D2Bot.printToConsole("Disabled CDKey: " + Starter.gameInfo.mpq, sdk.colors.D2Bot.Gold); - D2Bot.CDKeyDisabled(); - - if (Starter.gameInfo.switchKeys) { - ControlAction.timeoutDelay("Key switch delay", Starter.Config.SwitchKeyDelay * 1000); - D2Bot.restart(true); - } else { - D2Bot.stop(); - } + case getLocaleString(sdk.locale.text.CdKeyIntendedForAnotherProduct): + case getLocaleString(sdk.locale.text.LoDKeyIntendedForAnotherProduct): + case getLocaleString(sdk.locale.text.CdKeyDisabled): + case getLocaleString(sdk.locale.text.LoDKeyDisabled): + cdkeyError = true; break; - case getLocaleString(10913): - D2Bot.updateStatus("Disabled LoD CDKey"); - D2Bot.printToConsole("Disabled LoD CDKey: " + Starter.gameInfo.mpq, sdk.colors.D2Bot.Gold); - D2Bot.CDKeyDisabled(); - - if (Starter.gameInfo.switchKeys) { - ControlAction.timeoutDelay("Key switch delay", Starter.Config.SwitchKeyDelay * 1000); - D2Bot.restart(true); - } else { - D2Bot.stop(); - } - - break; - case getLocaleString(5347): - D2Bot.updateStatus("Disconnected from battle.net."); - D2Bot.printToConsole("Disconnected from battle.net."); + case getLocaleString(sdk.locale.text.Disconnected): + D2Bot.updateStatus("Disconnected"); + D2Bot.printToConsole("Disconnected"); Controls.OkCentered.click(); Controls.LoginErrorOk.click(); + ControlAction.timeoutDelay("Disconnected", (rand(3, 5)) * 60000); return; + case getLocaleString(sdk.locale.text.LoginError): + case getLocaleString(sdk.locale.text.BattlenetNotResponding): + case getLocaleString(sdk.locale.text.BattlenetNotResponding2): + case getLocaleString(sdk.locale.text.OnlyOneInstanceAtATime): + Controls.LoginErrorOk.click(); + Controls.BottomLeftExit.click(); + D2Bot.printToConsole(string); + ControlAction.timeoutDelay("Login Error Delay", 5 * 6e4); + D2Bot.printToConsole("Login Error - Restart"); + D2Bot.restart(); + + break; default: D2Bot.updateStatus("Login Error"); D2Bot.printToConsole("Login Error - " + string); + cdkeyError = true; + defaultPrint = false; + + break; + } + + if (cdkeyError) { + defaultPrint && D2Bot.printToConsole(string + Starter.gameInfo.mpq, sdk.colors.D2Bot.Gold); + defaultPrint && D2Bot.updateStatus(string); + D2Bot.CDKeyDisabled(); if (Starter.gameInfo.switchKeys) { ControlAction.timeoutDelay("Key switch delay", Starter.Config.SwitchKeyDelay * 1000); D2Bot.restart(true); } else { D2Bot.stop(); } - - break; } } Controls.LoginErrorOk.click(); delay(1000); Controls.BottomLeftExit.click(); + + while (true) { + delay(1000); + } }; Starter.LocationEvents.charSelect = function (loc) { @@ -1065,10 +1098,12 @@ const locations = {}; Starter.chanInfo.afterMsg = Starter.Config.AfterGameMessage; if (Starter.chanInfo.afterMsg) { - !Array.isArray(Starter.chanInfo.afterMsg) && (Starter.chanInfo.afterMsg = [Starter.chanInfo.afterMsg]); + if (!Array.isArray(Starter.chanInfo.afterMsg)) { + Starter.chanInfo.afterMsg = [Starter.chanInfo.afterMsg]; + } - for (let i = 0; i < Starter.chanInfo.afterMsg.length; i++) { - Starter.sayMsg(Starter.chanInfo.afterMsg[i]); + for (let msg of Starter.chanInfo.afterMsg) { + Starter.sayMsg(msg); delay(500); } } @@ -1107,148 +1142,359 @@ const locations = {}; Starter.chanInfo.announce = Starter.Config.AnnounceGames; if (Starter.chanInfo.announce) { - Starter.sayMsg("Next game is " + Starter.gameInfo.gameName + Starter.gameCount + (Starter.gameInfo.gamePass === "" ? "" : "//" + Starter.gameInfo.gamePass)); + let { gameName, gamePass } = Starter.gameInfo; + Starter.sayMsg( + "Next game is " + gameName + Starter.gameCount + (gamePass === "" ? "" : "//" + gamePass) + ); } Starter.LocationEvents.openCreateGameWindow(); }; const oogCheck = () => ( - AutoMule.outOfGameCheck() || TorchSystem.outOfGameCheck() - || Gambling.outOfGameCheck() || CraftingSystem.outOfGameCheck() || SoloEvents.outOfGameCheck() + AutoMule.outOfGameCheck() + || TorchSystem.outOfGameCheck() + || Gambling.outOfGameCheck() + || CraftingSystem.outOfGameCheck() + || SoloEvents.outOfGameCheck() ); - locations[sdk.game.locations.PreSplash] = () => ControlAction.click(); - locations[sdk.game.locations.GatewaySelect] = () => Controls.GatewayCancel.click(); - locations[sdk.game.locations.SplashScreen] = () => Starter.LocationEvents.login(); - locations[sdk.game.locations.MainMenu] = () => Starter.LocationEvents.login(); - locations[sdk.game.locations.Login] = () => Starter.LocationEvents.login(); - locations[sdk.game.locations.OtherMultiplayer] = () => Starter.LocationEvents.otherMultiplayerSelect(); - locations[sdk.game.locations.TcpIp] = () => Profile().type === sdk.game.profiletype.TcpIpHost ? Controls.TcpIpHost.click() : Controls.TcpIpCancel.click(); - locations[sdk.game.locations.TcpIpEnterIp] = () => Controls.TcpIpCancel.click(); - locations[sdk.game.locations.LoginError] = () => Starter.LocationEvents.loginError(); - locations[sdk.game.locations.LoginUnableToConnect] = () => Starter.LocationEvents.unableToConnect(); - locations[sdk.game.locations.TcpIpUnableToConnect] = () => Starter.LocationEvents.unableToConnect(); - locations[sdk.game.locations.CdKeyInUse] = () => Starter.LocationEvents.loginError(); - locations[sdk.game.locations.InvalidCdKey] = () => Starter.LocationEvents.loginError(); - locations[sdk.game.locations.RealmDown] = () => Starter.LocationEvents.realmDown(); - locations[sdk.game.locations.Disconnected] = () => { - ControlAction.timeoutDelay("Disconnected", 3000); - Controls.OkCentered.click(); - }; - locations[sdk.game.locations.RegisterEmail] = () => Controls.EmailDontRegisterContinue.control ? Controls.EmailDontRegisterContinue.click() : Controls.EmailDontRegister.click(); - locations[sdk.game.locations.MainMenuConnecting] = (loc) => !Starter.locationTimeout(Starter.Config.ConnectingTimeout * 1e3, loc) && Controls.LoginCancelWait.click(); - locations[sdk.game.locations.CharSelectPleaseWait] = (loc) => !Starter.locationTimeout(Starter.Config.PleaseWaitTimeout * 1e3, loc) && Controls.OkCentered.click(); - locations[sdk.game.locations.CharSelect] = (loc) => Starter.LocationEvents.charSelect(loc); - locations[sdk.game.locations.CharSelectConnecting] = (loc) => Starter.LocationEvents.charSelect(loc); - locations[sdk.game.locations.CharSelectNoChars] = (loc) => Starter.LocationEvents.charSelect(loc); - locations[sdk.game.locations.SelectDifficultySP] = () => Starter.LocationEvents.selectDifficultySP(); - locations[sdk.game.locations.CharacterCreate] = (loc) => !Starter.locationTimeout(5e3, loc) && Controls.BottomLeftExit.click(); - locations[sdk.game.locations.ServerDown] = () => { - ControlAction.timeoutDelay("Server Down", Time.minutes(5)); - Controls.OkCentered.click(); - }; - locations[sdk.game.locations.LobbyPleaseWait] = (loc) => !Starter.locationTimeout(Starter.Config.PleaseWaitTimeout * 1e3, loc) && Controls.OkCentered.click(); - locations[sdk.game.locations.Lobby] = () => { - D2Bot.updateStatus("Lobby"); - ControlAction.saveInfo(Starter.profileInfo); + const _locations = new Map([ + [ + sdk.game.locations.PreSplash, + function () { + ControlAction.click(); + } + ], + [ + sdk.game.locations.GatewaySelect, + function () { + Controls.GatewayCancel.click(); + } + ], + [ + sdk.game.locations.SplashScreen, + function () { + Starter.LocationEvents.login(); + } + ], + [ + sdk.game.locations.MainMenu, + function () { + Starter.LocationEvents.login(); + } + ], + [ + sdk.game.locations.Login, + function () { + Starter.LocationEvents.login(); + } + ], + [ + sdk.game.locations.OtherMultiplayer, + function () { + Starter.LocationEvents.otherMultiplayerSelect(); + } + ], + [ + sdk.game.locations.TcpIp, + function () { + Controls.TcpIpHost.click(); + } + ], + [ + sdk.game.locations.TcpIpEnterIp, + function () { + Controls.TcpIpCancel.click(); + } + ], + [ + sdk.game.locations.LoginError, + function () { + Starter.LocationEvents.loginError(); + } + ], + [ + sdk.game.locations.LoginUnableToConnect, + function () { + Starter.LocationEvents.unableToConnect(); + } + ], + [ + sdk.game.locations.TcpIpUnableToConnect, + function () { + Starter.LocationEvents.unableToConnect(); + } + ], + [ + sdk.game.locations.CdKeyInUse, + function () { + Starter.LocationEvents.loginError(); + } + ], + [ + sdk.game.locations.InvalidCdKey, + function () { + Starter.LocationEvents.loginError(); + } + ], + [ + sdk.game.locations.RealmDown, + function () { + Starter.LocationEvents.realmDown(); + } + ], + [ + sdk.game.locations.Disconnected, + function () { + ControlAction.timeoutDelay("Disconnected", 3000); + Controls.OkCentered.click(); + } + ], + [ + sdk.game.locations.RegisterEmail, + function () { + Controls.EmailDontRegisterContinue.control + ? Controls.EmailDontRegisterContinue.click() + : Controls.EmailDontRegister.click(); + } + ], + [ + sdk.game.locations.MainMenuConnecting, + function (loc) { + (!Starter.locationTimeout(Starter.Config.ConnectingTimeout * 1e3, loc) + && Controls.LoginCancelWait.click()); + } + ], + [ + sdk.game.locations.CharSelectPleaseWait, + function (loc) { + (!Starter.locationTimeout(Starter.Config.PleaseWaitTimeout * 1e3, loc) + && Controls.OkCentered.click()); + } + ], + [ + sdk.game.locations.CharSelect, + function (loc) { + Starter.LocationEvents.charSelect(loc); + } + ], + [ + sdk.game.locations.CharSelectConnecting, + function (loc) { + Starter.LocationEvents.charSelect(loc); + } + ], + [ + sdk.game.locations.CharSelectNoChars, + function (loc) { + Starter.LocationEvents.charSelect(loc); + } + ], + [ + sdk.game.locations.SelectDifficultySP, + function () { + Starter.LocationEvents.selectDifficultySP(); + } + ], + [ + sdk.game.locations.CharacterCreate, + function (loc) { + if (!Starter.locationTimeout(Time.seconds(5), loc)) { + Controls.BottomLeftExit.click(); + } + } + ], + [ + sdk.game.locations.ServerDown, + function () { + ControlAction.timeoutDelay("Server Down", Time.minutes(5)); + Controls.OkCentered.click(); + } + ], + [ + sdk.game.locations.LobbyPleaseWait, + function (loc) { + (!Starter.locationTimeout(Starter.Config.PleaseWaitTimeout * 1e3, loc) + && Controls.OkCentered.click()); + } + ], + [ + sdk.game.locations.Lobby, + function () { + D2Bot.updateStatus("Lobby"); + ControlAction.saveInfo(Starter.profileInfo); - me.blockKeys = false; + me.blockKeys = false; - !Starter.firstLogin && (Starter.firstLogin = true); - Starter.lastGameStatus === "pending" && (Starter.gameCount += 1); + !Starter.firstLogin && (Starter.firstLogin = true); + Starter.lastGameStatus === "pending" && (Starter.gameCount += 1); - if (Starter.Config.PingQuitDelay && Starter.pingQuit) { - ControlAction.timeoutDelay("Ping Delay", Starter.Config.PingQuitDelay * 1e3); - Starter.pingQuit = false; - } + if (Starter.Config.PingQuitDelay && Starter.pingQuit) { + ControlAction.timeoutDelay("Ping Delay", Starter.Config.PingQuitDelay * 1e3); + Starter.pingQuit = false; + } - if (Starter.Config.JoinChannel !== "" && Controls.LobbyEnterChat.click()) return; + if (Starter.Config.JoinChannel !== "" && Controls.LobbyEnterChat.click()) return; - if (Starter.inGame || Starter.gameInfo.error) { - !Starter.gameStart && (Starter.gameStart = DataFile.getStats().ingameTick); + if (Starter.inGame || Starter.gameInfo.error) { + !Starter.gameStart && (Starter.gameStart = DataFile.getStats().ingameTick); - if (getTickCount() - Starter.gameStart < Starter.Config.MinGameTime * 1e3 && !joinInfo) { - ControlAction.timeoutDelay("Min game time wait", Starter.Config.MinGameTime * 1e3 + Starter.gameStart - getTickCount()); - } - } + if (getTickCount() - Starter.gameStart < Starter.Config.MinGameTime * 1e3 && !joinInfo) { + let _waitTime = Starter.Config.MinGameTime * 1e3 + Starter.gameStart - getTickCount(); + ControlAction.timeoutDelay("Min game time wait", _waitTime); + } + } - if (Starter.inGame) { - if (oogCheck()) return; + if (Starter.inGame) { + if (oogCheck()) return; - D2Bot.updateRuns(); + D2Bot.updateRuns(); - Starter.gameCount += 1; - Starter.lastGameStatus = "ready"; - Starter.inGame = false; + Starter.gameCount += 1; + Starter.lastGameStatus = "ready"; + Starter.inGame = false; - if (Starter.Config.ResetCount && Starter.gameCount > Starter.Config.ResetCount) { - Starter.gameCount = 1; - DataFile.updateStats("runs", Starter.gameCount); + if (Starter.Config.ResetCount && Starter.gameCount > Starter.Config.ResetCount) { + Starter.gameCount = 1; + DataFile.updateStats("runs", Starter.gameCount); + } + } + + Starter.LocationEvents.openCreateGameWindow(); } - } + ], + [ + sdk.game.locations.LobbyChat, + function () { + Starter.LocationEvents.lobbyChat(); + } + ], + [ + sdk.game.locations.CreateGame, + function (loc) { + ControlAction.timeoutDelay("Create Game Delay", Starter.Config.DelayBeforeLogin * 1e3); + D2Bot.updateStatus("Creating Game"); + + if (typeof Starter.Config.CharacterDifference === "number") { + if (Controls.CharacterDifference.disabled === sdk.game.controls.Disabled) { + Controls.CharacterDifferenceButton.click(); + } + Controls.CharacterDifference.setText(Starter.Config.CharacterDifference.toString()); + } else if (!Starter.Config.CharacterDifference && Controls.CharacterDifference.disabled === 5) { + Controls.CharacterDifferenceButton.click(); + } - Starter.LocationEvents.openCreateGameWindow(); - }; - locations[sdk.game.locations.LobbyChat] = () => Starter.LocationEvents.lobbyChat(); - locations[sdk.game.locations.CreateGame] = (loc) => { - ControlAction.timeoutDelay("Create Game Delay", Starter.Config.DelayBeforeLogin * 1e3); - D2Bot.updateStatus("Creating Game"); - - if (typeof Starter.Config.CharacterDifference === "number") { - Controls.CharacterDifference.disabled === sdk.game.controls.Disabled && Controls.CharacterDifferenceButton.click(); - Controls.CharacterDifference.setText(Starter.Config.CharacterDifference.toString()); - } else if (!Starter.Config.CharacterDifference && Controls.CharacterDifference.disabled === 5) { - Controls.CharacterDifferenceButton.click(); - } + if (typeof Starter.Config.MaxPlayerCount === "number") { + Controls.MaxPlayerCount.setText(Starter.Config.MaxPlayerCount.toString()); + } - typeof Starter.Config.MaxPlayerCount === "number" && Controls.MaxPlayerCount.setText(Starter.Config.MaxPlayerCount.toString()); + D2Bot.requestGameInfo(); + delay(500); + + // todo - really don't need use profiles set difficulty for online. Only single player so re-write difficulty stuff + Starter.checkDifficulty(); - D2Bot.requestGameInfo(); - delay(500); - - // todo - really don't need use profiles set difficulty for online. Only single player so re-write difficulty stuff - Starter.checkDifficulty(); + Starter.gameInfo.gameName = DataFile.getStats().gameName; + Starter.gameInfo.gamePass = Starter.randomString(5, true); - Starter.gameInfo.gameName = DataFile.getStats().gameName; - Starter.gameInfo.gamePass = Starter.randomString(5, true); + if (!Starter.gameInfo.gameName || String.isEqual(Starter.gameInfo.gameName, "name")) { + Starter.gameInfo.gameName = ( + Starter.profileInfo.charName.substring(0, 7) + "-" + + Starter.randomString(3, false) + "-" + ); + } - switch (true) { - case Starter.gameInfo.gameName === "": - case Starter.gameInfo.gameName === "Name": - Starter.gameInfo.gameName = Starter.profileInfo.charName.substring(0, 7) + "-" + Starter.randomString(3, false) + "-"; + // FTJ handler + if (Starter.lastGameStatus === "pending") { + Starter.isUp = "no"; - break; - } + D2Bot.printToConsole("Failed to create game"); + ControlAction.timeoutDelay("FTJ delay", Starter.Config.FTJDelay * 1e3); + D2Bot.updateRuns(); + } - // FTJ handler - if (Starter.lastGameStatus === "pending") { - Starter.isUp = "no"; + let [gameName, gamePass, difficulty, gameDelay] = [ + (Starter.gameInfo.gameName + Starter.gameCount), + Starter.gameInfo.gamePass, + Starter.gameInfo.difficulty, + Time.seconds(Starter.Config.CreateGameDelay) + ]; + + ControlAction.createGame(gameName, gamePass, difficulty, gameDelay); + Starter.lastGameStatus = "pending"; + Starter.setNextGame(Starter.gameInfo); + Starter.locationTimeout(10000, loc); + } + ], + [ + sdk.game.locations.GameNameExists, + function () { + Controls.CreateGameWindow.click(); + Starter.gameCount += 1; + Starter.lastGameStatus = "ready"; + } + ], + [ + sdk.game.locations.WaitingInLine, + function () { + Starter.LocationEvents.waitingInLine(); + } + ], + [ + sdk.game.locations.JoinGame, + function () { + Starter.LocationEvents.openCreateGameWindow(); + } + ], + [ + sdk.game.locations.Ladder, + function () { + Starter.LocationEvents.openCreateGameWindow(); + } + ], + [ + sdk.game.locations.ChannelList, + function () { + Starter.LocationEvents.openCreateGameWindow(); + } + ], + [ + sdk.game.locations.LobbyLostConnection, + function () { + ControlAction.timeoutDelay("LostConnection", 3000); + Controls.OkCentered.click(); + } + ], + [ + sdk.game.locations.GameDoesNotExist, + function () { + Starter.LocationEvents.gameDoesNotExist(); + } + ], + [ + sdk.game.locations.GameIsFull, + function () { + Starter.LocationEvents.openCreateGameWindow(); + } + ], + ]); - D2Bot.printToConsole("Failed to create game"); - ControlAction.timeoutDelay("FTJ delay", Starter.Config.FTJDelay * 1e3); - D2Bot.updateRuns(); + /** + * Actual definition for LocationAction.run + */ + LocationAction.run = function () { + try { + let loc = getLocation(); + let func = _locations.get(loc); + if (typeof func === "function") { + func(loc); + } else { + console.log("Unhandled location: " + loc); + } + } catch (e) { + console.error(e); } - - ControlAction.createGame((Starter.gameInfo.gameName + Starter.gameCount), Starter.gameInfo.gamePass, Starter.gameInfo.difficulty, Starter.Config.CreateGameDelay * 1000); - Starter.lastGameStatus = "pending"; - Starter.setNextGame(Starter.gameInfo); - Starter.locationTimeout(10000, loc); - }; - locations[sdk.game.locations.GameNameExists] = () => { - Controls.CreateGameWindow.click(); - Starter.gameCount += 1; - Starter.lastGameStatus = "ready"; - }; - locations[sdk.game.locations.WaitingInLine] = () => Starter.LocationEvents.waitingInLine(); - locations[sdk.game.locations.JoinGame] = () => Starter.LocationEvents.openCreateGameWindow(); - locations[sdk.game.locations.Ladder] = () => Starter.LocationEvents.openCreateGameWindow(); - locations[sdk.game.locations.ChannelList] = () => Starter.LocationEvents.openCreateGameWindow(); - locations[sdk.game.locations.LobbyLostConnection] = () => { - ControlAction.timeoutDelay("LostConnection", 3000); - Controls.OkCentered.click(); }; - locations[sdk.game.locations.GameDoesNotExist] = () => Starter.LocationEvents.gameDoesNotExist(); - locations[sdk.game.locations.GameIsFull] = () => Starter.LocationEvents.openCreateGameWindow(); })(); From e88e4ab04339c67e4a70e8e7c4d265e833e20337 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Mon, 10 Jul 2023 15:18:38 -0400 Subject: [PATCH 191/263] Update PickitOverrides.js - using `me.getOwned`, going to depreciate `Item.getQuantityOwned` --- libs/SoloPlay/Functions/PickitOverrides.js | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/libs/SoloPlay/Functions/PickitOverrides.js b/libs/SoloPlay/Functions/PickitOverrides.js index 19793353..b4ba6bf6 100644 --- a/libs/SoloPlay/Functions/PickitOverrides.js +++ b/libs/SoloPlay/Functions/PickitOverrides.js @@ -75,18 +75,19 @@ Pickit.checkItem = function (unit) { if (unit.classid === sdk.items.StaminaPotion && (me.charlvl < 18 || me.staminaPercent <= 85 || me.walking) - && Item.getQuantityOwned(unit, true) < 2) { + && me.getOwned(unit, true).length < 2) { return resultObj(Pickit.Result.WANTED, "LowStamina"); } if (unit.classid === sdk.items.AntidotePotion - && me.getState(sdk.states.Poison) && Item.getQuantityOwned(unit, true) < 2) { + && me.getState(sdk.states.Poison) + && me.getOwned(unit, true).length < 2) { return resultObj(Pickit.Result.WANTED, "Poisoned"); } if (unit.classid === sdk.items.ThawingPotion && (me.getState(sdk.states.Frozen) || me.getState(sdk.states.FrozenSolid)) - && Item.getQuantityOwned(unit, true) < 2) { + && me.getOwned(unit, true).length < 2) { return resultObj(Pickit.Result.WANTED, "Frozen"); } From 5d05bbb88a1e6e8fdeca8c518f75a352fdadc28d Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Mon, 10 Jul 2023 15:19:27 -0400 Subject: [PATCH 192/263] index.d.ts -> globals.d.ts --- libs/SoloPlay/{index.d.ts => globals.d.ts} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename libs/SoloPlay/{index.d.ts => globals.d.ts} (100%) diff --git a/libs/SoloPlay/index.d.ts b/libs/SoloPlay/globals.d.ts similarity index 100% rename from libs/SoloPlay/index.d.ts rename to libs/SoloPlay/globals.d.ts From 7e0bf1702a62248c3a425c2d545dc35e90a1dbad Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Mon, 10 Jul 2023 15:21:26 -0400 Subject: [PATCH 193/263] typedef updates - add tsconfig so my definitions are actually recognized --- libs/SoloPlay/globals.d.ts | 307 ++++++++++++++++++++++++++++++++++-- libs/SoloPlay/tsconfig.json | 39 +++++ 2 files changed, 330 insertions(+), 16 deletions(-) create mode 100644 libs/SoloPlay/tsconfig.json diff --git a/libs/SoloPlay/globals.d.ts b/libs/SoloPlay/globals.d.ts index 62b4bb60..b518c738 100644 --- a/libs/SoloPlay/globals.d.ts +++ b/libs/SoloPlay/globals.d.ts @@ -3,6 +3,15 @@ declare global { interface Math { percentDifference(value1: number, value2: number): number; } + + interface Object { + mobCount(givenSettings?: { + range?: number, + coll?: number, + type: number, + ignoreClassids: number[], + }): number; + } interface ItemUnit { readonly isCharm: boolean; @@ -140,6 +149,8 @@ declare global { init: () => void; }; + switchToPrimary(): boolean; + switchToSecondary(): boolean; canTpToTown(): boolean; getMercEx(): MercUnit | null; getEquippedItem(bodyLoc: number): ItemUnit | null; @@ -148,6 +159,8 @@ declare global { checkSkill(skillId: number, subId: number): boolean; cleanUpInvoPotions(beltSize: number): boolean; needPotions(): boolean; + needBeltPots(): boolean; + needBufferPots(): boolean; getIdTool(): ItemUnit | null; getTpTool(): ItemUnit | null; getUnids(): ItemUnit[]; @@ -160,6 +173,14 @@ declare global { sortInventory(): boolean; cleanUpScrolls(tome: ItemUnit, scrollId: number): number; update(): void; + getOwned(itemInfo: ItemUnit | { + itemType?: number, + classid?: number, + quality?: number, + sockets?: number, + location?: number, + ethereal?: boolean, + }): ItemUnit[]; } interface Container { @@ -225,13 +246,6 @@ declare global { actMap: Map; } - type Charge = { - skill: number; - level: number; - charges: number; - maxcharges: number; - }; - namespace Mercenary { let minCost: number; @@ -246,8 +260,7 @@ declare global { namespace Misc { let townEnabled: boolean; let openChestsEnabled: boolean; - const presetShrineIds: number[]; - const presetChestIds: number[]; + const shrineStates: number[]; function openChestsInArea(area: number, chestIds: number[], sort?: Function): boolean; function getExpShrine(shrineLocs: number[]): boolean; @@ -269,13 +282,97 @@ declare global { } namespace Skill { - function switchCast(skillId: number, givenSettings: { hand?: number, x?: number, y?: number, switchBack?: boolean, oSkill?: boolean }): boolean; + function switchCast( + skillId: number, + givenSettings: { hand?: number, x?: number, y?: number, switchBack?: boolean, oSkill?: boolean } + ): boolean; } + type pathSettings = { + allowNodeActions?: boolean; + allowTeleport?: boolean; + allowClearing?: boolean; + allowTown?: boolean; + allowPicking?: boolean; + minDist?: number; + retry?: number; + pop?: boolean; + returnSpotOnError?: boolean; + callback?: Function; + clearSettings?: clearSettings; + }; + type clearSettings = { + clearPath?: boolean; + range?: number; + specType?: number; + sort?: Function; + }; + namespace Pather { + let initialized: boolean; + function canTeleport(): boolean; + function teleUsingCharges(x: number, y: number, maxRange: number): boolean; + function changeAct(act: number): boolean; + function checkWP(area: number, keepMenuOpen?: boolean): boolean; function clearToExit(currentarea: number, targetarea: number, givenSettings: pathSettings): boolean; } + namespace NodeAction { + const shrinesToIgnore: number[]; + let enabled: boolean; + + function go(arg: clearSettings): void; + } + + namespace PathDebug { + function coordsInPath(path: PathNode[], x: number, y: number): boolean; + } + + namespace Pickit { + function pickItem( + unit: ItemUnit, + status: PickitResult, + keptLine?: string, + givenSettings?: { allowClear: boolean, allowMove: boolean } + ): boolean; + } + + namespace Attack { + function clearPos( + x: number, + y: number, + range?: number, + pickit?: boolean, + cb?: function(): boolean, + ): boolean; + function killTarget(name: Monster | string | number): boolean; + } + + namespace ClassAttack { + function doAttack(unit: Monster): AttackResult; + function doAttack(unit: Monster, precast?: boolean): AttackResult; + function doAttack(unit: Monster, recheck?: boolean): AttackResult; + function doAttack(unit: Monster, precast?: boolean, once?: boolean): AttackResult; + function doCast(unit: Monster, timedSkill: number, untimedSkill: number): AttackResult; + function doCast( + unit: Monster, + choosenSkill: { have: boolean, skill: number, range: number, mana: number, timed: boolean } + ): AttackResult; + function afterAttack(pickit?: boolean): void; + } + + namespace CollMap { + function checkColl(unitA: Unit, unitB: Unit, coll: number, thickness: number): boolean; + } + + namespace Town { + function doChores(repair?: boolean, givenTasks?: extraTasks): boolean; + } + + namespace Precast { + function checkCTA(): boolean; + } + namespace CharData { const filePath: string; const threads: string[]; @@ -297,6 +394,86 @@ declare global { function _delete(deleteMain: boolean): boolean; } + namespace Developer { + const plugyMode: boolean; + const logPerformance: boolean; + const overlay: boolean; + const displayClockInConsole: boolean; + const logEquipped: boolean; + const hideChickens: boolean; + const addLadderRW: boolean; + const forcePacketCasting: { + enabled: boolean; + excludeProfiles: string[]; + }; + const fillAccount: { + bumpers: boolean; + socketMules: boolean; + imbueMule: boolean; + }; + const imbueStopLevel: number; + const stopAtLevel: { + enabled: boolean; + profiles: Array<[string, number]>; + }; + const developerMode: { + enabled: boolean; + profiles: string[]; + }; + const testingMode: { + enabled: boolean; + profiles: string[]; + }; + const setEmail: { + enabled: boolean; + profiles: string[]; + realms: string[]; + }; + const debugging: { + smallCharm: boolean; + largeCharm: boolean; + grandCharm: boolean; + baseCheck: boolean; + junkCheck: boolean; + autoEquip: boolean; + crafting: boolean; + pathing: boolean; + skills: boolean; + showStack: { + enabled: boolean; + profiles: string[]; + }; + }; + } + + namespace Tracker { + const GTPath: string; + const LPPath: string; + const SPPath: string; + const LPHeader: string; + const SPHeader: string; + const tick: number; + interface GameTracker { + Total: number; + InGame: number; + OOG: number; + LastLevel: number; + LastSave: number; + } + const _default: GameTracker; + function initialize(): boolean; + function getObj(path: string): GameTracker | false; + function readObj(jsonPath: string): GameTracker | false; + function writeObj(obj: GameTracker, path: string): boolean; + function resetGameTime(): void; + function reset(): void; + function checkValidity(): void; + function totalDays(milliseconds: number): string; + function script(starttime: number, subscript: string, startexp: number): boolean; + function leveling(): boolean; + function update(oogTick?: number): boolean; + } + namespace SetUp { let mercEnabled: boolean; const currentBuild: string; @@ -355,14 +532,18 @@ declare global { fullChores?: boolean, }; - namespace Town { - function doChores(repair?: boolean, givenTasks?: extraTasks): boolean; - } - namespace LocationAction { function run(): void; } + type PresetObjectUnit = { + x: number; + y: number; + area: number; + classid: number; + type: number; + }; + class ShrineInstance { constructor (shrine: ObjectUnit); @@ -387,8 +568,8 @@ declare global { Act: number; Level: number; Size: { - x: number; - y: number; + x: number; + y: number; }; SuperUnique: number[]; Monsters: number[]; @@ -436,5 +617,99 @@ declare global { function randomWpArea(checkValid: boolean): number; function getAreasWithShrine(shrineType: number): AreaDataInstance[]; } + + namespace GameData { + const myReference: Unit; + const townAreas: number[]; + + function monsterLevel(monsterID: number, areaID: number, adjustLevel: number): number; + function eliteExp(monsterID: number, areaID: number): number; + function monsterAvgHP(monsterID: number, areaID: number, adjustLevel: number): number; + function monsterMaxHP(monsterID: number, areaID: number, adjustLevel: number): number; + function eliteAvgHP(monsterID: number, areaID: number): number; + function monsterDamageModifier(): number; + function monsterMaxDmg(monsterID: number, areaID: number, adjustLevel: number): number; + function monsterAttack1AvgDmg(monsterID: number, areaID: number, adjustLevel: number): number; + function monsterAttack2AvgDmg(monsterID: number, areaID: number, adjustLevel: number): number; + function monsterSkill1AvgDmg(monsterID: number, areaID: number, adjustLevel: number): number; + function monsterAvgDmg(monsterID: number, areaID: number, adjustLevel: number): number; + function averagePackSize(monsterID: number): number; + function areaLevel(areaID: number): number; + function areaImmunites(areaID: number): string[]; + function levelModifier(clvl: number, mlvl: number): number; + function multiplayerModifier(count: number): number; + function partyModifier(playerID: number): number; + function killExp(playerID: number, monsterID: number, areaID: number): number; + function baseLevel(...skillIDs: number[]): number; + function skillLevel(...skillIDs: number[]): number; + function skillCooldown(skillId: number): boolean; + function stagedDamage( + l: number, + a: number, + b: number, + c: number, + d: number, + e: number, + f: number, + hitshift: number, + mult: number, + ): number; + const damageTypes: string[]; + const synergyCalc: Record; + const noMinSynergy: number[]; + const skillMult: Record; + function baseSkillDamage(skillId: number): number; + const skillRadius: Record; + const novaLike: Record; + const wolfBanned: Record; + const bearBanned: Record; + const humanBanned: Record; + const nonDamage: Record; + function shiftState(): string; + function bestForm(skillID: number): number; + function physicalAttackDamage(skillID: number): number; + function dmgModifier(skillID: number, target: Monster): number; + + interface SkillDamage { + type: string; + pmin: number; + pmax: number; + min: number; + max: number; + undeadOnly?: boolean; + } + function skillDamage(skillID: number, unit?: Monster): SkillDamage; + function avgSkillDamage(skillID: number, unit?: Monster): number; + function allSkillDamage(unit: Monster): SkillDamage[]; + const convictionEligible: Record; + const lowerResistEligible: Record; + const resistMap: Record; + const masteryMap: Record; + const pierceMap: Record; + const ignoreSkill: Record; + const buffs: Record; + const preAttackable: number[]; + function monsterResist(unit: Monster, type: string): number; + function getConviction(): number; + function getAmp(): number; + function monsterEffort( + unit: Monster, + areaID: number, + skillDamageInfo?: SkillDamage, + parent?: Monster, + preattack?: boolean, + all?: boolean, + ): { effort: number, skill: number, type: string, name?: string, cooldown?: boolean }; + function effectiveMonsterEffort( + unit: Monster, + areaID: number + ): { effort: number, skill: number, type: string, name?: string, cooldown?: boolean }; + function areaEffort(areaID: number, skills?: SkillDamage[]): number; + function areaSoloExp(areaID: number, skills?: SkillDamage[]): number; + function timeTillMissileImpact(skillId: number, monster: Monster): number; + function calculateKillableFallensByFrostNova(): number; + function calculateKillableSummonsByNova(): number; + function targetPointForSkill(skillId: number, monster: Monster): PathNode; + } } export{}; diff --git a/libs/SoloPlay/tsconfig.json b/libs/SoloPlay/tsconfig.json new file mode 100644 index 00000000..640e779b --- /dev/null +++ b/libs/SoloPlay/tsconfig.json @@ -0,0 +1,39 @@ +{ + "compilerOptions": { + "module": "None", + "lib": [ + "ES2015", + "es2015.collection", + "ES2015.Promise", + "ES2016.Array.Include", + "ES2017.Object", + "ScriptHost", + ], + "target": "ES6", + "allowJs": true, + "checkJs": false, + "moduleResolution": "classic", + "allowUmdGlobalAccess": true, + "esModuleInterop": true, + "noImplicitAny": true, + "noEmit": true, + "strict": true, + "baseUrl": "./", + // "rootDir": "./", + "outFile": "./out.js", + }, + "include": [ + "../../sdk/globals.d.ts", + "../../sdk/types/*.d.ts", + "globals.d.ts", + "*.js", + "./**/*.js", + ], + "exclude": [ + "**/ClassAttackOverrides/", + "node_modules", + "**/node_modules/*", + "**/data/", + "**/Data/", + ], +} From b5593195bda296bed9475cda3d24a2f85355869d Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Mon, 10 Jul 2023 15:47:43 -0400 Subject: [PATCH 194/263] Update bishibosh.js - add callback in case we kill bishi during nodeactions --- libs/SoloPlay/Scripts/bishibosh.js | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/libs/SoloPlay/Scripts/bishibosh.js b/libs/SoloPlay/Scripts/bishibosh.js index 5f90cdd2..34e69963 100644 --- a/libs/SoloPlay/Scripts/bishibosh.js +++ b/libs/SoloPlay/Scripts/bishibosh.js @@ -11,9 +11,18 @@ function bishibosh () { Pather.useWaypoint(sdk.areas.ColdPlains); } Precast.doPrecast(true); + const BISHIBOSH = getLocaleString(sdk.locale.monsters.Bishibosh); + let bishDead = false; - Pather.moveToPreset(sdk.areas.ColdPlains, sdk.unittype.Monster, sdk.monsters.preset.Bishibosh); - Attack.clear(15, 0, getLocaleString(sdk.locale.monsters.Bishibosh)); + Pather.moveToPresetMonster(sdk.areas.ColdPlains, sdk.monsters.preset.Bishibosh, { callback: function () { + let bishi = Game.getMonster(BISHIBOSH); + if (bishi && (bishi.distance < 10 || bishi.dead)) { + bishi.dead && (bishDead = true); + return true; + } + return false; + } }); + !bishDead && Attack.clear(15, 0, BISHIBOSH); Pickit.pickItems(); return true; From 09ca6db32bbcc58008cd84aa4d7445847a5db603 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Mon, 10 Jul 2023 15:48:38 -0400 Subject: [PATCH 195/263] Update pindle.js - move to anya first before checking for portal, was failing to detect it due to being too far away - skipTown if our next script is going to be nith --- libs/SoloPlay/Scripts/pindle.js | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/libs/SoloPlay/Scripts/pindle.js b/libs/SoloPlay/Scripts/pindle.js index 6d666dc2..53a75f24 100644 --- a/libs/SoloPlay/Scripts/pindle.js +++ b/libs/SoloPlay/Scripts/pindle.js @@ -10,6 +10,7 @@ function pindle () { Town.goToTown(5); myPrint("starting pindle"); + Town.move("anya"); !Pather.getPortal(sdk.areas.NihlathaksTemple) && Town.npcInteract("anya"); if (!Pather.usePortal(sdk.areas.NihlathaksTemple)) return true; @@ -18,5 +19,9 @@ function pindle () { Attack.killTarget(getLocaleString(sdk.locale.monsters.Pindleskin)); Pickit.pickItems(); + if (SoloIndex.index.nith.shouldRun()) { + Loader.skipTown.push("nith"); + } + return true; } From 49075c2ed596e0db2d2bfbf46ce5c858c3cd685a Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Tue, 11 Jul 2023 18:41:46 -0400 Subject: [PATCH 196/263] Update SoloIndex.js - add nith --- libs/SoloPlay/Tools/SoloIndex.js | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/libs/SoloPlay/Tools/SoloIndex.js b/libs/SoloPlay/Tools/SoloIndex.js index a1011535..fa0b4f75 100644 --- a/libs/SoloPlay/Tools/SoloIndex.js +++ b/libs/SoloPlay/Tools/SoloIndex.js @@ -729,6 +729,18 @@ const SoloIndex = { return true; } }, + "nith": { + preReq: function () { + return (me.expansion && me.accessToAct(5) && me.anya); + }, + skipIf: function () { + return !me.nightmare || !Pather.canTeleport(); + }, + shouldRun: function () { + if (!this.preReq() || this.skipIf()) return false; + return true; + } + }, "ancients": { preReq: function () { return (me.expansion && me.accessToAct(5)); From 2135ddd62ee02000f5ace9a2b424639657748bc8 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Wed, 12 Jul 2023 09:50:30 -0400 Subject: [PATCH 197/263] Update SoloIndex.js - wait until we have teleport for den --- libs/SoloPlay/Tools/SoloIndex.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libs/SoloPlay/Tools/SoloIndex.js b/libs/SoloPlay/Tools/SoloIndex.js index fa0b4f75..7ae8f48b 100644 --- a/libs/SoloPlay/Tools/SoloIndex.js +++ b/libs/SoloPlay/Tools/SoloIndex.js @@ -60,7 +60,7 @@ const SoloIndex = { "den": { skipIf: function () { /* xp is bad in den so only run it once we can blast through*/ - return me.den || (me.charlvl > 8 && me.charlvl < 12); + return me.den || (me.charlvl > 8 && me.charlvl < (me.sorceress ? 18 : 12)); }, shouldRun: function () { if (this.skipIf()) return false; From 052012d8242b058ed21be56c4290088c84ac8d7c Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Wed, 12 Jul 2023 13:18:25 -0400 Subject: [PATCH 198/263] Update AttackOverrides.js - fix canAttack for sorceress, other classes still need classattack to be reworked --- libs/SoloPlay/Functions/AttackOverrides.js | 36 ++++++++++++---------- 1 file changed, 20 insertions(+), 16 deletions(-) diff --git a/libs/SoloPlay/Functions/AttackOverrides.js b/libs/SoloPlay/Functions/AttackOverrides.js index c3f7aac1..6ab7f40d 100644 --- a/libs/SoloPlay/Functions/AttackOverrides.js +++ b/libs/SoloPlay/Functions/AttackOverrides.js @@ -188,26 +188,30 @@ Attack.checkResist = function (unit, val = -1, maxres = 100) { */ Attack.canAttack = function (unit) { if (!unit) return false; - if (unit.isMonster) { - // Unique/Champion - if (unit.isSpecial) { - if (Attack.checkResist(unit, this.getSkillElement(Config.AttackSkill[1])) - || Attack.checkResist(unit, this.getSkillElement(Config.AttackSkill[2]))) { - return true; - } - } else { - if (Attack.checkResist(unit, this.getSkillElement(Config.AttackSkill[3])) - || Attack.checkResist(unit, this.getSkillElement(Config.AttackSkill[4]))) { - return true; - } + if (!unit.isMonster) return false; + if (me.sorceress) { + return Config.AttackSkill.some(function (skill) { + return skill > -1 && Attack.checkResist(unit, Attack.getSkillElement(skill)); + }); + } + // Unique/Champion + if (unit.isSpecial) { + if (Attack.checkResist(unit, this.getSkillElement(Config.AttackSkill[1])) + || Attack.checkResist(unit, this.getSkillElement(Config.AttackSkill[2]))) { + return true; } - - if (Config.AttackSkill.length === 7) { - return Attack.checkResist(unit, this.getSkillElement(Config.AttackSkill[5])) - || Attack.checkResist(unit, this.getSkillElement(Config.AttackSkill[6])); + } else { + if (Attack.checkResist(unit, this.getSkillElement(Config.AttackSkill[3])) + || Attack.checkResist(unit, this.getSkillElement(Config.AttackSkill[4]))) { + return true; } } + if (Config.AttackSkill.length === 7) { + return Attack.checkResist(unit, this.getSkillElement(Config.AttackSkill[5])) + || Attack.checkResist(unit, this.getSkillElement(Config.AttackSkill[6])); + } + return false; }; From 08d9c176b27e1598b3d9a893d23d408b6283600c Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Thu, 13 Jul 2023 17:01:59 -0400 Subject: [PATCH 199/263] Better town chore speed for a3 - most of the time when we do town chores in a3 we end up changing acts anyway because a3 sucks. If we are going to do any of the tasks that require us to change acts then go ahead an do so. It allows us to accomplish chores faster - still needs to be cleaned up and refactored but provides the basic functionality - Keep our id tome full if we have the gold to do so, we can then speed up the process of id'ing/clearInvo in town as this allows us to use the closest npc rather than having to always visit the shop npc - fix `Town.getDistance` for call at the beggining of town chores, it was returning infinity so never got used --- libs/SoloPlay/Functions/NPCAction.js | 40 ++++++++++- libs/SoloPlay/Functions/TownOverrides.js | 86 +++++++++++++++++++----- 2 files changed, 106 insertions(+), 20 deletions(-) diff --git a/libs/SoloPlay/Functions/NPCAction.js b/libs/SoloPlay/Functions/NPCAction.js index 1cb85156..15d41c8f 100644 --- a/libs/SoloPlay/Functions/NPCAction.js +++ b/libs/SoloPlay/Functions/NPCAction.js @@ -8,6 +8,7 @@ // ugly but should handle scope issues if I decide to add this to the core in which case I can come back and remove this // but won't get immeadiate issues of trying to redefine a const (function (NPCAction) { + const PotData = require("../Modules/GameData/PotData"); /** * Easier shopping done at a specific npc * @param {string} npcName - NPC.NameOfNPC @@ -119,6 +120,16 @@ if (me.normal && me.highestAct >= 4) { let pAct = Math.max(wantedHpPot, wantedMpPot); pAct >= 4 ? me.act < 4 && Town.goToTown(4) : pAct > me.act && Town.goToTown(pAct); + } else if (!me.normal && me.act === 3 + && Town.getDistance(Town.tasks.get(me.act).Shop) > 10) { + // if we need to repair items as well or stack pots we should go ahead and change act + // unless we are already at our intended npc + let _needRepair = me.needRepair().length > 0; + let _needStack = CharData.pots.get("thawing").need() || CharData.pots.get("antidote").need(); + let _needMerc = me.needMerc(); + if (_needRepair || _needStack || _needMerc) { + Town.goToTown(me.highestAct >= 4 ? 4 : 1); + } } console.debug( @@ -138,7 +149,9 @@ let pot = npc.getItem(usePot); if (pot) { Storage.Inventory.CanFit(pot) && Packet.buyItem(pot, false); - pot = me.getItemsEx(usePot, sdk.items.mode.inStorage).filter(i => i.isInInventory).first(); + pot = me.getItemsEx(usePot, sdk.items.mode.inStorage) + .filter(i => i.isInInventory) + .first(); !!pot && Packet.placeInBelt(pot, i); pots.shift(); } else { @@ -226,12 +239,23 @@ if (have + invoScrolls >= (me.charlvl < 12 ? 5 : 13)) return true; if (me.gold < 450) return false; + if (me.act === 3 + && Town.getDistance(Town.tasks.get(me.act).Shop) > 10) { + // if we need to repair items as well or stack pots we should go ahead and change act + // unless we are already at our intended npc + let _needRepair = me.needRepair().length > 0; + let _needStack = CharData.pots.get("thawing").need() || CharData.pots.get("antidote").need(); + let _needMerc = me.needMerc(); + if (_needRepair || _needStack || _needMerc) { + Town.goToTown(me.highestAct >= 4 ? 4 : 1); + } + } + let npc = Town.initNPC("Shop", "fillTome"); if (!npc) return false; delay(500); - if (!myTome) { let tome = npc.getItem(classid); @@ -254,6 +278,7 @@ } } + /** @type {ItemUnit} */ let scroll = npc.getItem(scrollId); if (!scroll) return false; if (!myTome && !(myTome = me.getTome(classid))) return false; @@ -274,6 +299,17 @@ } } else { scroll.buy(true); + + if (scrollId !== sdk.items.ScrollofIdentify && me.gold > 10000) { + // we are already in the shop, lets check if we need id scrolls too + let idTome = me.getTome(sdk.items.TomeofIdentify); + if (idTome && idTome.getStat(sdk.stats.Quantity) < 20) { + scroll = npc.getItem(sdk.items.ScrollofIdentify); + if (scroll) { + scroll.buy(true); + } + } + } } } catch (e2) { console.error(e2); diff --git a/libs/SoloPlay/Functions/TownOverrides.js b/libs/SoloPlay/Functions/TownOverrides.js index 9a8cd467..f7fcf3de 100644 --- a/libs/SoloPlay/Functions/TownOverrides.js +++ b/libs/SoloPlay/Functions/TownOverrides.js @@ -81,7 +81,7 @@ Town.ignoredItemTypes = [ * @description Start a task and return the NPC Unit * @param {string} task * @param {string} reason - * @returns {boolean | Unit} + * @returns {boolean | NPCUnit} */ Town.initNPC = function (task = "", reason = "undefined") { console.info(true, reason, "initNPC"); @@ -234,6 +234,26 @@ Town.initNPC = function (task = "", reason = "undefined") { return npc; }; +/** +* @description Go to a town healer if we are below certain hp/mp percent or have a status effect +*/ +Town.heal = function () { + if (!me.needHealing()) return true; + if (me.act === 3 + && Town.getDistance(Town.tasks.get(me.act).Heal) > 10) { + // if we need to repair items as well or stack pots we should go ahead and change act + // unless we are already at our intended npc + let _needRepair = me.needRepair().length > 0; + let _needStack = CharData.pots.get("thawing").need() || CharData.pots.get("antidote").need(); + let _needMerc = me.needMerc(); + let _needPotions = me.normal && me.accessToAct(4) && me.needPotions(); + if (_needRepair || _needStack || _needMerc || _needPotions) { + Town.goToTown(me.highestAct >= 4 ? 4 : 1); + } + } + return !!(this.initNPC("Heal", "heal")); +}; + /** * Check if any of the systems need this item * @param {ItemUnit} item @@ -430,6 +450,19 @@ Town.identify = function () { } } + if (me.act === 3 + && Town.getDistance(Town.tasks.get(me.act).Shop) > 10) { + // if we need to repair items as well or stack pots we should go ahead and change act + // unless we are already at our intended npc + let _needRepair = me.needRepair().length > 0; + let _needStack = CharData.pots.get("thawing").need() || CharData.pots.get("antidote").need(); + let _needMerc = me.needMerc(); + let _needPotions = me.normal && me.accessToAct(4) && me.needPotions(); + if (_needRepair || _needStack || _needMerc || _needPotions) { + Town.goToTown(me.highestAct >= 4 ? 4 : 1); + } + } + let npc = Town.initNPC("Shop", "identify"); if (!npc) return false; @@ -726,19 +759,33 @@ Town.clearInventory = function () { ? sell.concat(sellOrDrop) : sellOrDrop.slice(0) ); - if (sell.length > 0 && this.initNPC("Shop", "clearInventory")) { - sell.forEach(function (item) { - try { - if (getUIFlag(sdk.uiflags.Shop) || getUIFlag(sdk.uiflags.NPCMenu)) { - console.log("clearInventory sell " + item.prettyPrint); - Item.logger("Sold", item); - item.sell(); - delay(100); - } - } catch (e) { - console.error(e); + if (sell.length > 0) { + if (me.act === 3 + && Town.getDistance(Town.tasks.get(me.act).Shop) > 10) { + // if we need to repair items as well or stack pots we should go ahead and change act + // unless we are already at our intended npc + let _needRepair = me.needRepair().length > 0; + let _needStack = CharData.pots.get("thawing").need() || CharData.pots.get("antidote").need(); + let _needMerc = me.needMerc(); + let _needPotions = me.normal && me.accessToAct(4) && me.needPotions(); + if (_needRepair || _needStack || _needMerc || _needPotions) { + Town.goToTown(me.highestAct >= 4 ? 4 : 1); } - }); + } + if (this.initNPC("Shop", "clearInventory")) { + sell.forEach(function (item) { + try { + if (getUIFlag(sdk.uiflags.Shop) || getUIFlag(sdk.uiflags.NPCMenu)) { + console.log("clearInventory sell " + item.prettyPrint); + Item.logger("Sold", item); + item.sell(); + delay(100); + } + } catch (e) { + console.error(e); + } + }); + } } Town.sell = []; @@ -925,9 +972,12 @@ Town.doChores = function (repair = false, givenTasks = {}) { // Use cainId if we are low on gold or we are closer to him than the shopNPC if (me.getUnids().length) { - if (me.gold < 5000 - || Town.getDistance("cain") < Town.getDistance(Town.tasks.get(me.act).Heal)) { - NPCAction.cainID(true); + // use our id tome if we have it first + if (!me.fieldID()) { + if (me.gold < 5000 + || Town.getDistance(NPC.Cain) < Town.getDistance(Town.tasks.get(me.act).Shop)) { + NPCAction.cainID(true); + } } } @@ -964,12 +1014,12 @@ Town.doChores = function (repair = false, givenTasks = {}) { // check pots again, we might have enough gold now if we didn't before me.needPotions() && NPCAction.buyPotions() && me.cancelUIFlags(); // check repair again, we might have enough gold now if we didn't before - me.needRepair() && NPCAction.repair() && me.cancelUIFlags(); + me.needRepair().length && NPCAction.repair() && me.cancelUIFlags(); me.sortInventory(); Quest.characterRespec(); - me.act !== preAct && this.goToTown(preAct); + me.act !== preAct && Town.goToTown(preAct); me.cancelUIFlags(); !me.barbarian && !Precast.checkCTA() && Precast.doPrecast(false); From 45af95669964cfd3fbbaadfff62df521dfebfdee Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Thu, 13 Jul 2023 17:02:32 -0400 Subject: [PATCH 200/263] Update Globals.js - remove PotData module from globals as its loaded in NPCActions --- libs/SoloPlay/Functions/Globals.js | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/libs/SoloPlay/Functions/Globals.js b/libs/SoloPlay/Functions/Globals.js index 1b71b2f1..87980d4d 100644 --- a/libs/SoloPlay/Functions/Globals.js +++ b/libs/SoloPlay/Functions/Globals.js @@ -23,8 +23,6 @@ const Overrides = require("../../modules/Override"); /** @global */ const Coords_1 = require("../Modules/Coords"); /** @global */ -const PotData = require("../Modules/GameData/PotData"); -/** @global */ const GameData = require("../Modules/GameData/GameData"); const MYCLASSNAME = sdk.player.class.nameOf(me.classid).toLowerCase(); @@ -48,7 +46,7 @@ const SetUp = { init: function () { // ensure finalBuild is properly formatted - let checkBuildTemplate = function () { + const checkBuildTemplate = function () { let build = (["Bumper", "Socketmule", "Imbuemule"].includes(SetUp.finalBuild) ? ["Javazon", "Cold", "Bone", "Hammerdin", "Whirlwind", "Wind", "Trapsin"][me.classid] : SetUp.finalBuild) + "Build"; @@ -168,8 +166,8 @@ const SetUp = { } let mercInfo = Mercenary.getMercInfo(merc); - if (mercInfo.classid !== me.data.merc.classid) { - me.data.merc.classid = mercInfo.classid; + if (merc.classid !== me.data.merc.classid) { + me.data.merc.classid = merc.classid; } if (mercInfo.act !== me.data.merc.act) { me.data.merc.act = mercInfo.act; From 90eba28eb52139224a6b4b1901074a68603cb885 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Thu, 13 Jul 2023 17:04:02 -0400 Subject: [PATCH 201/263] Update SoloIndex.js - skip andy/meph in nightmare once we can run nith. Doing pindle/nith is more efficient use of time --- libs/SoloPlay/Tools/SoloIndex.js | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/libs/SoloPlay/Tools/SoloIndex.js b/libs/SoloPlay/Tools/SoloIndex.js index 7ae8f48b..c69ab454 100644 --- a/libs/SoloPlay/Tools/SoloIndex.js +++ b/libs/SoloPlay/Tools/SoloIndex.js @@ -29,7 +29,7 @@ const SoloIndex = { // Act 4 "izual", "hellforge", "river", "hephasto", "diablo", // Act 5 - "shenk", "savebarby", "anya", "pindle", "ancients", "baal", "a5chests", + "shenk", "savebarby", "anya", "pindle", "nith", "ancients", "baal", "a5chests", ], index: { @@ -226,6 +226,8 @@ const SoloIndex = { skipIf: function () { if (me.charlvl < 11) return true; if (!me.andariel) return false; + // nith gives better xp/min at this point + if (me.nightmare && me.sorceress && me.anya) return true; if (me.hell && me.amazon && SetUp.currentBuild !== SetUp.finalBuild) return true; return false; }, @@ -548,6 +550,9 @@ const SoloIndex = { return (me.accessToAct(3) && me.travincal); }, skipIf: function () { + if (!me.mephisto) return false; + // nith gives better xp/min at this point + if (me.nightmare && me.sorceress && me.anya) return true; return false; }, shouldRun: function () { From 113de970c727979f078b0725e68d8a3dcf9e94fe Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Thu, 13 Jul 2023 17:33:31 -0400 Subject: [PATCH 202/263] update `me.getOwned` method - add handler for checking mode, or using custom callback --- libs/SoloPlay/Functions/Me.js | 7 ++++++- libs/SoloPlay/globals.d.ts | 2 ++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/libs/SoloPlay/Functions/Me.js b/libs/SoloPlay/Functions/Me.js index 7fbbcaa7..b6d09ac4 100644 --- a/libs/SoloPlay/Functions/Me.js +++ b/libs/SoloPlay/Functions/Me.js @@ -847,6 +847,7 @@ me.getUnids = function () { me.fieldID = function () { let list = me.getUnids(); if (!list) return false; + const loc = me.inTown ? "Town" : "Field"; while (list.length > 0) { let idTool = me.getIdTool(); @@ -863,7 +864,7 @@ me.fieldID = function () { delay(50); result = Pickit.checkItem(item); } - Town.itemResult(item, result, "Field", false); + Town.itemResult(item, result, loc, false); } delay(200); @@ -1067,10 +1068,12 @@ me.haveRunes = function (itemInfo = []) { * @param {ItemUnit | { * itemType?: number, * classid?: number, + * mode?: number, * quality?: number, * sockets?: number, * location?: number, * ethereal?: boolean, + * cb?: (item: ItemUnit) => boolean * }} itemInfo * @param {boolean} skipSame * @returns {ItemUnit[]} @@ -1083,10 +1086,12 @@ me.getOwned = function (itemInfo = {}, skipSame = false) { do { if (itemInfo.itemType !== undefined && itemInfo.itemType !== item.itemType) continue; if (itemInfo.classid !== undefined && itemInfo.classid !== item.classid) continue; + if (itemInfo.mode !== undefined && itemInfo.mode !== item.mode) continue; if (itemInfo.quality !== undefined && itemInfo.quality !== item.quality) continue; if (itemInfo.sockets !== undefined && itemInfo.sockets !== item.sockets) continue; if (itemInfo.location !== undefined && itemInfo.location !== item.location) continue; if (itemInfo.ethereal !== undefined && itemInfo.ethereal !== item.ethereal) continue; + if (typeof itemInfo.cb === "function" && !itemInfo.cb(item)) continue; if (skipSame && itemInfo.gid !== undefined && itemInfo.gid !== item.gid) continue; itemList.push(copyUnit(item)); } while (item.getNext()); diff --git a/libs/SoloPlay/globals.d.ts b/libs/SoloPlay/globals.d.ts index b518c738..82f1cb46 100644 --- a/libs/SoloPlay/globals.d.ts +++ b/libs/SoloPlay/globals.d.ts @@ -176,10 +176,12 @@ declare global { getOwned(itemInfo: ItemUnit | { itemType?: number, classid?: number, + mode?: number, quality?: number, sockets?: number, location?: number, ethereal?: boolean, + cb?: (item: ItemUnit) => boolean, }): ItemUnit[]; } From d119cc65ebc46ac279e8d384b17ff6b7a67664c0 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Fri, 14 Jul 2023 12:09:14 -0400 Subject: [PATCH 203/263] Update GameData.js - little bit of cleanup here, removing most of the arrow functions in favor of function declarations due to d2bs limitations --- libs/SoloPlay/Modules/GameData/GameData.js | 79 ++++++++++++++++------ 1 file changed, 58 insertions(+), 21 deletions(-) diff --git a/libs/SoloPlay/Modules/GameData/GameData.js b/libs/SoloPlay/Modules/GameData/GameData.js index d3053c6a..56e37146 100644 --- a/libs/SoloPlay/Modules/GameData/GameData.js +++ b/libs/SoloPlay/Modules/GameData/GameData.js @@ -95,7 +95,10 @@ const GameData = { myReference: me, - townAreas: [0, 1, 40, 75, 103, 109], + /** + * @param {number} monsterID + * @param {number} areaID + */ monsterLevel: function (monsterID, areaID) { return (me.diff ? AreaData.has(areaID) && AreaData.get(areaID).Level @@ -114,6 +117,10 @@ mLvl )][me.diff] * ExperienceModifier / 100; }, + /** + * @param {number} monsterID + * @param {number} areaID + */ eliteExp: function (monsterID, areaID) { return this.monsterExp(monsterID, areaID, 2) * 3; }, @@ -170,10 +177,15 @@ let attack1 = this.monsterAttack1AvgDmg(monsterID, areaID, adjustLevel); let attack2 = this.monsterAttack2AvgDmg(monsterID, areaID, adjustLevel); let skill1 = this.monsterSkill1AvgDmg(monsterID, areaID, adjustLevel); - let dmgs = [attack1, attack2, skill1].filter(x => x > 0); + let dmgs = [attack1, attack2, skill1] + .filter(function (x) { + return x > 0; + }); // ignore 0 dmg to avoid reducing average if (!dmgs.length) return 0; - return dmgs.reduce((acc, v) => acc + v) / dmgs.length; + return dmgs.reduce(function (acc, v) { + return acc + v; + }, 0) / dmgs.length; }, averagePackSize: function (monsterID) { let { GroupCount, MinionCount } = MonsterData.get(monsterID); @@ -201,7 +213,10 @@ } }); - return Object.keys(resists).filter(key => resists[key] >= 100); + return Object.keys(resists) + .filter(function (key) { + return resists[key] >= 100; + }); }, levelModifier: function (clvl, mlvl) { let bonus; @@ -229,7 +244,8 @@ return (count + 1) / 2; }, partyModifier: function (playerID) { - let party = getParty(GameData.myReference), level = 0, total = 0; + let party = getParty(GameData.myReference); + let level = 0, total = 0; if (!party) return 1; let partyid = party.partyid; @@ -247,7 +263,8 @@ return level / total; }, killExp: function (playerID, monsterID, areaID) { - let exp = this.monsterExp(monsterID, areaID), party = getParty(GameData.myReference); + let exp = this.monsterExp(monsterID, areaID); + let party = getParty(GameData.myReference); if (!party) return 0; let level = 0, total = 0; @@ -272,10 +289,14 @@ ); }, baseLevel: function (...skillIDs) { - return skillIDs.reduce((total, skillID) => total + GameData.myReference.getSkill(skillID, 0), 0); + return skillIDs.reduce(function (total, skillID) { + return total + GameData.myReference.getSkill(skillID, 0); + }, 0); }, skillLevel: function (...skillIDs) { - return skillIDs.reduce((total, skillID) => total + GameData.myReference.getSkill(skillID, 1), 0); + return skillIDs.reduce(function (total, skillID) { + return total + GameData.myReference.getSkill(skillID, 1); + }, 0); }, skillCooldown: function (skillID) { return getBaseStat("Skills", skillID, "delay") !== -1; @@ -409,7 +430,8 @@ 272: 25 / 3 }, baseSkillDamage: function (skillID) { // TODO: rework skill damage to use both damage fields - let l = this.skillLevel(skillID), m = this.skillMult[skillID] || 1; + let l = this.skillLevel(skillID); + let m = this.skillMult[skillID] || 1; let dmgFields = [ [ "MinDam", "MinLevDam1", @@ -917,7 +939,8 @@ break; } // No cap in classic - let staticCap = (me.gametype === sdk.game.gametype.Classic ? 0 : [0, 33, 50][me.diff]); + let staticCap = (me.gametype === sdk.game.gametype.Classic + ? 0 : [0, 33, 50][me.diff]); const [monsterId, areaId] = [unit.classid, unit.area]; let percentLeft = (unit.hp * 100 / unit.hpmax); if (staticCap > percentLeft) { @@ -1055,7 +1078,7 @@ rawDmg = GameData.skillDamage(skill, target); return getTotalDmg(rawDmg, target); } else { - console.log("Units to check: " + units.length); + // console.log("Units to check: " + units.length); for (let i = 0; i < units.length; i++) { if (units[i] !== undefined) { rawDmg = GameData.skillDamage(skill, units[i]); @@ -1343,9 +1366,13 @@ getConviction: function () { let merc = GameData.myReference.getMerc(); let sl = this.skillLevel(123); // conviction + /** @param {ItemUnit} item */ + function isInfinity (item) { + return item.prefixnum === sdk.locale.items.Infinity; + } if (( // Either me, or merc is wearing a conviction - merc && merc.getItemsEx().filter(item => item.getPrefix(sdk.locale.items.Infinity)).first() - || GameData.myReference.getItemsEx(-1, 1).filter(item => item.getPrefix(sdk.locale.items.Infinity)).first())) { + merc && merc.getItemsEx().filter(isInfinity).first() + || GameData.myReference.getItemsEx(-1, 1).filter(isInfinity).first())) { sl = 12; } return sl > 0 ? Math.min(150, 30 + (sl - 1) * 5) : 0; @@ -1420,7 +1447,9 @@ } } - buffDmg = buffDmg.reduce((t, v) => t + v, 0); + buffDmg = buffDmg.reduce(function (t, v) { + return t + v; + }, 0); for (let sk in skillDamageInfo) { if (preattack && this.preAttackable.indexOf(parseInt(sk)) === -1) continue; // cant preattack this skill @@ -1560,7 +1589,9 @@ } } - buffDmg = buffDmg.reduce((t, v) => t + v, 0); + buffDmg = buffDmg.reduce(function (t, v) { + return t + v; + }, 0); for (let sk in skillDamageInfo) { if (!this.ignoreSkill[sk]) { @@ -1627,10 +1658,10 @@ skills = skills || this.allSkillDamage(); AreaData.get(areaID).forEachMonsterAndMinion(function (mon, rarity, parent) { - effortpool += rarity * this.monsterEffort(mon.Index, areaID, skills, parent && parent.Index).effort; + effortpool += rarity * GameData.monsterEffort(mon.Index, areaID, skills, parent && parent.Index).effort; raritypool += rarity; - dmgAcc += rarity * this.monsterAvgDmg(mon.Index, areaID); + dmgAcc += rarity * GameData.monsterAvgDmg(mon.Index, areaID); }); // console.debug('avg dmg '+ AreaData.get(areaID).LocaleString+' -- ' + dmgAcc+' -- ' + avgDmg); @@ -1643,11 +1674,14 @@ let effortpool = 0, raritypool = 0, dmgAcc = 0; skills = skills || this.allSkillDamage(); - AreaData.get(areaID).forEachMonsterAndMinion((mon, rarity, parent) => { - effortpool += rarity * this.monsterExp(mon.Index, areaID) * this.levelModifier(GameData.myReference.charlvl, this.monsterLevel(mon.Index, areaID)) / this.monsterEffort(mon.Index, areaID, skills, parent && parent.Index).effort; + AreaData.get(areaID).forEachMonsterAndMinion(function (mon, rarity, parent) { + let monExp = GameData.monsterExp(mon.Index, areaID); + let lvlMod = GameData.levelModifier(GameData.myReference.charlvl, GameData.monsterLevel(mon.Index, areaID)); + let monEffort = GameData.monsterEffort(mon.Index, areaID, skills, parent && parent.Index).effort; + effortpool += rarity * monExp * lvlMod / monEffort; raritypool += rarity; - dmgAcc += (rarity * this.monsterAvgDmg(mon.Index, areaID)); + dmgAcc += (rarity * GameData.monsterAvgDmg(mon.Index, areaID)); }); let log = 1, avgDmg = 0; @@ -1659,7 +1693,10 @@ return (raritypool ? effortpool / raritypool : 0) - (avgDmg); }, mostUsedSkills: function (force = false) { - if (!force && GameData.myReference.hasOwnProperty("__cachedMostUsedSkills") && GameData.myReference.__cachedMostUsedSkills) return GameData.myReference.__cachedMostUsedSkills; + if (!force && GameData.myReference.hasOwnProperty("__cachedMostUsedSkills") + && GameData.myReference.__cachedMostUsedSkills) { + return GameData.myReference.__cachedMostUsedSkills; + } const effort = [], uniqueSkills = []; for (let i = 50; i < 120; i++) { From 8bfadeb39c9b78cfc82783317715bc127099a787 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Fri, 14 Jul 2023 12:14:55 -0400 Subject: [PATCH 204/263] Remove `Check.haveBase` from runeword files - phasing this out in favor of using `me.getOwned` with more controllable arguments --- .../BuildFiles/Runewords/AncientsPledge.js | 2 +- libs/SoloPlay/BuildFiles/Runewords/Bone.js | 4 +-- .../BuildFiles/Runewords/BreathOfTheDying.js | 17 +++++++-- .../BuildFiles/Runewords/CallToArms.js | 20 +++++++++-- .../BuildFiles/Runewords/ChainsOfHonor.js | 17 +++++++-- libs/SoloPlay/BuildFiles/Runewords/Chaos.js | 2 +- .../BuildFiles/Runewords/CrescentMoon.js | 2 +- .../BuildFiles/Runewords/DragonArmor.js | 2 +- .../BuildFiles/Runewords/DreamHelm.js | 2 +- .../BuildFiles/Runewords/DreamShield.js | 15 ++++++-- libs/SoloPlay/BuildFiles/Runewords/Duress.js | 2 +- libs/SoloPlay/BuildFiles/Runewords/Enigma.js | 2 +- libs/SoloPlay/BuildFiles/Runewords/Exile.js | 14 ++++++-- libs/SoloPlay/BuildFiles/Runewords/Faith.js | 27 ++++++++++---- .../BuildFiles/Runewords/Fortitude.js | 17 +++++++-- libs/SoloPlay/BuildFiles/Runewords/Fury.js | 2 +- libs/SoloPlay/BuildFiles/Runewords/Grief.js | 22 +++++++++--- .../BuildFiles/Runewords/HandOfJustice.js | 2 +- .../BuildFiles/Runewords/HeartOfTheOak.js | 16 +++++++-- libs/SoloPlay/BuildFiles/Runewords/Honor.js | 16 +++++++-- libs/SoloPlay/BuildFiles/Runewords/Ice.js | 35 ++++++++++++++----- .../BuildFiles/Runewords/KingsGrace.js | 2 +- .../SoloPlay/BuildFiles/Runewords/LastWish.js | 2 +- .../BuildFiles/Runewords/Lawbringer.js | 2 +- libs/SoloPlay/BuildFiles/Runewords/Lore.js | 2 +- libs/SoloPlay/BuildFiles/Runewords/Malice.js | 2 +- .../SoloPlay/BuildFiles/Runewords/MercDoom.js | 28 +++++++++++---- .../BuildFiles/Runewords/MercFortitude.js | 2 +- .../BuildFiles/Runewords/MercInfinity.js | 2 +- .../BuildFiles/Runewords/MercInsight.js | 24 ++++++++++++- .../BuildFiles/Runewords/MercPride.js | 2 +- .../BuildFiles/Runewords/MercTreachery.js | 2 +- libs/SoloPlay/BuildFiles/Runewords/Myth.js | 2 +- .../BuildFiles/Runewords/PDiamondShield.js | 4 +-- .../BuildFiles/Runewords/PhoenixShield.js | 28 +++++++++++---- libs/SoloPlay/BuildFiles/Runewords/Rhyme.js | 2 +- .../BuildFiles/Runewords/Sanctuary.js | 10 +++--- libs/SoloPlay/BuildFiles/Runewords/Silence.js | 15 ++++++-- libs/SoloPlay/BuildFiles/Runewords/Smoke.js | 4 +-- .../BuildFiles/Runewords/SpiritShield.js | 15 ++++++-- .../BuildFiles/Runewords/SpiritSword.js | 5 +-- libs/SoloPlay/BuildFiles/Runewords/Stealth.js | 4 +-- libs/SoloPlay/BuildFiles/Runewords/Steel.js | 16 +++++---- .../BuildFiles/Runewords/Treachery.js | 2 +- .../BuildFiles/Runewords/VoiceOfReason.js | 2 +- libs/SoloPlay/BuildFiles/Runewords/White.js | 14 +++++--- 46 files changed, 330 insertions(+), 101 deletions(-) diff --git a/libs/SoloPlay/BuildFiles/Runewords/AncientsPledge.js b/libs/SoloPlay/BuildFiles/Runewords/AncientsPledge.js index c7e80dbf..c8b69cc9 100644 --- a/libs/SoloPlay/BuildFiles/Runewords/AncientsPledge.js +++ b/libs/SoloPlay/BuildFiles/Runewords/AncientsPledge.js @@ -1,4 +1,4 @@ -(function() { +(function () { if (!me.checkItem({ name: sdk.locale.items.AncientsPledge }).have && !me.hell) { // Cube to Ort rune if (me.normal && !me.getItem(sdk.items.runes.Ort)) { diff --git a/libs/SoloPlay/BuildFiles/Runewords/Bone.js b/libs/SoloPlay/BuildFiles/Runewords/Bone.js index 4a1ea129..2bfcec55 100644 --- a/libs/SoloPlay/BuildFiles/Runewords/Bone.js +++ b/libs/SoloPlay/BuildFiles/Runewords/Bone.js @@ -1,4 +1,4 @@ -(function() { +(function () { const Bone = [ "[name] == UmRune # # [maxquantity] == 2", "[name] == SolRune # # [maxquantity] == 1", @@ -6,7 +6,7 @@ NTIP.buildList(Bone); // Cube to Um Rune - if (Item.getQuantityOwned(me.getItem(sdk.items.runes.Um)) < 2) { + if (me.getOwned({ classid: sdk.items.runes.Um }).length < 2) { Config.Recipes.push([Recipe.Rune, "Ko Rune"]); Config.Recipes.push([Recipe.Rune, "Fal Rune"]); Config.Recipes.push([Recipe.Rune, "Lem Rune"]); diff --git a/libs/SoloPlay/BuildFiles/Runewords/BreathOfTheDying.js b/libs/SoloPlay/BuildFiles/Runewords/BreathOfTheDying.js index beb42ad3..746a0972 100644 --- a/libs/SoloPlay/BuildFiles/Runewords/BreathOfTheDying.js +++ b/libs/SoloPlay/BuildFiles/Runewords/BreathOfTheDying.js @@ -1,4 +1,4 @@ -(function() { +(function () { const BoTD = [ "[name] == VexRune", "me.diff == 2 && [name] == HelRune # # [maxquantity] == 1", @@ -10,8 +10,21 @@ ]; NTIP.buildList(BoTD); + /** @type {GetOwnedSettings} */ + const wanted = { + classid: sdk.items.ColossusBlade, + mode: sdk.items.mode.inStorage, + sockets: 6, + ethereal: true, + /** @param {ItemUnit} item */ + cb: function (item) { + return item.isBaseType; + } + }; + // Have Zod rune but do not have a base yet - if (!Check.haveBase("colossusblade", 6) && me.getItem(sdk.items.runes.Zod)) { + if (!me.getOwned(wanted).length + && me.getItem(sdk.items.runes.Zod)) { NTIP.addLine("[name] == colossusblade && [flag] == ethereal && [quality] == normal # [sockets] == 0 # [maxquantity] == 1"); } diff --git a/libs/SoloPlay/BuildFiles/Runewords/CallToArms.js b/libs/SoloPlay/BuildFiles/Runewords/CallToArms.js index b5e6a5e4..0df5f165 100644 --- a/libs/SoloPlay/BuildFiles/Runewords/CallToArms.js +++ b/libs/SoloPlay/BuildFiles/Runewords/CallToArms.js @@ -1,4 +1,4 @@ -(function() { +(function () { const CTA = [ "[name] == AmnRune # # [maxquantity] == 1", "[name] == RalRune # # [maxquantity] == 1", @@ -8,12 +8,25 @@ ]; NTIP.buildList(CTA); + /** @type {GetOwnedSettings} */ + const wanted = { + classid: sdk.items.CrystalSword, + mode: sdk.items.mode.inStorage, + sockets: 6, + /** @param {ItemUnit} item */ + cb: function (item) { + return item.isBaseType; + } + }; + // Have Ohm before collecting base if (me.getItem(sdk.items.runes.Ohm)) { NTIP.addLine("[name] == crystalsword && [quality] >= normal && [quality] <= superior # [sockets] == 5 # [maxquantity] == 1"); // Have Ohm+Mal+Ist rune but do not have a base yet - if (me.getItem(sdk.items.runes.Ist) && me.getItem(sdk.items.runes.Mal) && !Check.haveBase("crystalsword", 5)) { + if (me.getItem(sdk.items.runes.Ist) + && me.getItem(sdk.items.runes.Mal) + && !me.getOwned(wanted).length) { NTIP.addLine("[name] == crystalsword && [quality] == normal # [sockets] == 0 # [maxquantity] == 1"); Config.Recipes.push([Recipe.Socket.Weapon, "crystalsword"]); } @@ -33,7 +46,8 @@ Config.Recipes.push([Recipe.Rune, "Ist Rune"]); Config.Recipes.push([Recipe.Rune, "Gul Rune"]); - if (me.checkItem({ name: sdk.locale.items.HeartoftheOak }).have || ["Zealer", "Smiter", "Auradin", "Meteorb", "Blizzballer", "Cold"].includes(SetUp.finalBuild)) { + if (me.checkItem({ name: sdk.locale.items.HeartoftheOak }).have + || ["Zealer", "Smiter", "Auradin", "Meteorb", "Blizzballer", "Cold"].includes(SetUp.finalBuild)) { Config.Recipes.push([Recipe.Rune, "Vex Rune"]); } } diff --git a/libs/SoloPlay/BuildFiles/Runewords/ChainsOfHonor.js b/libs/SoloPlay/BuildFiles/Runewords/ChainsOfHonor.js index bd7d8140..2f3f0626 100644 --- a/libs/SoloPlay/BuildFiles/Runewords/ChainsOfHonor.js +++ b/libs/SoloPlay/BuildFiles/Runewords/ChainsOfHonor.js @@ -1,4 +1,4 @@ -(function() { +(function () { const CoH = [ "[name] == DolRune # # [maxquantity] == 1", "[name] == UmRune", @@ -7,6 +7,19 @@ ]; NTIP.buildList(CoH); + /** @type {GetOwnedSettings} */ + const wanted = { + itemType: sdk.items.type.Armor, + mode: sdk.items.mode.inStorage, + sockets: 4, + ethereal: false, + /** @param {ItemUnit} item */ + cb: function (item) { + return item.isBaseType + && [sdk.items.ArchonPlate, sdk.items.DuskShroud, sdk.items.Wyrmhide].includes(item.classid); + } + }; + // Cube to Ber rune if (!me.getItem(sdk.items.runes.Ber)) { if (me.checkItem({ name: sdk.locale.items.CalltoArms }).have || ["Plaguewolf", "Wolf", "Uberconc"].includes(SetUp.finalBuild)) { @@ -32,7 +45,7 @@ // Have Ber rune before looking for normal base if (me.getItem(sdk.items.runes.Ber)) { - if (!Check.haveBase("armor", 4)) { + if (!me.getOwned(wanted).length) { NTIP.addLine("([name] == archonplate || [name] == duskshroud || [name] == wyrmhide) && [flag] != ethereal && [quality] >= normal && [quality] <= superior # [sockets] == 0 # [maxquantity] == 1"); } diff --git a/libs/SoloPlay/BuildFiles/Runewords/Chaos.js b/libs/SoloPlay/BuildFiles/Runewords/Chaos.js index e55695f5..975e7d96 100644 --- a/libs/SoloPlay/BuildFiles/Runewords/Chaos.js +++ b/libs/SoloPlay/BuildFiles/Runewords/Chaos.js @@ -1,4 +1,4 @@ -(function() { +(function () { const Chaos = [ "[name] == FalRune # # [maxquantity] == 1", "[name] == OhmRune", diff --git a/libs/SoloPlay/BuildFiles/Runewords/CrescentMoon.js b/libs/SoloPlay/BuildFiles/Runewords/CrescentMoon.js index fe39f039..a0d80e0e 100644 --- a/libs/SoloPlay/BuildFiles/Runewords/CrescentMoon.js +++ b/libs/SoloPlay/BuildFiles/Runewords/CrescentMoon.js @@ -1,4 +1,4 @@ -(function() { +(function () { const Crescent = [ "[name] == ShaelRune # # [maxquantity] == 2", "[name] == UmRune", diff --git a/libs/SoloPlay/BuildFiles/Runewords/DragonArmor.js b/libs/SoloPlay/BuildFiles/Runewords/DragonArmor.js index 03c01a30..63e9664b 100644 --- a/libs/SoloPlay/BuildFiles/Runewords/DragonArmor.js +++ b/libs/SoloPlay/BuildFiles/Runewords/DragonArmor.js @@ -1,4 +1,4 @@ -(function() { +(function () { const DragonArmor = [ "[name] == SurRune", "[name] == LoRune", diff --git a/libs/SoloPlay/BuildFiles/Runewords/DreamHelm.js b/libs/SoloPlay/BuildFiles/Runewords/DreamHelm.js index fc3bb762..c7ac17e1 100644 --- a/libs/SoloPlay/BuildFiles/Runewords/DreamHelm.js +++ b/libs/SoloPlay/BuildFiles/Runewords/DreamHelm.js @@ -1,4 +1,4 @@ -(function() { +(function () { const DreamHelm = [ "[name] == IoRune # # [maxquantity] == 1", "[name] == JahRune", diff --git a/libs/SoloPlay/BuildFiles/Runewords/DreamShield.js b/libs/SoloPlay/BuildFiles/Runewords/DreamShield.js index 4347c445..93936860 100644 --- a/libs/SoloPlay/BuildFiles/Runewords/DreamShield.js +++ b/libs/SoloPlay/BuildFiles/Runewords/DreamShield.js @@ -1,4 +1,4 @@ -(function() { +(function () { const DreamShield = [ "[name] == IoRune # # [maxquantity] == 1", "[name] == JahRune", @@ -7,7 +7,18 @@ ]; NTIP.buildList(DreamShield); - if (!Check.haveBase("sacredtarge", 3)) { + /** @type {GetOwnedSettings} */ + const wanted = { + classid: sdk.items.SacredTarge, + mode: sdk.items.mode.inStorage, + sockets: 3, + /** @param {ItemUnit} item */ + cb: function (item) { + return item.isBaseType; + } + }; + + if (!me.getOwned(wanted).length) { NTIP.addLine("[name] == sacredtarge && [flag] != ethereal && [quality] == normal # [fireresist] >= 45 && [sockets] == 0 # [maxquantity] == 1"); } diff --git a/libs/SoloPlay/BuildFiles/Runewords/Duress.js b/libs/SoloPlay/BuildFiles/Runewords/Duress.js index ceb95b8a..adf37b5d 100644 --- a/libs/SoloPlay/BuildFiles/Runewords/Duress.js +++ b/libs/SoloPlay/BuildFiles/Runewords/Duress.js @@ -1,4 +1,4 @@ -(function() { +(function () { const Duress = [ "[name] == ShaelRune # # [maxquantity] == 1", "[name] == UmRune # # [maxquantity] == 1", diff --git a/libs/SoloPlay/BuildFiles/Runewords/Enigma.js b/libs/SoloPlay/BuildFiles/Runewords/Enigma.js index a54c2ed2..2f42991d 100644 --- a/libs/SoloPlay/BuildFiles/Runewords/Enigma.js +++ b/libs/SoloPlay/BuildFiles/Runewords/Enigma.js @@ -1,4 +1,4 @@ -(function() { +(function () { const Enigma = [ "[name] == JahRune", "[name] == IthRune # # [maxquantity] == 1", diff --git a/libs/SoloPlay/BuildFiles/Runewords/Exile.js b/libs/SoloPlay/BuildFiles/Runewords/Exile.js index 3764c2a3..b3a62594 100644 --- a/libs/SoloPlay/BuildFiles/Runewords/Exile.js +++ b/libs/SoloPlay/BuildFiles/Runewords/Exile.js @@ -1,4 +1,4 @@ -(function() { +(function () { const Exile = [ "[name] == VexRune", "[name] == OhmRune", @@ -8,7 +8,17 @@ ]; NTIP.buildList(Exile); - if (!Check.haveBase("sacredtarge", 4)) { + const wanted = { + classid: sdk.items.SacredTarge, + mode: sdk.items.mode.inStorage, + sockets: 4, + /** @param {ItemUnit} item */ + cb: function (item) { + return item.isBaseType; + } + }; + + if (!me.getOwned(wanted).length) { NTIP.addLine("[name] == sacredtarge && [quality] == normal && [flag] == ethereal # [fireresist] >= 30 && [sockets] == 0 # [maxquantity] == 1"); } diff --git a/libs/SoloPlay/BuildFiles/Runewords/Faith.js b/libs/SoloPlay/BuildFiles/Runewords/Faith.js index f36b8241..21385b7f 100644 --- a/libs/SoloPlay/BuildFiles/Runewords/Faith.js +++ b/libs/SoloPlay/BuildFiles/Runewords/Faith.js @@ -1,4 +1,4 @@ -(function() { +(function () { const FaithRunes = [ "[name] == OhmRune", "[name] == JahRune", @@ -7,7 +7,8 @@ ]; NTIP.buildList(FaithRunes); // Cube to Ohm and Keep cubing to Jah rune - if (Item.getQuantityOwned(me.getItem(sdk.items.runes.Ohm) > 1) && me.checkItem({ name: sdk.locale.items.CalltoArms }).have) { + if (me.getOwned({ classid: sdk.items.runes.Ohm }).length > 1 + && me.checkItem({ name: sdk.locale.items.CalltoArms }).have) { if (!me.getItem(sdk.items.runes.Jah)) { Config.Recipes.push([Recipe.Rune, "Mal Rune"]); Config.Recipes.push([Recipe.Rune, "Ist Rune"]); @@ -17,16 +18,28 @@ !me.getItem(sdk.items.runes.Jah) && Config.Recipes.push([Recipe.Rune, "Ohm Rune"]); } // Cube to Jah rune - if (!me.getItem(sdk.items.runes.Jah) && me.checkItem({ name: sdk.locale.items.ChainofHonor }).have) { + if (!me.getItem(sdk.items.runes.Jah) + && me.checkItem({ name: sdk.locale.items.ChainsofHonor }).have) { Config.Recipes.push([Recipe.Rune, "Ohm Rune"]); Config.Recipes.push([Recipe.Rune, "Lo Rune"]); Config.Recipes.push([Recipe.Rune, "Sur Rune"]); Config.Recipes.push([Recipe.Rune, "Ber Rune"]); } + /** @type {GetOwnedSettings} */ + const wanted = { + itemType: sdk.items.type.AmazonBow, + mode: sdk.items.mode.inStorage, + sockets: 4, + /** @param {ItemUnit} item */ + cb: function (item) { + return item.isBaseType; + } + }; + if (me.amazon) { if (me.getItem(sdk.items.runes.Lo) && me.getItem(sdk.items.runes.Jah)) { - if (!Check.haveBase("amazonbow", 4)) { + if (!me.getOwned(wanted).length) { NTIP.addLine("[name] == grandmatronbow && [quality] == normal # [bowandcrossbowskilltab] == 3 && [sockets] == 0 # [maxquantity] == 1"); } @@ -36,12 +49,12 @@ } Config.Runewords.push([Runeword.Faith, "grandmatronbow"]); - Config.Recipes.push([Recipe.Socket.Bow, "grandmatronbow"]); - } else { if (me.getItem(sdk.items.runes.Lo) && me.getItem(sdk.items.runes.Jah)) { - if (!Check.haveBase("amazonbow", 4)) { + wanted.itemType = sdk.items.type.Bow; + + if (!me.getOwned(wanted).length) { NTIP.addLine("([name] == wardbow || [name] == bladebow || [name] == diamonbow) && [quality] == superior # [enhanceddamage] >= 5 && [sockets] == 4 # [maxquantity] == 1"); } else { NTIP.addLine("([name] == wardbow || [name] == bladebow || [name] == diamonbow) && [quality] == superior # [enhanceddamage] == 15 && [sockets] == 4 # [maxquantity] == 1"); diff --git a/libs/SoloPlay/BuildFiles/Runewords/Fortitude.js b/libs/SoloPlay/BuildFiles/Runewords/Fortitude.js index 1cda7e87..cc09ccdb 100644 --- a/libs/SoloPlay/BuildFiles/Runewords/Fortitude.js +++ b/libs/SoloPlay/BuildFiles/Runewords/Fortitude.js @@ -1,4 +1,4 @@ -(function() { +(function () { const Fortitude = [ "[name] == ElRune # # [maxquantity] == 1", "[name] == SolRune # # [maxquantity] == 1", @@ -7,9 +7,22 @@ ]; NTIP.buildList(Fortitude); + /** @type {GetOwnedSettings} */ + const wanted = { + itemType: sdk.items.type.Armor, + mode: sdk.items.mode.inStorage, + ethereal: false, + sockets: 4, + /** @param {ItemUnit} item */ + cb: function (item) { + return item.isBaseType + && [sdk.items.ArchonPlate, sdk.items.DuskShroud, sdk.items.Wyrmhide].includes(item.classid); + } + }; + // Have Lo rune before looking for normal base if (me.getItem(sdk.items.runes.Lo)) { - if (!Check.haveBase("armor", 4)) { + if (!me.getOwned(wanted).length) { NTIP.addLine("([name] == archonplate || [name] == duskshroud || [name] == wyrmhide) && [flag] != ethereal && [quality] >= normal && [quality] <= superior # [sockets] == 0 # [maxquantity] == 1"); } diff --git a/libs/SoloPlay/BuildFiles/Runewords/Fury.js b/libs/SoloPlay/BuildFiles/Runewords/Fury.js index 700deed6..846c1a62 100644 --- a/libs/SoloPlay/BuildFiles/Runewords/Fury.js +++ b/libs/SoloPlay/BuildFiles/Runewords/Fury.js @@ -1,4 +1,4 @@ -(function() { +(function () { const Fury = [ "[name] == JahRune", "[name] == GulRune", diff --git a/libs/SoloPlay/BuildFiles/Runewords/Grief.js b/libs/SoloPlay/BuildFiles/Runewords/Grief.js index 20b7e36a..87c0fc62 100644 --- a/libs/SoloPlay/BuildFiles/Runewords/Grief.js +++ b/libs/SoloPlay/BuildFiles/Runewords/Grief.js @@ -1,4 +1,4 @@ -(function() { +(function () { const Grief = [ "[name] == EthRune # # [maxquantity] == 1", "[name] == TirRune # # [maxquantity] == 1", @@ -8,10 +8,21 @@ ]; NTIP.buildList(Grief); + /** @type {GetOwnedSettings} */ + const wanted = { + classid: sdk.items.PhaseBlade, + mode: sdk.items.mode.inStorage, + sockets: 5, + /** @param {ItemUnit} item */ + cb: function (item) { + return item.isBaseType; + } + }; + if (me.getItem(sdk.items.runes.Lo)) { NTIP.addLine("[name] == phaseblade && [quality] >= normal && [quality] <= superior # [sockets] == 5 # [maxquantity] == 1"); - if (!Check.haveBase("phaseblade", 5)) { + if (!me.getOwned(wanted).length) { NTIP.addLine("[name] == phaseblade && [quality] == normal # [sockets] == 0 # [maxquantity] == 1"); Config.Recipes.push([Recipe.Socket.Weapon, "phaseblade"]); } @@ -25,7 +36,8 @@ Config.Recipes.push([Recipe.Rune, "Gul Rune"]); Config.Recipes.push([Recipe.Rune, "Vex Rune"]); - if (me.checkItem({ name: sdk.locale.items.CalltoArms }).have || ["Smiter", "Zealer"].indexOf(SetUp.finalBuild) === -1) { + if (me.checkItem({ name: sdk.locale.items.CalltoArms }).have + || ["Smiter", "Zealer"].indexOf(SetUp.finalBuild) === -1) { Config.Recipes.push([Recipe.Rune, "Ohm Rune"]); } } @@ -37,8 +49,8 @@ } if (SetUp.finalBuild === "Plaguewolf") { - // Only start making Grief after Chains of Honor is made - if (Check.haveItem("armor", "runeword", "Chains of Honor")) { + // Only start making Grief after Chains of Honor is made + if (me.checkItem({ name: sdk.locale.items.ChainsofHonor }).have) { Config.Runewords.push([Runeword.Grief, "phaseblade"]); Config.KeepRunewords.push("[type] == sword # [ias] >= 30 && [itemdeadlystrike] == 20 && [passivepoispierce] >= 20"); } diff --git a/libs/SoloPlay/BuildFiles/Runewords/HandOfJustice.js b/libs/SoloPlay/BuildFiles/Runewords/HandOfJustice.js index d3a8ee98..90d83eca 100644 --- a/libs/SoloPlay/BuildFiles/Runewords/HandOfJustice.js +++ b/libs/SoloPlay/BuildFiles/Runewords/HandOfJustice.js @@ -1,4 +1,4 @@ -(function() { +(function () { const HoJ = [ "[name] == SurRune", "[name] == ChamRune", diff --git a/libs/SoloPlay/BuildFiles/Runewords/HeartOfTheOak.js b/libs/SoloPlay/BuildFiles/Runewords/HeartOfTheOak.js index d4250292..e643314f 100644 --- a/libs/SoloPlay/BuildFiles/Runewords/HeartOfTheOak.js +++ b/libs/SoloPlay/BuildFiles/Runewords/HeartOfTheOak.js @@ -1,4 +1,4 @@ -(function() { +(function () { const HotO = [ "[name] == ThulRune # # [maxquantity] == 1", "[name] == PulRune", @@ -7,12 +7,24 @@ ]; NTIP.buildList(HotO); + /** @type {GetOwnedSettings} */ + const wanted = { + itemType: sdk.items.type.Mace, + mode: sdk.items.mode.inStorage, + sockets: 4, + ethereal: false, + /** @param {ItemUnit} item */ + cb: function (item) { + return item.isBaseType; + } + }; + // Have Vex rune before looking for base if (me.getItem(sdk.items.runes.Vex)) { NTIP.addLine("([name] == flail || [name] == knout) && [flag] != ethereal && [quality] >= normal && [quality] <= superior # [sockets] == 4 # [maxquantity] == 1"); // Have Vex rune but do not have a base yet - if (!Check.haveBase("mace", 4)) { + if (!me.getOwned(wanted).length) { NTIP.addLine("([name] == flail || [name] == knout) && [flag] != ethereal && [quality] == normal # [sockets] == 0 # [maxquantity] == 1"); Config.Recipes.push([Recipe.Socket.Weapon, "flail"]); Config.Recipes.push([Recipe.Socket.Weapon, "knout"]); diff --git a/libs/SoloPlay/BuildFiles/Runewords/Honor.js b/libs/SoloPlay/BuildFiles/Runewords/Honor.js index df3f2847..552ab402 100644 --- a/libs/SoloPlay/BuildFiles/Runewords/Honor.js +++ b/libs/SoloPlay/BuildFiles/Runewords/Honor.js @@ -1,4 +1,4 @@ -(function() { +(function () { const Honor = [ "[name] == AmnRune # # [maxquantity] == 1", "[name] == ElRune # # [maxquantity] == 1", @@ -8,6 +8,18 @@ ]; NTIP.buildList(Honor); + /** @type {GetOwnedSettings} */ + const wanted = { + itemType: sdk.items.type.Sword, + mode: sdk.items.mode.inStorage, + sockets: 4, + ethereal: false, + /** @param {ItemUnit} item */ + cb: function (item) { + return item.isBaseType; + } + }; + // Cube to Amn rune if (!me.getItem(sdk.items.runes.Amn)) { Config.Recipes.push([Recipe.Rune, "Thul Rune"]); @@ -15,7 +27,7 @@ // Have Sol rune before looking for base if (me.getItem(sdk.items.runes.Sol)) { - if (!Check.haveBase("sword", 5)) { + if (!me.getOwned(wanted).length) { if (me.accessToAct(5) && !me.getQuest(sdk.quest.id.SiegeOnHarrogath, sdk.quest.states.Completed)) { NTIP.addLine("((me.diff == 0 && [name] == flamberge) || (me.diff > 0 && [name] == zweihander) || (me.diff == 2 && [name] == colossussword)) && [flag] != ethereal && [quality] >= normal && [quality] <= superior && [level] >= 41 # [sockets] == 0 # [maxquantity] == 1"); } else { diff --git a/libs/SoloPlay/BuildFiles/Runewords/Ice.js b/libs/SoloPlay/BuildFiles/Runewords/Ice.js index 6342acd8..22eba936 100644 --- a/libs/SoloPlay/BuildFiles/Runewords/Ice.js +++ b/libs/SoloPlay/BuildFiles/Runewords/Ice.js @@ -1,4 +1,4 @@ -(function() { +(function () { const IceRunes = [ "[name] == AmnRune # # [maxquantity] == 1", "[name] == ShaelRune # # [maxquantity] == 1", @@ -7,8 +7,21 @@ ]; NTIP.buildList(IceRunes); + /** @type {GetOwnedSettings} */ + const wanted = { + itemType: sdk.items.type.AmazonBow, + mode: sdk.items.mode.inStorage, + sockets: 4, + ethereal: false, + /** @param {ItemUnit} item */ + cb: function (item) { + return item.isBaseType; + } + }; + // Cube to Lo and Keep cubing to Jah rune - if (Item.getQuantityOwned(me.getItem(sdk.items.runes.Lo)) > 1 && me.checkItem({ name: sdk.locale.items.ChainofHonor }).have) { + if (me.getOwned({ classid: sdk.items.runes.Lo }).length > 1 + && me.checkItem({ name: sdk.locale.items.ChainofHonor }).have) { if (!me.getItem(sdk.items.runes.Jah)) { Config.Recipes.push([Recipe.Rune, "Mal Rune"]); Config.Recipes.push([Recipe.Rune, "Ist Rune"]); @@ -34,23 +47,29 @@ Config.Recipes.push([Recipe.Socket.Bow, "matriarchalbow"]); Config.Recipes.push([Recipe.Socket.Bow, "grandmatronbow"]); - if (!Check.haveBase("amazonbow", 4) && me.getItem(sdk.items.runes.Lo) && me.getItem(sdk.items.runes.Jah)) { + if (!me.getOwned(wanted).length + && me.getItem(sdk.items.runes.Lo) + && me.getItem(sdk.items.runes.Jah)) { NTIP.addLine("([name] == matriarchalbow || [name] == grandmatronbow) && [quality] == normal # [sockets] == 0 # [maxquantity] == 1"); } Config.KeepRunewords.push("[type] == amazonbow # [enhanceddamage] >= 140 && [passivecoldpierce] >= 25"); } else { + wanted.itemType = sdk.items.type.Crossbow; + wanted.classid = sdk.items.DemonCrossbow; NTIP.addLine("[type] == demoncrossbow && [quality] == superior # [enhanceddamage] >= 10 && [sockets] == 4 # [maxquantity] == 1"); - Config.Runewords.push([Runeword.Ice, "demoncrossbow"]); - - Config.Recipes.push([Recipe.Socket.Crossbow, "demoncrossbow"]); - - if (!Check.haveBase("crossbow", 4) && me.getItem(sdk.items.runes.Lo) && me.getItem(sdk.items.runes.Jah)) { + + if (!me.getOwned(wanted).length + && me.getItem(sdk.items.runes.Lo) + && me.getItem(sdk.items.runes.Jah)) { NTIP.addLine("[name] == demoncrossbow && [quality] == normal # [sockets] == 0 # [maxquantity] == 1"); } + Config.Runewords.push([Runeword.Ice, "demoncrossbow"]); + Config.Recipes.push([Recipe.Socket.Crossbow, "demoncrossbow"]); + Config.KeepRunewords.push("[type] == demoncrossbow # [enhanceddamage] >= 140 && [passivecoldpierce] >= 25"); } })(); diff --git a/libs/SoloPlay/BuildFiles/Runewords/KingsGrace.js b/libs/SoloPlay/BuildFiles/Runewords/KingsGrace.js index 20f91a31..a7971e28 100644 --- a/libs/SoloPlay/BuildFiles/Runewords/KingsGrace.js +++ b/libs/SoloPlay/BuildFiles/Runewords/KingsGrace.js @@ -1,4 +1,4 @@ -(function() { +(function () { const KingsGrace = [ "[name] == AmnRune # # [maxquantity] == 1", "[name] == RalRune # # [maxquantity] == 1", diff --git a/libs/SoloPlay/BuildFiles/Runewords/LastWish.js b/libs/SoloPlay/BuildFiles/Runewords/LastWish.js index f464d19c..657c5541 100644 --- a/libs/SoloPlay/BuildFiles/Runewords/LastWish.js +++ b/libs/SoloPlay/BuildFiles/Runewords/LastWish.js @@ -1,4 +1,4 @@ -(function() { +(function () { // Jah/Mal/Jah/Sur/Jah/Ber const LW = [ "[name] == JahRune", diff --git a/libs/SoloPlay/BuildFiles/Runewords/Lawbringer.js b/libs/SoloPlay/BuildFiles/Runewords/Lawbringer.js index 421bfd7d..1c31be3b 100644 --- a/libs/SoloPlay/BuildFiles/Runewords/Lawbringer.js +++ b/libs/SoloPlay/BuildFiles/Runewords/Lawbringer.js @@ -1,4 +1,4 @@ -(function() { +(function () { const Lawbringer = [ "[name] == AmnRune # # [maxquantity] == 1", "[name] == LemRune", diff --git a/libs/SoloPlay/BuildFiles/Runewords/Lore.js b/libs/SoloPlay/BuildFiles/Runewords/Lore.js index 9ca241c4..07d0ba20 100644 --- a/libs/SoloPlay/BuildFiles/Runewords/Lore.js +++ b/libs/SoloPlay/BuildFiles/Runewords/Lore.js @@ -1,4 +1,4 @@ -(function() { +(function () { if (!Check.haveItem("helm", "runeword", "Lore")) { const loreRunes = [ "[name] == OrtRune # # [maxquantity] == 1", diff --git a/libs/SoloPlay/BuildFiles/Runewords/Malice.js b/libs/SoloPlay/BuildFiles/Runewords/Malice.js index 7b6783e2..04345cd2 100644 --- a/libs/SoloPlay/BuildFiles/Runewords/Malice.js +++ b/libs/SoloPlay/BuildFiles/Runewords/Malice.js @@ -1,4 +1,4 @@ -(function() { +(function () { const Malice = [ "[name] == IthRune # # [maxquantity] == 1", "[name] == ElRune # # [maxquantity] == 1", diff --git a/libs/SoloPlay/BuildFiles/Runewords/MercDoom.js b/libs/SoloPlay/BuildFiles/Runewords/MercDoom.js index cf5b95aa..2acafa9c 100644 --- a/libs/SoloPlay/BuildFiles/Runewords/MercDoom.js +++ b/libs/SoloPlay/BuildFiles/Runewords/MercDoom.js @@ -1,4 +1,4 @@ -(function() { +(function () { const Doom = [ "[name] == HelRune # # [maxquantity] == 1", "[name] == OhmRune", @@ -8,9 +8,21 @@ ]; NTIP.buildList(Doom); + /** @type {GetOwnedSettings} */ + const wanted = { + itemType: sdk.items.type.Polearm, + mode: sdk.items.mode.inStorage, + sockets: 5, + ethereal: true, + /** @param {ItemUnit} item */ + cb: function (item) { + return item.isBaseType; + } + }; + // Have Cham, Lo, and Ohm Rune before looking for normal base - if (me.getItem(sdk.items.runes.Cham) && me.getItem(sdk.items.runes.Lo) && me.getItem(sdk.items.runes.Ohm)) { - if (!Check.haveBase("polearm", 5)) { + if (me.haveRunes([sdk.items.runes.Cham, sdk.items.runes.Lo, sdk.items.runes.Ohm])) { + if (!me.getOwned(wanted).length) { NTIP.addLine("([name] == thresher || [name] == crypticaxe || [name] == greatpoleaxe || [name] == giantthresher) && [flag] == ethereal && [quality] == normal # [sockets] == 0 # [maxquantity] == 1"); } NTIP.addLine("([name] == thresher || [name] == crypticaxe || [name] == greatpoleaxe || [name] == giantthresher) && [flag] == ethereal && [quality] >= normal && [quality] <= superior # [sockets] == 5 # [maxquantity] == 1"); @@ -25,8 +37,8 @@ Config.Recipes.push([Recipe.Rune, "Vex Rune"]); Config.Recipes.push([Recipe.Rune, "Ohm Rune"]); if ((me.barbarian && me.haveAll([{ name: sdk.locale.items.Grief }, { name: sdk.locale.items.Fortitude }])) - || (["Witchyzon", "Wfzon"].includes(SetUp.finalBuild) && me.checkItem({ name: sdk.locale.items.ChainsofHonor }).have) - || (SetUp.currentBuild === "Faithbowzon")) { + || (["Witchyzon", "Wfzon"].includes(SetUp.finalBuild) && me.checkItem({ name: sdk.locale.items.ChainsofHonor }).have) + || (SetUp.currentBuild === "Faithbowzon")) { Config.Recipes.push([Recipe.Rune, "Lo Rune"]); } Config.Recipes.push([Recipe.Rune, "Sur Rune"]); @@ -35,8 +47,10 @@ } // Cube to Lo if (!me.getItem(sdk.items.runes.Lo)) { - if ((me.barbarian) || (SetUp.currentBuild === "Faithbowzon" && me.checkItem({ name: sdk.locale.items.CalltoArms }).have) - || (["Witchyzon", "Wfzon"].includes(SetUp.finalBuild) && me.haveAll([{ name: sdk.locale.items.ChainsofHonor }, { name: sdk.locale.items.CalltoArms }]))) { + if (me.barbarian + || (SetUp.currentBuild === "Faithbowzon" && me.checkItem({ name: sdk.locale.items.CalltoArms }).have) + || (["Witchyzon", "Wfzon"].includes(SetUp.finalBuild) + && me.haveAll([{ name: sdk.locale.items.ChainsofHonor }, { name: sdk.locale.items.CalltoArms }]))) { Config.Recipes.push([Recipe.Rune, "Mal Rune"]); Config.Recipes.push([Recipe.Rune, "Ist Rune"]); Config.Recipes.push([Recipe.Rune, "Gul Rune"]); diff --git a/libs/SoloPlay/BuildFiles/Runewords/MercFortitude.js b/libs/SoloPlay/BuildFiles/Runewords/MercFortitude.js index a01b6a08..711183bf 100644 --- a/libs/SoloPlay/BuildFiles/Runewords/MercFortitude.js +++ b/libs/SoloPlay/BuildFiles/Runewords/MercFortitude.js @@ -1,4 +1,4 @@ -(function() { +(function () { const fort = [ "[name] == ElRune # # [maxquantity] == 1", "[name] == SolRune # # [maxquantity] == 1", diff --git a/libs/SoloPlay/BuildFiles/Runewords/MercInfinity.js b/libs/SoloPlay/BuildFiles/Runewords/MercInfinity.js index 4b05d890..f6e5537a 100644 --- a/libs/SoloPlay/BuildFiles/Runewords/MercInfinity.js +++ b/libs/SoloPlay/BuildFiles/Runewords/MercInfinity.js @@ -1,4 +1,4 @@ -(function() { +(function () { const Inf = [ "[name] == BerRune", "[name] == MalRune", diff --git a/libs/SoloPlay/BuildFiles/Runewords/MercInsight.js b/libs/SoloPlay/BuildFiles/Runewords/MercInsight.js index a39dcc4f..b52dbbd8 100644 --- a/libs/SoloPlay/BuildFiles/Runewords/MercInsight.js +++ b/libs/SoloPlay/BuildFiles/Runewords/MercInsight.js @@ -20,6 +20,17 @@ ("(" + mid + ") && [quality] >= normal && [quality] <= superior && [flag] != runeword # [sockets] == 4 # [maxquantity] == 1"), ]; + /** @type {GetOwnedSettings} */ + const wanted = { + itemType: sdk.items.type.Polearm, + mode: sdk.items.mode.inStorage, + sockets: 4, + /** @param {ItemUnit} item */ + cb: function (item) { + return item.isBaseType; + } + }; + Config.Recipes.push([Recipe.Socket.Weapon, "giantthresher"]); Config.Recipes.push([Recipe.Socket.Weapon, "greatpoleaxe"]); Config.Recipes.push([Recipe.Socket.Weapon, "crypticaxe"]); @@ -42,10 +53,21 @@ Config.Runewords.push([Runeword.Insight, "scythe"]); Config.Runewords.push([Runeword.Insight, "voulge"]); } + + let currEquipped = Item.getMercEquipped(sdk.body.RightArm).prefixnum; + // if (currEquipped === sdk.locale.items.Insight) { + // if (Storage.Stash.UsedSpacePercent() < 75 + // || me.haveRunes([sdk.items.runes.Ral, sdk.items.runes.Tir, sdk.items.runes.Tal, sdk.items.runes.Sol])) { + // NTIP.buildList(Insight); + // } + // } else { + // } NTIP.buildList(Insight); - if (!me.hell && Item.getMercEquipped(sdk.body.RightArm).prefixnum !== sdk.locale.items.Insight && !Check.haveBase("polearm", 4)) { + if (!me.hell + && currEquipped !== sdk.locale.items.Insight + && !me.getOwned(wanted).length) { NTIP.addLine("[name] == voulge && [flag] != ethereal && [quality] == normal && [level] >= 26 && [level] <= 40 # [sockets] == 0 # [maxquantity] == 1"); } diff --git a/libs/SoloPlay/BuildFiles/Runewords/MercPride.js b/libs/SoloPlay/BuildFiles/Runewords/MercPride.js index 39f21f31..097d242b 100644 --- a/libs/SoloPlay/BuildFiles/Runewords/MercPride.js +++ b/libs/SoloPlay/BuildFiles/Runewords/MercPride.js @@ -1,4 +1,4 @@ -(function() { +(function () { const Pride = [ "[name] == ChamRune", "[name] == SurRune", diff --git a/libs/SoloPlay/BuildFiles/Runewords/MercTreachery.js b/libs/SoloPlay/BuildFiles/Runewords/MercTreachery.js index 11c72d3d..913acb66 100644 --- a/libs/SoloPlay/BuildFiles/Runewords/MercTreachery.js +++ b/libs/SoloPlay/BuildFiles/Runewords/MercTreachery.js @@ -1,4 +1,4 @@ -(function() { +(function () { const treach = [ "[name] == ShaelRune # # [maxquantity] == 1", "[name] == ThulRune # # [maxquantity] == 1", diff --git a/libs/SoloPlay/BuildFiles/Runewords/Myth.js b/libs/SoloPlay/BuildFiles/Runewords/Myth.js index e4a839ce..6b04fe9d 100644 --- a/libs/SoloPlay/BuildFiles/Runewords/Myth.js +++ b/libs/SoloPlay/BuildFiles/Runewords/Myth.js @@ -1,4 +1,4 @@ -(function() { +(function () { const Myth = [ "[name] == HelRune # # [maxquantity] == 1", "[name] == AmnRune # # [maxquantity] == 1", diff --git a/libs/SoloPlay/BuildFiles/Runewords/PDiamondShield.js b/libs/SoloPlay/BuildFiles/Runewords/PDiamondShield.js index fdf5617f..72246c81 100644 --- a/libs/SoloPlay/BuildFiles/Runewords/PDiamondShield.js +++ b/libs/SoloPlay/BuildFiles/Runewords/PDiamondShield.js @@ -1,4 +1,4 @@ -(function() { +(function () { const PDiamondShield = [ "[name] == perfectdiamond # # [maxquantity] == 3", "[name] == towershield && [quality] >= normal && [quality] <= superior # [sockets] == 3 # [maxquantity] == 1", @@ -6,7 +6,7 @@ NTIP.buildList(PDiamondShield); // cube to Pdiamonds - if (Item.getQuantityOwned(me.getItem(sdk.items.gems.Perfect.Diamond)) < 3) { + if (me.getOwned({ classid: sdk.items.gems.Perfect.Diamond }).length < 3) { Config.Recipes.push([Recipe.Gem, "flawlessdiamond"]); } diff --git a/libs/SoloPlay/BuildFiles/Runewords/PhoenixShield.js b/libs/SoloPlay/BuildFiles/Runewords/PhoenixShield.js index cb2b6667..21d5beef 100644 --- a/libs/SoloPlay/BuildFiles/Runewords/PhoenixShield.js +++ b/libs/SoloPlay/BuildFiles/Runewords/PhoenixShield.js @@ -1,4 +1,4 @@ -(function() { +(function () { const PhoenixRunes = [ "[name] == VexRune", "[name] == LoRune", @@ -6,17 +6,32 @@ "[name] == monarch && [flag] != ethereal && [quality] >= normal && [quality] <= superior # [sockets] == 4 # [maxquantity] == 1" ]; NTIP.buildList(PhoenixRunes); + + /** @type {GetOwnedSettings} */ + const wanted = { + classid: sdk.items.Monarch, + mode: sdk.items.mode.inStorage, + sockets: 4, + /** @param {ItemUnit} item */ + cb: function (item) { + return item.isBaseType; + } + }; + // Cube to vex and Keep cubing to Jah rune if (!me.getItem(sdk.items.runes.Jah)) { - if (Item.getQuantityOwned(me.getItem(sdk.items.runes.Vex)) < 2) { + if (me.getOwned({ classid: sdk.items.runes.Vex }).length < 2) { Config.Recipes.push([Recipe.Rune, "Mal Rune"]); Config.Recipes.push([Recipe.Rune, "Ist Rune"]); Config.Recipes.push([Recipe.Rune, "Gul Rune"]); } - (Item.getQuantityOwned(me.getItem(sdk.items.runes.Vex)) > 1 && !me.getItem(sdk.items.runes.Jah) && Config.Recipes.push([Recipe.Rune, "Vex Rune"])); + if (me.getOwned({ classid: sdk.items.runes.Vex }).length > 1 && !me.getItem(sdk.items.runes.Jah)) { + Config.Recipes.push([Recipe.Rune, "Vex Rune"]); + } } // Cube to Lo and Keep cubing to Jah rune - if (Item.getQuantityOwned(me.getItem(sdk.items.runes.Lo)) > 1 && me.checkItem({ name: sdk.locale.items.HeartoftheOak }).have) { + if (me.getOwned({ classid: sdk.items.runes.Lo }).length > 1 + && me.checkItem({ name: sdk.locale.items.HeartoftheOak }).have) { if (!me.getItem(sdk.items.runes.Jah)) { Config.Recipes.push([Recipe.Rune, "Vex Rune"]); Config.Recipes.push([Recipe.Rune, "Ohm Rune"]); @@ -24,13 +39,14 @@ !me.getItem(sdk.items.runes.Jah) && Config.Recipes.push([Recipe.Rune, "Lo Rune"]); } // Cube to Jah rune - if (me.checkItem({ name: sdk.locale.items.Enigma }).have && !me.getItem(sdk.items.runes.Jah)) { + if (me.checkItem({ name: sdk.locale.items.Enigma }).have + && !me.getItem(sdk.items.runes.Jah)) { Config.Recipes.push([Recipe.Rune, "Lo Rune"]); Config.Recipes.push([Recipe.Rune, "Sur Rune"]); Config.Recipes.push([Recipe.Rune, "Ber Rune"]); } - if (!Check.haveBase("shield", 4)) { + if (!me.getOwned(wanted).length) { NTIP.addLine("[name] == monarch && [flag] != ethereal && [quality] == normal # [sockets] == 0 # [maxquantity] == 1"); } diff --git a/libs/SoloPlay/BuildFiles/Runewords/Rhyme.js b/libs/SoloPlay/BuildFiles/Runewords/Rhyme.js index 1d25468a..d47b1763 100644 --- a/libs/SoloPlay/BuildFiles/Runewords/Rhyme.js +++ b/libs/SoloPlay/BuildFiles/Runewords/Rhyme.js @@ -1,4 +1,4 @@ -(function() { +(function () { const rhyme = [ "[name] == ShaelRune # # [maxquantity] == 1", "[name] == EthRune # # [maxquantity] == 1", diff --git a/libs/SoloPlay/BuildFiles/Runewords/Sanctuary.js b/libs/SoloPlay/BuildFiles/Runewords/Sanctuary.js index 09443544..328ca464 100644 --- a/libs/SoloPlay/BuildFiles/Runewords/Sanctuary.js +++ b/libs/SoloPlay/BuildFiles/Runewords/Sanctuary.js @@ -1,4 +1,4 @@ -(function() { +(function () { const Sanctuary = [ "[name] == KoRune # # [maxquantity] == 2", "[name] == MalRune", @@ -18,11 +18,13 @@ Config.Recipes.push([Recipe.Rune, "Lum Rune"]); } - if (!Check.haveBase("shield", 3)) { - NTIP.addLine("[name] == hyperion && [flag] != ethereal && [quality] == normal # [sockets] == 0 # [maxquantity] == 1"); + if (!me.getOwned({ classid: sdk.items.Hyperion, sockets: 3 }).length) { + if (Storage.Stash.UsedSpacePercent() < 75) { + NTIP.addLine("[name] == hyperion && [flag] != ethereal && [quality] == normal # [sockets] == 0 # [maxquantity] == 1"); + Config.Recipes.push([Recipe.Socket.Shield, "hyperion", Roll.NonEth]); + } } - Config.Recipes.push([Recipe.Socket.Shield, "hyperion", Roll.NonEth]); Config.Runewords.push([Runeword.Sanctuary, "hyperion"]); Config.KeepRunewords.push("[type] == shield # [fhr] >= 20 && [enhanceddefense] >= 130 && [fireresist] >= 50"); diff --git a/libs/SoloPlay/BuildFiles/Runewords/Silence.js b/libs/SoloPlay/BuildFiles/Runewords/Silence.js index faabccd1..baec6fa9 100644 --- a/libs/SoloPlay/BuildFiles/Runewords/Silence.js +++ b/libs/SoloPlay/BuildFiles/Runewords/Silence.js @@ -1,4 +1,4 @@ -(function() { +(function () { const Silence = [ "[name] == EldRune # # [maxquantity] == 1", "[name] == TirRune # # [maxquantity] == 1", @@ -9,12 +9,23 @@ ]; NTIP.buildList(Silence); + /** @type {GetOwnedSettings} */ + const wanted = { + classid: sdk.items.PhaseBlade, + mode: sdk.items.mode.inStorage, + sockets: 6, + /** @param {ItemUnit} item */ + cb: function (item) { + return item.isBaseType; + } + }; + // Have Vex before collecting base if (me.getItem(sdk.items.runes.Vex)) { NTIP.addLine("[name] == phaseblade && [quality] <= superior && [flag] != ethereal # [sockets] == 6 # [maxquantity] == 1"); // Have Ist+Vex rune but do not have a base yet - if (me.getItem(sdk.items.runes.Ist) && !Check.haveBase("phaseblade", 6)) { + if (me.getItem(sdk.items.runes.Ist) && !me.getOwned(wanted).length) { NTIP.addLine("[name] == phaseblade && [quality] == normal && [flag] != ethereal # [sockets] == 0 # [maxquantity] == 1"); Config.Recipes.push([Recipe.Socket.Weapon, "phaseblade"]); } diff --git a/libs/SoloPlay/BuildFiles/Runewords/Smoke.js b/libs/SoloPlay/BuildFiles/Runewords/Smoke.js index 07f74e16..0a6d42e0 100644 --- a/libs/SoloPlay/BuildFiles/Runewords/Smoke.js +++ b/libs/SoloPlay/BuildFiles/Runewords/Smoke.js @@ -1,5 +1,5 @@ -(function() { - if (!Check.haveItem("armor", "runeword", "Smoke") && !me.hell) { +(function () { + if (!me.checkItem({ name: sdk.locale.items.Smoke }).have && !me.hell) { // Cube to Lum Rune if (!me.getItem(sdk.items.runes.Lum)) { Config.Recipes.push([Recipe.Rune, "Io Rune"]); diff --git a/libs/SoloPlay/BuildFiles/Runewords/SpiritShield.js b/libs/SoloPlay/BuildFiles/Runewords/SpiritShield.js index d992bf80..9b33d9ab 100644 --- a/libs/SoloPlay/BuildFiles/Runewords/SpiritShield.js +++ b/libs/SoloPlay/BuildFiles/Runewords/SpiritShield.js @@ -1,4 +1,4 @@ -(function() { +(function () { const SpiritRunes = [ "[name] == TalRune # # [maxquantity] == 1", "[name] == ThulRune # # [maxquantity] == 1", @@ -7,6 +7,17 @@ ]; NTIP.buildList(SpiritRunes); + /** @type {GetOwnedSettings} */ + const wanted = { + classid: sdk.items.Monarch, + mode: sdk.items.mode.inStorage, + sockets: 4, + /** @param {ItemUnit} item */ + cb: function (item) { + return item.isBaseType; + } + }; + if (me.paladin) { NTIP.addLine("([name] == targe || [name] == rondache || [name] == heraldicshield || [name] == aerinshield || [name] == akarantarge || [name] == akaranrondache || [name] == gildedshield ||[name] == protectorshield || [name] == sacredtarge) && [flag] != ethereal && [quality] >= normal && [quality] <= superior # [fireresist] > 0 && [sockets] == 4"); NTIP.addLine("([name] == targe || [name] == rondache || [name] == heraldicshield || [name] == aerinshield || [name] == akarantarge || [name] == akaranrondache || [name] == gildedshield ||[name] == protectorshield || [name] == sacredtarge) && [flag] != ethereal && [quality] == normal # [fireresist] > 0 && [sockets] == 0"); @@ -35,7 +46,7 @@ } else { NTIP.addLine("[name] == monarch && [flag] != ethereal && [quality] >= normal && [quality] <= superior # [sockets] == 4 # [maxquantity] == 1"); - if (!Check.haveBase("shield", 4)) { + if (!me.getOwned(wanted).length) { NTIP.addLine("[name] == monarch && [flag] != ethereal && [quality] == normal # [sockets] == 0 # [maxquantity] == 1"); } diff --git a/libs/SoloPlay/BuildFiles/Runewords/SpiritSword.js b/libs/SoloPlay/BuildFiles/Runewords/SpiritSword.js index 3eb18496..9a29b95e 100644 --- a/libs/SoloPlay/BuildFiles/Runewords/SpiritSword.js +++ b/libs/SoloPlay/BuildFiles/Runewords/SpiritSword.js @@ -1,5 +1,6 @@ -(function() { - if (!Check.haveItem("sword", "runeword", "Spirit") && !me.hell) { +(function () { + if (!me.checkItem({ name: sdk.locale.items.Spirit, itemtype: sdk.items.type.Sword }).have + && !me.hell) { const SpiritSword = [ "[name] == TalRune # # [maxquantity] == 1", "[name] == ThulRune # # [maxquantity] == 1", diff --git a/libs/SoloPlay/BuildFiles/Runewords/Stealth.js b/libs/SoloPlay/BuildFiles/Runewords/Stealth.js index cca9178d..54534522 100644 --- a/libs/SoloPlay/BuildFiles/Runewords/Stealth.js +++ b/libs/SoloPlay/BuildFiles/Runewords/Stealth.js @@ -1,5 +1,5 @@ -(function() { - if (!Check.haveItem("armor", "runeword", "Stealth") && me.normal) { +(function () { + if (!me.checkItem({ name: sdk.locale.items.Stealth }).have && me.normal) { const stealthRunes = [ "[name] == TalRune # # [maxquantity] == 1", "[name] == EthRune # # [maxquantity] == 1", diff --git a/libs/SoloPlay/BuildFiles/Runewords/Steel.js b/libs/SoloPlay/BuildFiles/Runewords/Steel.js index ddb1da6f..b2733b0b 100644 --- a/libs/SoloPlay/BuildFiles/Runewords/Steel.js +++ b/libs/SoloPlay/BuildFiles/Runewords/Steel.js @@ -1,16 +1,20 @@ -(function() { +(function () { const Steel = [ "[name] == TirRune # # [maxquantity] == 2", "[name] == ElRune # # [maxquantity] == 2", ]; NTIP.buildList(Steel); + const leftArmTier = me.equipped.get(sdk.body.LeftArm).tier; + const commonType = "[type] == sword && [flag] != ethereal"; + const commonQuality = "[quality] >= normal && [quality] <= superior"; + const commonStat = "[wsm] <= 10 && [strreq] <= 150"; - if (me.equipped.get(sdk.body.LeftArm).tier < 500 && me.equipped.get(sdk.body.LeftArm).tier > 395) { - NTIP.addLine("[type] == sword && [flag] != ethereal && [quality] >= normal && [quality] <= superior && [wsm] <= 10 && [strreq] <= 150 && [class] == elite # [sockets] == 2 # [maxquantity] == 1"); - } else if (me.equipped.get(sdk.body.LeftArm).tier < 500 && me.equipped.get(sdk.body.LeftArm).tier > 278) { - NTIP.addLine("[type] == sword && [flag] != ethereal && [quality] >= normal && [quality] <= superior && [wsm] <= 10 && [strreq] <= 150 && [class] > normal # [sockets] == 2 # [maxquantity] == 1"); + if (leftArmTier < 500 && leftArmTier > 395) { + NTIP.addLine(commonType + " && " + commonQuality + " && " + commonStat + " && [class] == elite # [sockets] == 2 # [maxquantity] == 1"); + } else if (leftArmTier < 500 && leftArmTier > 278) { + NTIP.addLine(commonType + " && " + commonQuality + " && " + commonStat + " && [class] > normal # [sockets] == 2 # [maxquantity] == 1"); } else { - NTIP.addLine("[type] == sword && [flag] != ethereal && [quality] == superior && [wsm] <= 10 && [strreq] <= 150 # [enhanceddamage] >= 10 && [sockets] == 2 # [maxquantity] == 1"); + NTIP.addLine(commonType + " && [quality] == superior && " + commonStat + " # [enhanceddamage] >= 10 && [sockets] == 2 # [maxquantity] == 1"); } Config.Runewords.push([Runeword.Steel, "shortsword"]); diff --git a/libs/SoloPlay/BuildFiles/Runewords/Treachery.js b/libs/SoloPlay/BuildFiles/Runewords/Treachery.js index beab3398..c71b8eae 100644 --- a/libs/SoloPlay/BuildFiles/Runewords/Treachery.js +++ b/libs/SoloPlay/BuildFiles/Runewords/Treachery.js @@ -1,4 +1,4 @@ -(function() { +(function () { const treach = [ "[name] == ShaelRune # # [maxquantity] == 1", "[name] == ThulRune # # [maxquantity] == 1", diff --git a/libs/SoloPlay/BuildFiles/Runewords/VoiceOfReason.js b/libs/SoloPlay/BuildFiles/Runewords/VoiceOfReason.js index 6f5d8d07..d1a4bd73 100644 --- a/libs/SoloPlay/BuildFiles/Runewords/VoiceOfReason.js +++ b/libs/SoloPlay/BuildFiles/Runewords/VoiceOfReason.js @@ -1,4 +1,4 @@ -(function() { +(function () { const VoiceofReason = [ "[name] == LemRune", "[name] == KoRune", diff --git a/libs/SoloPlay/BuildFiles/Runewords/White.js b/libs/SoloPlay/BuildFiles/Runewords/White.js index a4bfa314..d705f33e 100644 --- a/libs/SoloPlay/BuildFiles/Runewords/White.js +++ b/libs/SoloPlay/BuildFiles/Runewords/White.js @@ -1,17 +1,23 @@ -(function() { +(function () { + const _wands = ("[type] == wand && ([name] != wand && [name] != yewwand && [name] != burntwand)"); + const _skills = ("[necromancerskills]+[poisonandboneskilltab]+[skillbonespear]+[skillbonespirit]+[skillteeth]+[skillbonewall]+[skillboneprison]+[skillamplifydamage]"); const white = [ "[name] == DolRune # # [maxquantity] == 1", "[name] == IoRune # # [maxquantity] == 1", - "[type] == wand && ([name] != wand && [name] != yewwand && [name] != burntwand) && [quality] >= normal && [quality] <= superior # [sockets] == 2 # [maxquantity] == 1", - "[type] == wand && ([name] != wand && [name] != yewwand && [name] != burntwand) && [quality] == normal # ([necromancerskills]+[poisonandboneskilltab]+[skillbonespear]+[skillbonespirit]+[skillteeth]+[skillbonewall]+[skillboneprison]+[skillamplifydamage]) >= 1 && [sockets] == 0 # [maxquantity] == 1", + (_wands + " && [quality] >= normal && [quality] <= superior # [sockets] == 2"), + (_wands + " && [quality] == normal # (" + _skills + ") >= 1 && [sockets] == 0 # [maxquantity] == 1"), ]; NTIP.buildList(white); + // if (me.equipped.get(sdk.body.RightArm).tier < NTIP.MAX_TIER) { + // NTIP.addLine((_wands + " && [quality] >= normal && [quality] <= superior # [sockets] == 2 # [maxquantity] == 1")); + // } + // Cube to Io rune if (!me.getItem(sdk.items.runes.Io)) { Config.Recipes.push([Recipe.Rune, "Hel Rune"]); } - + Config.Runewords.push([Runeword.White, "bonewand"]); Config.Runewords.push([Runeword.White, "grimwand"]); Config.Runewords.push([Runeword.White, "petrifiedwand"]); From 5ff2a0f75d200a26baadb4ed6f0dc5694f3e2de7 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Fri, 14 Jul 2023 12:16:45 -0400 Subject: [PATCH 205/263] Remove `Check.haveBase` from configs - Continue phasing it out in favor of `me.getOwned` - refactored the way `Config.imbueables` is initialized, makes it easier to read --- libs/SoloPlay/Config/Amazon.js | 133 +++++++++++++++----- libs/SoloPlay/Config/Assassin.js | 125 +++++++++++++++---- libs/SoloPlay/Config/Barbarian.js | 139 +++++++++++++++++---- libs/SoloPlay/Config/Druid.js | 96 ++++++++++++--- libs/SoloPlay/Config/Necromancer.js | 16 ++- libs/SoloPlay/Config/Paladin.js | 183 +++++++++++++++++++++------- libs/SoloPlay/Config/Sorceress.js | 103 ++++++++++++---- 7 files changed, 631 insertions(+), 164 deletions(-) diff --git a/libs/SoloPlay/Config/Amazon.js b/libs/SoloPlay/Config/Amazon.js index 5cb70927..d5994d64 100644 --- a/libs/SoloPlay/Config/Amazon.js +++ b/libs/SoloPlay/Config/Amazon.js @@ -20,6 +20,8 @@ includeIfNotIncluded("SoloPlay/Functions/MiscOverrides.js"); includeIfNotIncluded("SoloPlay/Functions/Globals.js"); + const LADDER_ENABLED = (me.ladder || Developer.addLadderRW); + SetUp.include(); SetUp.config(); @@ -29,11 +31,24 @@ // Config.PickitFiles.push("LLD.nip"); /* Gambling configuration. */ - Config.GambleItems.push("Amulet"); - Config.GambleItems.push("Ring"); - Config.GambleItems.push("Circlet"); - Config.GambleItems.push("Coronet"); + if (me.equipped.get(sdk.body.Neck).tier < 100000) { + Config.GambleItems.push("Amulet"); + } + if (me.equipped.get(sdk.body.LeftRing).tier < 100000 + || me.equipped.get(sdk.body.RightRing).tier < 100000) { + Config.GambleItems.push("Ring"); + } + if (me.equipped.get(sdk.body.Head).tier < 100000) { + Config.GambleItems.push("Circlet"); + Config.GambleItems.push("Coronet"); + } + if (me.equipped.get(sdk.body.RightArm).tier < 100000) { + Config.GambleItems.push("Javelin"); + Config.GambleItems.push("Pilum"); + Config.GambleItems.push("Short Spear"); + Config.GambleItems.push("Throwing Spear"); + } // AutoEquip setup const levelingTiers = [ // Weapon @@ -71,26 +86,60 @@ Config.LightningFuryDelay = 10; // Lightning fury interval in seconds. LF is treated as timed skill. Config.SummonValkyrie = true; // Summon Valkyrie - Config.imbueables = [ - { name: sdk.items.MaidenJavelin, condition: () => me.normal && me.expansion }, - { name: sdk.items.CeremonialJavelin, condition: () => !me.normal && (me.charlvl < 48 || me.trueStr < 107 || me.trueDex < 151) && me.expansion }, - { name: sdk.items.MatriarchalJavelin, condition: () => (me.equipped.get(sdk.body.RightArm).tier < 100000 && me.trueStr >= 107 && me.trueDex >= 151 && me.expansion) }, - { name: sdk.items.Belt, condition: () => (me.normal && (me.equipped.get(sdk.body.RightArm).tier > 100000 || me.classic)) }, - { name: sdk.items.MeshBelt, condition: () => (!me.normal && me.charlvl < 46 && me.trueStr > 58 && (me.equipped.get(sdk.body.RightArm).tier > 100000 || me.classic)) }, - { name: sdk.items.SpiderwebSash, condition: () => (!me.normal && me.trueStr > 50 && (me.equipped.get(sdk.body.RightArm).tier > 100000 || me.classic)) }, - ]; + Config.imbueables = (function () { + /** + * @param {number} name + * @param {function(): boolean} condition + */ + const _imbueObj = (name, condition) => ({ name: name, condition: condition }); + + return [ + _imbueObj( + sdk.items.MaidenJavelin, + function () { + return me.normal && me.expansion; + } + ), + _imbueObj( + sdk.items.CeremonialJavelin, + function () { + return !me.normal && (me.charlvl < 48 || me.trueStr < 107 || me.trueDex < 151) && me.expansion; + } + ), + _imbueObj( + sdk.items.MatriarchalJavelin, + function () { + return me.equipped.get(sdk.body.RightArm).tier < 100000 + && me.trueStr >= 107 && me.trueDex >= 151 && me.expansion; + } + ), + _imbueObj( + sdk.items.Belt, + function () { + return me.normal && (me.equipped.get(sdk.body.RightArm).tier > 100000 || me.classic); + } + ), + _imbueObj( + sdk.items.MeshBelt, + function () { + return !me.normal && me.charlvl < 46 && me.trueStr > 58 + && (me.equipped.get(sdk.body.RightArm).tier > 100000 || me.classic); + } + ), + _imbueObj( + sdk.items.SpiderwebSash, + function () { + return !me.normal && me.trueStr > 50 + && (me.equipped.get(sdk.body.RightArm).tier > 100000 || me.classic); + } + ), + ].filter((item) => item.condition()); + })(); let imbueArr = SetUp.imbueItems(); !me.smith && NTIP.buildList(imbueArr); - if (me.equipped.get(sdk.body.RightArm).tier < 100000) { - Config.GambleItems.push("Javelin"); - Config.GambleItems.push("Pilum"); - Config.GambleItems.push("Short Spear"); - Config.GambleItems.push("Throwing Spear"); - } - switch (me.gametype) { case sdk.game.gametype.Classic: // Res shield @@ -104,10 +153,13 @@ const { basicSocketables, addSocketableObj } = require("../Utils/General"); Config.socketables = Config.socketables.concat(basicSocketables.all); - Config.socketables.push(addSocketableObj(sdk.items.Bill, [], [], + Config.socketables.push(addSocketableObj( + sdk.items.Bill, [], [], me.normal, (item) => item.ilvl >= 26 && item.isBaseType )); - Config.socketables.push(addSocketableObj(sdk.items.Shako, [sdk.items.runes.Um], [sdk.items.gems.Perfect.Ruby], + Config.socketables.push(addSocketableObj( + sdk.items.Shako, + [sdk.items.runes.Um], [sdk.items.gems.Perfect.Ruby], true, (item) => item.unique && !item.ethereal )); @@ -129,15 +181,20 @@ case "Witchyzon": case "Faithbowzon": case "Wfzon": - (["Witchyzon", "Wfzon", "Faithbowzon"].includes(SetUp.currentBuild)) && NTIP.addLine("[type] == bowquiver # # [maxquantity] == 1"); + if (["Witchyzon", "Wfzon", "Faithbowzon"].includes(SetUp.currentBuild)) { + NTIP.addLine("[type] == bowquiver # # [maxquantity] == 1"); + } if (SetUp.finalBuild === "Wfzon") { if (!Check.haveItem(sdk.items.HydraBow, "unique", "Windforce")) { NTIP.addLine("[name] == hydrabow && [quality] == unique # [manaleech] >= 6 # [maxquantity] == 1"); } - Config.socketables.push(addSocketableObj(sdk.items.HydraBow, [sdk.items.runes.Shael], [sdk.items.runes.Amn], - true, (item) => item.unique + Config.socketables.push(addSocketableObj( + sdk.items.HydraBow, + [sdk.items.runes.Shael], [sdk.items.runes.Amn], + true, + (item) => item.unique )); } @@ -155,17 +212,24 @@ } // Spirit shield - while lvling and Wf final switch - if ((me.ladder || Developer.addLadderRW) && (me.equipped.get(5).tier < 1000 + if ((LADDER_ENABLED) && (me.equipped.get(5).tier < 1000 && (["Witchyzon", "Wfzon", "Faithbowzon"].indexOf(SetUp.currentBuild) === -1) || (SetUp.finalBuild === "Wfzon" && me.equipped.get(12).prefixnum !== sdk.locale.items.Spirit))) { includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/SpiritShield.js"); } - Config.socketables.push(addSocketableObj(sdk.items.DiamondBow, [sdk.items.runes.Nef, sdk.items.runes.Shael], [sdk.items.gems.Perfect.Skull], - false, (item) => item.unique + Config.socketables.push(addSocketableObj( + sdk.items.DiamondBow, + [sdk.items.runes.Nef, sdk.items.runes.Shael], [sdk.items.gems.Perfect.Skull], + false, + (item) => item.unique )); - Config.socketables.push(addSocketableObj(sdk.items.BoneVisage, [sdk.items.runes.Um], [sdk.items.gems.Perfect.Ruby], - true, (item) => item.unique && item.getStat(sdk.stats.LifeLeech) === 8 && item.getStat(sdk.stats.DamageResist) === 20 && !item.ethereal + Config.socketables.push(addSocketableObj( + sdk.items.BoneVisage, + [sdk.items.runes.Um], [sdk.items.gems.Perfect.Ruby], + true, + (item) => (item.unique && item.getStat(sdk.stats.LifeLeech) === 8 + && item.getStat(sdk.stats.DamageResist) === 20 && !item.ethereal) )); break; @@ -180,12 +244,15 @@ } // Infinity - if ((me.ladder || Developer.addLadderRW) && Item.getMercEquipped(sdk.body.RightArm).prefixnum !== sdk.locale.items.Infinity) { + if ((LADDER_ENABLED) + && Item.getMercEquipped(sdk.body.RightArm).prefixnum !== sdk.locale.items.Infinity) { includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/MercInfinity.js"); } // Spirit shield - if ((me.ladder || Developer.addLadderRW) && (me.equipped.get(sdk.body.LeftArm).tier < 1000 || me.equipped.get(sdk.body.LeftArmSecondary).prefixnum !== sdk.locale.items.Spirit)) { + if (LADDER_ENABLED + && (me.equipped.get(sdk.body.LeftArm).tier < 1000 + || me.equipped.get(sdk.body.LeftArmSecondary).prefixnum !== sdk.locale.items.Spirit)) { includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/SpiritShield.js"); } @@ -205,7 +272,7 @@ } // Merc Insight - if ((me.ladder || Developer.addLadderRW) && Item.getMercEquipped(sdk.body.RightArm).tier < 3600) { + if ((LADDER_ENABLED) && Item.getMercEquipped(sdk.body.RightArm).tier < 3600) { includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/MercInsight.js"); } @@ -225,7 +292,7 @@ } // Merc Doom - if ((me.ladder || Developer.addLadderRW) && Item.getMercEquipped(sdk.body.RightArm).prefixnum !== 20532 && SetUp.finalBuild !== "Javazon") { + if ((LADDER_ENABLED) && Item.getMercEquipped(sdk.body.RightArm).prefixnum !== 20532 && SetUp.finalBuild !== "Javazon") { includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/MercDoom.js"); } diff --git a/libs/SoloPlay/Config/Assassin.js b/libs/SoloPlay/Config/Assassin.js index cb66e6e7..81fac8e2 100644 --- a/libs/SoloPlay/Config/Assassin.js +++ b/libs/SoloPlay/Config/Assassin.js @@ -18,6 +18,8 @@ includeIfNotIncluded("SoloPlay/Functions/MiscOverrides.js"); includeIfNotIncluded("SoloPlay/Functions/Globals.js"); + const LADDER_ENABLED = (me.ladder || Developer.addLadderRW); + SetUp.include(); SetUp.config(); @@ -28,8 +30,13 @@ NTIP.addLine("[name] >= VexRune && [name] <= ZodRune"); /* Gambling configuration. */ - Config.GambleItems.push("Amulet"); - Config.GambleItems.push("Ring"); + if (me.equipped.get(sdk.body.Neck).tier < 100000) { + Config.GambleItems.push("Amulet"); + } + if (me.equipped.get(sdk.body.LeftRing).tier < 100000 + || me.equipped.get(sdk.body.RightRing).tier < 100000) { + Config.GambleItems.push("Ring"); + } // Config.GambleItems.push("Circlet"); // Config.GambleItems.push("Coronet"); @@ -65,8 +72,20 @@ /* Class specific configuration. */ Config.UseTraps = true; - Config.Traps = [sdk.skills.LightningSentry, sdk.skills.LightningSentry, sdk.skills.LightningSentry, sdk.skills.DeathSentry, sdk.skills.DeathSentry]; - Config.BossTraps = [sdk.skills.LightningSentry, sdk.skills.LightningSentry, sdk.skills.LightningSentry, sdk.skills.LightningSentry, sdk.skills.LightningSentry]; + Config.Traps = [ + sdk.skills.LightningSentry, + sdk.skills.LightningSentry, + sdk.skills.LightningSentry, + sdk.skills.DeathSentry, + sdk.skills.DeathSentry + ]; + Config.BossTraps = [ + sdk.skills.LightningSentry, + sdk.skills.LightningSentry, + sdk.skills.LightningSentry, + sdk.skills.LightningSentry, + sdk.skills.LightningSentry + ]; Config.SummonShadow = me.checkSkill(sdk.skills.ShadowMaster, sdk.skills.subindex.HardPoints) ? "Master" : 0; Config.UseFade = me.checkSkill(sdk.skills.Fade, sdk.skills.subindex.HardPoints); @@ -80,15 +99,53 @@ Config.DodgeRange = 10; Config.DodgeHP = 75; - Config.imbueables = [ - { name: sdk.items.Claws, condition: () => (me.normal) }, - { name: sdk.items.HandScythe, condition: () => (!me.normal && me.equipped.get(sdk.body.RightArm).tier < 777 && (me.trueStr < 79 || me.trueDex < 79)) }, - { name: sdk.items.GreaterTalons, condition: () => (me.equipped.get(sdk.body.RightArm).tier < 777 && me.trueStr >= 79 && me.trueDex >= 79) }, - { name: sdk.items.Belt, condition: () => (me.normal && (me.equipped.get(sdk.body.RightArm).tier > 777)) }, - { name: sdk.items.MeshBelt, condition: () => (!me.normal && me.charlvl < 46 && me.trueStr > 58 && (me.equipped.get(sdk.body.RightArm).tier > 777)) }, - { name: sdk.items.SpiderwebSash, condition: () => (!me.normal && me.trueStr > 50 && (me.equipped.get(sdk.body.RightArm).tier > 777)) }, - ].filter((item) => item.condition()); - + Config.imbueables = (function () { + /** + * @param {number} name + * @param {function(): boolean} condition + */ + const _imbueObj = (name, condition) => ({ name: name, condition: condition }); + + return [ + _imbueObj( + sdk.items.Claws, + function () { + return me.normal; + } + ), + _imbueObj( + sdk.items.HandScythe, + function () { + return !me.normal && me.equipped.get(sdk.body.RightArm).tier < 777 && (me.trueStr < 79 || me.trueDex < 79); + } + ), + _imbueObj( + sdk.items.GreaterTalons, + function () { + return me.equipped.get(sdk.body.RightArm).tier < 777 && me.trueStr >= 79 && me.trueDex >= 79; + } + ), + _imbueObj( + sdk.items.Belt, + function () { + return me.normal && me.equipped.get(sdk.body.RightArm).tier > 777; + } + ), + _imbueObj( + sdk.items.MeshBelt, + function () { + return !me.normal && me.charlvl < 46 && me.trueStr > 58 && me.equipped.get(sdk.body.RightArm).tier > 777; + } + ), + _imbueObj( + sdk.items.SpiderwebSash, + function () { + return !me.normal && me.trueStr > 50 && me.equipped.get(sdk.body.RightArm).tier > 777; + } + ), + ].filter((item) => item.condition()); + })(); + let imbueArr = SetUp.imbueItems(); !me.smith && NTIP.buildList(imbueArr); @@ -97,20 +154,38 @@ Config.socketables = Config.socketables.concat(basicSocketables.caster, basicSocketables.all); Config.socketables.push(addSocketableObj(sdk.items.Monarch, [], [], - !me.hell, (item) => !Check.haveBase("monarch", 4) && item.ilvl >= 41 && item.isBaseType && !item.ethereal + !me.hell, + /** @param {ItemUnit} item */ + function (item) { + /** @type {GetOwnedSettings} */ + const wanted = { + classid: sdk.items.Monarch, + mode: sdk.items.mode.inStorage, + sockets: 4, + /** @param {ItemUnit} item */ + cb: function (item) { + return item.isBaseType; + } + }; + return !me.getOwned(wanted).length && item.ilvl >= 41 && item.isBaseType && !item.ethereal; + } )); - Config.socketables.push(addSocketableObj(sdk.items.Shako, [sdk.items.runes.Um], [sdk.items.gems.Perfect.Ruby], + Config.socketables.push(addSocketableObj( + sdk.items.Shako, + [sdk.items.runes.Um], [sdk.items.gems.Perfect.Ruby], true, (item) => item.unique && !item.ethereal )); switch (SetUp.finalBuild) { case "Whirlsin": - Config.socketables.push(addSocketableObj(sdk.items.WingedHelm, [sdk.items.runes.Um], [sdk.items.gems.Perfect.Ruby], + Config.socketables.push(addSocketableObj( + sdk.items.WingedHelm, + [sdk.items.runes.Um], [sdk.items.gems.Perfect.Ruby], true, (item) => item.unique && !item.ethereal )); // Pride - if ((me.ladder || Developer.addLadderRW) && Item.getMercEquipped(sdk.body.RightArm).prefixnum !== sdk.locale.items.Pride) { + if ((LADDER_ENABLED) && Item.getMercEquipped(sdk.body.RightArm).prefixnum !== sdk.locale.items.Pride) { includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/MercPride.js"); } @@ -125,18 +200,20 @@ } // Fortitude - if ((me.ladder || Developer.addLadderRW) && !me.checkItem({ name: sdk.locale.items.Fortitude, itemtype: sdk.items.type.Armor }).have) { + if ((LADDER_ENABLED) && !me.checkItem({ name: sdk.locale.items.Fortitude, itemtype: sdk.items.type.Armor }).have) { includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/Fortitude.js"); } break; default: - Config.socketables.push(addSocketableObj(sdk.items.Demonhead, [sdk.items.runes.Um], [sdk.items.gems.Perfect.Ruby], + Config.socketables.push(addSocketableObj( + sdk.items.Demonhead, + [sdk.items.runes.Um], [sdk.items.gems.Perfect.Ruby], true, (item) => item.unique && !item.ethereal )); // Infinity - if ((me.ladder || Developer.addLadderRW) && Item.getMercEquipped(sdk.body.RightArm).prefixnum !== sdk.locale.items.Infinity) { + if ((LADDER_ENABLED) && Item.getMercEquipped(sdk.body.RightArm).prefixnum !== sdk.locale.items.Infinity) { includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/MercInfinity.js"); } @@ -171,17 +248,19 @@ } // Spirit Sword - if ((me.ladder || Developer.addLadderRW) && me.equipped.get(sdk.body.RightArm).tier < 777) { + if ((LADDER_ENABLED) && me.equipped.get(sdk.body.RightArm).tier < 777) { includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/SpiritSword.js"); } // Spirit shield - if ((me.ladder || Developer.addLadderRW) && (me.equipped.get(sdk.body.LeftArm).tier < 1000 || me.equipped.get(sdk.body.LeftArmSecondary).prefixnum !== sdk.locale.items.Spirit)) { + if ((LADDER_ENABLED) + && (me.equipped.get(sdk.body.LeftArm).tier < 1000 + || me.equipped.get(sdk.body.LeftArmSecondary).prefixnum !== sdk.locale.items.Spirit)) { includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/SpiritShield.js"); } // Merc Insight - if ((me.ladder || Developer.addLadderRW) && Item.getMercEquipped(sdk.body.RightArm).tier < 3600) { + if ((LADDER_ENABLED) && Item.getMercEquipped(sdk.body.RightArm).tier < 3600) { includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/MercInsight.js"); } diff --git a/libs/SoloPlay/Config/Barbarian.js b/libs/SoloPlay/Config/Barbarian.js index 2634e14d..10eb05fb 100644 --- a/libs/SoloPlay/Config/Barbarian.js +++ b/libs/SoloPlay/Config/Barbarian.js @@ -20,6 +20,8 @@ includeIfNotIncluded("SoloPlay/Functions/MiscOverrides.js"); includeIfNotIncluded("SoloPlay/Functions/Globals.js"); + const LADDER_ENABLED = (me.ladder || Developer.addLadderRW); + SetUp.include(); SetUp.config(); @@ -58,7 +60,9 @@ /* Attack configuration. */ Config.AttackSkill = [-1, 0, 0, 0, 0]; - Config.LowManaSkill = me.getSkill(sdk.skills.DoubleSwing, sdk.skills.subindex.SoftPoints) >= 9 ? [sdk.skills.DoubleSwing, 0] : [0, -1]; + Config.LowManaSkill = me.getSkill(sdk.skills.DoubleSwing, sdk.skills.subindex.SoftPoints) >= 9 + ? [sdk.skills.DoubleSwing, 0] + : [0, -1]; Config.MaxAttackCount = 1000; Config.BossPriority = me.normal; Config.ClearType = 0; @@ -68,14 +72,53 @@ Config.FindItem = true; // Use Find Item skill on corpses after clearing. Config.FindItemSwitch = false; // Switch to non-primary slot when using Find Item skills - Config.imbueables = [ - { name: sdk.items.AvengerGuard, condition: () => (me.normal && me.expansion) }, - { name: sdk.items.SlayerGuard, condition: () => (!me.normal && me.trueStr >= 118 && me.expansion) }, - { name: sdk.items.CarnageHelm, condition: () => (me.equipped.get(sdk.body.Head).tier < 100000 && me.trueStr >= 106 && me.expansion) }, - { name: sdk.items.Belt, condition: () => (me.normal && (me.equipped.get(sdk.body.Head).tier > 100000 || me.classic)) }, - { name: sdk.items.MeshBelt, condition: () => (!me.normal && me.charlvl < 46 && me.trueStr > 58 && (me.equipped.get(sdk.body.RightArm).tier > 100000 || me.classic)) }, - { name: sdk.items.SpiderwebSash, condition: () => (!me.normal && me.trueStr > 50 && (me.equipped.get(sdk.body.RightArm).tier > 100000 || me.classic)) }, - ].filter((item) => item.condition()); + Config.imbueables = (function () { + /** + * @param {number} name + * @param {function(): boolean} condition + */ + const _imbueObj = (name, condition) => ({ name: name, condition: condition }); + + return [ + _imbueObj( + sdk.items.AvengerGuard, + function () { + return me.normal && me.expansion; + } + ), + _imbueObj( + sdk.items.SlayerGuard, + function () { + return !me.normal && me.trueStr >= 118 && me.expansion; + } + ), + _imbueObj( + sdk.items.CarnageHelm, + function () { + return me.equipped.get(sdk.body.Head).tier < 100000 && me.trueStr >= 106 && me.expansion; + } + ), + _imbueObj( + sdk.items.Belt, + function () { + return me.normal && (me.equipped.get(sdk.body.Head).tier > 100000 || me.classic); + } + ), + _imbueObj( + sdk.items.MeshBelt, + function () { + return !me.normal && me.charlvl < 46 && me.trueStr > 58 + && (me.equipped.get(sdk.body.RightArm).tier > 100000 || me.classic); + } + ), + _imbueObj( + sdk.items.SpiderwebSash, + function () { + return !me.normal && me.trueStr > 50 && (me.equipped.get(sdk.body.RightArm).tier > 100000 || me.classic); + } + ), + ].filter((item) => item.condition()); + })(); let imbueArr = SetUp.imbueItems(); @@ -87,34 +130,66 @@ case sdk.game.gametype.Expansion: NTIP.addLine("[name] >= VexRune && [name] <= ZodRune"); const { basicSocketables, addSocketableObj } = require("../Utils/General"); + + /** @param {ItemUnit} item */ + const honorCheck = function (item) { + /** @type {GetOwnedSettings} */ + const wanted = { + itemType: sdk.items.type.Sword, + mode: sdk.items.mode.inStorage, + sockets: 5, + /** @param {ItemUnit} item */ + cb: function (item) { + return item.isBaseType; + } + }; + return (item.ilvl >= 41 && item.isBaseType && !item.ethereal + && !me.getOwned(wanted).length + && !me.checkItem({ name: sdk.locale.items.Honor }).have); + }; Config.socketables = Config.socketables.concat(basicSocketables.all); Config.socketables.push(addSocketableObj(sdk.items.Flamberge, [], [], - true, (item) => me.normal && me.equipped.get(sdk.body.LeftArm).tier < 600 && !Check.haveBase("sword", 5) && !me.checkItem({ name: sdk.locale.items.Honor }).have && item.ilvl >= 41 && item.isBaseType && !item.ethereal + true, + /** @param {ItemUnit} item */ + function (item) { + return me.normal && me.equipped.get(sdk.body.LeftArm).tier < 600 && honorCheck(item); + } )); Config.socketables.push(addSocketableObj(sdk.items.Zweihander, [], [], - true, (item) => me.equipped.get(sdk.body.LeftArm).tier < 1000 && !Check.haveBase("sword", 5) && !me.checkItem({ name: sdk.locale.items.Honor }).have && item.ilvl >= 41 && item.isBaseType && !item.ethereal + true, + /** @param {ItemUnit} item */ + function (item) { + return me.equipped.get(sdk.body.LeftArm).tier < 1000 && honorCheck(item); + } )); if (SetUp.finalBuild !== "Immortalwhirl") { - Config.socketables.push(addSocketableObj(sdk.items.SlayerGuard, [sdk.items.runes.Cham], [sdk.items.gems.Perfect.Ruby], + Config.socketables.push(addSocketableObj(sdk.items.SlayerGuard, + [sdk.items.runes.Cham], [sdk.items.gems.Perfect.Ruby], true, (item) => item.unique && !item.ethereal )); } if (["Immortalwhirl", "Singer"].indexOf(SetUp.finalBuild) === -1) { // Grief - if ((me.ladder || Developer.addLadderRW) && (!me.checkItem({ name: sdk.locale.items.Grief }).have || (SetUp.finalBuild === "Whirlwind" && me.equipped.get(sdk.body.LeftArm).prefixnum !== sdk.locale.items.Grief))) { + if (LADDER_ENABLED + && ( + !me.checkItem({ name: sdk.locale.items.Grief }).have + || (SetUp.finalBuild === "Whirlwind" && me.equipped.get(sdk.body.LeftArm).prefixnum !== sdk.locale.items.Grief) + )) { includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/Grief.js"); } // Fortitude - if ((me.ladder || Developer.addLadderRW) && SetUp.finalBuild !== "Uberconc" && me.checkItem({ name: sdk.locale.items.Grief }).have && !me.checkItem({ name: sdk.locale.items.Fortitude, itemtype: sdk.items.type.Armor }).have) { + if (LADDER_ENABLED && SetUp.finalBuild !== "Uberconc" + && me.checkItem({ name: sdk.locale.items.Grief }).have + && !me.checkItem({ name: sdk.locale.items.Fortitude, itemtype: sdk.items.type.Armor }).have) { includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/Fortitude.js"); } // Doom - if ((me.ladder || Developer.addLadderRW) && Item.getMercEquipped(sdk.body.RightArm).prefixnum !== 20532) { + if (LADDER_ENABLED && Item.getMercEquipped(sdk.body.RightArm).prefixnum !== 20532) { includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/MercDoom.js"); } } @@ -142,7 +217,8 @@ break; case "Singer": // Heart of the Oak - if (me.equipped.get(sdk.body.LeftArm).prefixnum !== sdk.locale.items.HeartoftheOak && me.checkItem({ name: sdk.locale.items.Enigma }).have) { + if (me.equipped.get(sdk.body.LeftArm).prefixnum !== sdk.locale.items.HeartoftheOak + && me.checkItem({ name: sdk.locale.items.Enigma }).have) { includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/HeartOfTheOak.js"); } @@ -154,17 +230,20 @@ break; case "Immortalwhirl": // Infinity - if ((me.ladder || Developer.addLadderRW) && Item.getMercEquipped(sdk.body.RightArm).prefixnum !== sdk.locale.items.Infinity) { + if (LADDER_ENABLED && Item.getMercEquipped(sdk.body.RightArm).prefixnum !== sdk.locale.items.Infinity) { includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/MercInfinity.js"); } - Config.socketables.push(addSocketableObj(sdk.items.AvengerGuard, [sdk.items.runes.Ber], [sdk.items.gems.Perfect.Ruby], + Config.socketables.push(addSocketableObj(sdk.items.AvengerGuard, + [sdk.items.runes.Ber], [sdk.items.gems.Perfect.Ruby], false, (item) => item.set && !item.ethereal )); - Config.socketables.push(addSocketableObj(sdk.items.OgreMaul, [sdk.items.runes.Shael], [], + Config.socketables.push(addSocketableObj(sdk.items.OgreMaul, + [sdk.items.runes.Shael], [], false, (item) => item.set && !item.ethereal )); - Config.socketables.push(addSocketableObj(sdk.items.SacredArmor, [sdk.items.runes.Ber], [sdk.items.gems.Perfect.Ruby], + Config.socketables.push(addSocketableObj(sdk.items.SacredArmor, + [sdk.items.runes.Ber], [sdk.items.gems.Perfect.Ruby], true, (item) => item.set && !item.ethereal )); @@ -179,11 +258,15 @@ /* Crafting */ if (me.equipped.get(sdk.body.Neck).tier < 100000) { - Check.currentBuild().caster ? Config.Recipes.push([Recipe.Caster.Amulet]) : Config.Recipes.push([Recipe.Blood.Amulet]); + Check.currentBuild().caster + ? Config.Recipes.push([Recipe.Caster.Amulet]) + : Config.Recipes.push([Recipe.Blood.Amulet]); } if (me.equipped.get(sdk.body.RingLeft).tier < 100000) { - Check.currentBuild().caster ? Config.Recipes.push([Recipe.Caster.Ring]) : Config.Recipes.push([Recipe.Blood.Ring]); + Check.currentBuild().caster + ? Config.Recipes.push([Recipe.Caster.Ring]) + : Config.Recipes.push([Recipe.Blood.Ring]); } if (me.equipped.get(sdk.body.LeftArm).tier < 1370) { @@ -260,7 +343,7 @@ } // Merc Insight - if ((me.ladder || Developer.addLadderRW) && Item.getMercEquipped(sdk.body.RightArm).tier < 3600) { + if (LADDER_ENABLED && Item.getMercEquipped(sdk.body.RightArm).tier < 3600) { includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/MercInsight.js"); } @@ -270,7 +353,7 @@ } // Merc Fortitude - if ((me.ladder || Developer.addLadderRW) && Item.getMercEquipped(sdk.body.Armor).prefixnum !== sdk.locale.items.Fortitude) { + if (LADDER_ENABLED && Item.getMercEquipped(sdk.body.Armor).prefixnum !== sdk.locale.items.Fortitude) { includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/MercFortitude.js"); } @@ -290,7 +373,11 @@ } // Duress - if (me.equipped.get(sdk.body.Armor).tier < 600 && (me.checkItem({ name: sdk.locale.items.CrescentMoon }).have || me.equipped.get(sdk.body.LeftArm).tier > 900)) { + if (me.equipped.get(sdk.body.Armor).tier < 600 + && ( + me.checkItem({ name: sdk.locale.items.CrescentMoon }).have + || me.equipped.get(sdk.body.LeftArm).tier > 900 + )) { includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/Duress.js"); } @@ -310,7 +397,7 @@ } // Spirit Sword - if ((me.ladder || Developer.addLadderRW) && me.equipped.get(sdk.body.LeftArmSecondary).prefixnum !== sdk.locale.items.Spirit) { + if (LADDER_ENABLED && me.equipped.get(sdk.body.LeftArmSecondary).prefixnum !== sdk.locale.items.Spirit) { includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/SpiritSword.js"); } diff --git a/libs/SoloPlay/Config/Druid.js b/libs/SoloPlay/Config/Druid.js index 7c7a1d97..7461c537 100644 --- a/libs/SoloPlay/Config/Druid.js +++ b/libs/SoloPlay/Config/Druid.js @@ -20,6 +20,8 @@ includeIfNotIncluded("SoloPlay/Functions/MiscOverrides.js"); includeIfNotIncluded("SoloPlay/Functions/Globals.js"); + const LADDER_ENABLED = (me.ladder || Developer.addLadderRW); + SetUp.include(); SetUp.config(); @@ -83,14 +85,53 @@ Config.SummonSpirit = 0; // 0 = disabled, 1 / "Oak Sage", 2 / "Heart of Wolverine", 3 / "Spirit of Barbs" Config.SummonAnimal = 0; // 0 = disabled, 1 or "Spirit Wolf" = summon spirit wolf, 2 or "Dire Wolf" = summon dire wolf, 3 or "Grizzly" = summon grizzly - Config.imbueables = [ - { name: sdk.items.SpiritMask, condition: () => (me.normal) }, - { name: sdk.items.TotemicMask, condition: () => (!me.normal && me.equipped.get(sdk.body.Head).tier < 100000 && (me.charlvl < 66 || me.trueStr < 118)) }, - { name: sdk.items.DreamSpirit, condition: () => (me.equipped.get(sdk.body.Head).tier < 100000 && me.trueStr >= 118) }, - { name: sdk.items.Belt, condition: () => (me.normal && (me.equipped.get(sdk.body.Head).tier > 100000)) }, - { name: sdk.items.MeshBelt, condition: () => (!me.normal && me.charlvl < 46 && me.trueStr > 58 && (me.equipped.get(sdk.body.Head).tier > 100000)) }, - { name: sdk.items.SpiderwebSash, condition: () => (!me.normal && me.trueStr > 50 && (me.equipped.get(sdk.body.Head).tier > 100000)) }, - ].filter((item) => item.condition()); + Config.imbueables = (function () { + /** + * @param {number} name + * @param {function(): boolean} condition + */ + const _imbueObj = (name, condition) => ({ name: name, condition: condition }); + + return [ + _imbueObj( + sdk.items.SpiritMask, + function () { + return me.normal; + } + ), + _imbueObj( + sdk.items.TotemicMask, + function () { + return !me.normal && me.equipped.get(sdk.body.Head).tier < 100000 && (me.charlvl < 66 || me.trueStr < 118); + } + ), + _imbueObj( + sdk.items.DreamSpirit, + function () { + return me.equipped.get(sdk.body.Head).tier < 100000 && me.trueStr >= 118; + } + ), + _imbueObj( + sdk.items.Belt, + function () { + return me.normal && me.equipped.get(sdk.body.Head).tier > 100000; + } + ), + _imbueObj( + sdk.items.MeshBelt, + function () { + return !me.normal && me.charlvl < 46 && me.trueStr > 58 + && (me.equipped.get(sdk.body.Head).tier > 100000); + } + ), + _imbueObj( + sdk.items.SpiderwebSash, + function () { + return !me.normal && me.trueStr > 50 && (me.equipped.get(sdk.body.Head).tier > 100000); + } + ), + ].filter((item) => item.condition()); + })(); let imbueArr = SetUp.imbueItems(); @@ -100,7 +141,21 @@ Config.socketables = Config.socketables.concat(basicSocketables.caster, basicSocketables.all); Config.socketables.push(addSocketableObj(sdk.items.Monarch, [], [], - !me.hell, (item) => !Check.haveBase("monarch", 4) && item.ilvl >= 41 && item.isBaseType && !item.ethereal + !me.hell, + /** @param {ItemUnit} item */ + function (item) { + /** @type {GetOwnedSettings} */ + const wanted = { + classid: sdk.items.Monarch, + mode: sdk.items.mode.inStorage, + sockets: 4, + /** @param {ItemUnit} item */ + cb: function (item) { + return item.isBaseType; + } + }; + return !me.getOwned(wanted).length && item.ilvl >= 41 && item.isBaseType && !item.ethereal; + } )); Config.socketables.push(addSocketableObj(sdk.items.TotemicMask, [sdk.items.runes.Um], [sdk.items.gems.Perfect.Ruby], true, (item) => item.unique && !item.ethereal @@ -116,11 +171,15 @@ /* Crafting */ if (me.equipped.get(sdk.body.Neck).tier < 100000) { - Check.currentBuild().caster ? Config.Recipes.push([Recipe.Caster.Amulet]) : Config.Recipes.push([Recipe.Blood.Amulet]); + Check.currentBuild().caster + ? Config.Recipes.push([Recipe.Caster.Amulet]) + : Config.Recipes.push([Recipe.Blood.Amulet]); } if (me.equipped.get(sdk.body.RingLeft).tier < 100000) { - Check.currentBuild().caster ? Config.Recipes.push([Recipe.Caster.Ring]) : Config.Recipes.push([Recipe.Blood.Ring]); + Check.currentBuild().caster + ? Config.Recipes.push([Recipe.Caster.Ring]) + : Config.Recipes.push([Recipe.Blood.Ring]); } // FinalBuild specific setup @@ -133,12 +192,13 @@ } if (SetUp.finalBuild === "Elemental") { - Config.socketables.push(addSocketableObj(sdk.items.SkySpirit, [sdk.items.runes.Um], [sdk.items.gems.Perfect.Ruby], + Config.socketables.push(addSocketableObj(sdk.items.SkySpirit, + [sdk.items.runes.Um], [sdk.items.gems.Perfect.Ruby], true, (item) => item.unique && item.getStat(sdk.stats.PassiveFirePierce) === 20 && !item.ethereal )); // Phoenix Shield - if ((me.ladder || Developer.addLadderRW) && SetUp.finalBuild === "Elemental" && me.checkItem({ name: sdk.locale.items.Enigma }).have && !me.checkItem({ name: sdk.locale.items.Phoenix, itemtype: sdk.items.type.Shield }).have) { + if (LADDER_ENABLED && SetUp.finalBuild === "Elemental" && me.checkItem({ name: sdk.locale.items.Enigma }).have && !me.checkItem({ name: sdk.locale.items.Phoenix, itemtype: sdk.items.type.Shield }).have) { includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/PhoneixShield.js"); } } @@ -198,17 +258,21 @@ Check.itemSockables(sdk.items.TotemicMask, "unique", "Jalal's Mane"); // Spirit Sword - if ((me.ladder || Developer.addLadderRW) && me.equipped.get(sdk.body.RightArm).tier < 777) { + if (LADDER_ENABLED && me.equipped.get(sdk.body.RightArm).tier < 777) { includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/SpiritSword.js"); } // Spirit Shield - if ((me.ladder || Developer.addLadderRW) && (me.equipped.get(sdk.body.LeftArm).tier < 1000 || me.equipped.get(sdk.body.LeftArmSecondary).prefixnum !== sdk.locale.items.Spirit)) { + if (LADDER_ENABLED + && ( + me.equipped.get(sdk.body.LeftArm).tier < 1000 + || me.equipped.get(sdk.body.LeftArmSecondary).prefixnum !== sdk.locale.items.Spirit + )) { includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/SpiritShield.js"); } // Merc Insight - if ((me.ladder || Developer.addLadderRW) && Item.getMercEquipped(sdk.body.RightArm).tier < 3600) { + if (LADDER_ENABLED && Item.getMercEquipped(sdk.body.RightArm).tier < 3600) { includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/MercInsight.js"); } diff --git a/libs/SoloPlay/Config/Necromancer.js b/libs/SoloPlay/Config/Necromancer.js index 837e581e..d1854e07 100644 --- a/libs/SoloPlay/Config/Necromancer.js +++ b/libs/SoloPlay/Config/Necromancer.js @@ -147,7 +147,21 @@ Config.socketables = Config.socketables.concat(basicSocketables.caster, basicSocketables.all); Config.socketables.push(addSocketableObj(sdk.items.Monarch, [], [], - !me.hell, (item) => !Check.haveBase("monarch", 4) && item.ilvl >= 41 && item.isBaseType && !item.ethereal + !me.hell, + /** @param {ItemUnit} item */ + function (item) { + /** @type {GetOwnedSettings} */ + const wanted = { + classid: sdk.items.Monarch, + mode: sdk.items.mode.inStorage, + sockets: 4, + /** @param {ItemUnit} item */ + cb: function (item) { + return item.isBaseType; + } + }; + return !me.getOwned(wanted).length && item.ilvl >= 41 && item.isBaseType && !item.ethereal; + } )); Config.socketables.push(addSocketableObj(sdk.items.Shako, [sdk.items.runes.Um], [sdk.items.gems.Perfect.Ruby], true, (item) => item.unique && !item.ethereal diff --git a/libs/SoloPlay/Config/Paladin.js b/libs/SoloPlay/Config/Paladin.js index 2d3d98e7..1b30d391 100644 --- a/libs/SoloPlay/Config/Paladin.js +++ b/libs/SoloPlay/Config/Paladin.js @@ -25,6 +25,8 @@ includeIfNotIncluded("SoloPlay/Functions/MiscOverrides.js"); includeIfNotIncluded("SoloPlay/Functions/Globals.js"); + const LADDER_ENABLED = (me.ladder || Developer.addLadderRW); + SetUp.include(); SetUp.config(); @@ -88,14 +90,53 @@ Config.Redemption = [45, 25]; // Maybe add auric shield? - Config.imbueables = [ - { name: sdk.items.WarScepter, condition: () => me.normal }, - { name: sdk.items.DivineScepter, condition: () => (!me.normal && (me.trueStr < 125 || me.trueDex < 60)) }, - { name: sdk.items.MightyScepter, condition: () => (me.equipped.get(sdk.body.RightArm).tier < 777 && (me.trueStr >= 125 || me.trueDex >= 60)) }, - { name: sdk.items.Belt, condition: () => (me.normal && (me.equipped.get(sdk.body.RightArm).tier > 777 || me.classic)) }, - { name: sdk.items.MeshBelt, condition: () => (!me.normal && me.charlvl < 46 && me.trueStr > 58 && (me.equipped.get(sdk.body.RightArm).tier > 777 || me.classic)) }, - { name: sdk.items.SpiderwebSash, condition: () => (!me.normal && me.trueStr > 50 && (me.equipped.get(sdk.body.RightArm).tier > 777 || me.classic)) }, - ]; + Config.imbueables = (function () { + /** + * @param {number} name + * @param {function(): boolean} condition + */ + const _imbueObj = (name, condition) => ({ name: name, condition: condition }); + + return [ + _imbueObj( + sdk.items.WarScepter, + function () { + return me.normal; + } + ), + _imbueObj( + sdk.items.DivineScepter, + function () { + return !me.normal && (me.trueStr < 125 || me.trueDex < 60); + } + ), + _imbueObj( + sdk.items.MightyScepter, + function () { + return me.equipped.get(sdk.body.RightArm).tier < 777 && (me.trueStr >= 125 || me.trueDex >= 60); + } + ), + _imbueObj( + sdk.items.Belt, + function () { + return me.normal && (me.equipped.get(sdk.body.RightArm).tier > 777 || me.classic); + } + ), + _imbueObj( + sdk.items.MeshBelt, + function () { + return !me.normal && me.charlvl < 46 && me.trueStr > 58 + && (me.equipped.get(sdk.body.RightArm).tier > 777 || me.classic); + } + ), + _imbueObj( + sdk.items.SpiderwebSash, + function () { + return !me.normal && me.trueStr > 50 && (me.equipped.get(sdk.body.RightArm).tier > 777 || me.classic); + } + ), + ].filter((item) => item.condition()); + })(); let imbueArr = SetUp.imbueItems(); @@ -120,11 +161,15 @@ /* Crafting */ if (me.equipped.get(sdk.body.Neck).tier < 100000) { - Check.currentBuild().caster ? Config.Recipes.push([Recipe.Caster.Amulet]) : Config.Recipes.push([Recipe.Blood.Amulet]); + Check.currentBuild().caster + ? Config.Recipes.push([Recipe.Caster.Amulet]) + : Config.Recipes.push([Recipe.Blood.Amulet]); } if (me.equipped.get(sdk.body.RingLeft).tier < 100000) { - Check.currentBuild().caster ? Config.Recipes.push([Recipe.Caster.Ring]) : Config.Recipes.push([Recipe.Blood.Ring]); + Check.currentBuild().caster + ? Config.Recipes.push([Recipe.Caster.Ring]) + : Config.Recipes.push([Recipe.Blood.Ring]); } if (me.equipped.get(sdk.body.Gloves).tier < 110000) { @@ -143,15 +188,17 @@ case "Smiter": case "Zealer": // Grief - if ((me.ladder || Developer.addLadderRW) && !me.checkItem({ name: sdk.locale.items.Grief }).have) { + if (LADDER_ENABLED && !me.checkItem({ name: sdk.locale.items.Grief }).have) { includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/Grief.js"); } if (SetUp.finalBuild === "Zealer") { - Config.socketables.push(addSocketableObj(sdk.items.GrimHelm, [sdk.items.runes.Ber], [sdk.items.gems.Perfect.Ruby], + Config.socketables.push(addSocketableObj(sdk.items.GrimHelm, + [sdk.items.runes.Ber], [sdk.items.gems.Perfect.Ruby], true, (item) => item.unique && item.getStat(sdk.stats.DamageResist) === 20 && !item.ethereal )); - Config.socketables.push(addSocketableObj(sdk.items.BoneVisage, [sdk.items.runes.Ber], [sdk.items.gems.Perfect.Ruby], + Config.socketables.push(addSocketableObj(sdk.items.BoneVisage, + [sdk.items.runes.Ber], [sdk.items.gems.Perfect.Ruby], true, (item) => item.unique && item.getStat(sdk.stats.DamageResist) === 20 && !item.ethereal && item.fname.toLowerCase().includes("vampire gaze") )); @@ -169,7 +216,8 @@ } // Fortitude - if ((me.ladder || Developer.addLadderRW) && !me.checkItem({ name: sdk.locale.items.Fortitude, itemtype: sdk.items.type.Armor }).have) { + if (LADDER_ENABLED + && !me.checkItem({ name: sdk.locale.items.Fortitude, itemtype: sdk.items.type.Armor }).have) { includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/Fortitude.js"); } } @@ -182,7 +230,7 @@ } // Spirit Shield - if ((me.ladder || Developer.addLadderRW) && me.equipped.get(sdk.body.LeftArm).tier < 110000) { + if (LADDER_ENABLED && me.equipped.get(sdk.body.LeftArm).tier < 110000) { includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/SpiritShield.js"); } @@ -194,16 +242,18 @@ ]); // Dream Shield - if ((me.ladder || Developer.addLadderRW) && !me.checkItem({ name: sdk.locale.items.Dream, itemtype: sdk.items.type.AuricShields }).have) { + if (LADDER_ENABLED + && !me.checkItem({ name: sdk.locale.items.Dream, itemtype: sdk.items.type.AuricShields }).have) { includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/DreamShield.js"); } // Dream Helm - if ((me.ladder || Developer.addLadderRW) && !me.checkItem({ name: sdk.locale.items.Dream, itemtype: sdk.items.type.Helm }).have) { + if (LADDER_ENABLED + && !me.checkItem({ name: sdk.locale.items.Dream, itemtype: sdk.items.type.Helm }).have) { includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/DreamHelm.js"); } - if ((me.ladder || Developer.addLadderRW) && !dreamerCheck) { + if (LADDER_ENABLED && !dreamerCheck) { // Cube to Jah rune if (!me.getItem(sdk.items.runes.Jah)) { if (me.checkItem({ name: sdk.locale.items.CalltoArms }).have) { @@ -235,7 +285,9 @@ } // Dragon Armor - if ((me.ladder || Developer.addLadderRW) && !me.checkItem({ name: sdk.locale.items.Dragon, itemtype: sdk.items.type.Armor }).have && dreamerCheck) { + if (LADDER_ENABLED + && !me.checkItem({ name: sdk.locale.items.Dragon, itemtype: sdk.items.type.Armor }).have + && dreamerCheck) { includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/DragonArmor.js"); } @@ -246,38 +298,53 @@ NTIP.addLine("[name] == phaseblade && [flag] != ethereal && [quality] == unique # [enhanceddamage] >= 230 && [sanctuaryaura] >= 10 # [tier] == 115000"); } - if (!me.haveAll([{ name: sdk.locale.items.CrescentMoon }, { name: sdk.locale.items.HandofJustice }]) && dreamerCheck) { + if (!me.haveAll([ + { name: sdk.locale.items.CrescentMoon }, + { name: sdk.locale.items.HandofJustice } + ]) && dreamerCheck) { includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/CrescentMoon.js"); // Lightsabre NTIP.addLine("[name] == phaseblade && [flag] != ethereal && [quality] == unique # [enhanceddamage] >= 150 && [itemabsorblightpercent] == 25 # [tier] == 105000"); } - if (!me.checkItem({ name: sdk.locale.items.VoiceofReason }).have && !me.haveSome([{ name: sdk.locale.items.CrescentMoon }, { name: sdk.locale.items.HandofJustice }]) && dreamerCheck) { + if (!me.checkItem({ name: sdk.locale.items.VoiceofReason }).have + && !me.haveSome([ + { name: sdk.locale.items.CrescentMoon }, + { name: sdk.locale.items.HandofJustice } + ]) && dreamerCheck) { includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/VoiceOfReason.js"); } - if ((me.ladder || Developer.addLadderRW) && Item.getMercEquipped(sdk.body.Armor).prefixnum !== sdk.locale.items.Fortitude - && me.haveAll([{ name: sdk.locale.items.HandofJustice }, { name: sdk.locale.items.Dragon, itemtype: sdk.items.type.Armor }, - { name: sdk.locale.items.Dream, itemtype: sdk.items.type.AuricShields }, { name: sdk.locale.items.Dream, itemtype: sdk.items.type.Helm }])) { + if (LADDER_ENABLED && Item.getMercEquipped(sdk.body.Armor).prefixnum !== sdk.locale.items.Fortitude + && me.haveAll([ + { name: sdk.locale.items.HandofJustice }, + { name: sdk.locale.items.Dragon, itemtype: sdk.items.type.Armor }, + { name: sdk.locale.items.Dream, itemtype: sdk.items.type.AuricShields }, + { name: sdk.locale.items.Dream, itemtype: sdk.items.type.Helm } + ])) { includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/MercFortitude.js"); } break; case "Sancdreamer": - dreamerCheck = me.haveAll([{ name: sdk.locale.items.Dream, itemtype: sdk.items.type.AuricShields }, { name: sdk.locale.items.Dream, itemtype: sdk.items.type.Helm }]); + dreamerCheck = me.haveAll([ + { name: sdk.locale.items.Dream, itemtype: sdk.items.type.AuricShields }, + { name: sdk.locale.items.Dream, itemtype: sdk.items.type.Helm } + ]); // Dream Shield - if ((me.ladder || Developer.addLadderRW) && !me.checkItem({ name: sdk.locale.items.Dream, itemtype: sdk.items.type.AuricShields }).have) { + if (LADDER_ENABLED + && !me.checkItem({ name: sdk.locale.items.Dream, itemtype: sdk.items.type.AuricShields }).have) { includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/DreamShield.js"); } // Dream Helm - if ((me.ladder || Developer.addLadderRW) && !me.checkItem({ name: sdk.locale.items.Dream, itemtype: sdk.items.type.Helm }).have) { + if (LADDER_ENABLED && !me.checkItem({ name: sdk.locale.items.Dream, itemtype: sdk.items.type.Helm }).have) { includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/DreamHelm.js"); } - if ((me.ladder || Developer.addLadderRW) && !dreamerCheck) { + if (LADDER_ENABLED && !dreamerCheck) { // Cube to Jah rune if (!me.getItem(sdk.items.runes.Jah)) { if (me.checkItem({ name: sdk.locale.items.CalltoArms }).have) { @@ -323,14 +390,22 @@ NTIP.addLine("[name] == phaseblade && [flag] != ethereal && [quality] == unique # [enhanceddamage] >= 150 && [itemabsorblightpercent] == 25 # [tier] == 105000"); } - if (!me.checkItem({ name: sdk.locale.items.VoiceofReason }).have && !me.haveSome([{ name: sdk.locale.items.CrescentMoon }, { name: sdk.locale.items.LastWish }]) && dreamerCheck) { + if (!me.checkItem({ name: sdk.locale.items.VoiceofReason }).have + && !me.haveSome([ + { name: sdk.locale.items.CrescentMoon }, + { name: sdk.locale.items.LastWish } + ]) && dreamerCheck) { includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/VoiceOfReason.js"); } - if ((me.ladder || Developer.addLadderRW) && me.haveAll([{ name: sdk.locale.items.LastWish }, { name: sdk.locale.items.ChainsofHonor }, - { name: sdk.locale.items.Dream, itemtype: sdk.items.type.AuricShields }, { name: sdk.locale.items.Dream, itemtype: sdk.items.type.Helm }])) { + if (LADDER_ENABLED && me.haveAll([ + { name: sdk.locale.items.LastWish }, + { name: sdk.locale.items.ChainsofHonor }, + { name: sdk.locale.items.Dream, itemtype: sdk.items.type.AuricShields }, + { name: sdk.locale.items.Dream, itemtype: sdk.items.type.Helm } + ])) { // Infinity - if ((me.ladder || Developer.addLadderRW) && Item.getMercEquipped(sdk.body.RightArm).prefixnum !== sdk.locale.items.Infinity) { + if (LADDER_ENABLED && Item.getMercEquipped(sdk.body.RightArm).prefixnum !== sdk.locale.items.Infinity) { if (!isIncluded("SoloPlay/BuildFiles/Runewords/MercInfinity.js")) { include("SoloPlay/BuildFiles/Runewords/MercInfinity.js"); } @@ -346,7 +421,7 @@ break; case "Torchadin": // Dragon Armor - if ((me.ladder || Developer.addLadderRW) && !me.checkItem({ name: sdk.locale.items.Dragon, itemtype: sdk.items.type.Armor }).have) { + if (LADDER_ENABLED && !me.checkItem({ name: sdk.locale.items.Dragon, itemtype: sdk.items.type.Armor }).have) { includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/DragonArmor.js"); } @@ -358,12 +433,16 @@ } // Exile - if ((me.ladder || Developer.addLadderRW) && !me.checkItem({ name: sdk.locale.items.Exile, itemtype: sdk.items.type.AuricShields }).have) { + if (LADDER_ENABLED + && !me.checkItem({ name: sdk.locale.items.Exile, itemtype: sdk.items.type.AuricShields }).have) { includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/Exile.js"); } - if ((me.ladder || Developer.addLadderRW) && !me.haveAll([{ name: sdk.locale.items.HandofJustice }, - { name: sdk.locale.items.Exile, itemtype: sdk.items.type.AuricShields }, { name: sdk.locale.items.Dragon, itemtype: sdk.items.type.Armor }])) { + if (LADDER_ENABLED && !me.haveAll([ + { name: sdk.locale.items.HandofJustice }, + { name: sdk.locale.items.Exile, itemtype: sdk.items.type.AuricShields }, + { name: sdk.locale.items.Dragon, itemtype: sdk.items.type.Armor } + ])) { // Cube to Cham rune if (!me.getItem(sdk.items.runes.Cham) || !me.getItem(sdk.items.runes.Sur) || !me.getItem(sdk.items.runes.Lo)) { if (me.checkItem({ name: sdk.locale.items.CalltoArms }).have) { @@ -374,7 +453,8 @@ if (me.checkItem({ name: sdk.locale.items.Exile, itemtype: sdk.items.type.AuricShields }).have) { Config.Recipes.push([Recipe.Rune, "Vex Rune"]); Config.Recipes.push([Recipe.Rune, "Ohm Rune"]); - } else if (!me.checkItem({ name: sdk.locale.items.Exile, itemtype: sdk.items.type.AuricShields }).have && !me.getItem(sdk.items.runes.Ohm)) { + } else if (!me.checkItem({ name: sdk.locale.items.Exile, itemtype: sdk.items.type.AuricShields }).have + && !me.getItem(sdk.items.runes.Ohm)) { Config.Recipes.push([Recipe.Rune, "Vex Rune"]); } } @@ -382,7 +462,10 @@ if (me.checkItem({ name: sdk.locale.items.Dragon, itemtype: sdk.items.type.Armor }).have) { Config.Recipes.push([Recipe.Rune, "Lo Rune"]); Config.Recipes.push([Recipe.Rune, "Sur Rune"]); - } else if ((!me.haveAll([{ name: sdk.locale.items.Dragon, itemtype: sdk.items.type.Armor }, { name: sdk.locale.items.HandofJustice }]) && !me.getItem(sdk.items.runes.Sur))) { + } else if ((!me.haveAll([ + { name: sdk.locale.items.Dragon, itemtype: sdk.items.type.Armor }, + { name: sdk.locale.items.HandofJustice } + ]) && !me.getItem(sdk.items.runes.Sur))) { Config.Recipes.push([Recipe.Rune, "Lo Rune"]); } @@ -411,12 +494,20 @@ NTIP.addLine("[name] == phaseblade && [flag] != ethereal && [quality] == unique # [enhanceddamage] >= 150 && [itemabsorblightpercent] == 25 # [tier] == 105000"); } - if (!me.checkItem({ name: sdk.locale.items.VoiceofReason }).have && !me.haveSome([{ name: sdk.locale.items.CrescentMoon }, { name: sdk.locale.items.HandofJustice }])) { + if (!me.checkItem({ name: sdk.locale.items.VoiceofReason }).have + && !me.haveSome([ + { name: sdk.locale.items.CrescentMoon }, + { name: sdk.locale.items.HandofJustice } + ])) { includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/VoiceOfReason.js"); } - if ((me.ladder || Developer.addLadderRW) && Item.getMercEquipped(sdk.body.Armor).prefixnum !== sdk.locale.items.Fortitude - && me.haveAll([{ name: sdk.locale.items.HandofJustice }, { name: sdk.locale.items.Dragon, itemtype: sdk.items.type.Armor }, { name: sdk.locale.items.Exile, itemtype: sdk.items.type.AuricShields }])) { + if (LADDER_ENABLED && Item.getMercEquipped(sdk.body.Armor).prefixnum !== sdk.locale.items.Fortitude + && me.haveAll([ + { name: sdk.locale.items.HandofJustice }, + { name: sdk.locale.items.Dragon, itemtype: sdk.items.type.Armor }, + { name: sdk.locale.items.Exile, itemtype: sdk.items.type.AuricShields } + ])) { includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/MercFortitude.js"); } break; @@ -438,17 +529,21 @@ } // Spirit Sword - if ((me.ladder || Developer.addLadderRW) && me.equipped.get(sdk.body.RightArm).tier < 777) { + if (LADDER_ENABLED && me.equipped.get(sdk.body.RightArm).tier < 777) { includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/SpiritSword.js"); } // Spirit Shield - if ((me.ladder || Developer.addLadderRW) && (me.equipped.get(sdk.body.LeftArm).tier < 1000 || me.equipped.get(sdk.body.LeftArmSecondary).prefixnum !== sdk.locale.items.Spirit)) { + if (LADDER_ENABLED + && ( + me.equipped.get(sdk.body.LeftArm).tier < 1000 + || me.equipped.get(sdk.body.LeftArmSecondary).prefixnum !== sdk.locale.items.Spirit + )) { includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/SpiritShield.js"); } // Merc Insight - if ((me.ladder || Developer.addLadderRW) && Item.getMercEquipped(sdk.body.RightArm).tier < 3600) { + if (LADDER_ENABLED && Item.getMercEquipped(sdk.body.RightArm).tier < 3600) { includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/MercInsight.js"); } diff --git a/libs/SoloPlay/Config/Sorceress.js b/libs/SoloPlay/Config/Sorceress.js index ae95467f..e73f69de 100644 --- a/libs/SoloPlay/Config/Sorceress.js +++ b/libs/SoloPlay/Config/Sorceress.js @@ -21,6 +21,8 @@ includeIfNotIncluded("SoloPlay/Functions/MiscOverrides.js"); includeIfNotIncluded("SoloPlay/Functions/Globals.js"); + const LADDER_ENABLED = (me.ladder || Developer.addLadderRW); + SetUp.include(); SetUp.config(); @@ -92,14 +94,44 @@ NTIP.buildList(levelingTiers); me.expansion && NTIP.buildList(expansionTiers); - Config.imbueables = [ - { name: sdk.items.JaredsStone, condition: () => (me.normal && me.expansion) }, - { name: sdk.items.SwirlingCrystal, condition: () => (!me.normal && me.charlvl < 66 && me.expansion) }, - { name: sdk.items.DimensionalShard, condition: () => (me.equipped.get(sdk.body.RightArm).tier < 777 && me.expansion) }, - { name: sdk.items.Belt, condition: () => (me.normal && (me.equipped.get(sdk.body.RightArm).tier > 777 || me.classic)) }, - { name: sdk.items.MeshBelt, condition: () => (!me.normal && me.charlvl < 46 && me.trueStr > 58 && (me.equipped.get(sdk.body.RightArm).tier > 777 || me.classic)) }, - { name: sdk.items.SpiderwebSash, condition: () => (!me.normal && me.trueStr > 50 && (me.equipped.get(sdk.body.RightArm).tier > 777 || me.classic)) }, - ].filter((item) => item.condition()); + Config.imbueables = (function () { + /** + * @param {number} name + * @param {function(): boolean} condition + */ + const _imbueObj = (name, condition) => ({ name: name, condition: condition }); + + return [ + _imbueObj( + sdk.items.JaredsStone, + () => (me.normal && me.expansion) + ), + _imbueObj( + sdk.items.SwirlingCrystal, + () => (!me.normal && me.charlvl < 66 && me.expansion) + ), + _imbueObj( + sdk.items.DimensionalShard, + () => (me.equipped.get(sdk.body.RightArm).tier < 777 && me.expansion) + ), + _imbueObj( + sdk.items.Belt, + () => (me.normal && (me.equipped.get(sdk.body.RightArm).tier > 777 || me.classic)) + ), + _imbueObj( + sdk.items.MeshBelt, + () => (!me.normal + && me.charlvl < 46 + && me.trueStr > 58 + && (me.equipped.get(sdk.body.RightArm).tier > 777 || me.classic) + ) + ), + _imbueObj( + sdk.items.SpiderwebSash, + () => (!me.normal && me.trueStr > 50 && (me.equipped.get(sdk.body.RightArm).tier > 777 || me.classic)) + ), + ].filter((item) => item.condition()); + })(); let imbueArr = SetUp.imbueItems(); @@ -108,7 +140,9 @@ switch (me.gametype) { case sdk.game.gametype.Classic: // Res shield - if ((me.equipped.get(sdk.body.LeftArm).tier < 487 && !me.equipped.get(sdk.body.RightArm).twoHanded) || (me.equipped.get(sdk.body.RightArm).tier < 487 && me.equipped.get(sdk.body.RightArm).twoHanded)) { + if ((me.equipped.get(sdk.body.LeftArm).tier < 487 + && !me.equipped.get(sdk.body.RightArm).twoHanded) + || (me.equipped.get(sdk.body.RightArm).tier < 487 && me.equipped.get(sdk.body.RightArm).twoHanded)) { includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/PDiamondShield.js"); } @@ -129,7 +163,9 @@ if (!maxedMage) { Config.Recipes.push([Recipe.Unique.Armor.ToExceptional, "Light Gauntlets", Roll.NonEth]); - ["Blova", "Lightning"].includes(SetUp.finalBuild) && Config.Recipes.push([Recipe.Unique.Armor.ToElite, "Battle Gauntlets", Roll.NonEth, "magefist"]); + if (["Blova", "Lightning"].includes(SetUp.finalBuild)) { + Config.Recipes.push([Recipe.Unique.Armor.ToElite, "Battle Gauntlets", Roll.NonEth, "magefist"]); + } } } @@ -140,12 +176,14 @@ case "Blova": case "Lightning": // Infinity - if ((me.ladder || Developer.addLadderRW) && Item.getMercEquipped(sdk.body.RightArm).prefixnum !== sdk.locale.items.Infinity) { + if (LADDER_ENABLED && Item.getMercEquipped(sdk.body.RightArm).prefixnum !== sdk.locale.items.Infinity) { includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/MercInfinity.js"); } // Spirit Shield - if ((me.ladder || Developer.addLadderRW) && SetUp.currentBuild === SetUp.finalBuild && (me.equipped.get(sdk.body.LeftArm).tier < 1000 || me.equipped.get(sdk.body.LeftArmSecondary).prefixnum !== sdk.locale.items.Spirit)) { + if (LADDER_ENABLED && SetUp.currentBuild === SetUp.finalBuild + && (me.equipped.get(sdk.body.LeftArm).tier < 1000 + || me.equipped.get(sdk.body.LeftArmSecondary).prefixnum !== sdk.locale.items.Spirit)) { includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/SpiritShield.js"); } @@ -160,9 +198,24 @@ } Config.socketables.push(addSocketableObj(sdk.items.Monarch, [], [], - !me.hell, (item) => !Check.haveBase("monarch", 4) && item.ilvl >= 41 && item.isBaseType && !item.ethereal + !me.hell, + /** @param {ItemUnit} item */ + function (item) { + /** @type {GetOwnedSettings} */ + const wanted = { + classid: sdk.items.Monarch, + mode: sdk.items.mode.inStorage, + sockets: 4, + /** @param {ItemUnit} item */ + cb: function (item) { + return item.isBaseType; + } + }; + return !me.getOwned(wanted).length && item.ilvl >= 41 && item.isBaseType && !item.ethereal; + } )); - Config.socketables.push(addSocketableObj(sdk.items.Shako, [sdk.items.runes.Um], [sdk.items.gems.Perfect.Ruby], + Config.socketables.push(addSocketableObj(sdk.items.Shako, + [sdk.items.runes.Um], [sdk.items.gems.Perfect.Ruby], true, (item) => item.unique && !item.ethereal )); @@ -172,10 +225,12 @@ case "Meteorb": case "Cold": case "Blizzballer": - Config.socketables.push(addSocketableObj(sdk.items.DeathMask, [sdk.items.runes.Um], [sdk.items.gems.Perfect.Ruby], + Config.socketables.push(addSocketableObj(sdk.items.DeathMask, + [sdk.items.runes.Um], [sdk.items.gems.Perfect.Ruby], true, (item) => item.set && !item.ethereal )); - Config.socketables.push(addSocketableObj(sdk.items.LacqueredPlate, [sdk.items.runes.Ber], [sdk.items.gems.Perfect.Ruby], + Config.socketables.push(addSocketableObj(sdk.items.LacqueredPlate, + [sdk.items.runes.Ber], [sdk.items.gems.Perfect.Ruby], true, (item) => item.set && !item.ethereal )); Config.socketables.push(addSocketableObj(sdk.items.SwirlingCrystal, [sdk.items.runes.Ist], [], @@ -192,10 +247,13 @@ // Go ahead and keep two P-diamonds prior to finding a moser's unless already using a better shield if (!Check.haveItem("shield", "unique", "Moser's Blessed Circle") - && !me.haveSome([{ name: sdk.locale.items.Sanctuary }, { name: sdk.locale.items.Spirit, itemtype: sdk.items.type.Shield }])) { + && !me.haveSome([ + { name: sdk.locale.items.Sanctuary }, + { name: sdk.locale.items.Spirit, itemtype: sdk.items.type.Shield } + ])) { NTIP.addLine("[name] == perfectdiamond # # [maxquantity] == 2"); - if (Item.getQuantityOwned(me.getItem(sdk.items.gems.Perfect.Diamond)) < 2) { + if (me.getOwned({ classid: sdk.items.gems.Perfect.Diamond }).length < 2) { Config.Recipes.push([Recipe.Gem, "flawlessdiamond"]); } } @@ -203,7 +261,10 @@ Check.itemSockables(sdk.items.RoundShield, "unique", "Moser's Blessed Circle"); // Sanctuary - if (!me.haveSome([{ name: sdk.locale.items.Sanctuary }, { name: sdk.locale.items.Spirit, itemtype: sdk.items.type.Shield }]) + if (!me.haveSome([ + { name: sdk.locale.items.Sanctuary }, + { name: sdk.locale.items.Spirit, itemtype: sdk.items.type.Shield } + ]) && ["Blova", "Lightning"].indexOf(SetUp.currentBuild) === -1) { includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/Sanctuary.js"); } @@ -214,12 +275,12 @@ } // Spirit Sword - if ((me.ladder || Developer.addLadderRW) && me.equipped.get(sdk.body.RightArm).tier < 777) { + if (LADDER_ENABLED && me.equipped.get(sdk.body.RightArm).tier < 777) { includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/SpiritSword.js"); } // Merc Insight - if ((me.ladder || Developer.addLadderRW) && Item.getMercEquipped(sdk.body.RightArm).tier < 3600) { + if (LADDER_ENABLED && Item.getMercEquipped(sdk.body.RightArm).tier < 3600) { includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/MercInsight.js"); } From da717ce01ed018b16cbbb117367b659fbfb5ba25 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Fri, 14 Jul 2023 12:17:27 -0400 Subject: [PATCH 206/263] Update General.js - Continue removing `Check.haveBase` --- libs/SoloPlay/Utils/General.js | 38 ++++++++++++++++++++++++++++------ 1 file changed, 32 insertions(+), 6 deletions(-) diff --git a/libs/SoloPlay/Utils/General.js b/libs/SoloPlay/Utils/General.js index ab2ead4d..3c490459 100644 --- a/libs/SoloPlay/Utils/General.js +++ b/libs/SoloPlay/Utils/General.js @@ -104,16 +104,42 @@ // spirit base basicSocketables.caster .push(addSocketableObj(sdk.items.BroadSword, [], [], true, - (item) => me.normal && !Check.haveBase("sword", 4) - && !me.checkItem({ name: sdk.locale.items.Spirit, itemtype: sdk.items.type.Sword }).have - && item.ilvl >= 26 && item.isBaseType && !item.ethereal + /** @param {ItemUnit} item */ + function (item) { + /** @type {GetOwnedSettings} */ + const wanted = { + itemType: sdk.items.type.Sword, + mode: sdk.items.mode.inStorage, + sockets: 4, + /** @param {ItemUnit} item */ + cb: function (item) { + return item.isBaseType; + } + }; + return me.normal && !me.getOwned(wanted).length + && !me.checkItem({ name: sdk.locale.items.Spirit, itemtype: sdk.items.type.Sword }).have + && item.ilvl >= 26 && item.isBaseType && !item.ethereal; + } )); // spirit base basicSocketables.caster .push(addSocketableObj(sdk.items.CrystalSword, [], [], true, - (item) => me.normal && !Check.haveBase("sword", 4) - && !me.checkItem({ name: sdk.locale.items.Spirit, itemtype: sdk.items.type.Sword }).have - && item.ilvl >= 26 && item.ilvl <= 40 && item.isBaseType && !item.ethereal + /** @param {ItemUnit} item */ + function (item) { + /** @type {GetOwnedSettings} */ + const wanted = { + itemType: sdk.items.type.Sword, + mode: sdk.items.mode.inStorage, + sockets: 4, + /** @param {ItemUnit} item */ + cb: function (item) { + return item.isBaseType; + } + }; + return me.normal && !me.getOwned(wanted).length + && !me.checkItem({ name: sdk.locale.items.Spirit, itemtype: sdk.items.type.Sword }).have + && item.ilvl >= 26 && item.ilvl <= 40 && item.isBaseType && !item.ethereal; + } )); // Lidless basicSocketables.caster From 2522600faa94f3f517099ec2adfa7af1e4fabd52 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Fri, 14 Jul 2023 12:19:55 -0400 Subject: [PATCH 207/263] Update Globals.js - Remove `Check.haveBase` --- libs/SoloPlay/Functions/Globals.js | 99 ++++++++++++++---------------- 1 file changed, 46 insertions(+), 53 deletions(-) diff --git a/libs/SoloPlay/Functions/Globals.js b/libs/SoloPlay/Functions/Globals.js index 87980d4d..0358b609 100644 --- a/libs/SoloPlay/Functions/Globals.js +++ b/libs/SoloPlay/Functions/Globals.js @@ -696,17 +696,19 @@ const Check = { }, broken: function () { - let gold = me.gold; + const gold = me.gold; + const rightArm = me.equipped.get(sdk.body.RightArm); + const leftArm = me.equipped.get(sdk.body.LeftArm); // Almost broken but not quite - if (((me.equipped.get(sdk.body.RightArm).durability <= 30 && me.equipped.get(sdk.body.RightArm).durability > 0) - || (me.equipped.get(sdk.body.LeftArm).durability <= 30 && me.equipped.get(sdk.body.LeftArm).durability > 0) + if (((rightArm.durability <= 30 && rightArm.durability > 0) + || (leftArm.durability <= 30 && leftArm.durability > 0) && !me.getMerc() && me.charlvl >= 15 && !me.normal && !me.nightmare && gold < 1000)) { return 1; } // Broken - if ((me.equipped.get(sdk.body.RightArm).durability === 0 || me.equipped.get(sdk.body.LeftArm).durability === 0) + if ((rightArm.durability === 0 || leftArm.durability === 0) && me.charlvl >= 15 && !me.normal && gold < 1000) { return 2; } @@ -733,7 +735,7 @@ const Check = { return false; case meleeChar && !me.normal: // check how broke we are - only for melee chars since casters don't care about weapons - let wep = items.filter(i => i.isEquipped && i.bodylocation === sdk.body.RightArm).first(); + let wep = me.equipped.get(sdk.body.RightArm); if (!!wep && meleeChar && wep.durabilityPercent === 0) { // we are really broke - go back to normal msg = " We are broken - lets get some easy gold in normal."; @@ -826,11 +828,24 @@ const Check = { break; case sdk.difficulty.Nightmare: - if ((me.haveRunes([sdk.items.runes.Tal, sdk.items.runes.Thul, sdk.items.runes.Ort, sdk.items.runes.Amn]) && Check.currentBuild().caster) + if ((me.haveRunes([sdk.items.runes.Tal, sdk.items.runes.Thul, sdk.items.runes.Ort, sdk.items.runes.Amn]) + && Check.currentBuild().caster) || (!me.paladin && me.checkItem({ name: sdk.locale.items.Spirit, itemtype: sdk.items.type.Sword }).have) - || (me.paladin && me.haveAll([{ name: sdk.locale.items.Spirit, itemtype: sdk.items.type.Sword }, { name: sdk.locale.items.Spirit, itemtype: sdk.items.type.AuricShields }])) + || (me.paladin && me.haveAll([ + { + name: sdk.locale.items.Spirit, + itemtype: sdk.items.type.Sword + }, + { + name: sdk.locale.items.Spirit, + itemtype: sdk.items.type.AuricShields + } + ])) || (me.necromancer && me.checkItem({ name: sdk.locale.items.White }).have - && (me.checkItem({ name: sdk.locale.items.Rhyme, itemtype: sdk.items.type.VoodooHeads }).have || me.equipped.get(sdk.body.LeftArm).tier > 800)) + && ( + me.checkItem({ name: sdk.locale.items.Rhyme, itemtype: sdk.items.type.VoodooHeads }).have + || me.equipped.get(sdk.body.LeftArm).tier > 800 + )) || (me.barbarian && (me.checkItem({ name: sdk.locale.items.Lawbringer }).have || me.baal))) { needRunes = false; } @@ -847,7 +862,13 @@ const Check = { return needRunes; }, - // todo: need to finish up adding locale string ids to sdk so I can remove this in favor of better me.checkItem prototype + /** + * @deprecated Use me.checkItem() instead + * @param {number | string} type + * @param {string} [flag] + * @param {string} [iName] + * @returns + */ haveItem: function (type, flag, iName) { let [isClassID, itemCHECK, typeCHECK] = [false, false, false]; @@ -888,21 +909,25 @@ const Check = { }); } - for (let i = 0; i < items.length; i++) { + const quality = (flag === "Set" || flag === "Unique" || flag === "Crafted") + ? sdk.items.quality[flag] + : undefined; + + for (let item of items) { switch (flag) { case "Set": case "Unique": case "Crafted": - itemCHECK = !!(items[i].quality === sdk.items.quality[flag]) && (iName ? items[i].fname.toLowerCase().includes(iName) : true); + itemCHECK = !!(item.quality === quality) && (iName ? item.fname.toLowerCase().includes(iName) : true); break; case "Runeword": - itemCHECK = !!(items[i].isRuneword) && (iName ? items[i].fname.toLowerCase().includes(iName) : true); + itemCHECK = !!(item.isRuneword) && (iName ? item.fname.toLowerCase().includes(iName) : true); break; } // don't waste time if first condition wasn't met if (itemCHECK && typeof type === "number") { - typeCHECK = isClassID ? items[i].classid === type : items[i].itemType === type; + typeCHECK = isClassID ? item.classid === type : item.itemType === type; } if (itemCHECK && typeCHECK) { @@ -941,17 +966,17 @@ const Check = { && getBaseStat("items", item.classid, "gemsockets") > 0; }); - for (let i = 0; i < items.length; i++) { - itemCHECK = !!(items[i].quality === quality) && (iName ? items[i].fname.toLowerCase().includes(iName) : true); + for (let item of items) { + itemCHECK = !!(item.quality === quality) && (iName ? item.fname.toLowerCase().includes(iName) : true); // don't waste time if first condition wasn't met - itemCHECK && (typeCHECK = isClassID ? items[i].classid === type : items[i].itemType === type); + itemCHECK && (typeCHECK = isClassID ? item.classid === type : item.itemType === type); if (itemCHECK && typeCHECK) { - if (!socketableCHECK && items[i].getItemsEx().length === 0) { + if (!socketableCHECK && item.getItemsEx().length === 0) { return true; } else if (socketableCHECK) { - SoloWants.addToList(items[i]); + SoloWants.addToList(item); return true; } @@ -961,40 +986,6 @@ const Check = { return false; }, - haveBase: function (type = undefined, sockets = undefined) { - if (!type || !sockets) return false; - let isClassID = false; - - switch (typeof type) { - case "string": - typeof type === "string" && (type = type.toLowerCase()); - if (!NTIPAliasType[type] && !NTIPAliasClassID[type]) return false; - isClassID = !!NTIPAliasClassID[type]; - type = isClassID ? NTIPAliasClassID[type] : NTIPAliasType[type]; - - break; - case "number": - if (!Object.values(sdk.items.type).includes(type) && !Object.values(sdk.items).includes(type)) return false; - isClassID = Object.values(sdk.items).includes(type); - - break; - } - - let items = me.getItemsEx() - .filter(function (item) { - return item.isBaseType && item.isInStorage && (isClassID ? item.classid === type : item.itemType === type); - }); - - for (let i = 0; i < items.length; i++) { - if (items[i].sockets === sockets - && (isClassID ? items[i].classid === type : items[i].itemType === type)) { - return true; - } - } - - return false; - }, - getMaxValue: function (buildInfo, stat) { if (!buildInfo || !buildInfo.stats || stat === undefined) return 0; let highest = 0; @@ -1110,7 +1101,9 @@ const Check = { usePreviousSocketQuest: function () { if (me.classic) return; if (!Check.resistance().Status) { - if (me.weaponswitch === 0 && me.equipped.get(sdk.body.LeftArm).fname.includes("Lidless Wall") && !me.equipped.get(sdk.body.LeftArm).socketed) { + if (me.weaponswitch === 0 + && me.equipped.get(sdk.body.LeftArm).fname.includes("Lidless Wall") + && !me.equipped.get(sdk.body.LeftArm).socketed) { if (!me.normal) { if (!me.data.normal.socketUsed) goToDifficulty(sdk.difficulty.Normal, " to use socket quest"); if (me.hell && !me.data.nightmare.socketUsed) goToDifficulty(sdk.difficulty.Nightmare, " to use socket quest"); From 2a681761c17fa30f832530dd07683fd4664e4073 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Fri, 14 Jul 2023 12:21:14 -0400 Subject: [PATCH 208/263] Update ItemUtilities.js - phasing out usage of `Item.getQuantityOwned`, in favor of `me.getOwned` --- libs/SoloPlay/Functions/ItemUtilities.js | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/libs/SoloPlay/Functions/ItemUtilities.js b/libs/SoloPlay/Functions/ItemUtilities.js index 7cf5dbe2..5dde6d3a 100644 --- a/libs/SoloPlay/Functions/ItemUtilities.js +++ b/libs/SoloPlay/Functions/ItemUtilities.js @@ -453,8 +453,12 @@ includeIfNotIncluded("core/Item.js"); const defenseScoreCheck = function (base, itemToCheck) { let [defScoreBase, defScoreItem] = [defenseScore(base), defenseScore(itemToCheck)]; - verbose && console.log("ÿc9betterThanStashedÿc0 :: BaseScore: ", defScoreBase, " itemToCheckScore: ", defScoreItem); - if (defScoreBase.def > defScoreItem.def || (defScoreBase.def === defScoreItem.def && (defScoreBase.eDef > defScoreItem.eDef || base.ilvl > itemToCheck.ilvl))) { + if (verbose) { + console.log("ÿc9betterThanStashedÿc0 :: BaseScore: ", defScoreBase, " itemToCheckScore: ", defScoreItem); + } + if (defScoreBase.def > defScoreItem.def + || (defScoreBase.def === defScoreItem.def + && (defScoreBase.eDef > defScoreItem.eDef || base.ilvl > itemToCheck.ilvl))) { return true; } return false; @@ -462,11 +466,14 @@ includeIfNotIncluded("core/Item.js"); const damageScoreCheck = function (base, itemToCheck) { let [gScoreBase, gScoreCheck] = [generalScore(base), generalScore(itemToCheck)]; - verbose && console.log("ÿc9betterThanStashedÿc0 :: BaseScore: " + generalScore(base) + " itemToCheckScore: " + generalScore(itemToCheck)); + if (verbose) { + console.log("ÿc9betterThanStashedÿc0 :: BaseScore: " + generalScore(base) + " itemToCheckScore: " + generalScore(itemToCheck)); + } switch (true) { case (gScoreBase > gScoreCheck || (gScoreBase === gScoreCheck && base.ilvl > itemToCheck.ilvl)): - case (me.barbarian && (gScoreBase === gScoreCheck && Item.getQuantityOwned(base) < 2)): - case (me.assassin && !Check.currentBuild().caster && (gScoreBase === gScoreCheck && Item.getQuantityOwned(base) < 2)): + case (me.barbarian && (gScoreBase === gScoreCheck && me.getOwned(base).length < 2)): + case (me.assassin && !Check.currentBuild().caster + && (gScoreBase === gScoreCheck && me.getOwned(base).length < 2)): return true; } return false; From a86774bcda7db578978b66ac675f7742f520f340 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Fri, 14 Jul 2023 12:22:02 -0400 Subject: [PATCH 209/263] Update ItemOverrides.js - remove `Item.getQuantityOwned`, use `me.getOwned` --- libs/SoloPlay/Functions/ItemOverrides.js | 18 ------------------ 1 file changed, 18 deletions(-) diff --git a/libs/SoloPlay/Functions/ItemOverrides.js b/libs/SoloPlay/Functions/ItemOverrides.js index 508ce1ef..a9d92f62 100644 --- a/libs/SoloPlay/Functions/ItemOverrides.js +++ b/libs/SoloPlay/Functions/ItemOverrides.js @@ -29,24 +29,6 @@ Item.helmTypes = [ sdk.items.type.Helm, sdk.items.type.PrimalHelm, sdk.items.type.Circlet, sdk.items.type.Pelt ]; -/** - * @param {ItemUnit} item - * @param {boolean} [skipSameItem] - */ -Item.getQuantityOwned = function (item, skipSameItem = false) { - if (!item) return 0; - - return me.getItemsEx() - .filter(function (check) { - return check.itemType === item.itemType - && (!skipSameItem || check.gid !== item.gid) - && check.classid === item.classid - && check.quality === item.quality - && check.sockets === item.sockets - && check.isInStorage; - }).length; -}; - /** * @param {ItemUnit} item */ From a121eb6dfdc50fbf41e4a08735a9c1671973b8b3 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Fri, 14 Jul 2023 12:23:00 -0400 Subject: [PATCH 210/263] Create eyeback.js - not used yet, but considering it --- libs/SoloPlay/Scripts/eyeback.js | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 libs/SoloPlay/Scripts/eyeback.js diff --git a/libs/SoloPlay/Scripts/eyeback.js b/libs/SoloPlay/Scripts/eyeback.js new file mode 100644 index 00000000..c25c4ac1 --- /dev/null +++ b/libs/SoloPlay/Scripts/eyeback.js @@ -0,0 +1,20 @@ +/** +* @filename eyeback.js +* @author kolton +* @desc kill Eyeback the Unleashed +* +*/ + +function eyeback () { + Town.doChores(); + Pather.useWaypoint(sdk.areas.ArreatPlateau); + Precast.doPrecast(true); + + if (!Pather.moveToPresetMonster(sdk.areas.FrigidHighlands, sdk.monsters.preset.EyebacktheUnleashed)) { + throw new Error("Failed to move to Eyeback the Unleashed"); + } + + Attack.killTarget(getLocaleString(sdk.locale.monsters.EyebacktheUnleashed)); + + return true; +} From 2dcfde2210692a4762bc30bbae138aaa600117d4 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Fri, 14 Jul 2023 12:24:37 -0400 Subject: [PATCH 211/263] small script cleanups --- libs/SoloPlay/Scripts/shenk.js | 7 ++- libs/SoloPlay/Scripts/summoner.js | 73 +++++++++++++++++++------------ 2 files changed, 49 insertions(+), 31 deletions(-) diff --git a/libs/SoloPlay/Scripts/shenk.js b/libs/SoloPlay/Scripts/shenk.js index 6ddc93b2..a0fcf8b3 100644 --- a/libs/SoloPlay/Scripts/shenk.js +++ b/libs/SoloPlay/Scripts/shenk.js @@ -1,6 +1,7 @@ /** * @filename shenk.js -* @author isid0re, theBGuy +* @author theBGuy +* @credit kolton for the original * @desc shenk quest for sockets, wp's, and mf * */ @@ -9,7 +10,9 @@ function shenk () { Town.doChores(false, { fullChores: true }); myPrint("starting shenk"); - Pather.checkWP(sdk.areas.FrigidHighlands, true) ? Pather.useWaypoint(sdk.areas.FrigidHighlands) : Pather.getWP(sdk.areas.FrigidHighlands); + Pather.checkWP(sdk.areas.FrigidHighlands, true) + ? Pather.useWaypoint(sdk.areas.FrigidHighlands) + : Pather.getWP(sdk.areas.FrigidHighlands); Precast.doPrecast(true); Pather.moveTo(3745, 5084); Attack.killTarget(getLocaleString(sdk.locale.monsters.EldritchtheRectifier)); diff --git a/libs/SoloPlay/Scripts/summoner.js b/libs/SoloPlay/Scripts/summoner.js index 4224c17c..a21aebec 100644 --- a/libs/SoloPlay/Scripts/summoner.js +++ b/libs/SoloPlay/Scripts/summoner.js @@ -10,50 +10,65 @@ function summoner () { const teleportPads = function () { if (!me.inArea(sdk.areas.ArcaneSanctuary) || Pather.useTeleport()) return true; + const [wpX, wpY] = [25449, 5449]; + const tppID = [192, 304, 305, 306]; + let ntppPath = [ + [53, 2], [103, -3], + [113, -68], [173, -58], + [243, -73], [293, -58], + [353, -68], [372, -62], [342, -17] + ]; + let stppPath = [ + [-56, 2], [-128, -7], + [-98, 78], [-176, 62], + [-243, 58], [-296, 62], + [-372, 62], [-366, 12] + ]; + let etppPath = [ + [28, 52], [-12, 92], + [53, 112], [72, 118], + [88, 172], [54, 227], + [43, 247], [88, 292], + [82, 378], [-16, 332], [2, 353] + ]; + let wtppPath = [ + [-26, -63], [2, -121], + [3, -133], [62, -117], + [34, -183], [54, -228], + [43, -243], [34, -303], + [72, -351], [64, -368], [23, -338] + ]; + const stand = Game.getPresetObject(me.area, sdk.objects.Journal).realCoords(); + console.debug(stand.x, stand.y); + + /** @type {[number, number][]} */ let tppPath; - let [wpX, wpY] = [25449, 5449]; - let ntppPath = [[53, 2], [103, -3], [113, -68], [173, -58], [243, -73], [293, -58], [353, -68], [372, -62], [342, -17]]; - let stppPath = [[-56, 2], [-128, -7], [-98, 78], [-176, 62], [-243, 58], [-296, 62], [-372, 62], [-366, 12]]; - let etppPath = [[28, 52], [-12, 92], [53, 112], [72, 118], [88, 172], [54, 227], [43, 247], [88, 292], [82, 378], [-16, 332], [2, 353]]; - let wtppPath = [[-26, -63], [2, -121], [3, -133], [62, -117], [34, -183], [54, -228], [43, -243], [34, -303], [72, -351], [64, -368], [23, -338]]; - let stand = Game.getPresetObject(me.area, sdk.objects.Journal); - let [tppPathX, tppPathY] = [(stand.roomx * 5 + stand.x), (stand.roomy * 5 + stand.y)]; - console.debug(tppPathX, tppPathY); - let tppID = [192, 304, 305, 306]; - - switch (tppPathX) { - case 25011: + if (stand.x === 25011) { tppPath = ntppPath; - break; - case 25866: + } else if (stand.x === 25866) { tppPath = stppPath; - break; - case 25431: - switch (tppPathY) { - case 5011: + } else if (stand.x === 25431) { + if (stand.y === 5011) { tppPath = etppPath; - break; - case 5861: + } else if (stand.y === 5861) { tppPath = wtppPath; - break; } - - break; } - if (getPath(me.area, me.x, me.y, tppPathX, tppPathY, 0, 10).length === 0) { + if (getPath(me.area, me.x, me.y, stand.x, stand.y, 0, 10).length === 0) { me.overhead("Using telepad layout"); - for (let i = 0; i < tppPath.length; i += 1) { + for (let [x, y] of tppPath) { for (let h = 0; h < 5; h += 1) { - Pather.moveTo(wpX - tppPath[i][0], wpY - tppPath[i][1]); + // todo - these are tkable, handle that + Pather.moveTo(wpX - x, wpY - y); - for (let activate = 0; activate < tppID.length; activate += 1) { - let telepad = Game.getObject(tppID[activate]); + for (let id of tppID) { + let telepad = Game.getObject(id); if (telepad) { do { - if (Math.abs((telepad.x - (wpX - tppPath[i][0]) + (telepad.y - (wpY - tppPath[i][1])))) <= 0) { + if (Math.abs((telepad.x - (wpX - x) + (telepad.y - (wpY - y)))) <= 0) { delay(100 + me.ping); telepad.interact(); } From b768c1255a2798ceafa792ae97e68e1371f5ce64 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Fri, 14 Jul 2023 12:25:21 -0400 Subject: [PATCH 212/263] Update globals.d.ts - typedef the settings for getOwned --- libs/SoloPlay/globals.d.ts | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/libs/SoloPlay/globals.d.ts b/libs/SoloPlay/globals.d.ts index 82f1cb46..41fcbe97 100644 --- a/libs/SoloPlay/globals.d.ts +++ b/libs/SoloPlay/globals.d.ts @@ -124,6 +124,17 @@ declare global { type EquippedMap = Map; + type GetOwnedSettings = { + itemType?: number, + classid?: number, + mode?: number, + quality?: number, + sockets?: number, + location?: number, + ethereal?: boolean, + cb?: (item: ItemUnit) => boolean, + }; + interface MeType { readonly maxNearMonsters: number; readonly dualWielding: boolean; @@ -173,16 +184,7 @@ declare global { sortInventory(): boolean; cleanUpScrolls(tome: ItemUnit, scrollId: number): number; update(): void; - getOwned(itemInfo: ItemUnit | { - itemType?: number, - classid?: number, - mode?: number, - quality?: number, - sockets?: number, - location?: number, - ethereal?: boolean, - cb?: (item: ItemUnit) => boolean, - }): ItemUnit[]; + getOwned(itemInfo: ItemUnit | GetOwnedSettings): ItemUnit[]; } interface Container { From 2cf108d1c050d879d9d0942a720674d9dd2d45fa Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Fri, 14 Jul 2023 12:42:44 -0400 Subject: [PATCH 213/263] Update AttackOverrides.js - handle odd bug with charges --- libs/SoloPlay/Functions/AttackOverrides.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/libs/SoloPlay/Functions/AttackOverrides.js b/libs/SoloPlay/Functions/AttackOverrides.js index 6ab7f40d..dbcb98dd 100644 --- a/libs/SoloPlay/Functions/AttackOverrides.js +++ b/libs/SoloPlay/Functions/AttackOverrides.js @@ -1177,7 +1177,7 @@ Attack.getCurrentChargedSkillIds = function (init = false) { if (!(charges instanceof Array)) charges = [charges]; for (let charge of charges) { - if (!charge) continue; + if (!charge || !charge.hasOwnProperty("skill")) continue; // add to total list of skillIds if (charge.charges > 0 && !currentChargedSkills.includes(charge.skill)) { currentChargedSkills.push(charge.skill); @@ -1233,6 +1233,7 @@ Attack.getItemCharges = function (skillId) { if (!(charges instanceof Array)) charges = [charges]; for (let charge of charges) { + if (!charge || !charge.hasOwnProperty("skill")) continue; if (validCharge(charge)) return true; } } From 8adbef1a437a6861ae31c88ca2edffe0358f1a54 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Fri, 14 Jul 2023 17:07:21 -0400 Subject: [PATCH 214/263] Fix typo - LeftRing -> RingLeft - RightRing -> RingRight --- libs/SoloPlay/Config/Amazon.js | 7 ++++--- libs/SoloPlay/Config/Assassin.js | 4 ++-- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/libs/SoloPlay/Config/Amazon.js b/libs/SoloPlay/Config/Amazon.js index d5994d64..ddec90c8 100644 --- a/libs/SoloPlay/Config/Amazon.js +++ b/libs/SoloPlay/Config/Amazon.js @@ -34,8 +34,8 @@ if (me.equipped.get(sdk.body.Neck).tier < 100000) { Config.GambleItems.push("Amulet"); } - if (me.equipped.get(sdk.body.LeftRing).tier < 100000 - || me.equipped.get(sdk.body.RightRing).tier < 100000) { + if (me.equipped.get(sdk.body.RingLeft).tier < 100000 + || me.equipped.get(sdk.body.RingRight).tier < 100000) { Config.GambleItems.push("Ring"); } if (me.equipped.get(sdk.body.Head).tier < 100000) { @@ -198,7 +198,8 @@ )); } - if ((SetUp.finalBuild === "Faithbowzon") && !me.checkItem({ name: sdk.locale.items.Faith, classid: sdk.items.GrandMatronBow }).have) { + if ((SetUp.finalBuild === "Faithbowzon") + && !me.checkItem({ name: sdk.locale.items.Faith, classid: sdk.items.GrandMatronBow }).have) { includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/Faith.js"); } diff --git a/libs/SoloPlay/Config/Assassin.js b/libs/SoloPlay/Config/Assassin.js index 81fac8e2..9d1b8ce9 100644 --- a/libs/SoloPlay/Config/Assassin.js +++ b/libs/SoloPlay/Config/Assassin.js @@ -33,8 +33,8 @@ if (me.equipped.get(sdk.body.Neck).tier < 100000) { Config.GambleItems.push("Amulet"); } - if (me.equipped.get(sdk.body.LeftRing).tier < 100000 - || me.equipped.get(sdk.body.RightRing).tier < 100000) { + if (me.equipped.get(sdk.body.RingLeft).tier < 100000 + || me.equipped.get(sdk.body.RingRight).tier < 100000) { Config.GambleItems.push("Ring"); } // Config.GambleItems.push("Circlet"); From 195adf256fef9593b3290f0408b020ef5e444023 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Sat, 15 Jul 2023 18:21:32 -0400 Subject: [PATCH 215/263] small bit of cleanup --- libs/SoloPlay/Functions/AttackOverrides.js | 7 +- .../ClassAttackOverrides/AmazonAttacks.js | 120 ++++++--- libs/SoloPlay/Functions/ConfigOverrides.js | 4 +- libs/SoloPlay/Functions/DynamicTiers.js | 58 ++++- libs/SoloPlay/Functions/ItemOverrides.js | 8 +- libs/SoloPlay/Functions/ItemPrototypes.js | 233 +++++------------- libs/SoloPlay/Functions/NTIPOverrides.js | 2 +- libs/SoloPlay/Functions/Polyfills.js | 23 -- libs/SoloPlay/Threads/ToolsThread.js | 9 +- 9 files changed, 207 insertions(+), 257 deletions(-) diff --git a/libs/SoloPlay/Functions/AttackOverrides.js b/libs/SoloPlay/Functions/AttackOverrides.js index dbcb98dd..7073732d 100644 --- a/libs/SoloPlay/Functions/AttackOverrides.js +++ b/libs/SoloPlay/Functions/AttackOverrides.js @@ -1439,7 +1439,10 @@ Attack.walkingSortMonsters = function (unitA, unitB) { }; Attack.pwnDury = function () { - let duriel = Misc.poll(() => Game.getMonster(sdk.monsters.Duriel)); + const getDuriel = function () { + return Game.getMonster(sdk.monsters.Duriel); + }; + let duriel = Misc.poll(getDuriel); if (!duriel) return false; const tick = getTickCount(); @@ -1457,7 +1460,7 @@ Attack.pwnDury = function () { break; } if (!duriel || !copyUnit(duriel).x) { - duriel = Misc.poll(() => Game.getMonster(-1, -1, gid), 1000, 80); + duriel = Misc.poll(getDuriel, 1000, 80); if (!duriel || !duriel.attackable) return true; } //ToDo; figure out static diff --git a/libs/SoloPlay/Functions/ClassAttackOverrides/AmazonAttacks.js b/libs/SoloPlay/Functions/ClassAttackOverrides/AmazonAttacks.js index c2b6d23c..8d0747ce 100644 --- a/libs/SoloPlay/Functions/ClassAttackOverrides/AmazonAttacks.js +++ b/libs/SoloPlay/Functions/ClassAttackOverrides/AmazonAttacks.js @@ -3,6 +3,8 @@ * @author theBGuy * @desc Amazon fixes to improve class attack functionality * +* @typedef {import('../../../../sdk/globals')} +* @typedef {import('../../globals')} */ /** @@ -76,7 +78,8 @@ ClassAttack.doAttack = function (unit, preattack, once) { // Precast Section -----------------------------------------------------------------------------------------------------------------// if (useSkills.SlowMissiles) { if (!unit.getState(sdk.states.SlowMissiles)) { - if ((unit.distance > 3 || unit.getEnchant(sdk.enchant.LightningEnchanted)) && unit.distance < 13 && !checkCollision(me, unit, sdk.collision.Ranged)) { + if ((unit.distance > 3 || unit.getEnchant(sdk.enchant.LightningEnchanted)) + && unit.distance < 13 && !checkCollision(me, unit, sdk.collision.Ranged)) { // Act Bosses and mini-bosses are immune to Slow Missles and pointless to use on lister or Cows, Use Inner-Sight instead if ([sdk.monsters.HellBovine].includes(unit.classid) || unit.isBoss) { // Check if already in this state @@ -90,31 +93,44 @@ ClassAttack.doAttack = function (unit, preattack, once) { } } - if (allowThrowing && Skill.canUse(sdk.skills.LightningFury) && unit.getEnchant(sdk.enchant.ManaBurn) && unit.getMobCount(7) > 2) { + if (allowThrowing + && Skill.canUse(sdk.skills.LightningFury) + && unit.getEnchant(sdk.enchant.ManaBurn) + && unit.getMobCount(7) > 2) { useSkills.LightFury = true; } - if (allowThrowing && Skill.canUse(sdk.skills.PlagueJavelin) && unit.getEnchant(sdk.enchant.ManaBurn) && unit.getMobCount(7) > 2) { + if (allowThrowing + && Skill.canUse(sdk.skills.PlagueJavelin) + && unit.getEnchant(sdk.enchant.ManaBurn) + && unit.getMobCount(7) > 2) { forcePlague = true; } if (useSkills.InnerSight) { - if (!unit.getState(sdk.states.InnerSight) && unit.distance > 3 && unit.distance < 13 && !checkCollision(me, unit, sdk.collision.Ranged)) { + if (!unit.getState(sdk.states.InnerSight) + && unit.distance > 3 && unit.distance < 13 + && !checkCollision(me, unit, sdk.collision.Ranged)) { Skill.cast(sdk.skills.InnerSight, sdk.skills.hand.Right, unit); } } // Handle Switch casting - let commonCheck = (gold > 500000 || unit.isBoss || [sdk.areas.ChaosSanctuary, sdk.areas.ThroneofDestruction].includes(me.area)); + const commonCheck = ( + gold > 500000 || unit.isBoss || [sdk.areas.ChaosSanctuary, sdk.areas.ThroneofDestruction].includes(me.area) + ); if (me.expansion && index === 1 && unit.curseable) { - if (CharData.skillData.haveChargedSkillOnSwitch(sdk.skills.LowerResist) - && !unit.getState(sdk.states.LowerResist) && commonCheck && !checkCollision(me, unit, sdk.collision.Ranged)) { + if (commonCheck && CharData.skillData.haveChargedSkillOnSwitch(sdk.skills.LowerResist) + && !unit.getState(sdk.states.LowerResist) + && !checkCollision(me, unit, sdk.collision.Ranged)) { // Switch cast lower resist Attack.switchCastCharges(sdk.skills.LowerResist, unit); } - if (CharData.skillData.haveChargedSkillOnSwitch(sdk.skills.Weaken) && !unit.getState(sdk.states.Weaken) - && !unit.getState(sdk.states.LowerResist) && commonCheck && !checkCollision(me, unit, sdk.collision.Ranged)) { + if (commonCheck && CharData.skillData.haveChargedSkillOnSwitch(sdk.skills.Weaken) + && !unit.getState(sdk.states.Weaken) + && !unit.getState(sdk.states.LowerResist) + && !checkCollision(me, unit, sdk.collision.Ranged)) { // Switch cast weaken Attack.switchCastCharges(sdk.skills.Weaken, unit); } @@ -129,12 +145,16 @@ ClassAttack.doAttack = function (unit, preattack, once) { if (useSkills.Decoy) { // Act Bosses or Immune to my main boss skill if ((unit.isPrimeEvil) || !Attack.checkResist(unit, Config.AttackSkill[1])) { - Misc.poll(() => !me.skillDelay, 1000, 40); + Misc.poll(function () { + return !me.skillDelay; + }, 1000, 40); // Don't use decoy if within melee distance if (unit.distance > 4) { // Check to see if decoy has already been cast - let decoy = Misc.poll(() => Game.getMonster(sdk.summons.Dopplezon), 1000, 10); + let decoy = Misc.poll(function () { + return Game.getMonster(sdk.summons.Dopplezon); + }, 1000, 10); if (!decoy && (getTickCount() - this.decoyTick >= decoyDuration) && unit.distance > 4) { if (unit.distance > 10 || checkCollision(me, unit, sdk.collision.Ranged)) { @@ -157,12 +177,16 @@ ClassAttack.doAttack = function (unit, preattack, once) { if ((useSkills.Plague) && !Attack.checkResist(unit, "lightning")) { if ((unit.distance <= 15) && !checkCollision(me, unit, sdk.collision.Ranged)) { // Cast Slow-Missles, then proceed with Plague Jav. Lowers amount of damage from projectiles. - !unit.getState(sdk.states.SlowMissiles) && useSkills.SlowMissiles && Skill.cast(sdk.skills.SlowMissiles, sdk.skills.hand.Right, unit); + if (!unit.getState(sdk.states.SlowMissiles) && useSkills.SlowMissiles) { + Skill.cast(sdk.skills.SlowMissiles, sdk.skills.hand.Right, unit); + } // Handle Switch casting if (!unit.dead) { - if (CharData.skillData.haveChargedSkillOnSwitch(sdk.skills.LowerResist) - && !unit.getState(sdk.states.LowerResist) && unit.curseable && commonCheck && !checkCollision(me, unit, sdk.collision.Ranged)) { + if (commonCheck && CharData.skillData.haveChargedSkillOnSwitch(sdk.skills.LowerResist) + && !unit.getState(sdk.states.LowerResist) + && unit.curseable + && !checkCollision(me, unit, sdk.collision.Ranged)) { // Switch cast lower resist Attack.switchCastCharges(sdk.skills.LowerResist, unit); } @@ -195,8 +219,12 @@ ClassAttack.doAttack = function (unit, preattack, once) { } // Only try attacking immunes if I have my end game javelin and they aren't lightning enchanted - use jab as main attack - if (useSkills.Jab && !Attack.checkResist(unit, Config.AttackSkill[1]) && Attack.checkResist(unit, "physical") && !unit.getEnchant(sdk.enchant.LightningEnchanted)) { - if ((unit.distance > 3 || checkCollision(me, unit, sdk.collision.Ranged)) && !Attack.getIntoPosition(unit, 3, sdk.collision.BlockWall)) { + if (useSkills.Jab + && !Attack.checkResist(unit, Config.AttackSkill[1]) + && Attack.checkResist(unit, "physical") + && !unit.getEnchant(sdk.enchant.LightningEnchanted)) { + if ((unit.distance > 3 || checkCollision(me, unit, sdk.collision.Ranged)) + && !Attack.getIntoPosition(unit, 3, sdk.collision.BlockWall)) { return Attack.Result.FAILED; } @@ -217,7 +245,8 @@ ClassAttack.doAttack = function (unit, preattack, once) { } } - if (preattack && Config.AttackSkill[0] > 0 && [sdk.skills.InnerSight, sdk.skills.SlowMissiles].indexOf(Config.AttackSkill[0]) === -1 + if (preattack && Config.AttackSkill[0] > 0 + && [sdk.skills.InnerSight, sdk.skills.SlowMissiles].indexOf(Config.AttackSkill[0]) === -1 && Attack.checkResist(unit, Config.AttackSkill[0]) && (!me.skillDelay || !Skill.isTimed(Config.AttackSkill[0]))) { if (unit.distance > preattackRange || checkCollision(me, unit, sdk.collision.Ranged)) { if (!Attack.getIntoPosition(unit, preattackRange, sdk.collision.Ranged)) { @@ -233,7 +262,7 @@ ClassAttack.doAttack = function (unit, preattack, once) { let mercRevive = 0; let skills = this.decideSkill(unit); - const switchBowAttack = (unit, attackSkill) => { + const switchBowAttack = function (unit, attackSkill) { if (Attack.getIntoPosition(unit, 20, sdk.collision.Ranged)) { try { const checkForShamans = unit.isFallen && !me.inArea(sdk.areas.BloodMoor); @@ -241,16 +270,22 @@ ClassAttack.doAttack = function (unit, preattack, once) { if (checkForShamans && !once) { // before we waste time let's see if there is a shaman we should kill const shaman = getUnits(sdk.unittype.Monster) - .filter(mon => mon.distance < 20 && mon.isShaman && mon.attackable) - .sort((a, b) => a.distance - b.distance).first(); + .filter(function (mon) { + return mon.distance < 20 && mon.isShaman && mon.attackable; + }) + .sort(Sort.units) + .first(); if (shaman) return ClassAttack.doAttack(shaman, null, true); } if (!Attack.useBowOnSwitch(unit, attackSkill, i === 5)) return Attack.Result.FAILED; if (unit.distance < 8 || me.inDanger()) { if (once) return Attack.Result.FAILED; let closeMob = getUnits(sdk.unittype.Monster) - .filter(mon => mon.distance < 10 && mon.attackable && mon.gid !== gid) - .sort(Attack.walkingSortMonsters).first(); + .filter(function (mon) { + return mon.distance < 10 && mon.attackable && mon.gid !== gid; + }) + .sort(Attack.walkingSortMonsters) + .first(); if (closeMob) return ClassAttack.doAttack(closeMob, null, true); } } @@ -266,18 +301,26 @@ ClassAttack.doAttack = function (unit, preattack, once) { && (index !== 1 || !unit.name.includes(getLocaleString(sdk.locale.text.Ghostly))) && (unit.distance >= 8 || (unit.isMoving && (unit.targetx !== me.x || unit.targety !== me.y))) && ([sdk.skills.Attack, sdk.skills.Jab].includes(skills.timed) || Skill.getManaCost(skills.timed) > me.mp)) { - let arrowSkill = (() => { + let arrowSkill = (function () { // todo - better determination of skills - if (Skill.canUse(sdk.skills.MagicArrow) && Skill.getManaCost(sdk.skills.MagicArrow) < me.mp) return sdk.skills.MagicArrow; - if (Skill.canUse(sdk.skills.FireArrow) && Skill.getManaCost(sdk.skills.FireArrow) < me.mp) return sdk.skills.FireArrow; + if (Skill.canUse(sdk.skills.MagicArrow) && Skill.getManaCost(sdk.skills.MagicArrow) < me.mp) { + return sdk.skills.MagicArrow; + } + if (Skill.canUse(sdk.skills.FireArrow) && Skill.getManaCost(sdk.skills.FireArrow) < me.mp) { + return sdk.skills.FireArrow; + } return 0; })(); if (switchBowAttack(unit, arrowSkill) === Attack.Result.SUCCESS) return Attack.Result.SUCCESS; } if ([sdk.skills.Attack, sdk.skills.Jab].includes(skills.timed) - && (unit.distance >= 12 || (unit.distance > 4 && unit.isMoving && (unit.targetx !== me.x || unit.targety !== me.y)) || unit.coldEnchanted)) { - let item = me.getItemsEx().filter(item => item.isEquipped && item.bodylocation === sdk.body.RightArm).first(); + && ( + unit.distance >= 12 + || (unit.distance > 4 && unit.isMoving && (unit.targetx !== me.x || unit.targety !== me.y)) + || unit.coldEnchanted + )) { + let item = me.getEquippedItem(sdk.body.RightArm); if (item && (item.getStat(sdk.stats.Quantity) * 100 / getBaseStat("items", item.classid, "maxstack")) > 30) { skills.timed = sdk.skills.Throw; } @@ -312,7 +355,9 @@ ClassAttack.doAttack = function (unit, preattack, once) { if (!!closeMob) { let findSkill = this.decideSkill(closeMob); - (this.doCast(closeMob, findSkill.timed, findSkill.untimed) === 1) || (Skill.canUse(sdk.skills.Decoy) && Skill.cast(sdk.skills.Decoy, sdk.skills.hand.Right, unit)); + if (this.doCast(closeMob, findSkill.timed, findSkill.untimed) !== 1) { + (Skill.canUse(sdk.skills.Decoy) && Skill.cast(sdk.skills.Decoy, sdk.skills.hand.Right, unit)); + } } } @@ -367,10 +412,14 @@ ClassAttack.doCast = function (unit, timedSkill, untimedSkill) { case sdk.skills.Throw: case sdk.skills.PlagueJavelin: case sdk.skills.LightningFury: - if (timedSkill === sdk.skills.LightningFury && this.lightFuryTick && getTickCount() - this.lightFuryTick < Time.seconds(Config.LightningFuryDelay)) { + if (timedSkill === sdk.skills.LightningFury + && this.lightFuryTick + && getTickCount() - this.lightFuryTick < Time.seconds(Config.LightningFuryDelay)) { break; } - let tsRange = timedSkill === sdk.skills.Throw && (unit.isShaman || unit.isUnraveler) ? Skill.getRange(timedSkill) - 5 : Skill.getRange(timedSkill); + let tsRange = timedSkill === sdk.skills.Throw && (unit.isShaman || unit.isUnraveler) + ? Skill.getRange(timedSkill) - 5 + : Skill.getRange(timedSkill); if (unit.distance > Skill.getRange(tsRange) || checkCollision(me, unit, sdk.collision.BlockMissile)) { if (!Attack.getIntoPosition(unit, Skill.getRange(tsRange), sdk.collision.BlockMissile)) { return Attack.Result.FAILED; @@ -394,7 +443,8 @@ ClassAttack.doCast = function (unit, timedSkill, untimedSkill) { break; default: // If main attack skill is lightning strike and charged strike's skill level is at least level 15, check current monster count. If monster count is less than 3, use CS as its more effective with small mobs - if (timedSkill === sdk.skills.LightningStrike && me.getSkill(sdk.skills.ChargedStrike, sdk.skills.subindex.SoftPoints) >= 15) { + if (timedSkill === sdk.skills.LightningStrike + && me.getSkill(sdk.skills.ChargedStrike, sdk.skills.subindex.SoftPoints) >= 15) { if (me.getMobCount(15, Coords_1.BlockBits.LineOfSight | Coords_1.BlockBits.Ranged | Coords_1.BlockBits.ClosedDoor | Coords_1.BlockBits.BlockWall) <= 3) { timedSkill = sdk.skills.ChargedStrike; } @@ -407,7 +457,7 @@ ClassAttack.doCast = function (unit, timedSkill, untimedSkill) { walk = (Skill.getRange(timedSkill) < 4 && unit.distance < 10 && !checkCollision(me, unit, sdk.collision.BlockWall) - ); + ); if (!Attack.getIntoPosition(unit, Skill.getRange(timedSkill), sdk.collision.Ranged, walk)) { return Attack.Result.FAILED; @@ -428,7 +478,7 @@ ClassAttack.doCast = function (unit, timedSkill, untimedSkill) { walk = (Skill.getRange(untimedSkill) < 4 && unit.distance < 10 && !checkCollision(me, unit, sdk.collision.BlockWall) - ); + ); if (!Attack.getIntoPosition(unit, Skill.getRange(untimedSkill), sdk.collision.Ranged, walk)) { return Attack.Result.FAILED; @@ -440,7 +490,9 @@ ClassAttack.doCast = function (unit, timedSkill, untimedSkill) { return Attack.Result.SUCCESS; } - Misc.poll(() => !me.skillDelay, 1000, 40); + Misc.poll(function () { + return !me.skillDelay; + }, 1000, 40); // Wait for Lightning Fury timeout while (this.lightFuryTick && getTickCount() - this.lightFuryTick < Config.LightningFuryDelay * 1000) { diff --git a/libs/SoloPlay/Functions/ConfigOverrides.js b/libs/SoloPlay/Functions/ConfigOverrides.js index f9a0d042..23a3b9e7 100644 --- a/libs/SoloPlay/Functions/ConfigOverrides.js +++ b/libs/SoloPlay/Functions/ConfigOverrides.js @@ -45,7 +45,9 @@ Config.init = function (notify) { if (Config.Silence && !Config.LocalChat.Enabled) { // Override the say function with print, so it just gets printed to console global._say = global.say; - global.say = (what) => console.log("Tryed to say: " + what); + global.say = function (what) { + console.log("Tryed to say: " + what); + }; } try { diff --git a/libs/SoloPlay/Functions/DynamicTiers.js b/libs/SoloPlay/Functions/DynamicTiers.js index 28c0a7f1..f8447f00 100644 --- a/libs/SoloPlay/Functions/DynamicTiers.js +++ b/libs/SoloPlay/Functions/DynamicTiers.js @@ -179,12 +179,22 @@ } chargedItems = chargedItems - .filter((v, i, a) => a.findIndex(el => ["skill", "level"].every(k => el[k] === v[k])) === i); + .filter(function (v, i, a) { + return a.findIndex(function (el) { + return ["skill", "level"].every(function (k) { + return el[k] === v[k]; + }); + }) === i; + }); if (skillId > 0) { chargedItems - .filter(check => check.skill === skillId) - .forEach(el => tier += el.level * 5); + .filter(function (check) { + return check.skill === skillId; + }) + .forEach(function (el) { + tier += el.level * 5; + }); } else { chargedItems.forEach(function (el) { if (el.skill === sdk.skills.Teleport) { @@ -376,7 +386,9 @@ } // pierce/mastery's not sure how I want to weight this so for now just its base value - buildInfo.usefulStats.forEach(stat => generalRating += item.getStatEx(stat)); + buildInfo.usefulStats.forEach(function (stat) { + generalRating += item.getStatEx(stat); + }); // start generalRating !item.isRuneword && (generalRating += (item.sockets * 10)); // priortize sockets @@ -388,7 +400,9 @@ sdk.stats.MagicBonus, sdk.stats.GoldBonus, sdk.stats.Defense, sdk.stats.ManaRecovery, sdk.stats.Strength, sdk.stats.Dexterity, sdk.stats.Vitality, sdk.stats.Energy, sdk.stats.MaxHp, sdk.stats.MaxMana, sdk.stats.ReplenishQuantity, sdk.stats.HpRegen, - ].reduce((acc, stat) => acc + item.getStatEx(stat) * _tierWeights.gen.get(stat), generalRating); + ].reduce(function (acc, stat) { + return acc + item.getStatEx(stat) * _tierWeights.gen.get(stat); + }, generalRating); }; const resistScore = function () { @@ -450,7 +464,9 @@ sdk.stats.AbsorbMagicPercent, sdk.stats.AbsorbColdPercent, sdk.stats.NormalDamageReduction, sdk.stats.DamageResist, sdk.stats.MagicDamageReduction, sdk.stats.MagicResist - ].reduce((acc, stat) => acc + item.getStatEx(stat) * _tierWeights.res.get(stat), resistRating)); + ].reduce(function (acc, stat) { + return acc + item.getStatEx(stat) * _tierWeights.res.get(stat); + }, resistRating)); }; const buildScore = function () { @@ -476,7 +492,9 @@ sdk.stats.ReplenishDurability, sdk.stats.IgnoreTargetDefense, sdk.stats.ToHit, sdk.stats.CrushingBlow, sdk.stats.OpenWounds, sdk.stats.DeadlyStrike, sdk.stats.LifeLeech, sdk.stats.ManaLeech, sdk.stats.DemonDamagePercent, sdk.stats.UndeadDamagePercent, - ].reduce((acc, stat) => acc + item.getStatEx(stat) * _tierWeights.gen.get(stat), meleeRating); + ].reduce(function (acc, stat) { + return acc + item.getStatEx(stat) * _tierWeights.gen.get(stat); + }, meleeRating); buildInfo.caster && (meleeRating /= 2); return meleeRating; @@ -490,7 +508,9 @@ [sdk.stats.AllSkills, -1], [sdk.stats.AddClassSkills, me.classid], [sdk.stats.AddSkillTab, buildInfo.tabSkills], - ].reduce((acc, [stat, subId]) => acc + item.getStatEx(stat, subId) * _tierWeights.skill.get(stat), 0); + ].reduce(function (acc, [stat, subId]) { + return acc + item.getStatEx(stat, subId) * _tierWeights.skill.get(stat); + }, 0); (!buildInfo.caster && item.getItemType() === "Weapon") && (skillsRating /= 4); const _misc = { wanted: 40, useful: 35 }; @@ -554,8 +574,14 @@ if (!ctcItems.length) return 0; ctcItems - .filter((v, i, a) => a.findIndex(el => ["ctcType", "skill"].every(k => el[k] === v[k])) === i) - .forEach(el => { + .filter(function (v, i, a) { + return a.findIndex(function (el) { + return ["ctcType", "skill"].every(function (k) { + return el[k] === v[k]; + }); + }) === i; + }) + .forEach(function (el) { if (!_tierWeights.ctc.has(el.skill)) return; ctcRating += (el.level * _tierWeights.ctc.get(el.skill) * _tierWeights.ctc.get(el.ctcType)); }); @@ -586,7 +612,9 @@ let tier = 0; Check.finalBuild().precastSkills - .forEach(skill => tier += item.getStatEx(sdk.stats.SingleSkill, skill) * 50); + .forEach(function (skill) { + tier += item.getStatEx(sdk.stats.SingleSkill, skill) * 50; + }); tier += item.getStatEx(sdk.stats.FCR) * 5; // add FCR tier += item.getStatEx(sdk.stats.FHR) * 3; // add faster hit recovery @@ -595,7 +623,9 @@ [sdk.stats.AllSkills, -1], [sdk.stats.AddClassSkills, me.classid], [sdk.stats.AddSkillTab, Check.finalBuild().tabSkills], - ].reduce((acc, [stat, subId]) => acc + item.getStatEx(stat, subId) * _tierWeights.skill.get(stat), tier); + ].reduce(function (acc, [stat, subId]) { + return acc + item.getStatEx(stat, subId) * _tierWeights.skill.get(stat); + }, tier); }; /** @@ -643,7 +673,9 @@ sdk.stats.FireResist, sdk.stats.LightResist, sdk.stats.ColdResist, sdk.stats.PoisonResist, sdk.stats.FHR, sdk.stats.FRW, sdk.stats.MagicBonus, sdk.stats.GoldBonus, sdk.stats.Defense, sdk.stats.Strength, sdk.stats.Dexterity, sdk.stats.Vitality, sdk.stats.Energy, - ].reduce((acc, stat) => acc + item.getStatEx(stat) * _tierWeights.charms.get(stat), charmRating); + ].reduce(function (acc, stat) { + return acc + item.getStatEx(stat) * _tierWeights.charms.get(stat); + }, charmRating); } return charmRating; }; diff --git a/libs/SoloPlay/Functions/ItemOverrides.js b/libs/SoloPlay/Functions/ItemOverrides.js index a9d92f62..d6a4b296 100644 --- a/libs/SoloPlay/Functions/ItemOverrides.js +++ b/libs/SoloPlay/Functions/ItemOverrides.js @@ -479,7 +479,9 @@ Item.removeItem = function (bodyLoc = -1, item = undefined) { /** * @param {ItemUnit} item */ -Item.hasSecondaryTier = (item) => Config.AutoEquip && me.expansion && NTIP.GetSecondaryTier(item) > 0; +Item.hasSecondaryTier = function (item) { + return Config.AutoEquip && me.expansion && NTIP.GetSecondaryTier(item) > 0; +}; /** * @param {ItemUnit} item @@ -702,7 +704,9 @@ Item.getMercEquipped = function (bodyLoc = -1) { if (mercenary) { let item = mercenary.getItemsEx() - .filter((item) => item.isEquipped && item.bodylocation === bodyLoc) + .filter(function (item) { + return item.isEquipped && item.bodylocation === bodyLoc; + }) .first(); if (item) { diff --git a/libs/SoloPlay/Functions/ItemPrototypes.js b/libs/SoloPlay/Functions/ItemPrototypes.js index 58e5de16..af5e83ba 100644 --- a/libs/SoloPlay/Functions/ItemPrototypes.js +++ b/libs/SoloPlay/Functions/ItemPrototypes.js @@ -3,132 +3,73 @@ * @author theBGuy * @desc Item related prototypes * @notes Unused, for now at least can't guarantee only units are attempting to use the prototypes -* me.getItem(sdk.items.runes.Ber).getQuantityOwned() throws an error if we don't have a ber rune * */ includeIfNotIncluded("SoloPlay/Functions/PrototypeOverrides.js"); -Unit.prototype.getQuantityOwned = function (skipSameItem = false) { - if (this === undefined || this.type !== sdk.unittype.Item) return 0; - - return me.getItemsEx() - .filter(check => - check.itemType === this.itemType - && (!skipSameItem || check.gid !== this.gid) - && check.classid === this.classid - && check.quality === this.quality - && check.sockets === this.sockets - && check.isInStorage - ).length; -}; - +/** @this {ItemUnit} */ Unit.prototype.hasTier = function () { - if (this === undefined || this.type !== sdk.unittype.Item) return false; + if (this.type !== sdk.unittype.Item) return false; return Config.AutoEquip && NTIP.GetTier(this) > 0; }; +/** @this {ItemUnit} */ Unit.prototype.hasSecondaryTier = function () { - if (this === undefined || this.type !== sdk.unittype.Item) return false; + if (this.type !== sdk.unittype.Item) return false; return Config.AutoEquip && NTIP.GetSecondaryTier(this) > 0 && me.expansion; }; +/** @this {ItemUnit} */ Unit.prototype.hasMercTier = function () { - if (this === undefined || this.type !== sdk.unittype.Item) return false; + if (this.type !== sdk.unittype.Item) return false; return Config.AutoEquip && NTIP.GetMercTier(this) > 0 && me.expansion; }; -Unit.prototype.bodyLocation = function () { - if (this === undefined || this.type !== sdk.unittype.Item) return []; - - let bodyLoc = (() => { - switch (true) { - case Item.shieldTypes.includes(this.itemType): - return sdk.body.LeftArm; - case this.itemType === sdk.items.type.Armor: - return sdk.body.Armor; - case this.itemType === sdk.items.type.Ring: - return [sdk.body.RingRight, sdk.body.RingLeft]; - case this.itemType === sdk.items.type.Amulet: - return sdk.body.Neck; - case this.itemType === sdk.items.type.Boots: - return sdk.body.Feet; - case this.itemType === sdk.items.type.Gloves: - return sdk.body.Gloves; - case this.itemType === sdk.items.type.Belt: - return sdk.body.Belt; - case Item.helmTypes.includes(this.itemType): - return sdk.body.Head; - case Item.weaponTypes.includes(this.itemType): - return me.barbarian ? [sdk.body.RightArm, sdk.body.LeftArm] : sdk.body.RightArm; - case [sdk.items.type.HandtoHand, sdk.items.type.AssassinClaw].includes(this.itemType): - return !Check.currentBuild().caster && me.assassin ? [sdk.body.RightArm, sdk.body.LeftArm] : sdk.body.RightArm; - default: - return false; - } - })(); - - return Array.isArray(bodyLoc) ? bodyLoc : [bodyLoc]; -}; - -Unit.prototype.secondaryBodyLocation = function () { - if (this === undefined || this.type !== sdk.unittype.Item) return []; - - let bodyLoc = (() => { - switch (true) { - case Item.shieldTypes.includes(this.itemType): - return sdk.body.LeftArmSecondary; - case Item.weaponTypes.includes(this.itemType): - return me.barbarian ? [sdk.body.RightArmSecondary, sdk.body.LeftArmSecondary] : sdk.body.RightArmSecondary; - case [sdk.items.type.HandtoHand, sdk.items.type.AssassinClaw].includes(this.itemType): - return !Check.currentBuild().caster && me.assassin ? [sdk.body.RightArmSecondary, sdk.body.LeftArmSecondary] : sdk.body.RightArmSecondary; - default: - return false; - } - })(); - - return Array.isArray(bodyLoc) ? bodyLoc : [bodyLoc]; +/** @this {ItemUnit} */ +Unit.prototype.canEquip = function () { + if (this.type !== sdk.unittype.Item || !this.identified) return false; + return me.charlvl >= this.lvlreq + && me.trueStr >= this.strreq + && me.trueDex >= this.dexreq; }; -Unit.prototype.mercBodyLocation = function () { - if (this === undefined || this.type !== sdk.unittype.Item) return []; - - let mercenary = me.getMercEx(); - if (!mercenary) return []; - - let bodyLoc = (() => { - switch (this.itemType) { - case sdk.items.type.Shield: - return (mercenary.classid === sdk.mercs.IronWolf ? sdk.body.LeftArm : []); - case sdk.items.type.Armor: - return sdk.body.Armor; - case sdk.items.type.Helm: - case sdk.items.type.Circlet: - return sdk.body.Head; - case sdk.items.type.PrimalHelm: - return (mercenary.classid === sdk.mercs.A5Barb ? sdk.body.Head : []); - case sdk.items.type.Bow: - return (mercenary.classid === sdk.mercs.Rogue ? sdk.body.RightArm : []); - case sdk.items.type.Spear: - case sdk.items.type.Polearm: - return (mercenary.classid === sdk.mercs.Guard ? sdk.body.RightArm : []); - case sdk.items.type.Sword: - return ([sdk.mercs.IronWolf, sdk.mercs.A5Barb].includes(mercenary.classid) ? sdk.body.RightArm : []); - default: - return false; - } - })(); +/** @this {ItemUnit} */ +Unit.prototype.bodyLocation = function () { + if (this.type !== sdk.unittype.Item || this.isInsertable) return []; + if (Item.shieldTypes.includes(this.itemType)) return [sdk.body.LeftArm]; + if (Item.helmTypes.includes(this.itemType)) return [sdk.body.Head]; + if (Item.weaponTypes.includes(this.itemType)) { + return me.barbarian && this.twoHanded && !this.strictlyTwoHanded + ? [sdk.body.RightArm, sdk.body.LeftArm] + : [sdk.body.RightArm]; + } - return Array.isArray(bodyLoc) ? bodyLoc : [bodyLoc]; -}; + switch (this.itemType) { + case sdk.items.type.Armor: + return [sdk.body.Armor]; + case sdk.items.type.Ring: + return [sdk.body.RingRight, sdk.body.RingLeft]; + case sdk.items.type.Amulet: + return [sdk.body.Neck]; + case sdk.items.type.Boots: + return [sdk.body.Feet]; + case sdk.items.type.Gloves: + return [sdk.body.Gloves]; + case sdk.items.type.Belt: + return [sdk.body.Belt]; + case sdk.items.type.AssassinClaw: + case sdk.items.type.HandtoHand: + return !Check.currentBuild().caster && me.assassin + ? [sdk.body.RightArm, sdk.body.LeftArm] : [sdk.body.RightArm]; + } -Unit.prototype.canEquip = function () { - if (this === undefined || this.type !== sdk.unittype.Item || !this.identified) return false; - return me.charlvl >= this.getStat(sdk.stats.LevelReq) && me.trueStr >= this.strreq && me.trueDex >= this.dexreq; + return []; }; +/** @this {ItemUnit} */ Unit.prototype.canEquipMerc = function (bodyLoc = -1) { - if (this === undefined || this.type !== sdk.unittype.Item || !this.identified || me.classic) return false; + if (this.type !== sdk.unittype.Item || !this.identified || me.classic) return false; let mercenary = me.getMercEx(); // dont have merc or he is dead @@ -145,78 +86,7 @@ Unit.prototype.canEquipMerc = function (bodyLoc = -1) { return true; }; -Unit.prototype.autoEquipCheck = function (checkCanEquip = true) { - if (!Config.AutoEquip) return true; - if (this === undefined || this.type !== sdk.unittype.Item) return false; - - let tier = NTIP.GetTier(this); - let bodyLoc = this.bodyLocation(); - - if (tier > 0 && bodyLoc.length) { - for (let i = 0; i < bodyLoc.length; i += 1) { - if (tier > me.equipped.get(bodyLoc[i]).tier && ((checkCanEquip ? this.canEquip() : true) || !this.identified)) { - if (this.twoHanded && !me.barbarian) { - if (tier < me.equipped.get(sdk.body.RightArm).tier + me.equipped.get(sdk.body.LeftArm).tier) return false; - } - - if (!me.barbarian && bodyLoc[i] === 5 && me.equipped.get(bodyLoc[i]).tier === -1) { - if (me.equipped.get(sdk.body.RightArm).twoHanded && tier < me.equipped.get(sdk.body.RightArm).tier) return false; - } - - return true; - } - } - } - - return false; -}; - -Unit.prototype.autoEquipCheckSecondary = function (checkCanEquip = true) { - if (!Config.AutoEquip) return true; - if (this === undefined || this.type !== sdk.unittype.Item || me.classic) return false; - - let tier = NTIP.GetSecondaryTier(this); - let bodyLoc = this.secondaryBodyLocation(); - - if (tier > 0 && bodyLoc.length) { - for (let i = 0; i < bodyLoc.length; i += 1) { - if (tier > me.equipped.get(bodyLoc[i]).secondaryTier && ((checkCanEquip ? this.canEquip() : true) || !this.identified)) { - if (this.twoHanded && !me.barbarian) { - if (tier < me.equipped.get(sdk.body.RightArmSecondary).secondaryTier + me.equipped.get(sdk.body.LeftArmSecondary).secondaryTier) return false; - } - - if (!me.barbarian && bodyLoc[i] === 12 && me.equipped.get(bodyLoc[i]).secondaryTier === -1) { - if (me.equipped.get(sdk.body.RightArmSecondary).twoHanded && tier < me.equipped.get(sdk.body.RightArmSecondary).secondaryTier) return false; - } - - return true; - } - } - } - - return false; -}; - -Unit.prototype.autoEquipCheckMerc = function (checkCanEquip = true) { - if (!Config.AutoEquip) return true; - if (this === undefined || this.type !== sdk.unittype.Item || me.classic || !me.getMercEx()) return false; - - let tier = NTIP.GetMercTier(this); - let bodyLoc = this.mercBodyLocation(); - - if (tier > 0 && bodyLoc.length) { - for (let i = 0; i < bodyLoc.length; i += 1) { - let oldTier = Item.getMercEquipped(bodyLoc[i]).tier; - - if (tier > oldTier && ((checkCanEquip ? this.canEquipMerc() : true) || !this.identified)) { - return true; - } - } - } - - return false; -}; - +/** @this {ItemUnit} */ Unit.prototype.shouldKeep = function () { if (this === undefined || this.type !== sdk.unittype.Item) return false; @@ -224,9 +94,14 @@ Unit.prototype.shouldKeep = function () { // only keep wanted items or cubing items (in rare cases where weapon being used is also a cubing wanted item) || (this.unique && Pickit.checkItem(this).result === 2) // or keep if item is worth selling - || (this.getItemCost(sdk.items.cost.ToSell) / (this.sizex * this.sizey) >= (!me.inTown && me.charlvl < 12 ? 5 : me.normal ? 50 : me.nightmare ? 500 : 1000))) { + || ( + (this.getItemCost(sdk.items.cost.ToSell) / (this.sizex * this.sizey)) + >= (!me.inTown && me.charlvl < 12 ? 5 : me.normal ? 50 : me.nightmare ? 500 : 1000) + )) { if ((Storage.Inventory.CanFit(this) && Storage.Inventory.MoveTo(this))) { - !AutoEquip.wanted(this) && NTIP.CheckItem(this, NTIP.CheckList) === Pickit.Result.UNWANTED && Town.sell.push(this); + if (!AutoEquip.wanted(this) && NTIP.CheckItem(this, NTIP.CheckList) === Pickit.Result.UNWANTED) { + Town.sell.push(this); + } return true; } } @@ -234,6 +109,7 @@ Unit.prototype.shouldKeep = function () { return false; }; +/** @this {ItemUnit} */ Unit.prototype.equipItem = function (bodyLoc = -1) { // can't equip if (this === undefined || this.type !== sdk.unittype.Item || !this.canEquip()) return false; @@ -259,7 +135,10 @@ Unit.prototype.equipItem = function (bodyLoc = -1) { if (cursorItem) { // rollback check let justEquipped = me.equipped.get(bodyLoc); - if (NTIP.GetTier(cursorItem) > justEquipped.tier && !this.questItem && !justEquipped.isRuneword/*Wierd bug with runewords that it'll fail to get correct item desc so don't attempt rollback*/) { + if (NTIP.GetTier(cursorItem) > justEquipped.tier + && !this.questItem + /*Wierd bug with runewords that it'll fail to get correct item desc so don't attempt rollback*/ + && !justEquipped.isRuneword) { console.debug("ROLLING BACK TO OLD ITEM BECAUSE IT WAS BETTER"); console.debug("OldItem: " + NTIP.GetTier(cursorItem) + " Just Equipped Item: " + me.equipped.get(bodyLoc).tier); clickItemAndWait(sdk.clicktypes.click.item.Left, bodyLoc); diff --git a/libs/SoloPlay/Functions/NTIPOverrides.js b/libs/SoloPlay/Functions/NTIPOverrides.js index 21d8f215..f4e84370 100644 --- a/libs/SoloPlay/Functions/NTIPOverrides.js +++ b/libs/SoloPlay/Functions/NTIPOverrides.js @@ -54,7 +54,7 @@ NTIP.generateTierFunc = function (tierType) { return /** @param {ItemUnit} item */ function (item) { let tier = -1; - const updateTier = (wanted) => { + const updateTier = function (wanted) { const tmpTier = wanted[tierType](item); if (tier < tmpTier) { diff --git a/libs/SoloPlay/Functions/Polyfills.js b/libs/SoloPlay/Functions/Polyfills.js index 6d7d328c..a9ae7597 100644 --- a/libs/SoloPlay/Functions/Polyfills.js +++ b/libs/SoloPlay/Functions/Polyfills.js @@ -160,29 +160,6 @@ if (!Object.is) { }); } -// if (!Object.fromEntries) { -// Object.defineProperty(Object, 'fromEntries', { -// value: function (entries) { -// if (!entries /*|| !entries[Symbol.iterator]*/) { throw new Error('Object.fromEntries() requires a single iterable argument'); } - -// const o = {}; - -// Object.keys(entries).forEach((key) => { -// const [k, v] = entries[key]; - -// o[k] = v; -// }); - -// return o; -// }, -// }); -// } - -// // filter an object -// Object.filter = function (obj, predicate) { -// return Object.fromEntries(Object.entries(obj).filter(predicate)); -// }; - // https://stackoverflow.com/questions/7837456/how-to-compare-arrays-in-javascript if (!Array.prototype.equals) { // Warn if overriding existing method diff --git a/libs/SoloPlay/Threads/ToolsThread.js b/libs/SoloPlay/Threads/ToolsThread.js index 4b80d43a..81d240a9 100644 --- a/libs/SoloPlay/Threads/ToolsThread.js +++ b/libs/SoloPlay/Threads/ToolsThread.js @@ -397,7 +397,7 @@ function main () { } itemString = ( "ÿc4MaxQuantity: ÿc0" + NTIP.getMaxQuantity(itemToCheck) - + " | ÿc4ItemsOwned: ÿc0" + Item.getQuantityOwned(itemToCheck) + + " | ÿc4ItemsOwned: ÿc0" + me.getOwned(itemToCheck).length + " | ÿc4Tier: ÿc0" + NTIP.GetTier(itemToCheck) + (special ? special : "") + " | ÿc4SecondaryTier: ÿc0" + NTIP.GetSecondaryTier(itemToCheck) + " | ÿc4MercTier: ÿc0" + NTIP.GetMercTier(itemToCheck) + "\n" @@ -590,7 +590,7 @@ function main () { } switch (msg) { - case "deleteAndRemake": + case "remake": Developer.testingMode.enabled && (quitFlag = true); break; @@ -769,8 +769,9 @@ function main () { (me.staminaPercent <= 20 || me.walking) && drinkSpecialPotion(sdk.items.StaminaPotion); me.getState(sdk.states.Poison) && drinkSpecialPotion(sdk.items.AntidotePotion); - [sdk.states.Frozen, sdk.states.FrozenSolid] - .some(state => me.getState(state)) && drinkSpecialPotion(sdk.items.ThawingPotion); + if (me.getState(sdk.states.Frozen) || me.getState(sdk.states.FrozenSolid)) { + drinkSpecialPotion(sdk.items.ThawingPotion); + } if (Config.ManaChicken > 0 && me.mpPercent <= Config.ManaChicken && !me.inTown) { if (!Developer.hideChickens) { From 2dbe591f4deb1cd0e9e4c7a86d224226828aad53 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Sat, 15 Jul 2023 18:54:26 -0400 Subject: [PATCH 216/263] Update SoloIndex.js - stop running cows past 50 in nightmare. It tends to waste too much time --- libs/SoloPlay/Tools/SoloIndex.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libs/SoloPlay/Tools/SoloIndex.js b/libs/SoloPlay/Tools/SoloIndex.js index c69ab454..911433b4 100644 --- a/libs/SoloPlay/Tools/SoloIndex.js +++ b/libs/SoloPlay/Tools/SoloIndex.js @@ -273,7 +273,7 @@ const SoloIndex = { case sdk.player.class.Druid: return me.nightmare && me.charlvl > 65; case sdk.player.class.Sorceress: - return me.nightmare && me.expansion && me.charlvl > 62; + return me.nightmare && me.expansion && me.charlvl > 50; } return false; }, From 41b30bec8208027ef5caa286a4afa50f1533550c Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Sun, 16 Jul 2023 16:46:37 -0400 Subject: [PATCH 217/263] Update TownOverrides.js - handle special case for act 2, if we need to stack potions and buy normal potions we can accomplish both at Lysander. --- libs/SoloPlay/Functions/TownOverrides.js | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/libs/SoloPlay/Functions/TownOverrides.js b/libs/SoloPlay/Functions/TownOverrides.js index f7fcf3de..2b3c460e 100644 --- a/libs/SoloPlay/Functions/TownOverrides.js +++ b/libs/SoloPlay/Functions/TownOverrides.js @@ -156,10 +156,19 @@ Town.initNPC = function (task = "", reason = "undefined") { } } - if (task === "Heal" && me.act === 2) { - // lets see if we are closer to Atma than Fara - if (Town.getDistance(NPC.Atma) < Town.getDistance(NPC.Fara)) { - wantedNpc = NPC.Atma; + if (me.act === 2) { + if (task === "Heal") { + // lets see if we are closer to Atma than Fara + if (Town.getDistance(NPC.Atma) < Town.getDistance(NPC.Fara)) { + wantedNpc = NPC.Atma; + } + } else if (reason === "buyPotions") { + if (Town.getDistance(NPC.Drognan) > 10) { + let _needStack = CharData.pots.get("thawing").need() || CharData.pots.get("antidote").need(); + if (_needStack) { + wantedNpc = NPC.Lysander; + } + } } } From 14adad86aaa345d51e3666ac332b2a9542175a2a Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Tue, 18 Jul 2023 10:59:15 -0400 Subject: [PATCH 218/263] Update SoloIndex.js - run nith in normal after lvl 30 --- libs/SoloPlay/Tools/SoloIndex.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/libs/SoloPlay/Tools/SoloIndex.js b/libs/SoloPlay/Tools/SoloIndex.js index 911433b4..33901b48 100644 --- a/libs/SoloPlay/Tools/SoloIndex.js +++ b/libs/SoloPlay/Tools/SoloIndex.js @@ -739,7 +739,10 @@ const SoloIndex = { return (me.expansion && me.accessToAct(5) && me.anya); }, skipIf: function () { - return !me.nightmare || !Pather.canTeleport(); + if (!Pather.canTeleport()) return true; + if (me.normal && me.charlvl < 30) return true; + // for now only run in norm/nightmare + return me.hell; }, shouldRun: function () { if (!this.preReq() || this.skipIf()) return false; From 4a36ed231ae37a45e3a65ee928f6cad4804f3d8c Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Tue, 18 Jul 2023 19:32:30 -0400 Subject: [PATCH 219/263] Update DynamicTiers.js - add mana after kill stat - add hp after kill stat --- libs/SoloPlay/Functions/DynamicTiers.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/libs/SoloPlay/Functions/DynamicTiers.js b/libs/SoloPlay/Functions/DynamicTiers.js index f8447f00..3931c3bf 100644 --- a/libs/SoloPlay/Functions/DynamicTiers.js +++ b/libs/SoloPlay/Functions/DynamicTiers.js @@ -262,7 +262,9 @@ [sdk.stats.OpenWounds, 1], [sdk.stats.DeadlyStrike, 1.5], [sdk.stats.LifeLeech, 4], + [sdk.stats.HealAfterKill, 1], [sdk.stats.ManaLeech, 2], + [sdk.stats.ManaAfterKill, 1], [sdk.stats.DemonDamagePercent, 0.5], [sdk.stats.UndeadDamagePercent, 0.5], [sdk.stats.ReplenishDurability, 15], From 9cd8156ef924ff271dd2ee411303f81d0cfb0694 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Tue, 18 Jul 2023 19:36:15 -0400 Subject: [PATCH 220/263] Update AttackOverrides.js - prebuild all possible spots for getting into position instead of only farthest ones first. The old way disregarded if we were already close enough to be in position and was detrimental to walking casters --- libs/SoloPlay/Functions/AttackOverrides.js | 103 +++++++++------------ 1 file changed, 45 insertions(+), 58 deletions(-) diff --git a/libs/SoloPlay/Functions/AttackOverrides.js b/libs/SoloPlay/Functions/AttackOverrides.js index 7073732d..22bee0d6 100644 --- a/libs/SoloPlay/Functions/AttackOverrides.js +++ b/libs/SoloPlay/Functions/AttackOverrides.js @@ -1787,12 +1787,13 @@ Attack.getIntoPosition = function (unit = false, distance = 0, coll = 0, walk = let potentialSpot = { x: null, y: null }; let fullDistance = distance; + const coords = []; + const nearMobs = getUnits(sdk.unittype.Monster) + .filter(function (m) { + return m.getStat(sdk.stats.Alignment) !== 2; + }); for (let n = 0; n < 3; n += 1) { - const coords = []; - const nearMobs = getUnits(sdk.unittype.Monster) - .filter(function (m) { - return m.getStat(sdk.stats.Alignment) !== 2; - }); + const temp = []; (n > 0) && (distance -= Math.floor(fullDistance / 3 - 1)); for (let currAngle of angles) { @@ -1804,15 +1805,16 @@ Attack.getIntoPosition = function (unit = false, distance = 0, coll = 0, walk = if (force && [cx, cy].distance < distance) continue; if (Pather.checkSpot(cx, cy, sdk.collision.BlockWall, false)) { coords.push({ x: cx, y: cy }); + temp.push({ x: cx, y: cy }); } } - if (!coords.length) continue; + if (!temp.length) continue; coords.sort(Sort.units); // If one of the valid positions is a position I am at already - and we aren't trying to force a new spot if (!force) { - for (let coord of coords) { + for (let coord of temp) { if ((getDistance(me, coord.x, coord.y) < 1 && !CollMap.checkColl(unit, { x: coord.x, y: coord.y }, _coll, 1)) || (getDistance(me, coord.x, coord.y) <= 5 && me.getMobCount(6) > 2)) { @@ -1820,62 +1822,47 @@ Attack.getIntoPosition = function (unit = false, distance = 0, coll = 0, walk = } } } + } + for (let coord of coords) { + // Valid position found - no collision between the spot and the unit + if (!CollMap.checkColl({ x: coord.x, y: coord.y }, unit, coll, 1)) { + const currCount = nearMobs + .filter(function (m) { + return getDistance(coord.x, coord.y, m.x, m.y) < 8; + }).length; + + // this might be a valid spot but also check the mob count at that node + if (caster) { + potentialSpot.x === null && (potentialSpot = { x: coord.x, y: coord.y }); + + if (currCount < count) { + count = currCount; + potentialSpot = { x: coord.x, y: coord.y }; + } - for (let coord of coords) { - // Valid position found - no collision between the spot and the unit - if (!CollMap.checkColl({ x: coord.x, y: coord.y }, unit, coll, 1)) { - Developer.debugging.pathing && console.time("countMobs"); - const currCount = nearMobs - .filter(function (m) { - return getDistance(coord.x, coord.y, m.x, m.y) < 8; - }).length; - Developer.debugging.pathing && console.timeEnd("countMobs"); - - // this might be a valid spot but also check the mob count at that node - if (caster) { - potentialSpot.x === null && (potentialSpot = { x: coord.x, y: coord.y }); - - if (currCount < count) { - count = currCount; - potentialSpot = { x: coord.x, y: coord.y }; - if (Developer.debugging.pathing) { - console.log( - sdk.colors.Blue + "CheckedSpot" + sdk.colors.Yellow + ": x: " + coord.x - + " y: " + coord.y + " mob amount: " + sdk.colors.NeonGreen + count - ); - } - } - - if (currCount > minMonCount) { - if (Developer.debugging.pathing) { - console.log( - sdk.colors.Red + "Not Zero, check next: currCount: " - + sdk.colors.NeonGreen + " " + currCount - ); - } - continue; - } + if (currCount > minMonCount) { + continue; } + } - // I am already in my optimal position - if (coord.distance < 3) return true; + // I am already in my optimal position + if (coord.distance < 3) return true; - // we are actually able to walk to where we want to go, hopefully prevent wall hugging - if (walk && (coord.distance < 6 || !CollMap.checkColl(me, unit, _coll))) { - Pather.walkTo(coord.x, coord.y, 2); - } else { - Pather.move(coord, _pathSettings); - } - if (Developer.debugging.pathing) { - console.log( - sdk.colors.Purple + "SecondCheck :: " + sdk.colors.Yellow - + "Moving to: x: " + coord.x + " y: " + coord.y - + " mob amount: " + sdk.colors.NeonGreen + currCount - ); - console.timeEnd("getIntoPosition"); - } - return true; + // we are actually able to walk to where we want to go, hopefully prevent wall hugging + if (walk && (coord.distance < 6 || !CollMap.checkColl(me, unit, _coll))) { + Pather.walkTo(coord.x, coord.y, 2); + } else { + Pather.move(coord, _pathSettings); } + if (Developer.debugging.pathing) { + console.log( + sdk.colors.Purple + "SecondCheck :: " + sdk.colors.Yellow + + "Moving to: x: " + coord.x + " y: " + coord.y + + " mob amount: " + sdk.colors.NeonGreen + currCount + ); + console.timeEnd("getIntoPosition"); + } + return true; } } From 1b31e95bddac6857cccbab6c6d24fb0d7438551b Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Wed, 19 Jul 2023 11:11:24 -0400 Subject: [PATCH 221/263] Update OOGOverrides.js - fix online hc character remake --- libs/SoloPlay/Tools/OOGOverrides.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/libs/SoloPlay/Tools/OOGOverrides.js b/libs/SoloPlay/Tools/OOGOverrides.js index fb3180c6..f2ae7dc2 100644 --- a/libs/SoloPlay/Tools/OOGOverrides.js +++ b/libs/SoloPlay/Tools/OOGOverrides.js @@ -1361,7 +1361,11 @@ const LocationAction = { } } - Starter.LocationEvents.openCreateGameWindow(); + if (Starter.deadCheck) { + Controls.LobbyQuit.click(); + } else { + Starter.LocationEvents.openCreateGameWindow(); + } } ], [ From 68698580d6cc66d60e0d0d50ffe68de7079798c3 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Thu, 20 Jul 2023 17:13:07 -0400 Subject: [PATCH 222/263] Update NTIPOverrides.js - fix line to be the actual line number of the nip file rather than the index of the line in the list - add index property to keep track of that value - remove finalcharm case as it's no longer used --- libs/SoloPlay/Functions/NTIPOverrides.js | 25 ++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/libs/SoloPlay/Functions/NTIPOverrides.js b/libs/SoloPlay/Functions/NTIPOverrides.js index f4e84370..893e8005 100644 --- a/libs/SoloPlay/Functions/NTIPOverrides.js +++ b/libs/SoloPlay/Functions/NTIPOverrides.js @@ -519,6 +519,11 @@ NTIP.CheckItem = function (item, entryList, verbose = false) { return verbose ? rval : result; }; +/** + * @param {string} filepath + * @param {boolean} notify + * @returns {boolean} + */ NTIP.OpenFile = function (filepath, notify) { if (!FileTools.exists(filepath)) { if (notify) { @@ -529,7 +534,7 @@ NTIP.OpenFile = function (filepath, notify) { } let nipfile, tick = getTickCount(), entries = 0; - let filename = filepath.substring(filepath.lastIndexOf("/") + 1, filepath.length); + const filename = filepath.substring(filepath.lastIndexOf("/") + 1, filepath.length); try { nipfile = File.open(filepath, 0); @@ -540,14 +545,16 @@ NTIP.OpenFile = function (filepath, notify) { if (!nipfile) return false; let lines = nipfile.readAllLines(); + let lineNumber = 1; nipfile.close(); /** - * @note removed tierd check for normal pick files as soloplay handles that + * @note removed tier'd check for normal pick files as soloplay handles that */ for (let entry of lines) { const info = { - line: NTIP.CheckList.list.length + 1, + index: NTIP.CheckList.list.length + 1, + line: lineNumber, file: filename, string: entry }; @@ -558,6 +565,7 @@ NTIP.OpenFile = function (filepath, notify) { NTIP.CheckList.add(line, info); entries++; } + lineNumber++; } if (notify) { @@ -793,6 +801,7 @@ NTIP.ParseLineInt = function (input, info) { p_end = p_section[i].indexOf("]"); p_keyword = p_section[i].substring(0, p_end); + /** @type {string} */ let keyword = p_keyword.toLowerCase(); switch (keyword) { @@ -804,14 +813,6 @@ NTIP.ParseLineInt = function (input, info) { p_result[2].InvoQuantity = quantity; } - break; - case "finalcharm": - let check = Boolean(p_section[i].split("==")[1].match(/(\b(?!split\b)[^ $]+\b)/g)); - - if (!isNaN(check)) { - p_result[2].FinalCharm = check; - } - break; case "maxquantity": value = Number(p_section[i].split("==")[1].match(/\d+/g)); @@ -826,7 +827,7 @@ NTIP.ParseLineInt = function (input, info) { case "charmtier": case "merctier": try { - p_result[2][keyword.charAt(0).toUpperCase() + keyword.slice(1)] = (new Function("return function(item) { return " + p_section[i].split("==")[1] + ";}")).call(null); // generate function out of + p_result[2][keyword.capitalize()] = (new Function("return function(item) { return " + p_section[i].split("==")[1] + ";}")).call(null); // generate function out of } catch (e) { Misc.errorReport("ÿc1Pickit Tier (" + keyword + ") error! Line # ÿc2" + info.line + " ÿc1Entry: ÿc0" + info.string + " (" + info.file + ") Error message: " + e.message); } From d746e1bcdf41d8e1bf82cb42ee531a4cfb4de80d Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Sat, 22 Jul 2023 12:20:42 -0400 Subject: [PATCH 223/263] Update Fury.js - fix keep line, fury doesn't have +all skills --- libs/SoloPlay/BuildFiles/Runewords/Fury.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libs/SoloPlay/BuildFiles/Runewords/Fury.js b/libs/SoloPlay/BuildFiles/Runewords/Fury.js index 846c1a62..ef66b3c7 100644 --- a/libs/SoloPlay/BuildFiles/Runewords/Fury.js +++ b/libs/SoloPlay/BuildFiles/Runewords/Fury.js @@ -10,5 +10,5 @@ Config.Runewords.push([Runeword.Fury, "suwayyah"]); - Config.KeepRunewords.push("[type] == assassinclaw # [itemallskills] == 2 && [ias] == 40 && [itemdeadlystrike] == 33"); + Config.KeepRunewords.push("[type] == assassinclaw # [lifeleech] >= 6 && [ias] == 40 && [itemdeadlystrike] == 33"); })(); From 3778e892281b43f21c68b0632c5c800146a12632 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Thu, 17 Aug 2023 16:35:50 -0400 Subject: [PATCH 224/263] Update PrecastOverrides.js - use new setup from the core --- libs/SoloPlay/Functions/PrecastOverrides.js | 38 ++++++++++++++------- 1 file changed, 25 insertions(+), 13 deletions(-) diff --git a/libs/SoloPlay/Functions/PrecastOverrides.js b/libs/SoloPlay/Functions/PrecastOverrides.js index 8c35eadf..b8ee23fd 100644 --- a/libs/SoloPlay/Functions/PrecastOverrides.js +++ b/libs/SoloPlay/Functions/PrecastOverrides.js @@ -13,7 +13,7 @@ Precast.enabled = true; // Can't be on a weapon due to consistent switching but // Clay Goldem from Stone RW, Iron Golem from Metalgrid, Posion Creeper from Carrior Wind ring, Oak, HoW, or SoB from wisp -new Overrides.Override(Precast, Precast.doPrecast, function (orignal, force) { +new Overrides.Override(Precast, Precast.doPrecast, function (orignal, force, partial) { if (!Precast.enabled) return false; switch (me.classid) { @@ -21,32 +21,44 @@ new Overrides.Override(Precast, Precast.doPrecast, function (orignal, force) { // Force BO 30 seconds before it expires if (this.haveCTA > -1) { let forceBo = (force - || (getTickCount() - this.skills.battleOrders.tick >= this.skills.battleOrders.duration - 30000) + || Precast.skills.get(sdk.skills.BattleOrders).remaining() < 25 || !me.getState(sdk.states.BattleCommand)); forceBo && this.precastCTA(forceBo); } - if (Skill.canUse(sdk.skills.HolyShield) - && Math.round(Skill.getManaCost(sdk.skills.HolyShield) * 100 / me.mpmax) < 35 - && (!me.getState(sdk.states.HolyShield) || force)) { - Precast.cast(sdk.skills.HolyShield); + if (Precast.skills.get(sdk.skills.HolyShield).needToCast(force || partial, 15) + && Math.round(Skill.getManaCost(sdk.skills.HolyShield) * 100 / me.mpmax) < 35) { + if (Precast.skills.get(sdk.skills.HolyShield).needToCast(force || partial, 15)) { + let _wearingShield = me.getItem(-1, sdk.items.mode.Equipped, Precast.shieldGid); + if (!_wearingShield) { + // try once to locate, in case we just swapped + _wearingShield = me.usingShield(); + Precast.shieldGid = _wearingShield ? _wearingShield.gid : 0; + if (!_wearingShield) { + break; + } + } + if (Precast.shieldGid > 0) { + Precast.cast(sdk.skills.HolyShield); + } + } } break; case sdk.player.class.Barbarian: - let needShout = (Skill.canUse(sdk.skills.Shout) && (force || !me.getState(sdk.states.Shout))); - let needBo = (Skill.canUse(sdk.skills.BattleOrders) && (force || !me.getState(sdk.states.BattleOrders))); - let needBc = (Skill.canUse(sdk.skills.BattleCommand) && (force || !me.getState(sdk.states.BattleCommand))); + let needShout = (Precast.skills.get(sdk.skills.Shout).needToCast(force || partial)); + let needBo = (Precast.skills.get(sdk.skills.BattleOrders).needToCast(force || partial)); + let needBc = (Precast.skills.get(sdk.skills.BattleCommand).needToCast(force || partial)); if (needShout || needBo || needBc) { let primary = Attack.getPrimarySlot(); let { x, y } = me; (needBo || needBc) && me.switchWeapons(this.getBetterSlot(sdk.skills.BattleOrders)); - needBc && Precast.cast(sdk.skills.BattleCommand, x, y, true); - needBo && Precast.cast(sdk.skills.BattleOrders, x, y, true); - needShout && Precast.cast(sdk.skills.Shout, x, y, true); - needBc && Precast.cast(sdk.skills.BattleCommand, x, y, true); + needBc && Precast.cast(sdk.skills.BattleCommand, x, y, false); + needBo && Precast.cast(sdk.skills.BattleOrders, x, y, false); + needShout && Precast.cast(sdk.skills.Shout, x, y, false); + needBc && Precast.cast(sdk.skills.BattleCommand, x, y, false); me.weaponswitch !== primary && me.switchWeapons(primary); } From 7e1dc26ed0ad89f540c602f3b91b76f02cebc20e Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Sat, 2 Sep 2023 17:53:47 -0400 Subject: [PATCH 225/263] Delete default.dbj - soloplay has been added to core kolbots `default.dbj` file so we no longer need to replace it --- default.dbj | 288 ---------------------------------------------------- 1 file changed, 288 deletions(-) delete mode 100644 default.dbj diff --git a/default.dbj b/default.dbj deleted file mode 100644 index 2e1800ab..00000000 --- a/default.dbj +++ /dev/null @@ -1,288 +0,0 @@ -/** -* @filename default.dbj -* @author kolton, theBGuy -* @desc gets executed upon gamejoin, main thread for bot -* -* @typedef {import("./sdk/globals")} -*/ -js_strict(true); -include("critical.js"); // required - -// globals needed for core gameplay -includeCoreLibs(); - -// system libs -includeSystemLibs(); -include("systems/mulelogger/MuleLogger.js"); -include("systems/gameaction/GameAction.js"); - -// main thread specific -const LocalChat = require("./libs/modules/LocalChat"); - -function main () { - D2Bot.init(); // Get D2Bot# handle - D2Bot.ingame(); - - (function (global, original) { - global.load = function (...args) { - original.apply(this, args); - delay(500); - }; - })([].filter.constructor("return this")(), load); - - // wait until game is ready - while (!me.gameReady) { - delay(50); - } - - clearAllEvents(); // remove any event listeners from game crash - - // load heartbeat if it isn't already running - let _heartbeat = getScript("threads/heartbeat.js"); - if (!_heartbeat || !_heartbeat.running) { - load("threads/heartbeat.js"); - } - - // SoloPlay runs in it's own thread - check to ensure it exists in the files - if (getScript("D2BotSoloPlay.dbj") && FileTools.exists("libs/SoloPlay/SoloPlay.js")) { - load("libs/SoloPlay/SoloPlay.js"); - getScript(true).stop(); // kill this thread - return true; - } - - // map mode runs in it's own thread - if (getScript("d2botmap.dbj")) { - load("libs/manualplay/threads/mapthread.js"); - getScript(true).stop(); // kill this thread - return true; - } - - // MuleLogger handler - if (MuleLogger.inGameCheck()) return true; - - // don't load default for dropper/mules - if (getScript("D2BotDropper.dbj") || getScript("D2BotMule.dbj")) { - if (FileTools.exists("libs/systems/dropper/ItemDB.js")) { - include("systems/dropper/ItemDB.js"); - } - load("threads/AreaWatcher.js"); - - while (me.ingame) { - delay(10000); - } - return true; - } - - let sojPause; - let sojCounter = 0; - let startTime = getTickCount(); - - this.scriptEvent = function (msg) { - if (msg === "quit") return; - if (typeof msg === "string" && msg === "soj") { - sojPause = true; - sojCounter = 0; - } - }; - - this.copyDataEvent = function (mode, msg) { - // "Mule Profile" option from D2Bot# - if (mode === 0 && msg === "mule") { - if (AutoMule.getInfo() && AutoMule.getInfo().hasOwnProperty("muleInfo")) { - if (AutoMule.getMuleItems().length > 0) { - D2Bot.printToConsole("Mule triggered"); - scriptBroadcast("mule"); - scriptBroadcast("quit"); - } else { - D2Bot.printToConsole("No items to mule."); - } - } else { - D2Bot.printToConsole("Profile not enabled for muling."); - } - } - - // getProfile - if (mode === 1638) { - msg = JSON.parse(msg); - - if (msg.Tag) { - GameAction.init(msg.Tag); - } - } - }; - - // Initialize libs - load config variables, build pickit list, attacks, containers and cubing and runeword recipes - Config.init(true); - Pickit.init(true); - Attack.init(); - Storage.Init(); - CraftingSystem.buildLists(); - Runewords.init(); - Cubing.init(); - LocalChat.init(); - - // Load event listeners - addEventListener("scriptmsg", this.scriptEvent); - addEventListener("copydata", this.copyDataEvent); - - // GameAction/AutoMule/TorchSystem/Gambling/Crafting handler - if (GameAction.inGameCheck() - || AutoMule.inGameCheck() - || TorchSystem.inGameCheck() - || Gambling.inGameCheck() - || CraftingSystem.inGameCheck()) { - return true; - } - - me.maxgametime = Config.MaxGameTime * 1000; - let stats = DataFile.getStats(); - - // Check for experience decrease -> log death. Skip report if life chicken is disabled. - if (stats.name === me.name && me.getStat(sdk.stats.Experience) < stats.experience && Config.LifeChicken > 0) { - D2Bot.printToConsole( - "You died in last game. | Area :: " + stats.lastArea + "\n" - + "Experience decreased by " + (stats.experience - me.getStat(sdk.stats.Experience)), - sdk.colors.D2Bot.Red - ); - DataFile.updateStats("deaths"); - D2Bot.updateDeaths(); - } - - DataFile.updateStats(["experience", "name"]); - - // Load threads - load("threads/ToolsThread.js"); - if (Config.TownCheck || Config.TownHP > 0 || Config.TownMP > 0) { - load("threads/TownChicken.js"); - } - - if (Config.DebugMode.Stack && FileTools.exists("libs/modules/Guard.js")) { - require("libs/modules/Guard"); - } - - if (Config.PublicMode) { - Config.PublicMode === true ? require("libs/modules/SimpleParty") : load("threads/Party.js"); - } - - Config.AntiHostile && load("threads/AntiHostile.js"); - - if (Config.FastPick) { - print("ÿc2Fast pickit active."); - addEventListener("itemaction", Pickit.itemEvent); - } - - // One time maintenance - check cursor, get corpse, clear leftover items, pick items in case anything important was dropped - if (!Scripts.UserAddon && !Scripts.Test) { - // main checks - Cubing.cursorCheck(); - Town.getCorpse(); - Town.clearBelt(); - Pather.init(); // initialize wp data - - let { x, y } = me; - Config.ClearInvOnStart && Town.clearInventory(); - [x, y].distance > 3 && Pather.moveTo(x, y); - Pickit.pickItems(); - me.hpPercent <= 10 && Town.heal() && me.cancelUIFlags(); - - if (Config.DebugMode.Memory) { - delay(2000); - getThreads() - .sort(function (a, b) { - return b.memory - a.memory; - }) - .forEach(function (thread) { - console.debug(thread); - }); - } - } - - me.automap = Config.AutoMap; - - // Next game = drop keys - TorchSystem.keyCheck() && scriptBroadcast("torch"); - - // Auto skill and stat - if (Config.AutoSkill.Enabled && include("core/Auto/AutoSkill.js")) { - AutoSkill.init(Config.AutoSkill.Build, Config.AutoSkill.Save); - } - - if (Config.AutoStat.Enabled && include("core/Auto/AutoStat.js")) { - AutoStat.init(Config.AutoStat.Build, Config.AutoStat.Save, Config.AutoStat.BlockChance, Config.AutoStat.UseBulk); - } - - // offline - !me.realm && D2Bot.updateRuns(); - - // Go - Loader.init(); - - if (Config.MinGameTime && getTickCount() - startTime < Config.MinGameTime * 1000) { - try { - Town.goToTown(); - - while (getTickCount() - startTime < Config.MinGameTime * 1000) { - me.overhead( - "Stalling for " - + Math.round(((startTime + Time.seconds(Config.MinGameTime)) - getTickCount()) / 1000) + " Seconds" - ); - delay(1000); - } - } catch (e1) { - print(e1); - } - } - - DataFile.updateStats("gold"); - - if (sojPause) { - try { - Town.doChores(); - me.maxgametime = 0; - - while (sojCounter < Config.SoJWaitTime) { - me.overhead("Waiting for SoJ sales... " + (Config.SoJWaitTime - sojCounter) + " min"); - delay(6e4); - - sojCounter += 1; - } - } catch (e2) { - print(e2); - } - } - - if (Config.LastMessage) { - switch (typeof Config.LastMessage) { - case "string": - say(Config.LastMessage.replace("$nextgame", DataFile.getStats().nextGame, "i")); - - break; - case "object": - for (let i = 0; i < Config.LastMessage.length; i += 1) { - say(Config.LastMessage[i].replace("$nextgame", DataFile.getStats().nextGame, "i")); - } - - break; - } - } - - removeEventListener("scriptmsg", this.scriptEvent); - - AutoMule.muleCheck() && scriptBroadcast("mule"); - CraftingSystem.checkFullSets() && scriptBroadcast("crafting"); - TorchSystem.keyCheck() && scriptBroadcast("torch"); - - // Anni handler. Mule Anni if it's in unlocked space and profile is set to mule torch/anni. - let anni = me.findItem(sdk.items.SmallCharm, sdk.items.mode.inStorage, -1, sdk.items.quality.Unique); - - if (anni && !Storage.Inventory.IsLocked(anni, Config.Inventory) - && AutoMule.getInfo() && AutoMule.getInfo().hasOwnProperty("torchMuleInfo")) { - scriptBroadcast("muleAnni"); - } - - removeEventListener("copydata", this.copyDataEvent); - - scriptBroadcast("quit"); - - return true; -} From bdd8cc911bdd05519074b382d5f96f8504dbeaaf Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Thu, 7 Sep 2023 17:59:31 -0400 Subject: [PATCH 226/263] Update OOGOverrides.js - fix hardcoded timeout for `Login Error Delay` - add `OkCenteredErrorPopUp` location - change `deleteAndRemake` -> `remake` --- libs/SoloPlay/Tools/OOGOverrides.js | 48 +++++++++++++++++++++++++++-- 1 file changed, 46 insertions(+), 2 deletions(-) diff --git a/libs/SoloPlay/Tools/OOGOverrides.js b/libs/SoloPlay/Tools/OOGOverrides.js index f2ae7dc2..ce1bae0d 100644 --- a/libs/SoloPlay/Tools/OOGOverrides.js +++ b/libs/SoloPlay/Tools/OOGOverrides.js @@ -38,6 +38,17 @@ const LocationAction = { }; })(); + /** + * @param {Control} control + * @returns {string} + */ + const parseControlText = (control) => { + if (!control) return ""; + let text = control.getText(); + if (!text || !text.length) return ""; + return text.join(" "); + }; + new Overrides.Override(Starter, Starter.receiveCopyData, function (orignal, mode, msg) { switch (mode) { case 1: // Join Info @@ -168,7 +179,7 @@ const LocationAction = { "\n" + sdk.colors.Red + "//-----------DataDump End-----------//" ); - } else if (msg === "deleteAndRemake") { + } else if (msg === "remake") { Starter.deadCheck = true; } else { orignal(msg); @@ -964,7 +975,7 @@ const LocationAction = { Controls.LoginErrorOk.click(); Controls.BottomLeftExit.click(); D2Bot.printToConsole(string); - ControlAction.timeoutDelay("Login Error Delay", 5 * 6e4); + ControlAction.timeoutDelay("Login Error Delay", Time.minutes(Starter.Config.UnableToConnectDelay)); D2Bot.printToConsole("Login Error - Restart"); D2Bot.restart(); @@ -1482,6 +1493,39 @@ const LocationAction = { Starter.LocationEvents.openCreateGameWindow(); } ], + [ + sdk.game.locations.OkCenteredErrorPopUp, + function () { + let string = parseControlText(Controls.OkCenteredText); + + switch (string) { + case getLocaleString(sdk.locale.text.CannotCreateGamesDeadHCChar): + Starter.deadCheck = true; + Controls.OkCentered.click(); + return; + case getLocaleString(sdk.locale.text.UsernameMustBeAtLeast): + case getLocaleString(sdk.locale.text.PasswordMustBeAtLeast): + case getLocaleString(sdk.locale.text.AccountMustBeAtLeast): + case getLocaleString(sdk.locale.text.PasswordCantBeMoreThan): + case getLocaleString(sdk.locale.text.AccountCantBeMoreThan): + case getLocaleString(sdk.locale.text.InvalidPassword): + D2Bot.printToConsole(string); + Starter.profileInfo.account = ""; + Starter.profileInfo.password = ""; + CharData.login.updateData({ account: "", password: "" }); + + break; + default: + D2Bot.updateStatus("Error"); + D2Bot.printToConsole("Error - " + string); + + break; + } + Controls.OkCentered.click(); + + ControlAction.timeoutDelay("Error", Time.minutes(1)); + } + ] ]); /** From bd5cc87d030709108b5491dcfc81b331b361b706 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Thu, 7 Sep 2023 18:16:53 -0400 Subject: [PATCH 227/263] Update DynamicTiers.js - fix using -1 as subid causing allskills to fail --- libs/SoloPlay/Functions/DynamicTiers.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/libs/SoloPlay/Functions/DynamicTiers.js b/libs/SoloPlay/Functions/DynamicTiers.js index 3931c3bf..a26f28a6 100644 --- a/libs/SoloPlay/Functions/DynamicTiers.js +++ b/libs/SoloPlay/Functions/DynamicTiers.js @@ -507,7 +507,7 @@ const skillsScore = function () { let skillsRating = [ - [sdk.stats.AllSkills, -1], + [sdk.stats.AllSkills, 0], [sdk.stats.AddClassSkills, me.classid], [sdk.stats.AddSkillTab, buildInfo.tabSkills], ].reduce(function (acc, [stat, subId]) { @@ -519,10 +519,10 @@ let selectedWeights = [_misc.wanted, _misc.useful]; let selectedSkills = [buildInfo.wantedSkills, buildInfo.usefulSkills]; - for (let i = 0; i < selectedWeights.length; i++) { - for (let j = 0; j < selectedSkills.length; j++) { - for (let k = 0; k < selectedSkills[j].length; k++) { - skillsRating += item.getStatEx(sdk.stats.SingleSkill, selectedSkills[j][k]) * selectedWeights[i]; + for (let weight of selectedWeights) { + for (let type of selectedSkills) { + for (let skill of type) { + skillsRating += item.getStatEx(sdk.stats.SingleSkill, skill) * weight; } } } From 68521f9dc62dcf91b334eac31185719ab162e5c7 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Tue, 3 Oct 2023 00:59:45 -0400 Subject: [PATCH 228/263] Update Globals.js - fix missing imbuemule case --- libs/SoloPlay/Functions/Globals.js | 1 + 1 file changed, 1 insertion(+) diff --git a/libs/SoloPlay/Functions/Globals.js b/libs/SoloPlay/Functions/Globals.js index 0358b609..9fc95c95 100644 --- a/libs/SoloPlay/Functions/Globals.js +++ b/libs/SoloPlay/Functions/Globals.js @@ -1086,6 +1086,7 @@ const Check = { switch (true) { case (SetUp.finalBuild === "Bumper" && Developer.fillAccount.bumpers): case (SetUp.finalBuild === "Socketmule" && Developer.fillAccount.socketMules): + case (SetUp.finalBuild === "Imbuemule" && Developer.fillAccount.imbueMule): SetUp.makeNext(); break; From fb23f7868a39473029ad461092cff3ab37dc604d Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Fri, 15 Dec 2023 18:13:14 -0500 Subject: [PATCH 229/263] Update PatherOverrides.js - Fix getting stuck at Atma --- libs/SoloPlay/Functions/PatherOverrides.js | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/libs/SoloPlay/Functions/PatherOverrides.js b/libs/SoloPlay/Functions/PatherOverrides.js index ca21e8d0..f0b721f3 100644 --- a/libs/SoloPlay/Functions/PatherOverrides.js +++ b/libs/SoloPlay/Functions/PatherOverrides.js @@ -727,6 +727,18 @@ Pather.move = function (target, givenSettings = {}) { } } } + } else if (fail > 0 && me.inArea(sdk.areas.LutGholein) && me.x > 5122 && me.y <= 5049) { + // dislike have this here but handle atma blocking us from inside the tavern + if (me.inArea(sdk.areas.LutGholein) && me.x > 5122 && me.y <= 5049) { + let atma = Game.getNPC(NPC.Atma); + if (atma && (atma.x === 5136 || atma.x === 5137) + && (atma.y >= 5048 && atma.y <= 5051)) { + // yup dumb lady is blocking the door, take side door + [[5140, 5038], [5148, 5031], [5154, 5025], [5161, 5030]].forEach(function (node) { + Pather.walkTo(node[0], node[1]); + }); + } + } } // Reduce node distance in new path From 738fd8c02674b6ed35f007f70c0eaf0b79f608eb Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Thu, 21 Dec 2023 00:05:26 -0500 Subject: [PATCH 230/263] Create Vector.js - Create vector class --- libs/SoloPlay/Modules/Vector.js | 191 ++++++++++++++++++++++++++++++++ 1 file changed, 191 insertions(+) create mode 100644 libs/SoloPlay/Modules/Vector.js diff --git a/libs/SoloPlay/Modules/Vector.js b/libs/SoloPlay/Modules/Vector.js new file mode 100644 index 00000000..cd88f6d5 --- /dev/null +++ b/libs/SoloPlay/Modules/Vector.js @@ -0,0 +1,191 @@ +/** + * @filename Vector.js + * @author theBGuy + * @desc Vector class + * + */ + +(function (module) { + /** + * @constructor + * @param {number} x + * @param {number} y + */ + function Vector (x, y) { + this.x = Math.trunc(x) || 0; + this.y = Math.trunc(y) || 0; + } + + Vector.prototype = { + /** + * Set the magnitude of the vector + * @param {number} magnitude + */ + setMagnitude: function (magnitude) { + let angle = this.getAngle(); + this.x = Math.cos(angle) * magnitude; + this.y = Math.sin(angle) * magnitude; + }, + + /** + * Get the magnitude of the vector + * @returns {number} + */ + getMagnitude: function () { + return Math.sqrt((this.x * this.x) + (this.y * this.y)); + }, + + /** + * Calculate the distance to another point + * @param {Vector} point + * @returns {number} + */ + getMagnitudeTo: function (point) { + let dx = this.x - point.x; + let dy = this.y - point.y; + return Math.sqrt(dx * dx + dy * dy); + }, + + /** + * Set the angle of the vector + * @param {number} angle + */ + setAngle: function (angle) { + let magnitude = this.getMagnitude(); + this.x = Math.cos(angle) * magnitude; + this.y = Math.sin(angle) * magnitude; + }, + + /** + * Get the angle of the vector + * @returns {number} + */ + getAngle: function () { + return Math.atan2(this.y, this.x); + }, + + /** + * Add a vector to this vector + * @param {Vector} vector + */ + add: function (vector) { + this.x += vector.x; + this.y += vector.y; + return this; + }, + + /** + * Subtract a vector from this vector + * @param {Vector} vector + */ + subtract: function (vector) { + this.x -= vector.x; + this.y -= vector.y; + return this; + }, + + /** + * Multiply this vector by a scalar value + * @param {number} scalar + */ + multiply: function (scalar) { + this.x *= scalar; + this.y *= scalar; + return this; + }, + + /** + * Divide this vector by a scalar value + * @param {number} scalar + */ + divide: function (scalar) { + this.x /= scalar; + this.y /= scalar; + return this; + }, + + /** + * Normalize this vector (make its magnitude 1) + */ + normalize: function () { + let magnitude = this.getMagnitude(); + if (magnitude !== 0) { + this.x /= magnitude; + this.y /= magnitude; + } + return this; + } + }; + + /** + * Generate a list of points along the line from pointA to pointB + * @param {Vector} pointA + * @param {Vector} pointB + * @param {number} numberOfPoints + * @returns {Vector[]} + * @todo Should this accept a unit as well and create a vector for it? + */ + Vector.path = function (pointA, pointB, numberOfPoints) { + if (!(pointA instanceof Vector)) { + pointA = new Vector(pointA.x, pointA.y); + } + if (!(pointB instanceof Vector)) { + pointB = new Vector(pointB.x, pointB.y); + } + if (!numberOfPoints) { + numberOfPoints = Math.floor(getDistance(pointA.x, pointA.y, pointB.x, pointB.y)); + } + // Calculate the vector from point A to point B + let trajectory = new Vector(pointB.x - pointA.x, pointB.y - pointA.y); + + // Normalize the trajectory vector + trajectory.normalize(); + + // Calculate the distance between point A and point B + let distance = pointA.getMagnitudeTo(pointB); + + // Calculate the distance between each generated point + let step = distance / numberOfPoints; + + /** + * @type {Vector[]} + */ + let points = []; + + // Generate an array of points from point A to point B + for (let i = 0; i <= numberOfPoints; i++) { + // Calculate the position of the current point along the trajectory + let position = new Vector(trajectory.x * step * i, trajectory.y * step * i); + + // Add the position to the starting point to get the actual point + let point = new Vector(pointA.x + position.x, pointA.y + position.y); + + // Add the point to the array + points.push(point); + } + + return points; + }; + + /** + * Find a third point in the same direction as point A to point B + * @param {Vector} vecA + * @param {Vector} vecB + * @param {number} distance + * @returns {Vector} + */ + Vector.spotOnDistance = function (vecA, vecB, distance) { + // Calculate the vector from point A to point B + let trajectory = new Vector(vecB.x - vecA.x, vecB.y - vecA.y); + + // Normalize the trajectory vector + trajectory.normalize(); + + // Multiply the normalized trajectory by the distance + trajectory.multiply(distance); + + return new Vector(vecA.x + trajectory.x, vecA.y + trajectory.y); + }; + + module.exports = Vector; +})(module); From a8ce98bc570bacdc7ed69b60c4aef8072bafb737 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Thu, 21 Dec 2023 00:09:16 -0500 Subject: [PATCH 231/263] Update GameData.js - Change `getTotalDamage` to cap applied damage at the monsters health. We can't do more damage to that unit than killing them - Add orb path damage calculation, orb is a bit special in it doesn't just do damage at a point, it travels a path dealing out damage in it's radius. - Add through damage calculation, unused currently but will handle lighting, bone spear, and molten boulder --- libs/SoloPlay/Modules/GameData/GameData.js | 246 +++++++++++++++++---- 1 file changed, 200 insertions(+), 46 deletions(-) diff --git a/libs/SoloPlay/Modules/GameData/GameData.js b/libs/SoloPlay/Modules/GameData/GameData.js index 56e37146..4d17d3bf 100644 --- a/libs/SoloPlay/Modules/GameData/GameData.js +++ b/libs/SoloPlay/Modules/GameData/GameData.js @@ -13,6 +13,7 @@ const AreaData = require("./AreaData"); const MissileData = require("./MissileData"); const Coords_1 = require("../Coords"); + const Vector = require("../Vector"); const sdk = require("../../../modules/sdk"); const HPLookup = [ ["1", "1", "1"], ["7", "107", "830"], @@ -662,51 +663,51 @@ let eliteBonus = (target.spectype && target.isSpecial) ? 1 : 0, hitcap = 1; switch (skillID) { // charged bolt/strike excluded, it's so unreliably random - case 15: // poison javalin - case 25: // plague javalin - case 16: // exploding arrow - case 27: // immolation arrow - case 31: // freezing arrow - case 35: // lightning fury - case 44: // frost nova - case 48: // nova - case 56: // meteor - case 59: // blizzard - case 64: // frozen orb - case 83: // poison explosion - case 92: // poison nova - case 112: // blessed hammer - case 154: // war cry - case 229: // molten boulder - case 234: // fissure - case 249: // armageddon - case 244: // volcano - case 250: // hurricane - case 251: // fireblast - case 261: // charged bolt sentry - case 262: // wake of fire - case 55: // glacial spike - case 47: // fire ball - case 42: // Static field. - case 38: // charged bolt + case sdk.skills.PoisonJavelin: // poison javalin + case sdk.skills.PlagueJavelin: // plague javalin + case sdk.skills.ExplodingArrow: // exploding arrow + case sdk.skills.ImmolationArrow: // immolation arrow + case sdk.skills.FreezingArrow: // freezing arrow + case sdk.skills.LightningFury: // lightning fury + case sdk.skills.FrostNova: // frost nova + case sdk.skills.Nova: // nova + case sdk.skills.Meteor: // meteor + case sdk.skills.Blizzard: // blizzard + case sdk.skills.FrozenOrb: // frozen orb + case sdk.skills.PoisonExplosion: // poison explosion + case sdk.skills.PoisonNova: // poison nova + case sdk.skills.BlessedHammer: // blessed hammer + case sdk.skills.WarCry: // war cry + case sdk.skills.MoltenBoulder: // molten boulder + case sdk.skills.Fissure: // fissure + case sdk.skills.Armageddon: // armageddon + case sdk.skills.Volcano: // volcano + case sdk.skills.Hurricane: // hurricane + case sdk.skills.FireBlast: // fireblast + case sdk.skills.ChargedBoltSentry: // charged bolt sentry + case sdk.skills.WakeofFire: // wake of fire + case sdk.skills.GlacialSpike: // glacial spike + case sdk.skills.FireBall: // fire ball + case sdk.skills.StaticField: // Static field. + case sdk.skills.ChargedBolt: // charged bolt hitcap = Infinity; break; - case 34: // lightning strike - hitcap = 1 + this.skillLevel(34); + case sdk.skills.LightningStrike: // lightning strike + hitcap = 1 + this.skillLevel(sdk.skills.LightningStrike); break; - case 67: // teeth - hitcap = 1 + this.skillLevel(67); + case sdk.skills.Teeth: // teeth + hitcap = 1 + this.skillLevel(sdk.skills.Teeth); break; - case 53: // chain lightning - hitcap = 5 + ((this.skillLevel(53) / 5) | 0); + case sdk.skills.ChainLightning: // chain lightning + hitcap = 5 + ((this.skillLevel(sdk.skills.ChainLightning) / 5) | 0); break; - case 24: - hitcap = 3 + ((this.skillLevel(24) / 5) | 0); + case sdk.skills.ChargedStrike: + hitcap = 3 + ((this.skillLevel(sdk.skills.ChargedStrike) / 5) | 0); break; - case 49: // lightning - case 84: // bone spear - case 271: // lightning sentry - case 276: // death sentry + case sdk.skills.Lightning: // lightning + case sdk.skills.BoneSpear: // bone spear + case sdk.skills.LightningSentry: // lightning sentry + case sdk.skills.DeathSentry: // death sentry hitcap = aps ? Math.sqrt(aps / Math.PI) * 2 : 1; break; default: @@ -760,13 +761,64 @@ skillDamage: function (skillID, unit) { // TODO: caluclate basic attack damage if (skillID === sdk.skills.Attack) { + let weapon = me.equipped.get(sdk.body.RightArm); + let [dexBonus, strBonus] = [0, 0]; + const isEth = weapon.ethereal; + const minDmg = (GameData.myReference.getStat(sdk.stats.MinDamage) || 1); + const maxDmg = (GameData.myReference.getStat(sdk.stats.MaxDamage) || 2); + const wepED = (weapon.getStat(sdk.stats.EnhancedDamage) || 0); + const isDeadlyStrike = Math.random() < GameData.myReference.getStat(sdk.stats.DeadlyStrike) / 100; + console.log(isEth, minDmg, maxDmg, wepED, isDeadlyStrike); + + switch (weapon.itemType) { + case sdk.items.type.AmazonBow: + case sdk.items.type.Crossbow: + case sdk.items.type.Bow: + dexBonus = (GameData.myReference.getStat(sdk.stats.Dexterity) / 100); + + break; + case sdk.items.type.AmazonJavelin: + case sdk.items.type.AmazonSpear: + strBonus = (GameData.myReference.getStat(sdk.stats.Strength) / 80); + dexBonus = (GameData.myReference.getStat(sdk.stats.Dexterity) / 50); + + break; + case sdk.items.type.HandtoHand: + case sdk.items.type.Knife: + case sdk.items.type.ThrowingAxe: + case sdk.items.type.ThrowingKnife: + strBonus = (GameData.myReference.getStat(sdk.stats.Strength) / 75); + dexBonus = (GameData.myReference.getStat(sdk.stats.Dexterity) / 75); + + break; + case sdk.items.type.Hammer: + strBonus = (GameData.myReference.getStat(sdk.stats.Strength) / 110); + + break; + default: + strBonus = (GameData.myReference.getStat(sdk.stats.Strength) / 100); + + break; + } + return { type: "Physical", - pmin: (GameData.myReference.getStat(sdk.stats.MinDamage) || 2), - pmax: (GameData.myReference.getStat(sdk.stats.MaxDamage) || 8), + pmin: ( + minDmg * (isEth ? 1.5 : 1) + // * (wepED > -1 ? (1 + wepED / 100) : 1) + * (1 + strBonus + dexBonus) + * (isDeadlyStrike ? 2 : 1) + ), + pmax: ( + maxDmg * (isEth ? 1.5 : 1) + // * (wepED > -1 ? (1 + wepED / 100) : 1) + * (1 + strBonus + dexBonus) + * (isDeadlyStrike ? 2 : 1) + ), min: 0, max: 0 }; // short sword, no reqs + } if (this.skillLevel(skillID) < 1) { @@ -1004,15 +1056,15 @@ : 0); /** - * * @param {skillDmgObj} skillData * @param {Monster | number | string} unit * @returns */ const getTotalDmg = function (skillData, unit) { - const isUndead = (typeof unit === "number" - ? MonsterData.get(unit).Undead - : MonsterData.get(unit.classid).Undead); + const mon = (typeof unit === "number" + ? MonsterData.get(unit) + : MonsterData.get(unit.classid)); + const isUndead = mon.Undead; const conviction = GameData.getConviction(); let totalDmg = 0; let avgPDmg = (skillData.pmin + skillData.pmax) / 2; @@ -1033,6 +1085,14 @@ resist = (resist < 100 ? Math.max(-100, resist - pierce) : 100); totalDmg += avgDmg * (100 - resist) / 100; } + if (typeof unit === "object") { + let { classid, area, charlvl } = unit; + let adjustLevel = charlvl - GameData.monsterLevel(classid, area); + let currentHealth = GameData.monsterMaxHP(classid, area, adjustLevel) / 100 * (unit.hp * 100 / unit.hpmax); + if (currentHealth < totalDmg) { + totalDmg = currentHealth; + } + } return totalDmg; }; @@ -1053,6 +1113,55 @@ }, 0); }; + /** + * Orb travels a 9 1/3 yard straight path and sends out bolts covering a 20yard radius. We need to create + * a box and check all monsters who are within that box. We can then calculate the damage of the orb + * Each bolt can hit a monster only once. Maximum of 46 bolts can be fired. + * @param {Monster} target + */ + const calculateOrbPathDamage = function (target) { + let totalDmg = 0; + const meVec = new Vector(me.x, me.y); + const targetVec = new Vector(target.x, target.y); + const orbVec = targetVec.subtract(meVec).normalize().multiply(9.33); + const orbPath = Vector.path(meVec, orbVec); + let units = getUnits(sdk.unittype.Monster) + .filter(function (mon) { + if (!mon.attackable) return false; + const distInPath = orbPath + .toSorted(function (a, b) { + return getDistance(mon, a) - getDistance(mon, b); + }).first(); + if (!distInPath) return false; + const distanceFromPath = getDistance(mon, distInPath); + if (distanceFromPath > 20) { + mon._modifier = 5; + } else if (distanceFromPath > 15) { + mon._modifier = 4; + } else if (distanceFromPath > 10) { + mon._modifier = 3; + } else if (distanceFromPath > 0) { + mon._modifier = 1; + } + return distanceFromPath < 25; + }) + .sort(function (a, b) { + const distAFromTarget = getDistance(target, a); + const distAFromMe = getDistance(me, a); + const distBFromTarget = getDistance(target, b); + const distBFromMe = getDistance(me, b); + + return (distAFromTarget + distAFromMe) - (distBFromTarget + distBFromMe); + }); + for (let i = 0; i < units.length; i++) { + if (units[i] !== undefined) { + let _a = GameData.skillDamage(sdk.skills.FrozenOrb, units[i]); + totalDmg += getTotalDmg(_a, units[i]) / units[i]._modifier; + } + } + return totalDmg; + }; + /** * @param {number} skill * @param {Monster} target @@ -1118,7 +1227,12 @@ }, 0); }; - const calculateThroughDamage = function () { + /** + * @param {number} skill + * @param {Monster} target + * @returns {number} + */ + const calculateThroughDamage = function (skill, target) { // determine maximum potential distance of this missile // build points from me -> monster -> max distance // iterate points checking if any monsters are in the path @@ -1127,6 +1241,44 @@ // - check monster size, based on size the boulder may knock back or go through them // - if we encounter a collision that causes the boulder to burst, add explosion damage // + const range = Skill.getRange(skill); + let totalDmg = 0; + const meVec = new Vector(me.x, me.y); + const targetVec = new Vector(target.x, target.y); + const misVec = targetVec.subtract(meVec).normalize().multiply(range); + const missilePath = Vector.path(meVec, misVec); + missilePath.sort(function (a, b) { + return getDistance(me, a) - getDistance(me, b); + }); + // check if we run into any cast-blockers + let cBlockIdx = missilePath.findIndex(function (point) { + let coll = getCollision(me.area, point.x, point.y); + return (coll & sdk.collision.BlockMissile); + }); + if (cBlockIdx > -1) { + missilePath.splice(cBlockIdx); + } + let units = getUnits(sdk.unittype.Monster) + .filter(function (mon) { + if (!mon.attackable) return false; + const distInPath = missilePath + .toSorted(function (a, b) { + return getDistance(mon, a) - getDistance(mon, b); + }).first(); + if (!distInPath) return false; + const distanceFromPath = getDistance(mon, distInPath); + return distanceFromPath <= 3; + }) + .sort(function (a, b) { + return getDistance(me, a) - getDistance(me, b); + }); + for (let i = 0; i < units.length; i++) { + if (units[i] !== undefined) { + let _a = GameData.skillDamage(skill, units[i]); + totalDmg += getTotalDmg(_a, units[i]); + } + } + return totalDmg; }; /** @@ -1175,6 +1327,8 @@ * Others? */ switch (skillID) { + case sdk.skills.FrozenOrb: + return calculateOrbPathDamage(unit); case sdk.skills.Blizzard: case sdk.skills.Meteor: case sdk.skills.FireBall: From 131fc6232e771a89df25d808644c539ab9a3c822 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Thu, 21 Dec 2023 14:38:25 -0500 Subject: [PATCH 232/263] Rebuild `SorceressAttacks` - Added `lastAttack` object so we can handle alternating static field or charged bolt with other attacks as those sometimes miss - Removed the `AttackData` object and replaced with `Skills` map. - Added `decideAttack` method to simplfy chooisng which skill we want and it takes an options parameter for overides handle different cases. By default it selects the skill that does the most damage and costs the least mana --- .../ClassAttackOverrides/SorceressAttacks.js | 337 +++++++++--------- 1 file changed, 175 insertions(+), 162 deletions(-) diff --git a/libs/SoloPlay/Functions/ClassAttackOverrides/SorceressAttacks.js b/libs/SoloPlay/Functions/ClassAttackOverrides/SorceressAttacks.js index 72e9a492..9d042fa6 100644 --- a/libs/SoloPlay/Functions/ClassAttackOverrides/SorceressAttacks.js +++ b/libs/SoloPlay/Functions/ClassAttackOverrides/SorceressAttacks.js @@ -50,85 +50,102 @@ includeIfNotIncluded("core/Attacks/Sorceress.js"); } }; - /** - * @constructor - * @param {number} skillId - * @param {number} reqLvl - * @param {number} range - */ - function ClassData (skillId = -1, range = 0) { - this.have = false; - this.skill = skillId; - this.range = range ? range : Skill.getRange(skillId); - this.mana = Infinity; - this.dmg = 0; - this.timed = Skill.isTimed(skillId); - this.reqLvl = getBaseStat("skills", skillId, "reqlevel"); - } - - /** - * Initialize data values - * @param {number} [range] - * @returns {void} - */ - ClassData.prototype.assignValues = function (range) { - this.have = Skill.canUse(this.skill); - if (!this.have) return; - this.range = range || Skill.getRange(this.skill); - this.mana = Skill.getManaCost(this.skill); - }; - - /** - * Calculate effective damage for a certain monster unit - * @param {Monster} unit - * @returns {void} - */ - ClassData.prototype.calcDmg = function (unit) { - if (!this.have) return; - this.dmg = GameData.avgSkillDamage(this.skill, unit); + const lastAttack = { + skill: -1, + count: 0, + gid: -1, + + setSkill: function (skill, gid) { + if (skill === this.skill && gid === this.gid) { + this.count++; + } else { + this.skill = skill; + this.count = 1; + this.gid = gid; + } + }, }; - const AttackData = { - "Attack": new ClassData(sdk.skills.Attack, 4), - "FireBolt": new ClassData(sdk.skills.FireBolt), - "ChargedBolt": new ClassData(sdk.skills.ChargedBolt), - "IceBolt": new ClassData(sdk.skills.IceBolt), - "Inferno": new ClassData(sdk.skills.Inferno), - "Telekinesis": new ClassData(sdk.skills.Telekinesis, 20), - "StaticField": new ClassData(sdk.skills.StaticField), - "IceBlast": new ClassData(sdk.skills.IceBlast), - "FrostNova": new ClassData(sdk.skills.FrostNova), - "FireBall": new ClassData(sdk.skills.FireBall), - // Blaze: new ClassData(sdk.skills.Blaze), - "Lightning": new ClassData(sdk.skills.Lightning), - "Nova": new ClassData(sdk.skills.Nova), - "FireWall": new ClassData(sdk.skills.FireWall), - "ChainLightning": new ClassData(sdk.skills.ChainLightning), - "GlacialSpike": new ClassData(sdk.skills.GlacialSpike), - "Meteor": new ClassData(sdk.skills.Meteor), - // ThunderStorm: new ClassData(sdk.skills.ThunderStorm), - "Blizzard": new ClassData(sdk.skills.Blizzard), - "Hydra": new ClassData(sdk.skills.Hydra), - "FrozenOrb": new ClassData(sdk.skills.FrozenOrb), - }; + const Skills = new Map([ + [sdk.skills.Attack, Skill.get(sdk.skills.Attack)], + [sdk.skills.FireBolt, Skill.get(sdk.skills.FireBolt)], + [sdk.skills.ChargedBolt, Skill.get(sdk.skills.ChargedBolt)], + [sdk.skills.IceBolt, Skill.get(sdk.skills.IceBolt)], + [sdk.skills.Inferno, Skill.get(sdk.skills.Inferno)], + [sdk.skills.Telekinesis, Skill.get(sdk.skills.Telekinesis)], + [sdk.skills.StaticField, Skill.get(sdk.skills.StaticField)], + [sdk.skills.IceBlast, Skill.get(sdk.skills.IceBlast)], + [sdk.skills.FrostNova, Skill.get(sdk.skills.FrostNova)], + [sdk.skills.FireBall, Skill.get(sdk.skills.FireBall)], + [sdk.skills.Lightning, Skill.get(sdk.skills.Lightning)], + [sdk.skills.Nova, Skill.get(sdk.skills.Nova)], + [sdk.skills.FireWall, Skill.get(sdk.skills.FireWall)], + [sdk.skills.ChainLightning, Skill.get(sdk.skills.ChainLightning)], + [sdk.skills.GlacialSpike, Skill.get(sdk.skills.GlacialSpike)], + [sdk.skills.Meteor, Skill.get(sdk.skills.Meteor)], + [sdk.skills.Blizzard, Skill.get(sdk.skills.Blizzard)], + [sdk.skills.Hydra, Skill.get(sdk.skills.Hydra)], + [sdk.skills.FrozenOrb, Skill.get(sdk.skills.FrozenOrb)], + ]); /** - * The keys never change so this makes it easier to iterate without calling Object.keys each time - * @type {Array} - */ - const AttackDataKeys = Object.keys(AttackData); - - /** - * Helper function to re-init AttackData - * @param {number} currLvl - * @todo decide when AttackData need to be re-initialized becasue doing it every attack is a waste + * @param {Monster} unit + * @param {{ + * manaSort?: boolean, + * checkSkillDelay?: boolean, + * checkLast: boolean, + * checkSafeStatic: boolean, + * minRange?: number, + * maxRange?: number + * }} options */ - const initAttackData = function (currLvl = me.charlvl) { - AttackDataKeys.forEach(function (sk) { - if (currLvl >= AttackData[sk].reqLvl) { - AttackData[sk].assignValues(); - } - }); + const decideAttack = function (unit, options = {}) { + const overrides = Object.assign({ + manaSort: true, + checkSkillDelay: false, + checkLast: true, + checkSafeStatic: true, + minRange: 0, + maxRange: 0, + }, options); + let _choices = []; + + for (let [skillId, skill] of Skills) { + if (!skill.have()) continue; + if (overrides.minRange && skill.range() < overrides.minRange) continue; + if (overrides.maxRange && skill.range() > overrides.maxRange) continue; + skill._dmg = GameData.avgSkillDamage(skillId, unit); + _choices.push(skill); + } + + return _choices + .sort(function (a, b) { + if (overrides.manaSort) { + if (b._dmg === a._dmg) { + return b.manaCost() - a.manaCost(); + } + } + return b._dmg - a._dmg; + }) + .find(function (skill) { + if (overrides.checkLast + && _choices.length > 2 + && lastAttack.count > 3 + // && skill.skillId === lastAttack.skill + // for now only check these two, just mixing in other attacks every third attack + && [sdk.skills.ChargedBolt, sdk.skills.StaticField].includes(skill.skillId) + && unit.gid === lastAttack.gid) { + return false; + } + if (overrides.checkSafeStatic + && _choices.length > 2 + && skill.skillId === sdk.skills.StaticField + && unit.distance > skill.range() + && me.inDanger(unit, skill.range() + 1)) { + return false; + } + return (me.mp > skill.manaCost()) && (!skill.timed || !overrides.checkSkillDelay || !me.skillDelay); + }) || -1; }; /** @@ -136,21 +153,22 @@ includeIfNotIncluded("core/Attacks/Sorceress.js"); * @param {Monster} unit */ const setDamageValues = function (unit) { - AttackDataKeys.forEach(function (sk) { - if (AttackData[sk].have) { - AttackData[sk].calcDmg(unit); - } - }); + for (let [skillId, skill] of Skills) { + if (!skill.have()) continue; + skill._dmg = GameData.avgSkillDamage(skillId, unit); + } }; /** * Check if this skill is the most damaging - * @param {ClassData} skill + * @param {SkillDataInfo} checkSkill * @returns {boolean} */ - const isHighestDmg = function (skill) { - for (let key of AttackDataKeys) { - if (AttackData[key].dmg > skill.dmg) { + const isHighestDmg = function (checkSkill) { + // eslint-disable-next-line no-unused-vars + for (let [_, skill] of Skills) { + if (!skill.have()) continue; + if (skill._dmg > checkSkill._dmg) { return false; } } @@ -160,11 +178,23 @@ includeIfNotIncluded("core/Attacks/Sorceress.js"); /** * Used to handle times when there isn't a valid skill we can use, to prevent throwing error */ - const DummyData = new ClassData(-1, -1); + const DummyData = new function () { + this.have = false; + this.skillId = -1; + this.range = 0; + this.mana = 0; + this.dmg = 0; + this.timed = false; + this.reqLvl = 0; + + this.manaCost = function () { + return 0; + }; + }; /** * Makes checking cases with it easier */ - const TELEPORT = new ClassData(sdk.skills.Teleport, 40); + const TELEPORT = Skill.get(sdk.skills.Teleport); /** * @param {Monster} unit @@ -215,31 +245,16 @@ includeIfNotIncluded("core/Attacks/Sorceress.js"); * @returns {dataObj} */ ClassAttack.decideDistanceSkill = function (unit, checkDelay = false) { - const currLvl = me.charlvl; - let selected = AttackDataKeys - .filter(function (sk) { - if (currLvl < AttackData[sk].reqLvl || AttackData[sk].range < 20) return false; - AttackData[sk].assignValues(); - if (!AttackData[sk].have) return false; - AttackData[sk].calcDmg(unit); - /** - * For now, no skill delay check. - * Things to consider: - * 1) If the skill we choose is timed and we are in skillDelay, how long is left to wait? - * 2) If not long then what is the damage difference between the skill we choose and the runner up non-timed skill - * 3) If the non-timed skill will do enough damage to kill this monster then use it, or if we have more than 1-2 seconds to wait - * and we don't need to move to cast the non-timed skill. - * 4) Anything else? - */ - return AttackData[sk].dmg > 0 && (!checkDelay || (!AttackData[sk].timed || !me.skillDelay)); - }) - .sort(function (a, b) { - return AttackData[b].dmg - AttackData[a].dmg; - }) - .first(); - return typeof AttackData[selected] === "object" - ? AttackData[selected] - : DummyData; + /** + * For now, no skill delay check. + * Things to consider: + * 1) If the skill we choose is timed and we are in skillDelay, how long is left to wait? + * 2) If not long then what is the damage difference between the skill we choose and the runner up non-timed skill + * 3) If the non-timed skill will do enough damage to kill this monster then use it, or if we have more than 1-2 seconds to wait + * and we don't need to move to cast the non-timed skill. + * 4) Anything else? + */ + return decideAttack(unit, { checkSkillDelay: checkDelay, minRange: 20 }); }; /** @@ -283,11 +298,10 @@ includeIfNotIncluded("core/Attacks/Sorceress.js"); ClassAttack.switchCurse(unit); } - initAttackData(currLvl); - TELEPORT.assignValues(); + TELEPORT.have(); - if (AttackData.FrostNova.have) { - if (me.mp > AttackData.FrostNova.mana) { + if (Skills.get(sdk.skills.FrostNova).have()) { + if (me.mp > Skills.get(sdk.skills.FrostNova).manaCost()) { frostNovaCheck() && Skill.cast(sdk.skills.FrostNova, sdk.skills.hand.Right); let ticktwo = getTickCount(); // if the nova cause the death of any monsters around us, its worth it @@ -303,8 +317,8 @@ includeIfNotIncluded("core/Attacks/Sorceress.js"); } } - if (AttackData.GlacialSpike.have) { - if (me.mp > AttackData.GlacialSpike.mana * 2) { + if (Skills.get(sdk.skills.GlacialSpike).have()) { + if (me.mp > Skills.get(sdk.skills.GlacialSpike).manaCost() * 2) { let shouldSpike = unit && unit.distance < 10 && getUnits(sdk.unittype.Monster).filter(function (el) { return getDistance(el, unit) < 4 && slowable(el, true); @@ -325,22 +339,25 @@ includeIfNotIncluded("core/Attacks/Sorceress.js"); // log damage values // if (Developer.debugging.skills) { - // Object.keys(data).forEach(k => typeof data[k] === "object" && console.log(getSkillById(data[k].skill) + " : " + data[k].dmg)); + // for (let [skillId, skill] of Skills) { + // console.log(getSkillById(skillId) + " : " + skill._dmg); + // } // } let rebuild = false; // If we have enough mana for Static and it will do more damage than our other skills then duh use it // should this return afterwards since the calulations will now be different? - if (AttackData.StaticField.have && (AttackData.StaticField.mana * 3) < me.mp) { + if (Skills.get(sdk.skills.StaticField).have() && (Skills.get(sdk.skills.StaticField).manaCost() * 3) < me.mp) { let closeMobCheck = getUnits(sdk.unittype.Monster) .filter(function (unit) { - return !!unit && unit.attackable && unit.distance < AttackData.StaticField.range; + return !!unit && unit.attackable && unit.distance < Skills.get(sdk.skills.StaticField).range(); }) .find(function (unit) { return Attack.checkResist(unit, "lightning") && unit.hpPercent > Config.CastStatic; }); - if (!!closeMobCheck && isHighestDmg(AttackData.StaticField) && !Coords_1.isBlockedBetween(me, closeMobCheck)) { + if (!!closeMobCheck && isHighestDmg(Skills.get(sdk.skills.StaticField)) + && !Coords_1.isBlockedBetween(me, closeMobCheck)) { Developer.debugging.skills && console.log("STATIC"); // check if we should use battle cry from cta if we have it battleCryCheck(closeMobCheck); @@ -356,37 +373,18 @@ includeIfNotIncluded("core/Attacks/Sorceress.js"); rebuild && setDamageValues(unit); /** - * @todo static field is a good skill but if we are currently out of range, check how dangerous it is to tele to spot before choosing that as our skill + * @todo static field is a good skill but if we are currently out of range, + * check how dangerous it is to tele to spot before choosing that as our skill */ - let sortedList = AttackDataKeys - .filter(function (k) { - return (AttackData[k].have && me.mp > AttackData[k].mana - && (!AttackData[k].timed || !me.skillDelay) - && (AttackData[k].skill !== sdk.skills.StaticField || !recheckSkill)); - }) - .sort(function (a, b) { - return AttackData[b].dmg - AttackData[a].dmg; - }); - if (!sortedList.length) return Attack.Result.FAILED; - - // A bit ugly but handle static and charged bolt here - let skillCheck = ( - (AttackData[sortedList[0]].skill === sdk.skills.StaticField - && unit.distance > AttackData.StaticField.range && me.inDanger(unit, 15)) - || (AttackData[sortedList[0]].skill === sdk.skills.ChargedBolt && recheckSkill) - ) - ? sortedList.at(1) - : sortedList.at(0); - - /** @type {ClassData} */ - let selectedSkill = typeof AttackData[skillCheck] === "object" - ? AttackData[skillCheck] - : DummyData; + let selectedSkill = decideAttack(unit); + if (selectedSkill === -1) return Attack.Result.FAILED; - switch (selectedSkill.skill) { + switch (selectedSkill.skillId) { case sdk.skills.ChargedBolt: - if (selectedSkill.skill === sdk.skills.ChargedBolt && AttackData.IceBolt.have && slowable(unit)) { - selectedSkill = AttackData.IceBolt; + if (selectedSkill.skillId === sdk.skills.ChargedBolt + && Skills.get(sdk.skills.IceBolt).have() + && slowable(unit)) { + selectedSkill = Skills.get(sdk.skills.IceBolt); } break; @@ -446,14 +444,14 @@ includeIfNotIncluded("core/Attacks/Sorceress.js"); if (CharData.skillData.bow.onSwitch && (index !== 1 || !unit.name.includes(getLocaleString(sdk.locale.text.Ghostly))) - && ([-1, sdk.skills.Attack].includes(selectedSkill.skill) - || selectedSkill.mana > me.mp - || (selectedSkill.mana * 3 > me.mp - && [sdk.skills.FireBolt, sdk.skills.ChargedBolt].includes(selectedSkill.skill)))) { + && ([-1, sdk.skills.Attack].includes(selectedSkill.skillId) + || selectedSkill.manaCost() > me.mp + || (selectedSkill.manaCost() * 3 > me.mp + && [sdk.skills.FireBolt, sdk.skills.ChargedBolt].includes(selectedSkill.skillId)))) { if (switchBowAttack(unit) === Attack.Result.SUCCESS) return Attack.Result.SUCCESS; } - if (selectedSkill === sdk.skills.Attack && me.inDanger(unit, 10)) { + if (selectedSkill.skillId === sdk.skills.Attack && me.inDanger(unit, 10)) { // try to stay safer for now, probably should see if there are any easy targets we can pick off return Attack.Result.CANTATTACK; } @@ -477,6 +475,7 @@ includeIfNotIncluded("core/Attacks/Sorceress.js"); + ((getTickCount() - tick) / 1000) + " seconds]----------------------//" ); } + lastAttack.setSkill(selectedSkill.skillId, gid); return Attack.Result.SUCCESS; case Attack.Result.CANTATTACK: // Try to telestomp @@ -507,7 +506,7 @@ includeIfNotIncluded("core/Attacks/Sorceress.js"); !!spot && Pather.walkTo(spot.x, spot.y); } - if (Attack.checkResist(unit, "lightning") && AttackData.StaticField.have + if (Attack.checkResist(unit, "lightning") && Skills.get(sdk.skills.StaticField).have() && unit.hpPercent > Config.CastStatic) { Skill.cast(sdk.skills.StaticField, sdk.skills.hand.Right); } @@ -530,12 +529,15 @@ includeIfNotIncluded("core/Attacks/Sorceress.js"); /** * @override * @param {Monster} unit - * @param {ClassData} choosenSkill + * @param {SkillDataInfo} choosenSkill * @returns {AttackResult} */ ClassAttack.doCast = function (unit, choosenSkill) { let noMana = false; - let { skill, range, mana, timed } = choosenSkill; + let skill = choosenSkill.skillId; + let range = choosenSkill.range(); + let mana = choosenSkill.manaCost(); + let timed = choosenSkill.timed; // unit became invalidated if (!unit || !unit.attackable) return Attack.Result.SUCCESS; if (!!skill && me.mp < mana) { @@ -543,16 +545,20 @@ includeIfNotIncluded("core/Attacks/Sorceress.js"); } // No valid skills can be found if (skill < 0) return Attack.Result.CANTATTACK; + // print damage values - if (Developer.debugging.skills && choosenSkill.have) { - console.log(sdk.colors.Yellow + "(Selected Main :: " + getSkillById(skill) + ") DMG: " + choosenSkill.dmg); + // if (Developer.debugging.skills && choosenSkill.have) { + if (Developer.debugging.skills && choosenSkill.have()) { + // console.log(sdk.colors.Yellow + "(Selected Main :: " + getSkillById(skill) + ") DMG: " + choosenSkill.dmg); + console.log(sdk.colors.Yellow + "(Selected Main :: " + getSkillById(skill) + ") DMG: " + choosenSkill._dmg); } if (![sdk.skills.FrostNova, sdk.skills.Nova, sdk.skills.StaticField].includes(skill)) { // need like a potential danger check, sometimes while me might not be immeadiate danger because there aren't a whole // lot of monsters around, we can suddenly be in danger if a ranged monsters hits us or if one of the monsters near us // does a lot of damage quickly - if (TELEPORT.have && me.mp > TELEPORT.mana + mana && me.inDanger()) { + // if (TELEPORT.have && me.mp > TELEPORT.mana + mana && me.inDanger()) { + if (TELEPORT.have() && me.mp > TELEPORT.manaCost() + mana && me.inDanger()) { //console.log("FINDING NEW SPOT"); Attack.getIntoPosition(unit, range, 0 | Coords_1.BlockBits.LineOfSight @@ -563,6 +569,11 @@ includeIfNotIncluded("core/Attacks/Sorceress.js"); } else if (me.inDanger()) { Attack.getIntoPosition(unit, range + 1, Coords_1.Collision.BLOCK_MISSILE, true); } else if (unit.distance < 3 && range > 4) { + // Attack.getIntoPositionEx(unit, { + // range: range, + // force: true, + // walk: Pather.useTeleport() + // }); Attack.getIntoPosition(unit, range, Coords_1.Collision.BLOCK_MISSILE, true); } } @@ -627,13 +638,16 @@ includeIfNotIncluded("core/Attacks/Sorceress.js"); && unit.distance < 10 && !checkCollision(me, unit, sdk.collision.BlockWalk) ); + // todo - handle nova/frost nova, BLOCK_MISSILE doesn't apply for them if (ranged) { if (!Attack.getIntoPosition(unit, range, Coords_1.Collision.BLOCK_MISSILE, walk)) { return Attack.Result.FAILED; } } else if (!Attack.getIntoPosition(unit, range, Coords_1.BlockBits.Ranged, walk)) { return Attack.Result.FAILED; - } + } /* else if (!Attack.getIntoPositionEx(unit, { range: range, coll: sdk.collision.LineOfSight, walk: walk })) { + return Attack.Result.FAILED; + } */ } if (!unit.dead && !checkCollision(me, unit, Coords_1.BlockBits.Ranged)) { @@ -678,7 +692,7 @@ includeIfNotIncluded("core/Attacks/Sorceress.js"); !unit.dead && Skill.cast(skill, Skill.getHand(skill), unit); } - if (AttackData.FrostNova.have && me.mp > AttackData.FrostNova.mana) { + if (Skills.get(sdk.skills.FrostNova).have() && me.mp > Skills.get(sdk.skills.FrostNova).manaCost()) { frostNovaCheck() && Skill.cast(sdk.skills.FrostNova, sdk.skills.hand.Right); } @@ -719,7 +733,6 @@ includeIfNotIncluded("core/Attacks/Sorceress.js"); return Attack.Result.SUCCESS; } else { - console.debug(choosenSkill); noMana = true; } From fe9155c495dde312371dba07d634988239edc7c6 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Thu, 21 Dec 2023 23:37:15 -0500 Subject: [PATCH 233/263] Update StorageOverrides.js - small cleanup. Handle opening cube --- libs/SoloPlay/Functions/StorageOverrides.js | 71 +++++++++++++-------- 1 file changed, 44 insertions(+), 27 deletions(-) diff --git a/libs/SoloPlay/Functions/StorageOverrides.js b/libs/SoloPlay/Functions/StorageOverrides.js index 47aa3c89..2d273ef0 100644 --- a/libs/SoloPlay/Functions/StorageOverrides.js +++ b/libs/SoloPlay/Functions/StorageOverrides.js @@ -43,7 +43,8 @@ includeIfNotIncluded("SoloPlay/Tools/Developer.js"); let x, y; // Make sure it is in this container. - if (item.location !== this.location || (item.mode !== sdk.items.mode.inStorage && item.mode !== sdk.items.mode.inBelt)) { + if (item.location !== this.location + || (item.mode !== sdk.items.mode.inStorage && item.mode !== sdk.items.mode.inBelt)) { return false; } @@ -202,10 +203,18 @@ includeIfNotIncluded("SoloPlay/Tools/Developer.js"); // always sort stash left-to-right if (this.location === sdk.storage.Stash) { nPos = this.FindSpot(item); - } else if (this.location === sdk.storage.Inventory && ((!itemIdsLeft && !itemIdsRight) || !itemIdsLeft || itemIdsRight.includes(item.classid) || itemIdsLeft.indexOf(item.classid) === -1)) { + } else if (this.location === sdk.storage.Inventory + && ( + (!itemIdsLeft && !itemIdsRight) + || !itemIdsLeft + || itemIdsRight.includes(item.classid) + || itemIdsLeft.indexOf(item.classid) === -1 + )) { // sort from right by default or if specified nPos = this.FindSpot(item, true, false, SetUp.sortSettings.ItemsSortedFromRightPriority); - } else if (this.location === sdk.storage.Inventory && itemIdsRight.indexOf(item.classid) === -1 && itemIdsLeft.includes(item.classid)) { + } else if (this.location === sdk.storage.Inventory + && itemIdsRight.indexOf(item.classid) === -1 + && itemIdsLeft.includes(item.classid)) { // sort from left only if specified nPos = this.FindSpot(item, false, false, SetUp.sortSettings.ItemsSortedFromLeftPriority); } @@ -423,8 +432,19 @@ includeIfNotIncluded("SoloPlay/Tools/Developer.js"); Container.prototype.MoveToSpot = function (item, mX, mY) { let cItem, cube; + // handle opening cube + if (this.location === sdk.storage.Cube || item.location === sdk.storage.Cube) { + cube = me.getItem(sdk.quest.item.Cube); + if (!cube) return false; + if ((cube.isInStash || item.isInStash) && !getUIFlag(sdk.uiflags.Stash) && !Town.openStash()) { + return false; + } + } + // Cube -> Stash, must place item in inventory first - if (item.location === sdk.storage.Cube && this.location === sdk.storage.Stash && !Storage.Inventory.MoveTo(item)) { + if (item.location === sdk.storage.Cube + && this.location === sdk.storage.Stash + && !Storage.Inventory.MoveTo(item)) { return false; } @@ -437,7 +457,7 @@ includeIfNotIncluded("SoloPlay/Tools/Developer.js"); if (this.location === sdk.storage.Stash && !Town.openStash()) return false; const [orgX, orgY, orgLoc] = [item.x, item.y, item.location]; - const moveItem = (x, y, location) => { + const moveItem = function (x, y, location) { for (let n = 0; n < 5; n += 1) { switch (location) { case sdk.storage.Belt: @@ -452,8 +472,9 @@ includeIfNotIncluded("SoloPlay/Tools/Developer.js"); case sdk.storage.Cube: cItem = Game.getCursorUnit(); cube = me.getItem(sdk.quest.item.Cube); - (cItem !== null && cube !== null) && sendPacket(1, sdk.packets.send.ItemToCube, 4, cItem.gid, 4, cube.gid); - + if (cItem !== null && cube !== null) { + sendPacket(1, sdk.packets.send.ItemToCube, 4, cItem.gid, 4, cube.gid); + } break; case sdk.storage.Stash: sendPacket(1, sdk.packets.send.ItemToBuffer, 4, item.gid, 4, x, 4, y, 4, 0x04); @@ -489,11 +510,9 @@ includeIfNotIncluded("SoloPlay/Tools/Developer.js"); * @param {ItemUnit} item */ Container.prototype.MoveTo = function (item) { - let nPos; - try { - //Can we even fit it in here? - nPos = this.FindSpot(item); + // Can we even fit it in here? + let nPos = this.FindSpot(item); if (!nPos) return false; return this.MoveToSpot(item, nPos.y, nPos.x); @@ -505,13 +524,11 @@ includeIfNotIncluded("SoloPlay/Tools/Developer.js"); }; Container.prototype.Dump = function () { - let x, y, string; - if (this.UsedSpacePercent() > 60) { - for (x = 0; x < this.height; x += 1) { - string = ""; + for (let x = 0; x < this.height; x += 1) { + let string = ""; - for (y = 0; y < this.width; y += 1) { + for (let y = 0; y < this.width; y += 1) { string += (this.buffer[x][y] > 0) ? "ÿc1x" : "ÿc0o"; } @@ -543,35 +560,35 @@ includeIfNotIncluded("SoloPlay/Tools/Developer.js"); * @param {number[][]} baseRef */ Container.prototype.Compare = function (baseRef) { - let h, w, n, item, itemList, reference; - Storage.Reload(); try { - itemList = []; - reference = baseRef.slice(0, baseRef.length); + const itemList = []; + const reference = baseRef.slice(0, baseRef.length); - //Insure valid reference. - if (typeof (reference) !== "object" || reference.length !== this.buffer.length || reference[0].length !== this.buffer[0].length) { + // Ensure valid reference. + if (typeof (reference) !== "object" + || reference.length !== this.buffer.length + || reference[0].length !== this.buffer[0].length) { throw new Error("Unable to compare different containers."); } - for (h = 0; h < this.height; h += 1) { + for (let h = 0; h < this.height; h += 1) { Loop: - for (w = 0; w < this.width; w += 1) { - item = this.itemList[this.buffer[h][w] - 1]; + for (let w = 0; w < this.width; w += 1) { + const item = this.itemList[this.buffer[h][w] - 1]; if (!item) { continue; } - for (n = 0; n < itemList.length; n += 1) { + for (let n = 0; n < itemList.length; n += 1) { if (itemList[n].gid === item.gid) { continue Loop; } } - //Check if the buffers changed and the current buffer has an item there. + // Check if the buffers changed and the current buffer has an item there. if (this.buffer[h][w] > 0 && reference[h][w] > 0) { itemList.push(copyUnit(item)); } From beaf0099f46f7e5d636e91fe3e09cf3d70937896 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Tue, 2 Jan 2024 12:25:00 -0500 Subject: [PATCH 234/263] Update CallToArms.js - Fix typo, cta is 5 sockets not 6 --- libs/SoloPlay/BuildFiles/Runewords/CallToArms.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libs/SoloPlay/BuildFiles/Runewords/CallToArms.js b/libs/SoloPlay/BuildFiles/Runewords/CallToArms.js index 0df5f165..200ab374 100644 --- a/libs/SoloPlay/BuildFiles/Runewords/CallToArms.js +++ b/libs/SoloPlay/BuildFiles/Runewords/CallToArms.js @@ -12,7 +12,7 @@ const wanted = { classid: sdk.items.CrystalSword, mode: sdk.items.mode.inStorage, - sockets: 6, + sockets: 5, /** @param {ItemUnit} item */ cb: function (item) { return item.isBaseType; From 2e76e5ef44772cb969f9e552e1ac49281806cc39 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Mon, 8 Jan 2024 18:03:13 -0800 Subject: [PATCH 235/263] Overlay fixes + f10 for pausing - Gold needed a resfix - Guard needed a resfix --- libs/SoloPlay/Modules/Guard.js | 4 ++-- libs/SoloPlay/Threads/ToolsThread.js | 1 + libs/SoloPlay/Tools/Overlay.js | 4 +++- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/libs/SoloPlay/Modules/Guard.js b/libs/SoloPlay/Modules/Guard.js index b0037324..976f328b 100644 --- a/libs/SoloPlay/Modules/Guard.js +++ b/libs/SoloPlay/Modules/Guard.js @@ -36,8 +36,8 @@ } this.hooks = []; - this.x = 500; - this.y = 600 - (400 + (self.hooks.length * 15)); + this.x = me.screensize ? 500 : 400; + this.y = (me.screensize ? 600 : 500) - (400 + (self.hooks.length * 15)); for (let i = 0; i < 22; i++) { (i => this.hooks.push(new UpdateableText(() => stack && stack.length > i && stack[i] || "")))(i); diff --git a/libs/SoloPlay/Threads/ToolsThread.js b/libs/SoloPlay/Threads/ToolsThread.js index 81d240a9..692bffda 100644 --- a/libs/SoloPlay/Threads/ToolsThread.js +++ b/libs/SoloPlay/Threads/ToolsThread.js @@ -308,6 +308,7 @@ function main () { */ const keyEvent = function (key) { switch (key) { + case sdk.keys.F10: case sdk.keys.PauseBreak: // pause default.dbj togglePause(); diff --git a/libs/SoloPlay/Tools/Overlay.js b/libs/SoloPlay/Tools/Overlay.js index 5e4248cc..b78cd232 100644 --- a/libs/SoloPlay/Tools/Overlay.js +++ b/libs/SoloPlay/Tools/Overlay.js @@ -315,7 +315,9 @@ const Overlay = { return "ÿc6Goldÿc0: ÿc0" + me.gold; }, function () { - return new Text("ÿc6Goldÿc0: ÿc0" + me.gold, 275, 586, 4, 6, 0); + let x = me.screensize ? 275 : 195; + let y = Overlay.resfix.y + 586; + return new Text("ÿc6Goldÿc0: ÿc0" + me.gold, x, y, 4, 6, 0); } ) ], From c44dc71283374cf08453bf814d1efbe4b2f38ad2 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Mon, 8 Jan 2024 18:07:42 -0800 Subject: [PATCH 236/263] [BugFix] StorageOverrides.js - Fix bug introduced in last update, re-did handling of cube -> stash. - Added a UIFlag check for stash before trying to send the item to the stash - Fixed moving item when we already have it in the location we want, i.e. calling `Storage.Stash.MoveTo(item)` when the item is already in the stash causing us to pick the item up and put it back --- libs/SoloPlay/Functions/StorageOverrides.js | 61 +++++++++++++-------- 1 file changed, 38 insertions(+), 23 deletions(-) diff --git a/libs/SoloPlay/Functions/StorageOverrides.js b/libs/SoloPlay/Functions/StorageOverrides.js index 2d273ef0..9dc58789 100644 --- a/libs/SoloPlay/Functions/StorageOverrides.js +++ b/libs/SoloPlay/Functions/StorageOverrides.js @@ -95,7 +95,9 @@ includeIfNotIncluded("SoloPlay/Tools/Developer.js"); } } } catch (e2) { - throw new Error("Storage.IsLocked error! Item info: " + item.name + " " + item.y + " " + item.sizey + " " + item.x + " " + item.sizex + " " + item.mode + " " + item.location); + const { name, y, sizey, x, sizex, mode, location } = item; + const errMsg = "Storage.IsLocked error! Item info: "; + throw new Error(errMsg + name + " " + y + " " + sizey + " " + x + " " + sizex + " " + mode + " " + item.location); } return false; @@ -133,7 +135,7 @@ includeIfNotIncluded("SoloPlay/Tools/Developer.js"); let makeCubeSpot = this.MakeSpot(cube, { x: 0, y: 0 }, true); // NOTE: passing these in buffer order [h/x][w/y] if (makeCubeSpot) { - // this item cannot be moved + // this item cannot be moved if (makeCubeSpot === -1) return false; // we couldnt move the item if (!this.MoveToSpot(cube, makeCubeSpot.y, makeCubeSpot.x)) return false; @@ -293,7 +295,6 @@ includeIfNotIncluded("SoloPlay/Tools/Developer.js"); for (x = startY; x !== endY; x += yDir) { //Check if there is something in this spot. if (this.buffer[x][y] > 0) { - // TODO: add makespot logic here. priorityClassIds should only be used when sorting -- in town, where it's safe! // TODO: collapse this down to just a MakeSpot(item, location) call, and have MakeSpot do the priority checks right at the top let bufferItemClass = this.itemList[this.buffer[x][y] - 1].classid; @@ -314,10 +315,7 @@ includeIfNotIncluded("SoloPlay/Tools/Developer.js"); makeSpot = this.MakeSpot(item, { x: x, y: y }); // NOTE: passing these in buffer order [h/x][w/y] if (makeSpot) { - // this item cannot be moved - if (makeSpot === -1) return false; - - return makeSpot; + return makeSpot !== -1 ? makeSpot : false; } } } @@ -325,7 +323,7 @@ includeIfNotIncluded("SoloPlay/Tools/Developer.js"); if (item.gid === undefined) return false; // ignore same gid - if (item.gid !== this.itemList[this.buffer[x][y] - 1].gid ) { + if (item.gid !== this.itemList[this.buffer[x][y] - 1].gid) { continue; } } @@ -334,8 +332,8 @@ includeIfNotIncluded("SoloPlay/Tools/Developer.js"); for (nx = 0; nx < item.sizey; nx += 1) { for (ny = 0; ny < item.sizex; ny += 1) { if (this.buffer[x + nx][y + ny]) { - // ignore same gid - if (item.gid !== this.itemList[this.buffer[x + nx][y + ny] - 1].gid ) { + // ignore same gid + if (item.gid !== this.itemList[this.buffer[x + nx][y + ny] - 1].gid) { continue Loop; } } @@ -357,15 +355,22 @@ includeIfNotIncluded("SoloPlay/Tools/Developer.js"); Container.prototype.MakeSpot = function (item, location, force) { let x, y, endx, endy, tmpLocation; let [itemsToMove, itemsMoved] = [[], []]; + const { + ItemsSortedFromRight, + ItemsSortedFromLeftPriority, + ItemsSortedFromRightPriority + } = SetUp.sortSettings; // TODO: test the scenario where all possible items have been moved, but this item still can't be placed // e.g. if there are many LCs in an inventory and the spot for a GC can't be freed up without // moving other items that ARE NOT part of the position desired // Make sure it's a valid item and item is in a priority sorting list if (!item || !item.classid - || (SetUp.sortSettings.ItemsSortedFromRightPriority.indexOf(item.classid) === -1 - && SetUp.sortSettings.ItemsSortedFromLeftPriority.indexOf(item.classid) === -1 - && !force)) { + || ( + ItemsSortedFromRightPriority.indexOf(item.classid) === -1 + && ItemsSortedFromLeftPriority.indexOf(item.classid) === -1 + && !force) + ) { return false; // only continue if the item is in the priority sort list } @@ -403,7 +408,7 @@ includeIfNotIncluded("SoloPlay/Tools/Developer.js"); // Move any item(s) out of the way if (itemsToMove.length) { for (let i = 0; i < itemsToMove.length; i++) { - let reverseX = !(SetUp.sortSettings.ItemsSortedFromRight.includes(item.classid)); + let reverseX = !(ItemsSortedFromRight.includes(item.classid)); tmpLocation = this.FindSpot(itemsToMove[i], reverseX, false); // D2Bot.printToConsole(itemsToMove[i].name + " moving from " + itemsToMove[i].x + "," + itemsToMove[i].y + " to " + tmpLocation.y + "," + tmpLocation.x, sdk.colors.D2Bot.Gold); @@ -430,22 +435,27 @@ includeIfNotIncluded("SoloPlay/Tools/Developer.js"); * @param {number} mY */ Container.prototype.MoveToSpot = function (item, mX, mY) { - let cItem, cube; + let cube; - // handle opening cube - if (this.location === sdk.storage.Cube || item.location === sdk.storage.Cube) { + // handle getting to cube + if (this.location === sdk.storage.Cube) { cube = me.getItem(sdk.quest.item.Cube); if (!cube) return false; - if ((cube.isInStash || item.isInStash) && !getUIFlag(sdk.uiflags.Stash) && !Town.openStash()) { + if ((cube.isInStash || item.isInStash) + && !getUIFlag(sdk.uiflags.Stash) + && !Town.openStash()) { return false; } } // Cube -> Stash, must place item in inventory first - if (item.location === sdk.storage.Cube - && this.location === sdk.storage.Stash - && !Storage.Inventory.MoveTo(item)) { - return false; + if (item.isInCube && this.location === sdk.storage.Stash) { + cube = me.getItem(sdk.quest.item.Cube); + if (!cube || !Packet.itemToCursor(item)) return false; + cube.isInStash && Storage.Stash.CanFit(item) && Cubing.closeCube(true); + if (!cube.isInStash && !Storage.Inventory.MoveTo(item)) { + return false; + } } // Can't deal with items on ground! @@ -458,6 +468,7 @@ includeIfNotIncluded("SoloPlay/Tools/Developer.js"); const [orgX, orgY, orgLoc] = [item.x, item.y, item.location]; const moveItem = function (x, y, location) { + let cItem; for (let n = 0; n < 5; n += 1) { switch (location) { case sdk.storage.Belt: @@ -477,6 +488,9 @@ includeIfNotIncluded("SoloPlay/Tools/Developer.js"); } break; case sdk.storage.Stash: + if (!getUIFlag(sdk.uiflags.Stash) && !Town.openStash()) { + continue; + } sendPacket(1, sdk.packets.send.ItemToBuffer, 4, item.gid, 4, x, 4, y, 4, 0x04); break; @@ -511,7 +525,8 @@ includeIfNotIncluded("SoloPlay/Tools/Developer.js"); */ Container.prototype.MoveTo = function (item) { try { - // Can we even fit it in here? + if (item.location === this.location) return true; + // Can we even fit it in here? let nPos = this.FindSpot(item); if (!nPos) return false; From cdda28dbbfd9b1170e20921c83cd3fae3c9e8acc Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Mon, 8 Jan 2024 18:10:24 -0800 Subject: [PATCH 237/263] Update CubingOverrides.js - Redo `Cubing.emptytCube` should be more efficient now. Move all items we can first to the invo without closing out the cube, then move the items to the stash. Log any that fail to make it to the stash --- libs/SoloPlay/Functions/CubingOverrides.js | 59 ++++++++++++++++++---- 1 file changed, 50 insertions(+), 9 deletions(-) diff --git a/libs/SoloPlay/Functions/CubingOverrides.js b/libs/SoloPlay/Functions/CubingOverrides.js index 1573c575..68a2298b 100644 --- a/libs/SoloPlay/Functions/CubingOverrides.js +++ b/libs/SoloPlay/Functions/CubingOverrides.js @@ -595,26 +595,67 @@ Cubing.buildLists = function () { // Added try again to emptying cube if it fails it will clear inventory then organize it Cubing.emptyCube = function () { - let cube = me.getItem(sdk.items.quest.Cube); - let items = me.findItems(-1, -1, sdk.storage.Cube); - if (!cube || !items) return false; + const locToName = {}; + locToName[sdk.storage.Cube] = "Cube"; + locToName[sdk.storage.Inventory] = "Inventory"; + locToName[sdk.storage.Stash] = "Stash"; + /** @param {ItemUnit} item */ + const prettyPrint = function (item) { + return item && ("- " + item.prettyPrint + " Location: " + (locToName[item.location] || "") + "\n"); + }; + + const cube = me.getItem(sdk.items.quest.Cube); + if (!cube) return false; + + const items = me.findItems(-1, -1, sdk.storage.Cube); + if (!items) return true; + + items.sort(function (a, b) { + return b.sizex * b.sizey - a.sizex * a.sizey; + }); + + /** @type {ItemUnit[]} */ + const failedItems = []; + let [invoSorted, stashSorted, failed] = [false, false, false]; while (items.length) { - !getUIFlag(sdk.uiflags.Cube) && Cubing.openCube(); + const item = items[0]; + + item.isInCube && !getUIFlag(sdk.uiflags.Cube) && Cubing.openCube(); + + if (item.isInCube && Storage.Inventory.CanFit(item) && Storage.Inventory.MoveTo(item)) { + // Move anything we can to the inventory first, so we don't have to open/close the cube + items.push(item); + items.shift(); + continue; + } - if (!Storage.Stash.MoveTo(items[0]) && !Storage.Inventory.MoveTo(items[0])) { + if (!invoSorted && !Storage.Inventory.CanFit(item)) { Town.clearInventory(); me.sortInventory(); + invoSorted = true; + } - if (!Storage.Stash.MoveTo(items[0]) && !Storage.Inventory.MoveTo(items[0])) { - return false; - } + if (!stashSorted && !Storage.Stash.CanFit(item)) { + Town.sortStash(); + stashSorted = true; + } + + if (!Storage.Stash.MoveTo(item)) { + failed = true; + failedItems.push(item); } items.shift(); } - return true; + this.closeCube(); + + if (failed) { + console.log("Failed to get all items from cube to stash. Items left: \n" + failedItems.map(prettyPrint).join(", ")); + } + + return !failed; }; /** @param {ItemUnit} unit */ From 7d12525c04a9315eb2a4af2ad9dff991d0df9428 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Mon, 8 Jan 2024 18:12:38 -0800 Subject: [PATCH 238/263] Add `Unit.size` prototype - mostly cleanup otherwise --- libs/SoloPlay/Functions/NPCAction.js | 34 +++++++++++++------ libs/SoloPlay/Functions/NTIPOverrides.js | 3 ++ libs/SoloPlay/Functions/PrototypeOverrides.js | 21 ++++++++++++ libs/SoloPlay/Functions/Quest.js | 2 +- 4 files changed, 49 insertions(+), 11 deletions(-) diff --git a/libs/SoloPlay/Functions/NPCAction.js b/libs/SoloPlay/Functions/NPCAction.js index 15d41c8f..49edb966 100644 --- a/libs/SoloPlay/Functions/NPCAction.js +++ b/libs/SoloPlay/Functions/NPCAction.js @@ -94,7 +94,9 @@ // Check if we need any potions for buffers if (buffer.mp < Config.MPBuffer || buffer.hp < Config.HPBuffer) { - if (Config.BeltColumn.some((c, i) => col[i] >= beltSize && (!needPots || c === "rv"))) { + if (Config.BeltColumn.some(function (c, i) { + return col[i] >= beltSize && (!needPots || c === "rv"); + })) { specialCheck = true; } } @@ -109,17 +111,23 @@ let [wantedHpPot, wantedMpPot] = [5, 5]; // only do this if we are low on gold in the first place if (me.normal && me.gold < Config.LowGold) { - const mpPotsEffects = PotData.getMpPots().map(el => el.effect[me.classid]); - const hpPotsEffects = PotData.getHpPots().map(el => el.effect[me.classid]); + const mpPotsEffects = PotData.getMpPots().map(function (el) { + return el.effect[me.classid]; + }); + const hpPotsEffects = PotData.getHpPots().map(function (el) { + return el.effect[me.classid]; + }); wantedHpPot = (hpPotsEffects.findIndex(eff => me.hpmax / 2 < eff) + 1 || hpPotsEffects.length - 1); wantedMpPot = (mpPotsEffects.findIndex(eff => me.mpmax / 2 < eff) + 1 || mpPotsEffects.length - 1); console.debug("Wanted hpPot: " + wantedHpPot + " Wanted mpPot: " + wantedMpPot); } - if (me.normal && me.highestAct >= 4) { - let pAct = Math.max(wantedHpPot, wantedMpPot); - pAct >= 4 ? me.act < 4 && Town.goToTown(4) : pAct > me.act && Town.goToTown(pAct); + if (me.normal) { + if (me.highestAct >= 4) { + let pAct = Math.max(wantedHpPot, wantedMpPot); + pAct >= 4 ? me.act < 4 && Town.goToTown(4) : pAct > me.act && Town.goToTown(pAct); + } } else if (!me.normal && me.act === 3 && Town.getDistance(Town.tasks.get(me.act).Shop) > 10) { // if we need to repair items as well or stack pots we should go ahead and change act @@ -141,7 +149,9 @@ if (!npc) return false; // special check, sometimes our rejuv slot is empty but we do still need buffer. Check if we can buy something to slot there - if (specialCheck && Config.BeltColumn.some((c, i) => c === "rv" && col[i] >= beltSize)) { + if (specialCheck && Config.BeltColumn.some(function (c, i) { + return c === "rv" && col[i] >= beltSize; + })) { let pots = [sdk.items.ThawingPotion, sdk.items.AntidotePotion, sdk.items.StaminaPotion]; Config.BeltColumn.forEach(function (c, i) { if (c === "rv" && col[i] >= beltSize && pots.length) { @@ -150,7 +160,9 @@ if (pot) { Storage.Inventory.CanFit(pot) && Packet.buyItem(pot, false); pot = me.getItemsEx(usePot, sdk.items.mode.inStorage) - .filter(i => i.isInInventory) + .filter(function (i) { + return i.isInInventory; + }) .first(); !!pot && Packet.placeInBelt(pot, i); pots.shift(); @@ -168,7 +180,7 @@ let pot = Town.getPotion(npc, Config.BeltColumn[i], wantedPot); if (pot) { - // print("ÿc2column ÿc0" + i + "ÿc2 needs ÿc0" + col[i] + " ÿc2potions"); + // console.log("ÿc2column ÿc0" + i + "ÿc2 needs ÿc0" + col[i] + " ÿc2potions"); // Shift+buy will trigger if there's no empty columns or if only the current column is empty if (useShift) { pot.buy(true); @@ -247,7 +259,9 @@ let _needStack = CharData.pots.get("thawing").need() || CharData.pots.get("antidote").need(); let _needMerc = me.needMerc(); if (_needRepair || _needStack || _needMerc) { - Town.goToTown(me.highestAct >= 4 ? 4 : 1); + if (!me.normal || me.accessToAct(4) || !me.needPotions()) { + Town.goToTown(me.highestAct >= 4 ? 4 : 1); + } } } diff --git a/libs/SoloPlay/Functions/NTIPOverrides.js b/libs/SoloPlay/Functions/NTIPOverrides.js index 893e8005..53e5f309 100644 --- a/libs/SoloPlay/Functions/NTIPOverrides.js +++ b/libs/SoloPlay/Functions/NTIPOverrides.js @@ -616,6 +616,9 @@ NTIP.ParseLineInt = function (input, info) { ["hardcore", "(!!me.playertype)"], ["classic", "(!me.gametype)"], ["distance", "(item.onGroundOrDropping && item.distance || Infinity)"], + ["flag", "item.getFlag("], + ["prefix", "item.getPrefix("], + ["suffix", "item.getSuffix("] ]); let p_result = input.split("#"); diff --git a/libs/SoloPlay/Functions/PrototypeOverrides.js b/libs/SoloPlay/Functions/PrototypeOverrides.js index 6c23721d..27eedf50 100644 --- a/libs/SoloPlay/Functions/PrototypeOverrides.js +++ b/libs/SoloPlay/Functions/PrototypeOverrides.js @@ -15,6 +15,7 @@ includeIfNotIncluded("SoloPlay/Functions/Polyfills.js"); */ if (!Unit.prototype.hasOwnProperty("isCharm")) { Object.defineProperty(Unit.prototype, "isCharm", { + /** @this {ItemUnit} */ get: function () { if (this.type !== sdk.unittype.Item) return false; return [sdk.items.SmallCharm, sdk.items.LargeCharm, sdk.items.GrandCharm].includes(this.classid); @@ -24,6 +25,7 @@ if (!Unit.prototype.hasOwnProperty("isCharm")) { if (!Unit.prototype.hasOwnProperty("isGem")) { Object.defineProperty(Unit.prototype, "isGem", { + /** @this {ItemUnit} */ get: function () { if (this.type !== sdk.unittype.Item) return false; return ( @@ -36,6 +38,7 @@ if (!Unit.prototype.hasOwnProperty("isGem")) { if (!Unit.prototype.hasOwnProperty("isInsertable")) { Object.defineProperty(Unit.prototype, "isInsertable", { + /** @this {ItemUnit} */ get: function () { if (this.type !== sdk.unittype.Item) return false; return [sdk.items.type.Jewel, sdk.items.type.Rune].includes(this.itemType) || this.isGem; @@ -45,6 +48,7 @@ if (!Unit.prototype.hasOwnProperty("isInsertable")) { if (!Unit.prototype.hasOwnProperty("isRuneword")) { Object.defineProperty(Unit.prototype, "isRuneword", { + /** @this {ItemUnit} */ get: function () { if (this.type !== sdk.unittype.Item) return false; return !!this.getFlag(sdk.items.flags.Runeword); @@ -54,6 +58,7 @@ if (!Unit.prototype.hasOwnProperty("isRuneword")) { if (!Unit.prototype.hasOwnProperty("isBroken")) { Object.defineProperty(Unit.prototype, "isBroken", { + /** @this {ItemUnit} */ get: function () { if (this.type !== sdk.unittype.Item) return false; if (this.getStat(sdk.stats.Indestructible)) return false; @@ -64,6 +69,7 @@ if (!Unit.prototype.hasOwnProperty("isBroken")) { if (!Unit.prototype.hasOwnProperty("isStunned")) { Object.defineProperty(Unit.prototype, "isStunned", { + /** @this {Monster | Player} */ get: function () { return this.getState(sdk.states.Stunned); }, @@ -72,6 +78,7 @@ if (!Unit.prototype.hasOwnProperty("isStunned")) { if (!Unit.prototype.hasOwnProperty("isUnderCoS")) { Object.defineProperty(Unit.prototype, "isUnderCoS", { + /** @this {Monster | Player} */ get: function () { return this.getState(sdk.states.Cloaked); }, @@ -80,14 +87,28 @@ if (!Unit.prototype.hasOwnProperty("isUnderCoS")) { if (!Unit.prototype.hasOwnProperty("isUnderLowerRes")) { Object.defineProperty(Unit.prototype, "isUnderLowerRes", { + /** @this {Monster | Player} */ get: function () { return this.getState(sdk.states.LowerResist); }, }); } +if (!Unit.prototype.hasOwnProperty("size")) { + Object.defineProperty(Unit.prototype, "size", { + /** @this {Monster} */ + get: function () { + if (this.type !== sdk.unittype.Monster) return 0; + const baseId = getBaseStat("monstats", this.classid, "baseid"); + const size = getBaseStat("monstats2", baseId, "sizex"); + return (typeof size !== "number" || size < 1 || size > 3) ? 3 : size; + }, + }); +} + if (!Unit.prototype.hasOwnProperty("isBaseType")) { Object.defineProperty(Unit.prototype, "isBaseType", { + /** @this {ItemUnit} */ get: function () { if (this.type !== sdk.unittype.Item) return false; return [sdk.items.quality.Normal, sdk.items.quality.Superior].includes(this.quality) diff --git a/libs/SoloPlay/Functions/Quest.js b/libs/SoloPlay/Functions/Quest.js index 9450c13f..e0611568 100644 --- a/libs/SoloPlay/Functions/Quest.js +++ b/libs/SoloPlay/Functions/Quest.js @@ -380,7 +380,7 @@ const Quest = { me.data[sdk.difficulty.nameOf(me.diff).toLowerCase()].respecUsed = true; CharData.updateData("me", me.data); delay(750 + me.ping * 2); - Town.clearBelt(); + me.clearBelt(); myPrint("respec done, restarting"); delay(1000 + me.ping); scriptBroadcast("quit"); From 22ac3a182228e6eb0953a15a4332f503162e4ab7 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Tue, 9 Jan 2024 10:04:41 -0800 Subject: [PATCH 239/263] [BugFix] SetUp.init check for merc - Was failing to update the merc prop --- libs/SoloPlay/Functions/Globals.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libs/SoloPlay/Functions/Globals.js b/libs/SoloPlay/Functions/Globals.js index 9fc95c95..fce5e0d0 100644 --- a/libs/SoloPlay/Functions/Globals.js +++ b/libs/SoloPlay/Functions/Globals.js @@ -205,8 +205,8 @@ const SetUp = { let changed = Misc.recursiveSearch(me.data.merc, _tempMerc); if (Object.keys(changed).length > 0) { - // CharData.updateData("merc", me.data.merc); - mUpdate = true; + CharData.updateData("merc", me.data); + // mUpdate = true; } } From 7470c8a15da9fce40c688c9ac3649d8d7209c361 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Tue, 9 Jan 2024 10:05:55 -0800 Subject: [PATCH 240/263] Update TownOverrides.js - Prevent going to a1 in normal to buy pots - Add force param to `Town.heal` --- libs/SoloPlay/Functions/TownOverrides.js | 26 ++++++++++++++++-------- 1 file changed, 17 insertions(+), 9 deletions(-) diff --git a/libs/SoloPlay/Functions/TownOverrides.js b/libs/SoloPlay/Functions/TownOverrides.js index 2b3c460e..9ec3d72c 100644 --- a/libs/SoloPlay/Functions/TownOverrides.js +++ b/libs/SoloPlay/Functions/TownOverrides.js @@ -230,7 +230,9 @@ Town.initNPC = function (task = "", reason = "undefined") { return false; } - Misc.poll(() => me.gameReady, 2000, 250); + Misc.poll(function () { + return me.gameReady; + }, 2000, 250); if (task === "Heal") { Config.DebugMode.Town && console.debug("Checking if we are frozen"); @@ -244,10 +246,10 @@ Town.initNPC = function (task = "", reason = "undefined") { }; /** -* @description Go to a town healer if we are below certain hp/mp percent or have a status effect -*/ -Town.heal = function () { - if (!me.needHealing()) return true; + * @description Go to a town healer if we are below certain hp/mp percent or have a status effect + */ +Town.heal = function (force = false) { + if (!me.needHealing() && !force) return true; if (me.act === 3 && Town.getDistance(Town.tasks.get(me.act).Heal) > 10) { // if we need to repair items as well or stack pots we should go ahead and change act @@ -255,9 +257,12 @@ Town.heal = function () { let _needRepair = me.needRepair().length > 0; let _needStack = CharData.pots.get("thawing").need() || CharData.pots.get("antidote").need(); let _needMerc = me.needMerc(); - let _needPotions = me.normal && me.accessToAct(4) && me.needPotions(); + let _needPotions = me.needPotions(); if (_needRepair || _needStack || _needMerc || _needPotions) { - Town.goToTown(me.highestAct >= 4 ? 4 : 1); + if (!_needPotions || !me.normal || me.accessToAct(4)) { + // trying to prevent us from going to a1 and ending up buying minor pots in normal + Town.goToTown(me.highestAct >= 4 ? 4 : 1); + } } } return !!(this.initNPC("Heal", "heal")); @@ -776,9 +781,12 @@ Town.clearInventory = function () { let _needRepair = me.needRepair().length > 0; let _needStack = CharData.pots.get("thawing").need() || CharData.pots.get("antidote").need(); let _needMerc = me.needMerc(); - let _needPotions = me.normal && me.accessToAct(4) && me.needPotions(); + let _needPotions = me.needPotions(); if (_needRepair || _needStack || _needMerc || _needPotions) { - Town.goToTown(me.highestAct >= 4 ? 4 : 1); + if (!_needPotions || !me.normal || me.accessToAct(4)) { + // trying to prevent us from going to a1 and ending up buying minor pots in normal + Town.goToTown(me.highestAct >= 4 ? 4 : 1); + } } } if (this.initNPC("Shop", "clearInventory")) { From 8c67385cbf0bef8a0582d7a849caee725446ceab Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Thu, 11 Jan 2024 12:56:52 -0800 Subject: [PATCH 241/263] Update NTIPOverrides.js - add aliases map + small cleanup in switch statement --- libs/SoloPlay/Functions/NTIPOverrides.js | 39 ++++++++++++------------ 1 file changed, 19 insertions(+), 20 deletions(-) diff --git a/libs/SoloPlay/Functions/NTIPOverrides.js b/libs/SoloPlay/Functions/NTIPOverrides.js index 53e5f309..507ad4ea 100644 --- a/libs/SoloPlay/Functions/NTIPOverrides.js +++ b/libs/SoloPlay/Functions/NTIPOverrides.js @@ -621,6 +621,19 @@ NTIP.ParseLineInt = function (input, info) { ["suffix", "item.getSuffix("] ]); + const _aliases = new Map([ + ["n", "name"], + ["id", "classid"], + ["t", "type"], + ["q", "quality"], + ["lvl", "level"], + ["ilvl", "level"], + ["f", "flag"], + ["hc", "hardcore"], + ["cl", "classic"], + ["clvl", "charlvl"], + ]); + let p_result = input.split("#"); if (p_result[0] && p_result[0].length > 4) { @@ -632,32 +645,18 @@ NTIP.ParseLineInt = function (input, info) { p_end = p_section[i].indexOf("]") + 1; property = p_section[i].substring(0, p_end - 1); + if (_aliases.has(property)) { + property = _aliases.get(property); + } + switch (property) { case "flag": - if (p_section[i][p_end] === "!") { - p_result[0] += "!item.getFlag("; - } else { - p_result[0] += "item.getFlag("; - } - - p_end += 2; - - break; case "prefix": - if (p_section[i][p_end] === "!") { - p_result[0] += "!item.getPrefix("; - } else { - p_result[0] += "item.getPrefix("; - } - - p_end += 2; - - break; case "suffix": if (p_section[i][p_end] === "!") { - p_result[0] += "!item.getSuffix("; + p_result[0] += "!" + _props.get(property); } else { - p_result[0] += "item.getSuffix("; + p_result[0] += _props.get(property); } p_end += 2; From ea5690f0fcc0ee6547b8d481bd9870e03f9677cb Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Sun, 21 Jan 2024 19:02:46 -0500 Subject: [PATCH 242/263] replace `haveItem` with `checkItem` - cleanup mostly, depreciating the useage of `Check.haveItem` --- libs/SoloPlay/BuildFiles/Runewords/Lore.js | 2 +- .../amazon/amazon.FaithbowzonBuild.js | 48 +++++++++++++---- .../amazon/amazon.FrostmaidenBuild.js | 5 ++ .../BuildFiles/amazon/amazon.JavazonBuild.js | 4 ++ .../BuildFiles/amazon/amazon.WfzonBuild.js | 48 +++++++++++++---- .../amazon/amazon.WitchyzonBuild.js | 48 +++++++++++++---- .../assassin/assassin.TrapsinBuild.js | 7 ++- .../assassin/assassin.WhirlsinBuild.js | 5 ++ libs/SoloPlay/BuildFiles/assassin/assassin.js | 2 +- .../barbarian/barbarian.FrenzyBuild.js | 5 ++ .../barbarian/barbarian.ImmortalwhirlBuild.js | 4 ++ .../barbarian/barbarian.SingerBuild.js | 4 ++ .../barbarian/barbarian.ThrowBuild.js | 50 +++++++++++++---- .../barbarian/barbarian.UberconcBuild.js | 39 +++++++++++--- .../barbarian/barbarian.WhirlwindBuild.js | 4 ++ .../BuildFiles/barbarian/barbarian.js | 2 +- .../BuildFiles/druid/druid.ElementalBuild.js | 6 ++- .../BuildFiles/druid/druid.FirewolfBuild.js | 3 ++ .../BuildFiles/druid/druid.PlaguewolfBuild.js | 3 ++ .../BuildFiles/druid/druid.StormbearBuild.js | 3 ++ .../BuildFiles/druid/druid.WindBuild.js | 6 ++- .../BuildFiles/druid/druid.WolfBuild.js | 45 +++++++++++++--- .../necromancer/necromancer.BoneBuild.js | 6 ++- .../necromancer/necromancer.PoisonBuild.js | 8 ++- .../necromancer/necromancer.StartBuild.js | 11 ++-- .../necromancer/necromancer.SummonBuild.js | 7 ++- .../paladin/paladin.AuradinBuild.js | 3 ++ .../paladin/paladin.ClassicauradinBuild.js | 3 ++ .../paladin/paladin.HammerdinBuild.js | 25 ++++++--- .../paladin/paladin.HammershockBuild.js | 53 +++++++++++++++---- .../paladin/paladin.SancdreamerBuild.js | 48 +++++++++++++---- .../BuildFiles/paladin/paladin.SmiterBuild.js | 32 +++++++++-- .../paladin/paladin.TorchadinBuild.js | 40 +++++++++++--- .../BuildFiles/paladin/paladin.ZealerBuild.js | 32 +++++++++-- libs/SoloPlay/BuildFiles/paladin/paladin.js | 8 ++- .../sorceress/sorceress.BlizzballerBuild.js | 5 ++ .../sorceress/sorceress.BlovaBuild.js | 12 ++++- .../sorceress/sorceress.ColdBuild.js | 4 ++ .../sorceress/sorceress.EsorbBuild.js | 5 ++ .../sorceress/sorceress.LevelingBuild.js | 31 ++++++++--- .../sorceress/sorceress.LightningBuild.js | 37 ++++++++++--- .../sorceress/sorceress.MeteorbBuild.js | 5 ++ .../sorceress/sorceress.StartBuild.js | 3 +- .../sorceress/sorceress.SteppingBuild.js | 33 +++++++++--- .../BuildFiles/sorceress/sorceress.js | 2 +- libs/SoloPlay/Config/Amazon.js | 8 ++- libs/SoloPlay/Config/Barbarian.js | 8 ++- libs/SoloPlay/Config/Druid.js | 2 +- libs/SoloPlay/Config/Necromancer.js | 4 +- libs/SoloPlay/Config/Paladin.js | 13 +++-- libs/SoloPlay/Config/Sorceress.js | 16 +++++- 51 files changed, 662 insertions(+), 145 deletions(-) diff --git a/libs/SoloPlay/BuildFiles/Runewords/Lore.js b/libs/SoloPlay/BuildFiles/Runewords/Lore.js index 07d0ba20..ce271e1d 100644 --- a/libs/SoloPlay/BuildFiles/Runewords/Lore.js +++ b/libs/SoloPlay/BuildFiles/Runewords/Lore.js @@ -1,5 +1,5 @@ (function () { - if (!Check.haveItem("helm", "runeword", "Lore")) { + if (!me.checkItem({ name: sdk.locale.items.Lore }).have) { const loreRunes = [ "[name] == OrtRune # # [maxquantity] == 1", "[name] == SolRune # # [maxquantity] == 1", diff --git a/libs/SoloPlay/BuildFiles/amazon/amazon.FaithbowzonBuild.js b/libs/SoloPlay/BuildFiles/amazon/amazon.FaithbowzonBuild.js index de5e7807..86abc663 100644 --- a/libs/SoloPlay/BuildFiles/amazon/amazon.FaithbowzonBuild.js +++ b/libs/SoloPlay/BuildFiles/amazon/amazon.FaithbowzonBuild.js @@ -39,8 +39,14 @@ max: 3, have: [], classid: sdk.items.SmallCharm, + /** @param {ItemUnit} check */ stats: function (check) { - return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MaxHp) === 20); + return ( + !check.unique + && check.classid === this.classid + && check.allRes === 5 + && check.getStat(sdk.stats.MaxHp) === 20 + ); } }, @@ -48,8 +54,14 @@ max: 2, have: [], classid: sdk.items.SmallCharm, + /** @param {ItemUnit} check */ stats: function (check) { - return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MagicBonus) === 7); + return ( + !check.unique + && check.classid === this.classid + && check.allRes === 5 + && check.getStat(sdk.stats.MagicBonus) === 7 + ); } }, @@ -57,8 +69,14 @@ max: 1, have: [], classid: sdk.items.SmallCharm, + /** @param {ItemUnit} check */ stats: function (check) { - return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.FHR) === 5); + return ( + !check.unique + && check.classid === this.classid + && check.allRes === 5 + && check.getStat(sdk.stats.FHR) === 5 + ); } }, @@ -66,9 +84,14 @@ max: 1, have: [], classid: sdk.items.GrandCharm, + /** @param {ItemUnit} check */ stats: function (check) { - return (!check.unique && check.classid === this.classid && check.getStat(sdk.stats.AddSkillTab, sdk.skills.tabs.BowandCrossbow) === 1 - && check.getStat(sdk.stats.MaxHp) >= 40); + return ( + !check.unique + && check.classid === this.classid + && check.getStat(sdk.stats.AddSkillTab, sdk.skills.tabs.BowandCrossbow) === 1 + && check.getStat(sdk.stats.MaxHp) >= 40 + ); } }, @@ -76,9 +99,14 @@ max: 1, have: [], classid: sdk.items.GrandCharm, + /** @param {ItemUnit} check */ stats: function (check) { - return (!check.unique && check.classid === this.classid && check.getStat(sdk.stats.AddSkillTab, sdk.skills.tabs.PassiveandMagic) === 1 - && check.getStat(sdk.stats.MaxHp) >= 40); + return ( + !check.unique + && check.classid === this.classid + && check.getStat(sdk.stats.AddSkillTab, sdk.skills.tabs.PassiveandMagic) === 1 + && check.getStat(sdk.stats.MaxHp) >= 40 + ); } }, }, @@ -97,9 +125,11 @@ respec: function () { if (me.classic) { return false; - } else { - return (me.checkItem({ name: sdk.locale.items.Faith }).have || Check.haveItem("diamondbow", "unique", "Witchwild String")); } + return ( + me.checkItem({ name: sdk.locale.items.Faith }).have + || me.checkItem({ name: sdk.locale.items.WitchwildString, classid: sdk.items.DiamondBow }).have + ); }, active: function () { diff --git a/libs/SoloPlay/BuildFiles/amazon/amazon.FrostmaidenBuild.js b/libs/SoloPlay/BuildFiles/amazon/amazon.FrostmaidenBuild.js index 6b6cbcb5..17940b88 100644 --- a/libs/SoloPlay/BuildFiles/amazon/amazon.FrostmaidenBuild.js +++ b/libs/SoloPlay/BuildFiles/amazon/amazon.FrostmaidenBuild.js @@ -34,6 +34,7 @@ max: 3, have: [], classid: sdk.items.SmallCharm, + /** @param {ItemUnit} check */ stats: function (check) { return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MaxHp) === 20); } @@ -43,6 +44,7 @@ max: 2, have: [], classid: sdk.items.SmallCharm, + /** @param {ItemUnit} check */ stats: function (check) { return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MagicBonus) === 7); } @@ -52,6 +54,7 @@ max: 1, have: [], classid: sdk.items.SmallCharm, + /** @param {ItemUnit} check */ stats: function (check) { return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.FHR) === 5); } @@ -61,6 +64,7 @@ max: 1, have: [], classid: sdk.items.GrandCharm, + /** @param {ItemUnit} check */ stats: function (check) { return (!check.unique && check.classid === this.classid && check.getStat(sdk.stats.AddSkillTab, sdk.skills.tabs.BowandCrossbow) === 1 && check.getStat(sdk.stats.MaxHp) >= 40); @@ -71,6 +75,7 @@ max: 1, have: [], classid: sdk.items.GrandCharm, + /** @param {ItemUnit} check */ stats: function (check) { return (!check.unique && check.classid === this.classid && check.getStat(sdk.stats.AddSkillTab, sdk.skills.tabs.PassiveandMagic) === 1 && check.getStat(sdk.stats.MaxHp) >= 40); diff --git a/libs/SoloPlay/BuildFiles/amazon/amazon.JavazonBuild.js b/libs/SoloPlay/BuildFiles/amazon/amazon.JavazonBuild.js index 740ad5a9..03038acd 100644 --- a/libs/SoloPlay/BuildFiles/amazon/amazon.JavazonBuild.js +++ b/libs/SoloPlay/BuildFiles/amazon/amazon.JavazonBuild.js @@ -24,6 +24,7 @@ max: 5, have: [], classid: sdk.items.SmallCharm, + /** @param {ItemUnit} check */ stats: function (check) { return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MaxHp) === 20); } @@ -33,6 +34,7 @@ max: 2, have: [], classid: sdk.items.SmallCharm, + /** @param {ItemUnit} check */ stats: function (check) { return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MagicBonus) === 7); } @@ -42,6 +44,7 @@ max: 1, have: [], classid: sdk.items.SmallCharm, + /** @param {ItemUnit} check */ stats: function (check) { return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.FHR) === 5); } @@ -51,6 +54,7 @@ max: 2, have: [], classid: sdk.items.GrandCharm, + /** @param {ItemUnit} check */ stats: function (check) { return (!check.unique && check.classid === this.classid && check.getStat(sdk.stats.AddSkillTab, sdk.skills.tabs.JavelinandSpear) === 1 && check.getStat(sdk.stats.MaxHp) >= 40); diff --git a/libs/SoloPlay/BuildFiles/amazon/amazon.WfzonBuild.js b/libs/SoloPlay/BuildFiles/amazon/amazon.WfzonBuild.js index b5af9faa..2a3bb1a3 100644 --- a/libs/SoloPlay/BuildFiles/amazon/amazon.WfzonBuild.js +++ b/libs/SoloPlay/BuildFiles/amazon/amazon.WfzonBuild.js @@ -36,8 +36,14 @@ max: 3, have: [], classid: sdk.items.SmallCharm, + /** @param {ItemUnit} check */ stats: function (check) { - return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MaxHp) === 20); + return ( + !check.unique + && check.classid === this.classid + && check.allRes === 5 + && check.getStat(sdk.stats.MaxHp) === 20 + ); } }, @@ -45,8 +51,14 @@ max: 2, have: [], classid: sdk.items.SmallCharm, + /** @param {ItemUnit} check */ stats: function (check) { - return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MagicBonus) === 7); + return ( + !check.unique + && check.classid === this.classid + && check.allRes === 5 + && check.getStat(sdk.stats.MagicBonus) === 7 + ); } }, @@ -54,8 +66,14 @@ max: 1, have: [], classid: sdk.items.SmallCharm, + /** @param {ItemUnit} check */ stats: function (check) { - return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.FHR) === 5); + return ( + !check.unique + && check.classid === this.classid + && check.allRes === 5 + && check.getStat(sdk.stats.FHR) === 5 + ); } }, @@ -63,9 +81,14 @@ max: 1, have: [], classid: sdk.items.GrandCharm, + /** @param {ItemUnit} check */ stats: function (check) { - return (!check.unique && check.classid === this.classid && check.getStat(sdk.stats.AddSkillTab, sdk.skills.tabs.BowandCrossbow) === 1 - && check.getStat(sdk.stats.MaxHp) >= 40); + return ( + !check.unique + && check.classid === this.classid + && check.getStat(sdk.stats.AddSkillTab, sdk.skills.tabs.BowandCrossbow) === 1 + && check.getStat(sdk.stats.MaxHp) >= 40 + ); } }, @@ -73,9 +96,14 @@ max: 1, have: [], classid: sdk.items.GrandCharm, + /** @param {ItemUnit} check */ stats: function (check) { - return (!check.unique && check.classid === this.classid && check.getStat(sdk.stats.AddSkillTab, sdk.skills.tabs.PassiveandMagic) === 1 - && check.getStat(sdk.stats.MaxHp) >= 40); + return ( + !check.unique + && check.classid === this.classid + && check.getStat(sdk.stats.AddSkillTab, sdk.skills.tabs.PassiveandMagic) === 1 + && check.getStat(sdk.stats.MaxHp) >= 40 + ); } }, }, @@ -92,9 +120,11 @@ respec: function () { if (me.classic) { return false; - } else { - return (Check.haveItem("hydrabow", "unique", "Windforce") || Check.haveItem("diamondbow", "unique", "Witchwild String")); } + return ( + Check.haveItem("hydrabow", "unique", "Windforce") + || me.checkItem({ name: sdk.locale.items.WitchwildString, classid: sdk.items.DiamondBow }).have + ); }, active: function () { diff --git a/libs/SoloPlay/BuildFiles/amazon/amazon.WitchyzonBuild.js b/libs/SoloPlay/BuildFiles/amazon/amazon.WitchyzonBuild.js index f21721df..72d08cd1 100644 --- a/libs/SoloPlay/BuildFiles/amazon/amazon.WitchyzonBuild.js +++ b/libs/SoloPlay/BuildFiles/amazon/amazon.WitchyzonBuild.js @@ -38,8 +38,14 @@ max: 3, have: [], classid: sdk.items.SmallCharm, + /** @param {ItemUnit} check */ stats: function (check) { - return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MaxHp) === 20); + return ( + !check.unique + && check.classid === this.classid + && check.allRes === 5 + && check.getStat(sdk.stats.MaxHp) === 20 + ); } }, @@ -47,8 +53,14 @@ max: 2, have: [], classid: sdk.items.SmallCharm, + /** @param {ItemUnit} check */ stats: function (check) { - return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MagicBonus) === 7); + return ( + !check.unique + && check.classid === this.classid + && check.allRes === 5 + && check.getStat(sdk.stats.MagicBonus) === 7 + ); } }, @@ -56,8 +68,14 @@ max: 1, have: [], classid: sdk.items.SmallCharm, + /** @param {ItemUnit} check */ stats: function (check) { - return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.FHR) === 5); + return ( + !check.unique + && check.classid === this.classid + && check.allRes === 5 + && check.getStat(sdk.stats.FHR) === 5 + ); } }, @@ -65,9 +83,14 @@ max: 1, have: [], classid: sdk.items.GrandCharm, + /** @param {ItemUnit} check */ stats: function (check) { - return (!check.unique && check.classid === this.classid && check.getStat(sdk.stats.AddSkillTab, sdk.skills.tabs.BowandCrossbow) === 1 - && check.getStat(sdk.stats.MaxHp) >= 40); + return ( + !check.unique + && check.classid === this.classid + && check.getStat(sdk.stats.AddSkillTab, sdk.skills.tabs.BowandCrossbow) === 1 + && check.getStat(sdk.stats.MaxHp) >= 40 + ); } }, @@ -75,9 +98,14 @@ max: 1, have: [], classid: sdk.items.GrandCharm, + /** @param {ItemUnit} check */ stats: function (check) { - return (!check.unique && check.classid === this.classid && check.getStat(sdk.stats.AddSkillTab, sdk.skills.tabs.PassiveandMagic) === 1 - && check.getStat(sdk.stats.MaxHp) >= 40); + return ( + !check.unique + && check.classid === this.classid + && check.getStat(sdk.stats.AddSkillTab, sdk.skills.tabs.PassiveandMagic) === 1 + && check.getStat(sdk.stats.MaxHp) >= 40 + ); } }, }, @@ -94,9 +122,11 @@ respec: function () { if (me.classic) { return false; - } else { - return Check.haveItem("diamondbow", "unique", "Witchwild String") && Check.haveItem("vampirefangbelt", "unique", "Nosferatu's Coil"); } + return ( + me.checkItem({ name: sdk.locale.items.WitchwildString, classid: sdk.items.DiamondBow }).have + && me.checkItem({ name: sdk.locale.items.NosferatusCoil }).have + ); }, active: function () { diff --git a/libs/SoloPlay/BuildFiles/assassin/assassin.TrapsinBuild.js b/libs/SoloPlay/BuildFiles/assassin/assassin.TrapsinBuild.js index 1bed4844..ecccaa8a 100644 --- a/libs/SoloPlay/BuildFiles/assassin/assassin.TrapsinBuild.js +++ b/libs/SoloPlay/BuildFiles/assassin/assassin.TrapsinBuild.js @@ -45,6 +45,7 @@ max: 3, have: [], classid: sdk.items.SmallCharm, + /** @param {ItemUnit} check */ stats: function (check) { return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MaxHp) === 20); } @@ -54,6 +55,7 @@ max: 2, have: [], classid: sdk.items.SmallCharm, + /** @param {ItemUnit} check */ stats: function (check) { return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MagicBonus) === 7); } @@ -63,6 +65,7 @@ max: 1, have: [], classid: sdk.items.SmallCharm, + /** @param {ItemUnit} check */ stats: function (check) { return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.FHR) === 5); } @@ -72,6 +75,7 @@ max: 2, have: [], classid: sdk.items.SmallCharm, + /** @param {ItemUnit} check */ stats: function (check) { return (!check.unique && check.classid === this.classid && check.getStat(sdk.stats.MaxHp) === 20 && check.getStat(sdk.stats.MaxMana) === 17); } @@ -81,6 +85,7 @@ max: 2, have: [], classid: sdk.items.GrandCharm, + /** @param {ItemUnit} check */ stats: function (check) { return (!check.unique && check.classid === this.classid && check.getStat(sdk.stats.AddSkillTab, sdk.skills.tabs.Traps) === 1 && check.getStat(sdk.stats.MaxHp) >= 40); @@ -100,7 +105,7 @@ }, respec: function () { - return (Attack.checkInfinity() || (me.data.merc.gear.includes(sdk.locale.items.Infinity) && !Misc.poll(() => me.getMerc(), 200, 50))) && Check.haveItem("armor", "runeword", "Enigma"); + return (Attack.checkInfinity() || (me.data.merc.gear.includes(sdk.locale.items.Infinity) && !Misc.poll(() => me.getMerc(), 200, 50))) && me.checkItem({ name: sdk.locale.items.Enigma, itemtype: sdk.items.type.Armor }).have; }, active: function () { diff --git a/libs/SoloPlay/BuildFiles/assassin/assassin.WhirlsinBuild.js b/libs/SoloPlay/BuildFiles/assassin/assassin.WhirlsinBuild.js index ed2fe915..eb19cef8 100644 --- a/libs/SoloPlay/BuildFiles/assassin/assassin.WhirlsinBuild.js +++ b/libs/SoloPlay/BuildFiles/assassin/assassin.WhirlsinBuild.js @@ -36,6 +36,7 @@ max: 3, have: [], classid: sdk.items.SmallCharm, + /** @param {ItemUnit} check */ stats: function (check) { return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MaxHp) === 20); } @@ -45,6 +46,7 @@ max: 2, have: [], classid: sdk.items.SmallCharm, + /** @param {ItemUnit} check */ stats: function (check) { return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MagicBonus) === 7); } @@ -54,6 +56,7 @@ max: 1, have: [], classid: sdk.items.SmallCharm, + /** @param {ItemUnit} check */ stats: function (check) { return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.FHR) === 5); } @@ -63,6 +66,7 @@ max: 2, have: [], classid: sdk.items.SmallCharm, + /** @param {ItemUnit} check */ stats: function (check) { return (!check.unique && check.classid === this.classid && check.getStat(sdk.stats.MaxHp) === 20 && check.getStat(sdk.stats.MaxMana) === 17); } @@ -72,6 +76,7 @@ max: 1, have: [], classid: sdk.items.GrandCharm, + /** @param {ItemUnit} check */ stats: function (check) { return (!check.unique && check.classid === this.classid && check.getStat(sdk.stats.AddSkillTab, sdk.skills.tabs.ShadowDisciplines) === 1 && check.getStat(sdk.stats.MaxHp) >= 40); diff --git a/libs/SoloPlay/BuildFiles/assassin/assassin.js b/libs/SoloPlay/BuildFiles/assassin/assassin.js index c0e23d84..3ba768d2 100644 --- a/libs/SoloPlay/BuildFiles/assassin/assassin.js +++ b/libs/SoloPlay/BuildFiles/assassin/assassin.js @@ -8,7 +8,7 @@ const CharInfo = { respecOne: 32, respecTwo: 0, - levelCap: (function() { + levelCap: (function () { const currentDiff = sdk.difficulty.nameOf(me.diff); const softcoreMode = { "Normal": 33, diff --git a/libs/SoloPlay/BuildFiles/barbarian/barbarian.FrenzyBuild.js b/libs/SoloPlay/BuildFiles/barbarian/barbarian.FrenzyBuild.js index f8bc20ab..c5fa64bf 100644 --- a/libs/SoloPlay/BuildFiles/barbarian/barbarian.FrenzyBuild.js +++ b/libs/SoloPlay/BuildFiles/barbarian/barbarian.FrenzyBuild.js @@ -37,6 +37,7 @@ max: 4, have: [], classid: sdk.items.SmallCharm, + /** @param {ItemUnit} check */ stats: function (check) { return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MaxHp) === 20); } @@ -46,6 +47,7 @@ max: 2, have: [], classid: sdk.items.SmallCharm, + /** @param {ItemUnit} check */ stats: function (check) { return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MagicBonus) === 7); } @@ -55,6 +57,7 @@ max: 2, have: [], classid: sdk.items.SmallCharm, + /** @param {ItemUnit} check */ stats: function (check) { return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.FHR) === 5); } @@ -64,6 +67,7 @@ max: 1, have: [], classid: sdk.items.GrandCharm, + /** @param {ItemUnit} check */ stats: function (check) { return (!check.unique && check.classid === this.classid && check.getStat(sdk.stats.AddSkillTab, sdk.skills.tabs.BarbCombat) === 1 && check.getStat(sdk.stats.MaxHp) >= 40); @@ -74,6 +78,7 @@ max: 1, have: [], classid: sdk.items.GrandCharm, + /** @param {ItemUnit} check */ stats: function (check) { return (!check.unique && check.classid === this.classid && check.getStat(sdk.stats.AddSkillTab, sdk.skills.tabs.Masteries) === 1 && check.getStat(sdk.stats.MaxHp) >= 40); diff --git a/libs/SoloPlay/BuildFiles/barbarian/barbarian.ImmortalwhirlBuild.js b/libs/SoloPlay/BuildFiles/barbarian/barbarian.ImmortalwhirlBuild.js index de76d8f6..b4061a06 100644 --- a/libs/SoloPlay/BuildFiles/barbarian/barbarian.ImmortalwhirlBuild.js +++ b/libs/SoloPlay/BuildFiles/barbarian/barbarian.ImmortalwhirlBuild.js @@ -34,6 +34,7 @@ max: 3, have: [], classid: sdk.items.SmallCharm, + /** @param {ItemUnit} check */ stats: function (check) { return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MaxHp) === 20); } @@ -43,6 +44,7 @@ max: 2, have: [], classid: sdk.items.SmallCharm, + /** @param {ItemUnit} check */ stats: function (check) { return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MagicBonus) === 7); } @@ -52,6 +54,7 @@ max: 1, have: [], classid: sdk.items.SmallCharm, + /** @param {ItemUnit} check */ stats: function (check) { return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.FHR) === 5); } @@ -61,6 +64,7 @@ max: 2, have: [], classid: sdk.items.GrandCharm, + /** @param {ItemUnit} check */ stats: function (check) { return (!check.unique && check.classid === this.classid && check.getStat(sdk.stats.AddSkillTab, sdk.skills.tabs.Masteries) === 1 && check.getStat(sdk.stats.MaxHp) >= 40); diff --git a/libs/SoloPlay/BuildFiles/barbarian/barbarian.SingerBuild.js b/libs/SoloPlay/BuildFiles/barbarian/barbarian.SingerBuild.js index 100d012d..a9a754c0 100644 --- a/libs/SoloPlay/BuildFiles/barbarian/barbarian.SingerBuild.js +++ b/libs/SoloPlay/BuildFiles/barbarian/barbarian.SingerBuild.js @@ -35,6 +35,7 @@ max: 3, have: [], classid: sdk.items.SmallCharm, + /** @param {ItemUnit} check */ stats: function (check) { return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MaxHp) === 20); } @@ -44,6 +45,7 @@ max: 2, have: [], classid: sdk.items.SmallCharm, + /** @param {ItemUnit} check */ stats: function (check) { return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MagicBonus) === 7); } @@ -53,6 +55,7 @@ max: 1, have: [], classid: sdk.items.SmallCharm, + /** @param {ItemUnit} check */ stats: function (check) { return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.FHR) === 5); } @@ -62,6 +65,7 @@ max: 2, have: [], classid: sdk.items.GrandCharm, + /** @param {ItemUnit} check */ stats: function (check) { return (!check.unique && check.classid === this.classid && check.getStat(sdk.stats.AddSkillTab, sdk.skills.tabs.Warcries) === 1 && check.getStat(sdk.stats.MaxHp) >= 40); diff --git a/libs/SoloPlay/BuildFiles/barbarian/barbarian.ThrowBuild.js b/libs/SoloPlay/BuildFiles/barbarian/barbarian.ThrowBuild.js index 46fe1abd..6af5af53 100644 --- a/libs/SoloPlay/BuildFiles/barbarian/barbarian.ThrowBuild.js +++ b/libs/SoloPlay/BuildFiles/barbarian/barbarian.ThrowBuild.js @@ -24,7 +24,7 @@ [sdk.skills.Howl, 9], [sdk.skills.BattleOrders, 20], [sdk.skills.BattleCommand, 1], - [sdk.skills.ThrowMastery, 20], + [sdk.skills.ThrowingMastery, 20], [sdk.skills.NaturalResistance, 5], [sdk.skills.DoubleSwing, 20], [sdk.skills.Frenzy, 1], @@ -37,8 +37,14 @@ max: 4, have: [], classid: sdk.items.SmallCharm, + /** @param {ItemUnit} check */ stats: function (check) { - return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MaxHp) === 20); + return ( + !check.unique + && check.classid === this.classid + && check.allRes === 5 + && check.getStat(sdk.stats.MaxHp) === 20 + ); } }, @@ -46,8 +52,14 @@ max: 4, have: [], classid: sdk.items.SmallCharm, + /** @param {ItemUnit} check */ stats: function (check) { - return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.FHR) === 5); + return ( + !check.unique + && check.classid === this.classid + && check.allRes === 5 + && check.getStat(sdk.stats.FHR) === 5 + ); } }, @@ -55,9 +67,14 @@ max: 1, have: [], classid: sdk.items.GrandCharm, + /** @param {ItemUnit} check */ stats: function (check) { - return (!check.unique && check.classid === this.classid && check.getStat(sdk.stats.AddSkillTab, sdk.skills.tabs.BarbCombat) === 1 - && check.getStat(sdk.stats.MaxHp) >= 40); + return ( + !check.unique + && check.classid === this.classid + && check.getStat(sdk.stats.AddSkillTab, sdk.skills.tabs.BarbCombat) === 1 + && check.getStat(sdk.stats.MaxHp) >= 40 + ); } }, @@ -65,9 +82,14 @@ max: 1, have: [], classid: sdk.items.GrandCharm, + /** @param {ItemUnit} check */ stats: function (check) { - return (!check.unique && check.classid === this.classid && check.getStat(sdk.stats.AddSkillTab, sdk.skills.tabs.Masteries) === 1 - && check.getStat(sdk.stats.MaxHp) >= 40); + return ( + !check.unique + && check.classid === this.classid + && check.getStat(sdk.stats.AddSkillTab, sdk.skills.tabs.Masteries) === 1 + && check.getStat(sdk.stats.MaxHp) >= 40 + ); } }, }, @@ -75,7 +97,13 @@ AutoBuildTemplate: { 1: { Update: function () { - Config.AttackSkill = [-1, sdk.skills.DoubleThrow, sdk.skills.Frenzy, sdk.skills.DoubleThrow, sdk.skills.Berserk]; + Config.AttackSkill = [ + -1, + sdk.skills.DoubleThrow, + sdk.skills.Frenzy, + sdk.skills.DoubleThrow, + sdk.skills.Berserk + ]; Config.LowManaSkill = [sdk.skills.DoubleSwing, sdk.skills.DoubleSwing]; Config.BeltColumn = ["hp", "hp", "mp", "rv"]; } @@ -85,9 +113,11 @@ respec: function () { if (me.classic) { return false; - } else { - return Check.haveItem("throwingknife", "unique", "Warshrike") && Check.haveItem("throwingaxe", "unique", "Lacerator"); } + return ( + me.checkItem({ name: sdk.locale.items.Warshrike, }).have + && me.checkItem({ name: sdk.locale.items.Lacerator, }).have + ); }, active: function () { diff --git a/libs/SoloPlay/BuildFiles/barbarian/barbarian.UberconcBuild.js b/libs/SoloPlay/BuildFiles/barbarian/barbarian.UberconcBuild.js index 22876901..3a0ba7f8 100644 --- a/libs/SoloPlay/BuildFiles/barbarian/barbarian.UberconcBuild.js +++ b/libs/SoloPlay/BuildFiles/barbarian/barbarian.UberconcBuild.js @@ -37,8 +37,14 @@ max: 4, have: [], classid: sdk.items.SmallCharm, + /** @param {ItemUnit} check */ stats: function (check) { - return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MaxHp) === 20); + return ( + !check.unique + && check.classid === this.classid + && check.allRes === 5 + && check.getStat(sdk.stats.MaxHp) === 20 + ); } }, @@ -46,8 +52,14 @@ max: 4, have: [], classid: sdk.items.SmallCharm, + /** @param {ItemUnit} check */ stats: function (check) { - return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.FHR) === 5); + return ( + !check.unique + && check.classid === this.classid + && check.allRes === 5 + && check.getStat(sdk.stats.FHR) === 5 + ); } }, @@ -55,9 +67,14 @@ max: 2, have: [], classid: sdk.items.GrandCharm, + /** @param {ItemUnit} check */ stats: function (check) { - return (!check.unique && check.classid === this.classid && check.getStat(sdk.stats.AddSkillTab, sdk.skills.tabs.BarbCombat) === 1 - && check.getStat(sdk.stats.MaxHp) >= 40); + return ( + !check.unique + && check.classid === this.classid + && check.getStat(sdk.stats.AddSkillTab, sdk.skills.tabs.BarbCombat) === 1 + && check.getStat(sdk.stats.MaxHp) >= 40 + ); } }, }, @@ -65,7 +82,13 @@ AutoBuildTemplate: { 1: { Update: function () { - Config.AttackSkill = [-1, sdk.skills.Concentrate, sdk.skills.Berserk, sdk.skills.Concentrate, sdk.skills.Berserk]; + Config.AttackSkill = [ + -1, + sdk.skills.Concentrate, + sdk.skills.Berserk, + sdk.skills.Concentrate, + sdk.skills.Berserk + ]; Config.LowManaSkill = [0, 0]; Config.BeltColumn = ["hp", "hp", "mp", "rv"]; } @@ -75,9 +98,11 @@ respec: function () { if (me.classic) { return me.charlvl >= 75 && me.diablo; - } else { - return me.checkItem({ name: sdk.locale.items.Grief, itemtype: sdk.items.type.Sword }).have && Check.haveItem("monarch", "unique", "Stormshield"); } + return ( + me.checkItem({ name: sdk.locale.items.Grief, itemtype: sdk.items.type.Sword }).have + && me.checkItem({ name: sdk.locale.items.Stormshield }).have + ); }, active: function () { diff --git a/libs/SoloPlay/BuildFiles/barbarian/barbarian.WhirlwindBuild.js b/libs/SoloPlay/BuildFiles/barbarian/barbarian.WhirlwindBuild.js index 9b4cd6e8..b4d7639e 100644 --- a/libs/SoloPlay/BuildFiles/barbarian/barbarian.WhirlwindBuild.js +++ b/libs/SoloPlay/BuildFiles/barbarian/barbarian.WhirlwindBuild.js @@ -36,6 +36,7 @@ max: 4, have: [], classid: sdk.items.SmallCharm, + /** @param {ItemUnit} check */ stats: function (check) { return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MaxHp) === 20); } @@ -45,6 +46,7 @@ max: 2, have: [], classid: sdk.items.SmallCharm, + /** @param {ItemUnit} check */ stats: function (check) { return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MagicBonus) === 7); } @@ -54,6 +56,7 @@ max: 2, have: [], classid: sdk.items.SmallCharm, + /** @param {ItemUnit} check */ stats: function (check) { return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.FHR) === 5); } @@ -63,6 +66,7 @@ max: 2, have: [], classid: sdk.items.GrandCharm, + /** @param {ItemUnit} check */ stats: function (check) { return (!check.unique && check.classid === this.classid && check.getStat(sdk.stats.AddSkillTab, sdk.skills.tabs.Masteries) === 1 && check.getStat(sdk.stats.MaxHp) >= 40); diff --git a/libs/SoloPlay/BuildFiles/barbarian/barbarian.js b/libs/SoloPlay/BuildFiles/barbarian/barbarian.js index 50011bf2..a2a748be 100644 --- a/libs/SoloPlay/BuildFiles/barbarian/barbarian.js +++ b/libs/SoloPlay/BuildFiles/barbarian/barbarian.js @@ -8,7 +8,7 @@ const CharInfo = { respecOne: me.expansion ? 30 : 30, respecTwo: me.expansion ? 74 : 74, - levelCap: (function() { + levelCap: (function () { const currentDiff = sdk.difficulty.nameOf(me.diff); const softcoreMode = { "Normal": me.expansion ? 33 : 33, diff --git a/libs/SoloPlay/BuildFiles/druid/druid.ElementalBuild.js b/libs/SoloPlay/BuildFiles/druid/druid.ElementalBuild.js index b0e43958..fd77ea35 100644 --- a/libs/SoloPlay/BuildFiles/druid/druid.ElementalBuild.js +++ b/libs/SoloPlay/BuildFiles/druid/druid.ElementalBuild.js @@ -75,6 +75,7 @@ max: 3, have: [], classid: sdk.items.SmallCharm, + /** @param {ItemUnit} check */ stats: function (check) { return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MaxHp) === 20); } @@ -84,6 +85,7 @@ max: 2, have: [], classid: sdk.items.SmallCharm, + /** @param {ItemUnit} check */ stats: function (check) { return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MagicBonus) === 7); } @@ -93,6 +95,7 @@ max: 1, have: [], classid: sdk.items.SmallCharm, + /** @param {ItemUnit} check */ stats: function (check) { return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.FHR) === 5); } @@ -102,6 +105,7 @@ max: 2, have: [], classid: sdk.items.GrandCharm, + /** @param {ItemUnit} check */ stats: function (check) { return (!check.unique && check.classid === this.classid && check.getStat(sdk.stats.AddSkillTab, sdk.skills.tabs.Elemental) === 1 && check.getStat(sdk.stats.MaxHp) >= 40); @@ -120,7 +124,7 @@ }, respec: function () { - return Check.haveItem("armor", "runeword", "Enigma"); + return me.checkItem({ name: sdk.locale.items.Enigma, itemtype: sdk.items.type.Armor }).have; }, active: function () { diff --git a/libs/SoloPlay/BuildFiles/druid/druid.FirewolfBuild.js b/libs/SoloPlay/BuildFiles/druid/druid.FirewolfBuild.js index fee8715c..1de30932 100644 --- a/libs/SoloPlay/BuildFiles/druid/druid.FirewolfBuild.js +++ b/libs/SoloPlay/BuildFiles/druid/druid.FirewolfBuild.js @@ -38,6 +38,7 @@ max: 2, have: [], classid: sdk.items.SmallCharm, + /** @param {ItemUnit} check */ stats: function (check) { return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MagicBonus) === 7); } @@ -47,6 +48,7 @@ max: 6, have: [], classid: sdk.items.SmallCharm, + /** @param {ItemUnit} check */ stats: function (check) { return (!check.unique && check.classid === this.classid && ((check.getStat(sdk.stats.PoisonLength) * check.getStat(sdk.stats.PoisonMaxDamage)) / 256) >= 141); @@ -57,6 +59,7 @@ max: 2, have: [], classid: sdk.items.GrandCharm, + /** @param {ItemUnit} check */ stats: function (check) { return (!check.unique && check.classid === this.classid && check.getStat(sdk.stats.AddSkillTab, sdk.skills.tabs.ShapeShifting) === 1 && check.getStat(sdk.stats.MaxHp) >= 40); diff --git a/libs/SoloPlay/BuildFiles/druid/druid.PlaguewolfBuild.js b/libs/SoloPlay/BuildFiles/druid/druid.PlaguewolfBuild.js index 3013084e..e66cb246 100644 --- a/libs/SoloPlay/BuildFiles/druid/druid.PlaguewolfBuild.js +++ b/libs/SoloPlay/BuildFiles/druid/druid.PlaguewolfBuild.js @@ -35,6 +35,7 @@ max: 2, have: [], classid: sdk.items.SmallCharm, + /** @param {ItemUnit} check */ stats: function (check) { return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MagicBonus) === 7); } @@ -44,6 +45,7 @@ max: 6, have: [], classid: sdk.items.SmallCharm, + /** @param {ItemUnit} check */ stats: function (check) { return (!check.unique && check.classid === this.classid && ((check.getStat(sdk.stats.PoisonLength) * check.getStat(sdk.stats.PoisonMaxDamage)) / 256) >= 141); @@ -54,6 +56,7 @@ max: 2, have: [], classid: sdk.items.GrandCharm, + /** @param {ItemUnit} check */ stats: function (check) { return (!check.unique && check.classid === this.classid && check.getStat(sdk.stats.AddSkillTab, sdk.skills.tabs.ShapeShifting) === 1 && check.getStat(sdk.stats.MaxHp) >= 40); diff --git a/libs/SoloPlay/BuildFiles/druid/druid.StormbearBuild.js b/libs/SoloPlay/BuildFiles/druid/druid.StormbearBuild.js index 43b0a3fc..120fdce8 100644 --- a/libs/SoloPlay/BuildFiles/druid/druid.StormbearBuild.js +++ b/libs/SoloPlay/BuildFiles/druid/druid.StormbearBuild.js @@ -35,6 +35,7 @@ max: 2, have: [], classid: sdk.items.SmallCharm, + /** @param {ItemUnit} check */ stats: function (check) { return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MagicBonus) === 7); } @@ -44,6 +45,7 @@ max: 6, have: [], classid: sdk.items.SmallCharm, + /** @param {ItemUnit} check */ stats: function (check) { return (!check.unique && check.classid === this.classid && ((check.getStat(sdk.stats.PoisonLength) * check.getStat(sdk.stats.PoisonMaxDamage)) / 256) >= 141); @@ -54,6 +56,7 @@ max: 2, have: [], classid: sdk.items.GrandCharm, + /** @param {ItemUnit} check */ stats: function (check) { return (!check.unique && check.classid === this.classid && check.getStat(sdk.stats.AddSkillTab, sdk.skills.tabs.ShapeShifting) === 1 && check.getStat(sdk.stats.MaxHp) >= 40); diff --git a/libs/SoloPlay/BuildFiles/druid/druid.WindBuild.js b/libs/SoloPlay/BuildFiles/druid/druid.WindBuild.js index fc7c945b..993ac212 100644 --- a/libs/SoloPlay/BuildFiles/druid/druid.WindBuild.js +++ b/libs/SoloPlay/BuildFiles/druid/druid.WindBuild.js @@ -34,6 +34,7 @@ max: 3, have: [], classid: sdk.items.SmallCharm, + /** @param {ItemUnit} check */ stats: function (check) { return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MaxHp) === 20); } @@ -43,6 +44,7 @@ max: 2, have: [], classid: sdk.items.SmallCharm, + /** @param {ItemUnit} check */ stats: function (check) { return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MagicBonus) === 7); } @@ -52,6 +54,7 @@ max: 1, have: [], classid: sdk.items.SmallCharm, + /** @param {ItemUnit} check */ stats: function (check) { return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.FHR) === 5); } @@ -61,6 +64,7 @@ max: 2, have: [], classid: sdk.items.GrandCharm, + /** @param {ItemUnit} check */ stats: function (check) { return (!check.unique && check.classid === this.classid && check.getStat(sdk.stats.AddSkillTab, sdk.skills.tabs.Elemental) === 1 && check.getStat(sdk.stats.MaxHp) >= 40); @@ -80,7 +84,7 @@ }, respec: function () { - return Check.haveItem("armor", "runeword", "Enigma"); + return me.checkItem({ name: sdk.locale.items.Enigma, itemtype: sdk.items.type.Armor }).have; }, active: function () { diff --git a/libs/SoloPlay/BuildFiles/druid/druid.WolfBuild.js b/libs/SoloPlay/BuildFiles/druid/druid.WolfBuild.js index 095ceee7..293a40e0 100644 --- a/libs/SoloPlay/BuildFiles/druid/druid.WolfBuild.js +++ b/libs/SoloPlay/BuildFiles/druid/druid.WolfBuild.js @@ -33,8 +33,14 @@ max: 3, have: [], classid: sdk.items.SmallCharm, + /** @param {ItemUnit} check */ stats: function (check) { - return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MaxHp) === 20); + return ( + !check.unique + && check.classid === this.classid + && check.allRes === 5 + && check.getStat(sdk.stats.MaxHp) === 20 + ); } }, @@ -42,8 +48,14 @@ max: 2, have: [], classid: sdk.items.SmallCharm, + /** @param {ItemUnit} check */ stats: function (check) { - return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MagicBonus) === 7); + return ( + !check.unique + && check.classid === this.classid + && check.allRes === 5 + && check.getStat(sdk.stats.MagicBonus) === 7 + ); } }, @@ -51,8 +63,14 @@ max: 1, have: [], classid: sdk.items.SmallCharm, + /** @param {ItemUnit} check */ stats: function (check) { - return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.FHR) === 5); + return ( + !check.unique + && check.classid === this.classid + && check.allRes === 5 + && check.getStat(sdk.stats.FHR) === 5 + ); } }, @@ -60,9 +78,14 @@ max: 2, have: [], classid: sdk.items.GrandCharm, + /** @param {ItemUnit} check */ stats: function (check) { - return (!check.unique && check.classid === this.classid && check.getStat(sdk.stats.AddSkillTab, sdk.skills.tabs.ShapeShifting) === 1 - && check.getStat(sdk.stats.MaxHp) >= 40); + return ( + !check.unique + && check.classid === this.classid + && check.getStat(sdk.stats.AddSkillTab, sdk.skills.tabs.ShapeShifting) === 1 + && check.getStat(sdk.stats.MaxHp) >= 40 + ); } }, }, @@ -70,7 +93,12 @@ AutoBuildTemplate: { 1: { Update: function () { - Config.AttackSkill = [-1, sdk.skills.Fury, sdk.skills.FeralRage, sdk.skills.Fury, sdk.skills.FeralRage, sdk.skills.Rabies, -1]; + Config.AttackSkill = [ + -1, + sdk.skills.Fury, sdk.skills.FeralRage, + sdk.skills.Fury, sdk.skills.FeralRage, + sdk.skills.Rabies, -1 + ]; Config.LowManaSkill = [0, 0]; Config.Wereform = "Werewolf"; Config.SummonAnimal = "Grizzly"; @@ -80,7 +108,10 @@ }, respec: function () { - return Check.haveItem("stalagmite", "unique", "Ribcracker") && Check.haveItem("armor", "runeword", "Chains of Honor"); + return ( + me.checkItem({ name: sdk.locale.items.Ribcracker, classid: sdk.items.Stalagmite }).have + && me.checkItem({ name: sdk.locale.items.ChainsofHonor, }).have + ); }, active: function () { diff --git a/libs/SoloPlay/BuildFiles/necromancer/necromancer.BoneBuild.js b/libs/SoloPlay/BuildFiles/necromancer/necromancer.BoneBuild.js index 5edeb29f..c361153f 100644 --- a/libs/SoloPlay/BuildFiles/necromancer/necromancer.BoneBuild.js +++ b/libs/SoloPlay/BuildFiles/necromancer/necromancer.BoneBuild.js @@ -37,6 +37,7 @@ max: 4, have: [], classid: sdk.items.SmallCharm, + /** @param {ItemUnit} check */ stats: function (check) { return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MaxHp) === 20); @@ -47,6 +48,7 @@ max: 3, have: [], classid: sdk.items.SmallCharm, + /** @param {ItemUnit} check */ stats: function (check) { return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MagicBonus) === 7); @@ -57,6 +59,7 @@ max: 1, have: [], classid: sdk.items.SmallCharm, + /** @param {ItemUnit} check */ stats: function (check) { return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.FHR) === 5); @@ -67,6 +70,7 @@ max: 2, have: [], classid: sdk.items.GrandCharm, + /** @param {ItemUnit} check */ stats: function (check) { return (!check.unique && check.classid === this.classid && check.getStat(sdk.stats.AddSkillTab, sdk.skills.tabs.PoisonandBone) === 1 @@ -89,7 +93,7 @@ if (me.classic) { return me.charlvl >= 75 && me.diablo; } else { - return Check.haveItem("armor", "runeword", "Enigma"); + return me.checkItem({ name: sdk.locale.items.Enigma, itemtype: sdk.items.type.Armor }).have; } }, diff --git a/libs/SoloPlay/BuildFiles/necromancer/necromancer.PoisonBuild.js b/libs/SoloPlay/BuildFiles/necromancer/necromancer.PoisonBuild.js index 241f40f1..2671835f 100644 --- a/libs/SoloPlay/BuildFiles/necromancer/necromancer.PoisonBuild.js +++ b/libs/SoloPlay/BuildFiles/necromancer/necromancer.PoisonBuild.js @@ -35,6 +35,7 @@ max: 4, have: [], classid: sdk.items.SmallCharm, + /** @param {ItemUnit} check */ stats: function (check) { return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MaxHp) === 20); } @@ -44,6 +45,7 @@ max: 2, have: [], classid: sdk.items.SmallCharm, + /** @param {ItemUnit} check */ stats: function (check) { return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MagicBonus) === 7); } @@ -53,6 +55,7 @@ max: 2, have: [], classid: sdk.items.SmallCharm, + /** @param {ItemUnit} check */ stats: function (check) { return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.FHR) === 5); } @@ -62,6 +65,7 @@ max: 2, have: [], classid: sdk.items.GrandCharm, + /** @param {ItemUnit} check */ stats: function (check) { return (!check.unique && check.classid === this.classid && check.getStat(sdk.stats.AddSkillTab, sdk.skills.tabs.PoisonandBone) === 1 && check.getStat(sdk.stats.MaxHp) >= 40); @@ -83,7 +87,7 @@ if (me.classic) { return me.charlvl >= 75 && me.diablo; } else { - return Check.haveItem("armor", "runeword", "Enigma"); + return me.checkItem({ name: sdk.locale.items.Enigma, itemtype: sdk.items.type.Armor }).have; } }, @@ -121,7 +125,7 @@ "[name] == lightgauntlets && [quality] == unique # [fcr] >= 20 && [addfireskills] == 1 # [tier] == tierscore(item, 100000)", ] : [ // Weapon - "([type] == wand || [type] == sword && ([quality] >= magic || [flag] == runeword) || [type] == knife && [quality] >= magic) && [flag] != ethereal # [secondarymindamage] == 0 && [itemchargedskill] >= 0 # [tier] == tierscore(item)", + // "([type] == wand || [type] == sword && ([quality] >= magic || [flag] == runeword) || [type] == knife && [quality] >= magic) && [flag] != ethereal # [secondarymindamage] == 0 && [itemchargedskill] >= 0 # [tier] == tierscore(item)", // Helmet - Harlequin's Crest "[name] == shako && [quality] == unique && [flag] != ethereal # [damageresist] == 10 # [tier] == tierscore(item, 100000)", // Belt - Arach's diff --git a/libs/SoloPlay/BuildFiles/necromancer/necromancer.StartBuild.js b/libs/SoloPlay/BuildFiles/necromancer/necromancer.StartBuild.js index 59cd0e37..b73c9ec6 100644 --- a/libs/SoloPlay/BuildFiles/necromancer/necromancer.StartBuild.js +++ b/libs/SoloPlay/BuildFiles/necromancer/necromancer.StartBuild.js @@ -16,11 +16,13 @@ usefulskills: [sdk.skills.AmplifyDamage, sdk.skills.BoneArmor, sdk.skills.Decrepify, sdk.skills.BoneWall], wantedMerc: MercData[sdk.skills.Might], stats: [ - ["strength", 20], ["vitality", 70], ["strength", 35], + ["strength", 20], ["energy", 45], + ["vitality", 70], ["strength", 35], ["energy", 85], ["vitality", "all"] ], skills: [ [sdk.skills.Teeth, 4], // charlvl 4 + // [sdk.skills.BoneArmor, 1], // charlvl 7 [sdk.skills.AmplifyDamage, 1], // charlvl 5 [sdk.skills.ClayGolem, 1], // charlvl 6 [sdk.skills.BoneArmor, 1], // charlvl 7 @@ -51,7 +53,7 @@ Config.TownHP = me.hardcore ? 0 : 35; Config.BeltColumn = ["hp", "hp", "hp", "hp"]; Config.HPBuffer = 6; - Config.MPBuffer = 6; + Config.MPBuffer = 8; Config.AttackSkill = [-1, 0, 0, 0, 0, -1, -1]; Config.LowManaSkill = [0, 0]; Config.Golem = "Clay"; @@ -64,8 +66,9 @@ Config.AttackSkill = [-1, sdk.skills.Teeth, -1, sdk.skills.Teeth, -1, -1, -1]; Config.BeltColumn = ["hp", "hp", "mp", "mp"]; - Config.HPBuffer = 2; - Config.MPBuffer = 6; + Config.HPBuffer = Storage.BeltSize() < 4 ? 4 : 2; + Config.MPBuffer = 8; + Config.RejuvBuffer = 6; SetUp.belt(); }); build.AutoBuildTemplate[18] = buildAutoBuildTempObj(() => { diff --git a/libs/SoloPlay/BuildFiles/necromancer/necromancer.SummonBuild.js b/libs/SoloPlay/BuildFiles/necromancer/necromancer.SummonBuild.js index 76435580..74dd5c2a 100644 --- a/libs/SoloPlay/BuildFiles/necromancer/necromancer.SummonBuild.js +++ b/libs/SoloPlay/BuildFiles/necromancer/necromancer.SummonBuild.js @@ -23,6 +23,7 @@ max: 4, have: [], classid: sdk.items.SmallCharm, + /** @param {ItemUnit} check */ stats: function (check) { return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MaxHp) === 20); } @@ -32,6 +33,7 @@ max: 4, have: [], classid: sdk.items.SmallCharm, + /** @param {ItemUnit} check */ stats: function (check) { return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MagicBonus) === 7); } @@ -41,6 +43,7 @@ max: 2, have: [], classid: sdk.items.GrandCharm, + /** @param {ItemUnit} check */ stats: function (check) { return (!check.unique && check.classid === this.classid && check.getStat(sdk.stats.AddSkillTab, sdk.skills.tabs.NecroSummoning) === 1 && check.getStat(sdk.stats.MaxHp) >= 40); @@ -69,7 +72,7 @@ if (me.classic) { return me.charlvl >= 75 && me.diablo; } else { - return Check.haveItem("armor", "runeword", "Enigma"); + return me.checkItem({ name: sdk.locale.items.Enigma, itemtype: sdk.items.type.Armor }).have; } }, @@ -132,7 +135,7 @@ "[name] == lightgauntlets && [quality] == unique # [fcr] >= 20 && [addfireskills] == 1 # [tier] == tierscore(item, 100000)", ] : [ // Weapon - "([type] == wand || [type] == sword && ([quality] >= magic || [flag] == runeword) || [type] == knife && [quality] >= magic) && [flag] != ethereal # [secondarymindamage] == 0 && [itemchargedskill] >= 0 # [tier] == tierscore(item)", + // "([type] == wand || [type] == sword && ([quality] >= magic || [flag] == runeword) || [type] == knife && [quality] >= magic) && [flag] != ethereal # [secondarymindamage] == 0 && [itemchargedskill] >= 0 # [tier] == tierscore(item)", // Helmet - Harlequin's Crest "[name] == shako && [quality] == unique && [flag] != ethereal # [damageresist] == 10 # [tier] == tierscore(item, 100000)", // Belt - Arach's diff --git a/libs/SoloPlay/BuildFiles/paladin/paladin.AuradinBuild.js b/libs/SoloPlay/BuildFiles/paladin/paladin.AuradinBuild.js index cb1c9f56..6b467113 100644 --- a/libs/SoloPlay/BuildFiles/paladin/paladin.AuradinBuild.js +++ b/libs/SoloPlay/BuildFiles/paladin/paladin.AuradinBuild.js @@ -35,6 +35,7 @@ max: 6, have: [], classid: sdk.items.SmallCharm, + /** @param {ItemUnit} check */ stats: function (check) { return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MaxHp) === 20); } @@ -44,6 +45,7 @@ max: 2, have: [], classid: sdk.items.SmallCharm, + /** @param {ItemUnit} check */ stats: function (check) { return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MagicBonus) === 7); } @@ -53,6 +55,7 @@ max: 2, have: [], classid: sdk.items.GrandCharm, + /** @param {ItemUnit} check */ stats: function (check) { return (!check.unique && check.classid === this.classid && check.getStat(sdk.stats.AddSkillTab, sdk.skills.tabs.Offensive) === 1 && check.getStat(sdk.stats.MaxHp) >= 40); diff --git a/libs/SoloPlay/BuildFiles/paladin/paladin.ClassicauradinBuild.js b/libs/SoloPlay/BuildFiles/paladin/paladin.ClassicauradinBuild.js index 44d2a1ae..a35a270c 100644 --- a/libs/SoloPlay/BuildFiles/paladin/paladin.ClassicauradinBuild.js +++ b/libs/SoloPlay/BuildFiles/paladin/paladin.ClassicauradinBuild.js @@ -35,6 +35,7 @@ max: 6, have: [], classid: sdk.items.SmallCharm, + /** @param {ItemUnit} check */ stats: function (check) { return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MaxHp) === 20); } @@ -44,6 +45,7 @@ max: 2, have: [], classid: sdk.items.SmallCharm, + /** @param {ItemUnit} check */ stats: function (check) { return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MagicBonus) === 7); } @@ -53,6 +55,7 @@ max: 2, have: [], classid: sdk.items.GrandCharm, + /** @param {ItemUnit} check */ stats: function (check) { return (!check.unique && check.classid === this.classid && check.getStat(sdk.stats.AddSkillTab, sdk.skills.tabs.Offensive) === 1 && check.getStat(sdk.stats.MaxHp) >= 40); diff --git a/libs/SoloPlay/BuildFiles/paladin/paladin.HammerdinBuild.js b/libs/SoloPlay/BuildFiles/paladin/paladin.HammerdinBuild.js index 77115cc5..528deff0 100644 --- a/libs/SoloPlay/BuildFiles/paladin/paladin.HammerdinBuild.js +++ b/libs/SoloPlay/BuildFiles/paladin/paladin.HammerdinBuild.js @@ -33,8 +33,10 @@ max: 3, have: [], classid: sdk.items.SmallCharm, + /** @param {ItemUnit} check */ stats: function (check) { - return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MaxHp) === 20); + return (!check.unique && check.classid === this.classid + && check.allRes === 5 && check.getStat(sdk.stats.MaxHp) === 20); } }, @@ -42,8 +44,10 @@ max: 2, have: [], classid: sdk.items.SmallCharm, + /** @param {ItemUnit} check */ stats: function (check) { - return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MagicBonus) === 7); + return (!check.unique && check.classid === this.classid + && check.allRes === 5 && check.getStat(sdk.stats.MagicBonus) === 7); } }, @@ -51,8 +55,10 @@ max: 3, have: [], classid: sdk.items.SmallCharm, + /** @param {ItemUnit} check */ stats: function (check) { - return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.FHR) === 5); + return (!check.unique && check.classid === this.classid + && check.allRes === 5 && check.getStat(sdk.stats.FHR) === 5); } }, @@ -60,8 +66,10 @@ max: 2, have: [], classid: sdk.items.GrandCharm, + /** @param {ItemUnit} check */ stats: function (check) { - return (!check.unique && check.classid === this.classid && check.getStat(sdk.stats.AddSkillTab, sdk.skills.tabs.PalaCombat) === 1 + return (!check.unique && check.classid === this.classid + && check.getStat(sdk.stats.AddSkillTab, sdk.skills.tabs.PalaCombat) === 1 && check.getStat(sdk.stats.MaxHp) >= 40); } }, @@ -70,7 +78,12 @@ AutoBuildTemplate: { 1: { Update: function () { - Config.AttackSkill = [-1, sdk.skills.BlessedHammer, sdk.skills.Concentration, sdk.skills.BlessedHammer, sdk.skills.Concentration, sdk.skills.HolyBolt, sdk.skills.Concentration]; + Config.AttackSkill = [ + -1, + sdk.skills.BlessedHammer, sdk.skills.Concentration, + sdk.skills.BlessedHammer, sdk.skills.Concentration, + sdk.skills.HolyBolt, sdk.skills.Concentration + ]; Config.LowManaSkill = [0, sdk.skills.Concentration]; if (me.hell && !me.accessToAct(5)) { @@ -86,7 +99,7 @@ if (me.classic) { return me.charlvl >= 75 && me.diablo; } else { - return Check.haveItem("armor", "runeword", "Enigma"); + return me.checkItem({ name: sdk.locale.items.Enigma, itemtype: sdk.items.type.Armor }).have; } }, diff --git a/libs/SoloPlay/BuildFiles/paladin/paladin.HammershockBuild.js b/libs/SoloPlay/BuildFiles/paladin/paladin.HammershockBuild.js index bf53e16f..a01dd032 100644 --- a/libs/SoloPlay/BuildFiles/paladin/paladin.HammershockBuild.js +++ b/libs/SoloPlay/BuildFiles/paladin/paladin.HammershockBuild.js @@ -12,7 +12,14 @@ caster: false, skillstab: sdk.skills.tabs.PalaCombat, wantedskills: [sdk.skills.BlessedHammer, sdk.skills.HolyShock], - usefulskills: [sdk.skills.HolyShield, sdk.skills.ResistLightning, sdk.skills.Zeal, sdk.skills.Concentration, sdk.skills.Vigor, sdk.skills.BlessedAim], + usefulskills: [ + sdk.skills.HolyShield, + sdk.skills.ResistLightning, + sdk.skills.Zeal, + sdk.skills.Concentration, + sdk.skills.Vigor, + sdk.skills.BlessedAim + ], precastSkills: [sdk.skills.HolyShield], usefulStats: [sdk.stats.PierceLtng, sdk.stats.PassiveLightningMastery, sdk.stats.PassiveLightningPierce], wantedMerc: MercData[sdk.skills.HolyFreeze], @@ -36,8 +43,14 @@ max: 6, have: [], classid: sdk.items.SmallCharm, + /** @param {ItemUnit} check */ stats: function (check) { - return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MaxHp) === 20); + return ( + !check.unique + && check.classid === this.classid + && check.allRes === 5 + && check.getStat(sdk.stats.MaxHp) === 20 + ); } }, @@ -45,8 +58,14 @@ max: 2, have: [], classid: sdk.items.SmallCharm, + /** @param {ItemUnit} check */ stats: function (check) { - return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MagicBonus) === 7); + return ( + !check.unique + && check.classid === this.classid + && check.allRes === 5 + && check.getStat(sdk.stats.MagicBonus) === 7 + ); } }, @@ -54,9 +73,14 @@ max: 2, have: [], classid: sdk.items.GrandCharm, + /** @param {ItemUnit} check */ stats: function (check) { - return (!check.unique && check.classid === this.classid && check.getStat(sdk.stats.AddSkillTab, sdk.skills.tabs.Offensive) === 1 - && check.getStat(sdk.stats.MaxHp) >= 40); + return ( + !check.unique + && check.classid === this.classid + && check.getStat(sdk.stats.AddSkillTab, sdk.skills.tabs.Offensive) === 1 + && check.getStat(sdk.stats.MaxHp) >= 40 + ); } }, }, @@ -65,7 +89,12 @@ 1: { Update: function () { Config.Vigor = false; - Config.AttackSkill = [-1, sdk.skills.BlessedHammer, sdk.skills.Concentration, sdk.skills.BlessedHammer, sdk.skills.Concentration, sdk.skills.Zeal, sdk.skills.HolyShock]; + Config.AttackSkill = [ + -1, + sdk.skills.BlessedHammer, sdk.skills.Concentration, + sdk.skills.BlessedHammer, sdk.skills.Concentration, + sdk.skills.Zeal, sdk.skills.HolyShock + ]; Config.LowManaSkill = [-1, -1]; Config.SkipImmune = ["magic and lightning and physical"]; // Don't think this ever happens but should skip if it does Config.BeltColumn = ["hp", "hp", "mp", "rv"]; @@ -77,13 +106,19 @@ respec: function () { if (me.classic) { return me.charlvl >= 75 && me.diablo; - } else { - return Check.haveItem("scepter", "unique", "Heaven's Light") && Check.haveItem("armor", "runeword", "Enigma"); } + return ( + me.checkItem({ name: sdk.locale.items.HeavensLight }).have + && me.checkItem({ name: sdk.locale.items.Enigma, itemtype: sdk.items.type.Armor }).have + ); }, active: function () { - return this.respec() && (me.getSkill(sdk.skills.HolyShock, sdk.skills.subindex.HardPoints) === 20 && me.getSkill(sdk.skills.BlessedHammer, sdk.skills.subindex.HardPoints) === 20); + return ( + this.respec() + && me.getSkill(sdk.skills.HolyShock, sdk.skills.subindex.HardPoints) === 20 + && me.getSkill(sdk.skills.BlessedHammer, sdk.skills.subindex.HardPoints) === 20 + ); }, }; diff --git a/libs/SoloPlay/BuildFiles/paladin/paladin.SancdreamerBuild.js b/libs/SoloPlay/BuildFiles/paladin/paladin.SancdreamerBuild.js index 7a015b14..8c963fb5 100644 --- a/libs/SoloPlay/BuildFiles/paladin/paladin.SancdreamerBuild.js +++ b/libs/SoloPlay/BuildFiles/paladin/paladin.SancdreamerBuild.js @@ -17,7 +17,13 @@ wantedskills: [sdk.skills.Zeal, sdk.skills.Sanctuary], usefulskills: [sdk.skills.HolyShield, sdk.skills.Sacrifice, sdk.skills.ResistLightning], precastSkills: [sdk.skills.HolyShield], - usefulStats: [sdk.stats.PassiveLightningMastery, sdk.stats.PassiveLightningPierce, sdk.stats.PierceLtng, sdk.stats.PassiveMagMastery, sdk.stats.PassiveMagPierce], + usefulStats: [ + sdk.stats.PassiveLightningMastery, + sdk.stats.PassiveLightningPierce, + sdk.stats.PierceLtng, + sdk.stats.PassiveMagMastery, + sdk.stats.PassiveMagPierce + ], wantedMerc: MercData[sdk.skills.HolyFreeze], stats: [ ["strength", 103], ["dexterity", 136], ["vitality", 300], ["dexterity", "block"], ["vitality", "all"] @@ -37,8 +43,14 @@ max: 6, have: [], classid: sdk.items.SmallCharm, + /** @param {ItemUnit} check */ stats: function (check) { - return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MaxHp) === 20); + return ( + !check.unique + && check.classid === this.classid + && check.allRes === 5 + && check.getStat(sdk.stats.MaxHp) === 20 + ); } }, @@ -46,8 +58,14 @@ max: 2, have: [], classid: sdk.items.SmallCharm, + /** @param {ItemUnit} check */ stats: function (check) { - return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MagicBonus) === 7); + return ( + !check.unique + && check.classid === this.classid + && check.allRes === 5 + && check.getStat(sdk.stats.MagicBonus) === 7 + ); } }, @@ -55,9 +73,14 @@ max: 2, have: [], classid: sdk.items.GrandCharm, + /** @param {ItemUnit} check */ stats: function (check) { - return (!check.unique && check.classid === this.classid && check.getStat(sdk.stats.AddSkillTab, sdk.skills.tabs.Offensive) === 1 - && check.getStat(sdk.stats.MaxHp) >= 40); + return ( + !check.unique + && check.classid === this.classid + && check.getStat(sdk.stats.AddSkillTab, sdk.skills.tabs.Offensive) === 1 + && check.getStat(sdk.stats.MaxHp) >= 40 + ); } }, }, @@ -66,7 +89,12 @@ 1: { Update: function () { Config.Vigor = false; - Config.AttackSkill = [-1, sdk.skills.Zeal, sdk.skills.Sanctuary, sdk.skills.Zeal, sdk.skills.Sanctuary, -1, -1]; + Config.AttackSkill = [ + -1, + sdk.skills.Zeal, sdk.skills.Sanctuary, + sdk.skills.Zeal, sdk.skills.Sanctuary, + -1, -1 + ]; Config.LowManaSkill = [-1, -1]; Config.SkipImmune = ["lightning and magic and physical"]; // Don't think this ever happens but should skip if it does @@ -79,10 +107,12 @@ respec: function () { if (me.classic) { return false; - } else { - return me.haveAll([{ name: sdk.locale.items.Dream, itemtype: sdk.items.type.AuricShields }, { name: sdk.locale.items.Dream, itemtype: sdk.items.type.Helm }, - { name: sdk.locale.items.LastWish }]); } + return me.haveAll([ + { name: sdk.locale.items.Dream, itemtype: sdk.items.type.AuricShields }, + { name: sdk.locale.items.Dream, itemtype: sdk.items.type.Helm }, + { name: sdk.locale.items.LastWish } + ]); }, active: function () { diff --git a/libs/SoloPlay/BuildFiles/paladin/paladin.SmiterBuild.js b/libs/SoloPlay/BuildFiles/paladin/paladin.SmiterBuild.js index 61472a24..e34745b4 100644 --- a/libs/SoloPlay/BuildFiles/paladin/paladin.SmiterBuild.js +++ b/libs/SoloPlay/BuildFiles/paladin/paladin.SmiterBuild.js @@ -34,8 +34,14 @@ max: 6, have: [], classid: sdk.items.SmallCharm, + /** @param {ItemUnit} check */ stats: function (check) { - return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MaxHp) === 20); + return ( + !check.unique + && check.classid === this.classid + && check.allRes === 5 + && check.getStat(sdk.stats.MaxHp) === 20 + ); } }, @@ -43,8 +49,14 @@ max: 2, have: [], classid: sdk.items.SmallCharm, + /** @param {ItemUnit} check */ stats: function (check) { - return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MagicBonus) === 7); + return ( + !check.unique + && check.classid === this.classid + && check.allRes === 5 + && check.getStat(sdk.stats.MagicBonus) === 7 + ); } }, @@ -52,9 +64,14 @@ max: 2, have: [], classid: sdk.items.GrandCharm, + /** @param {ItemUnit} check */ stats: function (check) { - return (!check.unique && check.classid === this.classid && check.getStat(sdk.stats.AddSkillTab, sdk.skills.tabs.PalaCombat) === 1 - && check.getStat(sdk.stats.MaxHp) >= 40); + return ( + !check.unique + && check.classid === this.classid + && check.getStat(sdk.stats.AddSkillTab, sdk.skills.tabs.PalaCombat) === 1 + && check.getStat(sdk.stats.MaxHp) >= 40 + ); } }, }, @@ -62,7 +79,12 @@ AutoBuildTemplate: { 1: { Update: function () { - Config.AttackSkill = [-1, sdk.skills.Smite, sdk.skills.Fanaticism, sdk.skills.Smite, sdk.skills.Fanaticism, sdk.skills.BlessedHammer, sdk.skills.Concentration]; + Config.AttackSkill = [ + -1, + sdk.skills.Smite, sdk.skills.Fanaticism, + sdk.skills.Smite, sdk.skills.Fanaticism, + sdk.skills.BlessedHammer, sdk.skills.Concentration + ]; Config.LowManaSkill = [0, sdk.skills.Fanaticism]; Config.BeltColumn = ["hp", "hp", "mp", "rv"]; SetUp.belt(); diff --git a/libs/SoloPlay/BuildFiles/paladin/paladin.TorchadinBuild.js b/libs/SoloPlay/BuildFiles/paladin/paladin.TorchadinBuild.js index 98519d56..06b30154 100644 --- a/libs/SoloPlay/BuildFiles/paladin/paladin.TorchadinBuild.js +++ b/libs/SoloPlay/BuildFiles/paladin/paladin.TorchadinBuild.js @@ -36,8 +36,14 @@ max: 6, have: [], classid: sdk.items.SmallCharm, + /** @param {ItemUnit} check */ stats: function (check) { - return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MaxHp) === 20); + return ( + !check.unique + && check.classid === this.classid + && check.allRes === 5 + && check.getStat(sdk.stats.MaxHp) === 20 + ); } }, @@ -45,8 +51,14 @@ max: 2, have: [], classid: sdk.items.SmallCharm, + /** @param {ItemUnit} check */ stats: function (check) { - return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MagicBonus) === 7); + return ( + !check.unique + && check.classid === this.classid + && check.allRes === 5 + && check.getStat(sdk.stats.MagicBonus) === 7 + ); } }, @@ -54,9 +66,14 @@ max: 2, have: [], classid: sdk.items.GrandCharm, + /** @param {ItemUnit} check */ stats: function (check) { - return (!check.unique && check.classid === this.classid && check.getStat(sdk.stats.AddSkillTab, sdk.skills.tabs.Offensive) === 1 - && check.getStat(sdk.stats.MaxHp) >= 40); + return ( + !check.unique + && check.classid === this.classid + && check.getStat(sdk.stats.AddSkillTab, sdk.skills.tabs.Offensive) === 1 + && check.getStat(sdk.stats.MaxHp) >= 40 + ); } }, }, @@ -65,7 +82,12 @@ 1: { Update: function () { Config.Vigor = false; - Config.AttackSkill = [-1, sdk.skills.Zeal, sdk.skills.Conviction, sdk.skills.Zeal, sdk.skills.Conviction, -1, -1]; + Config.AttackSkill = [ + -1, + sdk.skills.Zeal, sdk.skills.Conviction, + sdk.skills.Zeal, sdk.skills.Conviction, + -1, -1 + ]; Config.LowManaSkill = [-1, -1]; Config.SkipImmune = ["fire and physical"]; Config.BeltColumn = ["hp", "hp", "mp", "rv"]; @@ -77,9 +99,13 @@ respec: function () { if (me.classic) { return false; - } else { - return me.haveAll([{ name: sdk.locale.items.HandofJustice }, { name: sdk.locale.items.Dragon, itemtype: sdk.items.type.Armor }]); } + return ( + me.haveAll([ + { name: sdk.locale.items.HandofJustice }, + { name: sdk.locale.items.Dragon, itemtype: sdk.items.type.Armor } + ]) + ); }, active: function () { diff --git a/libs/SoloPlay/BuildFiles/paladin/paladin.ZealerBuild.js b/libs/SoloPlay/BuildFiles/paladin/paladin.ZealerBuild.js index 5860146c..7b589760 100644 --- a/libs/SoloPlay/BuildFiles/paladin/paladin.ZealerBuild.js +++ b/libs/SoloPlay/BuildFiles/paladin/paladin.ZealerBuild.js @@ -36,8 +36,14 @@ max: 6, have: [], classid: sdk.items.SmallCharm, + /** @param {ItemUnit} check */ stats: function (check) { - return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MaxHp) === 20); + return ( + !check.unique + && check.classid === this.classid + && check.allRes === 5 + && check.getStat(sdk.stats.MaxHp) === 20 + ); } }, @@ -45,8 +51,14 @@ max: 2, have: [], classid: sdk.items.SmallCharm, + /** @param {ItemUnit} check */ stats: function (check) { - return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MagicBonus) === 7); + return ( + !check.unique + && check.classid === this.classid + && check.allRes === 5 + && check.getStat(sdk.stats.MagicBonus) === 7 + ); } }, @@ -54,9 +66,14 @@ max: 2, have: [], classid: sdk.items.GrandCharm, + /** @param {ItemUnit} check */ stats: function (check) { - return (!check.unique && check.classid === this.classid && check.getStat(sdk.stats.AddSkillTab, sdk.skills.tabs.PalaCombat) === 1 - && check.getStat(sdk.stats.MaxHp) >= 40); + return ( + !check.unique + && check.classid === this.classid + && check.getStat(sdk.stats.AddSkillTab, sdk.skills.tabs.PalaCombat) === 1 + && check.getStat(sdk.stats.MaxHp) >= 40 + ); } }, }, @@ -64,7 +81,12 @@ AutoBuildTemplate: { 1: { Update: function () { - Config.AttackSkill = [-1, sdk.skills.Zeal, sdk.skills.Fanaticism, sdk.skills.Zeal, sdk.skills.Fanaticism, -1, -1]; + Config.AttackSkill = [ + -1, + sdk.skills.Zeal, sdk.skills.Fanaticism, + sdk.skills.Zeal, sdk.skills.Fanaticism, + -1, -1 + ]; Config.LowManaSkill = [-1, -1]; Config.BeltColumn = ["hp", "hp", "mp", "rv"]; SetUp.belt(); diff --git a/libs/SoloPlay/BuildFiles/paladin/paladin.js b/libs/SoloPlay/BuildFiles/paladin/paladin.js index 60de8ff4..7323bc04 100644 --- a/libs/SoloPlay/BuildFiles/paladin/paladin.js +++ b/libs/SoloPlay/BuildFiles/paladin/paladin.js @@ -8,7 +8,7 @@ const CharInfo = { respecOne: 19, respecTwo: 0, - levelCap: (function() { + levelCap: (function () { const currentDiff = sdk.difficulty.nameOf(me.diff); const softcoreMode = { "Normal": me.expansion ? 33 : 33, @@ -31,7 +31,11 @@ const CharInfo = { switch (true) { case currLevel < this.respecOne: - case !justRepeced && currLevel > this.respecOne && !me.checkSkill(sdk.skills.Concentration, sdk.skills.subindex.HardPoints): + case ( + !justRepeced + && currLevel > this.respecOne + && !me.checkSkill(sdk.skills.Concentration, sdk.skills.subindex.HardPoints) + ): return "Start"; case Check.finalBuild().respec() && justRepeced: case Check.finalBuild().active(): diff --git a/libs/SoloPlay/BuildFiles/sorceress/sorceress.BlizzballerBuild.js b/libs/SoloPlay/BuildFiles/sorceress/sorceress.BlizzballerBuild.js index c5755a7c..b313a42d 100644 --- a/libs/SoloPlay/BuildFiles/sorceress/sorceress.BlizzballerBuild.js +++ b/libs/SoloPlay/BuildFiles/sorceress/sorceress.BlizzballerBuild.js @@ -42,6 +42,7 @@ max: 3, have: [], classid: sdk.items.SmallCharm, + /** @param {ItemUnit} check */ stats: function (check) { return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MaxHp) === 20); } @@ -51,6 +52,7 @@ max: 2, have: [], classid: sdk.items.SmallCharm, + /** @param {ItemUnit} check */ stats: function (check) { return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MagicBonus) === 7); } @@ -60,6 +62,7 @@ max: 3, have: [], classid: sdk.items.SmallCharm, + /** @param {ItemUnit} check */ stats: function (check) { return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.FHR) === 5); } @@ -69,6 +72,7 @@ max: 1, have: [], classid: sdk.items.GrandCharm, + /** @param {ItemUnit} check */ stats: function (check) { return (!check.unique && check.classid === this.classid && check.getStat(sdk.stats.AddSkillTab, sdk.skills.tabs.Fire) === 1 && check.getStat(sdk.stats.MaxHp) >= 40); @@ -79,6 +83,7 @@ max: 1, have: [], classid: sdk.items.GrandCharm, + /** @param {ItemUnit} check */ stats: function (check) { return (!check.unique && check.classid === this.classid && check.getStat(sdk.stats.AddSkillTab, sdk.skills.tabs.Cold) === 1 && check.getStat(sdk.stats.MaxHp) >= 40); diff --git a/libs/SoloPlay/BuildFiles/sorceress/sorceress.BlovaBuild.js b/libs/SoloPlay/BuildFiles/sorceress/sorceress.BlovaBuild.js index e701945a..19ba2a27 100644 --- a/libs/SoloPlay/BuildFiles/sorceress/sorceress.BlovaBuild.js +++ b/libs/SoloPlay/BuildFiles/sorceress/sorceress.BlovaBuild.js @@ -14,7 +14,12 @@ wantedskills: [sdk.skills.Blizzard, sdk.skills.Nova], usefulskills: [sdk.skills.LightningMastery, sdk.skills.ColdMastery, sdk.skills.GlacialSpike], precastSkills: [sdk.skills.FrozenArmor], - usefulStats: [sdk.stats.PassiveColdPierce, sdk.stats.PassiveColdMastery, sdk.stats.PassiveLightningMastery, sdk.stats.PassiveLightningPierce], + usefulStats: [ + sdk.stats.PassiveColdPierce, + sdk.stats.PassiveColdMastery, + sdk.stats.PassiveLightningMastery, + sdk.stats.PassiveLightningPierce + ], wantedMerc: MercData[sdk.skills.HolyFreeze], stats: [ ["strength", 156], ["dexterity", 35], ["vitality", "all"] @@ -38,6 +43,7 @@ max: 3, have: [], classid: sdk.items.SmallCharm, + /** @param {ItemUnit} check */ stats: function (check) { return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MaxHp) === 20); } @@ -47,6 +53,7 @@ max: 2, have: [], classid: sdk.items.SmallCharm, + /** @param {ItemUnit} check */ stats: function (check) { return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MagicBonus) === 7); } @@ -56,6 +63,7 @@ max: 3, have: [], classid: sdk.items.SmallCharm, + /** @param {ItemUnit} check */ stats: function (check) { return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.FHR) === 5); } @@ -65,6 +73,7 @@ max: 1, have: [], classid: sdk.items.GrandCharm, + /** @param {ItemUnit} check */ stats: function (check) { return (!check.unique && check.classid === this.classid && check.getStat(sdk.stats.AddSkillTab, sdk.skills.tabs.Lightning) === 1 && check.getStat(sdk.stats.MaxHp) >= 40); @@ -75,6 +84,7 @@ max: 1, have: [], classid: sdk.items.GrandCharm, + /** @param {ItemUnit} check */ stats: function (check) { return (!check.unique && check.classid === this.classid && check.getStat(sdk.stats.AddSkillTab, sdk.skills.tabs.Cold) === 1 && check.getStat(sdk.stats.MaxHp) >= 40); diff --git a/libs/SoloPlay/BuildFiles/sorceress/sorceress.ColdBuild.js b/libs/SoloPlay/BuildFiles/sorceress/sorceress.ColdBuild.js index 0d494374..3ff76555 100644 --- a/libs/SoloPlay/BuildFiles/sorceress/sorceress.ColdBuild.js +++ b/libs/SoloPlay/BuildFiles/sorceress/sorceress.ColdBuild.js @@ -38,6 +38,7 @@ max: 3, have: [], classid: sdk.items.SmallCharm, + /** @param {ItemUnit} check */ stats: function (check) { return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MaxHp) === 20); } @@ -47,6 +48,7 @@ max: 3, have: [], classid: sdk.items.SmallCharm, + /** @param {ItemUnit} check */ stats: function (check) { return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MagicBonus) === 7); } @@ -56,6 +58,7 @@ max: 1, have: [], classid: sdk.items.SmallCharm, + /** @param {ItemUnit} check */ stats: function (check) { return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.FHR) === 5); } @@ -65,6 +68,7 @@ max: 2, have: [], classid: sdk.items.GrandCharm, + /** @param {ItemUnit} check */ stats: function (check) { return (!check.unique && check.classid === this.classid && check.getStat(sdk.stats.AddSkillTab, sdk.skills.tabs.Cold) === 1 && check.getStat(sdk.stats.MaxHp) >= 40); diff --git a/libs/SoloPlay/BuildFiles/sorceress/sorceress.EsorbBuild.js b/libs/SoloPlay/BuildFiles/sorceress/sorceress.EsorbBuild.js index afda5d5e..b3d913fc 100644 --- a/libs/SoloPlay/BuildFiles/sorceress/sorceress.EsorbBuild.js +++ b/libs/SoloPlay/BuildFiles/sorceress/sorceress.EsorbBuild.js @@ -34,6 +34,7 @@ max: 3, have: [], classid: sdk.items.SmallCharm, + /** @param {ItemUnit} check */ stats: function (check) { return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MaxHp) === 20); } @@ -43,6 +44,7 @@ max: 2, have: [], classid: sdk.items.SmallCharm, + /** @param {ItemUnit} check */ stats: function (check) { return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MagicBonus) === 7); } @@ -52,6 +54,7 @@ max: 3, have: [], classid: sdk.items.SmallCharm, + /** @param {ItemUnit} check */ stats: function (check) { return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.FHR) === 5); } @@ -61,6 +64,7 @@ max: 1, have: [], classid: sdk.items.GrandCharm, + /** @param {ItemUnit} check */ stats: function (check) { return (!check.unique && check.classid === this.classid && check.getStat(sdk.stats.AddSkillTab, sdk.skills.tabs.Lightning) === 1 && check.getStat(sdk.stats.MaxHp) >= 40); @@ -71,6 +75,7 @@ max: 1, have: [], classid: sdk.items.GrandCharm, + /** @param {ItemUnit} check */ stats: function (check) { return (!check.unique && check.classid === this.classid && check.getStat(sdk.stats.AddSkillTab, sdk.skills.tabs.Cold) === 1 && check.getStat(sdk.stats.MaxHp) >= 40); diff --git a/libs/SoloPlay/BuildFiles/sorceress/sorceress.LevelingBuild.js b/libs/SoloPlay/BuildFiles/sorceress/sorceress.LevelingBuild.js index 6272b038..d41efc74 100644 --- a/libs/SoloPlay/BuildFiles/sorceress/sorceress.LevelingBuild.js +++ b/libs/SoloPlay/BuildFiles/sorceress/sorceress.LevelingBuild.js @@ -13,19 +13,35 @@ skillstab: sdk.skills.tabs.Cold, wantedskills: [sdk.skills.Blizzard, sdk.skills.FireBall, sdk.skills.ColdMastery], usefulskills: [sdk.skills.GlacialSpike, sdk.skills.Meteor, sdk.skills.FireMastery, sdk.skills.StaticField], - usefulStats: [sdk.stats.PassiveColdPierce, sdk.stats.PassiveColdMastery, sdk.stats.PassiveFireMastery, sdk.stats.PassiveFirePierce], + usefulStats: [ + sdk.stats.PassiveColdPierce, + sdk.stats.PassiveColdMastery, + sdk.stats.PassiveFireMastery, + sdk.stats.PassiveFirePierce + ], wantedMerc: MercData[sdk.skills.HolyFreeze], stats: [], skills: [], active: function () { - return (me.charlvl > CharInfo.respecOne && me.charlvl > CharInfo.respecTwo && me.checkSkill(sdk.skills.FireMastery, sdk.skills.subindex.HardPoints) && !Check.finalBuild().active()); + const { respecOne, respecTwo } = CharInfo; + return ( + me.charlvl > respecOne && me.charlvl > respecTwo + && me.checkSkill(sdk.skills.FireMastery, sdk.skills.subindex.HardPoints) + && !Check.finalBuild().active() + ); }, AutoBuildTemplate: { 1: { Update: function () { - Config.AttackSkill = [-1, sdk.skills.Blizzard, sdk.skills.IceBlast, sdk.skills.Blizzard, sdk.skills.IceBlast, sdk.skills.Meteor, sdk.skills.FireBall]; + Config.AttackSkill = [ + -1, + sdk.skills.Blizzard, sdk.skills.IceBlast, + sdk.skills.Blizzard, sdk.skills.IceBlast, + sdk.skills.Meteor, sdk.skills.FireBall, + sdk.skills.Nova, sdk.skills.StaticField + ]; Config.LowManaSkill = [-1, -1]; Config.SkipImmune = ["fire and cold"]; Config.BeltColumn = ["hp", "hp", "mp", "mp"]; @@ -36,7 +52,7 @@ } }, }; - + // Has to be set after its loaded build.stats = me.classic ? [ @@ -46,7 +62,7 @@ ["strength", 61], ["vitality", 200], ["strength", 127], ["vitality", 252], ["dexterity", "block"], ["vitality", "all"] ]; - + build.skills = me.classic ? [ // Total skills at respec = 70 @@ -74,9 +90,12 @@ [sdk.skills.Meteor, 1], // points left 59 [sdk.skills.FireMastery, 1], // points left 58 [sdk.skills.ColdMastery, 1], // points left 57 + [sdk.skills.LightningMastery, 1], // points left 56 + [sdk.skills.Nova, 1], // points left 55 [sdk.skills.FrozenOrb, 1], // points left 51 [sdk.skills.FireBall, 20], // points left 32 [sdk.skills.Blizzard, 20], // points left 13 + [sdk.skills.Nova, 5], // points left 8 [sdk.skills.IceBlast, 15], // points left 0 [sdk.skills.Meteor, 15], [sdk.skills.IceBlast, 20], @@ -84,7 +103,7 @@ [sdk.skills.ColdMastery, 5], [sdk.skills.FireBolt, 20], ]; - + return build; })(); })(module); diff --git a/libs/SoloPlay/BuildFiles/sorceress/sorceress.LightningBuild.js b/libs/SoloPlay/BuildFiles/sorceress/sorceress.LightningBuild.js index 48ef2ddf..23410a79 100644 --- a/libs/SoloPlay/BuildFiles/sorceress/sorceress.LightningBuild.js +++ b/libs/SoloPlay/BuildFiles/sorceress/sorceress.LightningBuild.js @@ -38,8 +38,10 @@ max: 3, have: [], classid: sdk.items.SmallCharm, + /** @param {ItemUnit} check */ stats: function (check) { - return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MaxHp) === 20); + return (!check.unique && check.classid === this.classid + && check.allRes === 5 && check.getStat(sdk.stats.MaxHp) === 20); } }, @@ -47,8 +49,10 @@ max: 2, have: [], classid: sdk.items.SmallCharm, + /** @param {ItemUnit} check */ stats: function (check) { - return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MagicBonus) === 7); + return (!check.unique && check.classid === this.classid + & check.allRes === 5 && check.getStat(sdk.stats.MagicBonus) === 7); } }, @@ -56,8 +60,10 @@ max: 1, have: [], classid: sdk.items.SmallCharm, + /** @param {ItemUnit} check */ stats: function (check) { - return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.FHR) === 5); + return (!check.unique && check.classid === this.classid + && check.allRes === 5 && check.getStat(sdk.stats.FHR) === 5); } }, @@ -65,8 +71,10 @@ max: 2, have: [], classid: sdk.items.SmallCharm, + /** @param {ItemUnit} check */ stats: function (check) { - return (!check.unique && check.classid === this.classid && check.getStat(sdk.stats.MaxHp) === 20 && check.getStat(sdk.stats.MaxMana) === 17); + return (!check.unique && check.classid === this.classid + && check.getStat(sdk.stats.MaxHp) === 20 && check.getStat(sdk.stats.MaxMana) === 17); } }, @@ -74,8 +82,10 @@ max: 2, have: [], classid: sdk.items.GrandCharm, + /** @param {ItemUnit} check */ stats: function (check) { - return (!check.unique && check.classid === this.classid && check.getStat(sdk.stats.AddSkillTab, sdk.skills.tabs.Lightning) === 1 + return (!check.unique && check.classid === this.classid + && check.getStat(sdk.stats.AddSkillTab, sdk.skills.tabs.Lightning) === 1 && check.getStat(sdk.stats.MaxHp) >= 40); } }, @@ -84,7 +94,12 @@ AutoBuildTemplate: { 1: { Update: function () { - Config.AttackSkill = [-1, sdk.skills.Lightning, sdk.skills.ChainLightning, sdk.skills.ChainLightning, sdk.skills.Lightning, -1, -1]; + Config.AttackSkill = [ + -1, + sdk.skills.Lightning, sdk.skills.ChainLightning, + sdk.skills.ChainLightning, sdk.skills.Lightning, + 1, -1 + ]; Config.LowManaSkill = [-1, -1]; Config.SkipImmune = ["lightning"]; } @@ -95,12 +110,18 @@ if (me.classic) { return me.charlvl >= 75 && me.diablo; } else { - return (Attack.checkInfinity() || (me.data.merc.gear.includes(sdk.locale.items.Infinity) && !Misc.poll(() => me.getMerc(), 200, 50))); + return (Attack.checkInfinity() + || ( + me.data.merc.gear.includes(sdk.locale.items.Infinity) + && !Misc.poll(() => me.getMerc(), 200, 50) + )); } }, active: function () { - return this.respec() && me.getSkill(sdk.skills.Lightning, sdk.skills.subindex.HardPoints) === 20 && !me.checkSkill(sdk.skills.Blizzard, sdk.skills.subindex.HardPoints); + return this.respec() + && me.getSkill(sdk.skills.Lightning, sdk.skills.subindex.HardPoints) === 20 + && !me.checkSkill(sdk.skills.Blizzard, sdk.skills.subindex.HardPoints); }, }; diff --git a/libs/SoloPlay/BuildFiles/sorceress/sorceress.MeteorbBuild.js b/libs/SoloPlay/BuildFiles/sorceress/sorceress.MeteorbBuild.js index a260983e..87c63fbb 100644 --- a/libs/SoloPlay/BuildFiles/sorceress/sorceress.MeteorbBuild.js +++ b/libs/SoloPlay/BuildFiles/sorceress/sorceress.MeteorbBuild.js @@ -38,6 +38,7 @@ max: 3, have: [], classid: sdk.items.SmallCharm, + /** @param {ItemUnit} check */ stats: function (check) { return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MaxHp) === 20); } @@ -47,6 +48,7 @@ max: 2, have: [], classid: sdk.items.SmallCharm, + /** @param {ItemUnit} check */ stats: function (check) { return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MagicBonus) === 7); } @@ -56,6 +58,7 @@ max: 3, have: [], classid: sdk.items.SmallCharm, + /** @param {ItemUnit} check */ stats: function (check) { return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.FHR) === 5); } @@ -65,6 +68,7 @@ max: 1, have: [], classid: sdk.items.GrandCharm, + /** @param {ItemUnit} check */ stats: function (check) { return (!check.unique && check.classid === this.classid && check.getStat(sdk.stats.AddSkillTab, sdk.skills.tabs.Fire) === 1 && check.getStat(sdk.stats.MaxHp) >= 40); @@ -75,6 +79,7 @@ max: 1, have: [], classid: sdk.items.GrandCharm, + /** @param {ItemUnit} check */ stats: function (check) { return (!check.unique && check.classid === this.classid && check.getStat(sdk.stats.AddSkillTab, sdk.skills.tabs.Cold) === 1 && check.getStat(sdk.stats.MaxHp) >= 40); diff --git a/libs/SoloPlay/BuildFiles/sorceress/sorceress.StartBuild.js b/libs/SoloPlay/BuildFiles/sorceress/sorceress.StartBuild.js index ecc27fa0..cb0d0753 100644 --- a/libs/SoloPlay/BuildFiles/sorceress/sorceress.StartBuild.js +++ b/libs/SoloPlay/BuildFiles/sorceress/sorceress.StartBuild.js @@ -41,7 +41,8 @@ ], active: function () { - return me.charlvl < CharInfo.respecOne && !me.checkSkill(sdk.skills.ColdMastery, sdk.skills.subindex.HardPoints); + const { respecOne } = CharInfo; + return me.charlvl < respecOne && !me.checkSkill(sdk.skills.ColdMastery, sdk.skills.subindex.HardPoints); }, }; diff --git a/libs/SoloPlay/BuildFiles/sorceress/sorceress.SteppingBuild.js b/libs/SoloPlay/BuildFiles/sorceress/sorceress.SteppingBuild.js index bd1148da..78b5ccc2 100644 --- a/libs/SoloPlay/BuildFiles/sorceress/sorceress.SteppingBuild.js +++ b/libs/SoloPlay/BuildFiles/sorceress/sorceress.SteppingBuild.js @@ -19,16 +19,33 @@ skills: [], active: function () { - return me.charlvl > CharInfo.respecOne && me.charlvl < CharInfo.respecTwo && me.checkSkill(sdk.skills.Blizzard, sdk.skills.subindex.HardPoints) && !me.checkSkill(sdk.skills.Nova, sdk.skills.subindex.HardPoints) && !me.checkSkill(sdk.skills.FireMastery, sdk.skills.subindex.HardPoints); + const { respecOne, respecTwo } = CharInfo; + return ( + me.charlvl > respecOne && me.charlvl < respecTwo + && me.checkSkill(sdk.skills.Blizzard, sdk.skills.subindex.HardPoints) + && !me.checkSkill(sdk.skills.Nova, sdk.skills.subindex.HardPoints) + && !me.checkSkill(sdk.skills.FireMastery, sdk.skills.subindex.HardPoints) + ); }, AutoBuildTemplate: { 1: { Update: function () { - Config.AttackSkill = [-1, sdk.skills.Blizzard, sdk.skills.IceBlast, sdk.skills.Blizzard, sdk.skills.IceBlast, -1, -1]; + Config.AttackSkill = [ + -1, + sdk.skills.Blizzard, sdk.skills.IceBlast, + sdk.skills.Blizzard, sdk.skills.IceBlast, + (me.checkSkill(sdk.skills.Lightning, sdk.skills.subindex.HardPoints) + ? sdk.skills.Lightning + : sdk.skills.StaticField + ), -1 + ]; Config.BeltColumn = ["hp", "hp", "mp", "mp"]; Config.HPBuffer = me.expansion && !me.normal ? 2 : 5; - Config.MPBuffer = (me.expansion && !me.normal || Item.getMercEquipped(sdk.body.RightArm).prefixnum === sdk.locale.items.Insight) ? 2 : 5; + Config.MPBuffer = ( + me.expansion + && !me.normal || Item.getMercEquipped(sdk.body.RightArm).prefixnum === sdk.locale.items.Insight + ) ? 2 : 5; Config.SkipImmune = ["cold"]; SetUp.belt(); } @@ -74,9 +91,13 @@ [sdk.skills.ColdMastery, 1, false], [sdk.skills.FrozenOrb, 1, false], [sdk.skills.Blizzard, 20, false], - [sdk.skills.IceBlast, 20, false], - [sdk.skills.ColdMastery, 5], - [sdk.skills.GlacialSpike, 20, false], + [sdk.skills.ChainLightning, 1], + [sdk.skills.LightningMastery, 1], + [sdk.skills.Lightning, 5, false], + [sdk.skills.LightningMastery, 20], + // [sdk.skills.IceBlast, 20, false], + // [sdk.skills.ColdMastery, 5], + // [sdk.skills.GlacialSpike, 20, false], ]; return build; diff --git a/libs/SoloPlay/BuildFiles/sorceress/sorceress.js b/libs/SoloPlay/BuildFiles/sorceress/sorceress.js index ccb4ccba..1f94e46e 100644 --- a/libs/SoloPlay/BuildFiles/sorceress/sorceress.js +++ b/libs/SoloPlay/BuildFiles/sorceress/sorceress.js @@ -8,7 +8,7 @@ const CharInfo = { respecOne: me.expansion ? 26 : 26, respecTwo: me.expansion ? 63 : 60, - levelCap: (function() { + levelCap: (function () { const currentDiff = sdk.difficulty.nameOf(me.diff); const softcoreMode = { "Normal": me.expansion ? 33 : 33, diff --git a/libs/SoloPlay/Config/Amazon.js b/libs/SoloPlay/Config/Amazon.js index ddec90c8..78d1eb1c 100644 --- a/libs/SoloPlay/Config/Amazon.js +++ b/libs/SoloPlay/Config/Amazon.js @@ -203,11 +203,15 @@ includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/Faith.js"); } - if (!Check.haveItem(sdk.items.DiamondBow, "unique", "Witchwild String") + if (!me.checkItem({ name: sdk.locale.items.WitchwildString, classid: sdk.items.DiamondBow }).have && (SetUp.finalBuild === "Witchyzon" || ["Wfzon", "Faithbowzon"].indexOf(SetUp.currentBuild) === -1)) { NTIP.addLine("[name] == shortsiegebow && [quality] == unique # [fireresist] == 40 # [maxquantity] == 1"); // Witchyzon only - keep the bow but don't upgrade it until we have our wanted belt - if ((SetUp.finalBuild === "Witchyzon" && Check.haveItem("vampirefangbelt", "unique", "Nosferatu's Coil")) || ["Wfzon", "Faithbowzon"].includes(SetUp.finalBuild)) { + if (["Wfzon", "Faithbowzon"].includes(SetUp.finalBuild) + || ( + SetUp.finalBuild === "Witchyzon" + && me.checkItem({ name: sdk.locale.items.NosferatusCoil }).have + )) { Config.Recipes.push([Recipe.Unique.Weapon.ToElite, "Short Siege Bow", Roll.NonEth]); } } diff --git a/libs/SoloPlay/Config/Barbarian.js b/libs/SoloPlay/Config/Barbarian.js index 10eb05fb..5eeaced4 100644 --- a/libs/SoloPlay/Config/Barbarian.js +++ b/libs/SoloPlay/Config/Barbarian.js @@ -280,7 +280,7 @@ Config.Recipes.push([Recipe.Unique.Weapon.ToElite, "dimensionalblade", Roll.Eth]); } - if (!Check.haveItem("falcata", "unique", "Bloodletter")) { + if (!me.checkItem({ name: sdk.locale.items.Bloodletter, classid: sdk.items.Falcata }).have) { NTIP.addLine("[name] == PulRune # # [maxquantity] == 1"); NTIP.addLine("[name] == perfectemerald # # [maxquantity] == 1"); // Bloodletter @@ -290,6 +290,7 @@ } if (!Check.haveItem("dimensionalblade", "unique", "Ginther's Rift")) { + // if (!me.checkItem({ name: sdk.locale.items.GinthersRift, classid: sdk.items.DimensionalBlade }).have) { NTIP.addLine("[name] == PulRune # # [maxquantity] == 1"); NTIP.addLine("[name] == perfectemerald # # [maxquantity] == 1"); @@ -310,7 +311,10 @@ } // Voice Of Reason - Lem/Ko/El/Eld - if (me.equipped.get(sdk.body.RightArm).tier > 1100 && me.equipped.get(sdk.body.LeftArm).tier < 1270 && !Check.haveItem("ring", "unique", "Raven Frost")) { + if (me.equipped.get(sdk.body.RightArm).tier > 1100 + && me.equipped.get(sdk.body.LeftArm).tier < 1270 + && !me.checkItem({ name: sdk.locale.items.Ravenfrost }).have + ) { includeIfNotIncluded("SoloPlay/BuildFiles/Runewords/VoiceOfReason.js"); } diff --git a/libs/SoloPlay/Config/Druid.js b/libs/SoloPlay/Config/Druid.js index 7461c537..30366063 100644 --- a/libs/SoloPlay/Config/Druid.js +++ b/libs/SoloPlay/Config/Druid.js @@ -240,7 +240,7 @@ } // Don't have upgraded Ribcracker - if (!Check.haveItem("stalagmite", "unique", "Ribcracker")) { + if (!me.checkItem({ name: sdk.locale.items.Ribcracker, classid: sdk.items.Stalagmite }).have) { // Perfect ribcracker NTIP.addLine("[name] == quarterstaff && [quality] == unique # [enhanceddamage] == 300 && [ias] >= 50 # [maxquantity] == 1"); // Perfect upped ribcracker diff --git a/libs/SoloPlay/Config/Necromancer.js b/libs/SoloPlay/Config/Necromancer.js index d1854e07..c23443bb 100644 --- a/libs/SoloPlay/Config/Necromancer.js +++ b/libs/SoloPlay/Config/Necromancer.js @@ -72,8 +72,8 @@ Config.ClearPath = { Range: (Pather.canTeleport() ? 30 : 20), Spectype: 0 }; /* Class specific configuration. */ - Config.Dodge = Check.haveItem("armor", "runeword", "Enigma"); - Config.DodgeRange = Check.haveItem("armor", "runeword", "Enigma") ? 10 : 5; + Config.Dodge = me.checkItem({ name: sdk.locale.items.Enigma, itemtype: sdk.items.type.Armor }).have; + Config.DodgeRange = me.checkItem({ name: sdk.locale.items.Enigma, itemtype: sdk.items.type.Armor }).have ? 10 : 5; Config.DodgeHP = 90; // Dodge only if HP percent is less than or equal to Config.DodgeHP. 100 = always dodge. /* Summons. */ diff --git a/libs/SoloPlay/Config/Paladin.js b/libs/SoloPlay/Config/Paladin.js index 1b30d391..31af1011 100644 --- a/libs/SoloPlay/Config/Paladin.js +++ b/libs/SoloPlay/Config/Paladin.js @@ -42,9 +42,14 @@ Config.GambleItems.push("Coronet"); let weapons = [ - sdk.items.type.Scepter, sdk.items.type.Mace, - sdk.items.type.Sword, sdk.items.type.Knife, sdk.items.type.Axe, - sdk.items.type.Wand, sdk.items.type.Hammer, sdk.items.type.Club + sdk.items.type.Scepter, + sdk.items.type.Mace, + sdk.items.type.Sword, + sdk.items.type.Knife, + sdk.items.type.Axe, + sdk.items.type.Wand, + sdk.items.type.Hammer, + sdk.items.type.Club ].map(el => "[type] == " + el).join(" || "); // AutoEquip setup @@ -205,7 +210,7 @@ Check.itemSockables(sdk.items.GrimHelm, "unique", "Vampire Gaze"); Check.itemSockables(sdk.items.BoneVisage, "unique", "Vampire Gaze"); - if (!Check.haveItem("bonevisage", "unique", "Vampire Gaze")) { + if (!me.checkItem({ name: sdk.locale.items.VampireGaze, classid: sdk.items.BoneVisage }).have) { // Upgrade Vamp Gaze to Elite Config.Recipes.push([Recipe.Unique.Armor.ToElite, "Grim Helm", Roll.NonEth]); } diff --git a/libs/SoloPlay/Config/Sorceress.js b/libs/SoloPlay/Config/Sorceress.js index e73f69de..bf5bba07 100644 --- a/libs/SoloPlay/Config/Sorceress.js +++ b/libs/SoloPlay/Config/Sorceress.js @@ -41,10 +41,22 @@ Config.GambleItems.push("Coronet"); // AutoEquip setup + const _wepTypes = [ + "[type] == axe", + "[type] == club", + "[type] == knife", + "[type] == hammer", + "[type] == javelin", + "[type] == mace", + "[type] == orb", + "[type] == scepter", + "[type] == sword", + "[type] == wand", + ].join(" || "); const levelingTiers = [ // Weapon "me.normal && [type] == orb && [quality] >= normal && [flag] != ethereal # [itemchargedskill] >= 0 # [tier] == tierscore(item)", - "me.charlvl > 1 && ([type] == orb || [type] == wand || [type] == sword || [type] == knife) && ([quality] >= magic || [flag] == runeword) && [flag] != ethereal && [2handed] == 0 # [itemchargedskill] >= 0 # [tier] == tierscore(item)", + "me.charlvl > 1 && (" + _wepTypes + ") && ([quality] >= magic || [flag] == runeword) && [flag] != ethereal && [2handed] == 0 # [itemchargedskill] >= 0 # [tier] == tierscore(item)", "me.classic && [type] == staff && [quality] >= magic # [itemchargedskill] >= 0 # [tier] == tierscore(item)", // Shield "[type] == shield && ([quality] >= magic || [flag] == runeword) && [flag] != ethereal # [itemchargedskill] >= 0 # [tier] == tierscore(item)", @@ -246,7 +258,7 @@ } // Go ahead and keep two P-diamonds prior to finding a moser's unless already using a better shield - if (!Check.haveItem("shield", "unique", "Moser's Blessed Circle") + if (!me.checkItem({ name: sdk.locale.items.MosersBlessedCircle }).have && !me.haveSome([ { name: sdk.locale.items.Sanctuary }, { name: sdk.locale.items.Spirit, itemtype: sdk.items.type.Shield } From 6098aec80bae472e245bd28df0d5b05346b3e264 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Sun, 21 Jan 2024 23:43:50 -0500 Subject: [PATCH 243/263] More cleanup, fixed missed `haveItem` calls - Can replace Windforce and GinthersRift checks now that they've been added to sdk --- .../amazon/amazon.FrostmaidenBuild.js | 59 +++++++++++++++---- .../BuildFiles/amazon/amazon.WfzonBuild.js | 2 +- .../barbarian/barbarian.UberconcBuild.js | 1 + .../BuildFiles/barbarian/barbarian.js | 12 +++- .../paladin/paladin.AuradinBuild.js | 49 ++++++++++++--- .../paladin/paladin.ClassicauradinBuild.js | 35 ++++++++--- .../sorceress/sorceress.BlizzballerBuild.js | 57 ++++++++++++++---- .../sorceress/sorceress.EsorbBuild.js | 44 +++++++++++--- .../sorceress/sorceress.MeteorbBuild.js | 57 ++++++++++++++---- libs/SoloPlay/Config/Amazon.js | 2 +- libs/SoloPlay/Config/Barbarian.js | 3 +- 11 files changed, 259 insertions(+), 62 deletions(-) diff --git a/libs/SoloPlay/BuildFiles/amazon/amazon.FrostmaidenBuild.js b/libs/SoloPlay/BuildFiles/amazon/amazon.FrostmaidenBuild.js index 17940b88..4940acc5 100644 --- a/libs/SoloPlay/BuildFiles/amazon/amazon.FrostmaidenBuild.js +++ b/libs/SoloPlay/BuildFiles/amazon/amazon.FrostmaidenBuild.js @@ -12,7 +12,13 @@ caster: false, skillstab: sdk.skills.tabs.BowandCrossbow, wantedskills: [sdk.skills.Strafe, sdk.skills.FreezingArrow, sdk.skills.ExplodingArrow], - usefulskills: [sdk.skills.Penetrate, sdk.skills.Valkyrie, sdk.skills.Pierce, sdk.skills.IceArrow, sdk.skills.ColdArrow], + usefulskills: [ + sdk.skills.Penetrate, + sdk.skills.Valkyrie, + sdk.skills.Pierce, + sdk.skills.IceArrow, + sdk.skills.ColdArrow + ], precastSkills: [sdk.skills.Valkyrie], wantedMerc: MercData[sdk.skills.Might], stats: [ @@ -36,7 +42,12 @@ classid: sdk.items.SmallCharm, /** @param {ItemUnit} check */ stats: function (check) { - return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MaxHp) === 20); + return ( + !check.unique + && check.classid === this.classid + && check.allRes === 5 + && check.getStat(sdk.stats.MaxHp) === 20 + ); } }, @@ -46,7 +57,12 @@ classid: sdk.items.SmallCharm, /** @param {ItemUnit} check */ stats: function (check) { - return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MagicBonus) === 7); + return ( + !check.unique + && check.classid === this.classid + && check.allRes === 5 + && check.getStat(sdk.stats.MagicBonus) === 7 + ); } }, @@ -56,7 +72,12 @@ classid: sdk.items.SmallCharm, /** @param {ItemUnit} check */ stats: function (check) { - return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.FHR) === 5); + return ( + !check.unique + && check.classid === this.classid + && check.allRes === 5 + && check.getStat(sdk.stats.FHR) === 5 + ); } }, @@ -66,8 +87,12 @@ classid: sdk.items.GrandCharm, /** @param {ItemUnit} check */ stats: function (check) { - return (!check.unique && check.classid === this.classid && check.getStat(sdk.stats.AddSkillTab, sdk.skills.tabs.BowandCrossbow) === 1 - && check.getStat(sdk.stats.MaxHp) >= 40); + return ( + !check.unique + && check.classid === this.classid + && check.getStat(sdk.stats.AddSkillTab, sdk.skills.tabs.BowandCrossbow) === 1 + && check.getStat(sdk.stats.MaxHp) >= 40 + ); } }, @@ -77,8 +102,12 @@ classid: sdk.items.GrandCharm, /** @param {ItemUnit} check */ stats: function (check) { - return (!check.unique && check.classid === this.classid && check.getStat(sdk.stats.AddSkillTab, sdk.skills.tabs.PassiveandMagic) === 1 - && check.getStat(sdk.stats.MaxHp) >= 40); + return ( + !check.unique + && check.classid === this.classid + && check.getStat(sdk.stats.AddSkillTab, sdk.skills.tabs.PassiveandMagic) === 1 + && check.getStat(sdk.stats.MaxHp) >= 40 + ); } }, }, @@ -86,7 +115,12 @@ AutoBuildTemplate: { 1: { Update: function () { - Config.AttackSkill = [-1, sdk.skills.FreezingArrow, -1, sdk.skills.FreezingArrow, -1, sdk.skills.Strafe, -1]; + Config.AttackSkill = [ + -1, + sdk.skills.FreezingArrow, -1, + sdk.skills.FreezingArrow, -1, + sdk.skills.Strafe, -1 + ]; Config.LowManaSkill = [0, -1]; } }, @@ -95,9 +129,12 @@ respec: function () { if (me.classic) { return false; - } else { - return me.haveAll([{ name: sdk.locale.items.Dream, itemtype: sdk.items.type.Helm }, { name: sdk.locale.items.Brand }, { name: sdk.locale.items.ChainsofHonor }]); } + return me.haveAll([ + { name: sdk.locale.items.Dream, itemtype: sdk.items.type.Helm }, + { name: sdk.locale.items.Brand }, + { name: sdk.locale.items.ChainsofHonor } + ]); }, active: function () { diff --git a/libs/SoloPlay/BuildFiles/amazon/amazon.WfzonBuild.js b/libs/SoloPlay/BuildFiles/amazon/amazon.WfzonBuild.js index 2a3bb1a3..a9212fe3 100644 --- a/libs/SoloPlay/BuildFiles/amazon/amazon.WfzonBuild.js +++ b/libs/SoloPlay/BuildFiles/amazon/amazon.WfzonBuild.js @@ -122,7 +122,7 @@ return false; } return ( - Check.haveItem("hydrabow", "unique", "Windforce") + me.checkItem({ name: sdk.locale.items.Windforce, classid: sdk.items.HydraBow }).have || me.checkItem({ name: sdk.locale.items.WitchwildString, classid: sdk.items.DiamondBow }).have ); }, diff --git a/libs/SoloPlay/BuildFiles/barbarian/barbarian.UberconcBuild.js b/libs/SoloPlay/BuildFiles/barbarian/barbarian.UberconcBuild.js index 3a0ba7f8..30fa4690 100644 --- a/libs/SoloPlay/BuildFiles/barbarian/barbarian.UberconcBuild.js +++ b/libs/SoloPlay/BuildFiles/barbarian/barbarian.UberconcBuild.js @@ -6,6 +6,7 @@ */ +// eslint-disable-next-line no-unused-vars (function (module, require) { module.exports = (function () { const build = { diff --git a/libs/SoloPlay/BuildFiles/barbarian/barbarian.js b/libs/SoloPlay/BuildFiles/barbarian/barbarian.js index a2a748be..152ba70e 100644 --- a/libs/SoloPlay/BuildFiles/barbarian/barbarian.js +++ b/libs/SoloPlay/BuildFiles/barbarian/barbarian.js @@ -28,12 +28,18 @@ const CharInfo = { const nSkills = me.getStat(sdk.stats.NewSkills); const currLevel = me.charlvl; const justRepeced = (nSkills >= currLevel); + const { respecOne, respecTwo } = this; switch (true) { - case currLevel < this.respecOne && !me.checkSkill(sdk.skills.WarCry, sdk.skills.subindex.HardPoints): + case currLevel < respecOne && !me.checkSkill(sdk.skills.WarCry, sdk.skills.subindex.HardPoints): return "Start"; - case currLevel >= this.respecOne && currLevel < this.respecTwo && justRepeced: - case currLevel >= this.respecOne && currLevel < this.respecTwo && me.checkSkill(sdk.skills.NaturalResistance, sdk.skills.subindex.HardPoints) && !me.checkSkill(sdk.skills.Berserk, sdk.skills.subindex.HardPoints): + case currLevel >= respecOne && currLevel < respecTwo && justRepeced: + case ( + currLevel >= respecOne + && currLevel < respecTwo + && me.checkSkill(sdk.skills.NaturalResistance, sdk.skills.subindex.HardPoints) + && !me.checkSkill(sdk.skills.Berserk, sdk.skills.subindex.HardPoints) + ): return "Stepping"; case Check.finalBuild().respec() && justRepeced: case Check.finalBuild().active(): diff --git a/libs/SoloPlay/BuildFiles/paladin/paladin.AuradinBuild.js b/libs/SoloPlay/BuildFiles/paladin/paladin.AuradinBuild.js index 6b467113..47601b81 100644 --- a/libs/SoloPlay/BuildFiles/paladin/paladin.AuradinBuild.js +++ b/libs/SoloPlay/BuildFiles/paladin/paladin.AuradinBuild.js @@ -14,7 +14,14 @@ wantedskills: [sdk.skills.Zeal, sdk.skills.Conviction], usefulskills: [sdk.skills.HolyShield, sdk.skills.ResistFire, sdk.skills.ResistLightning], precastSkills: [sdk.skills.HolyShield], - usefulStats: [sdk.stats.PassiveLightningMastery, sdk.stats.PassiveLightningPierce, sdk.stats.PierceLtng, sdk.stats.PassiveFireMastery, sdk.stats.PassiveFirePierce, sdk.stats.PierceFire], + usefulStats: [ + sdk.stats.PassiveLightningMastery, + sdk.stats.PassiveLightningPierce, + sdk.stats.PierceLtng, + sdk.stats.PassiveFireMastery, + sdk.stats.PassiveFirePierce, + sdk.stats.PierceFire + ], wantedMerc: MercData[sdk.skills.HolyFreeze], stats: [ ["strength", 103], ["dexterity", 136], ["vitality", 300], ["dexterity", "block"], ["vitality", "all"] @@ -37,7 +44,12 @@ classid: sdk.items.SmallCharm, /** @param {ItemUnit} check */ stats: function (check) { - return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MaxHp) === 20); + return ( + !check.unique + && check.classid === this.classid + && check.allRes === 5 + && check.getStat(sdk.stats.MaxHp) === 20 + ); } }, @@ -47,7 +59,12 @@ classid: sdk.items.SmallCharm, /** @param {ItemUnit} check */ stats: function (check) { - return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MagicBonus) === 7); + return ( + !check.unique + && check.classid === this.classid + && check.allRes === 5 + && check.getStat(sdk.stats.MagicBonus) === 7 + ); } }, @@ -57,8 +74,12 @@ classid: sdk.items.GrandCharm, /** @param {ItemUnit} check */ stats: function (check) { - return (!check.unique && check.classid === this.classid && check.getStat(sdk.stats.AddSkillTab, sdk.skills.tabs.Offensive) === 1 - && check.getStat(sdk.stats.MaxHp) >= 40); + return ( + !check.unique + && check.classid === this.classid + && check.getStat(sdk.stats.AddSkillTab, sdk.skills.tabs.Offensive) === 1 + && check.getStat(sdk.stats.MaxHp) >= 40 + ); } }, }, @@ -67,10 +88,18 @@ 1: { Update: function () { Config.Vigor = false; - Config.AttackSkill = [-1, sdk.skills.Zeal, sdk.skills.Conviction, sdk.skills.Zeal, sdk.skills.Conviction, -1, -1]; + Config.AttackSkill = [ + -1, + sdk.skills.Zeal, sdk.skills.Conviction, + sdk.skills.Zeal, sdk.skills.Conviction, + -1, -1 + ]; Config.LowManaSkill = [-1, -1]; - if (!me.haveSome([{ name: sdk.locale.items.Dragon, itemtype: sdk.items.type.Armor }, { name: sdk.locale.items.HandofJustice }])) { + if (!me.haveSome([ + { name: sdk.locale.items.Dragon, itemtype: sdk.items.type.Armor }, + { name: sdk.locale.items.HandofJustice } + ])) { Config.SkipImmune = ["lightning and physical"]; } else { Config.SkipImmune = ["lightning and fire and physical"]; // Don't think this ever happens but should skip if it does @@ -85,9 +114,11 @@ respec: function () { if (me.classic) { return false; - } else { - return me.haveAll([{ name: sdk.locale.items.Dream, itemtype: sdk.items.type.AuricShields }, { name: sdk.locale.items.Dream, itemtype: sdk.items.type.Helm }]); } + return me.haveAll([ + { name: sdk.locale.items.Dream, itemtype: sdk.items.type.AuricShields }, + { name: sdk.locale.items.Dream, itemtype: sdk.items.type.Helm } + ]); }, active: function () { diff --git a/libs/SoloPlay/BuildFiles/paladin/paladin.ClassicauradinBuild.js b/libs/SoloPlay/BuildFiles/paladin/paladin.ClassicauradinBuild.js index a35a270c..2690bbe9 100644 --- a/libs/SoloPlay/BuildFiles/paladin/paladin.ClassicauradinBuild.js +++ b/libs/SoloPlay/BuildFiles/paladin/paladin.ClassicauradinBuild.js @@ -37,7 +37,12 @@ classid: sdk.items.SmallCharm, /** @param {ItemUnit} check */ stats: function (check) { - return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MaxHp) === 20); + return ( + !check.unique + && check.classid === this.classid + && check.allRes === 5 + && check.getStat(sdk.stats.MaxHp) === 20 + ); } }, @@ -47,7 +52,12 @@ classid: sdk.items.SmallCharm, /** @param {ItemUnit} check */ stats: function (check) { - return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MagicBonus) === 7); + return ( + !check.unique + && check.classid === this.classid + && check.allRes === 5 + && check.getStat(sdk.stats.MagicBonus) === 7 + ); } }, @@ -57,8 +67,12 @@ classid: sdk.items.GrandCharm, /** @param {ItemUnit} check */ stats: function (check) { - return (!check.unique && check.classid === this.classid && check.getStat(sdk.stats.AddSkillTab, sdk.skills.tabs.Offensive) === 1 - && check.getStat(sdk.stats.MaxHp) >= 40); + return ( + !check.unique + && check.classid === this.classid + && check.getStat(sdk.stats.AddSkillTab, sdk.skills.tabs.Offensive) === 1 + && check.getStat(sdk.stats.MaxHp) >= 40 + ); } }, }, @@ -67,7 +81,12 @@ 1: { Update: function () { Config.Vigor = false; - Config.AttackSkill = [-1, sdk.skills.Zeal, sdk.skills.HolyShock, sdk.skills.Zeal, sdk.skills.HolyShock, sdk.skills.Zeal, sdk.skills.HolyFreeze]; + Config.AttackSkill = [ + -1, + sdk.skills.Zeal, sdk.skills.HolyShock, + sdk.skills.Zeal, sdk.skills.HolyShock, + sdk.skills.Zeal, sdk.skills.HolyFreeze + ]; Config.LowManaSkill = [-1, -1]; Config.SkipImmune = ["lightning and cold and physical"]; // Don't think this ever happens but should skip if it does @@ -79,9 +98,11 @@ respec: function () { if (me.classic) { return me.charlvl >= 75 && me.diablo; - } else { - return me.haveAll([{ name: sdk.locale.items.Dream, itemtype: sdk.items.type.AuricShields }, { name: sdk.locale.items.Dream, itemtype: sdk.items.type.Helm }]); } + return me.haveAll([ + { name: sdk.locale.items.Dream, itemtype: sdk.items.type.AuricShields }, + { name: sdk.locale.items.Dream, itemtype: sdk.items.type.Helm } + ]); }, active: function () { diff --git a/libs/SoloPlay/BuildFiles/sorceress/sorceress.BlizzballerBuild.js b/libs/SoloPlay/BuildFiles/sorceress/sorceress.BlizzballerBuild.js index b313a42d..4452c331 100644 --- a/libs/SoloPlay/BuildFiles/sorceress/sorceress.BlizzballerBuild.js +++ b/libs/SoloPlay/BuildFiles/sorceress/sorceress.BlizzballerBuild.js @@ -14,7 +14,12 @@ wantedskills: [sdk.skills.Blizzard, sdk.skills.FireBall, sdk.skills.ColdMastery], usefulskills: [sdk.skills.GlacialSpike, sdk.skills.Meteor, sdk.skills.FireMastery, sdk.skills.StaticField], precastSkills: [sdk.skills.FrozenArmor], - usefulStats: [sdk.stats.PassiveColdPierce, sdk.stats.PassiveColdMastery, sdk.stats.PassiveFireMastery, sdk.stats.PassiveFirePierce], + usefulStats: [ + sdk.stats.PassiveColdPierce, + sdk.stats.PassiveColdMastery, + sdk.stats.PassiveFireMastery, + sdk.stats.PassiveFirePierce + ], wantedMerc: MercData[sdk.skills.HolyFreeze], stats: [ ["energy", 50], ["strength", 48], ["vitality", 165], @@ -44,7 +49,12 @@ classid: sdk.items.SmallCharm, /** @param {ItemUnit} check */ stats: function (check) { - return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MaxHp) === 20); + return ( + !check.unique + && check.classid === this.classid + && check.allRes === 5 + && check.getStat(sdk.stats.MaxHp) === 20 + ); } }, @@ -54,7 +64,12 @@ classid: sdk.items.SmallCharm, /** @param {ItemUnit} check */ stats: function (check) { - return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MagicBonus) === 7); + return ( + !check.unique + && check.classid === this.classid + && check.allRes === 5 + && check.getStat(sdk.stats.MagicBonus) === 7 + ); } }, @@ -64,7 +79,12 @@ classid: sdk.items.SmallCharm, /** @param {ItemUnit} check */ stats: function (check) { - return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.FHR) === 5); + return ( + !check.unique + && check.classid === this.classid + && check.allRes === 5 + && check.getStat(sdk.stats.FHR) === 5 + ); } }, @@ -74,8 +94,12 @@ classid: sdk.items.GrandCharm, /** @param {ItemUnit} check */ stats: function (check) { - return (!check.unique && check.classid === this.classid && check.getStat(sdk.stats.AddSkillTab, sdk.skills.tabs.Fire) === 1 - && check.getStat(sdk.stats.MaxHp) >= 40); + return ( + !check.unique + && check.classid === this.classid + && check.getStat(sdk.stats.AddSkillTab, sdk.skills.tabs.Fire) === 1 + && check.getStat(sdk.stats.MaxHp) >= 40 + ); } }, @@ -85,8 +109,12 @@ classid: sdk.items.GrandCharm, /** @param {ItemUnit} check */ stats: function (check) { - return (!check.unique && check.classid === this.classid && check.getStat(sdk.stats.AddSkillTab, sdk.skills.tabs.Cold) === 1 - && check.getStat(sdk.stats.MaxHp) >= 40); + return ( + !check.unique + && check.classid === this.classid + && check.getStat(sdk.stats.AddSkillTab, sdk.skills.tabs.Cold) === 1 + && check.getStat(sdk.stats.MaxHp) >= 40 + ); } }, }, @@ -94,7 +122,12 @@ AutoBuildTemplate: { 1: { Update: function () { - Config.AttackSkill = [-1, sdk.skills.Blizzard, sdk.skills.FireBall, sdk.skills.Blizzard, sdk.skills.FireBall, sdk.skills.Meteor, sdk.skills.GlacialSpike]; + Config.AttackSkill = [ + -1, + sdk.skills.Blizzard, sdk.skills.FireBall, + sdk.skills.Blizzard, sdk.skills.FireBall, + sdk.skills.Meteor, sdk.skills.GlacialSpike + ]; Config.LowManaSkill = [-1, -1]; Config.SkipImmune = ["fire and cold"]; Config.HPBuffer = me.expansion ? 1 : 5; @@ -118,7 +151,11 @@ }, active: function () { - return this.respec() && me.getSkill(sdk.skills.FireBall, sdk.skills.subindex.HardPoints) === 20 && me.getSkill(sdk.skills.Blizzard, sdk.skills.subindex.HardPoints) === 20; + return ( + this.respec() + && me.getSkill(sdk.skills.FireBall, sdk.skills.subindex.HardPoints) === 20 + && me.getSkill(sdk.skills.Blizzard, sdk.skills.subindex.HardPoints) === 20 + ); }, }; diff --git a/libs/SoloPlay/BuildFiles/sorceress/sorceress.EsorbBuild.js b/libs/SoloPlay/BuildFiles/sorceress/sorceress.EsorbBuild.js index b3d913fc..b09e4aee 100644 --- a/libs/SoloPlay/BuildFiles/sorceress/sorceress.EsorbBuild.js +++ b/libs/SoloPlay/BuildFiles/sorceress/sorceress.EsorbBuild.js @@ -36,7 +36,12 @@ classid: sdk.items.SmallCharm, /** @param {ItemUnit} check */ stats: function (check) { - return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MaxHp) === 20); + return ( + !check.unique + && check.classid === this.classid + && check.allRes === 5 + && check.getStat(sdk.stats.MaxHp) === 20 + ); } }, @@ -46,7 +51,12 @@ classid: sdk.items.SmallCharm, /** @param {ItemUnit} check */ stats: function (check) { - return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MagicBonus) === 7); + return ( + !check.unique + && check.classid === this.classid + && check.allRes === 5 + && check.getStat(sdk.stats.MagicBonus) === 7 + ); } }, @@ -56,7 +66,12 @@ classid: sdk.items.SmallCharm, /** @param {ItemUnit} check */ stats: function (check) { - return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.FHR) === 5); + return ( + !check.unique + && check.classid === this.classid + && check.allRes === 5 + && check.getStat(sdk.stats.FHR) === 5 + ); } }, @@ -66,8 +81,12 @@ classid: sdk.items.GrandCharm, /** @param {ItemUnit} check */ stats: function (check) { - return (!check.unique && check.classid === this.classid && check.getStat(sdk.stats.AddSkillTab, sdk.skills.tabs.Lightning) === 1 - && check.getStat(sdk.stats.MaxHp) >= 40); + return ( + !check.unique + && check.classid === this.classid + && check.getStat(sdk.stats.AddSkillTab, sdk.skills.tabs.Lightning) === 1 + && check.getStat(sdk.stats.MaxHp) >= 40 + ); } }, @@ -77,8 +96,12 @@ classid: sdk.items.GrandCharm, /** @param {ItemUnit} check */ stats: function (check) { - return (!check.unique && check.classid === this.classid && check.getStat(sdk.stats.AddSkillTab, sdk.skills.tabs.Cold) === 1 - && check.getStat(sdk.stats.MaxHp) >= 40); + return ( + !check.unique + && check.classid === this.classid + && check.getStat(sdk.stats.AddSkillTab, sdk.skills.tabs.Cold) === 1 + && check.getStat(sdk.stats.MaxHp) >= 40 + ); } }, }, @@ -86,7 +109,12 @@ AutoBuildTemplate: { 1: { Update: function () { - Config.AttackSkill = [-1, sdk.skills.FrozenOrb, sdk.skills.StaticField, sdk.skills.FrozenOrb, sdk.skills.StaticField, -1, -1]; + Config.AttackSkill = [ + -1, + sdk.skills.FrozenOrb, sdk.skills.StaticField, + sdk.skills.FrozenOrb, sdk.skills.StaticField, + -1, -1 + ]; Config.LowManaSkill = [-1, -1]; Config.SkipImmune = ["cold"]; Config.HPBuffer = me.expansion ? 1 : 5; diff --git a/libs/SoloPlay/BuildFiles/sorceress/sorceress.MeteorbBuild.js b/libs/SoloPlay/BuildFiles/sorceress/sorceress.MeteorbBuild.js index 87c63fbb..af9e17ae 100644 --- a/libs/SoloPlay/BuildFiles/sorceress/sorceress.MeteorbBuild.js +++ b/libs/SoloPlay/BuildFiles/sorceress/sorceress.MeteorbBuild.js @@ -14,7 +14,12 @@ wantedskills: [sdk.skills.FrozenOrb, sdk.skills.Meteor, sdk.skills.ColdMastery], usefulskills: [sdk.skills.FireBall, sdk.skills.FireMastery, sdk.skills.StaticField], precastSkills: [sdk.skills.FrozenArmor], - usefulStats: [sdk.stats.PassiveColdPierce, sdk.stats.PassiveColdMastery, sdk.stats.PassiveFireMastery, sdk.stats.PassiveFirePierce], + usefulStats: [ + sdk.stats.PassiveColdPierce, + sdk.stats.PassiveColdMastery, + sdk.stats.PassiveFireMastery, + sdk.stats.PassiveFirePierce + ], wantedMerc: MercData[sdk.skills.HolyFreeze], skills: [ [sdk.skills.Warmth, 1], @@ -40,7 +45,12 @@ classid: sdk.items.SmallCharm, /** @param {ItemUnit} check */ stats: function (check) { - return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MaxHp) === 20); + return ( + !check.unique + && check.classid === this.classid + && check.allRes === 5 + && check.getStat(sdk.stats.MaxHp) === 20 + ); } }, @@ -50,7 +60,12 @@ classid: sdk.items.SmallCharm, /** @param {ItemUnit} check */ stats: function (check) { - return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MagicBonus) === 7); + return ( + !check.unique + && check.classid === this.classid + && check.allRes === 5 + && check.getStat(sdk.stats.MagicBonus) === 7 + ); } }, @@ -60,7 +75,12 @@ classid: sdk.items.SmallCharm, /** @param {ItemUnit} check */ stats: function (check) { - return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.FHR) === 5); + return ( + !check.unique + && check.classid === this.classid + && check.allRes === 5 + && check.getStat(sdk.stats.FHR) === 5 + ); } }, @@ -70,8 +90,12 @@ classid: sdk.items.GrandCharm, /** @param {ItemUnit} check */ stats: function (check) { - return (!check.unique && check.classid === this.classid && check.getStat(sdk.stats.AddSkillTab, sdk.skills.tabs.Fire) === 1 - && check.getStat(sdk.stats.MaxHp) >= 40); + return ( + !check.unique + && check.classid === this.classid + && check.getStat(sdk.stats.AddSkillTab, sdk.skills.tabs.Fire) === 1 + && check.getStat(sdk.stats.MaxHp) >= 40 + ); } }, @@ -81,8 +105,12 @@ classid: sdk.items.GrandCharm, /** @param {ItemUnit} check */ stats: function (check) { - return (!check.unique && check.classid === this.classid && check.getStat(sdk.stats.AddSkillTab, sdk.skills.tabs.Cold) === 1 - && check.getStat(sdk.stats.MaxHp) >= 40); + return ( + !check.unique + && check.classid === this.classid + && check.getStat(sdk.stats.AddSkillTab, sdk.skills.tabs.Cold) === 1 + && check.getStat(sdk.stats.MaxHp) >= 40 + ); } }, }, @@ -90,7 +118,12 @@ AutoBuildTemplate: { 1: { Update: function () { - Config.AttackSkill = [-1, sdk.skills.Meteor, sdk.skills.FireBall, sdk.skills.Meteor, sdk.skills.FireBall, sdk.skills.FrozenOrb, sdk.skills.GlacialSpike]; + Config.AttackSkill = [ + -1, + sdk.skills.Meteor, sdk.skills.FireBall, + sdk.skills.Meteor, sdk.skills.FireBall, + sdk.skills.FrozenOrb, sdk.skills.GlacialSpike + ]; Config.LowManaSkill = [-1, -1]; Config.SkipImmune = ["fire and cold"]; Config.HPBuffer = me.expansion ? 1 : 5; @@ -114,7 +147,11 @@ }, active: function () { - return this.respec() && me.getSkill(sdk.skills.Meteor, sdk.skills.subindex.HardPoints) === 20 && me.getSkill(sdk.skills.FrozenOrb, sdk.skills.subindex.HardPoints) === 20; + return ( + this.respec() + && me.getSkill(sdk.skills.Meteor, sdk.skills.subindex.HardPoints) === 20 + && me.getSkill(sdk.skills.FrozenOrb, sdk.skills.subindex.HardPoints) === 20 + ); }, }; diff --git a/libs/SoloPlay/Config/Amazon.js b/libs/SoloPlay/Config/Amazon.js index 78d1eb1c..f0409585 100644 --- a/libs/SoloPlay/Config/Amazon.js +++ b/libs/SoloPlay/Config/Amazon.js @@ -186,7 +186,7 @@ } if (SetUp.finalBuild === "Wfzon") { - if (!Check.haveItem(sdk.items.HydraBow, "unique", "Windforce")) { + if (!me.checkItem({ name: sdk.locale.items.Windforce, classid: sdk.items.HydraBow }).have) { NTIP.addLine("[name] == hydrabow && [quality] == unique # [manaleech] >= 6 # [maxquantity] == 1"); } diff --git a/libs/SoloPlay/Config/Barbarian.js b/libs/SoloPlay/Config/Barbarian.js index 5eeaced4..ceebf1da 100644 --- a/libs/SoloPlay/Config/Barbarian.js +++ b/libs/SoloPlay/Config/Barbarian.js @@ -289,8 +289,7 @@ NTIP.addLine("[name] == falcata && [quality] == unique && [flag] != ethereal # [enhanceddamage] >= 140 && [ias] >= 20 # [maxquantity] == 1"); } - if (!Check.haveItem("dimensionalblade", "unique", "Ginther's Rift")) { - // if (!me.checkItem({ name: sdk.locale.items.GinthersRift, classid: sdk.items.DimensionalBlade }).have) { + if (!me.checkItem({ name: sdk.locale.items.GinthersRift, classid: sdk.items.DimensionalBlade }).have) { NTIP.addLine("[name] == PulRune # # [maxquantity] == 1"); NTIP.addLine("[name] == perfectemerald # # [maxquantity] == 1"); From 5013df9d1594dc5324d298bd9ce490082c1f3acf Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Sun, 28 Jan 2024 01:45:24 -0500 Subject: [PATCH 244/263] [BugFix] bodyLocations for barb weapons - Fixed the check whether the item can be dual wielded or not. It's now items that aren't twoHanded can be equipped to both slots or if they are two-handed but not strictly two-handed, i.e. Swords but not mauls --- libs/SoloPlay/Functions/ItemOverrides.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libs/SoloPlay/Functions/ItemOverrides.js b/libs/SoloPlay/Functions/ItemOverrides.js index d6a4b296..4ff3b670 100644 --- a/libs/SoloPlay/Functions/ItemOverrides.js +++ b/libs/SoloPlay/Functions/ItemOverrides.js @@ -66,7 +66,7 @@ Item.getBodyLoc = function (item) { if (Item.shieldTypes.includes(item.itemType)) return [sdk.body.LeftArm]; if (Item.helmTypes.includes(item.itemType)) return [sdk.body.Head]; if (Item.weaponTypes.includes(item.itemType)) { - return me.barbarian && item.twoHanded && !item.strictlyTwoHanded + return me.barbarian && (!item.twoHanded || (item.twoHanded && !item.strictlyTwoHanded)) ? [sdk.body.RightArm, sdk.body.LeftArm] : [sdk.body.RightArm]; } @@ -494,7 +494,7 @@ Item.getSecondaryBodyLoc = function (item) { : [sdk.body.RightArmSecondary]; } if (Item.weaponTypes.includes(item.itemType)) { - return me.barbarian && item.twoHanded && !item.strictlyTwoHanded + return me.barbarian && (!item.twoHanded || (item.twoHanded && !item.strictlyTwoHanded)) ? [sdk.body.RightArmSecondary, sdk.body.LeftArmSecondary] : [sdk.body.RightArmSecondary]; } From c505d1e0ec380f63d10b3983a5c9efc29f126c1b Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Sun, 28 Jan 2024 01:51:32 -0500 Subject: [PATCH 245/263] Simpler itemtype check for bo-sticks - As long as it's a valid type, check it --- .../barbarian/barbarian.FrenzyBuild.js | 43 +++++++++++++---- .../barbarian/barbarian.ImmortalwhirlBuild.js | 48 +++++++++++++------ .../barbarian/barbarian.SingerBuild.js | 2 +- .../barbarian/barbarian.ThrowBuild.js | 2 +- .../barbarian/barbarian.UberconcBuild.js | 2 +- .../barbarian/barbarian.WhirlwindBuild.js | 2 +- 6 files changed, 71 insertions(+), 28 deletions(-) diff --git a/libs/SoloPlay/BuildFiles/barbarian/barbarian.FrenzyBuild.js b/libs/SoloPlay/BuildFiles/barbarian/barbarian.FrenzyBuild.js index c5fa64bf..55992edf 100644 --- a/libs/SoloPlay/BuildFiles/barbarian/barbarian.FrenzyBuild.js +++ b/libs/SoloPlay/BuildFiles/barbarian/barbarian.FrenzyBuild.js @@ -39,7 +39,12 @@ classid: sdk.items.SmallCharm, /** @param {ItemUnit} check */ stats: function (check) { - return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MaxHp) === 20); + return ( + !check.unique + && check.classid === this.classid + && check.allRes === 5 + && check.getStat(sdk.stats.MaxHp) === 20 + ); } }, @@ -49,7 +54,12 @@ classid: sdk.items.SmallCharm, /** @param {ItemUnit} check */ stats: function (check) { - return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MagicBonus) === 7); + return ( + !check.unique + && check.classid === this.classid + && check.allRes === 5 + && check.getStat(sdk.stats.MagicBonus) === 7 + ); } }, @@ -59,7 +69,12 @@ classid: sdk.items.SmallCharm, /** @param {ItemUnit} check */ stats: function (check) { - return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.FHR) === 5); + return ( + !check.unique + && check.classid === this.classid + && check.allRes === 5 + && check.getStat(sdk.stats.FHR) === 5 + ); } }, @@ -69,8 +84,12 @@ classid: sdk.items.GrandCharm, /** @param {ItemUnit} check */ stats: function (check) { - return (!check.unique && check.classid === this.classid && check.getStat(sdk.stats.AddSkillTab, sdk.skills.tabs.BarbCombat) === 1 - && check.getStat(sdk.stats.MaxHp) >= 40); + return ( + !check.unique + && check.classid === this.classid + && check.getStat(sdk.stats.AddSkillTab, sdk.skills.tabs.BarbCombat) === 1 + && check.getStat(sdk.stats.MaxHp) >= 40 + ); } }, @@ -80,8 +99,12 @@ classid: sdk.items.GrandCharm, /** @param {ItemUnit} check */ stats: function (check) { - return (!check.unique && check.classid === this.classid && check.getStat(sdk.stats.AddSkillTab, sdk.skills.tabs.Masteries) === 1 - && check.getStat(sdk.stats.MaxHp) >= 40); + return ( + !check.unique + && check.classid === this.classid + && check.getStat(sdk.stats.AddSkillTab, sdk.skills.tabs.Masteries) === 1 + && check.getStat(sdk.stats.MaxHp) >= 40 + ); } }, }, @@ -90,7 +113,9 @@ 1: { Update: function () { Config.AttackSkill = [sdk.skills.WarCry, sdk.skills.Frenzy, -1, sdk.skills.Frenzy, -1]; - Config.LowManaSkill = me.getSkill(sdk.skills.DoubleSwing, sdk.skills.subindex.SoftPoints) >= 9 ? [sdk.skills.DoubleSwing, 0] : [0, -1]; + Config.LowManaSkill = me.getSkill(sdk.skills.DoubleSwing, sdk.skills.subindex.SoftPoints) >= 9 + ? [sdk.skills.DoubleSwing, 0] + : [0, -1]; Config.BeltColumn = ["hp", "hp", "mp", "rv"]; Config.MPBuffer = 2; Config.HPBuffer = 2; @@ -134,7 +159,7 @@ "[type] == ring && [quality] == unique # [dexterity] >= 15 && [tohit] >= 150 # [tier] == 100000", "[type] == ring && [quality] == unique # [maxstamina] == 50 && [lifeleech] >= 3 # [tier] == 100000", // Switch - BO Sticks - "([type] == club || [type] == sword || [type] == knife || [type] == throwingknife || [type] == mace) && ([quality] == magic || [flag] == runeword) && [2handed] == 0 # [itemallskills]+[warcriesskilltab]+[barbarianskills] >= 1 # [secondarytier] == 100000 + secondaryscore(item)", + "[type] >= 1 && ([quality] == magic || [flag] == runeword) && [2handed] == 0 # [itemallskills]+[warcriesskilltab]+[barbarianskills] >= 1 # [secondarytier] == 100000 + secondaryscore(item)", // Merc Armor - Fortitude "[type] == armor && [flag] == runeword # [enhanceddefense] >= 200 && [enhanceddamage] >= 300 # [merctier] == 100000", // Merc Final Helmet - Eth Andy's diff --git a/libs/SoloPlay/BuildFiles/barbarian/barbarian.ImmortalwhirlBuild.js b/libs/SoloPlay/BuildFiles/barbarian/barbarian.ImmortalwhirlBuild.js index b4061a06..8309b4b8 100644 --- a/libs/SoloPlay/BuildFiles/barbarian/barbarian.ImmortalwhirlBuild.js +++ b/libs/SoloPlay/BuildFiles/barbarian/barbarian.ImmortalwhirlBuild.js @@ -36,7 +36,12 @@ classid: sdk.items.SmallCharm, /** @param {ItemUnit} check */ stats: function (check) { - return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MaxHp) === 20); + return ( + !check.unique + && check.classid === this.classid + && check.allRes === 5 + && check.getStat(sdk.stats.MaxHp) === 20 + ); } }, @@ -46,7 +51,12 @@ classid: sdk.items.SmallCharm, /** @param {ItemUnit} check */ stats: function (check) { - return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MagicBonus) === 7); + return ( + !check.unique + && check.classid === this.classid + && check.allRes === 5 + && check.getStat(sdk.stats.MagicBonus) === 7 + ); } }, @@ -56,7 +66,12 @@ classid: sdk.items.SmallCharm, /** @param {ItemUnit} check */ stats: function (check) { - return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.FHR) === 5); + return ( + !check.unique + && check.classid === this.classid + && check.allRes === 5 + && check.getStat(sdk.stats.FHR) === 5 + ); } }, @@ -66,8 +81,12 @@ classid: sdk.items.GrandCharm, /** @param {ItemUnit} check */ stats: function (check) { - return (!check.unique && check.classid === this.classid && check.getStat(sdk.stats.AddSkillTab, sdk.skills.tabs.Masteries) === 1 - && check.getStat(sdk.stats.MaxHp) >= 40); + return ( + !check.unique + && check.classid === this.classid + && check.getStat(sdk.stats.AddSkillTab, sdk.skills.tabs.Masteries) === 1 + && check.getStat(sdk.stats.MaxHp) >= 40 + ); } }, }, @@ -87,16 +106,15 @@ respec: function () { if (me.classic) { return false; - } else { - return me.haveAll([ - { name: sdk.locale.items.ImmortalKingsMaul, quality: sdk.items.quality.Set }, - { name: sdk.locale.items.ImmortalKingsBoots, quality: sdk.items.quality.Set }, - { name: sdk.locale.items.ImmortalKingsGloves, quality: sdk.items.quality.Set }, - { name: sdk.locale.items.ImmortalKingsBelt, quality: sdk.items.quality.Set }, - { name: sdk.locale.items.ImmortalKingsArmor, quality: sdk.items.quality.Set }, - { name: sdk.locale.items.ImmortalKingsHelmet, quality: sdk.items.quality.Set }, - ]); } + return me.haveAll([ + { name: sdk.locale.items.ImmortalKingsMaul, quality: sdk.items.quality.Set }, + { name: sdk.locale.items.ImmortalKingsBoots, quality: sdk.items.quality.Set }, + { name: sdk.locale.items.ImmortalKingsGloves, quality: sdk.items.quality.Set }, + { name: sdk.locale.items.ImmortalKingsBelt, quality: sdk.items.quality.Set }, + { name: sdk.locale.items.ImmortalKingsArmor, quality: sdk.items.quality.Set }, + { name: sdk.locale.items.ImmortalKingsHelmet, quality: sdk.items.quality.Set }, + ]); }, active: function () { @@ -126,7 +144,7 @@ "[type] == ring && [quality] == unique # [dexterity] >= 15 && [tohit] >= 150 # [tier] == 100000", "[type] == ring && [quality] == unique # [maxstamina] == 50 && [lifeleech] >= 3 # [tier] == 100000", // Switch - BO sticks - "([type] == club || [type] == sword || [type] == knife || [type] == throwingknife || [type] == mace) && ([quality] == magic || [flag] == runeword) && [2handed] == 0 # [itemallskills]+[warcriesskilltab]+[barbarianskills] >= 1 # [secondarytier] == 100000 + secondaryscore(item)", + "[type] >= 1 && ([quality] == magic || [flag] == runeword) && [2handed] == 0 # [itemallskills]+[warcriesskilltab]+[barbarianskills] >= 1 # [secondarytier] == 100000 + secondaryscore(item)", // Merc Armor - Fortitude "[type] == armor && [flag] == runeword # [enhanceddefense] >= 200 && [enhanceddamage] >= 300 # [merctier] == 100000", // Merc Final Helmet - Eth Andy's diff --git a/libs/SoloPlay/BuildFiles/barbarian/barbarian.SingerBuild.js b/libs/SoloPlay/BuildFiles/barbarian/barbarian.SingerBuild.js index a9a754c0..69e2f908 100644 --- a/libs/SoloPlay/BuildFiles/barbarian/barbarian.SingerBuild.js +++ b/libs/SoloPlay/BuildFiles/barbarian/barbarian.SingerBuild.js @@ -115,7 +115,7 @@ // Rings - SoJ "[type] == ring && [quality] == unique # [itemmaxmanapercent] == 25 # [tier] == 100000", // Switch - BO sticks - "([type] == club || [type] == sword || [type] == knife || [type] == throwingknife || [type] == mace) && ([quality] == magic || [flag] == runeword) && [2handed] == 0 # [itemallskills]+[warcriesskilltab]+[barbarianskills] >= 1 # [secondarytier] == 100000 + secondaryscore(item)", + "[type] >= 1 && ([quality] == magic || [flag] == runeword) && [2handed] == 0 # [itemallskills]+[warcriesskilltab]+[barbarianskills] >= 1 # [secondarytier] == 100000 + secondaryscore(item)", // Merc Armor - Fortitude "[type] == armor && [flag] == runeword # [enhanceddefense] >= 200 && [enhanceddamage] >= 300 # [merctier] == 100000", // Merc Final Helmet - Eth Andy's diff --git a/libs/SoloPlay/BuildFiles/barbarian/barbarian.ThrowBuild.js b/libs/SoloPlay/BuildFiles/barbarian/barbarian.ThrowBuild.js index 6af5af53..38c0ea82 100644 --- a/libs/SoloPlay/BuildFiles/barbarian/barbarian.ThrowBuild.js +++ b/libs/SoloPlay/BuildFiles/barbarian/barbarian.ThrowBuild.js @@ -151,7 +151,7 @@ "[type] == ring && [quality] == unique # [dexterity] >= 15 && [tohit] >= 150 # [tier] == 100000", "[type] == ring && [quality] == unique # [maxstamina] == 50 && [lifeleech] >= 3 # [tier] == 100000", // Switch - BO sticks - "([type] == club || [type] == sword || [type] == knife || [type] == throwingknife || [type] == mace) && ([quality] == magic || [flag] == runeword) && [2handed] == 0 # [itemallskills]+[warcriesskilltab]+[barbarianskills] >= 1 # [secondarytier] == 100000 + secondaryscore(item)", + "[type] >= 1 && ([quality] == magic || [flag] == runeword) && [2handed] == 0 # [itemallskills]+[warcriesskilltab]+[barbarianskills] >= 1 # [secondarytier] == 100000 + secondaryscore(item)", // Merc Armor - Fortitude "[type] == armor && [flag] == runeword # [enhanceddefense] >= 200 && [enhanceddamage] >= 300 # [merctier] == 100000", // Merc Final Helmet - Eth Andy's diff --git a/libs/SoloPlay/BuildFiles/barbarian/barbarian.UberconcBuild.js b/libs/SoloPlay/BuildFiles/barbarian/barbarian.UberconcBuild.js index 30fa4690..37e5678e 100644 --- a/libs/SoloPlay/BuildFiles/barbarian/barbarian.UberconcBuild.js +++ b/libs/SoloPlay/BuildFiles/barbarian/barbarian.UberconcBuild.js @@ -133,7 +133,7 @@ "[type] == ring && [quality] == unique # [dexterity] >= 15 && [tohit] >= 150 # [tier] == 100000", "[name] == ring && [quality] == unique # [maxstamina] == 50 && [lifeleech] >= 3 # [tier] == 100000", // Switch - BO sticks - "([type] == club || [type] == sword || [type] == knife || [type] == throwingknife || [type] == mace) && ([quality] == magic || [flag] == runeword) && [2handed] == 0 # [itemallskills]+[warcriesskilltab]+[barbarianskills] >= 1 # [secondarytier] == 100000 + secondaryscore(item)", + "[type] >= 1 && ([quality] == magic || [flag] == runeword) && [2handed] == 0 # [itemallskills]+[warcriesskilltab]+[barbarianskills] >= 1 # [secondarytier] == 100000 + secondaryscore(item)", // Merc Armor - Fortitude "[type] == armor && [flag] == runeword # [enhanceddefense] >= 200 && [enhanceddamage] >= 300 # [merctier] == 100000", // Merc Final Helmet - Eth Andy's diff --git a/libs/SoloPlay/BuildFiles/barbarian/barbarian.WhirlwindBuild.js b/libs/SoloPlay/BuildFiles/barbarian/barbarian.WhirlwindBuild.js index b4d7639e..cd94e04b 100644 --- a/libs/SoloPlay/BuildFiles/barbarian/barbarian.WhirlwindBuild.js +++ b/libs/SoloPlay/BuildFiles/barbarian/barbarian.WhirlwindBuild.js @@ -124,7 +124,7 @@ "[type] == ring && [quality] == unique # [dexterity] >= 15 && [tohit] >= 150 # [tier] == 100000", "[type] == ring && [quality] == unique # [maxstamina] == 50 && [lifeleech] >= 3 # [tier] == 100000", // Switch - BO sticks - "([type] == club || [type] == sword || [type] == knife || [type] == throwingknife || [type] == mace) && ([quality] == magic || [flag] == runeword) && [2handed] == 0 # [itemallskills]+[warcriesskilltab]+[barbarianskills] >= 1 # [secondarytier] == 100000 + secondaryscore(item)", + "[type] >= 1 && ([quality] == magic || [flag] == runeword) && [2handed] == 0 # [itemallskills]+[warcriesskilltab]+[barbarianskills] >= 1 # [secondarytier] == 100000 + secondaryscore(item)", // Merc Armor - Fortitude "[type] == armor && [flag] == runeword # [enhanceddefense] >= 200 && [enhanceddamage] >= 300 # [merctier] == 100000", // Merc Final Helmet - Eth Andy's From 1a2731bdc8ea7122d800295d91d6a71b72d2015d Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Wed, 31 Jan 2024 00:11:33 -0500 Subject: [PATCH 246/263] Update DynamicTiers.js - Small fix for barbarian, warcries should be the tab we want on secondary weapons --- libs/SoloPlay/Functions/DynamicTiers.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/libs/SoloPlay/Functions/DynamicTiers.js b/libs/SoloPlay/Functions/DynamicTiers.js index a26f28a6..d1171bfb 100644 --- a/libs/SoloPlay/Functions/DynamicTiers.js +++ b/libs/SoloPlay/Functions/DynamicTiers.js @@ -11,7 +11,7 @@ * no point checking for stats that cannot ever exist. Also handle some of the misc stats that appear as they can be helpful. */ -(function () { +(function (global) { /** * @param {ItemUnit} item */ @@ -624,7 +624,7 @@ return [ [sdk.stats.AllSkills, -1], [sdk.stats.AddClassSkills, me.classid], - [sdk.stats.AddSkillTab, Check.finalBuild().tabSkills], + [sdk.stats.AddSkillTab, (me.barbarian ? sdk.skills.tabs.Warcries : Check.finalBuild().tabSkills)], ].reduce(function (acc, [stat, subId]) { return acc + item.getStatEx(stat, subId) * _tierWeights.skill.get(stat); }, tier); @@ -688,4 +688,4 @@ global.tierscore = tierscore; global.secondaryscore = secondaryscore; global.charmscore = charmscore; -})(); +})([].filter.constructor("return this")()); From 4505c40c5426bdcb6ab808d39117f60450709f6e Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Sun, 4 Feb 2024 17:03:46 -0500 Subject: [PATCH 247/263] Update README.md - Added buymeacoffee link --- README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.md b/README.md index c48155b3..47092ab3 100644 --- a/README.md +++ b/README.md @@ -199,6 +199,10 @@ Pull requests are welcome. For major changes, please open an issue first to disc ![Kolbot-SoloPlay Paladin](https://user-images.githubusercontent.com/60308670/165398785-8ef1afd7-d232-4bc4-a23e-a1f1321ce0ed.png) + + Buy Me A Coffee + + [![Donate](https://img.shields.io/badge/PayPal-Donate-blue.svg?style=for-the-badge&logo=paypal)](https://www.paypal.com/donate/?business=Y9S4AWX9QUZXW&no_recurring=0¤cy_code=USD) ## Statistics (will become filled out as data becomes available) From 4ea8a53c785ec2690114dbdf189a4b5a7e989be5 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Mon, 1 Apr 2024 00:27:16 -0400 Subject: [PATCH 248/263] [BugFix] "cannot redefined non-configurable property 'classic'" - Remove inclusion of PrototypesOverrides, Me.js shouldn't be included in an OOG file this was causing us to include prototypes which then includes Me.js --- libs/SoloPlay/Tools/Tracker.js | 36 +++++++++++++++++++++++++++++++--- 1 file changed, 33 insertions(+), 3 deletions(-) diff --git a/libs/SoloPlay/Tools/Tracker.js b/libs/SoloPlay/Tools/Tracker.js index f117dd84..27e7b50a 100644 --- a/libs/SoloPlay/Tools/Tracker.js +++ b/libs/SoloPlay/Tools/Tracker.js @@ -7,16 +7,46 @@ includeIfNotIncluded("core/experience.js"); includeIfNotIncluded("SoloPlay/Tools/Developer.js"); -includeIfNotIncluded("SoloPlay/Functions/PrototypeOverrides.js"); const Tracker = { GTPath: "libs/SoloPlay/Data/" + me.profile + "/" + me.profile + "-GameTime.json", LPPath: "libs/SoloPlay/Data/" + me.profile + "/" + me.profile + "-LevelingPerformance.csv", SPPath: "libs/SoloPlay/Data/" + me.profile + "/" + me.profile + "-ScriptPerformance.csv", // Leveling Performance - LPHeader: "Total Time,InGame,Split Time,Area,Charlevel,Gained EXP,EXP/Minute,Difficulty,Gold,Fire Resist,Cold Resist,Light Resist,Poison Resist,Current Build" + "\n", + LPHeader: [ + "Total Time", + "InGame", + "Split Time", + "Area", + "Charlevel", + "Gained EXP", + "EXP/Minute", + "Difficulty", + "Gold", + "Fire Resist", + "Cold Resist", + "Light Resist", + "Poison Resist", + "Current Build" + ].join(",") + "\n", // Script Performance - SPHeader: "Total Time,InGame,Sequence,Script,Charlevel,Gained EXP,EXP/Minute,EXP Gain %,Difficulty,Gold,Fire Resist,Cold Resist,Light Resist,Poison Resist,Current Build" + "\n", + SPHeader: [ + "Total Time", + "InGame", + "Sequence", + "Script", + "Charlevel", + "Gained EXP", + "EXP/Minute", + "EXP Gain %", + "Difficulty", + "Gold", + "Fire Resist", + "Cold Resist", + "Light Resist", + "Poison Resist", + "Current Build" + ].join(",") + "\n", tick: 0, /** * @typedef {Object} GameTracker From 65f6a1897ace251767bfbd7ce20d857e6854784f Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Wed, 10 Apr 2024 01:01:16 -0400 Subject: [PATCH 249/263] [BugFix] temp fix midway final items being over valued - Use halfway value of 50k instead of 100k in final item check --- libs/SoloPlay/Functions/DynamicTiers.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libs/SoloPlay/Functions/DynamicTiers.js b/libs/SoloPlay/Functions/DynamicTiers.js index d1171bfb..1f6d3e98 100644 --- a/libs/SoloPlay/Functions/DynamicTiers.js +++ b/libs/SoloPlay/Functions/DynamicTiers.js @@ -599,7 +599,7 @@ tier += ctcScore(); tier += chargeditemscore(item, -1, buildInfo); - if (tier > 1 && tier < NTIP.MAX_TIER && NTIP.CheckItem(item, NTIP.FinalGear) === Pickit.Result.WANTED) { + if (tier > 1 && tier < 50000 /* NTIP.MAX_TIER */ && NTIP.CheckItem(item, NTIP.FinalGear) === Pickit.Result.WANTED) { // console.debug(item.prettyPrint + "~~~" + tier); tier += NTIP.MAX_TIER; } From 910b7367147db5a2367970e551add2668128a04d Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Wed, 17 Apr 2024 12:53:16 -0400 Subject: [PATCH 250/263] [BugFix] Don't touch cube if it's in locked invo spot --- libs/SoloPlay/Functions/StorageOverrides.js | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/libs/SoloPlay/Functions/StorageOverrides.js b/libs/SoloPlay/Functions/StorageOverrides.js index 9dc58789..9747cd81 100644 --- a/libs/SoloPlay/Functions/StorageOverrides.js +++ b/libs/SoloPlay/Functions/StorageOverrides.js @@ -132,6 +132,11 @@ includeIfNotIncluded("SoloPlay/Tools/Developer.js"); return true; } + // If cube is in locked inventory spot, don't touch it + if (cube && cube.isInInventory && Storage.Inventory.IsLocked(cube, Config.Inventory)) { + return true; + } + let makeCubeSpot = this.MakeSpot(cube, { x: 0, y: 0 }, true); // NOTE: passing these in buffer order [h/x][w/y] if (makeCubeSpot) { @@ -173,7 +178,11 @@ includeIfNotIncluded("SoloPlay/Tools/Developer.js"); let item = this.itemList[this.buffer[x][y] - 1]; - if (item.classid === sdk.quest.item.Cube && item.isInStash && item.x === 0 && item.y === 0) { + if (item.classid === sdk.quest.item.Cube + && ( + (item.isInInventory && Storage.Inventory.IsLocked(item, Config.Inventory)) + || (item.isInStash && item.x === 0 && item.y === 0) + )) { continue; // dont touch the cube } From d0908bb0efb7c8193b4ad47b6970b542cdc1ec7f Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Wed, 17 Apr 2024 13:45:46 -0400 Subject: [PATCH 251/263] [Hotfix] Don't touch the cube during init, leave that to storage for handling --- libs/SoloPlay/Utils/Init.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/libs/SoloPlay/Utils/Init.js b/libs/SoloPlay/Utils/Init.js index a5059e64..c6a3f2ce 100644 --- a/libs/SoloPlay/Utils/Init.js +++ b/libs/SoloPlay/Utils/Init.js @@ -64,7 +64,11 @@ }); me.getItemsEx() - .filter(item => item.isInInventory && sdk.quest.items.includes(item.classid)) + .filter(item => ( + item.isInInventory + && sdk.quest.items.includes(item.classid) + && item.classid !== sdk.quest.item.Cube + )) .forEach(item => { Quest.stashItem(item); }); From dd72d50c1404d0e29d7185ab6126677c5bba1ef1 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Fri, 3 May 2024 00:21:49 -0400 Subject: [PATCH 252/263] Add datestamp to `Item.logItem` - Nice to know what day an item was kept --- libs/SoloPlay/Functions/ItemOverrides.js | 31 +++++++++++++++++------- 1 file changed, 22 insertions(+), 9 deletions(-) diff --git a/libs/SoloPlay/Functions/ItemOverrides.js b/libs/SoloPlay/Functions/ItemOverrides.js index 4ff3b670..794ebd83 100644 --- a/libs/SoloPlay/Functions/ItemOverrides.js +++ b/libs/SoloPlay/Functions/ItemOverrides.js @@ -887,13 +887,26 @@ Item.removeItemsMerc = function () { */ Item.logItem = function (action, unit, keptLine, force) { if (!this.useItemLog || unit === undefined || !unit || !unit.fname) return false; - if (!Config.LogKeys && ["pk1", "pk2", "pk3"].includes(unit.code)) return false; - if (!Config.LogOrgans && ["dhn", "bey", "mbr"].includes(unit.code)) return false; - if (!Config.LogLowRunes && ["r01", "r02", "r03", "r04", "r05", "r06", "r07", "r08", "r09", "r10", "r11", "r12", "r13", "r14"].includes(unit.code)) return false; - if (!Config.LogMiddleRunes && ["r15", "r16", "r17", "r18", "r19", "r20", "r21", "r22", "r23"].includes(unit.code)) return false; - if (!Config.LogHighRunes && ["r24", "r25", "r26", "r27", "r28", "r29", "r30", "r31", "r32", "r33"].includes(unit.code)) return false; - if (!Config.LogLowGems && ["gcv", "gcy", "gcb", "gcg", "gcr", "gcw", "skc", "gfv", "gfy", "gfb", "gfg", "gfr", "gfw", "skf", "gsv", "gsy", "gsb", "gsg", "gsr", "gsw", "sku"].includes(unit.code)) return false; - if (!Config.LogHighGems && ["gzv", "gly", "glb", "glg", "glr", "glw", "skl", "gpv", "gpy", "gpb", "gpg", "gpr", "gpw", "skz"].includes(unit.code)) return false; + + const keys = ["pk1", "pk2", "pk3"]; + const organs = ["dhn", "bey", "mbr"]; + const lowRunes = ["r01", "r02", "r03", "r04", "r05", "r06", "r07", "r08", "r09", "r10", "r11", "r12", "r13", "r14"]; + const middleRunes = ["r15", "r16", "r17", "r18", "r19", "r20", "r21", "r22", "r23"]; + const highRunes = ["r24", "r25", "r26", "r27", "r28", "r29", "r30", "r31", "r32", "r33"]; + const lowGems = [ + "gcv", "gcy", "gcb", "gcg", "gcr", + "gcw", "skc", "gfv", "gfy", "gfb", + "gfg", "gfr", "gfw", "skf", "gsv", + "gsy", "gsb", "gsg", "gsr", "gsw", "sku" + ]; + const highGems = ["gzv", "gly", "glb", "glg", "glr", "glw", "skl", "gpv", "gpy", "gpb", "gpg", "gpr", "gpw", "skz"]; + if (!Config.LogKeys && keys.includes(unit.code)) return false; + if (!Config.LogOrgans && organs.includes(unit.code)) return false; + if (!Config.LogLowRunes && lowRunes.includes(unit.code)) return false; + if (!Config.LogMiddleRunes && middleRunes.includes(unit.code)) return false; + if (!Config.LogHighRunes && highRunes.includes(unit.code)) return false; + if (!Config.LogLowGems && lowGems.includes(unit.code)) return false; + if (!Config.LogHighGems && highGems.includes(unit.code)) return false; for (let skipCode of Config.SkipLogging) { if (skipCode === unit.classid || skipCode === unit.code) return false; @@ -960,8 +973,8 @@ Item.logItem = function (action, unit, keptLine, force) { } desc += "$" + (unit.getFlag(sdk.items.flags.Ethereal) ? ":eth" : ""); - let itemObj = { - title: action + " " + name, + const itemObj = { + title: (new Date().dateStamp() + " ") + action + " " + name, description: desc, image: code, textColor: unit.quality, From 1591630536bf60f8cff30d5b052308429ea14da3 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Sun, 12 May 2024 23:48:02 -0400 Subject: [PATCH 253/263] Fix socketing moser's when we already have final shield --- libs/SoloPlay/Utils/General.js | 10 +++++++++- libs/SoloPlay/globals.d.ts | 1 + 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/libs/SoloPlay/Utils/General.js b/libs/SoloPlay/Utils/General.js index 3c490459..0e0b055f 100644 --- a/libs/SoloPlay/Utils/General.js +++ b/libs/SoloPlay/Utils/General.js @@ -55,6 +55,14 @@ ], }; + /** + * @param {number} classid + * @param {number[]} socketWith + * @param {number[]} temp + * @param {boolean} useSocketQuest + * @param {(item?: ItemUnit) => boolean | void} condition + * @returns {{ classid: number, socketWith: number[], temp: number[], useSocketQuest: boolean, condition: (item?: ItemUnit) => boolean | void }} + */ const addSocketableObj = (classid, socketWith = [], temp = [], useSocketQuest = false, condition = () => {}) => ({ classid: classid, socketWith: socketWith, @@ -83,7 +91,7 @@ // Moser's basicSocketables.caster .push(addSocketableObj(sdk.items.RoundShield, [sdk.items.runes.Um], [sdk.items.gems.Perfect.Diamond], - false, (item) => item.unique && !item.ethereal + false, (item) => item.unique && !item.ethereal && me.equipped.get(sdk.body.LeftArm).tier < NTIP.MAX_TIER )); // Spirit Forge basicSocketables.caster diff --git a/libs/SoloPlay/globals.d.ts b/libs/SoloPlay/globals.d.ts index 41fcbe97..5729895a 100644 --- a/libs/SoloPlay/globals.d.ts +++ b/libs/SoloPlay/globals.d.ts @@ -508,6 +508,7 @@ declare global { function nextDifficulty(announce: boolean): string | false; function runes(): boolean; function haveItem(type: string | number, flag?: string | number, iName?: string): boolean; + function itemSocketables(type: string | number, quality: string | number, iName?: string): boolean; function currentBuild(): Build; function finalBuild(): Build; } From 53a8ff9dbf05a7248dacac77b0220b0932900aac Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Thu, 16 May 2024 19:28:54 -0400 Subject: [PATCH 254/263] Update ItemOverrides.js --- libs/SoloPlay/Functions/ItemOverrides.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/libs/SoloPlay/Functions/ItemOverrides.js b/libs/SoloPlay/Functions/ItemOverrides.js index 794ebd83..d3ad40b0 100644 --- a/libs/SoloPlay/Functions/ItemOverrides.js +++ b/libs/SoloPlay/Functions/ItemOverrides.js @@ -972,9 +972,10 @@ Item.logItem = function (action, unit, keptLine, force) { desc += ("\n\\xffc0Line: " + keptLine); } desc += "$" + (unit.getFlag(sdk.items.flags.Ethereal) ? ":eth" : ""); + const formattedDate = new Date().dateStamp().replace(/\//g, "-"); const itemObj = { - title: (new Date().dateStamp() + " ") + action + " " + name, + title: formattedDate + " " + action.replace(/ÿc[0-9!"+<:;.*]|\/|\\/g, "").trim() + " " + name, description: desc, image: code, textColor: unit.quality, From e445cfadcde3dca0c61fa9f9ca268f10a4cbaea2 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Mon, 27 May 2024 12:11:53 -0400 Subject: [PATCH 255/263] Cleanup + add support for new syntax in NTIP --- libs/SoloPlay/Functions/NTIPOverrides.js | 111 ++++------------------- 1 file changed, 18 insertions(+), 93 deletions(-) diff --git a/libs/SoloPlay/Functions/NTIPOverrides.js b/libs/SoloPlay/Functions/NTIPOverrides.js index 507ad4ea..76b708bb 100644 --- a/libs/SoloPlay/Functions/NTIPOverrides.js +++ b/libs/SoloPlay/Functions/NTIPOverrides.js @@ -594,49 +594,12 @@ NTIP.ParseLineInt = function (input, info) { return null; } - const _props = new Map([ - ["wsm", 'getBaseStat("items", item.classid, "speed")'], - ["weaponspeed", 'getBaseStat("items", item.classid, "speed")'], - ["minimumsockets", 'getBaseStat("items", item.classid, "gemsockets")'], - ["strreq", "item.strreq"], - ["dexreq", "item.dexreq"], - ["2handed", 'getBaseStat("items", item.classid, "2handed")'], - ["color", "item.getColor()"], - ["type", "item.itemType"], - ["name", "item.classid"], - ["classid", "item.classid"], - ["class", "item.itemclass"], - ["quality", "item.quality"], - ["level", "item.ilvl"], - ["europe", '("' + me.realm.toLowerCase() + '"===" europe")'], - ["uswest", '("' + me.realm.toLowerCase() + '"===" uswest")'], - ["useast", '("' + me.realm.toLowerCase() + '"===" useast")'], - ["asia", '("' + me.realm.toLowerCase() + '"===" asia")'], - ["ladder", "me.ladder"], - ["hardcore", "(!!me.playertype)"], - ["classic", "(!me.gametype)"], - ["distance", "(item.onGroundOrDropping && item.distance || Infinity)"], - ["flag", "item.getFlag("], - ["prefix", "item.getPrefix("], - ["suffix", "item.getSuffix("] - ]); - - const _aliases = new Map([ - ["n", "name"], - ["id", "classid"], - ["t", "type"], - ["q", "quality"], - ["lvl", "level"], - ["ilvl", "level"], - ["f", "flag"], - ["hc", "hardcore"], - ["cl", "classic"], - ["clvl", "charlvl"], - ]); - let p_result = input.split("#"); if (p_result[0] && p_result[0].length > 4) { + if (NTIP.parseAliasIn.test(p_result[0])) { + p_result[0] = NTIP.parseAliasIn.convert(p_result[0]); + } p_section = p_result[0].split("["); p_result[0] = p_section[0]; @@ -645,8 +608,8 @@ NTIP.ParseLineInt = function (input, info) { p_end = p_section[i].indexOf("]") + 1; property = p_section[i].substring(0, p_end - 1); - if (_aliases.has(property)) { - property = _aliases.get(property); + if (NTIP._aliases.has(property)) { + property = NTIP._aliases.get(property); } switch (property) { @@ -654,16 +617,16 @@ NTIP.ParseLineInt = function (input, info) { case "prefix": case "suffix": if (p_section[i][p_end] === "!") { - p_result[0] += "!" + _props.get(property); + p_result[0] += "!" + NTIP._props.get(property); } else { - p_result[0] += _props.get(property); + p_result[0] += NTIP._props.get(property); } p_end += 2; break; default: - if (!_props.has(property)) { + if (!NTIP._props.has(property)) { Misc.errorReport("Unknown property: " + property + " File: " + info.file + " Line: " + info.line); return false; @@ -696,58 +659,20 @@ NTIP.ParseLineInt = function (input, info) { if (isNaN(p_keyword)) { try { switch (property) { - case "color": - if (NTIPAliasColor[p_keyword] === undefined) { - throw new Error("Unknown color: " + p_keyword + " File: " + info.file + " Line: " + info.line); - } - - p_result[0] += NTIPAliasColor[p_keyword]; - - break; - case "type": - if (NTIPAliasType[p_keyword] === undefined) { - throw new Error("Unknown type: " + p_keyword + " File: " + info.file + " Line: " + info.line); - } - - p_result[0] += NTIPAliasType[p_keyword]; - - break; - case "name": - if (NTIPAliasClassID[p_keyword] === undefined) { - throw new Error("Unknown name: " + p_keyword + " File: " + info.file + " Line: " + info.line); - } - - p_result[0] += NTIPAliasClassID[p_keyword]; - - break; - case "class": - if (NTIPAliasClass[p_keyword] === undefined) { - throw new Error("Unknown class: " + p_keyword + " File: " + info.file + " Line: " + info.line); - } - - p_result[0] += NTIPAliasClass[p_keyword]; - - break; - case "quality": - if (NTIPAliasQuality[p_keyword] === undefined) { - throw new Error("Unknown quality: " + p_keyword + " File: " + info.file + " Line: " + info.line); - } - - p_result[0] += NTIPAliasQuality[p_keyword]; - - break; - case "flag": - if (NTIPAliasFlag[p_keyword] === undefined) { - throw new Error("Unknown flag: " + p_keyword + " File: " + info.file + " Line: " + info.line); - } - - p_result[0] += NTIPAliasFlag[p_keyword] + ")"; - - break; case "prefix": case "suffix": p_result[0] += "\"" + p_keyword + "\")"; + break; + default: + if (!NTIP._lists.has(property)) { + throw new Error("Unknown property: " + property + " File: " + info.file + " Line: " + info.line); + } else if (NTIP._lists.get(property)[p_keyword] === undefined) { + throw new Error("Unknown " + property + ": " + p_keyword + " File: " + info.file + " Line: " + info.line); + } + p_result[0] += NTIP._lists.get(property)[p_keyword]; + property === "flag" && (p_result[0] += ")"); + break; } } catch (e) { From 12c156c81993644a433736d425c7560cb88820fd Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Tue, 28 May 2024 14:56:10 -0400 Subject: [PATCH 256/263] [Hotfix] Missed updating reference to NTIP --- libs/SoloPlay/Functions/NTIPOverrides.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libs/SoloPlay/Functions/NTIPOverrides.js b/libs/SoloPlay/Functions/NTIPOverrides.js index 76b708bb..d4c38bde 100644 --- a/libs/SoloPlay/Functions/NTIPOverrides.js +++ b/libs/SoloPlay/Functions/NTIPOverrides.js @@ -631,7 +631,7 @@ NTIP.ParseLineInt = function (input, info) { return false; } - p_result[0] += _props.get(property); + p_result[0] += NTIP._props.get(property); } for (p_start = p_end; p_end < p_section[i].length; p_end += 1) { From 34a177be4bd24fe0ec6702bab601f59d0a708056 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Wed, 12 Jun 2024 01:47:40 -0400 Subject: [PATCH 257/263] Fix typo `ArticBlast` -> `ArcticBlast`, add SkipImmune to Druid leveling/Wind build - We can't attack them cold + phys immune so this fixes switch casting on them for no reason --- libs/SoloPlay/BuildFiles/druid/druid.LevelingBuild.js | 3 ++- libs/SoloPlay/BuildFiles/druid/druid.WindBuild.js | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/libs/SoloPlay/BuildFiles/druid/druid.LevelingBuild.js b/libs/SoloPlay/BuildFiles/druid/druid.LevelingBuild.js index 20ab4a9c..dc1189e7 100644 --- a/libs/SoloPlay/BuildFiles/druid/druid.LevelingBuild.js +++ b/libs/SoloPlay/BuildFiles/druid/druid.LevelingBuild.js @@ -46,7 +46,8 @@ AutoBuildTemplate: { 1: { Update: function () { - Config.AttackSkill = [-1, sdk.skills.Tornado, -1, sdk.skills.Tornado, -1, sdk.skills.ArticBlast, -1]; + Config.SkipImmune = ["cold and physical"]; + Config.AttackSkill = [-1, sdk.skills.Tornado, -1, sdk.skills.Tornado, -1, sdk.skills.ArcticBlast, -1]; Config.LowManaSkill = [-1, -1]; Config.SummonAnimal = "Grizzly"; Config.SummonSpirit = "Oak Sage"; diff --git a/libs/SoloPlay/BuildFiles/druid/druid.WindBuild.js b/libs/SoloPlay/BuildFiles/druid/druid.WindBuild.js index 993ac212..fb868544 100644 --- a/libs/SoloPlay/BuildFiles/druid/druid.WindBuild.js +++ b/libs/SoloPlay/BuildFiles/druid/druid.WindBuild.js @@ -75,7 +75,8 @@ AutoBuildTemplate: { 1: { Update: function () { - Config.AttackSkill = [-1, sdk.skills.Tornado, -1, sdk.skills.Tornado, -1, sdk.skills.ArticBlast, -1]; + Config.SkipImmune = ["cold and physical"]; + Config.AttackSkill = [-1, sdk.skills.Tornado, -1, sdk.skills.Tornado, -1, sdk.skills.ArcticBlast, -1]; Config.LowManaSkill = [-1, -1]; Config.SummonAnimal = "Grizzly"; Config.SummonSpirit = "Oak Sage"; From 39650c6c6254ee0c2523526bbb1d45afc2178163 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Mon, 17 Jun 2024 19:08:15 -0400 Subject: [PATCH 258/263] Update README.md --- README.md | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 47092ab3..2f84384c 100644 --- a/README.md +++ b/README.md @@ -101,6 +101,14 @@ In expansion, it transitions to the final build when final gear requirements are **A:** A Bumper is a level 40 character that has not done baal quest in normal and is used to "bump" low level characters to hell difficulty where they can power level following chaos runs. +### **Q: Can I enable my own Pickit files?** + +**A:** Yes, similar to how core kolbot works open the class config file located `libs\SoloPlay\Config\` and look for the section header. + +`/* Pickit configuration. */` + +add your pickit files here or uncomment the kolton nip already present. + ### **Q: Does this work for Diablo 2 Resurected?** **A:** No, Kolbot does not work with d2r and SoloPlay runs using Kolbot. SoloPlay only works on diablo 2. @@ -118,7 +126,7 @@ In expansion, it transitions to the final build when final gear requirements are |:------:|:-------|-------:| | 1.| Download Kolbot here: [github.com/blizzhackers/kolbot](https://github.com/blizzhackers/kolbot). |![blizzhackers github](https://i.imgur.com/RksqKEA.jpg) | | 2.| Click the green button to Download SoloPlay. |![enter image description here](https://i.imgur.com/cNqZDbW.jpg) | -| 3.a| Copy and paste the following: `default.dbj`, `D2BotSoloPlay.dbj`, and the entire `\libs` folder into `\d2bs\kolbot\`.| ![kolbot](https://i.imgur.com/WNxJOhq.png) | +| 3.a| Copy and paste the following: `D2BotSoloPlay.dbj`, and the entire `\libs` folder into `\d2bs\kolbot\`.| ![kolbot](https://i.imgur.com/WNxJOhq.png) | |3.b|A successful installation will show 1 new file in the folder: `D2BotSoloPlay.dbj` and look similar to the following image|![image](https://github.com/blizzhackers/kolbot-SoloPlay/assets/60308670/a85713f4-df22-4aa8-937d-21cba4366b0c) | 4.| Select Add for new a Kolbot Profile. | ![Add-profile.jpg](https://imgur.com/tHs9ZoH.jpg)| | 4.a| Select and Input a profile name. See the **[Possible Profile Name Choices](#possible-profile-names)** below for a list of available options. | ![extract into](https://i.imgur.com/2YcGKVH.png) | From 7748f76eff7fe59e8221d8970935481f243227f9 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Fri, 21 Jun 2024 15:24:33 -0400 Subject: [PATCH 259/263] Update SoloIndex.js - Fix exclusion of bumper from the diffCompleted check for running chaos --- libs/SoloPlay/Tools/SoloIndex.js | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/libs/SoloPlay/Tools/SoloIndex.js b/libs/SoloPlay/Tools/SoloIndex.js index 33901b48..736d7e4a 100644 --- a/libs/SoloPlay/Tools/SoloIndex.js +++ b/libs/SoloPlay/Tools/SoloIndex.js @@ -666,12 +666,13 @@ const SoloIndex = { }, shouldRun: function () { if (!this.preReq() || this.skipIf()) return false; - switch (true) { - case !me.diablo: - case (me.normal && me.classic): - case (me.normal && me.expansion && (me.charlvl < 30 || !me.diffCompleted)): - case (me.nightmare && (Pather.canTeleport() || me.charlvl <= 65)): - case (me.hell): + if (!me.diablo) return true; + if (me.normal) { + if (me.classic || me.charlvl < 30) return true; + return !me.diffCompleted && me.data.finalBuild !== "Bumper"; + } else if (me.nightmare) { + return (Pather.canTeleport() || me.charlvl <= 65); + } else if (me.hell) { return true; } return false; From ff0ccd6bb81b2b40d029392191868e182ecda06c Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Tue, 29 Oct 2024 02:59:19 -0400 Subject: [PATCH 260/263] Update README.md --- README.md | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 2f84384c..49b6c352 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,8 @@ ![extract into](https://i.imgur.com/TcRmoRm.png) -*- "the one bot to rule them all."* +*"the one bot to rule them all."* -
+For a very quick kolbot+soloplay installer check out https://github.com/theBGuy/kolbot-soloplay-installer # Table of contents - [What is SoloPlay?](#the-big-question-what-is-kolbot-soloplay) @@ -13,8 +13,7 @@ - [Install-Guide](#install-guide) - [Extras](#extras) - [Discord](#discord) - -
+- [Donate](#donate) # The big question, What is Kolbot-SoloPlay? - SoloPlay is a D2BS based auto-play system to level any single legacy diablo 2 character class from 1-99. That sounds like a bunch of verbage so let me break it down a bit. D2BS stands for ``Diablo 2 Botting System`` if you are familiar with Kolbot that is what you are using. SoloPlay only works for Diablo 2, so before you ask it is not for Diablo 2 Resurrected. Alright next, what is an auto-play system? In simple terms, SoloPlay works by set it and forget philosophy. It is profile driven so the only thing you need to worry about is filling out the profile with the correct format and then press start (how to set up the profile is defined below). After that it takes care of the rest, no setting up config files or settings files (like sonic or horde), ect. The goal is to be the fastest leveling system there is across all modes (classic/expansion, hardcore/softcore, ladder/non-ladder) @@ -77,6 +76,10 @@ **A:** In classic, the bot will switch to the final build after it defeats diablo and meets a level requirement. In expansion, it transitions to the final build when final gear requirements are met ``(Navigate to libs\SoloPlay\``[``BuildFiles\``](libs/SoloPlay/BuildFiles/)) and look for the file with the name of the final build you choose to see what items are needed for each build and what level is required for classic. +### **Q: Where can I see what items are used in the final build I selected?** + +**A:** Same place as above answer ``(Navigate to libs\SoloPlay\``[``BuildFiles\``](libs/SoloPlay/BuildFiles/)) + ### **Q: The bot has beaten diablo (classic) / baal, so why isn't moving on to the next difficulty?** **A:** The bot will only progress once it has reached a minimum character level (``navigate to libs\SoloPlay\``[``BuildFiles``](libs/SoloPlay/BuildFiles/)``\classname\classname.js`` and see `CharInfo.levelCap` for level requirments) and will not start the next difficulty with negative resistances. If the bot is more than 5 levels higher than the minimum character level and has not reached the required resistances, it will automatically move to the next difficulty. @@ -87,7 +90,7 @@ In expansion, it transitions to the final build when final gear requirements are ### **Q: HELP!!! There is an error when starting the bot?** -**A:** There was a bad installation OR the profile settings are wrong. First verify that you using the kolbot version linked the install guide below. Next, confirm you have installed all the files into their proper locations (including overwriting the existing `default.dbj`). Finally, verify the profile name and infotag follow the format of the install guide's instructions. +**A:** There was a bad installation OR the profile settings are wrong. First verify that you using the kolbot version linked the install guide below. Next, confirm you have installed all the files into their proper locations. Finally, verify the profile name and infotag follow the format of the install guide's instructions. ### **Q: HELP!!! The bot auto created my account and I can't find the password!** @@ -124,10 +127,10 @@ add your pickit files here or uncomment the kolton nip already present. | Step | Instructions | | |:------:|:-------|-------:| -| 1.| Download Kolbot here: [github.com/blizzhackers/kolbot](https://github.com/blizzhackers/kolbot). |![blizzhackers github](https://i.imgur.com/RksqKEA.jpg) | +| 1.| Download Kolbot here: [github.com/blizzhackers/kolbot](https://github.com/blizzhackers/kolbot). |![blizzhackers github](https://github.com/user-attachments/assets/0667e3f0-06ba-4ca7-b3c4-187e0c9c289c) | | 2.| Click the green button to Download SoloPlay. |![enter image description here](https://i.imgur.com/cNqZDbW.jpg) | -| 3.a| Copy and paste the following: `D2BotSoloPlay.dbj`, and the entire `\libs` folder into `\d2bs\kolbot\`.| ![kolbot](https://i.imgur.com/WNxJOhq.png) | -|3.b|A successful installation will show 1 new file in the folder: `D2BotSoloPlay.dbj` and look similar to the following image|![image](https://github.com/blizzhackers/kolbot-SoloPlay/assets/60308670/a85713f4-df22-4aa8-937d-21cba4366b0c) +| 3.a| Copy and paste the following: `D2BotSoloPlay.dbj`, and the entire `\libs` folder into `\d2bs\kolbot\`.| ![image](https://github.com/user-attachments/assets/b213af6f-9088-4d44-8584-889365f23b7c)| +|3.b|A successful installation will show 1 new file in the folder: `D2BotSoloPlay.dbj` and look similar to the following image|![image](https://github.com/user-attachments/assets/d5ef5093-7045-4407-a430-136b972417ab) | 4.| Select Add for new a Kolbot Profile. | ![Add-profile.jpg](https://imgur.com/tHs9ZoH.jpg)| | 4.a| Select and Input a profile name. See the **[Possible Profile Name Choices](#possible-profile-names)** below for a list of available options. | ![extract into](https://i.imgur.com/2YcGKVH.png) | | 4.b| ***Optional*** Input your account name. If no name than a random account is created. | | @@ -207,6 +210,7 @@ Pull requests are welcome. For major changes, please open an issue first to disc ![Kolbot-SoloPlay Paladin](https://user-images.githubusercontent.com/60308670/165398785-8ef1afd7-d232-4bc4-a23e-a1f1321ce0ed.png) +## Donate Buy Me A Coffee From 081a0113733910086fb355924c46fdaba429d022 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Fri, 10 Jan 2025 01:24:02 -0500 Subject: [PATCH 261/263] Fix blova build socketing shako + add socket griffon --- .../sorceress/sorceress.BlovaBuild.js | 91 +++++++++++++------ libs/SoloPlay/Config/Sorceress.js | 18 +++- 2 files changed, 78 insertions(+), 31 deletions(-) diff --git a/libs/SoloPlay/BuildFiles/sorceress/sorceress.BlovaBuild.js b/libs/SoloPlay/BuildFiles/sorceress/sorceress.BlovaBuild.js index 19ba2a27..48d03b74 100644 --- a/libs/SoloPlay/BuildFiles/sorceress/sorceress.BlovaBuild.js +++ b/libs/SoloPlay/BuildFiles/sorceress/sorceress.BlovaBuild.js @@ -1,10 +1,9 @@ /** -* @filename Sorceress.BlovaBuild.js -* @author isid0re, theBGuy -* @desc Blizzard + Nova based final build -* -*/ - + * @filename Sorceress.BlovaBuild.js + * @author isid0re, theBGuy + * @desc Blizzard + Nova based final build + * + */ (function (module) { module.exports = (function () { @@ -18,11 +17,13 @@ sdk.stats.PassiveColdPierce, sdk.stats.PassiveColdMastery, sdk.stats.PassiveLightningMastery, - sdk.stats.PassiveLightningPierce + sdk.stats.PassiveLightningPierce, ], wantedMerc: MercData[sdk.skills.HolyFreeze], stats: [ - ["strength", 156], ["dexterity", 35], ["vitality", "all"] + ["strength", 156], + ["dexterity", 35], + ["vitality", "all"], ], skills: [ [sdk.skills.Warmth, 1], @@ -45,8 +46,13 @@ classid: sdk.items.SmallCharm, /** @param {ItemUnit} check */ stats: function (check) { - return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MaxHp) === 20); - } + return ( + !check.unique + && check.classid === this.classid + && check.allRes === 5 + && check.getStat(sdk.stats.MaxHp) === 20 + ); + }, }, ResMf: { @@ -55,8 +61,13 @@ classid: sdk.items.SmallCharm, /** @param {ItemUnit} check */ stats: function (check) { - return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.MagicBonus) === 7); - } + return ( + !check.unique + && check.classid === this.classid + && check.allRes === 5 + && check.getStat(sdk.stats.MagicBonus) === 7 + ); + }, }, ResFHR: { @@ -65,8 +76,13 @@ classid: sdk.items.SmallCharm, /** @param {ItemUnit} check */ stats: function (check) { - return (!check.unique && check.classid === this.classid && check.allRes === 5 && check.getStat(sdk.stats.FHR) === 5); - } + return ( + !check.unique + && check.classid === this.classid + && check.allRes === 5 + && check.getStat(sdk.stats.FHR) === 5 + ); + }, }, SkillerLight: { @@ -75,9 +91,13 @@ classid: sdk.items.GrandCharm, /** @param {ItemUnit} check */ stats: function (check) { - return (!check.unique && check.classid === this.classid && check.getStat(sdk.stats.AddSkillTab, sdk.skills.tabs.Lightning) === 1 - && check.getStat(sdk.stats.MaxHp) >= 40); - } + return ( + !check.unique + && check.classid === this.classid + && check.getStat(sdk.stats.AddSkillTab, sdk.skills.tabs.Lightning) === 1 + && check.getStat(sdk.stats.MaxHp) >= 40 + ); + }, }, SkillerCold: { @@ -86,21 +106,33 @@ classid: sdk.items.GrandCharm, /** @param {ItemUnit} check */ stats: function (check) { - return (!check.unique && check.classid === this.classid && check.getStat(sdk.stats.AddSkillTab, sdk.skills.tabs.Cold) === 1 - && check.getStat(sdk.stats.MaxHp) >= 40); - } + return ( + !check.unique + && check.classid === this.classid + && check.getStat(sdk.stats.AddSkillTab, sdk.skills.tabs.Cold) === 1 + && check.getStat(sdk.stats.MaxHp) >= 40 + ); + }, }, }, AutoBuildTemplate: { - 1: { + 1: { Update: function () { - Config.AttackSkill = [-1, sdk.skills.Blizzard, sdk.skills.Nova, sdk.skills.Blizzard, sdk.skills.Nova, -1, sdk.skills.IceBlast]; + Config.AttackSkill = [ + -1, + sdk.skills.Blizzard, + sdk.skills.Nova, + sdk.skills.Blizzard, + sdk.skills.Nova, + -1, + sdk.skills.IceBlast, + ]; Config.LowManaSkill = [-1, -1]; Config.SkipImmune = ["lightning and cold"]; Config.HPBuffer = me.expansion ? 1 : 5; Config.MPBuffer = me.expansion ? 1 : 5; - } + }, }, }, @@ -108,15 +140,22 @@ if (me.classic) { return me.charlvl >= 75 && me.diablo; } else { - return (Attack.checkInfinity() || (me.data.merc.gear.includes(sdk.locale.items.Infinity) && !Misc.poll(() => me.getMerc(), 200, 50))); + return ( + Attack.checkInfinity() + || (me.data.merc.gear.includes(sdk.locale.items.Infinity) && !Misc.poll(() => me.getMerc(), 200, 50)) + ); } }, active: function () { - return this.respec() && me.getSkill(sdk.skills.Nova, sdk.skills.subindex.HardPoints) === 20 && me.getSkill(sdk.skills.Blizzard, sdk.skills.subindex.HardPoints) >= 1; + return ( + this.respec() + && me.getSkill(sdk.skills.Nova, sdk.skills.subindex.HardPoints) === 20 + && me.getSkill(sdk.skills.Blizzard, sdk.skills.subindex.HardPoints) >= 1 + ); }, }; - + // autoequip final gear let finalGear = [ // Weapon - HotO diff --git a/libs/SoloPlay/Config/Sorceress.js b/libs/SoloPlay/Config/Sorceress.js index bf5bba07..b3b652fe 100644 --- a/libs/SoloPlay/Config/Sorceress.js +++ b/libs/SoloPlay/Config/Sorceress.js @@ -226,12 +226,20 @@ return !me.getOwned(wanted).length && item.ilvl >= 41 && item.isBaseType && !item.ethereal; } )); - Config.socketables.push(addSocketableObj(sdk.items.Shako, - [sdk.items.runes.Um], [sdk.items.gems.Perfect.Ruby], - true, (item) => item.unique && !item.ethereal - )); - + + if (SetUp.finalBuild === "Lightning") { + Config.socketables.push(addSocketableObj(sdk.items.Shako, + [sdk.items.runes.Um], [sdk.items.gems.Perfect.Ruby], + true, (item) => item.unique && !item.ethereal + )); + } else { + Config.socketables.push(addSocketableObj(sdk.items.Diadem, + [sdk.items.runes.Um], [sdk.items.gems.Perfect.Ruby], + true, (item) => item.unique && !item.ethereal && item.getStat(sdk.stats.PierceLtng) === 20 + )); + } Check.itemSockables(sdk.items.Shako, "unique", "Harlequin Crest"); + Check.itemSockables(sdk.items.Diadem, "unique", "Griffon's Eye"); break; case "Meteorb": From 7c8837eaf879b8d9b69b0c7aba90faa3852cdf97 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Wed, 15 Jan 2025 15:07:34 -0500 Subject: [PATCH 262/263] Update ToolsThread.js - Log realm going down event --- libs/SoloPlay/Threads/ToolsThread.js | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/libs/SoloPlay/Threads/ToolsThread.js b/libs/SoloPlay/Threads/ToolsThread.js index 692bffda..358cb427 100644 --- a/libs/SoloPlay/Threads/ToolsThread.js +++ b/libs/SoloPlay/Threads/ToolsThread.js @@ -552,6 +552,12 @@ function main () { Config.KillDclone && Messaging.sendToScript(SoloEvents.filePath, "killdclone"); } + break; + case 0x0f: // "Realm going down in %Param1 minutes." + { + let realmDownStr = getLocaleString(sdk.locale.text.RealmGoingDownInXMinutes).replace("%d", param1); + D2Bot.printToConsole(realmDownStr, sdk.colors.D2Bot.DarkGold); + } break; } }; From e4912a3fd25d7df1081ca9fedd393addcd43e1dc Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Mon, 20 Jan 2025 02:10:46 -0500 Subject: [PATCH 263/263] Set default `Config.UseMerc` value to true to allow checking merc for infinity before config is loaded during init - Lightning/Blova builds need to use `Attack.checkInfinity` in order to determine which build to load. The method checks `Config,UseMerc` which at the time of `SetUp.init` is still the default value as we haven't loaded our actual config yet --- libs/SoloPlay/Functions/ConfigOverrides.js | 1 + 1 file changed, 1 insertion(+) diff --git a/libs/SoloPlay/Functions/ConfigOverrides.js b/libs/SoloPlay/Functions/ConfigOverrides.js index 23a3b9e7..31f62530 100644 --- a/libs/SoloPlay/Functions/ConfigOverrides.js +++ b/libs/SoloPlay/Functions/ConfigOverrides.js @@ -59,3 +59,4 @@ Config.init = function (notify) { console.error(e3); } }; +Config.UseMerc = true;